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
15using 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
26MediaTrackVideoSupplier::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
56MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
57{
58}
59
60
61const media_format&
62MediaTrackVideoSupplier::Format() const
63{
64	return fFormat;
65}
66
67
68status_t
69MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const
70{
71	if (!fVideoTrack)
72		return B_NO_INIT;
73	return fVideoTrack->EncodedFormat(format);
74}
75
76
77status_t
78MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
79{
80	if (!fVideoTrack)
81		return B_NO_INIT;
82	return fVideoTrack->GetCodecInfo(info);
83}
84
85
86status_t
87MediaTrackVideoSupplier::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
139if (modifiers() & B_SHIFT_KEY) {
140BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
141BTranslatorRoster* roster = BTranslatorRoster::Default();
142BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow());
143memcpy(bitmap->Bits(), buffer, bitmap->BitsLength());
144BBitmapStream bitmapStream(bitmap);
145roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0);
146bitmapStream.DetachBitmap(&bitmap);
147delete 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
157status_t
158MediaTrackVideoSupplier::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
174status_t
175MediaTrackVideoSupplier::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
203status_t
204MediaTrackVideoSupplier::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
264BRect
265MediaTrackVideoSupplier::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
272color_space
273MediaTrackVideoSupplier::ColorSpace() const
274{
275	return fFormat.u.raw_video.display.format;
276}
277
278
279uint32
280MediaTrackVideoSupplier::BytesPerRow() const
281{
282	return fFormat.u.raw_video.display.bytes_per_row;
283}
284
285
286// #pragma mark -
287
288
289status_t
290MediaTrackVideoSupplier::_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
368status_t
369MediaTrackVideoSupplier::_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