xref: /haiku/src/apps/mediaplayer/supplier/MediaTrackVideoSupplier.cpp (revision a906d0a031e721e2f2ec9d95274103e74a3a774f)
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: %lld -> %lld\n", _performanceTime, *performanceTime);
174 		fPerformanceTime = *performanceTime;
175 		fCurrentFrame = fVideoTrack->CurrentFrame();
176 	}
177 
178 	return ret;
179 }
180 
181 
182 status_t
183 MediaTrackVideoSupplier::SeekToFrame(int64* frame)
184 {
185 	if (!fVideoTrack)
186 		return B_NO_INIT;
187 
188 	int64 wantFrame = *frame;
189 
190 	if (wantFrame == fCurrentFrame)
191 		return B_OK;
192 
193 	status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
194 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
195 	if (ret != B_OK)
196 		return ret;
197 	if (wantFrame > *frame) {
198 		// Work around a rounding problem with some extractors and
199 		// converting frames <-> time <-> internal time.
200 		int64 nextWantFrame = wantFrame + 1;
201 		if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame,
202 			B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) {
203 			if (nextWantFrame == wantFrame) {
204 				wantFrame++;
205 				*frame = wantFrame;
206 			}
207 		}
208 	}
209 
210 //if (wantFrame != *frame) {
211 //	printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame);
212 //}
213 
214 	if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) {
215 		// The current frame is already closer to the wanted frame
216 		// than the next keyframe before it.
217 		*frame = fCurrentFrame;
218 		return B_OK;
219 	}
220 
221 	ret = fVideoTrack->SeekToFrame(frame);
222 	if (ret != B_OK)
223 		return ret;
224 
225 //if (wantFrame != *frame) {
226 //	printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
227 //		fCurrentFrame);
228 //}
229 
230 	fCurrentFrame = *frame;
231 	fPerformanceTime = fVideoTrack->CurrentTime();
232 
233 	return ret;
234 }
235 
236 
237 // #pragma mark -
238 
239 
240 BRect
241 MediaTrackVideoSupplier::Bounds() const
242 {
243 	return BRect(0, 0, 	fFormat.u.raw_video.display.line_width - 1,
244 		fFormat.u.raw_video.display.line_count - 1);
245 }
246 
247 
248 color_space
249 MediaTrackVideoSupplier::ColorSpace() const
250 {
251 	return fFormat.u.raw_video.display.format;
252 }
253 
254 
255 uint32
256 MediaTrackVideoSupplier::BytesPerRow() const
257 {
258 	return fFormat.u.raw_video.display.bytes_per_row;
259 }
260 
261 
262 // #pragma mark -
263 
264 
265 status_t
266 MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow)
267 {
268 	// get the encoded format
269 	memset(&fFormat, 0, sizeof(media_format));
270 	status_t ret = fVideoTrack->EncodedFormat(&fFormat);
271 	if (ret < B_OK) {
272 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
273 			"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
274 		return ret;
275 	}
276 
277 	// get ouput video frame size
278 	uint32 width = fFormat.u.encoded_video.output.display.line_width;
279 	uint32 height = fFormat.u.encoded_video.output.display.line_count;
280 	if (format == B_NO_COLOR_SPACE) {
281 		format = fFormat.u.encoded_video.output.display.format;
282 		if (format == B_NO_COLOR_SPACE) {
283 			// if still no preferred format, try the most commonly
284 			// supported overlay format
285 			format = B_YCbCr422;
286 		} else {
287 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
288 				"preferred color space: %s\n",
289 				color_space_to_string(format));
290 		}
291 	}
292 
293 	uint32 minBytesPerRow;
294 	if (format == B_YCbCr422)
295 		minBytesPerRow = ((width * 2 + 3) / 4) * 4;
296 	else
297 		minBytesPerRow = width * 4;
298 	bytesPerRow = max_c(bytesPerRow, minBytesPerRow);
299 
300 	ret = _SetDecodedFormat(width, height, format, bytesPerRow);
301 	if (ret < B_OK) {
302 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
303 			"fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n",
304 			strerror(ret));
305 		format = B_RGB32;
306 		bytesPerRow = max_c(bytesPerRow, width * 4);
307 
308 		ret = _SetDecodedFormat(width, height, format, bytesPerRow);
309 		if (ret < B_OK) {
310 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
311 				"fVideoTrack->DecodedFormat(): %s - giving up\n",
312 				strerror(ret));
313 			return ret;
314 		}
315 	}
316 
317 	if (fFormat.u.raw_video.display.format != format) {
318 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
319 			" codec changed colorspace of decoded format (%s -> %s)!\n",
320 			color_space_to_string(format),
321 			color_space_to_string(fFormat.u.raw_video.display.format));
322 		// check if the codec forgot to adjust bytes_per_row
323 		format = fFormat.u.raw_video.display.format;
324 		if (format == B_YCbCr422)
325 			minBytesPerRow = ((width * 2 + 3) / 4) * 4;
326 		else
327 			minBytesPerRow = width * 4;
328 		if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) {
329 			printf("  -> stupid codec forgot to adjust bytes_per_row!\n");
330 
331 			ret = _SetDecodedFormat(width, height, format, minBytesPerRow);
332 		}
333 	}
334 
335 	if (fFormat.u.raw_video.last_active != height - 1) {
336 		printf("should skip %ld lines at bottom!\n",
337 			(height - 1) - fFormat.u.raw_video.last_active);
338 	}
339 
340 	return ret;
341 }
342 
343 
344 status_t
345 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height,
346 	color_space format, uint32 bytesPerRow)
347 {
348 	// specifiy the decoded format. we derive this information from
349 	// the encoded format (width & height).
350 	memset(&fFormat, 0, sizeof(media_format));
351 //	fFormat.u.raw_video.last_active = height - 1;
352 //	fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
353 //	fFormat.u.raw_video.pixel_width_aspect = 1;
354 //	fFormat.u.raw_video.pixel_height_aspect = 1;
355 	fFormat.u.raw_video.display.format = format;
356 	fFormat.u.raw_video.display.line_width = width;
357 	fFormat.u.raw_video.display.line_count = height;
358 	fFormat.u.raw_video.display.bytes_per_row = bytesPerRow;
359 
360 	return fVideoTrack->DecodedFormat(&fFormat);
361 }
362 
363