repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / mediaplayer / supplier / MediaTrackVideoSupplier.cpp
blobe83c09e0c2ed568453ca51301a269c2c0ab78fa7
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"
7 #include <new>
8 #include <stdio.h>
9 #include <string.h>
11 #include <MediaTrack.h>
13 #include "ColorSpaceToString.h"
15 using std::nothrow;
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
25 // constructor
26 MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
27 int32 trackIndex, status_t& initStatus)
29 VideoTrackSupplier(),
30 fVideoTrack(track),
32 fPerformanceTime(0),
33 fDuration(0),
34 fCurrentFrame(0),
36 fTrackIndex(trackIndex)
38 if (!fVideoTrack) {
39 printf("MediaTrackVideoSupplier() - no video track\n");
40 return;
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);
52 // }
55 // destructor
56 MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
61 const media_format&
62 MediaTrackVideoSupplier::Format() const
64 return fFormat;
68 status_t
69 MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const
71 if (!fVideoTrack)
72 return B_NO_INIT;
73 return fVideoTrack->EncodedFormat(format);
77 status_t
78 MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
80 if (!fVideoTrack)
81 return B_NO_INIT;
82 return fVideoTrack->GetCodecInfo(info);
86 status_t
87 MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
88 const media_raw_video_format& format, bool& wasCached)
90 if (!fVideoTrack)
91 return B_NO_INIT;
92 if (!buffer)
93 return B_BAD_VALUE;
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;
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);
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));
120 } else
121 fPerformanceTime = mediaHeader.start_time;
123 fCurrentFrame = fVideoTrack->CurrentFrame();
124 if (performanceTime)
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);
136 delete bitmap;
138 #endif // DEBUG_DECODED_FRAME
140 return ret;
144 status_t
145 MediaTrackVideoSupplier::FindKeyFrameForFrame(int64* frame)
147 if (!fVideoTrack)
148 return B_NO_INIT;
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;
158 status_t
159 MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
161 if (!fVideoTrack)
162 return B_NO_INIT;
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;
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);
176 fPerformanceTime = *performanceTime;
177 fCurrentFrame = fVideoTrack->CurrentFrame();
180 return ret;
184 status_t
185 MediaTrackVideoSupplier::SeekToFrame(int64* frame)
187 if (!fVideoTrack)
188 return B_NO_INIT;
190 int64 wantFrame = *frame;
192 if (wantFrame == fCurrentFrame)
193 return B_OK;
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;
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;
220 return B_OK;
223 ret = fVideoTrack->SeekToFrame(frame);
224 if (ret != B_OK)
225 return ret;
227 //if (wantFrame != *frame) {
228 // printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
229 // fCurrentFrame);
232 fCurrentFrame = *frame;
233 fPerformanceTime = fVideoTrack->CurrentTime();
235 return ret;
239 // #pragma mark -
242 BRect
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);
250 color_space
251 MediaTrackVideoSupplier::ColorSpace() const
253 return fFormat.u.raw_video.display.format;
257 uint32
258 MediaTrackVideoSupplier::BytesPerRow() const
260 return fFormat.u.raw_video.display.bytes_per_row;
264 // #pragma mark -
267 status_t
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);
273 if (ret < B_OK) {
274 printf("MediaTrackVideoSupplier::_SwitchFormat() - "
275 "fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
276 return 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
287 format = B_YCbCr422;
288 } else {
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;
298 else
299 minBytesPerRow = width * 4;
300 bytesPerRow = max_c(bytesPerRow, minBytesPerRow);
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);
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;
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");
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);
342 return ret;
346 status_t
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);