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 028cf8e18b..4f2d3ca98c 100644 --- a/src/add-ons/media/plugins/avi_reader/avi_reader.cpp +++ b/src/add-ons/media/plugins/avi_reader/avi_reader.cpp @@ -51,21 +51,18 @@ struct avi_cookie char * buffer; unsigned buffer_size; - int64 frame_count; - bigtime_t duration; + bool is_audio; + bool is_video; + media_format format; - bool audio; - - // audio only: - int64 byte_pos; - uint32 bytes_per_sec_rate; - uint32 bytes_per_sec_scale; - - // video only: - uint32 frame_pos; + bigtime_t duration; + int64 frame_count; + int64 frame_pos; uint32 frames_per_sec_rate; uint32 frames_per_sec_scale; + + // video only: uint32 line_count; }; @@ -111,7 +108,7 @@ aviReader::Sniff(int32 *streamCount) TRACE("aviReader::Sniff: this stream seems to be supported\n"); fFile = new OpenDMLFile(pos_io_source); - if (B_OK != fFile->Init()) { + if (fFile->Init() < B_OK) { ERROR("aviReader::Sniff: can't setup OpenDMLFile\n"); return B_ERROR; } @@ -144,6 +141,8 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) cookie->stream = streamNumber; cookie->buffer = 0; cookie->buffer_size = 0; + cookie->is_audio = false; + cookie->is_video = false; BMediaFormats formats; media_format *format = &cookie->format; @@ -167,54 +166,21 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) return B_ERROR; } - if (audio_format->format_tag == 0x0001) {// PCM - cookie->frame_count = stream_header->length / ((stream_header->sample_size + 7) / 8); - TRACE("audio frame_count %Ld (is PCM)\n", cookie->frame_count); - } else if (stream_header->rate) { // not PCM - cookie->frame_count = (stream_header->length * (int64)audio_format->frames_per_sec) / stream_header->rate; - TRACE("audio frame_count %Ld (using rate)\n", cookie->frame_count); - } else { // not PCM - cookie->frame_count = (stream_header->length * (int64)audio_format->frames_per_sec) / (stream_header->sample_size * audio_format->avg_bytes_per_sec); - TRACE("audio frame_count %Ld (using fallback)\n", cookie->frame_count); - } + cookie->is_audio = true; + cookie->duration = fFile->StreamInfo(streamNumber)->duration; + cookie->frame_count = fFile->StreamInfo(streamNumber)->frame_count; + cookie->frame_pos = 0; + cookie->frames_per_sec_rate = fFile->StreamInfo(streamNumber)->frames_per_sec_rate; + cookie->frames_per_sec_scale = fFile->StreamInfo(streamNumber)->frames_per_sec_scale; - if (stream_header->rate && stream_header->scale) { - cookie->duration = (1000000LL * (int64)stream_header->length * (int64)stream_header->scale) / stream_header->rate; - TRACE("audio duration %.6f (%Ld) (using scale & rate)\n", cookie->duration / 1E6, cookie->duration); - } else if (stream_header->rate) { - cookie->duration = (1000000LL * (int64)stream_header->length) / stream_header->rate; - TRACE("audio duration %.6f (%Ld) (using rate)\n", cookie->duration / 1E6, cookie->duration); - } else { - cookie->duration = fFile->Duration(); - TRACE("audio duration %.6f (%Ld) (using fallback)\n", cookie->duration / 1E6, cookie->duration); - } - - cookie->audio = true; - cookie->byte_pos = 0; - - if (stream_header->scale && stream_header->rate && stream_header->sample_size) { - cookie->bytes_per_sec_rate = stream_header->rate * stream_header->sample_size; - cookie->bytes_per_sec_scale = stream_header->scale; - TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using both)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale); - } else if (audio_format->avg_bytes_per_sec) { - cookie->bytes_per_sec_rate = audio_format->avg_bytes_per_sec; - cookie->bytes_per_sec_scale = 1; - TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using avg_bytes_per_sec)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale); - } else if (stream_header->rate) { - cookie->bytes_per_sec_rate = stream_header->rate; - cookie->bytes_per_sec_scale = 1; - TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using rate)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale); - } else { - cookie->bytes_per_sec_rate = 128000; - cookie->bytes_per_sec_scale = 8; - TRACE("audio bytes_per_sec_rate %ld, bytes_per_sec_scale %ld (using fallback)\n", cookie->bytes_per_sec_rate, cookie->bytes_per_sec_scale); - } + TRACE("audio frame_count %Ld\n", cookie->frame_count); + TRACE("audio duration %.6f (%Ld)\n", cookie->duration / 1E6, cookie->duration); 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; - if (B_OK != formats.GetFormatFor(description, format)) + if (formats.GetFormatFor(description, format) < B_OK) format->type = B_MEDIA_RAW_AUDIO; format->u.raw_audio.frame_rate = audio_format->frames_per_sec; format->u.raw_audio.channel_count = audio_format->channels; @@ -237,14 +203,14 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) // some encoded format description.family = B_WAV_FORMAT_FAMILY; description.u.wav.codec = audio_format->format_tag; - if (B_OK != formats.GetFormatFor(description, format)) + if (formats.GetFormatFor(description, format) < B_OK) format->type = B_MEDIA_ENCODED_AUDIO; format->u.encoded_audio.bit_rate = 8 * audio_format->avg_bytes_per_sec; TRACE("bit_rate %.3f\n", format->u.encoded_audio.bit_rate); format->u.encoded_audio.output.frame_rate = audio_format->frames_per_sec; format->u.encoded_audio.output.channel_count = audio_format->channels; } - // this doesn't seem to work (it's not even a fourcc) + // 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; @@ -270,27 +236,14 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) return B_ERROR; } - cookie->audio = false; + cookie->is_video = true; + cookie->duration = fFile->StreamInfo(streamNumber)->duration; + cookie->frame_count = fFile->StreamInfo(streamNumber)->frame_count; cookie->frame_pos = 0; + 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; - if (stream_header->scale && stream_header->rate) { - cookie->frames_per_sec_rate = stream_header->rate; - cookie->frames_per_sec_scale = stream_header->scale; - TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using both)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale); - } else if (fFile->AviMainHeader()->micro_sec_per_frame) { - cookie->frames_per_sec_rate = 1000000; - cookie->frames_per_sec_scale = fFile->AviMainHeader()->micro_sec_per_frame; - TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using micro_sec_per_frame)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale); - } else { - cookie->frames_per_sec_rate = 25; - cookie->frames_per_sec_scale = 1; - TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using fallback)\n", cookie->frames_per_sec_rate, cookie->frames_per_sec_scale); - } - - cookie->frame_count = stream_header->length; - cookie->duration = (cookie->frame_count * (int64)cookie->frames_per_sec_scale * 1000000LL) / cookie->frames_per_sec_rate; - TRACE("video frame_count %Ld\n", cookie->frame_count); TRACE("video duration %.6f (%Ld)\n", cookie->duration / 1E6, cookie->duration); @@ -299,13 +252,13 @@ aviReader::AllocateCookie(int32 streamNumber, void **_cookie) description.u.avi.codec = video_format->compression; else description.u.avi.codec = stream_header->fourcc_handler; - if (B_OK != formats.GetFormatFor(description, format)) + if (formats.GetFormatFor(description, format) < B_OK) format->type = B_MEDIA_ENCODED_VIDEO; format->user_data_type = B_CODEC_TYPE_INFO; *(uint32 *)format->user_data = description.u.avi.codec; format->user_data[4] = 0; format->u.encoded_video.max_bit_rate = 8 * fFile->AviMainHeader()->max_bytes_per_sec; - format->u.encoded_video.avg_bit_rate = format->u.encoded_video.max_bit_rate / 2; // XXX fix this + format->u.encoded_video.avg_bit_rate = (format->u.encoded_video.max_bit_rate * 3 / 4); // XXX fix this format->u.encoded_video.output.field_rate = cookie->frames_per_sec_rate / (float)cookie->frames_per_sec_scale; format->u.encoded_video.output.interlace = 1; // 1: progressive format->u.encoded_video.output.first_active = 0; @@ -360,18 +313,25 @@ aviReader::GetStreamInfo(void *_cookie, int64 *frameCount, bigtime_t *duration, status_t -aviReader::Seek(void *cookie, - uint32 seekTo, +aviReader::Seek(void *_cookie, uint32 seekTo, int64 *frame, bigtime_t *time) { - TRACE("aviReader::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n", + avi_cookie *cookie = (avi_cookie *)_cookie; + + TRACE("aviReader::Seek: stream %d, seekTo%s%s%s%s, time %Ld, 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" : "", (seekTo & B_MEDIA_SEEK_CLOSEST_FORWARD) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "", (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", *time, *frame); - return B_ERROR; + status_t rv = fFile->Seek(cookie->stream, seekTo, frame, time); + 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); + } + return rv; } @@ -396,24 +356,26 @@ aviReader::GetNextChunk(void *_cookie, cookie->buffer_size = (size + 15) & ~15; cookie->buffer = new char [cookie->buffer_size]; } + + mediaHeader->start_time = (cookie->frame_pos * 1000000 * cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate; - if (cookie->audio) { - mediaHeader->start_time = (cookie->byte_pos * 1000000LL * (int64)cookie->bytes_per_sec_scale) / cookie->bytes_per_sec_rate; + if (cookie->is_audio) { mediaHeader->type = B_MEDIA_ENCODED_AUDIO; mediaHeader->u.encoded_audio.buffer_flags = keyframe ? B_MEDIA_KEY_FRAME : 0; - - cookie->byte_pos += size; - } else { - mediaHeader->start_time = (cookie->frame_pos * 1000000LL * (int64)cookie->frames_per_sec_scale) / cookie->frames_per_sec_rate; + cookie->frame_pos += size; + } 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; - + mediaHeader->u.encoded_video.line_count = cookie->line_count; cookie->frame_pos += 1; + } else { + return B_BAD_VALUE; } - TRACE("stream %d: start_time %.6f\n", cookie->stream, mediaHeader->start_time / 1000000.0); + 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; 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 73be039975..46d50889d3 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.cpp @@ -96,18 +96,10 @@ OpenDMLFile::Init() { fParser = new OpenDMLParser(fSource); - if (fParser->Parse() < B_OK) { - ERROR("OpenDMLFile::SetTo: warning, file parsing failed\n"); - } - - if (!fParser->AviMainHeader()) { - ERROR("OpenDMLFile::SetTo: avi main header not found\n"); + if (fParser->Init() < B_OK) { + ERROR("OpenDMLFile::SetTo: parser init failed\n"); return B_ERROR; } - - if (fParser->StreamCount() == 0) { - ERROR("OpenDMLFile::SetTo: no streams found\n"); - } if (fParser->StreamInfo(0)->odml_index_size != 0) { fIndex = new OpenDMLIndex(fSource, fParser); @@ -391,18 +383,26 @@ OpenDMLFile::AviGetNextChunkInfo(int stream_index, int64 *start, uint32 *size, b } #endif + status_t OpenDMLFile::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe) { return fIndex->GetNextChunkInfo(stream_index, start, size, keyframe); } +status_t +OpenDMLFile::Seek(int stream_index, uint32 seekTo, int64 *frame, bigtime_t *time) +{ + return fIndex->Seek(stream_index, seekTo, frame, time); +} + int OpenDMLFile::StreamCount() { return fParser->StreamCount(); } +/* bigtime_t OpenDMLFile::Duration() { @@ -422,7 +422,8 @@ OpenDMLFile::FrameCount() return fParser->AviMainHeader()->total_frames; return 0; } - +*/ + bool OpenDMLFile::IsVideo(int stream_index) { @@ -466,3 +467,9 @@ OpenDMLFile::StreamFormat(int stream_index) return (fParser->StreamInfo(stream_index)->stream_header_valid) ? &fParser->StreamInfo(stream_index)->stream_header : 0; } + +const stream_info * +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 6970cb7af6..d5b3ff0d8b 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLFile.h @@ -40,10 +40,14 @@ public: status_t Init(); int StreamCount(); + + const stream_info * StreamInfo(int index); +/* bigtime_t Duration(); uint32 FrameCount(); - +*/ + bool IsVideo(int stream_index); bool IsAudio(int stream_index); @@ -54,6 +58,7 @@ public: const avi_stream_header * StreamFormat(int stream_index); 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); BPositionIO *Source() { return fSource; } 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 b605a1ec76..aad2778ad2 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.cpp @@ -41,12 +41,6 @@ #define DO_SWAP_INT16(x) x = B_SWAP_INT16(x) #endif -struct movie_chunk -{ - movie_chunk * next; - int64 start; - uint32 size; -}; OpenDMLParser::OpenDMLParser(BPositionIO *source) : fSource(source), @@ -128,6 +122,10 @@ OpenDMLParser::CreateNewStreamInfo() 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) { @@ -142,18 +140,128 @@ OpenDMLParser::CreateNewStreamInfo() } status_t -OpenDMLParser::Parse() +OpenDMLParser::Init() { - TRACE("OpenDMLParser::Parse\n"); + TRACE("OpenDMLParser::Init\n"); if (!fSource) { - ERROR("OpenDMLParser::Parse: no source\n"); + ERROR("OpenDMLParser::Init: no source\n"); return B_ERROR; } if (fSize < 32) { - ERROR("OpenDMLParser::Parse: file too small\n"); + ERROR("OpenDMLParser::Init: file too small\n"); return B_ERROR; } + + if (Parse() < B_OK) { + ERROR("OpenDMLParser::Init: warning: file parsing failed\n"); + } + + if (AviMainHeader() == NULL) { + ERROR("OpenDMLParser::Init: avi main header not found\n"); + return B_ERROR; + } + + if (StreamCount() == 0) { + ERROR("OpenDMLParser::Init: no streams found\n"); + return B_ERROR; + } + + uint32 frame_count = OdmlExtendedHeader() ? OdmlExtendedHeader()->total_frames : AviMainHeader()->total_frames; + bigtime_t duration = frame_count * AviMainHeader()->micro_sec_per_frame; + printf("AVI Header frame count %lu, duration %.6f\n", frame_count, duration / 1E6); + + for (int i = 0; i < fStreamCount; i++) { + SetupStreamLength(const_cast(StreamInfo(i))); + } + return B_OK; +} + +void +OpenDMLParser::SetupStreamLength(stream_info *stream) +{ + if (stream->is_audio) + SetupAudioStreamLength(stream); + if (stream->is_video) + SetupVideoStreamLength(stream); +} + +// F:\avi-info\Information on AVI file.htm + +void +OpenDMLParser::SetupAudioStreamLength(stream_info *stream) +{ + stream->frame_count = stream->stream_header.length; + + if (stream->audio_format->format_tag == 0x0001 + && stream->stream_header.sample_size != 0 + && stream->stream_header.sample_size != 1) { // PCM + stream->frame_count /= (stream->stream_header.sample_size + 7) / 8; + printf("audio: messing 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; + printf("audio: 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; + printf("audio: 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_scale = 1000; + printf("audio: using micro_sec_per_frame\n"); + } else { + printf("audio: no idea what to do\n"); + } + + if (stream->audio_format->avg_bytes_per_sec) { + int64 expected_frame_count = (stream->duration * stream->audio_format->avg_bytes_per_sec) / 1000000; + printf("audio: expected frame_count %lld, calculated stream frame_count %lld\n", expected_frame_count, stream->frame_count); + if (expected_frame_count * 9 > stream->frame_count * 10) { + printf("audio: something is wrong, ignoring calculated stream frame_count, rate and scale\n"); + stream->frame_count = expected_frame_count; + stream->frames_per_sec_rate = stream->audio_format->avg_bytes_per_sec; + stream->frames_per_sec_scale = 1; + } + } + + printf("audio: frame_count %lld, duration %.6f, fps %.3f\n", + stream->frame_count, stream->duration / 1E6, stream->frames_per_sec_rate / (double)stream->frames_per_sec_scale); +} + +void +OpenDMLParser::SetupVideoStreamLength(stream_info *stream) +{ + stream->frame_count = stream->stream_header.length; + 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; + printf("video: using rate+scale\n"); + } else if (AviMainHeader()->micro_sec_per_frame) { + stream->frames_per_sec_rate = 1000000; + stream->frames_per_sec_scale = AviMainHeader()->micro_sec_per_frame; + printf("video: using micro_sec_per_frame\n"); + } else { + stream->frames_per_sec_rate = 25; + stream->frames_per_sec_scale = 1; + printf("video: using fallback\n"); + } + stream->duration = (stream->frame_count * stream->frames_per_sec_scale * 1000000) / stream->frames_per_sec_rate; + + printf("video: frame_count %lld, duration %.6f, fps %.3f\n", + stream->frame_count, stream->duration / 1E6, stream->frames_per_sec_rate / (double)stream->frames_per_sec_scale); +} + +status_t +OpenDMLParser::Parse() +{ + TRACE("OpenDMLParser::Parse\n"); uint64 pos = 0; int riff_chunk_number = 0; @@ -460,11 +568,6 @@ OpenDMLParser::ParseChunk_strh(uint64 start, uint32 size) DO_SWAP_INT16(fCurrentStream->stream_header.rect_bottom); #endif - if (fCurrentStream->stream_header.scale == 0) { - printf("OpenDMLParser::ParseChunk_strh: scale is 0\n"); - fCurrentStream->stream_header.scale = 1; - } - 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'); @@ -478,7 +581,6 @@ OpenDMLParser::ParseChunk_strh(uint64 start, uint32 size) 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); 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 c252560359..ee3db7f967 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/OpenDMLParser.h @@ -30,7 +30,7 @@ struct stream_info { - stream_info * next; + stream_info * next; // TODO: replace with a vector<> bool is_audio; bool is_video; bool stream_header_valid; @@ -41,6 +41,11 @@ struct stream_info bitmap_info_header video_format; int64 odml_index_start; uint32 odml_index_size; + + bigtime_t duration; + int64 frame_count; + uint32 frames_per_sec_rate; + uint32 frames_per_sec_scale; }; class OpenDMLParser @@ -49,7 +54,7 @@ public: OpenDMLParser(BPositionIO *source); ~OpenDMLParser(); - status_t Parse(); + status_t Init(); int StreamCount(); @@ -64,6 +69,7 @@ public: const odml_extended_header * OdmlExtendedHeader(); private: + status_t Parse(); status_t ParseChunk_AVI(int number, uint64 start, uint32 size); status_t ParseChunk_LIST(uint64 start, uint32 size); status_t ParseChunk_idx1(uint64 start, uint32 size); @@ -78,8 +84,12 @@ private: status_t ParseList_INFO(uint64 start, uint32 size); status_t ParseList_strl(uint64 start, uint32 size); -private: void CreateNewStreamInfo(); + void SetupStreamLength(stream_info *stream); + void SetupAudioStreamLength(stream_info *stream); + void SetupVideoStreamLength(stream_info *stream); + +private: BPositionIO * fSource; int64 fSize; 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 79680ccec2..e113a144c3 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.cpp +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.cpp @@ -33,12 +33,6 @@ #include -struct StandardIndex::stream_data -{ - uint32 chunk_id; - uint32 pos; -}; - StandardIndex::StandardIndex(BPositionIO *source, OpenDMLParser *parser) : Index(source, parser) , fIndex(NULL) @@ -53,6 +47,10 @@ 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; } @@ -63,6 +61,11 @@ 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; + + printf("StandardIndex::Init: seekHintsStride %lu\n", seekHintsStride); + printf("StandardIndex::Init: seekHintsMax %lu\n", seekHintsMax); { BStopWatch w("StandardIndex::Init: malloc"); @@ -92,13 +95,61 @@ StandardIndex::Init() } } + //DumpIndex(); + 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].pos = 0; + 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; } + +{ BStopWatch w("StandardIndex::Init: scan index"); - //DumpIndex(); + 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; + int seek_hints_next = seekHintsStride; + int seek_hints_count = 0; + for (int 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; + } +} + + 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); + } + } return B_OK; } @@ -132,15 +183,15 @@ status_t StandardIndex::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bool *keyframe) { stream_data *data = &fStreamData[stream_index]; - while (data->pos < fIndexSize) { - if ((fIndex[data->pos].chunk_id & 0xffff) == data->chunk_id) { - *keyframe = fIndex[data->pos].flags & AVIIF_KEYFRAME; - *start = fDataOffset + fIndex[data->pos].chunk_offset + 8; // skip 8 bytes (chunk id + chunk size) - *size = fIndex[data->pos].chunk_length; - data->pos++; + 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->pos++; + data->stream_pos++; } return B_ERROR; @@ -150,13 +201,59 @@ StandardIndex::GetNextChunkInfo(int stream_index, int64 *start, uint32 *size, bo status_t StandardIndex::Seek(int stream_index, uint32 seekTo, int64 *frame, bigtime_t *time) { -/* - printf("StandardIndex::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n", + printf("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" : ""); -*/ + (seekTo & B_MEDIA_SEEK_CLOSEST_BACKWARD) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "", + *time, *frame); + + const stream_info *stream = fParser->StreamInfo(stream_index); + stream_data *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) / (1000000 * stream->frames_per_sec_scale); + else + return B_BAD_VALUE; + + if (stream->is_audio) { + int64 bytes = 0; + for (int 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) { + data->stream_pos = i; + goto done; + } + bytes = bytesNext; + } + } + } else if (stream->is_video) { + int pos = 0; + for (int i = 0; i < fIndexSize; i++) { + if ((fIndex[i].chunk_id & 0xffff) == data->chunk_id) { + if (pos == frame_pos) { + data->stream_pos = i; + goto done; + } + pos++; + } + } + } else { + return B_BAD_VALUE; + } + + printf("seek failed, position not found\n"); return B_ERROR; + +done: + printf("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 878e55edfa..d523f97d55 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/StandardIndex.h @@ -43,7 +43,24 @@ private: void DumpIndex(); private: - struct stream_data; + 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; diff --git a/src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h b/src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h index 13fd19ddfa..479d6afc1b 100644 --- a/src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h +++ b/src/add-ons/media/plugins/avi_reader/libOpenDML/avi.h @@ -57,6 +57,7 @@ struct avi_standard_index_entry } _PACKED; #define AVIIF_LIST 0x00000001 #define AVIIF_KEYFRAME 0x00000010 +#define AVIIF_KEYFRAME_SHIFT 4 #define AVIIF_FIRSTPART 0x00000020 #define AVIIF_LASTPART 0x00000040 #define AVIIF_MIDPART (AVIIF_LASTPART | AVIFF_FIRSTPART)