From ff72ec4feefdf23442cb3426ebfa656f3c28fc80 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Wed, 10 Dec 2014 09:34:51 +0100 Subject: [PATCH] APE reader: optimize seeking * The APE SDK can only decode rather big chunks at a time (several seconds), so our reader keeps an internal buffer of the last decoded chunk. * However, this was always dropped when seeking, meaning the same few seconds of sound were decoded again and again. * I'm not sure why MediaPlayer is trying to seek to the position it already is at, but we can make it work, so why not. Now the existing buffer is reused if possible when seeking. This makes it possible to play APE files in MediaPlayer. Also, several more style fixes. --- .../media/plugins/ape_reader/APEReader.cpp | 62 +++++++++++-------- .../media/plugins/ape_reader/APEReader.h | 38 +++++++----- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/src/add-ons/media/plugins/ape_reader/APEReader.cpp b/src/add-ons/media/plugins/ape_reader/APEReader.cpp index 33d179d880..78ad2b57b1 100644 --- a/src/add-ons/media/plugins/ape_reader/APEReader.cpp +++ b/src/add-ons/media/plugins/ape_reader/APEReader.cpp @@ -81,20 +81,22 @@ TAPEReader::GetNextChunk(void* oCookie, const void** oChunkBuffer, int64 aOutSize; // check whether song is finished or not - if ( mReadPosTotal-mReadPos+mPlayPos >= mDataSize ) + if (mReadPosTotal - mReadPos + mPlayPos >= mDataSize) return B_ERROR; + // reading data - if ( mPlayPos >= mReadPos ) { + if (mPlayPos >= mReadPos ) ReadBlocks(); - } + // passing data - if ( mReadPos-mPlayPos >= BUFFER_SIZE ) { + if (mReadPos-mPlayPos >= BUFFER_SIZE) aOutSize = BUFFER_SIZE; - } else { + else aOutSize = mReadPos-mPlayPos; - } + *oChunkBuffer = &mDecodedData[mPlayPos]; mPlayPos += aOutSize; + // passing info *oChunkSize = aOutSize; oMediaHeader->start_time = CurrentTime(); @@ -108,8 +110,9 @@ TAPEReader::GetStreamInfo(void* oCookie, int64* oFrameCount, bigtime_t* oDuration, media_format* oFormat, const void** oInfoBuffer, size_t* oInfoSize) { - if ( LoadAPECheck() != B_OK ) + if (LoadAPECheck() != B_OK) return LoadAPECheck(); + *oFrameCount = mDataSize / (mDecomp->GetInfo(APE_INFO_BITS_PER_SAMPLE) / 8 * mDecomp->GetInfo(APE_INFO_CHANNELS)); *oDuration = mDecomp->GetInfo(APE_INFO_LENGTH_MS) @@ -118,11 +121,11 @@ TAPEReader::GetStreamInfo(void* oCookie, int64* oFrameCount, oFormat->type = B_MEDIA_RAW_AUDIO; oFormat->u.raw_audio.frame_rate = mDecomp->GetInfo(APE_INFO_SAMPLE_RATE); oFormat->u.raw_audio.channel_count = mDecomp->GetInfo(APE_INFO_CHANNELS); - if ( mDecomp->GetInfo(APE_INFO_BITS_PER_SAMPLE) == 16 ) { + if ( mDecomp->GetInfo(APE_INFO_BITS_PER_SAMPLE) == 16 ) oFormat->u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT; - } else { + else oFormat->u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR; - } + oFormat->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN; oFormat->u.raw_audio.buffer_size = BUFFER_SIZE; oInfoBuffer = NULL; @@ -141,12 +144,12 @@ TAPEReader::LoadAPECheck() const status_t TAPEReader::ReadBlocks() { - int aBlocksRead; - int aRetVal = 0; + int aBlocksRead; + int aRetVal = 0; aRetVal = mDecomp->GetData(reinterpret_cast(mDecodedData), BLOCK_COUNT, &aBlocksRead); - if ( aRetVal != ERROR_SUCCESS ) + if (aRetVal != ERROR_SUCCESS) return B_ERROR; mPlayPos = 0; @@ -160,12 +163,12 @@ status_t TAPEReader::FindKeyFrame(void* cookie, uint32 flags, int64* frame, bigtime_t* time) { - if ( flags & B_MEDIA_SEEK_TO_FRAME ) { + if (flags & B_MEDIA_SEEK_TO_FRAME) { *time = *frame * 1000 / mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) * mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS); printf("FindKeyFrame for frame %Ld: %Ld\n", *frame, *time); - } else if ( flags & B_MEDIA_SEEK_TO_TIME ) { - *frame = (*time)/1000*mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) + } else if (flags & B_MEDIA_SEEK_TO_TIME) { + *frame = (*time) / 1000 * mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) / mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS); printf("FindKeyFrame for time %Ld: %Ld\n", *time, *frame); } else @@ -178,21 +181,28 @@ TAPEReader::FindKeyFrame(void* cookie, uint32 flags, int64* frame, status_t TAPEReader::Seek(void *cookie, uint32 flags, int64 *frame, bigtime_t *time) { - int32 aNewBlock; + int32 aNewBlock; - if ( flags & B_MEDIA_SEEK_TO_FRAME ) { + if (flags & B_MEDIA_SEEK_TO_FRAME) { printf("Seek to frame %Ld\n", *frame); aNewBlock = *frame; - } else if ( flags & B_MEDIA_SEEK_TO_TIME ) { + } else if (flags & B_MEDIA_SEEK_TO_TIME) { printf("Seek for time %Ld\n", *time); - aNewBlock = (*time)/1000*mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) + aNewBlock = (*time) / 1000 * mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) / mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS); } else return B_ERROR; - mReadPosTotal = aNewBlock*mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN); - mDecomp->Seek(aNewBlock); - ReadBlocks(); + int64 aNewTime = aNewBlock * mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN); + if (mReadPosTotal - mReadPos < aNewTime && mReadPosTotal > aNewTime) { + // Requested seek frame is already in the current buffer, no need to + // actually seek, just set the play position + mPlayPos = aNewTime - mReadPosTotal + mReadPos; + } else { + mReadPosTotal = aNewBlock * mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN); + mDecomp->Seek(aNewBlock); + ReadBlocks(); + } return B_OK; } @@ -203,12 +213,14 @@ TAPEReader::Sniff(int32* oStreamCount) Unset(); // prepare about file mSrcPIO = dynamic_cast(Source()); - if ( mSrcPIO == NULL ) + if (mSrcPIO == NULL) return B_ERROR; + mPositionBridgeIO.SetPositionIO(mSrcPIO); mDecomp = CreateIAPEDecompressEx(&mPositionBridgeIO); - if ( mDecomp == NULL ) + if (mDecomp == NULL) return B_ERROR; + // prepare about data mDataSize = static_cast(mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS)) *mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN); diff --git a/src/add-ons/media/plugins/ape_reader/APEReader.h b/src/add-ons/media/plugins/ape_reader/APEReader.h index 5400523841..8ca5333cbc 100644 --- a/src/add-ons/media/plugins/ape_reader/APEReader.h +++ b/src/add-ons/media/plugins/ape_reader/APEReader.h @@ -1,23 +1,24 @@ #ifndef ___APEReader_H_ #define ___APEReader_H_ -//------------------------------------------------------------------------------ -// BeOS -// MAC + + #include "MACLib.h" #include "MonkeysAudioMIMEType.h" #include "PositionBridgeIO.h" -// Proj + #include "ReaderPlugin.h" // Haiku private header -//------------------------------------------------------------------------------ -const int32 BLOCK_COUNT = 1024*4; // number of blocks, get from MACLib at once -const int32 BUFFER_SIZE = 1024*4; // size of audio data passing to Media Kit + + +const int32 BLOCK_COUNT = 1024*4; // number of blocks, get from MACLib at once +const int32 BUFFER_SIZE = 1024*4; // size of audio data passing to Media Kit const int32 MEDIA_FILE_FORMAT_VERSION = 100; // media_file_format::version -//============================================================================== + + class TAPEReader : public Reader { public: TAPEReader(); - virtual ~TAPEReader(); + virtual ~TAPEReader(); virtual const char* Copyright(); @@ -28,16 +29,18 @@ public: virtual status_t AllocateCookie(int32 oStreamNumber, void** oCookie); virtual status_t FreeCookie(void* oCookie); - virtual status_t GetStreamInfo(void* oCookie, int64* oFrameCount, bigtime_t* oDuration, media_format* oFormat, - const void** oInfoBuffer, size_t* oInfoSize); + virtual status_t GetStreamInfo(void* oCookie, int64* oFrameCount, + bigtime_t* oDuration, media_format* oFormat, + const void** oInfoBuffer, size_t* oInfoSize); virtual status_t Seek(void *cookie, uint32 flags, - int64 *frame, bigtime_t *time); + int64 *frame, bigtime_t *time); virtual status_t FindKeyFrame(void* cookie, uint32 flags, int64* frame, bigtime_t* time); - virtual status_t GetNextChunk(void* oCookie, const void** oChunkBuffer, size_t* oChunkSize, media_header* oMediaHeader); + virtual status_t GetNextChunk(void* oCookie, const void** oChunkBuffer, + size_t* oChunkSize, media_header* oMediaHeader); private: typedef Reader SUPER; @@ -57,7 +60,8 @@ private: IAPEDecompress* mDecomp; TPositionBridgeIO mPositionBridgeIO; }; -//============================================================================== + + class TAPEReaderPlugin : public ReaderPlugin { public: @@ -66,7 +70,9 @@ public: virtual Reader* NewReader(); }; -//============================================================================== + + MediaPlugin* instantiate_plugin(); -//============================================================================== + + #endif // ___APEReader_H_