xref: /haiku/src/apps/mediaplayer/supplier/MediaTrackVideoSupplier.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
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 
15 using 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
26 MediaTrackVideoSupplier::MediaTrackVideoSupplier(BMediaTrack* track,
27 		int32 trackIndex, status_t& initStatus)
28 	: VideoTrackSupplier()
29 	, fVideoTrack(track)
30 
31 	, fPerformanceTime(0)
32 	, fDuration(0)
33 	, fCurrentFrame(0)
34 
35 	, fTrackIndex(trackIndex)
36 {
37 	if (!fVideoTrack) {
38 		printf("MediaTrackVideoSupplier() - no video track\n");
39 		return;
40 	}
41 
42 	initStatus = _SwitchFormat(B_NO_COLOR_SPACE, 0);
43 
44 	fDuration = fVideoTrack->Duration();
45 
46 //	for (bigtime_t time = 0; time < fDuration; time += 10000) {
47 //		bigtime_t keyFrameTime = time;
48 //		fVideoTrack->FindKeyFrameForTime(&keyFrameTime,
49 //			B_MEDIA_SEEK_CLOSEST_BACKWARD);
50 //		printf("keyframe time for time: %lld -> %lld\n", time, keyFrameTime);
51 //	}
52 }
53 
54 // destructor
55 MediaTrackVideoSupplier::~MediaTrackVideoSupplier()
56 {
57 }
58 
59 
60 const media_format&
61 MediaTrackVideoSupplier::Format() const
62 {
63 	return fFormat;
64 }
65 
66 
67 status_t
68 MediaTrackVideoSupplier::GetEncodedFormat(media_format* format) const
69 {
70 	if (!fVideoTrack)
71 		return B_NO_INIT;
72 	return fVideoTrack->EncodedFormat(format);
73 }
74 
75 
76 status_t
77 MediaTrackVideoSupplier::GetCodecInfo(media_codec_info* info) const
78 {
79 	if (!fVideoTrack)
80 		return B_NO_INIT;
81 	return fVideoTrack->GetCodecInfo(info);
82 }
83 
84 
85 status_t
86 MediaTrackVideoSupplier::ReadFrame(void* buffer, bigtime_t* performanceTime,
87 	const media_format* format, bool& wasCached)
88 {
89 	if (!fVideoTrack)
90 		return B_NO_INIT;
91 	if (!buffer)
92 		return B_BAD_VALUE;
93 
94 	status_t ret = B_OK;
95 	if (format->u.raw_video.display.format
96 			!= fFormat.u.raw_video.display.format
97 		|| fFormat.u.raw_video.display.bytes_per_row
98 			!= format->u.raw_video.display.bytes_per_row) {
99 		ret = _SwitchFormat(format->u.raw_video.display.format,
100 			format->u.raw_video.display.bytes_per_row);
101 		if (ret < B_OK) {
102 			fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
103 				"unable to switch media format: %s\n", strerror(ret));
104 			return ret;
105 		}
106 	}
107 
108 	// read a frame
109 	int64 frameCount = 1;
110 	// TODO: how does this work for interlaced video (field count > 1)?
111 	media_header mediaHeader;
112 	ret = fVideoTrack->ReadFrames(buffer, &frameCount, &mediaHeader);
113 
114 	if (ret < B_OK) {
115 		if (ret != B_LAST_BUFFER_ERROR) {
116 			fprintf(stderr, "MediaTrackVideoSupplier::ReadFrame() - "
117 				"error while reading frame of track: %s\n", strerror(ret));
118 		}
119 	} else {
120 		fPerformanceTime = mediaHeader.start_time;
121 	}
122 
123 	fCurrentFrame = fVideoTrack->CurrentFrame();
124 	if (performanceTime)
125 		*performanceTime = fPerformanceTime;
126 
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;
137 }
138 #endif // DEBUG_DECODED_FRAME
139 
140 	return ret;
141 }
142 
143 
144 status_t
145 MediaTrackVideoSupplier::SeekToTime(bigtime_t* performanceTime)
146 {
147 	if (!fVideoTrack)
148 		return B_NO_INIT;
149 
150 bigtime_t _performanceTime = *performanceTime;
151 	status_t ret = fVideoTrack->FindKeyFrameForTime(performanceTime,
152 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
153 	if (ret < B_OK)
154 		return ret;
155 
156 	ret = fVideoTrack->SeekToTime(performanceTime);
157 	if (ret == B_OK) {
158 if (_performanceTime != *performanceTime)
159 printf("seeked by time: %lld -> %lld\n", _performanceTime, *performanceTime);
160 		fPerformanceTime = *performanceTime;
161 		fCurrentFrame = fVideoTrack->CurrentFrame();
162 	}
163 
164 	return ret;
165 }
166 
167 
168 status_t
169 MediaTrackVideoSupplier::SeekToFrame(int64* frame)
170 {
171 	if (!fVideoTrack)
172 		return B_NO_INIT;
173 
174 	int64 wantFrame = *frame;
175 	int64 currentFrame = fVideoTrack->CurrentFrame();
176 
177 	if (wantFrame == currentFrame)
178 		return B_OK;
179 
180 	status_t ret = fVideoTrack->FindKeyFrameForFrame(frame,
181 		B_MEDIA_SEEK_CLOSEST_BACKWARD);
182 	if (ret < B_OK)
183 		return ret;
184 
185 	if (*frame < currentFrame && wantFrame > currentFrame) {
186 		*frame = currentFrame;
187 		return B_OK;
188 	}
189 
190 if (wantFrame != *frame) {
191 	printf("seeked by frame: %lld -> %lld, was %lld\n", wantFrame, *frame,
192 		currentFrame);
193 }
194 
195 	ret = fVideoTrack->SeekToFrame(frame);
196 	if (ret == B_OK) {
197 		fCurrentFrame = *frame;
198 		fPerformanceTime = fVideoTrack->CurrentTime();
199 	}
200 
201 	return ret;
202 }
203 
204 
205 // #pragma mark -
206 
207 
208 BRect
209 MediaTrackVideoSupplier::Bounds() const
210 {
211 	return BRect(0, 0, 	fFormat.u.raw_video.display.line_width - 1,
212 		fFormat.u.raw_video.display.line_count - 1);
213 }
214 
215 
216 color_space
217 MediaTrackVideoSupplier::ColorSpace() const
218 {
219 	return fFormat.u.raw_video.display.format;
220 }
221 
222 
223 uint32
224 MediaTrackVideoSupplier::BytesPerRow() const
225 {
226 	return fFormat.u.raw_video.display.bytes_per_row;
227 }
228 
229 
230 // #pragma mark -
231 
232 
233 status_t
234 MediaTrackVideoSupplier::_SwitchFormat(color_space format, uint32 bytesPerRow)
235 {
236 	// get the encoded format
237 	memset(&fFormat, 0, sizeof(media_format));
238 	status_t ret = fVideoTrack->EncodedFormat(&fFormat);
239 	if (ret < B_OK) {
240 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
241 			"fVideoTrack->EncodedFormat(): %s\n", strerror(ret));
242 		return ret;
243 	}
244 
245 	// get ouput video frame size
246 	uint32 width = fFormat.u.encoded_video.output.display.line_width;
247 	uint32 height = fFormat.u.encoded_video.output.display.line_count;
248 	if (format == B_NO_COLOR_SPACE) {
249 		format = fFormat.u.encoded_video.output.display.format;
250 		if (format == B_NO_COLOR_SPACE) {
251 			// if still no preferred format, try the most commonly
252 			// supported overlay format
253 			format = B_YCbCr422;
254 		} else {
255 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
256 				"preferred color space: %s\n",
257 				color_space_to_string(format));
258 		}
259 	}
260 
261 	uint32 minBytesPerRow;
262 	if (format == B_YCbCr422)
263 		minBytesPerRow = ((width * 2 + 3) / 4) * 4;
264 	else
265 		minBytesPerRow = width * 4;
266 	bytesPerRow = max_c(bytesPerRow, minBytesPerRow);
267 
268 	ret = _SetDecodedFormat(width, height, format, bytesPerRow);
269 	if (ret < B_OK) {
270 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
271 			"fVideoTrack->DecodedFormat(): %s - retrying with B_RGB32\n",
272 			strerror(ret));
273 		format = B_RGB32;
274 		bytesPerRow = max_c(bytesPerRow, width * 4);
275 
276 		ret = _SetDecodedFormat(width, height, format, bytesPerRow);
277 		if (ret < B_OK) {
278 			printf("MediaTrackVideoSupplier::_SwitchFormat() - "
279 				"fVideoTrack->DecodedFormat(): %s - giving up\n",
280 				strerror(ret));
281 			return ret;
282 		}
283 	}
284 
285 	if (fFormat.u.raw_video.display.format != format) {
286 		printf("MediaTrackVideoSupplier::_SwitchFormat() - "
287 			" codec changed colorspace of decoded format (%s -> %s)!\n",
288 			color_space_to_string(format),
289 			color_space_to_string(fFormat.u.raw_video.display.format));
290 		// check if the codec forgot to adjust bytes_per_row
291 		format = fFormat.u.raw_video.display.format;
292 		if (format == B_YCbCr422)
293 			minBytesPerRow = ((width * 2 + 3) / 4) * 4;
294 		else
295 			minBytesPerRow = width * 4;
296 		if (minBytesPerRow > fFormat.u.raw_video.display.bytes_per_row) {
297 			printf("  -> stupid codec forgot to adjust bytes_per_row!\n");
298 
299 			ret = _SetDecodedFormat(width, height, format, minBytesPerRow);
300 		}
301 	}
302 
303 	if (fFormat.u.raw_video.last_active != height - 1) {
304 		printf("should skip %ld lines at bottom!\n",
305 			(height - 1) - fFormat.u.raw_video.last_active);
306 	}
307 
308 	return ret;
309 }
310 
311 
312 status_t
313 MediaTrackVideoSupplier::_SetDecodedFormat(uint32 width, uint32 height,
314 	color_space format, uint32 bytesPerRow)
315 {
316 	// specifiy the decoded format. we derive this information from
317 	// the encoded format (width & height).
318 	memset(&fFormat, 0, sizeof(media_format));
319 //	fFormat.u.raw_video.last_active = height - 1;
320 //	fFormat.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
321 //	fFormat.u.raw_video.pixel_width_aspect = 1;
322 //	fFormat.u.raw_video.pixel_height_aspect = 1;
323 	fFormat.u.raw_video.display.format = format;
324 	fFormat.u.raw_video.display.line_width = width;
325 	fFormat.u.raw_video.display.line_count = height;
326 	fFormat.u.raw_video.display.bytes_per_row = bytesPerRow;
327 
328 	return fVideoTrack->DecodedFormat(&fFormat);
329 }
330 
331