xref: /haiku/src/apps/mediaplayer/supplier/MediaTrackVideoSupplier.cpp (revision f6d0ea5cad3469d65f1e740cd6e37b545a3e34e0)
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
MediaTrackVideoSupplier(BMediaTrack * track,int32 trackIndex,status_t & initStatus)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
~MediaTrackVideoSupplier()56 MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
57 {
58 }
59 
60 
61 const media_format&
Format() const62 MediaTrackVideoSupplier::Format() const
63 {
64 	return fFormat;
65 }
66 
67 
68 status_t
GetEncodedFormat(media_format * format) const69 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
GetCodecInfo(media_codec_info * info) const78 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
ReadFrame(void * buffer,bigtime_t * performanceTime,const media_raw_video_format & format,bool & wasCached)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 	// Hack for single frame video (cover art). Media player really wants a
96 	// video track and will not seek in the middle of a video frame. So we
97 	// pretend to be a 25fps stream and keep rendering the same frame over
98 	// and over again.
99 	if (fVideoTrack->CountFrames() < 2) {
100 		static int already = false;
101 		if (already) {
102 			wasCached = true;
103 			return B_OK;
104 		}
105 		already = true;
106 	}
107 
108 	status_t ret = B_OK;
109 	if (format.display.format
110 			!= fFormat.u.raw_video.display.format
111 		|| fFormat.u.raw_video.display.bytes_per_row
112 			!= format.display.bytes_per_row) {
113 		ret = _SwitchFormat(format.display.format,
114 			format.display.bytes_per_row);
115 		if (ret < B_OK) {
116 			fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
117 				"unable to switch media format: %s\n", strerror(ret));
118 			return ret;
119 		}
120 	}
121 
122 	// read a frame
123 	int64 frameCount = 1;
124 	// TODO: how does this work for interlaced video (field count > 1)?
125 	media_header mediaHeader;
126 	ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
127 
128 	if (ret < B_OK && ret != B_LAST_BUFFER_ERROR) {
129 		fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
130 			"error while reading frame of track: %s\n", strerror(ret));
131 	} else
132 		fPerformanceTime = mediaHeader.start_time;
133 
134 	fCurrentFrame = fVideoTrack->CurrentFrame();
135 	if (performanceTime)
136 		*performanceTime = fPerformanceTime;
137 
138 #if DEBUG_DECODED_FRAME
139 if (modifiers() & B_SHIFT_KEY) {
140 BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
141 BTranslatorRoster* roster = BTranslatorRoster::Default();
142 BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow());
143 memcpy(bitmap->Bits(), buffer, bitmap->BitsLength());
144 BBitmapStream bitmapStream(bitmap);
145 roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0);
146 bitmapStream.DetachBitmap(&bitmap);
147 delete bitmap;
148 }
149 #endif // DEBUG_DECODED_FRAME
150 
151 	if (ret == B_LAST_BUFFER_ERROR && fVideoTrack->CountFrames() < 2)
152 		return B_OK;
153 	return ret;
154 }
155 
156 
157 status_t
FindKeyFrameForFrame(int64 * frame)158 MediaTrackVideoSupplier::FindKeyFrameForFrame(int64* frame)
159 {
160 	if (!fVideoTrack)
161 		return B_NO_INIT;
162 
163 	if (fVideoTrack->CountFrames() < 2)
164 		return B_OK;
165 
166 //int64 wantedFrame = *frame;
167 	status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
168 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
169 //printf("found keyframe for frame %lld -> %lld\n", wantedFrame, *frame);
170 	return ret;
171 }
172 
173 
174 status_t
SeekToTime(bigtime_t * performanceTime)175 MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
176 {
177 	if (!fVideoTrack)
178 		return B_NO_INIT;
179 
180 	if (fVideoTrack->CountFrames() < 2)
181 		return B_OK;
182 
183 	bigtime_t _performanceTime = *performanceTime;
184 	status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime,
185 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
186 	if (ret < B_OK)
187 		return ret;
188 
189 	ret = fVideoTrack->SeekToTime(performanceTime);
190 	if (ret == B_OK) {
191 		if (_performanceTime != *performanceTime) {
192 			printf("seeked by time: %" B_PRIdBIGTIME " -> %" B_PRIdBIGTIME
193 				"\n", _performanceTime, *performanceTime);
194 		}
195 		fPerformanceTime = *performanceTime;
196 		fCurrentFrame = fVideoTrack->CurrentFrame();
197 	}
198 
199 	return ret;
200 }
201 
202 
203 status_t
SeekToFrame(int64 * frame)204 MediaTrackVideoSupplier::SeekToFrame(int64* frame)
205 {
206 	if (!fVideoTrack)
207 		return B_NO_INIT;
208 
209 	if (fVideoTrack->CountFrames() < 2)
210 		return B_OK;
211 
212 	int64 wantFrame = *frame;
213 
214 	if (wantFrame == fCurrentFrame)
215 		return B_OK;
216 
217 	status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
218 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
219 	if (ret != B_OK)
220 		return ret;
221 	if (wantFrame > *frame) {
222 		// Work around a rounding problem with some extractors and
223 		// converting frames <-> time <-> internal time.
224 		int64 nextWantFrame = wantFrame + 1;
225 		if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame,
226 			B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) {
227 			if (nextWantFrame == wantFrame) {
228 				wantFrame++;
229 				*frame = wantFrame;
230 			}
231 		}
232 	}
233 
234 //if (wantFrame != *frame) {
235 //	printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame);
236 //}
237 
238 	if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) {
239 		// The current frame is already closer to the wanted frame
240 		// than the next keyframe before it.
241 		*frame = fCurrentFrame;
242 		return B_OK;
243 	}
244 
245 	ret = fVideoTrack->SeekToFrame(frame);
246 	if (ret != B_OK)
247 		return ret;
248 
249 //if (wantFrame != *frame) {
250 //	printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
251 //		fCurrentFrame);
252 //}
253 
254 	fCurrentFrame = *frame;
255 	fPerformanceTime = fVideoTrack->CurrentTime();
256 
257 	return ret;
258 }
259 
260 
261 // #pragma mark -
262 
263 
264 BRect
Bounds() const265 MediaTrackVideoSupplier::Bounds() const
266 {
267 	return BRect(0, 0, 	fFormat.u.raw_video.display.line_width - 1,
268 		fFormat.u.raw_video.display.line_count - 1);
269 }
270 
271 
272 color_space
ColorSpace() const273 MediaTrackVideoSupplier::ColorSpace() const
274 {
275 	return fFormat.u.raw_video.display.format;
276 }
277 
278 
279 uint32
BytesPerRow() const280 MediaTrackVideoSupplier::BytesPerRow() const
281 {
282 	return fFormat.u.raw_video.display.bytes_per_row;
283 }
284 
285 
286 // #pragma mark -
287 
288 
289 status_t
_SwitchFormat(color_space format,uint32 bytesPerRow)290 MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow)
291 {
292 	// get the encoded format
293 	fFormat.Clear();
294 	status_t ret = fVideoTrack->EncodedFormat(&fFormat);
295 	if (ret < B_OK) {
296 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
297 			"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
298 		return ret;
299 	}
300 
301 	// get ouput video frame size
302 	uint32 width = fFormat.u.encoded_video.output.display.line_width;
303 	uint32 height = fFormat.u.encoded_video.output.display.line_count;
304 	if (format == B_NO_COLOR_SPACE) {
305 		format = fFormat.u.encoded_video.output.display.format;
306 		if (format == B_NO_COLOR_SPACE) {
307 			// if still no preferred format, try the most commonly
308 			// supported overlay format
309 			format = B_YCbCr422;
310 		} else {
311 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
312 				"preferred color space: %s\n",
313 				color_space_to_string(format));
314 		}
315 	}
316 
317 	uint32 minBytesPerRow;
318 	if (format == B_YCbCr422)
319 		minBytesPerRow = ((width * 2 + 3) / 4) * 4;
320 	else
321 		minBytesPerRow = width * 4;
322 	bytesPerRow = max_c(bytesPerRow, minBytesPerRow);
323 
324 	ret = _SetDecodedFormat(width, height, format, bytesPerRow);
325 	if (ret < B_OK) {
326 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
327 			"fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n",
328 			strerror(ret));
329 		format = B_RGB32;
330 		bytesPerRow = max_c(bytesPerRow, width * 4);
331 
332 		ret = _SetDecodedFormat(width, height, format, bytesPerRow);
333 		if (ret < B_OK) {
334 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
335 				"fVideoTrack->DecodedFormat(): %s - giving up\n",
336 				strerror(ret));
337 			return ret;
338 		}
339 	}
340 
341 	if (fFormat.u.raw_video.display.format != format) {
342 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
343 			" codec changed colorspace of decoded format (%s -> %s)!\n",
344 			color_space_to_string(format),
345 			color_space_to_string(fFormat.u.raw_video.display.format));
346 		// check if the codec forgot to adjust bytes_per_row
347 		format = fFormat.u.raw_video.display.format;
348 		if (format == B_YCbCr422)
349 			minBytesPerRow = ((width * 2 + 3) / 4) * 4;
350 		else
351 			minBytesPerRow = width * 4;
352 		if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) {
353 			printf("  -> stupid codec forgot to adjust bytes_per_row!\n");
354 
355 			ret = _SetDecodedFormat(width, height, format, minBytesPerRow);
356 		}
357 	}
358 
359 	if (fFormat.u.raw_video.last_active != height - 1) {
360 		printf("should skip %" B_PRId32 " lines at bottom!\n",
361 			(height - 1) - fFormat.u.raw_video.last_active);
362 	}
363 
364 	return ret;
365 }
366 
367 
368 status_t
_SetDecodedFormat(uint32 width,uint32 height,color_space format,uint32 bytesPerRow)369 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height,
370 	color_space format, uint32 bytesPerRow)
371 {
372 	// specifiy the decoded format. we derive this information from
373 	// the encoded format (width & height).
374 	fFormat.Clear();
375 //	fFormat.u.raw_video.last_active = height - 1;
376 //	fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
377 //	fFormat.u.raw_video.pixel_width_aspect = 1;
378 //	fFormat.u.raw_video.pixel_height_aspect = 1;
379 	fFormat.u.raw_video.display.format = format;
380 	fFormat.u.raw_video.display.line_width = width;
381 	fFormat.u.raw_video.display.line_count = height;
382 	fFormat.u.raw_video.display.bytes_per_row = bytesPerRow;
383 
384 	return fVideoTrack->DecodedFormat(&fFormat);
385 }
386 
387