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.
This commit is contained in:
Adrien Destugues
2014-12-10 09:34:51 +01:00
parent 5480f203d3
commit ff72ec4fee
2 changed files with 59 additions and 41 deletions
@@ -81,20 +81,22 @@ TAPEReader::GetNextChunk(void* oCookie, const void** oChunkBuffer,
int64 aOutSize; int64 aOutSize;
// check whether song is finished or not // check whether song is finished or not
if ( mReadPosTotal-mReadPos+mPlayPos >= mDataSize ) if (mReadPosTotal - mReadPos + mPlayPos >= mDataSize)
return B_ERROR; return B_ERROR;
// reading data // reading data
if ( mPlayPos >= mReadPos ) { if (mPlayPos >= mReadPos )
ReadBlocks(); ReadBlocks();
}
// passing data // passing data
if ( mReadPos-mPlayPos >= BUFFER_SIZE ) { if (mReadPos-mPlayPos >= BUFFER_SIZE)
aOutSize = BUFFER_SIZE; aOutSize = BUFFER_SIZE;
} else { else
aOutSize = mReadPos-mPlayPos; aOutSize = mReadPos-mPlayPos;
}
*oChunkBuffer = &mDecodedData[mPlayPos]; *oChunkBuffer = &mDecodedData[mPlayPos];
mPlayPos += aOutSize; mPlayPos += aOutSize;
// passing info // passing info
*oChunkSize = aOutSize; *oChunkSize = aOutSize;
oMediaHeader->start_time = CurrentTime(); oMediaHeader->start_time = CurrentTime();
@@ -108,8 +110,9 @@ TAPEReader::GetStreamInfo(void* oCookie, int64* oFrameCount,
bigtime_t* oDuration, media_format* oFormat, const void** oInfoBuffer, bigtime_t* oDuration, media_format* oFormat, const void** oInfoBuffer,
size_t* oInfoSize) size_t* oInfoSize)
{ {
if ( LoadAPECheck() != B_OK ) if (LoadAPECheck() != B_OK)
return LoadAPECheck(); return LoadAPECheck();
*oFrameCount = mDataSize / (mDecomp->GetInfo(APE_INFO_BITS_PER_SAMPLE) / 8 *oFrameCount = mDataSize / (mDecomp->GetInfo(APE_INFO_BITS_PER_SAMPLE) / 8
* mDecomp->GetInfo(APE_INFO_CHANNELS)); * mDecomp->GetInfo(APE_INFO_CHANNELS));
*oDuration = mDecomp->GetInfo(APE_INFO_LENGTH_MS) *oDuration = mDecomp->GetInfo(APE_INFO_LENGTH_MS)
@@ -118,11 +121,11 @@ TAPEReader::GetStreamInfo(void* oCookie, int64* oFrameCount,
oFormat->type = B_MEDIA_RAW_AUDIO; oFormat->type = B_MEDIA_RAW_AUDIO;
oFormat->u.raw_audio.frame_rate = mDecomp->GetInfo(APE_INFO_SAMPLE_RATE); oFormat->u.raw_audio.frame_rate = mDecomp->GetInfo(APE_INFO_SAMPLE_RATE);
oFormat->u.raw_audio.channel_count = mDecomp->GetInfo(APE_INFO_CHANNELS); 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; 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.format = media_raw_audio_format::B_AUDIO_UCHAR;
}
oFormat->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN; oFormat->u.raw_audio.byte_order = B_MEDIA_LITTLE_ENDIAN;
oFormat->u.raw_audio.buffer_size = BUFFER_SIZE; oFormat->u.raw_audio.buffer_size = BUFFER_SIZE;
oInfoBuffer = NULL; oInfoBuffer = NULL;
@@ -141,12 +144,12 @@ TAPEReader::LoadAPECheck() const
status_t status_t
TAPEReader::ReadBlocks() TAPEReader::ReadBlocks()
{ {
int aBlocksRead; int aBlocksRead;
int aRetVal = 0; int aRetVal = 0;
aRetVal = mDecomp->GetData(reinterpret_cast<char*>(mDecodedData), aRetVal = mDecomp->GetData(reinterpret_cast<char*>(mDecodedData),
BLOCK_COUNT, &aBlocksRead); BLOCK_COUNT, &aBlocksRead);
if ( aRetVal != ERROR_SUCCESS ) if (aRetVal != ERROR_SUCCESS)
return B_ERROR; return B_ERROR;
mPlayPos = 0; mPlayPos = 0;
@@ -160,12 +163,12 @@ status_t
TAPEReader::FindKeyFrame(void* cookie, uint32 flags, int64* frame, TAPEReader::FindKeyFrame(void* cookie, uint32 flags, int64* frame,
bigtime_t* time) 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) *time = *frame * 1000 / mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS)
* mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS); * mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS);
printf("FindKeyFrame for frame %Ld: %Ld\n", *frame, *time); printf("FindKeyFrame for frame %Ld: %Ld\n", *frame, *time);
} else if ( flags & B_MEDIA_SEEK_TO_TIME ) { } else if (flags & B_MEDIA_SEEK_TO_TIME) {
*frame = (*time)/1000*mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS) *frame = (*time) / 1000 * mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS)
/ mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS); / mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS);
printf("FindKeyFrame for time %Ld: %Ld\n", *time, *frame); printf("FindKeyFrame for time %Ld: %Ld\n", *time, *frame);
} else } else
@@ -178,21 +181,28 @@ TAPEReader::FindKeyFrame(void* cookie, uint32 flags, int64* frame,
status_t status_t
TAPEReader::Seek(void *cookie, uint32 flags, int64 *frame, bigtime_t *time) 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); printf("Seek to frame %Ld\n", *frame);
aNewBlock = *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); 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); / mDecomp->GetInfo(APE_DECOMPRESS_LENGTH_MS);
} else } else
return B_ERROR; return B_ERROR;
mReadPosTotal = aNewBlock*mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN); int64 aNewTime = aNewBlock * mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN);
mDecomp->Seek(aNewBlock); if (mReadPosTotal - mReadPos < aNewTime && mReadPosTotal > aNewTime) {
ReadBlocks(); // 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; return B_OK;
} }
@@ -203,12 +213,14 @@ TAPEReader::Sniff(int32* oStreamCount)
Unset(); Unset();
// prepare about file // prepare about file
mSrcPIO = dynamic_cast<BPositionIO*>(Source()); mSrcPIO = dynamic_cast<BPositionIO*>(Source());
if ( mSrcPIO == NULL ) if (mSrcPIO == NULL)
return B_ERROR; return B_ERROR;
mPositionBridgeIO.SetPositionIO(mSrcPIO); mPositionBridgeIO.SetPositionIO(mSrcPIO);
mDecomp = CreateIAPEDecompressEx(&mPositionBridgeIO); mDecomp = CreateIAPEDecompressEx(&mPositionBridgeIO);
if ( mDecomp == NULL ) if (mDecomp == NULL)
return B_ERROR; return B_ERROR;
// prepare about data // prepare about data
mDataSize = static_cast<int64>(mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS)) mDataSize = static_cast<int64>(mDecomp->GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS))
*mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN); *mDecomp->GetInfo(APE_INFO_BLOCK_ALIGN);
@@ -1,23 +1,24 @@
#ifndef ___APEReader_H_ #ifndef ___APEReader_H_
#define ___APEReader_H_ #define ___APEReader_H_
//------------------------------------------------------------------------------
// BeOS
// MAC
#include "MACLib.h" #include "MACLib.h"
#include "MonkeysAudioMIMEType.h" #include "MonkeysAudioMIMEType.h"
#include "PositionBridgeIO.h" #include "PositionBridgeIO.h"
// Proj
#include "ReaderPlugin.h" // Haiku private header #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 const int32 MEDIA_FILE_FORMAT_VERSION = 100; // media_file_format::version
//==============================================================================
class TAPEReader : public Reader class TAPEReader : public Reader
{ {
public: public:
TAPEReader(); TAPEReader();
virtual ~TAPEReader(); virtual ~TAPEReader();
virtual const char* Copyright(); virtual const char* Copyright();
@@ -28,16 +29,18 @@ public:
virtual status_t AllocateCookie(int32 oStreamNumber, void** oCookie); virtual status_t AllocateCookie(int32 oStreamNumber, void** oCookie);
virtual status_t FreeCookie(void* oCookie); virtual status_t FreeCookie(void* oCookie);
virtual status_t GetStreamInfo(void* oCookie, int64* oFrameCount, bigtime_t* oDuration, media_format* oFormat, virtual status_t GetStreamInfo(void* oCookie, int64* oFrameCount,
const void** oInfoBuffer, size_t* oInfoSize); bigtime_t* oDuration, media_format* oFormat,
const void** oInfoBuffer, size_t* oInfoSize);
virtual status_t Seek(void *cookie, uint32 flags, 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, virtual status_t FindKeyFrame(void* cookie, uint32 flags,
int64* frame, bigtime_t* time); 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: private:
typedef Reader SUPER; typedef Reader SUPER;
@@ -57,7 +60,8 @@ private:
IAPEDecompress* mDecomp; IAPEDecompress* mDecomp;
TPositionBridgeIO mPositionBridgeIO; TPositionBridgeIO mPositionBridgeIO;
}; };
//==============================================================================
class TAPEReaderPlugin : public ReaderPlugin class TAPEReaderPlugin : public ReaderPlugin
{ {
public: public:
@@ -66,7 +70,9 @@ public:
virtual Reader* NewReader(); virtual Reader* NewReader();
}; };
//==============================================================================
MediaPlugin* instantiate_plugin(); MediaPlugin* instantiate_plugin();
//==============================================================================
#endif // ___APEReader_H_ #endif // ___APEReader_H_