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