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	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
128if (modifiers() & B_SHIFT_KEY) {
129BFile fileStream("/boot/home/Desktop/decoded.png", B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
130BTranslatorRoster* roster = BTranslatorRoster::Default();
131BBitmap* bitmap = new BBitmap(Bounds(), 0, ColorSpace(), BytesPerRow());
132memcpy(bitmap->Bits(), buffer, bitmap->BitsLength());
133BBitmapStream bitmapStream(bitmap);
134roster->Translate(&bitmapStream, NULL, NULL, &fileStream, B_PNG_FORMAT, 0);
135bitmapStream.DetachBitmap(&bitmap);
136delete bitmap;
137}
138#endif // DEBUG_DECODED_FRAME
139
140	return ret;
141}
142
143
144status_t
145MediaTrackVideoSupplier::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
158status_t
159MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
160{
161	if (!fVideoTrack)
162		return B_NO_INIT;
163
164bigtime_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: %" B_PRIdBIGTIME " -> %" B_PRIdBIGTIME
174				"\n", _performanceTime, *performanceTime);
175		}
176		fPerformanceTime = *performanceTime;
177		fCurrentFrame = fVideoTrack->CurrentFrame();
178	}
179
180	return ret;
181}
182
183
184status_t
185MediaTrackVideoSupplier::SeekToFrame(int64* frame)
186{
187	if (!fVideoTrack)
188		return B_NO_INIT;
189
190	int64 wantFrame = *frame;
191
192	if (wantFrame == fCurrentFrame)
193		return B_OK;
194
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;
208			}
209		}
210	}
211
212//if (wantFrame != *frame) {
213//	printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame);
214//}
215
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;
221	}
222
223	ret = fVideoTrack->SeekToFrame(frame);
224	if (ret != B_OK)
225		return ret;
226
227//if (wantFrame != *frame) {
228//	printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
229//		fCurrentFrame);
230//}
231
232	fCurrentFrame = *frame;
233	fPerformanceTime = fVideoTrack->CurrentTime();
234
235	return ret;
236}
237
238
239// #pragma mark -
240
241
242BRect
243MediaTrackVideoSupplier::Bounds() const
244{
245	return BRect(0, 0, 	fFormat.u.raw_video.display.line_width - 1,
246		fFormat.u.raw_video.display.line_count - 1);
247}
248
249
250color_space
251MediaTrackVideoSupplier::ColorSpace() const
252{
253	return fFormat.u.raw_video.display.format;
254}
255
256
257uint32
258MediaTrackVideoSupplier::BytesPerRow() const
259{
260	return fFormat.u.raw_video.display.bytes_per_row;
261}
262
263
264// #pragma mark -
265
266
267status_t
268MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow)
269{
270	// get the encoded format
271	fFormat.Clear();
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;
277	}
278
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));
292		}
293	}
294
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);
301
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);
309
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;
316		}
317	}
318
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");
332
333			ret = _SetDecodedFormat(width, height, format, minBytesPerRow);
334		}
335	}
336
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);
340	}
341
342	return ret;
343}
344
345
346status_t
347MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height,
348	color_space format, uint32 bytesPerRow)
349{
350	// specifiy the decoded format. we derive this information from
351	// the encoded format (width & height).
352	fFormat.Clear();
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;
361
362	return fVideoTrack->DecodedFormat(&fFormat);
363}
364
365