From 201970d309c260bc998325566babdfc75e555725 Mon Sep 17 00:00:00 2001 From: David McPaul Date: Tue, 24 Feb 2009 08:12:27 +0000 Subject: [PATCH] First cut at improving avi seeking git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29308 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../media/plugins/avi_reader/avi_reader.cpp | 78 ++++-- .../avi_reader/libOpenDML/FallbackIndex.cpp | 130 +++++++-- .../plugins/avi_reader/libOpenDML/Index.cpp | 169 ++++++++++- .../plugins/avi_reader/libOpenDML/Index.h | 51 +++- .../avi_reader/libOpenDML/OpenDMLFile.cpp | 8 +- .../avi_reader/libOpenDML/OpenDMLFile.h | 4 +- .../avi_reader/libOpenDML/OpenDMLIndex.cpp | 18 -- .../avi_reader/libOpenDML/OpenDMLIndex.h | 5 - .../avi_reader/libOpenDML/OpenDMLParser.cpp | 111 ++++---- .../avi_reader/libOpenDML/OpenDMLParser.h | 30 +- .../avi_reader/libOpenDML/StandardIndex.cpp | 264 ++++-------------- .../avi_reader/libOpenDML/StandardIndex.h | 28 -- 12 files changed, 526 insertions(+), 370 deletions(-) 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 c0bdc3fc22..91bfb71977 100644 --- a/src/add-ons/media/plugins/avi_reader/avi_reader.cpp +++ b/src/add-ons/media/plugins/avi_reader/avi_reader.cpp @@ -65,6 +65,13 @@ struct avi_cookie // video only: uint32 line_count; + + // audio only: + uint32 sample_size; + uint32 frame_size; + int64 byte_pos; + uint16 bytes_per_second; + bool is_vbr; }; @@ -150,6 +157,9 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) cookie->buffer_size = 0; cookie->is_audio = false; cookie->is_video = false; + cookie->byte_pos = 0; + cookie->is_vbr = false; + cookie->bytes_per_second = 0; BMediaFormats formats; media_format *format = &cookie->format; @@ -182,6 +192,9 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) TRACE("audio frame_count %Ld, duration %.6f\n", cookie->frame_count, cookie->duration / 1E6); + cookie->bytes_per_second = audio_format->avg_bytes_per_sec; + cookie->sample_size = stream_header->sample_size == 0 ? audio_format->bits_per_sample / 8 * audio_format->channels : stream_header->sample_size; + if (audio_format->format_tag == 0x0001) { // a raw PCM format description.family = B_BEOS_FORMAT_FAMILY; @@ -205,6 +218,7 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) 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; + cookie->frame_size = cookie->sample_size; } else { // some encoded format description.family = B_WAV_FORMAT_FAMILY; @@ -214,11 +228,18 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) format->u.encoded_audio.bit_rate = 8 * audio_format->avg_bytes_per_sec; format->u.encoded_audio.output.frame_rate = audio_format->frames_per_sec; format->u.encoded_audio.output.channel_count = audio_format->channels; - TRACE("audio: bit_rate %.3f, frame_rate %.1f, channel_count %lu\n", + cookie->frame_size = audio_format->block_align == 0 ? 1 : audio_format->block_align; + + // detect vbr audio in avi hack + cookie->is_vbr = cookie->frame_size >= 960; + + TRACE("audio: bit_rate %.3f, frame_rate %.1f, channel_count %lu, frame_size %ld, is vbr %s\n", format->u.encoded_audio.bit_rate, format->u.encoded_audio.output.frame_rate, - format->u.encoded_audio.output.channel_count); + format->u.encoded_audio.output.channel_count, + cookie->frame_size, cookie->is_vbr ? "true" : "false"); } + // TODO: this doesn't seem to work (it's not even a fourcc) format->user_data_type = B_CODEC_TYPE_INFO; *(uint32 *)format->user_data = audio_format->format_tag; format->user_data[4] = 0; @@ -252,6 +273,7 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) cookie->frames_per_sec_rate = fFile->StreamInfo(streamNumber)->frames_per_sec_rate; cookie->frames_per_sec_scale = fFile->StreamInfo(streamNumber)->frames_per_sec_scale; cookie->line_count = fFile->AviMainHeader()->height; + cookie->frame_size = 1; TRACE("video frame_count %Ld, duration %.6f\n", cookie->frame_count, cookie->duration / 1E6); @@ -338,9 +360,10 @@ aviReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration, status_t aviReader::Seek(void *_cookie, uint32 seekTo, int64 *frame, bigtime_t *time) { + // Seek changes the position of the stream avi_cookie *cookie = (avi_cookie *)_cookie; - TRACE("aviReader::Seek: stream %d, seekTo%s%s%s%s, time %Ld, frame %Ld\n", + TRACE("aviReader::Seek: stream %d, seekTo%s%s%s%s, time %.6f, frame %Ld\n", cookie->stream, (seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", (seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", @@ -348,25 +371,29 @@ aviReader::Seek(void *_cookie, uint32 seekTo, int64 *frame, bigtime_t *time) " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", - *time, *frame); + *time / 1000000.0, *frame); status_t rv = fFile->Seek(cookie->stream, seekTo, frame, time, false); if (rv == B_OK) { cookie->frame_pos = *frame; - TRACE("aviReader::Seek: stream %d, success, setting frame_pos " - "to %lld\n", cookie->stream, cookie->frame_pos); + if (cookie->is_audio && !cookie->is_vbr) { + // calculate byte_pos from time + cookie->byte_pos = *time * cookie->bytes_per_second / 1000000LL; + } + TRACE("aviReader::Seek: stream %d, success, frame_pos = %Ld, time = %.6f\n", cookie->stream, cookie->frame_pos, *time / 1000000.0); } + return rv; } status_t -aviReader::FindKeyFrame(void *_cookie, uint32 flags, int64 *frame, - bigtime_t *time) +aviReader::FindKeyFrame(void *_cookie, uint32 flags, int64 *frame, bigtime_t *time) { + // FindKeyFrame does not change the position of the stream avi_cookie *cookie = (avi_cookie *)_cookie; - TRACE("aviReader::FindKeyFrame: stream %d, flags%s%s%s%s, time %Ld, " + TRACE("aviReader::FindKeyFrame: stream %d, flags%s%s%s%s, time %.6f, " "frame %Ld\n", cookie->stream, (flags & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", @@ -375,12 +402,11 @@ aviReader::FindKeyFrame(void *_cookie, uint32 flags, int64 *frame, " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", (flags & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", - *time, *frame); + *time / 1000000.0, *frame); status_t rv = fFile->Seek(cookie->stream, flags, frame, time, true); if (rv == B_OK) { - TRACE("aviReader::FindKeyFrame: stream %d, success\n", - cookie->stream); + TRACE("aviReader::FindKeyFrame: stream %d, success\n", cookie->stream); } return rv; } @@ -393,6 +419,7 @@ aviReader::GetNextChunk(void *_cookie, const void **chunkBuffer, avi_cookie *cookie = (avi_cookie *)_cookie; int64 start; uint32 size; bool keyframe; + if (fFile->GetNextChunkInfo(cookie->stream, &start, &size, &keyframe) < B_OK) return B_LAST_BUFFER_ERROR; @@ -412,30 +439,41 @@ aviReader::GetNextChunk(void *_cookie, const void **chunkBuffer, } } - mediaHeader->start_time = (cookie->frame_pos * 1000000 + mediaHeader->start_time = (cookie->frame_pos * 1000000LL * cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate; + TRACE("stream %d (%s): start_time %.6f, pos %.3f %%, frame %Ld chunk size %ld\n", + cookie->stream, cookie->is_audio ? "A" : cookie->is_video ? "V" : "?", + mediaHeader->start_time / 1000000.0, cookie->frame_pos * 100.0 + / cookie->frame_count, cookie->frame_pos, size); + if (cookie->is_audio) { mediaHeader->type = B_MEDIA_ENCODED_AUDIO; mediaHeader->u.encoded_audio.buffer_flags = keyframe ? B_MEDIA_KEY_FRAME : 0; - cookie->frame_pos += size; + + cookie->frame_pos += (uint64)(ceil((double)size / (double)cookie->frame_size)) * cookie->frames_per_sec_scale; + cookie->byte_pos += size; + // frame_pos is sample no for vbr encoded audio and byte position for everything else +// if (cookie->is_vbr) { + // advance by frame_size +// } else { +// cookie->frame_pos += (uint64)(ceil((double)size / (double)cookie->frame_size)) * cookie->frames_per_sec / cookie->avg_bytes_per_sec; + // advance by bytes in chunk and calculate frame_pos +// time = cookie->byte_pos * 1000000LL / cookie->bytes_per_second; +// cookie->frame_pos = time * cookie->frames_per_sec_rate / cookie->frames_per_sec_scale / 1000000LL; +// } } else if (cookie->is_video) { mediaHeader->type = B_MEDIA_ENCODED_VIDEO; mediaHeader->u.encoded_video.field_flags = keyframe ? B_MEDIA_KEY_FRAME : 0; mediaHeader->u.encoded_video.first_active_line = 0; mediaHeader->u.encoded_video.line_count = cookie->line_count; - cookie->frame_pos += 1; + cookie->frame_pos += cookie->frame_size; } else { return B_BAD_VALUE; } -// TRACE("stream %d (%s): start_time %.6f, pos %.3f %%\n", -// cookie->stream, cookie->is_audio ? "A" : cookie->is_video ? "V" : "?", -// mediaHeader->start_time / 1000000.0, cookie->frame_pos * 100.0 -// / cookie->frame_count); - *chunkBuffer = cookie->buffer; *chunkSize = size; return (int)size == fFile->Source()->ReadAt(start, cookie->buffer, size) ? diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/FallbackIndex.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/FallbackIndex.cpp index c359a53c2f..60080064db 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/FallbackIndex.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/FallbackIndex.cpp @@ -23,9 +23,24 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include #include "FallbackIndex.h" +#include "OpenDMLParser.h" +//#define TRACE_START_INDEX +#ifdef TRACE_START_INDEX + #define TRACE printf +#else + #define TRACE(a...) +#endif + +#define ERROR(a...) fprintf(stderr, a) + +struct chunk { + uint32 chunk_id; + uint32 size; +}; FallbackIndex::FallbackIndex(BPositionIO *source, OpenDMLParser *parser) : Index(source, parser) @@ -41,22 +56,101 @@ FallbackIndex::~FallbackIndex() status_t FallbackIndex::Init() { - return B_ERROR; + // Attempt to build an index by parsing the movi chunk + + bool end_of_movi = false; + chunk aChunk; + + TRACE("Building Fallback index\n"); + + int stream_index; + off_t position; + uint32 size; + uint64 frame[fStreamCount]; + uint64 frame_no; + bigtime_t pts = 0; + bool keyframe = false; + uint32 sample_size; + uint64 entries = 0; + + const OpenDMLStream *stream; + + for (uint32 i=0;i < fStreamCount; i++) { + frame[i] = 0; + } + + position = fParser->MovieListStart(); + + while (end_of_movi == false) { + + if ((int32)8 != fSource->ReadAt(position, &aChunk, 8)) { + ERROR("libOpenDML: FallbackIndex::Init file reading failed\n"); + return B_IO_ERROR; + } + + position += 8; + + stream_index = ((aChunk.chunk_id & 0xff) - '0') * 10; + stream_index += ((aChunk.chunk_id >> 8) & 0xff) - '0'; + + if ((stream_index < 0) || (stream_index >= fStreamCount)) { + if (entries == 0) { + ERROR("libOpenDML: FallbackIndex::Init - Failed to build an index, file is too corrupt\n"); + return B_IO_ERROR; + } else { + ERROR("libOpenDML: FallbackIndex::Init - Error while trying to build index, file is corrupt but will continue after creating %Ld entries in index\n",entries); + return B_OK; + } + } + + entries++; + + stream = fParser->StreamInfo(stream_index); + + size = aChunk.size; + frame_no = frame[stream_index]; + + if (stream->is_video) { + // Video is easy enough, it is always 1 frame = 1 index + pts = frame[stream_index] * 1000000LL * stream->frames_per_sec_scale / stream->frames_per_sec_rate; + frame[stream_index]++; + } else if (stream->is_audio) { + pts = frame[stream_index] * 1000000LL / stream->audio_format->frames_per_sec; + + // Audio varies based on many different hacks over the years + // The simplest is chunk size / sample size = no of samples in the chunk for uncompressed audio + // ABR Compressed audio is more difficult and VBR Compressed audio is even harder + // What follows is what I have worked out from various sources across the internet. + if (stream->audio_format->format_tag != 0x0001) { + // VBR audio is detected as having a block_align >= 960 + if (stream->audio_format->block_align >= 960) { + // VBR Audio so block_align is the largest no of samples in a chunk + // scale is the no of samples in a frame + // rate is the sample rate + // but we must round up when calculating no of frames in a chunk + frame[stream_index] += (uint64)(ceil((double)size / (double)stream->audio_format->block_align)) * stream->frames_per_sec_scale; + } else { + // ABR Audio so use Chunk Size and avergae bytes per second to determine how many samples there are in the chunk + frame[stream_index] += (uint64)(ceil((double)size / (double)stream->audio_format->block_align)) * stream->audio_format->frames_per_sec / stream->audio_format->avg_bytes_per_sec; + } + } else { + // sample size can be corrupt + if (stream->stream_header.sample_size > 0) { + sample_size = stream->stream_header.sample_size; + } else { + // compute sample size + sample_size = stream->audio_format->bits_per_sample * stream->audio_format->channels / 8; + } + frame[stream_index] += size / stream->stream_header.sample_size; + } + } + + AddIndex(stream_index, position, size, frame_no, pts, keyframe); + + position += aChunk.size; + + end_of_movi = position >= fParser->MovieListSize(); + } + + return B_OK; } - - -status_t -FallbackIndex::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, - bool *keyframe) -{ - return B_ERROR; -} - - -status_t -FallbackIndex::Seek(int stream_index, uint32 seekTo, int64 *frame, - bigtime_t *time, bool readOnly) -{ - return B_ERROR; -} - diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.cpp index 9880cd15fd..56a1a805ed 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.cpp @@ -24,15 +24,182 @@ */ #include #include +#include "ReaderPlugin.h" // B_MEDIA_* #include "Index.h" +#include "OpenDMLParser.h" + +//#define TRACE_INDEX +#ifdef TRACE_INDEX + #define TRACE printf +#else + #define TRACE(a...) +#endif + +#define ERROR(a...) fprintf(stderr, a) Index::Index(BPositionIO *source, OpenDMLParser *parser) : fSource(source) , fParser(parser) { + fStreamCount = parser->StreamCount(); + fStreamData.resize(fStreamCount); } - Index::~Index() { + fStreamData.clear(); } + +status_t +Index::GetNextChunkInfo(int stream_index, off_t *start, + uint32 *size, bool *keyframe) { + MediaStream *data = &fStreamData[stream_index]; + + if (data->current_chunk < data->seek_index_next) { + *keyframe = data->seek_index[data->current_chunk].keyframe; + // skip 8 bytes (chunk id + chunk size) + *start = data->seek_index[data->current_chunk].position + 8; + *size = data->seek_index[data->current_chunk].size; + data->current_chunk++; + return B_OK; + } + + data->current_chunk = 0; + + return B_LAST_BUFFER_ERROR; // should this be end of chunk? +} + +status_t +Index::Seek(int stream_index, uint32 seekTo, int64 *frame, + bigtime_t *time, bool readOnly) { + TRACE("Index::Seek: stream %d, seekTo%s%s%s%s, time %Ld, " + "frame %Ld\n", stream_index, + (seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", + (seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", + (seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ? + " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", + (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? + " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", + *time, *frame); + + const OpenDMLStream *stream = fParser->StreamInfo(stream_index); + MediaStream *data = &fStreamData[stream_index]; + + int64 frame_pos; + if (seekTo & B_MEDIA_SEEK_TO_FRAME) + frame_pos = *frame; + else if (seekTo & B_MEDIA_SEEK_TO_TIME) { + frame_pos = (*time * stream->frames_per_sec_rate) + / (1000000LL * stream->frames_per_sec_scale); + } else + return B_BAD_VALUE; + + if (stream->is_audio) { + // frame_pos is sample no for audio + // Scan index for the chunk that contains the sample no asked for + for (uint32 i = 1; i < data->seek_index_next; i++) { + if (data->seek_index[i].frame_no > frame_pos) { + // previous chunk contains the frame we wanted + // we can only seek to chunk boundaries + frame_pos = data->seek_index[i-1].frame_no; + if (!readOnly) { + data->current_chunk = i-1; // position file to start of chunk + } + goto done; + } + + if (i+1 == data->seek_index_next) { + // Last chunk + frame_pos = data->seek_index[i].frame_no; + if (!readOnly) { + data->current_chunk = i; // position file to start of chunk + } + goto done; + } + } + } else if (stream->is_video) { + // iterate over all index entries of the stream, + // there is one entry per frame (TODO: actually one per field, + // if I interprete the documentation correctly...) + int64 pos = 0; + int64 lastKeyframePos = 0; + int64 lastKeyframeIndex = 0; + for (uint32 i = 0; i < data->seek_index_next; i++) { + // remember the last known keyframe index/frame + if (data->seek_index[i].keyframe) { + lastKeyframePos = pos; + lastKeyframeIndex = i; + TRACE("keyframe at index %ld, frame %ld (seek: %ld)\n", i, + pos, frame_pos); + } + + if (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) { + // use the index and frame of the last keyframe + if (pos == frame_pos) { + frame_pos = lastKeyframePos; + if (!readOnly) + data->current_chunk = lastKeyframeIndex; + goto done; + } + } else if (seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) { + // use the index and frame of the last keyframe + // if this frame is a keyframe and we at or past + // the seek position + if (pos >= frame_pos && pos == lastKeyframePos) { + frame_pos = lastKeyframePos; + if (!readOnly) + data->current_chunk = lastKeyframeIndex; + goto done; + } + } else { + // ignore keyframes + if (pos == frame_pos) { + if (!readOnly) + data->current_chunk = i; + goto done; + } + } + pos++; + } + } else { + return B_BAD_VALUE; + } + + ERROR("libOpenDML: seek failed, position not found\n"); + return B_ERROR; + +done: + TRACE("seek done: index: pos %d\n", data->current_chunk); + // recalculate frame and time after seek + *frame = frame_pos; + *time = (frame_pos * 1000000LL * stream->frames_per_sec_scale) + / stream->frames_per_sec_rate; + return B_OK; +} + +void +Index::AddIndex(int stream_index, off_t position, uint32 size, uint64 frame, bigtime_t pts, bool keyframe) { + + IndexEntry seek_index; + + // Should be in a constructor + seek_index.frame_no = frame; + seek_index.position = position; + seek_index.size = size; + seek_index.pts = pts; + seek_index.keyframe = keyframe; + + fStreamData[stream_index].seek_index.push_back(seek_index); + fStreamData[stream_index].seek_index_next++; +} + +void +Index::DumpIndex(int stream_index) +{ + IndexEntry _index; + for (uint32 i = 0; i < fStreamData[stream_index].seek_index_next; i++) { + _index = fStreamData[stream_index].seek_index[i]; + printf("Frame %Ld, pos %Ld, size %ld, pts %Ld\n",_index.frame_no, _index.position, _index.size, _index.pts); + } +} + diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.h index c52da9cd32..87a4674fce 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/Index.h @@ -25,12 +25,45 @@ #ifndef _INDEX_H #define _INDEX_H - #include +#include + +/* + This class handles all indexing of an AVI file + Subclasses should override Init and create index entries based on the + specialised index. + + Seek and GetNextChunk will then work. + + Current known subclasses are: + Standard Index - Original AVI index idx1 + OpenDMLIndex - Open DML Standard Index + FallBackIndex - Index created from the movi chunk +*/ + class BPositionIO; class OpenDMLParser; +class IndexEntry { +public: + IndexEntry() {frame_no = 0;position=0;size=0;pts=0;keyframe=false;}; + + uint64 frame_no; // frame_no or sample_no + off_t position; // The offset in the stream where the frame is + uint32 size; // The size of the data available + bigtime_t pts; // Presentation Time Stamp for this frame + bool keyframe; // Is this a keyframe. +}; + +class MediaStream { +public: + MediaStream() {seek_index_next=0;current_chunk=0;} ; + ~MediaStream() {seek_index.clear();}; + std::vector seek_index; + uint64 seek_index_next; + uint64 current_chunk; +}; class Index { public: @@ -39,14 +72,22 @@ public: virtual status_t Init() = 0; - virtual status_t GetNextChunkInfo(int stream_index, int64 *start, - uint32 *size, bool *keyframe) = 0; - virtual status_t Seek(int stream_index, uint32 seekTo, int64 *frame, - bigtime_t *time, bool readOnly) = 0; + status_t GetNextChunkInfo(int stream_index, off_t *start, + uint32 *size, bool *keyframe); + + status_t Seek(int stream_index, uint32 seekTo, int64 *frame, + bigtime_t *time, bool readOnly); + + void AddIndex(int stream_index, off_t position, uint32 size, uint64 frame, bigtime_t pts, bool keyframe); + void DumpIndex(int stream_index); protected: BPositionIO * fSource; OpenDMLParser * fParser; + int fStreamCount; + +private: + std::vector fStreamData; }; #endif // _INDEX_H diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp index 03b7e60c1d..e8dd931d29 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp @@ -108,6 +108,7 @@ OpenDMLFile::Init() fIndex = NULL; } } + if (!fIndex && fParser->StandardIndexSize() != 0) { fIndex = new StandardIndex(fSource, fParser); if (fIndex->Init() < B_OK) { @@ -115,6 +116,7 @@ OpenDMLFile::Init() fIndex = NULL; } } + if (!fIndex) { fIndex = new FallbackIndex(fSource, fParser); if (fIndex->Init() < B_OK) { @@ -125,6 +127,8 @@ OpenDMLFile::Init() } } +// fIndex->DumpIndex(1); + TRACE("OpenDMLFile::SetTo: this is a %s AVI file with %d streams\n", fParser->OdmlExtendedHeader() ? "OpenDML" : "standard", fParser->StreamCount()); return B_OK; @@ -385,7 +389,7 @@ OpenDMLFile::AviGetNextChunkInfo(int stream_index, int64 *start, uint32 *size, b status_t -OpenDMLFile::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, +OpenDMLFile::GetNextChunkInfo(int stream_index, off_t *start, uint32 *size, bool *keyframe) { return fIndex->GetNextChunkInfo(stream_index, start, size, keyframe); @@ -470,7 +474,7 @@ OpenDMLFile::StreamFormat(int stream_index) &fParser->StreamInfo(stream_index)->stream_header : 0; } -const stream_info * +const OpenDMLStream * OpenDMLFile::StreamInfo(int index) { return fParser->StreamInfo(index); diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h index 3f3f2cb6b2..754ef22d9e 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h @@ -41,7 +41,7 @@ public: int StreamCount(); - const stream_info * StreamInfo(int index); + const OpenDMLStream * StreamInfo(int index); /* bigtime_t Duration(); @@ -57,7 +57,7 @@ public: const bitmap_info_header * VideoFormat(int stream_index); const avi_stream_header * StreamFormat(int stream_index); - status_t GetNextChunkInfo(int stream_index, int64 *start, + status_t GetNextChunkInfo(int stream_index, off_t *start, uint32 *size, bool *keyframe); status_t Seek(int stream_index, uint32 seekTo, int64 *frame, bigtime_t *time, bool readOnly); diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.cpp index ebcf994a10..a7f118788f 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.cpp @@ -43,21 +43,3 @@ OpenDMLIndex::Init() { return B_ERROR; } - - -status_t -OpenDMLIndex::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, - bool *keyframe) -{ - return B_ERROR; -} - - -status_t -OpenDMLIndex::Seek(int stream_index, uint32 seekTo, int64 *frame, - bigtime_t *time, bool readOnly) -{ - return B_ERROR; -} - - diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.h index 951fd042a7..c6f5225bf5 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLIndex.h @@ -34,11 +34,6 @@ public: ~OpenDMLIndex(); status_t Init(); - - status_t GetNextChunkInfo(int stream_index, int64* start, - uint32* size, bool* keyframe); - status_t Seek(int stream_index, uint32 seekTo, int64* frame, - bigtime_t* time, bool readOnly); }; #endif diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp index dd1208ecc3..536502419e 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp @@ -27,7 +27,7 @@ #include "OpenDMLParser.h" #include "avi.h" -//#define TRACE_ODML_PARSER +#define TRACE_ODML_PARSER #ifdef TRACE_ODML_PARSER #define TRACE printf #else @@ -68,16 +68,13 @@ OpenDMLParser::StreamCount() return fStreamCount; } -const stream_info * +const OpenDMLStream * OpenDMLParser::StreamInfo(int index) { if (index < 0 || index >= fStreamCount) return NULL; - stream_info *info = fStreams; - while (index--) - info = info->next; - return info; + return &fStreams[index]; } int64 @@ -113,30 +110,23 @@ OpenDMLParser::OdmlExtendedHeader() 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 = 0; - info->video_format_valid = false; - info->odml_index_start = 0; - info->odml_index_size = 0; - info->duration = 0; - info->frame_count = 0; - info->frames_per_sec_rate = 1; - info->frames_per_sec_scale = 1; + OpenDMLStream info; + + info.is_audio = false; + info.is_video = false; + info.stream_header_valid = false; + info.audio_format = 0; + info.video_format_valid = false; + info.odml_index_start = 0; + info.odml_index_size = 0; + info.duration = 0; + info.frame_count = 0; + info.frames_per_sec_rate = 1; + info.frames_per_sec_scale = 1; - // 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; + fStreams.push_back(info); + + fCurrentStream = fStreams.last(); } status_t @@ -176,13 +166,13 @@ OpenDMLParser::Init() #endif for (int i = 0; i < fStreamCount; i++) { - SetupStreamLength(const_cast(StreamInfo(i))); + SetupStreamLength(const_cast(StreamInfo(i))); } return B_OK; } void -OpenDMLParser::SetupStreamLength(stream_info *stream) +OpenDMLParser::SetupStreamLength(OpenDMLStream *stream) { if (stream->is_audio) SetupAudioStreamLength(stream); @@ -193,7 +183,7 @@ OpenDMLParser::SetupStreamLength(stream_info *stream) // F:\avi-info\Information on AVI file.htm void -OpenDMLParser::SetupAudioStreamLength(stream_info *stream) +OpenDMLParser::SetupAudioStreamLength(OpenDMLStream *stream) { stream->frame_count = stream->stream_header.length; @@ -201,43 +191,50 @@ OpenDMLParser::SetupAudioStreamLength(stream_info *stream) && stream->stream_header.sample_size != 0 && stream->stream_header.sample_size != 1) { // PCM stream->frame_count /= (stream->stream_header.sample_size + 7) / 8; - TRACE("audio: messing up PCM frame_count?\n"); + TRACE("audio: messed up PCM frame_count?\n"); } if (stream->stream_header.rate && stream->stream_header.scale) { stream->frames_per_sec_rate = stream->stream_header.rate; stream->frames_per_sec_scale = stream->stream_header.scale; - stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000) / stream->frames_per_sec_rate; - TRACE("audio: using rate+scale\n"); + stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000LL) / stream->frames_per_sec_rate; + TRACE("audio: duration calculated using rate+scale\n"); } else if (stream->audio_format->avg_bytes_per_sec) { stream->frames_per_sec_rate = stream->audio_format->avg_bytes_per_sec; stream->frames_per_sec_scale = 1; - stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000) / stream->frames_per_sec_rate; - TRACE("audio: using avg_bytes_per_sec\n"); + stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000LL) / stream->frames_per_sec_rate; + TRACE("audio: duration calculated using avg_bytes_per_sec\n"); } else if (AviMainHeader()->micro_sec_per_frame) { uint32 video_frame_count = OdmlExtendedHeader() ? OdmlExtendedHeader()->total_frames : AviMainHeader()->total_frames; stream->duration = video_frame_count * AviMainHeader()->micro_sec_per_frame; - stream->frames_per_sec_rate = (stream->frame_count * 1000 * 1000000) / stream->duration; + stream->frames_per_sec_rate = (stream->frame_count * 1000 * 1000000LL) / stream->duration; stream->frames_per_sec_scale = 1000; - TRACE("audio: using micro_sec_per_frame\n"); + TRACE("audio: duration calculated using micro_sec_per_frame\n"); } else { - TRACE("audio: no idea what to do\n"); + TRACE("audio: duration could not be calculated no idea what to do\n"); } - if (stream->audio_format->avg_bytes_per_sec) { - int64 expectedFrameCount - = (stream->duration * stream->audio_format->avg_bytes_per_sec) - / 1000000; - TRACE("audio: expected frame_count %lld, calculated stream " - "frame_count %lld\n", expectedFrameCount, stream->frame_count); - if (expectedFrameCount * 9 > stream->frame_count * 10) { - TRACE("audio: something is wrong, ignoring calculated stream " - "frame_count, rate and scale\n"); - stream->frame_count = expectedFrameCount; - stream->frames_per_sec_rate - = stream->audio_format->avg_bytes_per_sec; + // The stream details are often wrong, if there is a audio format structure we attempt to use that + if (stream->audio_format->format_tag == 0x0001) { // RAW PCM + if (stream->audio_format->avg_bytes_per_sec) { + int64 expectedFrameCount + = (stream->duration * stream->audio_format->avg_bytes_per_sec) + / 1000000; + TRACE("audio: expected frame_count %lld, stream " + "frame_count %lld\n", expectedFrameCount, stream->frame_count); + if (expectedFrameCount * 9 > stream->frame_count * 10) { + TRACE("audio: something is wrong, ignoring stream frame_count\n"); + stream->frame_count = expectedFrameCount; + } + + stream->frames_per_sec_rate = stream->audio_format->avg_bytes_per_sec; stream->frames_per_sec_scale = 1; } + } else { + // encoded format usually has audio format details correct so always use them +// stream->frames_per_sec_rate = stream->audio_format->frames_per_sec; +// stream->frames_per_sec_scale = 1; + stream->frame_count = stream->duration * stream->audio_format->frames_per_sec / 1000000LL; } TRACE("audio: frame_count %lld, duration %.6f, fps %.3f\n", @@ -246,7 +243,7 @@ OpenDMLParser::SetupAudioStreamLength(stream_info *stream) } void -OpenDMLParser::SetupVideoStreamLength(stream_info *stream) +OpenDMLParser::SetupVideoStreamLength(OpenDMLStream *stream) { stream->frame_count = stream->stream_header.length; if (stream->stream_header.rate && stream->stream_header.scale) { @@ -410,8 +407,8 @@ OpenDMLParser::ParseChunk_AVI(int number, uint64 start, uint32 size) } if (Chunksize == 0) { - ERROR("OpenDMLParser::ParseChunk_AVI: chunk '"FOURCC_FORMAT"' has size 0\n", FOURCC_PARAM(Chunkfcc)); - return B_ERROR; + TRACE("OpenDMLParser::ParseChunk_AVI: chunk '"FOURCC_FORMAT"' has size 0\n", FOURCC_PARAM(Chunkfcc)); + return B_OK; } if (Chunkfcc == FOURCC('L','I','S','T')) { @@ -893,8 +890,10 @@ OpenDMLParser::ParseList_movi(uint64 start, uint32 size) { TRACE("OpenDMLParser::ParseList_movi, size %lu\n", size); - if (fMovieListStart == 0) + if (fMovieListStart == 0) { fMovieListStart = start; + fMovieListSize = size; + } fMovieChunkCount++; return B_OK; diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h index ee3db7f967..4ff3ad90d7 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h @@ -26,19 +26,30 @@ #define _OPEN_DML_PARSER_H #include +#include + #include "avi.h" -struct stream_info +class OpenDMLStream { - stream_info * next; // TODO: replace with a vector<> +public: + OpenDMLStream(); + ~OpenDMLStream(); + bool is_audio; bool is_video; + bool is_subtitle; + bool stream_header_valid; avi_stream_header stream_header; + + bool audio_format_valid; wave_format_ex *audio_format; size_t audio_format_size; + bool video_format_valid; bitmap_info_header video_format; + int64 odml_index_start; uint32 odml_index_size; @@ -58,12 +69,13 @@ public: int StreamCount(); - const stream_info * StreamInfo(int index); + const OpenDMLStream * StreamInfo(int index); int64 StandardIndexStart(); uint32 StandardIndexSize(); int64 MovieListStart(); + uint32 MovieListSize() {return fMovieListSize;}; const avi_main_header * AviMainHeader(); const odml_extended_header * OdmlExtendedHeader(); @@ -85,16 +97,18 @@ private: status_t ParseList_strl(uint64 start, uint32 size); void CreateNewStreamInfo(); - void SetupStreamLength(stream_info *stream); - void SetupAudioStreamLength(stream_info *stream); - void SetupVideoStreamLength(stream_info *stream); + void SetupStreamLength(OpenDMLStream *stream); + void SetupAudioStreamLength(OpenDMLStream *stream); + void SetupVideoStreamLength(OpenDMLStream *stream); private: BPositionIO * fSource; int64 fSize; + // TODO can be multiple Movi Lists int64 fMovieListStart; + uint32 fMovieListSize; int64 fStandardIndexStart; uint32 fStandardIndexSize; @@ -108,8 +122,8 @@ private: odml_extended_header fOdmlExtendedHeader; bool fOdmlExtendedHeaderValid; - stream_info * fStreams; - stream_info * fCurrentStream; + vector fStreams; + OpenDMLStream * fCurrentStream; }; #endif diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.cpp b/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.cpp index ad848165c7..fabdb9b0a2 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.cpp @@ -25,9 +25,9 @@ */ #include #include +#include #include #include -#include "ReaderPlugin.h" // B_MEDIA_* #include "StandardIndex.h" #include "OpenDMLParser.h" @@ -48,8 +48,6 @@ StandardIndex::StandardIndex(BPositionIO *source, OpenDMLParser *parser) : Index(source, parser) , fIndex(NULL) , fIndexSize(0) - , fStreamData(NULL) - , fStreamCount(parser->StreamCount()) , fDataOffset(parser->MovieListStart() - 4) { } @@ -58,11 +56,6 @@ StandardIndex::StandardIndex(BPositionIO *source, OpenDMLParser *parser) StandardIndex::~StandardIndex() { delete [] fIndex; - if (fStreamData) { - for (int i = 0; i < fStreamCount; i++) - delete [] fStreamData[i].seek_hints; - } - delete [] fStreamData; } @@ -72,12 +65,7 @@ StandardIndex::Init() uint32 indexBytes = fParser->StandardIndexSize(); fIndexSize = indexBytes / sizeof(avi_standard_index_entry); indexBytes = fIndexSize * sizeof(avi_standard_index_entry); - uint32 seekHintsStride = 1800 * fStreamCount; - uint32 seekHintsMax = fIndexSize / seekHintsStride; - TRACE("StandardIndex::Init: seekHintsStride %lu\n", seekHintsStride); - TRACE("StandardIndex::Init: seekHintsMax %lu\n", seekHintsMax); - #ifdef TRACE_START_INDEX { BStopWatch w("StandardIndex::Init: malloc"); #endif @@ -114,216 +102,78 @@ StandardIndex::Init() //DumpIndex(); #endif - fStreamData = new stream_data[fStreamCount]; - for (int i = 0; i < fStreamCount; i++) { - fStreamData[i].chunk_id = i / 10 + '0' + (i % 10 + '0') * 256; - fStreamData[i].chunk_count = 0; - fStreamData[i].keyframe_count = 0; - fStreamData[i].stream_pos = 0; - fStreamData[i].stream_size = 0; - fStreamData[i].seek_hints = new seek_hint[seekHintsMax]; - fStreamData[i].seek_hints_count = 0; - fStreamData[i].seek_hints_next = seekHintsStride; - } - #ifdef TRACE_START_INDEX { BStopWatch w("StandardIndex::Init: scan index"); #endif - for (int stream = 0; stream < fStreamCount; stream++) { - uint32 chunk_id = fStreamData[stream].chunk_id; - uint64 stream_size = 0; - uint32 chunk_count = 0; - uint32 keyframe_count = 0; - uint32 seek_hints_next = seekHintsStride; - uint32 seek_hints_count = 0; - for (uint32 i = 0; i < fIndexSize; i++) { - if ((fIndex[i].chunk_id & 0xffff) == chunk_id) { - stream_size += fIndex[i].chunk_length; - chunk_count++; - keyframe_count += (fIndex[i].flags >> AVIIF_KEYFRAME_SHIFT) & 1; - - if (i >= seek_hints_next) { - seek_hints_next = i + seekHintsStride; - seek_hint *hint = &fStreamData[stream].seek_hints[ - seek_hints_count++]; - hint->stream_pos = fIndex[i].chunk_offset; - hint->index_pos = i; - } - } - } - fStreamData[stream].stream_size = stream_size; - fStreamData[stream].chunk_count = chunk_count; - fStreamData[stream].keyframe_count = keyframe_count; - fStreamData[stream].seek_hints_count = seek_hints_count; - } -#ifdef TRACE_START_INDEX -} - - for (int i = 0; i < fStreamCount; i++) { - printf("stream %d, stream_size %llu\n", i, fStreamData[i].stream_size); - printf("stream %d, chunk_count %lu\n", i, fStreamData[i].chunk_count); - printf("stream %d, keyframe_count %lu\n", i, - fStreamData[i].keyframe_count); - printf("stream %d, seek_hints_count %lu\n", i, - fStreamData[i].seek_hints_count); - for (int j = 0; j < fStreamData[i].seek_hints_count; j++) { - printf(" seek_hint %3d, index_pos %6lu, stream_pos %lld\n", j, - fStreamData[i].seek_hints[j].index_pos, - fStreamData[i].seek_hints[j].stream_pos); - } - } -#endif // TRACE_START_INDEX - - return B_OK; -} - - -void -StandardIndex::DumpIndex() -{ - uint32 chunk = fIndex->chunk_id; - int count = 0; - int pos = 0; - - printf("StandardIndex::DumpIndex %lu entries\n", fIndexSize); - for (uint32 i = 0; i < fIndexSize; i++) { - count++; - if (chunk != fIndex[i].chunk_id) { - printf("%3d %c%c%c%c", count, FOURCC_PARAM(chunk)); - chunk = fIndex[i].chunk_id; - count = 0; - if (++pos % 8 == 0) - printf("\n"); - } - } - if (count) - printf("%3d %c%c%c%c", count, FOURCC_PARAM(chunk)); - printf("\n"); -} - - -status_t -StandardIndex::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, - bool *keyframe) -{ - stream_data *data = &fStreamData[stream_index]; - while (data->stream_pos < fIndexSize) { - if ((fIndex[data->stream_pos].chunk_id & 0xffff) == data->chunk_id) { - *keyframe = fIndex[data->stream_pos].flags & AVIIF_KEYFRAME; - *start = fDataOffset + fIndex[data->stream_pos].chunk_offset + 8; - // skip 8 bytes (chunk id + chunk size) - *size = fIndex[data->stream_pos].chunk_length; - data->stream_pos++; - return B_OK; - } - data->stream_pos++; - } - - return B_ERROR; -} - - -status_t -StandardIndex::Seek(int stream_index, uint32 seekTo, int64 *frame, - bigtime_t *time, bool readOnly) -{ - TRACE("StandardIndex::Seek: stream %d, seekTo%s%s%s%s, time %Ld, " - "frame %Ld\n", stream_index, - (seekTo & B_MEDIA_SEEK_TO_TIME) ? " B_MEDIA_SEEK_TO_TIME" : "", - (seekTo & B_MEDIA_SEEK_TO_FRAME) ? " B_MEDIA_SEEK_TO_FRAME" : "", - (seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ? - " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", - (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? - " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", - *time, *frame); + int stream_index; + off_t position; + uint32 size; + uint64 frame[fStreamCount]; + uint64 frame_no; + bigtime_t pts = 0; + bool keyframe; + uint32 sample_size; - const stream_info *stream = fParser->StreamInfo(stream_index); - stream_data *data = &fStreamData[stream_index]; + const OpenDMLStream *stream; - int64 frame_pos; - if (seekTo & B_MEDIA_SEEK_TO_FRAME) - frame_pos = *frame; - else if (seekTo & B_MEDIA_SEEK_TO_TIME) { - frame_pos = (*time * stream->frames_per_sec_rate) - / (1000000 * stream->frames_per_sec_scale); - } else - return B_BAD_VALUE; + for (uint32 i=0;i < fStreamCount; i++) { + frame[i] = 0; + } - if (stream->is_audio) { - // TODO: Actually take keyframe flags into account! - int64 bytes = 0; - for (uint32 i = 0; i < fIndexSize; i++) { - if ((fIndex[i].chunk_id & 0xffff) == data->chunk_id) { - int64 bytesNext = bytes + fIndex[i].chunk_length; - if (bytes <= frame_pos && bytesNext > frame_pos) { - if (!readOnly) - data->stream_pos = i; - goto done; - } - bytes = bytesNext; - } - } - } else if (stream->is_video) { - // iterate over all index entries of the stream, - // there is one entry per frame (TODO: actually one per field, - // if I interprete the documentation correctly...) - int pos = 0; - int lastKeyframePos = 0; - int lastKeyframeIndex = 0; - for (uint32 i = 0; i < fIndexSize; i++) { - // ignore index entries which are not for this stream - if ((fIndex[i].chunk_id & 0xffff) != data->chunk_id) - continue; + for (uint32 i = 0; i < fIndexSize; i++) { + + stream_index = ((fIndex[i].chunk_id & 0xff) - '0') * 10; + stream_index += ((fIndex[i].chunk_id >> 8) & 0xff) - '0'; - // remember the last known keyframe index/frame - if (fIndex[i].flags & AVIIF_KEYFRAME) { - lastKeyframePos = pos; - lastKeyframeIndex = i; - TRACE("keyframe at index %ld, frame %ld (seek: %ld)\n", i, - pos, frame_pos); - } + stream = fParser->StreamInfo(stream_index); + + keyframe = (fIndex[i].flags >> AVIIF_KEYFRAME_SHIFT) & 1; + size = fIndex[i].chunk_length; + + // Some muxers write chunk_offset as non-relative, need to handle this case. + position = fDataOffset + fIndex[i].chunk_offset; + frame_no = frame[stream_index]; - if (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) { - // use the index and frame of the last keyframe - if (pos == frame_pos) { - frame_pos = lastKeyframePos; - if (!readOnly) - data->stream_pos = lastKeyframeIndex; - goto done; - } - } else if (seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) { - // use the index and frame of the last keyframe - // if this frame is a keyframe and we at or past - // the seek position - if (pos >= frame_pos && pos == lastKeyframePos) { - frame_pos = lastKeyframePos; - if (!readOnly) - data->stream_pos = lastKeyframeIndex; - goto done; + if (stream->is_video) { + // Video is easy enough, it is always 1 frame = 1 index + pts = frame[stream_index] * 1000000LL * stream->frames_per_sec_scale / stream->frames_per_sec_rate; + frame[stream_index]++; + } else if (stream->is_audio) { + + pts = frame[stream_index] * 1000000LL / stream->audio_format->frames_per_sec; + + // Audio varies based on many different hacks over the years + // The simplest is chunk size / sample size = no of samples in the chunk for uncompressed audio + // ABR Compressed audio is more difficult and VBR Compressed audio is even harder + // What follows is what I have worked out from various sources across the internet. + if (stream->audio_format->format_tag != 0x0001) { + // VBR audio is detected as having a block_align >= 960 + if (stream->audio_format->block_align >= 960) { + // VBR Audio so block_align is the largest no of samples in a chunk + // scale is the no of samples in a frame + // rate is the sample rate + // but we must round up when calculating no of frames in a chunk + frame[stream_index] += (uint64)(ceil((double)size / (double)stream->audio_format->block_align)) * stream->frames_per_sec_scale; + } else { + // ABR Audio so use Chunk Size and average bytes per second to determine how many samples there are in the chunk + frame[stream_index] += (uint64)(ceil((double)size / (double)stream->audio_format->block_align)) * stream->audio_format->frames_per_sec / stream->audio_format->avg_bytes_per_sec; } } else { - // ignore keyframes - if (pos == frame_pos) { - if (!readOnly) - data->stream_pos = i; - goto done; + // sample size can be corrupt + if (stream->stream_header.sample_size > 0) { + sample_size = stream->stream_header.sample_size; + } else { + // compute sample size + sample_size = stream->audio_format->bits_per_sample * stream->audio_format->channels / 8; } + frame[stream_index] += size / stream->stream_header.sample_size; } - pos++; } - } else { - return B_BAD_VALUE; + + AddIndex(stream_index, position, size, frame_no, pts, keyframe); } - ERROR("libOpenDML: seek failed, position not found\n"); - return B_ERROR; - -done: - TRACE("seek done: index: pos %d, size %d\n", data->stream_pos, fIndexSize); - *frame = frame_pos; - *time = (frame_pos * 1000000 * stream->frames_per_sec_scale) - / stream->frames_per_sec_rate; return B_OK; } diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.h index 1a64f16bb6..5db4d303af 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.h @@ -36,37 +36,9 @@ public: status_t Init(); - status_t GetNextChunkInfo(int stream_index, int64* start, - uint32* size, bool* keyframe); - status_t Seek(int stream_index, uint32 seekTo, int64* frame, - bigtime_t* time, bool readOnly); - private: - void DumpIndex(); - -private: - struct seek_hint - { - uint64 stream_pos; - uint32 index_pos; - }; - - struct stream_data - { - uint32 chunk_id; - uint32 chunk_count; - uint32 keyframe_count; - uint32 stream_pos; - uint64 stream_size; - seek_hint * seek_hints; - int seek_hints_count; - uint32 seek_hints_next; - }; - avi_standard_index_entry *fIndex; uint32 fIndexSize; - stream_data * fStreamData; - int fStreamCount; int64 fDataOffset; };