2 * Copyright 2007-2008, Haiku. Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT License.
5 #include "MediaTrackVideoSupplier.h"
11 #include <MediaTrack.h>
13 #include "ColorSpaceToString.h"
17 #define DEBUG_DECODED_FRAME 0
18 #if DEBUG_DECODED_FRAME
20 # include <BitmapStream.h>
22 # include <TranslatorRoster.h>
23 #endif // DEBUG_DECODED_FRAME
26 MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack
* track
,
27 int32 trackIndex
, status_t
& initStatus
)
36 fTrackIndex(trackIndex
)
39 printf("MediaTrackVideoSupplier() - no video track\n");
43 initStatus
= _SwitchFormat(B_NO_COLOR_SPACE
, 0);
45 fDuration
= fVideoTrack
->Duration();
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);
56 MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
62 MediaTrackVideoSupplier::Format() const
69 MediaTrackVideoSupplier::GetEncodedFormat(media_format
* format
) const
73 return fVideoTrack
->EncodedFormat(format
);
78 MediaTrackVideoSupplier::GetCodecInfo(media_codec_info
* info
) const
82 return fVideoTrack
->GetCodecInfo(info
);
87 MediaTrackVideoSupplier::ReadFrame(void* buffer
, bigtime_t
* performanceTime
,
88 const media_raw_video_format
& format
, bool& wasCached
)
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
);
103 fprintf(stderr
, "MediaTrackVideoSupplier::ReadFrame() - "
104 "unable to switch media format: %s\n", strerror(ret
));
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
);
116 if (ret
!= B_LAST_BUFFER_ERROR
) {
117 fprintf(stderr
, "MediaTrackVideoSupplier::ReadFrame() - "
118 "error while reading frame of track: %s\n", strerror(ret
));
121 fPerformanceTime
= mediaHeader
.start_time
;
123 fCurrentFrame
= fVideoTrack
->CurrentFrame();
125 *performanceTime
= fPerformanceTime
;
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
);
138 #endif // DEBUG_DECODED_FRAME
145 MediaTrackVideoSupplier::FindKeyFrameForFrame(int64
* frame
)
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);
159 MediaTrackVideoSupplier::SeekToTime(bigtime_t
* performanceTime
)
164 bigtime_t _performanceTime
= *performanceTime
;
165 status_t ret
= fVideoTrack
->FindKeyFrameForTime(performanceTime
,
166 B_MEDIA_SEEK_CLOSEST_BACKWARD
);
170 ret
= fVideoTrack
->SeekToTime(performanceTime
);
172 if (_performanceTime
!= *performanceTime
) {
173 printf("seeked by time: %" B_PRIdBIGTIME
" -> %" B_PRIdBIGTIME
174 "\n", _performanceTime
, *performanceTime
);
176 fPerformanceTime
= *performanceTime
;
177 fCurrentFrame
= fVideoTrack
->CurrentFrame();
185 MediaTrackVideoSupplier::SeekToFrame(int64
* frame
)
190 int64 wantFrame
= *frame
;
192 if (wantFrame
== fCurrentFrame
)
195 status_t ret
= fVideoTrack
->FindKeyFrameForFrame(frame
,
196 B_MEDIA_SEEK_CLOSEST_BACKWARD
);
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
) {
212 //if (wantFrame != *frame) {
213 // printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame);
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
;
223 ret
= fVideoTrack
->SeekToFrame(frame
);
227 //if (wantFrame != *frame) {
228 // printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
232 fCurrentFrame
= *frame
;
233 fPerformanceTime
= fVideoTrack
->CurrentTime();
243 MediaTrackVideoSupplier::Bounds() const
245 return BRect(0, 0, fFormat
.u
.raw_video
.display
.line_width
- 1,
246 fFormat
.u
.raw_video
.display
.line_count
- 1);
251 MediaTrackVideoSupplier::ColorSpace() const
253 return fFormat
.u
.raw_video
.display
.format
;
258 MediaTrackVideoSupplier::BytesPerRow() const
260 return fFormat
.u
.raw_video
.display
.bytes_per_row
;
268 MediaTrackVideoSupplier::_SwitchFormat(color_space format
, uint32 bytesPerRow
)
270 // get the encoded format
271 memset(&fFormat
, 0, sizeof(media_format
));
272 status_t ret
= fVideoTrack
->EncodedFormat(&fFormat
);
274 printf("MediaTrackVideoSupplier::_SwitchFormat() - "
275 "fVideoTrack->EncodedFormat(): %s\n", strerror(ret
));
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
289 printf("MediaTrackVideoSupplier::_SwitchFormat() - "
290 "preferred color space: %s\n",
291 color_space_to_string(format
));
295 uint32 minBytesPerRow
;
296 if (format
== B_YCbCr422
)
297 minBytesPerRow
= ((width
* 2 + 3) / 4) * 4;
299 minBytesPerRow
= width
* 4;
300 bytesPerRow
= max_c(bytesPerRow
, minBytesPerRow
);
302 ret
= _SetDecodedFormat(width
, height
, format
, bytesPerRow
);
304 printf("MediaTrackVideoSupplier::_SwitchFormat() - "
305 "fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n",
308 bytesPerRow
= max_c(bytesPerRow
, width
* 4);
310 ret
= _SetDecodedFormat(width
, height
, format
, bytesPerRow
);
312 printf("MediaTrackVideoSupplier::_SwitchFormat() - "
313 "fVideoTrack->DecodedFormat(): %s - giving up\n",
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;
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");
333 ret
= _SetDecodedFormat(width
, height
, format
, minBytesPerRow
);
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
);
347 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width
, uint32 height
,
348 color_space format
, uint32 bytesPerRow
)
350 // specifiy the decoded format. we derive this information from
351 // the encoded format (width & height).
352 memset(&fFormat
, 0, sizeof(media_format
));
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
;
362 return fVideoTrack
->DecodedFormat(&fFormat
);