1/*
2 * Copyright (c) 2005, David McPaul based on avi_reader copyright (c) 2004 Marcus Overhagen
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 *  * Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
22 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
23 * OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include <stdio.h>
26#include <string.h>
27#include <malloc.h>
28#include <DataIO.h>
29#include <StopWatch.h>
30#include <ByteOrder.h>
31#include <InterfaceDefs.h>
32#include <MediaFormats.h>
33#include "RawFormats.h"
34
35#include "mov_reader.h"
36
37//#define TRACE_MOV_READER
38#ifdef TRACE_MOV_READER
39  #define TRACE printf
40#else
41  #define TRACE(a...)
42#endif
43
44#define ERROR(a...) fprintf(stderr, a)
45
46struct mov_cookie
47{
48	unsigned	stream;
49	char *		buffer;
50	unsigned	buffer_size;
51
52	int64		frame_count;
53	bigtime_t 	duration;
54	media_format format;
55
56	bool		audio;
57
58	// audio only:
59	off_t		byte_pos;
60	uint32		bytes_per_sec_rate;
61	uint32		bytes_per_sec_scale;
62
63	// video only:
64	uint32		line_count;
65
66	// Common
67	uint32		frame_pos;
68	uint32		frames_per_sec_rate;
69	uint32		frames_per_sec_scale;
70};
71
72
73movReader::movReader()
74 :	theFileReader(0)
75{
76	TRACE("movReader::movReader\n");
77}
78
79movReader::~movReader()
80{
81 	delete theFileReader;
82}
83
84const char *
85movReader::Copyright()
86{
87	return "mov_reader & libMOV, " B_UTF8_COPYRIGHT " by David McPaul";
88}
89
90status_t
91movReader::Sniff(int32 *streamCount)
92{
93	TRACE("movReader::Sniff\n");
94
95	BPositionIO *pos_io_source;
96
97	pos_io_source = dynamic_cast<BPositionIO *>(Reader::Source());
98	if (!pos_io_source) {
99		TRACE("movReader::Sniff: not a BPositionIO\n");
100		return B_ERROR;
101	}
102
103	if (!MOVFileReader::IsSupported(pos_io_source)) {
104		TRACE("movReader::Sniff: unsupported file type\n");
105		return B_ERROR;
106	}
107
108	TRACE("movReader::Sniff: this stream seems to be supported\n");
109
110	theFileReader = new MOVFileReader(pos_io_source);
111	if (B_OK != theFileReader->ParseFile()) {
112		ERROR("movReader::Sniff: error parsing file\n");
113		return B_ERROR;
114	}
115
116	*streamCount = theFileReader->getStreamCount();
117	return B_OK;
118}
119
120void
121movReader::GetFileFormatInfo(media_file_format *mff)
122{
123	mff->capabilities =   media_file_format::B_READABLE
124						| media_file_format::B_KNOWS_ENCODED_VIDEO
125						| media_file_format::B_KNOWS_ENCODED_AUDIO
126						| media_file_format::B_IMPERFECTLY_SEEKABLE;
127	mff->family = B_QUICKTIME_FORMAT_FAMILY;
128	mff->version = 100;
129	strcpy(mff->mime_type, "video/quicktime");
130	strcpy(mff->file_extension, "mov");
131	strcpy(mff->short_name,  "MOV");
132	strcpy(mff->pretty_name, "Quicktime (MOV) file format");
133}
134
135status_t
136movReader::AllocateCookie(int32 streamNumber, void **_cookie)
137{
138	uint32 codecID;
139
140	mov_cookie *cookie = new mov_cookie;
141	*_cookie = cookie;
142
143	cookie->stream = streamNumber;
144	cookie->buffer = 0;
145	cookie->buffer_size = 0;
146	cookie->frame_pos = 0;
147
148	BMediaFormats formats;
149	media_format *format = &cookie->format;
150	media_format_description description;
151
152	if (theFileReader->IsActive(cookie->stream) == false) {
153		ERROR("movReader::AllocateCookie: stream %d is not active\n", cookie->stream);
154		delete cookie;
155		return B_ERROR;
156	}
157
158	const mov_stream_header *stream_header;
159	stream_header = theFileReader->StreamFormat(cookie->stream);
160	if (!stream_header) {
161		ERROR("movReader::AllocateCookie: stream %d has no header\n", cookie->stream);
162		delete cookie;
163		return B_ERROR;
164	}
165
166	TRACE("movReader::AllocateCookie: stream %ld (%s)\n", streamNumber, theFileReader->IsAudio(cookie->stream) ? "audio" : theFileReader->IsVideo(cookie->stream)  ? "video" : "unknown");
167
168	if (theFileReader->IsAudio(cookie->stream)) {
169		const AudioMetaData *audio_format = theFileReader->AudioFormat(cookie->stream);
170		if (!audio_format) {
171			ERROR("movReader::AllocateCookie: audio stream %d has no format\n", cookie->stream);
172			delete cookie;
173			return B_ERROR;
174		}
175
176		codecID = B_BENDIAN_TO_HOST_INT32(audio_format->compression);
177
178		cookie->frame_count = theFileReader->getAudioFrameCount(cookie->stream);
179		cookie->duration = theFileReader->getAudioDuration(cookie->stream);
180
181		cookie->audio = true;
182		cookie->byte_pos = 0;
183
184		if (stream_header->scale && stream_header->rate) {
185			cookie->bytes_per_sec_rate = stream_header->rate * audio_format->SampleSize * audio_format->NoOfChannels / 8;
186			cookie->bytes_per_sec_scale = stream_header->scale;
187			cookie->frames_per_sec_rate = stream_header->rate;
188			cookie->frames_per_sec_scale = stream_header->scale;
189			TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using both)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
190		} else if (stream_header->rate) {
191			cookie->bytes_per_sec_rate = stream_header->rate * audio_format->SampleSize * audio_format->NoOfChannels / 8;
192			cookie->bytes_per_sec_scale = 1;
193			cookie->frames_per_sec_rate = stream_header->rate;
194			cookie->frames_per_sec_scale = 1;
195			TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using rate)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
196		} else if (audio_format->BufferSize) {
197			cookie->bytes_per_sec_rate = audio_format->BufferSize;
198			cookie->bytes_per_sec_scale = 1;
199			cookie->frames_per_sec_rate = audio_format->BufferSize * 8 / audio_format->SampleSize / audio_format->NoOfChannels;
200			cookie->frames_per_sec_scale = 1;
201			TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using PacketSize)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
202		} else {
203			cookie->bytes_per_sec_rate = 128000;
204			cookie->bytes_per_sec_scale = 8;
205			cookie->frames_per_sec_rate = 16000;
206			cookie->frames_per_sec_scale = 1;
207			TRACE("bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using fallback)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale);
208		}
209
210		if ((audio_format->compression == AUDIO_NONE) ||
211			(audio_format->compression == AUDIO_RAW) ||
212			(audio_format->compression == AUDIO_TWOS1) ||
213			(audio_format->compression == AUDIO_TWOS2)) {
214			description.family = B_BEOS_FORMAT_FAMILY;
215			description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
216			if (B_OK != formats.GetFormatFor(description, format)) {
217				format->type = B_MEDIA_RAW_AUDIO;
218			}
219
220			format->u.raw_audio.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
221			format->u.raw_audio.channel_count = audio_format->NoOfChannels;
222
223			format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
224
225			if (audio_format->SampleSize <= 8)
226				format->u.raw_audio.format = B_AUDIO_FORMAT_UINT8;
227			else if (audio_format->SampleSize <= 16)
228				format->u.raw_audio.format = B_AUDIO_FORMAT_INT16;
229			else if (audio_format->SampleSize <= 24)
230				format->u.raw_audio.format = B_AUDIO_FORMAT_INT24;
231			else if (audio_format->SampleSize <= 32)
232				format->u.raw_audio.format = B_AUDIO_FORMAT_INT32;
233			else {
234				ERROR("movReader::AllocateCookie: unhandled bits per sample %d\n", audio_format->SampleSize);
235				return B_ERROR;
236			}
237
238			if (audio_format->compression == AUDIO_TWOS1) {
239				if (audio_format->SampleSize <= 8) {
240					format->u.raw_audio.format = B_AUDIO_FORMAT_INT8;
241				} else if (audio_format->SampleSize <= 16) {
242					format->u.raw_audio.format = B_AUDIO_FORMAT_INT16;
243					format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
244				}
245			}
246			if (audio_format->compression == AUDIO_TWOS2) {
247				if (audio_format->SampleSize <= 8) {
248					format->u.raw_audio.format = B_AUDIO_FORMAT_INT8;
249				} else if (audio_format->SampleSize <= 16) {
250					format->u.raw_audio.format = B_AUDIO_FORMAT_INT16;
251					format->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
252				}
253			}
254
255			format->u.raw_audio.buffer_size = stream_header->suggested_buffer_size;
256		} else {
257
258			description.family = B_QUICKTIME_FORMAT_FAMILY;
259			description.u.quicktime.codec = audio_format->compression;
260			if (B_OK != formats.GetFormatFor(description, format)) {
261				format->type = B_MEDIA_ENCODED_AUDIO;
262			}
263
264			format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
265
266			if (audio_format->SampleSize <= 8)
267				format->u.raw_audio.format = B_AUDIO_FORMAT_UINT8;
268			else if (audio_format->SampleSize <= 16)
269				format->u.raw_audio.format = B_AUDIO_FORMAT_INT16;
270			else if (audio_format->SampleSize <= 24)
271				format->u.raw_audio.format = B_AUDIO_FORMAT_INT24;
272			else if (audio_format->SampleSize <= 32)
273				format->u.raw_audio.format = B_AUDIO_FORMAT_INT32;
274			else {
275				ERROR("movReader::AllocateCookie: unhandled bits per sample %d\n", audio_format->SampleSize);
276				return B_ERROR;
277			}
278
279			format->u.encoded_audio.frame_size = audio_format->FrameSize;
280			format->u.encoded_audio.output.buffer_size = audio_format->BufferSize;
281
282			TRACE("compression ");
283
284			switch (audio_format->compression) {
285				case AUDIO_MS_PCM02:
286					TRACE("MS PCM02\n");
287					format->u.raw_audio.format |= B_AUDIO_FORMAT_CHANNEL_ORDER_WAVE;
288					format->u.encoded_audio.bit_rate = 8 * cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
289					format->u.encoded_audio.output.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
290					format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
291					break;
292				case AUDIO_INTEL_PCM17:
293					TRACE("INTEL PCM\n");
294					format->u.encoded_audio.bit_rate = 8 * cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
295					format->u.encoded_audio.output.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
296					format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
297					break;
298				case AUDIO_MPEG3_CBR:
299					TRACE("MP3\n");
300					format->u.encoded_audio.bit_rate = 8 * cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
301					format->u.encoded_audio.output.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
302					format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
303					break;
304				case AUDIO_IMA4:
305					TRACE("IMA4\n");
306					format->u.encoded_audio.bit_rate = audio_format->BitRate;
307					format->u.encoded_audio.output.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;;
308					format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
309					break;
310				default:
311					TRACE("OTHER %s\n",(char *)(&codecID));
312					format->u.encoded_audio.bit_rate = 8 * cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
313					format->u.encoded_audio.output.frame_rate = cookie->frames_per_sec_rate / cookie->frames_per_sec_scale;
314					format->u.encoded_audio.output.channel_count = audio_format->NoOfChannels;
315					break;
316			}
317		}
318
319		TRACE("Audio NoOfChannels %d, SampleSize %d, SampleRate %f, FrameSize %ld\n",audio_format->NoOfChannels, audio_format->SampleSize, audio_format->SampleRate, audio_format->FrameSize);
320
321		TRACE("Audio frame_rate %f, channel_count %ld, format %ld, buffer_size %ld, frame_size %ld, bit_rate %f\n",
322				format->u.encoded_audio.output.frame_rate, format->u.encoded_audio.output.channel_count, format->u.encoded_audio.output.format,format->u.encoded_audio.output.buffer_size, format->u.encoded_audio.frame_size, format->u.encoded_audio.bit_rate);
323
324		// Some codecs have additional setup data that they need, put it in metadata
325		size_t size = audio_format->VOLSize;
326		const void *data = audio_format->theVOL;
327		if (size > 0) {
328			TRACE("VOL SIZE %ld\n", size);
329			format->SetMetaData(data, size);
330		}
331
332		if (codecID != 0) {
333			// Put the codeid in the user data in case someone wants it
334			format->user_data_type = B_CODEC_TYPE_INFO;
335			*(uint32 *)format->user_data = codecID; format->user_data[4] = 0;
336		}
337
338		return B_OK;
339	}
340
341	if (theFileReader->IsVideo(cookie->stream)) {
342		const VideoMetaData *video_format = theFileReader->VideoFormat(cookie->stream);
343		if (!video_format) {
344			ERROR("movReader::AllocateCookie: video stream %d has no format\n", cookie->stream);
345			delete cookie;
346			return B_ERROR;
347		}
348
349		codecID = B_BENDIAN_TO_HOST_INT32(video_format->compression);
350
351		cookie->audio = false;
352		cookie->line_count = theFileReader->MovMainHeader()->height;
353
354		if (stream_header->scale && stream_header->rate) {
355			cookie->frames_per_sec_rate = stream_header->rate;
356			cookie->frames_per_sec_scale = stream_header->scale;
357			TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using both)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
358		} else if (theFileReader->MovMainHeader()->micro_sec_per_frame) {
359			cookie->frames_per_sec_rate = 1000000;
360			cookie->frames_per_sec_scale = theFileReader->MovMainHeader()->micro_sec_per_frame;
361			TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using micro_sec_per_frame)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
362		} else {
363			cookie->frames_per_sec_rate = 25;
364			cookie->frames_per_sec_scale = 1;
365			TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using fallback)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale);
366		}
367
368		cookie->frame_count = stream_header->length;
369		cookie->duration = (cookie->frame_count * (int64)cookie->frames_per_sec_scale * 1000000LL) / cookie->frames_per_sec_rate;
370
371		TRACE("frame_count %Ld\n", cookie->frame_count);
372		TRACE("duration %.6f (%Ld)\n", cookie->duration / 1E6, cookie->duration);
373		TRACE("compression %s\n", (char *)(&codecID));
374
375		description.family = B_QUICKTIME_FORMAT_FAMILY;
376		if (stream_header->fourcc_handler == 'ekaf' || stream_header->fourcc_handler == 0) // 'fake' or 0 fourcc => used compression id
377			description.u.quicktime.codec = video_format->compression;
378		else
379			description.u.quicktime.codec = video_format->compression;
380		if (B_OK != formats.GetFormatFor(description, format))
381			format->type = B_MEDIA_ENCODED_VIDEO;
382
383		format->user_data_type = B_CODEC_TYPE_INFO;
384		*(uint32 *)format->user_data = description.u.quicktime.codec; format->user_data[4] = 0;
385
386		format->u.encoded_video.max_bit_rate = 8 * theFileReader->MovMainHeader()->max_bytes_per_sec;
387		format->u.encoded_video.avg_bit_rate = format->u.encoded_video.max_bit_rate / 2; // XXX fix this
388		format->u.encoded_video.output.field_rate = cookie->frames_per_sec_rate / (float)cookie->frames_per_sec_scale;
389		format->u.encoded_video.output.interlace = 1; // 1: progressive
390		format->u.encoded_video.output.first_active = 0;
391		format->u.encoded_video.output.last_active = cookie->line_count - 1;
392		format->u.encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT;
393		format->u.encoded_video.output.pixel_width_aspect = 1;
394		format->u.encoded_video.output.pixel_height_aspect = 1;
395		format->u.encoded_video.output.display.line_width = theFileReader->MovMainHeader()->width;
396		format->u.encoded_video.output.display.line_count = cookie->line_count;
397		format->u.encoded_video.output.display.bytes_per_row = 0; // format->u.encoded_video.output.display.line_width * 4;
398		format->u.encoded_video.output.display.pixel_offset = 0;
399		format->u.encoded_video.output.display.line_offset = 0;
400		format->u.encoded_video.output.display.flags = 0;
401
402		TRACE("max_bit_rate %.3f\n", format->u.encoded_video.max_bit_rate);
403		TRACE("field_rate   %.3f\n", format->u.encoded_video.output.field_rate);
404
405		// Some decoders need additional metadata passed via a special Atom
406		size_t size = video_format->VOLSize;
407		const void *data = video_format->theVOL;
408		if (size > 0) {
409			TRACE("VOL SIZE  %ld\n", size);
410			format->SetMetaData(data, size);
411		}
412
413		if (codecID != 0) {
414			// Put the codeid in the user data in case someone wants it
415			format->user_data_type = B_CODEC_TYPE_INFO;
416			*(uint32 *)format->user_data = codecID; format->user_data[4] = 0;
417		}
418
419		return B_OK;
420	}
421
422	delete cookie;
423	return B_ERROR;
424}
425
426
427status_t
428movReader::FreeCookie(void *_cookie)
429{
430	mov_cookie *cookie = (mov_cookie *)_cookie;
431
432	delete [] cookie->buffer;
433
434	delete cookie;
435	return B_OK;
436}
437
438
439status_t
440movReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration,
441						 media_format *format, const void **infoBuffer, size_t *infoSize)
442{
443	mov_cookie *cookie = (mov_cookie *)_cookie;
444
445	if (cookie) {
446		*frameCount = cookie->frame_count;
447		*duration = cookie->duration;
448		*format = cookie->format;
449
450		// Copy metadata to infoBuffer
451		if (theFileReader->IsVideo(cookie->stream)) {
452			const VideoMetaData *video_format = theFileReader->VideoFormat(cookie->stream);
453			*infoBuffer = video_format->theVOL;
454			*infoSize = video_format->VOLSize;
455		} else {
456			const AudioMetaData *audio_format = theFileReader->AudioFormat(cookie->stream);
457			*infoBuffer = audio_format->theVOL;
458			*infoSize = audio_format->VOLSize;
459		}
460	}
461
462	return B_OK;
463}
464
465
466status_t
467movReader::Seek(void *cookie,
468				uint32 seekTo,
469				int64 *frame, bigtime_t *time)
470{
471	// Seek to the requested frame
472
473	mov_cookie *movcookie = (mov_cookie *)cookie;
474
475	if (seekTo & B_MEDIA_SEEK_TO_TIME) {
476		// frame = (time * rate) / fps / 1000000LL
477		*frame = ((*time * movcookie->frames_per_sec_rate) / (int64)movcookie->frames_per_sec_scale) / 1000000LL;
478		movcookie->frame_pos = *frame;
479		TRACE("Time %Ld to Frame %Ld\n",*time, *frame);
480	}
481
482	if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
483		// time = frame * 1000000LL * fps / rate
484		*time = (*frame * 1000000LL * (int64)movcookie->frames_per_sec_scale) / movcookie->frames_per_sec_rate;
485		movcookie->frame_pos = *frame;
486		TRACE("Frame %Ld to Time %Ld\n", *frame, *time);
487	}
488
489	TRACE("movReader::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n",
490		(seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
491		(seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
492		(seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
493		(seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
494		*time, *frame);
495
496	return B_OK;
497}
498
499status_t
500movReader::FindKeyFrame(void* cookie, uint32 flags,
501							int64* frame, bigtime_t* time)
502{
503	// Find the nearest keyframe to the given time or frame.
504
505	mov_cookie *movcookie = (mov_cookie *)cookie;
506
507	bool keyframe = false;
508
509	if (flags & B_MEDIA_SEEK_TO_TIME) {
510		// convert time to frame as we seek by frame
511		// frame = (time * rate) / fps / 1000000LL
512		*frame = ((*time * movcookie->frames_per_sec_rate) / (int64)movcookie->frames_per_sec_scale) / 1000000LL;
513	}
514
515	TRACE("movReader::FindKeyFrame: seekTo%s%s%s%s, time %Ld, frame %Ld\n",
516		(flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
517		(flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
518		(flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
519		(flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
520		*time, *frame);
521
522	if (movcookie->audio) {
523		// Audio does not have keyframes?  Or all audio frames are keyframes?
524		return B_OK;
525	} else {
526		while (*frame > 0 && *frame <= movcookie->frame_count) {
527			keyframe = theFileReader->IsKeyFrame(movcookie->stream, *frame);
528
529			if (keyframe)
530				break;
531
532			if (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) {
533				(*frame)--;
534			} else {
535				(*frame)++;
536			}
537		}
538
539		// We consider frame 0 to be a keyframe.
540		if (!keyframe && *frame > 0) {
541			TRACE("Did NOT find keyframe at frame %Ld\n",*frame);
542			return B_LAST_BUFFER_ERROR;
543		}
544	}
545
546	// convert frame found to time
547	// time = frame * 1000000LL * fps / rate
548	*time = (*frame * 1000000LL * (int64)movcookie->frames_per_sec_scale) / movcookie->frames_per_sec_rate;
549
550	TRACE("Found keyframe at frame %Ld time %Ld\n",*frame,*time);
551
552	return B_OK;
553}
554
555status_t
556movReader::GetNextChunk(void *_cookie,
557						const void **chunkBuffer, size_t *chunkSize,
558						media_header *mediaHeader)
559{
560	mov_cookie *cookie = (mov_cookie *)_cookie;
561
562	int64 start; uint32 size; bool keyframe; uint32 chunkFrameCount;
563
564	if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->frame_pos, &start, &size, &keyframe, &chunkFrameCount))
565		return B_LAST_BUFFER_ERROR;
566
567	if (cookie->buffer_size < size) {
568		delete [] cookie->buffer;
569		cookie->buffer_size = (size + 15) & ~15;
570		cookie->buffer = new char [cookie->buffer_size];
571	}
572
573	if (cookie->audio) {
574		TRACE("Audio stream %d: chunk %ld expected start %lld Size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe);
575		mediaHeader->type = B_MEDIA_ENCODED_AUDIO;
576		mediaHeader->u.encoded_audio.buffer_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
577
578		// This will only work with raw audio I think.
579		mediaHeader->start_time = (cookie->byte_pos * 1000000LL * cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate;
580//		TRACE("Audio - Frames in Chunk %ld / Actual Start Time %Ld using byte_pos\n",theFileReader->getNoFramesInChunk(cookie->stream,cookie->frame_pos),mediaHeader->start_time);
581
582		// We should find the current frame position (ie first frame in chunk) then compute using fps
583//		cookie->frame_pos = theFileReader->getFirstFrameInChunk(cookie->stream,cookie->frame_pos);
584		mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
585//		TRACE("Audio - Frames in Chunk %ld / Actual Start Time %Ld using frame_no %ld\n",theFileReader->getNoFramesInChunk(cookie->stream,cookie->frame_pos),mediaHeader->start_time, cookie->frame_pos);
586
587		cookie->byte_pos += size;
588	} else {
589		TRACE("Video stream %d: frame %ld start %lld Size %ld key %d\n",cookie->stream, cookie->frame_pos, start, size, keyframe);
590		mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
591		mediaHeader->type = B_MEDIA_ENCODED_VIDEO;
592		mediaHeader->u.encoded_video.field_flags = keyframe ? B_MEDIA_KEY_FRAME : 0;
593		mediaHeader->u.encoded_video.first_active_line = 0;
594		mediaHeader->u.encoded_video.line_count = cookie->line_count;
595	}
596
597	cookie->frame_pos += chunkFrameCount;
598	TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0);
599
600	*chunkBuffer = cookie->buffer;
601	*chunkSize = size;
602	return (int)size == theFileReader->Source()->ReadAt(start, cookie->buffer, size) ? B_OK : B_LAST_BUFFER_ERROR;
603}
604
605
606Reader *
607movReaderPlugin::NewReader()
608{
609	return new movReader;
610}
611
612
613MediaPlugin *instantiate_plugin()
614{
615	return new movReaderPlugin;
616}
617