2 * Copyright (c) 2004-2007, Marcus Overhagen
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.
29 #include <ByteOrder.h>
30 #include <InterfaceDefs.h>
31 #include <MediaFormats.h>
33 #include "RawFormats.h"
34 #include "avi_reader.h"
36 //#define TRACE_AVI_READER
37 #ifdef TRACE_AVI_READER
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
63 uint32 frames_per_sec_rate
;
64 uint32 frames_per_sec_scale
;
73 uint16 bytes_per_second
;
78 aviReader::aviReader()
81 TRACE("aviReader::aviReader\n");
85 aviReader::~aviReader()
92 aviReader::Copyright()
94 return "AVI & OpenDML reader, " B_UTF8_COPYRIGHT
" by Marcus Overhagen";
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");
111 if (!OpenDMLFile::IsSupported(pos_io_source
)) {
112 TRACE("aviReader::Sniff: unsupported file type\n");
116 TRACE("aviReader::Sniff: this stream seems to be supported\n");
118 fFile
= new(std::nothrow
) OpenDMLFile(pos_io_source
);
120 ERROR("aviReader::Sniff: out of memory\n");
123 if (fFile
->Init() < B_OK
) {
124 ERROR("aviReader::Sniff: can't setup OpenDMLFile\n");
128 *streamCount
= fFile
->StreamCount();
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
;
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");
148 aviReader::AllocateCookie(int32 streamNumber
, void **_cookie
)
150 avi_cookie
*cookie
= new(std::nothrow
) avi_cookie
;
155 cookie
->stream
= streamNumber
;
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
);
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
);
181 ERROR("aviReader::GetStreamInfo: audio stream %d has no format\n", cookie
->stream
);
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) {
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
;
215 ERROR("aviReader::AllocateCookie: unhandled bits per sample %d\n", audio_format
->bits_per_sample
);
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
;
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.
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]);
262 if (fFile
->IsVideo(cookie
->stream
)) {
263 const bitmap_info_header
*video_format
= fFile
->VideoFormat(cookie
->stream
);
265 ERROR("aviReader::GetStreamInfo: video stream %d has no format\n", cookie
->stream
);
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
;
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
);
331 TRACE("aviReader::GetStreamInfo: stream is neither video nor audio\n");
339 aviReader::FreeCookie(void *_cookie
)
341 avi_cookie
*cookie
= (avi_cookie
*)_cookie
;
343 delete [] cookie
->buffer
;
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
;
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",
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);
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);
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, "
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);
414 TRACE("aviReader::FindKeyFrame: stream %d, success\n", cookie
->stream
);
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
,
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
);
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;
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
;
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
;
485 *chunkBuffer
= cookie
->buffer
;
487 return (int)size
== fFile
->Source()->ReadAt(start
, cookie
->buffer
, size
) ?
488 B_OK
: B_LAST_BUFFER_ERROR
;
493 aviReaderPlugin::NewReader()
495 return new(std::nothrow
) aviReader
;
499 MediaPlugin
*instantiate_plugin()
501 return new(std::nothrow
) aviReaderPlugin
;