vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / plugins / mov_reader / mov_reader.cpp
blob28f6d99e35733dd061bb7c3f8969c96b3f203b97
1 /*
2 * Copyright (c) 2005, David McPaul based on avi_reader copyright (c) 2004 Marcus Overhagen
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
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.
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.
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"
35 #include "mov_reader.h"
37 //#define TRACE_MOV_READER
38 #ifdef TRACE_MOV_READER
39 #define TRACE printf
40 #else
41 #define TRACE(a...)
42 #endif
44 #define ERROR(a...) fprintf(stderr, a)
46 struct mov_cookie
48 unsigned stream;
49 char * buffer;
50 unsigned buffer_size;
52 int64 frame_count;
53 bigtime_t duration;
54 media_format format;
56 bool audio;
58 // audio only:
59 off_t byte_pos;
60 uint32 bytes_per_sec_rate;
61 uint32 bytes_per_sec_scale;
63 // video only:
64 uint32 line_count;
66 // Common
67 uint32 frame_pos;
68 uint32 frames_per_sec_rate;
69 uint32 frames_per_sec_scale;
73 movReader::movReader()
74 : theFileReader(0)
76 TRACE("movReader::movReader\n");
79 movReader::~movReader()
81 delete theFileReader;
84 const char *
85 movReader::Copyright()
87 return "mov_reader & libMOV, " B_UTF8_COPYRIGHT " by David McPaul";
90 status_t
91 movReader::Sniff(int32 *streamCount)
93 TRACE("movReader::Sniff\n");
95 BPositionIO *pos_io_source;
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;
103 if (!MOVFileReader::IsSupported(pos_io_source)) {
104 TRACE("movReader::Sniff: unsupported file type\n");
105 return B_ERROR;
108 TRACE("movReader::Sniff: this stream seems to be supported\n");
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;
116 *streamCount = theFileReader->getStreamCount();
117 return B_OK;
120 void
121 movReader::GetFileFormatInfo(media_file_format *mff)
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");
135 status_t
136 movReader::AllocateCookie(int32 streamNumber, void **_cookie)
138 uint32 codecID;
140 mov_cookie *cookie = new mov_cookie;
141 *_cookie = cookie;
143 cookie->stream = streamNumber;
144 cookie->buffer = 0;
145 cookie->buffer_size = 0;
146 cookie->frame_pos = 0;
148 BMediaFormats formats;
149 media_format *format = &cookie->format;
150 media_format_description description;
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;
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;
166 TRACE("movReader::AllocateCookie: stream %ld (%s)\n", streamNumber, theFileReader->IsAudio(cookie->stream) ? "audio" : theFileReader->IsVideo(cookie->stream) ? "video" : "unknown");
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;
176 codecID = B_BENDIAN_TO_HOST_INT32(audio_format->compression);
178 cookie->frame_count = theFileReader->getAudioFrameCount(cookie->stream);
179 cookie->duration = theFileReader->getAudioDuration(cookie->stream);
181 cookie->audio = true;
182 cookie->byte_pos = 0;
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);
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;
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;
223 format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
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;
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;
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;
255 format->u.raw_audio.buffer_size = stream_header->suggested_buffer_size;
256 } else {
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;
264 format->u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
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;
279 format->u.encoded_audio.frame_size = audio_format->FrameSize;
280 format->u.encoded_audio.output.buffer_size = audio_format->BufferSize;
282 TRACE("compression ");
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;
319 TRACE("Audio NoOfChannels %d, SampleSize %d, SampleRate %f, FrameSize %ld\n",audio_format->NoOfChannels, audio_format->SampleSize, audio_format->SampleRate, audio_format->FrameSize);
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);
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);
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;
338 return B_OK;
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;
349 codecID = B_BENDIAN_TO_HOST_INT32(video_format->compression);
351 cookie->audio = false;
352 cookie->line_count = theFileReader->MovMainHeader()->height;
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);
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;
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));
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;
383 format->user_data_type = B_CODEC_TYPE_INFO;
384 *(uint32 *)format->user_data = description.u.quicktime.codec; format->user_data[4] = 0;
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;
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);
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);
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;
419 return B_OK;
422 delete cookie;
423 return B_ERROR;
427 status_t
428 movReader::FreeCookie(void *_cookie)
430 mov_cookie *cookie = (mov_cookie *)_cookie;
432 delete [] cookie->buffer;
434 delete cookie;
435 return B_OK;
439 status_t
440 movReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration,
441 media_format *format, const void **infoBuffer, size_t *infoSize)
443 mov_cookie *cookie = (mov_cookie *)_cookie;
445 if (cookie) {
446 *frameCount = cookie->frame_count;
447 *duration = cookie->duration;
448 *format = cookie->format;
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;
462 return B_OK;
466 status_t
467 movReader::Seek(void *cookie,
468 uint32 seekTo,
469 int64 *frame, bigtime_t *time)
471 // Seek to the requested frame
473 mov_cookie *movcookie = (mov_cookie *)cookie;
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);
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);
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);
496 return B_OK;
499 status_t
500 movReader::FindKeyFrame(void* cookie, uint32 flags,
501 int64* frame, bigtime_t* time)
503 // Find the nearest keyframe to the given time or frame.
505 mov_cookie *movcookie = (mov_cookie *)cookie;
507 bool keyframe = false;
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;
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);
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);
529 if (keyframe)
530 break;
532 if (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) {
533 (*frame)--;
534 } else {
535 (*frame)++;
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;
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;
550 TRACE("Found keyframe at frame %Ld time %Ld\n",*frame,*time);
552 return B_OK;
555 status_t
556 movReader::GetNextChunk(void *_cookie,
557 const void **chunkBuffer, size_t *chunkSize,
558 media_header *mediaHeader)
560 mov_cookie *cookie = (mov_cookie *)_cookie;
562 int64 start; uint32 size; bool keyframe; uint32 chunkFrameCount;
564 if (!theFileReader->GetNextChunkInfo(cookie->stream, cookie->frame_pos, &start, &size, &keyframe, &chunkFrameCount))
565 return B_LAST_BUFFER_ERROR;
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];
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;
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);
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);
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;
597 cookie->frame_pos += chunkFrameCount;
598 TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0);
600 *chunkBuffer = cookie->buffer;
601 *chunkSize = size;
602 return (int)size == theFileReader->Source()->ReadAt(start, cookie->buffer, size) ? B_OK : B_LAST_BUFFER_ERROR;
606 Reader *
607 movReaderPlugin::NewReader()
609 return new movReader;
613 MediaPlugin *instantiate_plugin()
615 return new movReaderPlugin;