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) {
172if (_performanceTime != *performanceTime)
173printf("seeked by time: %lld -> %lld\n", _performanceTime, *performanceTime);
174		fPerformanceTime = *performanceTime;
175		fCurrentFrame = fVideoTrack->CurrentFrame();
176	}
177
178	return ret;
179}
180
181
182status_t
183MediaTrackVideoSupplier::SeekToFrame(int64* frame)
184{
185	if (!fVideoTrack)
186		return B_NO_INIT;
187
188	int64 wantFrame = *frame;
189
190	if (wantFrame == fCurrentFrame)
191		return B_OK;
192
193	status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
194		B_MEDIA_SEEK_CLOSEST_BACKWARD);
195	if (ret != B_OK)
196		return ret;
197	if (wantFrame > *frame) {
198		// Work around a rounding problem with some extractors and
199		// converting frames <-> time <-> internal time.
200		int64 nextWantFrame = wantFrame + 1;
201		if (fVideoTrack->FindKeyFrameForFrame(&nextWantFrame,
202			B_MEDIA_SEEK_CLOSEST_BACKWARD) == B_OK) {
203			if (nextWantFrame == wantFrame) {
204				wantFrame++;
205				*frame = wantFrame;
206			}
207		}
208	}
209
210//if (wantFrame != *frame) {
211//	printf("keyframe for frame: %lld -> %lld\n", wantFrame, *frame);
212//}
213
214	if (*frame <= fCurrentFrame && wantFrame >= fCurrentFrame) {
215		// The current frame is already closer to the wanted frame
216		// than the next keyframe before it.
217		*frame = fCurrentFrame;
218		return B_OK;
219	}
220
221	ret = fVideoTrack->SeekToFrame(frame);
222	if (ret != B_OK)
223		return ret;
224
225//if (wantFrame != *frame) {
226//	printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
227//		fCurrentFrame);
228//}
229
230	fCurrentFrame = *frame;
231	fPerformanceTime = fVideoTrack->CurrentTime();
232
233	return ret;
234}
235
236
237// #pragma mark -
238
239
240BRect
241MediaTrackVideoSupplier::Bounds() const
242{
243	return BRect(0, 0, 	fFormat.u.raw_video.display.line_width - 1,
244		fFormat.u.raw_video.display.line_count - 1);
245}
246
247
248color_space
249MediaTrackVideoSupplier::ColorSpace() const
250{
251	return fFormat.u.raw_video.display.format;
252}
253
254
255uint32
256MediaTrackVideoSupplier::BytesPerRow() const
257{
258	return fFormat.u.raw_video.display.bytes_per_row;
259}
260
261
262// #pragma mark -
263
264
265status_t
266MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow)
267{
268	// get the encoded format
269	memset(&fFormat, 0, sizeof(media_format));
270	status_t ret = fVideoTrack->EncodedFormat(&fFormat);
271	if (ret < B_OK) {
272		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
273			"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
274		return ret;
275	}
276
277	// get ouput video frame size
278	uint32 width = fFormat.u.encoded_video.output.display.line_width;
279	uint32 height = fFormat.u.encoded_video.output.display.line_count;
280	if (format == B_NO_COLOR_SPACE) {
281		format = fFormat.u.encoded_video.output.display.format;
282		if (format == B_NO_COLOR_SPACE) {
283			// if still no preferred format, try the most commonly
284			// supported overlay format
285			format = B_YCbCr422;
286		} else {
287			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
288				"preferred color space: %s\n",
289				color_space_to_string(format));
290		}
291	}
292
293	uint32 minBytesPerRow;
294	if (format == B_YCbCr422)
295		minBytesPerRow = ((width * 2 + 3) / 4) * 4;
296	else
297		minBytesPerRow = width * 4;
298	bytesPerRow = max_c(bytesPerRow, minBytesPerRow);
299
300	ret = _SetDecodedFormat(width, height, format, bytesPerRow);
301	if (ret < B_OK) {
302		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
303			"fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n",
304			strerror(ret));
305		format = B_RGB32;
306		bytesPerRow = max_c(bytesPerRow, width * 4);
307
308		ret = _SetDecodedFormat(width, height, format, bytesPerRow);
309		if (ret < B_OK) {
310			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
311				"fVideoTrack->DecodedFormat(): %s - giving up\n",
312				strerror(ret));
313			return ret;
314		}
315	}
316
317	if (fFormat.u.raw_video.display.format != format) {
318		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
319			" codec changed colorspace of decoded format (%s -> %s)!\n",
320			color_space_to_string(format),
321			color_space_to_string(fFormat.u.raw_video.display.format));
322		// check if the codec forgot to adjust bytes_per_row
323		format = fFormat.u.raw_video.display.format;
324		if (format == B_YCbCr422)
325			minBytesPerRow = ((width * 2 + 3) / 4) * 4;
326		else
327			minBytesPerRow = width * 4;
328		if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) {
329			printf("  -> stupid codec forgot to adjust bytes_per_row!\n");
330
331			ret = _SetDecodedFormat(width, height, format, minBytesPerRow);
332		}
333	}
334
335	if (fFormat.u.raw_video.last_active != height - 1) {
336		printf("should skip %ld lines at bottom!\n",
337			(height - 1) - fFormat.u.raw_video.last_active);
338	}
339
340	return ret;
341}
342
343
344status_t
345MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height,
346	color_space format, uint32 bytesPerRow)
347{
348	// specifiy the decoded format. we derive this information from
349	// the encoded format (width & height).
350	memset(&fFormat, 0, sizeof(media_format));
351//	fFormat.u.raw_video.last_active = height - 1;
352//	fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
353//	fFormat.u.raw_video.pixel_width_aspect = 1;
354//	fFormat.u.raw_video.pixel_height_aspect = 1;
355	fFormat.u.raw_video.display.format = format;
356	fFormat.u.raw_video.display.line_width = width;
357	fFormat.u.raw_video.display.line_count = height;
358	fFormat.u.raw_video.display.bytes_per_row = bytesPerRow;
359
360	return fVideoTrack->DecodedFormat(&fFormat);
361}
362
363