From a3d7ce0637e9062542d81f233d633beb18b5bc37 Mon Sep 17 00:00:00 2001 From: beveloper Date: Sat, 24 Jan 2004 17:06:57 +0000 Subject: [PATCH] initial support for reading OpenDML AVI files git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6256 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/media/plugins/avi_reader/Jamfile | 6 + .../media/plugins/avi_reader/avi_reader.cpp | 269 ++++++++ .../media/plugins/avi_reader/avi_reader.h | 40 ++ .../plugins/avi_reader/libOpenDML/Jamfile | 6 + .../avi_reader/libOpenDML/OpenDMLFile.cpp | 313 +++++++++ .../avi_reader/libOpenDML/OpenDMLFile.h | 44 ++ .../avi_reader/libOpenDML/OpenDMLParser.cpp | 609 ++++++++++++++++++ .../avi_reader/libOpenDML/OpenDMLParser.h | 74 +++ .../media/plugins/avi_reader/libOpenDML/avi.h | 144 +++++ 9 files changed, 1505 insertions(+) create mode 100644 src/add-ons/media/plugins/avi_reader/avi_reader.h create mode 100644 src/add-ons/media/plugins/avi_reader/libOpenDML/Jamfile create mode 100644 src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp create mode 100644 src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h create mode 100644 src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp create mode 100644 src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h create mode 100644 src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h diff --git a/src/add-ons/media/plugins/avi_reader/Jamfile b/src/add-ons/media/plugins/avi_reader/Jamfile index 867f171bf9..a30b78d152 100644 --- a/src/add-ons/media/plugins/avi_reader/Jamfile +++ b/src/add-ons/media/plugins/avi_reader/Jamfile @@ -4,6 +4,12 @@ UsePrivateHeaders media ; Addon avi_reader : media plugins : avi_reader.cpp + : + false + : + libopendml.a ; LinkSharedOSLibs avi_reader : be libmedia.so ; + +SubInclude OBOS_TOP src add-ons media plugins avi_reader libOpenDML ; diff --git a/src/add-ons/media/plugins/avi_reader/avi_reader.cpp b/src/add-ons/media/plugins/avi_reader/avi_reader.cpp index e69de29bb2..f04646623e 100644 --- a/src/add-ons/media/plugins/avi_reader/avi_reader.cpp +++ b/src/add-ons/media/plugins/avi_reader/avi_reader.cpp @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include +#include +#include +#include "RawFormats.h" +#include "avi_reader.h" + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) +#endif + +// http://www.microsoft.com/Developer/PRODINFO/directx/dxm/help/ds/FiltDev/DV_Data_AVI_File_Format.htm + +struct avi_cookie +{ + int stream; + char * buffer; + int buffer_size; + + bool audio; + + int64 byte_pos; + uint32 bytes_per_sec_rate; + uint32 bytes_per_sec_scale; + + uint32 frame_pos; +}; + + +aviReader::aviReader() + : fFile(0) +{ + TRACE("aviReader::aviReader\n"); +} + + +aviReader::~aviReader() +{ + delete fFile; +} + + +const char * +aviReader::Copyright() +{ + return "AVI & OpenDML reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen"; +} + + +status_t +aviReader::Sniff(int32 *streamCount) +{ + TRACE("aviReader::Sniff\n"); + + BPositionIO *pos_io_source; + + pos_io_source = dynamic_cast(Reader::Source()); + if (!pos_io_source) { + TRACE("aviReader::Sniff: not a BPositionIO\n"); + return B_ERROR; + } + + if (!OpenDMLFile::IsSupported(pos_io_source)) { + TRACE("aviReader::Sniff: unsupported file type\n"); + return B_ERROR; + } + + TRACE("aviReader::Sniff: this stream seems to be supported\n"); + + fFile = new OpenDMLFile(); + if (B_OK != fFile->SetTo(pos_io_source)) { + TRACE("aviReader::Sniff: can't setup OpenDMLFile\n"); + return B_ERROR; + } + + *streamCount = fFile->StreamCount(); + return B_OK; +} + +void +aviReader::GetFileFormatInfo(media_file_format *mff) +{ + mff->capabilities = media_file_format::B_READABLE + | media_file_format::B_KNOWS_ENCODED_VIDEO + | media_file_format::B_KNOWS_ENCODED_AUDIO + | media_file_format::B_IMPERFECTLY_SEEKABLE; + mff->family = B_MISC_FORMAT_FAMILY; + mff->version = 100; + strcpy(mff->mime_type, "audio/x-avi"); + strcpy(mff->file_extension, "avi"); + strcpy(mff->short_name, "AVI"); + strcpy(mff->pretty_name, "Audio/Video Interleaved (AVI) file format"); +} + +status_t +aviReader::AllocateCookie(int32 streamNumber, void **_cookie) +{ + avi_cookie *cookie = new avi_cookie; + *_cookie = cookie; + + cookie->stream = streamNumber; + cookie->buffer = 0; + cookie->buffer_size = 0; + + return B_OK; +} + +status_t +aviReader::FreeCookie(void *_cookie) +{ + avi_cookie *cookie = (avi_cookie *)_cookie; + + delete [] cookie->buffer; + + delete cookie; + return B_OK; +} + + +status_t +aviReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration, + media_format *format, void **infoBuffer, int32 *infoSize) +{ + avi_cookie *cookie = (avi_cookie *)_cookie; + + *duration = fFile->Duration(); + *infoBuffer = 0; + *infoSize = 0; + + BMediaFormats formats; + media_format_description description; + + const avi_stream_header *stream_header; + stream_header = fFile->StreamFormat(cookie->stream); + if (!stream_header) { + TRACE("aviReader::GetStreamInfo: stream %d has no header\n", cookie->stream); + return B_ERROR; + } + + if (fFile->IsAudio(cookie->stream)) { + const wave_format_ex *audio_format = fFile->AudioFormat(cookie->stream); + if (!audio_format) { + TRACE("aviReader::GetStreamInfo: audio stream %d has no format\n", cookie->stream); + return B_ERROR; + } + + if (audio_format->format_tag == 0x0001) // PCM + *frameCount = stream_header->length / ((stream_header->sample_size + 7) / 8); + else // not PCM + *frameCount = (stream_header->length * audio_format->frames_per_sec) / (stream_header->sample_size * audio_format->avg_bytes_per_sec); + + cookie->audio = true; + cookie->byte_pos = 0; + + cookie->bytes_per_sec_rate = audio_format->avg_bytes_per_sec; + cookie->bytes_per_sec_scale = 1; + + if (audio_format->format_tag == 0x0001) { + // a raw PCM format + description.family = B_BEOS_FORMAT_FAMILY; + description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO; + formats.GetFormatFor(description, format); + format->u.raw_audio.frame_rate = audio_format->frames_per_sec; + format->u.raw_audio.channel_count = audio_format->channels; + if (audio_format->bits_per_sample <= 8) + format->u.raw_audio.format = B_AUDIO_FORMAT_UINT8; + else if (audio_format->bits_per_sample <= 16) + format->u.raw_audio.format = B_AUDIO_FORMAT_INT16; + else if (audio_format->bits_per_sample <= 24) + format->u.raw_audio.format = B_AUDIO_FORMAT_INT24; + else if (audio_format->bits_per_sample <= 32) + format->u.raw_audio.format = B_AUDIO_FORMAT_INT32; + else { + TRACE("WavReader::AllocateCookie: unhandled bits per sample %d\n", audio_format->bits_per_sample); + return B_ERROR; + } + format->u.raw_audio.format |= B_AUDIO_FORMAT_CHANNEL_ORDER_WAVE; + format->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN; + format->u.raw_audio.buffer_size = stream_header->suggested_buffer_size; + } else { + // some encoded format + description.family = B_WAV_FORMAT_FAMILY; + description.u.wav.codec = audio_format->format_tag; + formats.GetFormatFor(description, format); + format->u.encoded_audio.output.frame_rate = audio_format->frames_per_sec; + format->u.encoded_audio.output.channel_count = audio_format->channels; + } + return B_OK; + } + + if (fFile->IsVideo(cookie->stream)) { + const bitmap_info_header *video_format = fFile->VideoFormat(cookie->stream); + if (!video_format) { + TRACE("aviReader::GetStreamInfo: video stream %d has no format\n", cookie->stream); + return B_ERROR; + } + + *frameCount = fFile->FrameCount(); + + cookie->audio = false; + cookie->frame_pos = 0; + + return B_OK; + } + + return B_ERROR; +} + + +status_t +aviReader::Seek(void *cookie, + uint32 seekTo, + int64 *frame, bigtime_t *time) +{ + + return B_OK; +} + + +status_t +aviReader::GetNextChunk(void *_cookie, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader) +{ + avi_cookie *cookie = (avi_cookie *)_cookie; + + int64 start; uint32 size; bool keyframe; + if (!fFile->GetNextChunkInfo(cookie->stream, &start, &size, &keyframe)) + return B_LAST_BUFFER_ERROR; + + if (cookie->buffer_size < size) { + delete [] cookie->buffer; + cookie->buffer_size = (size + 15) & ~15; + cookie->buffer = new char [cookie->buffer_size]; + } + + if (cookie->audio) { + mediaHeader->start_time = (cookie->byte_pos * 1000000ULL * cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate; + + cookie->byte_pos += size; + } else { + cookie->frame_pos += 1; + } + + printf("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0); + + *chunkBuffer = cookie->buffer; + *chunkSize = size; + return size == fFile->Source()->ReadAt(start, cookie->buffer, size) ? B_OK : B_LAST_BUFFER_ERROR; +} + + +Reader * +aviReaderPlugin::NewReader() +{ + return new aviReader; +} + + +MediaPlugin *instantiate_plugin() +{ + return new aviReaderPlugin; +} diff --git a/src/add-ons/media/plugins/avi_reader/avi_reader.h b/src/add-ons/media/plugins/avi_reader/avi_reader.h new file mode 100644 index 0000000000..8331ab0949 --- /dev/null +++ b/src/add-ons/media/plugins/avi_reader/avi_reader.h @@ -0,0 +1,40 @@ +#include "ReaderPlugin.h" +#include "libOpenDML/OpenDMLFile.h" + +class aviReader : public Reader +{ +public: + aviReader(); + ~aviReader(); + + const char *Copyright(); + + status_t Sniff(int32 *streamCount); + + void GetFileFormatInfo(media_file_format *mff); + + status_t AllocateCookie(int32 streamNumber, void **cookie); + status_t FreeCookie(void *cookie); + + status_t GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration, + media_format *format, void **infoBuffer, int32 *infoSize); + + status_t Seek(void *cookie, + uint32 seekTo, + int64 *frame, bigtime_t *time); + + status_t GetNextChunk(void *cookie, + void **chunkBuffer, int32 *chunkSize, + media_header *mediaHeader); +private: + OpenDMLFile *fFile; +}; + + +class aviReaderPlugin : public ReaderPlugin +{ +public: + Reader *NewReader(); +}; + +MediaPlugin *instantiate_plugin(); diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/Jamfile b/src/add-ons/media/plugins/avi_reader/libOpenDML/Jamfile new file mode 100644 index 0000000000..c00a08281f --- /dev/null +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/Jamfile @@ -0,0 +1,6 @@ +SubDir OBOS_TOP src add-ons media plugins avi_reader libOpenDML ; + +StaticLibrary opendml : + OpenDMLFile.cpp + OpenDMLParser.cpp +; diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp new file mode 100644 index 0000000000..b5db62f6dd --- /dev/null +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp @@ -0,0 +1,313 @@ +#include +#include "OpenDMLFile.h" + +#define TRACE printf + +#define INDEX_CHUNK_SIZE 32768 + +struct OpenDMLFile::stream_data +{ + const stream_info *info; + + uint32 chunk_id; + + char * superindex; + int superindex_entry_size; + int superindex_entry_count; + int superindex_entry_pos; + + // index info (superindex entry) + int64 index_entry_start; + int64 index_base_offset; + int index_entry_size; + int index_entry_count; + int index_entry_pos; + + // index chunk + char * index_chunk; + int index_chunk_entry_count; + int index_chunk_entry_pos; +}; + +OpenDMLFile::OpenDMLFile() + : fSource(0), + fParser(0), + fStreamCount(0), + fStreamData(0) +{ +} + +OpenDMLFile::~OpenDMLFile() +{ + delete fParser; + delete [] fStreamData; +} + +/* static */ bool +OpenDMLFile::IsSupported(BPositionIO *source) +{ + uint8 h[12]; + if (12 != source->ReadAt(0, h, 12)) + return false; + return h[0] == 'R' && h[1] == 'I' && h[2] == 'F' && h[3] == 'F' && + h[8] == 'A' && h[9] == 'V' && h[10] == 'I' && h[11] == ' '; +} + +status_t +OpenDMLFile::SetTo(BPositionIO *source) +{ + delete fParser; + fSource = source; + fParser = new OpenDMLParser; + fParser->Parse(source); + + if (!fParser->AviMainHeader()) { + TRACE("OpenDMLFile::SetTo: avi main header not found\n"); + return B_ERROR; + } + + if (fParser->StreamCount() != 0 && fParser->StandardIndexSize() == 0) { + TRACE("OpenDMLFile::SetTo file has no standard avi index\n"); + bool found_odml_index = false; + for (int i = 0; i < fParser->StreamCount(); i++) { + if (fParser->StreamInfo(i)->odml_index_size != 0) { + found_odml_index = true; + break; + } + } + if (!found_odml_index) { + TRACE("OpenDMLFile::SetTo file has no standard avi index, and no OpenDML track index found\n"); + return B_ERROR; + } + } + + TRACE("OpenDMLFile::SetTo: this is a %s AVI file with %d streams\n", fParser->OdmlExtendedHeader() ? "OpenDML" : "standard", fParser->StreamCount()); + + InitData(); + + return B_OK; +} + +void +OpenDMLFile::InitData() +{ + delete [] fStreamData; + + fStreamCount = fParser->StreamCount(); + fStreamData = new stream_data[fStreamCount]; + + for (int stream = 0; stream < fStreamCount; stream++) { + TRACE("OpenDMLFile::InitData: stream %d\n", stream); + + fStreamData[stream].info = fParser->StreamInfo(stream); + if (fStreamData[stream].info->odml_index_size) { + TRACE("OpenDMLFile::InitData: index header, start %Ld, size %lu\n", fStreamData[stream].info->odml_index_start, fStreamData[stream].info->odml_index_size); + + odml_index_header h; + + // XXX error checking + endian conv. + + fSource->ReadAt(fStreamData[stream].info->odml_index_start, &h, sizeof(h)); + + TRACE("longs_per_entry %u\n", h.longs_per_entry); + TRACE("index_sub_type %u\n", h.index_sub_type); + TRACE("index_type %u\n", h.index_type); + TRACE("entries_used %lu\n", h.entries_used); + TRACE("chunk_id "FOURCC_FORMAT"\n", FOURCC_PARAM(h.chunk_id)); + + if (h.index_type == AVI_INDEX_OF_INDEXES) { + int size = h.entries_used * h.longs_per_entry * 4; + TRACE("OpenDMLFile::InitData: reading superindex of %d bytes\n", size); + fStreamData[stream].superindex_entry_size = h.longs_per_entry * 4; + fStreamData[stream].superindex_entry_count = h.entries_used; + fStreamData[stream].superindex_entry_pos = 0; + fStreamData[stream].superindex = new char [size]; + fSource->ReadAt(fStreamData[stream].info->odml_index_start + sizeof(h), fStreamData[stream].superindex, size); + } else if (h.index_type == AVI_INDEX_OF_CHUNKS){ + TRACE("OpenDMLFile::InitData: creating fake superindex\n"); + fStreamData[stream].superindex_entry_size = 16; + fStreamData[stream].superindex_entry_count = 1; + fStreamData[stream].superindex_entry_pos = 0; + fStreamData[stream].superindex = new char [16]; + ((odml_superindex_entry *)fStreamData[stream].superindex)->start = fStreamData[stream].info->odml_index_start; + ((odml_superindex_entry *)fStreamData[stream].superindex)->size = fStreamData[stream].info->odml_index_size; + ((odml_superindex_entry *)fStreamData[stream].superindex)->duration = 0; + } else if (h.index_type == AVI_INDEX_IS_DATA){ + TRACE("OpenDMLFile::InitData: AVI_INDEX_IS_DATA not supported\n"); + fStreamData[stream].superindex = 0; + fStreamData[stream].superindex_entry_count = 0; + fStreamData[stream].superindex_entry_pos = 0; + } else { + TRACE("OpenDMLFile::InitData: index type not recongnized\n"); + fStreamData[stream].superindex = 0; + fStreamData[stream].superindex_entry_count = 0; + fStreamData[stream].superindex_entry_pos = 0; + } + + for (int i = 0; i < fStreamData[stream].superindex_entry_count; i++) { + odml_superindex_entry *entry = (odml_superindex_entry *) (fStreamData[stream].superindex + i * fStreamData[stream].superindex_entry_size); + TRACE("superindex entry %d: start %10Ld, size %8ld, duration %lu\n", i, entry->start, entry->size, entry->duration); + } + + fStreamData[stream].index_entry_start = 0; + fStreamData[stream].index_base_offset = 0; + fStreamData[stream].index_entry_size = 0; + fStreamData[stream].index_entry_count = 0; + fStreamData[stream].index_entry_pos = 0; + fStreamData[stream].index_chunk = new char [INDEX_CHUNK_SIZE]; + fStreamData[stream].index_chunk_entry_count = 0; + fStreamData[stream].index_chunk_entry_pos = 0; + } else { + fStreamData[stream].superindex = 0; + } + + } +} + +bool +OpenDMLFile::ReadIndexInfo(int stream_index) +{ + stream_data *data = &fStreamData[stream_index]; + + if (data->superindex_entry_pos >= data->superindex_entry_count) { + TRACE("reached end of superindex\n"); + return false; + } + + odml_superindex_entry *entry = (odml_superindex_entry *) (data->superindex + data->superindex_entry_pos * data->superindex_entry_size); + + TRACE("OpenDMLFile::ReadIndexInfo: stream %d, pos %d, start %Ld, size %lu, duration %u\n", + stream_index, data->superindex_entry_pos, entry->start, entry->size, entry->duration); + + odml_chunk_index_header chunk_index_header; + + if (sizeof(chunk_index_header) != fSource->ReadAt(entry->start + 8, &chunk_index_header, sizeof(chunk_index_header))) { + TRACE("read error\n"); + return false; + } + + TRACE("longs_per_entry %u\n", chunk_index_header.longs_per_entry); + TRACE("index_sub_type %u\n", chunk_index_header.index_sub_type); + TRACE("index_type %u\n", chunk_index_header.index_type); + TRACE("entries_used %lu\n", chunk_index_header.entries_used); + TRACE("chunk_id "FOURCC_FORMAT"\n", FOURCC_PARAM(chunk_index_header.chunk_id)); + TRACE("base_offset %Ld\n", chunk_index_header.base_offset); + + data->index_base_offset = chunk_index_header.base_offset; + data->index_entry_start = entry->start + sizeof(chunk_index_header) + 8; + data->index_entry_size = chunk_index_header.longs_per_entry * 4; + data->index_entry_count = chunk_index_header.entries_used; + data->index_entry_pos = 0; + + data->superindex_entry_pos++; + return true; +} + +bool +OpenDMLFile::ReadIndexChunk(int stream_index) +{ + stream_data *data = &fStreamData[stream_index]; + + while (data->index_entry_pos >= data->index_entry_count) { + if (!ReadIndexInfo(stream_index)) + return false; + } + + data->index_chunk_entry_count = min_c(data->index_entry_count - data->index_entry_pos, INDEX_CHUNK_SIZE / data->index_entry_size); + data->index_chunk_entry_pos = 0; + int size = data->index_chunk_entry_count * data->index_entry_size; + int64 start = data->index_entry_start + data->index_entry_pos * data->index_entry_size; + + TRACE("OpenDMLFile::ReadIndexChunk: stream %d, index_chunk_entry_count %d, size %d, start %Ld\n", + stream_index, data->index_chunk_entry_count, size, start); + + if (size != fSource->ReadAt(start, data->index_chunk, size)) { + TRACE("read error\n"); + return false; + } + + data->index_entry_pos += data->index_chunk_entry_count; + return true; +} + +bool +OpenDMLFile::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe) +{ + stream_data *data = &fStreamData[stream_index]; + + while (data->index_chunk_entry_pos >= data->index_chunk_entry_count) { + if (!ReadIndexChunk(stream_index)) + return false; + } + + odml_index_entry *entry = (odml_index_entry *)(data->index_chunk + data->index_chunk_entry_pos * data->index_entry_size); + + *start = data->index_base_offset + entry->start; + *size = entry->size & 0x7fffffff; + *keyframe = (entry->size & 0x80000000) ? false : true; + + data->index_chunk_entry_pos++; + + printf("OpenDMLFile::GetNextChunkInfo: stream %d: start %15Ld, size %6d%s\n", + stream_index, *start, *size, *keyframe ? ", keyframe" : ""); + return true; +} + +int +OpenDMLFile::StreamCount() +{ + return fStreamCount; +} + +bigtime_t +OpenDMLFile::Duration() +{ + if (!fParser->AviMainHeader()) + return 0; + if (fParser->OdmlExtendedHeader()) + return fParser->OdmlExtendedHeader()->total_frames * (bigtime_t)fParser->AviMainHeader()->micro_sec_per_frame; + return fParser->AviMainHeader()->total_frames * (bigtime_t)fParser->AviMainHeader()->micro_sec_per_frame; +} + +uint32 +OpenDMLFile::FrameCount() +{ + if (fParser->OdmlExtendedHeader()) + return fParser->OdmlExtendedHeader()->total_frames; + if (fParser->AviMainHeader()) + return fParser->AviMainHeader()->total_frames; + return 0; +} + +bool +OpenDMLFile::IsVideo(int stream_index) +{ + return fStreamData[stream_index].info->is_video; +} + +bool +OpenDMLFile::IsAudio(int stream_index) +{ + return fStreamData[stream_index].info->is_audio; +} + +const wave_format_ex * +OpenDMLFile::AudioFormat(int stream_index) +{ + return (fStreamData[stream_index].info->is_audio && fStreamData[stream_index].info->audio_format_valid) ? + &fStreamData[stream_index].info->audio_format : 0; +} + +const bitmap_info_header * +OpenDMLFile::VideoFormat(int stream_index) +{ + return (fStreamData[stream_index].info->is_video && fStreamData[stream_index].info->video_format_valid) ? + &fStreamData[stream_index].info->video_format : 0; +} + +const avi_stream_header * +OpenDMLFile::StreamFormat(int stream_index) +{ + return (fStreamData[stream_index].info->stream_header_valid) ? + &fStreamData[stream_index].info->stream_header : 0; +} diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h new file mode 100644 index 0000000000..eb84f5de30 --- /dev/null +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h @@ -0,0 +1,44 @@ +#include +#include "OpenDMLParser.h" + +class OpenDMLFile +{ +public: + OpenDMLFile(); + ~OpenDMLFile(); + + static bool IsSupported(BPositionIO *source); + + status_t SetTo(BPositionIO *source); + + int StreamCount(); + + bigtime_t Duration(); + uint32 FrameCount(); + + bool IsVideo(int stream_index); + bool IsAudio(int stream_index); + + const wave_format_ex * AudioFormat(int stream_index); + const bitmap_info_header * VideoFormat(int stream_index); + const avi_stream_header * StreamFormat(int stream_index); + + bool GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe); + + BPositionIO *Source() { return fSource; } + +private: + void InitData(); + bool ReadIndexChunk(int stream_index); + bool ReadIndexInfo(int stream_index); + +private: + BPositionIO * fSource; + OpenDMLParser * fParser; + + struct stream_data; + + int fStreamCount; + stream_data * fStreamData; +}; + diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp new file mode 100644 index 0000000000..9789f9351a --- /dev/null +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp @@ -0,0 +1,609 @@ +#include +#include +#include "OpenDMLParser.h" +#include "avi.h" + +#define TRACE_THIS 1 +#if TRACE_THIS + #define TRACE printf +#else + #define TRACE(a...) +#endif + +struct movie_chunk +{ + movie_chunk * next; + int64 start; + uint32 size; +}; + +OpenDMLParser::OpenDMLParser() + : fSource(0), + fSize(0), + fStandardIndexStart(0), + fStandardIndexSize(0), + fStreamCount(0), + fMovieChunkCount(0), + fAviMainHeaderValid(false), + fOdmlExtendedHeaderValid(false), + fStreams(0), + fCurrentStream(0) +{ +} + +OpenDMLParser::~OpenDMLParser() +{ +} + +int +OpenDMLParser::StreamCount() +{ + return fStreamCount; +} + +const stream_info * +OpenDMLParser::StreamInfo(int index) +{ + if (index < 0 || index >= fStreamCount) + return 0; + + stream_info *info = fStreams; + while (index--) + info = info->next; + return info; +} + +int64 +OpenDMLParser::StandardIndexStart() +{ + return fStandardIndexStart; +} + +uint32 +OpenDMLParser::StandardIndexSize() +{ + return fStandardIndexSize; +} + +const avi_main_header * +OpenDMLParser::AviMainHeader() +{ + return fAviMainHeaderValid ? &fAviMainHeader : 0; +} + +const odml_extended_header * +OpenDMLParser::OdmlExtendedHeader() +{ + return fOdmlExtendedHeaderValid ? &fOdmlExtendedHeader : 0; +} + +void +OpenDMLParser::CreateNewStreamInfo() +{ + stream_info *info = new stream_info; + info->next = 0; + info->is_audio = false; + info->is_video = false; + info->stream_header_valid = false; + info->audio_format_valid = false; + info->video_format_valid = false; + info->odml_index_start = 0; + info->odml_index_size = 0; + + // append the new stream_info to the fStreams list and point fCurrentStream to it + if (fStreams) { + stream_info *cur = fStreams; + while (cur->next) + cur = cur->next; + cur->next = info; + } else { + fStreams = info; + } + fCurrentStream = info; +} + +void +OpenDMLParser::Parse(BPositionIO *source) +{ + TRACE("OpenDMLParser::Parse\n"); + + fSource = source; + fSize = source->Seek(0, SEEK_END); + + if (fSize < 32) { + TRACE("OpenDMLParser::Parse: file to small\n"); + return; + } + + uint64 pos = 0; + int riff_chunk_number = 0; + while (pos < (uint64)fSize) { + uint32 temp; + uint32 fourcc; + uint32 size; + + if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::Parse: read error at pos %llu\n", pos); + goto err; + } + pos += 4; + fourcc = AVI_UINT32(temp); + + if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::Parse: read error at pos %llu\n", pos); + goto err; + } + pos += 4; + size = AVI_UINT32(temp); + + if (fourcc == FOURCC('J','U','N','K')) { + TRACE("OpenDMLParser::Parse: JUNK chunk ignored, size: %lu bytes\n", size); + goto cont; + } + + if (fourcc != FOURCC('R','I','F','F')) { + if (riff_chunk_number == 0) { + TRACE("OpenDMLParser::Parse: not a RIFF file\n"); + } else { + TRACE("OpenDMLParser::Parse: unknown chunk '"FOURCC_FORMAT"' (expected 'RIFF'), size = %lu ignored\n", FOURCC_PARAM(fourcc), size); + goto cont; + } + } + + TRACE("OpenDMLParser::Parse: RIFF chunk %d size: %lu bytes\n", riff_chunk_number, size); + + if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::Parse: read error at pos %llu\n", pos); + goto err; + } + fourcc = AVI_UINT32(temp); + + if (riff_chunk_number == 0 && fourcc != FOURCC('A','V','I',' ')) { + TRACE("OpenDMLParser::Parse: not a AVI file\n"); + goto err; + } + + if (fourcc != FOURCC('A','V','I',' ') && fourcc != FOURCC('A','V','I','X')) { + TRACE("OpenDMLParser::Parse: unknown chunk '"FOURCC_FORMAT"' , size = %lu ignored\n", FOURCC_PARAM(fourcc), size); + goto cont; + } + + ParseChunk_AVI(riff_chunk_number, pos + 4, size - 4); + +cont: + pos += (size) + (size & 1); + riff_chunk_number++; + } + return; + +err: + fStreamCount = 0; +} + +void +OpenDMLParser::ParseChunk_AVI(int number, uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_AVI\n"); + uint64 pos = start; + uint64 end = start + size; + + while (pos < end) { + uint32 temp; + uint32 Chunkfcc; + uint32 Chunksize; + + if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::ParseChunk_AVI: read error at pos %llu\n",pos); + return; + } + pos += 4; + Chunkfcc = AVI_UINT32(temp); + + if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::ParseChunk_AVI: read error at pos %llu\n",pos); + return; + } + pos += 4; + Chunksize = AVI_UINT32(temp); + + TRACE("OpenDMLParser::ParseChunk_AVI: chunk '"FOURCC_FORMAT"', size = %lu\n", FOURCC_PARAM(Chunkfcc), Chunksize); + + if (Chunkfcc == FOURCC('J','U','N','K')) + goto cont; + else if (Chunkfcc == FOURCC('L','I','S','T')) + ParseChunk_LIST(pos, Chunksize); + else if (Chunkfcc == FOURCC('i','d','x','1')) { + ParseChunk_idx1(pos, Chunksize); + } else { + TRACE("OpenDMLParser::ParseChunk_AVI: unknown chunk ignored\n"); + } + +cont: + pos += (Chunksize) + (Chunksize & 1); + } +} + +void +OpenDMLParser::ParseChunk_LIST(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_LIST\n"); + uint32 temp; + uint32 fourcc; + + if (sizeof(temp) != fSource->ReadAt(start, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::ParseChunk_LIST: read error at pos %llu\n", start); + return; + } + fourcc = AVI_UINT32(temp); + + TRACE("OpenDMLParser::ParseChunk_LIST: type '"FOURCC_FORMAT"'\n", FOURCC_PARAM(fourcc)); + + if (fourcc == FOURCC('m','o','v','i')) + ParseList_movi(start + 4, size - 4); + else if (fourcc == FOURCC('r','e','c',' ')) + ParseList_movi(start + 4, size - 4); //XXX parse rec simliar to movi??? + else if (fourcc == FOURCC('h','d','r','l')) + ParseList_generic(start + 4, size - 4); + else if (fourcc == FOURCC('s','t','r','l')) + ParseList_strl(start + 4, size - 4); + else if (fourcc == FOURCC('o','d','m','l')) + ParseList_generic(start + 4, size - 4); + else + TRACE("OpenDMLParser::ParseChunk_LIST: unknown list type ignored\n"); +} + +void +OpenDMLParser::ParseChunk_idx1(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_idx1\n"); + + if (fStandardIndexSize != 0) { + TRACE("OpenDMLParser::ParseChunk_idx1: found a second chunk\n"); + return; + } + + fStandardIndexStart = start; + fStandardIndexSize = size; +} + +void +OpenDMLParser::ParseChunk_avih(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_avih\n"); + + if (fAviMainHeaderValid) { + TRACE("OpenDMLParser::ParseChunk_avih: found a second chunk\n"); + return; + } + + if (size < sizeof(fAviMainHeader)) { + TRACE("OpenDMLParser::ParseChunk_avih: warning, avi header chunk too small\n"); + } + + memset(&fAviMainHeader, 0, sizeof(fAviMainHeader)); + size = min_c(size, sizeof(fAviMainHeader)); + if ((ssize_t)size != fSource->ReadAt(start, &fAviMainHeader, size)) { + TRACE("OpenDMLParser::ParseChunk_avih: read error at pos %llu\n", start); + return; + } + + #if B_HOST_IS_BENDIAN + B_SWAP_INT32(&fAviMainHeader.micro_sec_per_frame); + B_SWAP_INT32(&fAviMainHeader.max_bytes_per_sec); + B_SWAP_INT32(&fAviMainHeader.padding_granularity); + B_SWAP_INT32(&fAviMainHeader.flags); + B_SWAP_INT32(&fAviMainHeader.total_frames); + B_SWAP_INT32(&fAviMainHeader.initial_frames); + B_SWAP_INT32(&fAviMainHeader.streams); + B_SWAP_INT32(&fAviMainHeader.suggested_buffer_size); + B_SWAP_INT32(&fAviMainHeader.width); + B_SWAP_INT32(&fAviMainHeader.height); + #endif + + fAviMainHeaderValid = true; + + TRACE("fAviMainHeader:\n"); + TRACE("micro_sec_per_frame = %lu\n", fAviMainHeader.micro_sec_per_frame); + TRACE("max_bytes_per_sec = %lu\n", fAviMainHeader.max_bytes_per_sec); + TRACE("padding_granularity = %lu\n", fAviMainHeader.padding_granularity); + TRACE("flags = 0x%lx\n", fAviMainHeader.flags); + TRACE("total_frames = %lu\n", fAviMainHeader.total_frames); + TRACE("initial_frames = %lu\n", fAviMainHeader.initial_frames); + TRACE("streams = %lu\n", fAviMainHeader.streams); + TRACE("suggested_buffer_size = %lu\n", fAviMainHeader.suggested_buffer_size); + TRACE("width = %lu\n", fAviMainHeader.width); + TRACE("height = %lu\n", fAviMainHeader.height); +} + +void +OpenDMLParser::ParseChunk_strh(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_strh\n"); + + if (fCurrentStream == 0) { + TRACE("OpenDMLParser::ParseChunk_strh: error, no Stream info\n"); + return; + } + + if (fCurrentStream->stream_header_valid) { + TRACE("OpenDMLParser::ParseChunk_strh: error, already have stream header\n"); + return; + } + + if (size < sizeof(fCurrentStream->stream_header)) { + TRACE("OpenDMLParser::ParseChunk_strh: warning, avi stream header chunk too small\n"); + } + + memset(&fCurrentStream->stream_header, 0, sizeof(fCurrentStream->stream_header)); + + size = min_c(size, sizeof(fCurrentStream->stream_header)); + if ((ssize_t)size != fSource->ReadAt(start, &fCurrentStream->stream_header, size)) { + TRACE("OpenDMLParser::ParseChunk_strh: read error at pos %llu\n", start); + return; + } + + #if B_HOST_IS_BENDIAN + B_SWAP_INT32(&fCurrentStream->stream_header.fourcc_type); + B_SWAP_INT32(&fCurrentStream->stream_header.fourcc_handler); + B_SWAP_INT32(&fCurrentStream->stream_header.flags); + B_SWAP_INT16(&fCurrentStream->stream_header.priority); + B_SWAP_INT16(&fCurrentStream->stream_header.language); + B_SWAP_INT32(&fCurrentStream->stream_header.initial_frames); + B_SWAP_INT32(&fCurrentStream->stream_header.scale); + B_SWAP_INT32(&fCurrentStream->stream_header.rate); + B_SWAP_INT32(&fCurrentStream->stream_header.start); + B_SWAP_INT32(&fCurrentStream->stream_header.length); + B_SWAP_INT32(&fCurrentStream->stream_header.suggested_buffer_size); + B_SWAP_INT32(&fCurrentStream->stream_header.quality); + B_SWAP_INT32(&fCurrentStream->stream_header.sample_size); + B_SWAP_INT16(&fCurrentStream->stream_header.rect_left); + B_SWAP_INT16(&fCurrentStream->stream_header.rect_top); + B_SWAP_INT16(&fCurrentStream->stream_header.rect_right); + B_SWAP_INT16(&fCurrentStream->stream_header.rect_bottom); + #endif + + fCurrentStream->stream_header_valid = true; + fCurrentStream->is_audio = fCurrentStream->stream_header.fourcc_type == FOURCC('a','u','d','s'); + fCurrentStream->is_video = fCurrentStream->stream_header.fourcc_type == FOURCC('v','i','d','s'); + + TRACE("stream_header, Stream %d, is_audio %d, is_video %d:\n", fStreamCount - 1, fCurrentStream->is_audio, fCurrentStream->is_video); + TRACE("fourcc_type = '"FOURCC_FORMAT"'\n",FOURCC_PARAM(fCurrentStream->stream_header.fourcc_type)); + TRACE("fourcc_handler = '"FOURCC_FORMAT"'\n",FOURCC_PARAM(fCurrentStream->stream_header.fourcc_handler)); + TRACE("flags = 0x%lx\n", fCurrentStream->stream_header.flags); + TRACE("priority = %u\n", fCurrentStream->stream_header.priority); + TRACE("language = %u\n", fCurrentStream->stream_header.language); + TRACE("initial_frames = %lu\n", fCurrentStream->stream_header.initial_frames); + TRACE("scale = %lu\n", fCurrentStream->stream_header.scale); + TRACE("rate = %lu\n", fCurrentStream->stream_header.rate); + TRACE("frames/sec = %.3f\n", fCurrentStream->stream_header.rate / (float)fCurrentStream->stream_header.scale); + TRACE("start = %lu\n", fCurrentStream->stream_header.start); + TRACE("length = %lu\n", fCurrentStream->stream_header.length); + TRACE("suggested_buffer_size = %lu\n", fCurrentStream->stream_header.suggested_buffer_size); + TRACE("quality = %lu\n", fCurrentStream->stream_header.quality); + TRACE("sample_size = %lu\n", fCurrentStream->stream_header.sample_size); + TRACE("rect_left = %d\n", fCurrentStream->stream_header.rect_left); + TRACE("rect_top = %d\n", fCurrentStream->stream_header.rect_top); + TRACE("rect_right = %d\n", fCurrentStream->stream_header.rect_right ); + TRACE("rect_bottom = %d\n", fCurrentStream->stream_header.rect_bottom); +} + +void +OpenDMLParser::ParseChunk_strf(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_strf\n"); + + if (fCurrentStream == 0) { + TRACE("OpenDMLParser::ParseChunk_strf: error, no Stream info\n"); + return; + } + + if (fCurrentStream->is_audio) { + + if (fCurrentStream->audio_format_valid) { + TRACE("OpenDMLParser::ParseChunk_strf: error, already have audio format header\n"); + return; + } + +// if (size < sizeof(fCurrentStream->audio_format)) { +// TRACE("OpenDMLParser::ParseChunk_strf: warning, avi audio header chunk too small\n"); +// } + + memset(&fCurrentStream->audio_format, 0, sizeof(fCurrentStream->audio_format)); + + size = min_c(size, sizeof(fCurrentStream->audio_format)); + if ((ssize_t)size != fSource->ReadAt(start, &fCurrentStream->audio_format, size)) { + TRACE("OpenDMLParser::ParseChunk_strf: read error at pos %llu\n", start); + return; + } + + #if B_HOST_IS_BENDIAN + B_SWAP_INT16(&fCurrentStream->audio_format.format_tag); + B_SWAP_INT16(&fCurrentStream->audio_format.channels); + B_SWAP_INT32(&fCurrentStream->audio_format.frames_per_sec); + B_SWAP_INT32(&fCurrentStream->audio_format.avg_bytes_per_sec); + B_SWAP_INT32(&fCurrentStream->audio_format.block_align); + B_SWAP_INT32(&fCurrentStream->audio_format.bits_per_sample); + B_SWAP_INT32(&fCurrentStream->audio_format.extra_size); + #endif + + fCurrentStream->audio_format_valid = true; + + TRACE("audio_format:\n"); + TRACE("format_tag = 0x%x\n", fCurrentStream->audio_format.format_tag); + TRACE("channels = %u\n", fCurrentStream->audio_format.channels); + TRACE("frames_per_sec = %lu\n", fCurrentStream->audio_format.frames_per_sec); + TRACE("avg_bytes_per_sec = %lu\n", fCurrentStream->audio_format.avg_bytes_per_sec); + TRACE("block_align = %u\n", fCurrentStream->audio_format.block_align); + TRACE("bits_per_sample = %u\n", fCurrentStream->audio_format.bits_per_sample); + TRACE("extra_size = %u\n", fCurrentStream->audio_format.extra_size); + + // XXX read extra data + + } else if (fCurrentStream->is_video) { + + if (fCurrentStream->video_format_valid) { + TRACE("OpenDMLParser::ParseChunk_strf: error, already have video format header\n"); + return; + } + +// if (size < sizeof(fCurrentStream->video_format)) { +// TRACE("OpenDMLParser::ParseChunk_strf: warning, avi video header chunk too small\n"); +// } + + memset(&fCurrentStream->video_format, 0, sizeof(fCurrentStream->video_format)); + + size = min_c(size, sizeof(fCurrentStream->video_format)); + if ((ssize_t)size != fSource->ReadAt(start, &fCurrentStream->video_format, size)) { + TRACE("OpenDMLParser::ParseChunk_strf: read error at pos %llu\n", start); + return; + } + + #if B_HOST_IS_BENDIAN + B_SWAP_INT32(&fCurrentStream->video_format.size); + B_SWAP_INT32(&fCurrentStream->video_format.width); + B_SWAP_INT32(&fCurrentStream->video_format.height); + B_SWAP_INT16(&fCurrentStream->video_format.planes); + B_SWAP_INT16(&fCurrentStream->video_format.bit_count); + B_SWAP_INT32(&fCurrentStream->video_format.compression); + B_SWAP_INT32(&fCurrentStream->video_format.image_size); + B_SWAP_INT32(&fCurrentStream->video_format.x_pels_per_meter); + B_SWAP_INT32(&fCurrentStream->video_format.y_pels_per_meter); + B_SWAP_INT32(&fCurrentStream->video_format.clr_used); + B_SWAP_INT32(&fCurrentStream->video_format.clr_important); + #endif + + fCurrentStream->video_format_valid = true; + + TRACE("audio_format:\n"); + TRACE("size = %lu\n", fCurrentStream->video_format.size); + TRACE("width = %lu\n", fCurrentStream->video_format.width); + TRACE("height = %lu\n", fCurrentStream->video_format.height); + TRACE("planes = %u\n", fCurrentStream->video_format.planes); + TRACE("bit_count = %u\n", fCurrentStream->video_format.bit_count); + TRACE("compression = 0x%08lx\n", fCurrentStream->video_format.compression); + TRACE("image_size = %lu\n", fCurrentStream->video_format.image_size); + TRACE("x_pels_per_meter = %lu\n", fCurrentStream->video_format.x_pels_per_meter); + TRACE("y_pels_per_meter = %lu\n", fCurrentStream->video_format.y_pels_per_meter); + TRACE("clr_used = %lu\n", fCurrentStream->video_format.clr_used); + TRACE("clr_important = %lu\n", fCurrentStream->video_format.clr_important); + + } else { + TRACE("OpenDMLParser::ParseChunk_strf: error, unknown Stream type\n"); + } +} + +void +OpenDMLParser::ParseChunk_indx(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_indx\n"); + + if (fCurrentStream == 0) { + TRACE("OpenDMLParser::ParseChunk_indx: error, no stream info\n"); + return; + } + // XXX + fCurrentStream->odml_index_start = start; + fCurrentStream->odml_index_size = size; +} + +void +OpenDMLParser::ParseChunk_dmlh(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseChunk_dmlh\n"); + + if (fOdmlExtendedHeaderValid) { + TRACE("OpenDMLParser::ParseChunk_dmlh: found a second chunk\n"); + return; + } + + if (size < sizeof(fOdmlExtendedHeader)) { + TRACE("OpenDMLParser::ParseChunk_dmlh: warning, avi header chunk too small\n"); + } + + memset(&fOdmlExtendedHeader, 0, sizeof(fOdmlExtendedHeader)); + size = min_c(size, sizeof(fOdmlExtendedHeader)); + if ((ssize_t)size != fSource->ReadAt(start, &fOdmlExtendedHeader, size)) { + TRACE("OpenDMLParser::ParseChunk_dmlh: read error at pos %llu\n", start); + return; + } + + #if B_HOST_IS_BENDIAN + B_SWAP_INT32(&fOdmlExtendedHeader.total_frames); + #endif + + fOdmlExtendedHeaderValid = true; + + TRACE("fOdmlExtendedHeader:\n"); + TRACE("total_frames = %ld\n", fOdmlExtendedHeader.total_frames); +} + +void +OpenDMLParser::ParseList_strl(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseList_strl\n"); + + CreateNewStreamInfo(); + fStreamCount++; + + ParseList_generic(start, size); +} + +void +OpenDMLParser::ParseList_generic(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseList_generic\n"); + uint64 pos = start; + uint64 end = start + size; + + while (pos < end) { + uint32 temp; + uint32 Chunkfcc; + uint32 Chunksize; + + if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::ParseList_generic: read error at pos %llu\n",pos); + return; + } + pos += 4; + Chunkfcc = AVI_UINT32(temp); + + if (sizeof(temp) != fSource->ReadAt(pos, &temp, sizeof(temp))) { + TRACE("OpenDMLParser::ParseList_generic: read error at pos %llu\n",pos); + return; + } + pos += 4; + Chunksize = AVI_UINT32(temp); + + TRACE("OpenDMLParser::ParseList_generic: chunk '"FOURCC_FORMAT"', size = %ld\n", FOURCC_PARAM(Chunkfcc), Chunksize); + + if (Chunkfcc == FOURCC('J','U','N','K')) + goto cont; + else if (Chunkfcc == FOURCC('a','v','i','h')) + ParseChunk_avih(pos, Chunksize); + else if (Chunkfcc == FOURCC('L','I','S','T')) + ParseChunk_LIST(pos, Chunksize); + else if (Chunkfcc == FOURCC('s','t','r','h')) + ParseChunk_strh(pos, Chunksize); + else if (Chunkfcc == FOURCC('s','t','r','f')) + ParseChunk_strf(pos, Chunksize); + else if (Chunkfcc == FOURCC('i','n','d','x')) + ParseChunk_indx(pos, Chunksize); + else if (Chunkfcc == FOURCC('d','m','l','h')) + ParseChunk_dmlh(pos, Chunksize); + else + TRACE("OpenDMLParser::ParseList_generic: unknown chunk ignored\n"); + +cont: + pos += (Chunksize) + (Chunksize & 1); + } +} + +void +OpenDMLParser::ParseList_movi(uint64 start, uint32 size) +{ + TRACE("OpenDMLParser::ParseList_movi\n"); + + fMovieChunkCount++; + return; +} + diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h new file mode 100644 index 0000000000..9dc4938910 --- /dev/null +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h @@ -0,0 +1,74 @@ +#ifndef _OPENDML_PARSER_H +#define _OPENDML_PARSER_H + +#include +#include "avi.h" + +struct stream_info +{ + stream_info * next; + bool is_audio; + bool is_video; + bool stream_header_valid; + avi_stream_header stream_header; + bool audio_format_valid; + wave_format_ex audio_format; + bool video_format_valid; + bitmap_info_header video_format; + int64 odml_index_start; + uint32 odml_index_size; +}; + +class OpenDMLParser +{ +public: + OpenDMLParser(); + ~OpenDMLParser(); + void Parse(BPositionIO *source); + + int StreamCount(); + + const stream_info * StreamInfo(int index); + + int64 StandardIndexStart(); + uint32 StandardIndexSize(); + + const avi_main_header * AviMainHeader(); + const odml_extended_header * OdmlExtendedHeader(); + +private: + void ParseChunk_AVI(int number, uint64 start, uint32 size); + void ParseChunk_LIST(uint64 start, uint32 size); + void ParseChunk_idx1(uint64 start, uint32 size); + void ParseChunk_indx(uint64 start, uint32 size); + void ParseChunk_avih(uint64 start, uint32 size); + void ParseChunk_strh(uint64 start, uint32 size); + void ParseChunk_strf(uint64 start, uint32 size); + void ParseChunk_dmlh(uint64 start, uint32 size); + void ParseList_movi(uint64 start, uint32 size); + void ParseList_generic(uint64 start, uint32 size); + void ParseList_strl(uint64 start, uint32 size); + +private: + void CreateNewStreamInfo(); + + BPositionIO * fSource; + int64 fSize; + + int64 fStandardIndexStart; + uint32 fStandardIndexSize; + + int fStreamCount; + int fMovieChunkCount; + + avi_main_header fAviMainHeader; + bool fAviMainHeaderValid; + + odml_extended_header fOdmlExtendedHeader; + bool fOdmlExtendedHeaderValid; + + stream_info * fStreams; + stream_info * fCurrentStream; +}; + +#endif // _OPENDML_PARSER_H diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h new file mode 100644 index 0000000000..57c1ffee99 --- /dev/null +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h @@ -0,0 +1,144 @@ +#ifndef _AVI_H +#define _AVI_H + +#include + +#define AVI_UINT32(a) ((uint32)B_LENDIAN_TO_HOST_INT32(a)) + +#define FOURCC(a,b,c,d) ((((uint32)(d)) << 24) | (((uint32)(c)) << 16) | (((uint32)(b)) << 8) | ((uint32)(a))) +#define FOURCC_FORMAT "%c%c%c%c" +#define FOURCC_PARAM(f) (int)(f & 0xff),(int)((f >> 8) & 0xff),(int)((f >> 16) & 0xff),(int)((f >> 24) & 0xff) + +struct avi_main_header +{ + uint32 micro_sec_per_frame; + uint32 max_bytes_per_sec; + uint32 padding_granularity; + uint32 flags; + uint32 total_frames; + uint32 initial_frames; + uint32 streams; + uint32 suggested_buffer_size; + uint32 width; + uint32 height; + uint32 reserved[4]; +} _PACKED; + +struct avi_standard_index_entry +{ + uint32 chunk_id; + uint32 flags; + uint32 chunk_offset; + uint32 chunk_length; +} _PACKED; + +struct odml_extended_header +{ + uint32 total_frames; +} _PACKED; + +struct avi_stream_header +{ + uint32 fourcc_type; + uint32 fourcc_handler; + uint32 flags; + uint16 priority; + uint16 language; + uint32 initial_frames; + uint32 scale; + uint32 rate; + uint32 start; + uint32 length; + uint32 suggested_buffer_size; + uint32 quality; + uint32 sample_size; + int16 rect_left; + int16 rect_top; + int16 rect_right; + int16 rect_bottom; +} _PACKED; + +struct bitmap_info_header +{ + uint32 size; + uint32 width; + uint32 height; + uint16 planes; + uint16 bit_count; + uint32 compression; + uint32 image_size; + uint32 x_pels_per_meter; + uint32 y_pels_per_meter; + uint32 clr_used; + uint32 clr_important; +} _PACKED; + +struct wave_format_ex +{ + uint16 format_tag; + uint16 channels; + uint32 frames_per_sec; + uint32 avg_bytes_per_sec; + uint16 block_align; + uint16 bits_per_sample; + uint16 extra_size; + // char extra_data[extra_size] +} _PACKED; + +struct odml_index_header +{ + uint16 longs_per_entry; + uint8 index_sub_type; + uint8 index_type; + uint32 entries_used; + uint32 chunk_id; + uint32 reserved[3]; + // index entries go here +} _PACKED; + +struct odml_chunk_index_header // also field index +{ + uint16 longs_per_entry; + uint8 index_sub_type; + uint8 index_type; + uint32 entries_used; + uint32 chunk_id; + uint64 base_offset; + uint32 reserved; + // index entries go here +} _PACKED; +typedef odml_chunk_index_header odml_field_index_header; + + + +// index_type codes +#define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex + // array points to an index chunk +#define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex array + // points to a chunk in the file +#define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is really the data + +// index_sub_type codes for INDEX_OF_CHUNKS +#define AVI_INDEX_2FIELD 0x01 // when fields within frames are also indexed + +struct odml_superindex_entry +{ + uint64 start; + uint32 size; + uint32 duration; +}; + +struct odml_index_entry +{ + uint32 start; + uint32 size; // bit 31 set if not keyframe +}; + +struct odml_field_index_entry +{ + uint32 start; + uint32 size; // bit 31 set if not keyframe + uint32 start_field2; +}; + +#endif // _AVI_H