xref: /haiku/src/apps/mediaplayer/supplier/MediaTrackVideoSupplier.cpp (revision 5bd0fbd13a1e832f91643aaa921fbc0879abd518)
1 /*
2  * Copyright 2007-2008, Haiku. Stephan Aßmus <superstippi@gmx.de>
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 #include "MediaTrackVideoSupplier.h"
6 
7 #include <new>
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include <MediaTrack.h>
12 
13 #include "ColorSpaceToString.h"
14 
15 using std::nothrow;
16 
17 #define DEBUG_DECODED_FRAME 0
18 #if DEBUG_DECODED_FRAME
19 #  include <Bitmap.h>
20 #  include <BitmapStream.h>
21 #  include <File.h>
22 #  include <TranslatorRoster.h>
23 #endif // DEBUG_DECODED_FRAME
24 
25 // constructor
26 MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
27 		int32 trackIndex, status_t& initStatus)
28 	:
29 	VideoTrackSupplier(),
30 	fVideoTrack(track),
31 
32 	fPerformanceTime(0),
33 	fDuration(0),
34 	fCurrentFrame(0),
35 
36 	fTrackIndex(trackIndex)
37 {
38 	if (!fVideoTrack) {
39 		printf("MediaTrackVideoSupplier() - no video track\n");
40 		return;
41 	}
42 
43 	initStatus = _SwitchFormat(B_NO_COLOR_SPACE, 0);
44 
45 	fDuration = fVideoTrack->Duration();
46 
47 //	for (bigtime_t time = 0; time < fDuration; time += 10000) {
48 //		bigtime_t keyFrameTime = time;
49 //		fVideoTrack->FindKeyFrameForTime(&keyFrameTime,
50 //			B_MEDIA_SEEK_CLOSEST_BACKWARD);
51 //		printf("keyframe time for time: %lld -> %lld\n", time, keyFrameTime);
52 //	}
53 }
54 
55 // destructor
56 MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
57 {
58 }
59 
60 
61 const media_format&
62 MediaTrackVideoSupplier::Format() const
63 {
64 	return fFormat;
65 }
66 
67 
68 status_t
69 MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const
70 {
71 	if (!fVideoTrack)
72 		return B_NO_INIT;
73 	return fVideoTrack->EncodedFormat(format);
74 }
75 
76 
77 status_t
78 MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
79 {
80 	if (!fVideoTrack)
81 		return B_NO_INIT;
82 	return fVideoTrack->GetCodecInfo(info);
83 }
84 
85 
86 status_t
87 MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
88 	const media_raw_video_format& format, bool& wasCached)
89 {
90 	if (!fVideoTrack)
91 		return B_NO_INIT;
92 	if (!buffer)
93 		return B_BAD_VALUE;
94 
95 	status_t ret = B_OK;
96 	if (format.display.format
97 			!= fFormat.u.raw_video.display.format
98 		|| fFormat.u.raw_video.display.bytes_per_row
99 			!= format.display.bytes_per_row) {
100 		ret = _SwitchFormat(format.display.format,
101 			format.display.bytes_per_row);
102 		if (ret < B_OK) {
103 			fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
104 				"unable to switch media format: %s\n", strerror(ret));
105 			return ret;
106 		}
107 	}
108 
109 	// read a frame
110 	int64 frameCount = 1;
111 	// TODO: how does this work for interlaced video (field count > 1)?
112 	media_header mediaHeader;
113 	ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
114 
115 	if (ret < B_OK) {
116 		if (ret != B_LAST_BUFFER_ERROR) {
117 			fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
118 				"error while reading frame of track: %s\n", strerror(ret));
119 		}
120 	} else
121 		fPerformanceTime = mediaHeader.start_time;
122 
123 	fCurrentFrame = fVideoTrack->CurrentFrame();
124 	if (performanceTime)
125 		*performanceTime = fPerformanceTime;
126 
127 #if DEBUG_DECODED_FRAME
128 if (modifiers() & B_SHIFT_KEY) {
129 BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
130 BTranslatorRoster* roster = BTranslatorRoster::Default();
131 BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow());
132 memcpy(bitmap->Bits(), buffer, bitmap->BitsLength());
133 BBitmapStream bitmapStream(bitmap);
134 roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0);
135 bitmapStream.DetachBitmap(&bitmap);
136 delete bitmap;
137 }
138 #endif // DEBUG_DECODED_FRAME
139 
140 	return ret;
141 }
142 
143 
144 status_t
145 MediaTrackVideoSupplier::FindKeyFrameForFrame(int64* frame)
146 {
147 	if (!fVideoTrack)
148 		return B_NO_INIT;
149 
150 //int64 wantedFrame = *frame;
151 	status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
152 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
153 //printf("found keyframe for frame %lld -> %lld\n", wantedFrame, *frame);
154 	return ret;
155 }
156 
157 
158 status_t
159 MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
160 {
161 	if (!fVideoTrack)
162 		return B_NO_INIT;
163 
164 bigtime_t _performanceTime = *performanceTime;
165 	status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime,
166 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
167 	if (ret < B_OK)
168 		return ret;
169 
170 	ret = fVideoTrack->SeekToTime(performanceTime);
171 	if (ret == B_OK) {
172 		if (_performanceTime != *performanceTime) {
173 			printf("seeked by time: %" B_PRIdBIGTIME " -> %" B_PRIdBIGTIME
174 				"\n", _performanceTime, *performanceTime);
175 		}
176 		fPerformanceTime = *performanceTime;
177 		fCurrentFrame = fVideoTrack->CurrentFrame();
178 	}
179 
180 	return ret;
181 }
182 
183 
184 status_t
185 MediaTrackVideoSupplier::SeekToFrame(int64* frame)
186 {
187 	if (!fVideoTrack)
188 		return B_NO_INIT;
189 
190 	int64 wantFrame = *frame;
191 
192 	if (wantFrame == fCurrentFrame)
193 		return B_OK;
194 
195 	status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
196 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
197 	if (ret != B_OK)
198 		return ret;
199 	if (wantFrame > *frame) {
200 		// Work around a rounding problem with some extractors and
201 		// converting frames <-> time <-> internal time.
202 		int64 nextWantFrame = wantFrame + 1;
203 		if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame,
204 			B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) {
205 			if (nextWantFrame == wantFrame) {
206 				wantFrame++;
207 				*frame = wantFrame;
208 			}
209 		}
210 	}
211 
212 //if (wantFrame != *frame) {
213 //	printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame);
214 //}
215 
216 	if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) {
217 		// The current frame is already closer to the wanted frame
218 		// than the next keyframe before it.
219 		*frame = fCurrentFrame;
220 		return B_OK;
221 	}
222 
223 	ret = fVideoTrack->SeekToFrame(frame);
224 	if (ret != B_OK)
225 		return ret;
226 
227 //if (wantFrame != *frame) {
228 //	printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
229 //		fCurrentFrame);
230 //}
231 
232 	fCurrentFrame = *frame;
233 	fPerformanceTime = fVideoTrack->CurrentTime();
234 
235 	return ret;
236 }
237 
238 
239 // #pragma mark -
240 
241 
242 BRect
243 MediaTrackVideoSupplier::Bounds() const
244 {
245 	return BRect(0, 0, 	fFormat.u.raw_video.display.line_width - 1,
246 		fFormat.u.raw_video.display.line_count - 1);
247 }
248 
249 
250 color_space
251 MediaTrackVideoSupplier::ColorSpace() const
252 {
253 	return fFormat.u.raw_video.display.format;
254 }
255 
256 
257 uint32
258 MediaTrackVideoSupplier::BytesPerRow() const
259 {
260 	return fFormat.u.raw_video.display.bytes_per_row;
261 }
262 
263 
264 // #pragma mark -
265 
266 
267 status_t
268 MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow)
269 {
270 	// get the encoded format
271 	fFormat.Clear();
272 	status_t ret = fVideoTrack->EncodedFormat(&fFormat);
273 	if (ret < B_OK) {
274 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
275 			"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
276 		return ret;
277 	}
278 
279 	// get ouput video frame size
280 	uint32 width = fFormat.u.encoded_video.output.display.line_width;
281 	uint32 height = fFormat.u.encoded_video.output.display.line_count;
282 	if (format == B_NO_COLOR_SPACE) {
283 		format = fFormat.u.encoded_video.output.display.format;
284 		if (format == B_NO_COLOR_SPACE) {
285 			// if still no preferred format, try the most commonly
286 			// supported overlay format
287 			format = B_YCbCr422;
288 		} else {
289 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
290 				"preferred color space: %s\n",
291 				color_space_to_string(format));
292 		}
293 	}
294 
295 	uint32 minBytesPerRow;
296 	if (format == B_YCbCr422)
297 		minBytesPerRow = ((width * 2 + 3) / 4) * 4;
298 	else
299 		minBytesPerRow = width * 4;
300 	bytesPerRow = max_c(bytesPerRow, minBytesPerRow);
301 
302 	ret = _SetDecodedFormat(width, height, format, bytesPerRow);
303 	if (ret < B_OK) {
304 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
305 			"fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n",
306 			strerror(ret));
307 		format = B_RGB32;
308 		bytesPerRow = max_c(bytesPerRow, width * 4);
309 
310 		ret = _SetDecodedFormat(width, height, format, bytesPerRow);
311 		if (ret < B_OK) {
312 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
313 				"fVideoTrack->DecodedFormat(): %s - giving up\n",
314 				strerror(ret));
315 			return ret;
316 		}
317 	}
318 
319 	if (fFormat.u.raw_video.display.format != format) {
320 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
321 			" codec changed colorspace of decoded format (%s -> %s)!\n",
322 			color_space_to_string(format),
323 			color_space_to_string(fFormat.u.raw_video.display.format));
324 		// check if the codec forgot to adjust bytes_per_row
325 		format = fFormat.u.raw_video.display.format;
326 		if (format == B_YCbCr422)
327 			minBytesPerRow = ((width * 2 + 3) / 4) * 4;
328 		else
329 			minBytesPerRow = width * 4;
330 		if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) {
331 			printf("  -> stupid codec forgot to adjust bytes_per_row!\n");
332 
333 			ret = _SetDecodedFormat(width, height, format, minBytesPerRow);
334 		}
335 	}
336 
337 	if (fFormat.u.raw_video.last_active != height - 1) {
338 		printf("should skip %" B_PRId32 " lines at bottom!\n",
339 			(height - 1) - fFormat.u.raw_video.last_active);
340 	}
341 
342 	return ret;
343 }
344 
345 
346 status_t
347 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height,
348 	color_space format, uint32 bytesPerRow)
349 {
350 	// specifiy the decoded format. we derive this information from
351 	// the encoded format (width & height).
352 	fFormat.Clear();
353 //	fFormat.u.raw_video.last_active = height - 1;
354 //	fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
355 //	fFormat.u.raw_video.pixel_width_aspect = 1;
356 //	fFormat.u.raw_video.pixel_height_aspect = 1;
357 	fFormat.u.raw_video.display.format = format;
358 	fFormat.u.raw_video.display.line_width = width;
359 	fFormat.u.raw_video.display.line_count = height;
360 	fFormat.u.raw_video.display.bytes_per_row = bytesPerRow;
361 
362 	return fVideoTrack->DecodedFormat(&fFormat);
363 }
364 
365