vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / plugins / avi_reader / avi_reader.cpp
blob4d4b04268201675ae1541a00b3f04c74f2f8cf51
1 /*
2 * Copyright (c) 2004-2007, 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 <ByteOrder.h>
30 #include <InterfaceDefs.h>
31 #include <MediaFormats.h>
32 #include <new>
33 #include "RawFormats.h"
34 #include "avi_reader.h"
36 //#define TRACE_AVI_READER
37 #ifdef TRACE_AVI_READER
38 #define TRACE printf
39 #else
40 #define TRACE(a...)
41 #endif
43 #define ERROR(a...) fprintf(stderr, a)
45 // http://web.archive.org/web/20030618161228/http://www.microsoft.com/Developer/PRODINFO/directx/dxm/help/ds/FiltDev/DV_Data_AVI_File_Format.htm
46 // http://mediaxw.sourceforge.net/files/doc/Video%20for%20Windows%20Reference%20-%20Chapter%204%20-%20AVI%20Files.pdf
49 struct avi_cookie
51 unsigned stream;
52 char * buffer;
53 unsigned buffer_size;
55 bool is_audio;
56 bool is_video;
58 media_format format;
60 bigtime_t duration;
61 int64 frame_count;
62 int64 frame_pos;
63 uint32 frames_per_sec_rate;
64 uint32 frames_per_sec_scale;
66 // video only:
67 uint32 line_count;
69 // audio only:
70 uint32 sample_size;
71 uint32 frame_size;
72 int64 byte_pos;
73 uint16 bytes_per_second;
74 bool is_vbr;
78 aviReader::aviReader()
79 : fFile(NULL)
81 TRACE("aviReader::aviReader\n");
85 aviReader::~aviReader()
87 delete fFile;
91 const char *
92 aviReader::Copyright()
94 return "AVI & OpenDML reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen";
98 status_t
99 aviReader::Sniff(int32 *streamCount)
101 TRACE("aviReader::Sniff\n");
103 BPositionIO *pos_io_source;
105 pos_io_source = dynamic_cast<BPositionIO *>(Reader::Source());
106 if (!pos_io_source) {
107 TRACE("aviReader::Sniff: not a BPositionIO\n");
108 return B_ERROR;
111 if (!OpenDMLFile::IsSupported(pos_io_source)) {
112 TRACE("aviReader::Sniff: unsupported file type\n");
113 return B_ERROR;
116 TRACE("aviReader::Sniff: this stream seems to be supported\n");
118 fFile = new(std::nothrow) OpenDMLFile(pos_io_source);
119 if (!fFile) {
120 ERROR("aviReader::Sniff: out of memory\n");
121 return B_NO_MEMORY;
123 if (fFile->Init() < B_OK) {
124 ERROR("aviReader::Sniff: can't setup OpenDMLFile\n");
125 return B_ERROR;
128 *streamCount = fFile->StreamCount();
129 return B_OK;
132 void
133 aviReader::GetFileFormatInfo(media_file_format *mff)
135 mff->capabilities = media_file_format::B_READABLE
136 | media_file_format::B_KNOWS_ENCODED_VIDEO
137 | media_file_format::B_KNOWS_ENCODED_AUDIO
138 | media_file_format::B_IMPERFECTLY_SEEKABLE;
139 mff->family = B_MISC_FORMAT_FAMILY;
140 mff->version = 100;
141 strcpy(mff->mime_type, "audio/x-avi");
142 strcpy(mff->file_extension, "avi");
143 strcpy(mff->short_name, "AVI");
144 strcpy(mff->pretty_name, "Audio/Video Interleaved (AVI) file format");
147 status_t
148 aviReader::AllocateCookie(int32 streamNumber, void **_cookie)
150 avi_cookie *cookie = new(std::nothrow) avi_cookie;
151 if (!cookie)
152 return B_NO_MEMORY;
153 *_cookie = cookie;
155 cookie->stream = streamNumber;
156 cookie->buffer = 0;
157 cookie->buffer_size = 0;
158 cookie->is_audio = false;
159 cookie->is_video = false;
160 cookie->byte_pos = 0;
161 cookie->is_vbr = false;
162 cookie->bytes_per_second = 0;
164 BMediaFormats formats;
165 media_format *format = &cookie->format;
166 media_format_description description;
168 const avi_stream_header *stream_header;
169 stream_header = fFile->StreamFormat(cookie->stream);
170 if (!stream_header) {
171 ERROR("aviReader::GetStreamInfo: stream %d has no header\n", cookie->stream);
172 delete cookie;
173 return B_ERROR;
176 TRACE("aviReader::AllocateCookie: stream %ld (%s)\n", streamNumber, fFile->IsAudio(cookie->stream) ? "audio" : fFile->IsVideo(cookie->stream) ? "video" : "unknown");
178 if (fFile->IsAudio(cookie->stream)) {
179 const wave_format_ex *audio_format = fFile->AudioFormat(cookie->stream);
180 if (!audio_format) {
181 ERROR("aviReader::GetStreamInfo: audio stream %d has no format\n", cookie->stream);
182 delete cookie;
183 return B_ERROR;
186 cookie->is_audio = true;
187 cookie->duration = fFile->StreamInfo(streamNumber)->duration;
188 cookie->frame_count = fFile->StreamInfo(streamNumber)->frame_count;
189 cookie->frame_pos = 0;
190 cookie->frames_per_sec_rate = fFile->StreamInfo(streamNumber)->frames_per_sec_rate;
191 cookie->frames_per_sec_scale = fFile->StreamInfo(streamNumber)->frames_per_sec_scale;
193 TRACE("audio frame_count %Ld, duration %.6f\n", cookie->frame_count, cookie->duration / 1E6);
195 cookie->bytes_per_second = audio_format->avg_bytes_per_sec;
196 cookie->sample_size = stream_header->sample_size == 0 ? audio_format->bits_per_sample / 8 * audio_format->channels : stream_header->sample_size;
198 if (audio_format->format_tag == 0x0001) {
199 // a raw PCM format
200 description.family = B_BEOS_FORMAT_FAMILY;
201 description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
202 if (formats.GetFormatFor(description, format) < B_OK)
203 format->type = B_MEDIA_RAW_AUDIO;
204 format->u.raw_audio.frame_rate = audio_format->frames_per_sec;
205 format->u.raw_audio.channel_count = audio_format->channels;
206 if (audio_format->bits_per_sample <= 8)
207 format->u.raw_audio.format = B_AUDIO_FORMAT_UINT8;
208 else if (audio_format->bits_per_sample <= 16)
209 format->u.raw_audio.format = B_AUDIO_FORMAT_INT16;
210 else if (audio_format->bits_per_sample <= 24)
211 format->u.raw_audio.format = B_AUDIO_FORMAT_INT24;
212 else if (audio_format->bits_per_sample <= 32)
213 format->u.raw_audio.format = B_AUDIO_FORMAT_INT32;
214 else {
215 ERROR("aviReader::AllocateCookie: unhandled bits per sample %d\n", audio_format->bits_per_sample);
216 return B_ERROR;
218 format->u.raw_audio.format |= B_AUDIO_FORMAT_CHANNEL_ORDER_WAVE;
219 format->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
220 format->u.raw_audio.buffer_size = stream_header->suggested_buffer_size;
221 cookie->frame_size = cookie->sample_size;
222 } else {
223 // some encoded format
224 description.family = B_WAV_FORMAT_FAMILY;
225 description.u.wav.codec = audio_format->format_tag;
226 if (formats.GetFormatFor(description, format) < B_OK)
227 format->type = B_MEDIA_ENCODED_AUDIO;
228 format->u.encoded_audio.bit_rate = 8 * audio_format->avg_bytes_per_sec;
229 format->u.encoded_audio.output.frame_rate = audio_format->frames_per_sec;
230 format->u.encoded_audio.output.channel_count = audio_format->channels;
231 cookie->frame_size = audio_format->block_align == 0 ? 1 : audio_format->block_align;
233 // detect vbr audio in avi hack
234 cookie->is_vbr = cookie->frame_size >= 960;
236 TRACE("audio: bit_rate %.3f, frame_rate %.1f, channel_count %lu, frame_size %ld, is vbr %s\n",
237 format->u.encoded_audio.bit_rate,
238 format->u.encoded_audio.output.frame_rate,
239 format->u.encoded_audio.output.channel_count,
240 cookie->frame_size, cookie->is_vbr ? "true" : "false");
243 // TODO: this doesn't seem to work (it's not even a fourcc)
244 format->user_data_type = B_CODEC_TYPE_INFO;
245 *(uint32 *)format->user_data = audio_format->format_tag;
246 format->user_data[4] = 0;
248 // put the wave_format_ex struct, including extra data, into the format meta data.
249 size_t size;
250 const void *data = fFile->AudioFormat(cookie->stream, &size);
251 format->SetMetaData(data, size);
253 #ifdef TRACE_AVI_READER
254 uint8 *p = 18 + (uint8 *)data;
255 TRACE("extra_data: %ld: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
256 size - 18, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]);
257 #endif
259 return B_OK;
262 if (fFile->IsVideo(cookie->stream)) {
263 const bitmap_info_header *video_format = fFile->VideoFormat(cookie->stream);
264 if (!video_format) {
265 ERROR("aviReader::GetStreamInfo: video stream %d has no format\n", cookie->stream);
266 delete cookie;
267 return B_ERROR;
270 cookie->is_video = true;
271 cookie->duration = fFile->StreamInfo(streamNumber)->duration;
272 cookie->frame_count = fFile->StreamInfo(streamNumber)->frame_count;
273 cookie->frame_pos = 0;
274 cookie->frames_per_sec_rate = fFile->StreamInfo(streamNumber)->frames_per_sec_rate;
275 cookie->frames_per_sec_scale = fFile->StreamInfo(streamNumber)->frames_per_sec_scale;
276 cookie->line_count = fFile->AviMainHeader()->height;
277 cookie->frame_size = 1;
279 TRACE("video frame_count %Ld, duration %.6f\n", cookie->frame_count,
280 cookie->duration / 1E6);
282 description.family = B_AVI_FORMAT_FAMILY;
283 if (stream_header->fourcc_handler == 'ekaf'
284 || stream_header->fourcc_handler == 0) {
285 // 'fake' or 0 fourcc => used compression id
286 description.u.avi.codec = video_format->compression;
287 } else
288 description.u.avi.codec = stream_header->fourcc_handler;
290 TRACE("codec '%.4s' (fourcc: '%.4s', compression: '%.4s')\n",
291 (char*)&description.u.avi.codec,
292 (char*)&stream_header->fourcc_handler,
293 (char*)&video_format->compression);
295 if (formats.GetFormatFor(description, format) < B_OK)
296 format->type = B_MEDIA_ENCODED_VIDEO;
298 format->user_data_type = B_CODEC_TYPE_INFO;
299 *(uint32 *)format->user_data = description.u.avi.codec;
300 format->user_data[4] = 0;
301 format->u.encoded_video.max_bit_rate = 8 * fFile->AviMainHeader()->max_bytes_per_sec;
302 format->u.encoded_video.avg_bit_rate = (format->u.encoded_video.max_bit_rate * 3 / 4); // XXX fix this
303 format->u.encoded_video.output.field_rate = cookie->frames_per_sec_rate / (float)cookie->frames_per_sec_scale;
304 format->u.encoded_video.output.interlace = 1; // 1: progressive
305 format->u.encoded_video.output.first_active = 0;
306 format->u.encoded_video.output.last_active = cookie->line_count - 1;
307 format->u.encoded_video.output.orientation = B_VIDEO_TOP_LEFT_RIGHT;
308 // TODO: Properly set the display aspect ratio. It is supposed to be
309 // the ratio of the final image. For example 16:9 actually has
310 // 16 and 9 as values.
311 format->u.encoded_video.output.pixel_width_aspect = 0;
312 format->u.encoded_video.output.pixel_height_aspect = 0;
313 // format->u.encoded_video.output.display.format = 0;
314 format->u.encoded_video.output.display.line_width = fFile->AviMainHeader()->width;
315 format->u.encoded_video.output.display.line_count = cookie->line_count;
316 format->u.encoded_video.output.display.bytes_per_row = 0; // format->u.encoded_video.output.display.line_width * 4;
317 format->u.encoded_video.output.display.pixel_offset = 0;
318 format->u.encoded_video.output.display.line_offset = 0;
319 format->u.encoded_video.output.display.flags = 0;
321 TRACE("max_bit_rate %.3f\n", format->u.encoded_video.max_bit_rate);
322 TRACE("field_rate %.3f\n", format->u.encoded_video.output.field_rate);
323 #ifdef TRACE_AVI_READER
324 uint32 encoding = format->Encoding();
325 TRACE("encoding '%.4s'\n", (char*)&encoding);
326 #endif
328 return B_OK;
331 TRACE("aviReader::GetStreamInfo: stream is neither video nor audio\n");
333 delete cookie;
334 return B_ERROR;
338 status_t
339 aviReader::FreeCookie(void *_cookie)
341 avi_cookie *cookie = (avi_cookie *)_cookie;
343 delete [] cookie->buffer;
345 delete cookie;
346 return B_OK;
350 status_t
351 aviReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration,
352 media_format *format, const void **infoBuffer, size_t *infoSize)
354 avi_cookie *cookie = (avi_cookie *)_cookie;
356 *frameCount = cookie->frame_count;
357 *duration = cookie->duration;
358 *format = cookie->format;
359 *infoBuffer = 0;
360 *infoSize = 0;
361 return B_OK;
365 status_t
366 aviReader::Seek(void *_cookie, uint32 seekTo, int64 *frame, bigtime_t *time)
368 // Seek changes the position of the stream
369 avi_cookie *cookie = (avi_cookie *)_cookie;
371 TRACE("aviReader::Seek: stream %d, seekTo%s%s%s%s, time %.6f, frame %Ld\n",
372 cookie->stream,
373 (seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
374 (seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
375 (seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ?
376 " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
377 (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ?
378 " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
379 *time / 1000000.0, *frame);
381 status_t rv = fFile->Seek(cookie->stream, seekTo, frame, time, false);
382 if (rv == B_OK) {
383 cookie->frame_pos = *frame;
384 if (cookie->is_audio && !cookie->is_vbr) {
385 // calculate byte_pos from time
386 cookie->byte_pos = *time * cookie->bytes_per_second / 1000000LL;
388 TRACE("aviReader::Seek: stream %d, success, frame_pos = %Ld, time = %.6f\n", cookie->stream, cookie->frame_pos, *time / 1000000.0);
391 return rv;
395 status_t
396 aviReader::FindKeyFrame(void *_cookie, uint32 flags, int64 *frame, bigtime_t *time)
398 // FindKeyFrame does not change the position of the stream
399 avi_cookie *cookie = (avi_cookie *)_cookie;
401 TRACE("aviReader::FindKeyFrame: stream %d, flags%s%s%s%s, time %.6f, "
402 "frame %Ld\n",
403 cookie->stream,
404 (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "",
405 (flags & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "",
406 (flags & B_MEDIA_SEEK_CLOSEST_FORWARD) ?
407 " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
408 (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ?
409 " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
410 *time / 1000000.0, *frame);
412 status_t rv = fFile->Seek(cookie->stream, flags, frame, time, true);
413 if (rv == B_OK) {
414 TRACE("aviReader::FindKeyFrame: stream %d, success\n", cookie->stream);
416 return rv;
420 status_t
421 aviReader::GetNextChunk(void *_cookie, const void **chunkBuffer,
422 size_t *chunkSize, media_header *mediaHeader)
424 avi_cookie *cookie = (avi_cookie *)_cookie;
426 int64 start; uint32 size; bool keyframe;
428 if (fFile->GetNextChunkInfo(cookie->stream, &start, &size,
429 &keyframe) < B_OK)
430 return B_LAST_BUFFER_ERROR;
432 if (size > 0x200000) { // 2 MB
433 ERROR("stream %u: frame too big: %lu bytes\n", cookie->stream, size);
434 return B_NO_MEMORY;
437 if (cookie->buffer_size < size) {
438 delete [] cookie->buffer;
439 cookie->buffer_size = (size + 15) & ~15;
440 cookie->buffer = new(std::nothrow) char [cookie->buffer_size];
441 if (!cookie->buffer) {
442 cookie->buffer_size = 0;
443 return B_NO_MEMORY;
447 mediaHeader->start_time = (cookie->frame_pos * 1000000LL
448 * cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate;
450 TRACE("stream %d (%s): start_time %.6f, pos %.3f %%, frame %Ld chunk size %ld offset %Ld\n",
451 cookie->stream, cookie->is_audio ? "A" : cookie->is_video ? "V" : "?",
452 mediaHeader->start_time / 1000000.0, cookie->frame_pos * 100.0
453 / cookie->frame_count, cookie->frame_pos, size, start);
455 if (cookie->is_audio) {
456 mediaHeader->type = B_MEDIA_ENCODED_AUDIO;
457 mediaHeader->u.encoded_audio.buffer_flags = keyframe ?
458 B_MEDIA_KEY_FRAME : 0;
460 cookie->byte_pos += size;
461 // frame_pos is sample no for vbr encoded audio and byte position for everything else
462 if (cookie->is_vbr) {
463 // advance by frame_size
464 cookie->frame_pos += cookie->frame_size;
465 } else {
466 cookie->frame_pos += (uint64)(ceil((double)size / (double)cookie->frame_size)) * cookie->frames_per_sec_scale;
467 // cookie->frame_pos += (uint64)(ceil((double)size / (double)cookie->frame_size)) * cookie->frames_per_sec / cookie->avg_bytes_per_sec;
468 // advance by bytes in chunk and calculate frame_pos
469 // time = cookie->byte_pos * 1000000LL / cookie->bytes_per_second;
470 // cookie->frame_pos = time * cookie->frames_per_sec_rate / cookie->frames_per_sec_scale / 1000000LL;
472 } else if (cookie->is_video) {
473 mediaHeader->type = B_MEDIA_ENCODED_VIDEO;
474 mediaHeader->u.encoded_video.field_flags = keyframe ?
475 B_MEDIA_KEY_FRAME : 0;
476 mediaHeader->u.encoded_video.first_active_line = 0;
477 mediaHeader->u.encoded_video.line_count = cookie->line_count;
478 mediaHeader->u.encoded_video.field_number = 0;
479 mediaHeader->u.encoded_video.field_sequence = cookie->frame_pos;
480 cookie->frame_pos += cookie->frame_size;
481 } else {
482 return B_BAD_VALUE;
485 *chunkBuffer = cookie->buffer;
486 *chunkSize = size;
487 return (int)size == fFile->Source()->ReadAt(start, cookie->buffer, size) ?
488 B_OK : B_LAST_BUFFER_ERROR;
492 Reader *
493 aviReaderPlugin::NewReader()
495 return new(std::nothrow) aviReader;
499 MediaPlugin *instantiate_plugin()
501 return new(std::nothrow) aviReaderPlugin;