1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-03 06:03:13 +02:00
audacity/src/ondemand/ODDecodeFFmpegTask.cpp

659 lines
26 KiB
C++

/*
* ODDecodeFFmpegTask.cpp
* Audacity
*
* Created by apple on 3/8/10.
* Copyright 2010 __MyCompanyName__. All rights reserved.
*
*/
#include <wx/wxprec.h>
#include "../Experimental.h"
// For compilers that support precompilation, includes "wx/wx.h".
#ifdef EXPERIMENTAL_OD_FFMPEG
#include "../Audacity.h" // needed before FFmpeg.h
#include "../FFmpeg.h" // which brings in avcodec.h, avformat.h
#include "../import/ImportFFmpeg.h"
#ifndef WX_PRECOMP
// Include your minimal set of headers here, or wx.h
#include <wx/window.h>
#endif
extern FFmpegLibs *FFmpegLibsInst;
#include "ODDecodeFFmpegTask.h"
#define ODFFMPEG_SEEKING_TEST_UNKNOWN 0
#define ODFFMPEG_SEEKING_TEST_FAILED -1
#define ODFFMPEG_SEEKING_TEST_SUCCESS 1
#define kMaxSamplesInCache 4410000
//struct for caching the decoded samples to be used over multiple blockfiles
typedef struct _FFmpegDecodeCache
{
uint8_t* samplePtr;//interleaved samples
sampleCount start;
sampleCount len;
int numChannels;
SampleFormat samplefmt; // input (from libav) sample format
} FFMpegDecodeCache;
//------ ODFFmpegDecoder declaration and defs - here because we strip dependencies from .h files
///class to decode a particular file (one per file). Saves info such as filename and length (after the header is read.)
class ODFFmpegDecoder:public ODFileDecoder
{
public:
///This should handle unicode converted to UTF-8 on mac/linux, but OD TODO:check on windows
ODFFmpegDecoder(const wxString & fileName, streamContext** scs, int numStreams,WaveTrack*** channels, AVFormatContext* formatContext, int streamIndex);
virtual ~ODFFmpegDecoder();
///Decodes the samples for this blockfile from the real file into a float buffer.
///This is file specific, so subclasses must implement this only.
///the buffer was defined like
///samplePtr sampleData = NewSamples(mLen, floatSample);
///this->ReadData(sampleData, floatSample, 0, mLen);
///This class should call ReadHeader() first, so it knows the length, and can prepare
///the file object if it needs to.
virtual int Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel);
///This is a must implement abstract virtual in the superclass.
///However it doesn't do anything because ImportFFMpeg does all that for us.
virtual bool ReadHeader() {return true;}
bool SeekingAllowed() ;
private:
void InsertCache(FFMpegDecodeCache* cache);
//puts the actual audio samples into the blockfile's data array
int FillDataFromCache(samplePtr & data, sampleFormat outFormat, sampleCount & start, sampleCount& len, unsigned int channel);
///REFACTORABLE CODE FROM IMPORT FFMPEG
///! Reads next audio frame
///\return pointer to the stream context structure to which the frame belongs to or NULL on error, or 1 if stream is not to be imported.
streamContext* ReadNextFrame();
///! Decodes the frame
///\param sc - stream context (from ReadNextFrame)
///\param flushing - true if flushing (no more frames left), false otherwise
///\return 0 on success, -1 if it can't decode any further
int DecodeFrame(streamContext *sc, bool flushing);
int mNumStreams;
streamContext **mScs; //!< Array of pointers to stream contexts. Length is mNumStreams.
WaveTrack*** mChannels;
AVFormatContext *mFormatContext; //!< Format description, also contains metadata and some useful info
std::vector<FFMpegDecodeCache*> mDecodeCache;
int mNumSamplesInCache;
sampleCount mCurrentPos; //the index of the next sample to be decoded
sampleCount mCurrentLen; //length of the last packet decoded
bool mSeekingAllowedStatus;
int mStreamIndex;
};
//------ ODDecodeFFmpegTask definitions
ODDecodeFFmpegTask::ODDecodeFFmpegTask(void* scs,int numStreams, WaveTrack*** channels, void* formatContext, int streamIndex)
{
mScs=scs;
mNumStreams=numStreams;
mChannels=channels;
mFormatContext = formatContext;
mStreamIndex = streamIndex;
//TODO we probably need to create a new WaveTrack*** pointer and copy.
//same for streamContext, but we should also use a ref counting system - this should be added to streamContext
// mScs = (streamContext**)malloc(sizeof(streamContext**)*mFormatContext->nb_streams);
}
ODDecodeFFmpegTask::~ODDecodeFFmpegTask()
{
}
ODTask* ODDecodeFFmpegTask::Clone()
{
//we need to create copies of mScs. It would be better to use a reference counter system.
ODDecodeFFmpegTask* clone = new ODDecodeFFmpegTask((void*)mScs,mNumStreams,mChannels,mFormatContext,mStreamIndex);
clone->mDemandSample=GetDemandSample();
//the decoders and blockfiles should not be copied. They are created as the task runs.
return clone;
}
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
//
//compare to FLACImportPlugin::Open(wxString filename)
ODFileDecoder* ODDecodeFFmpegTask::CreateFileDecoder(const wxString & fileName)
{
// Open the file for import
ODFFmpegDecoder *decoder = new ODFFmpegDecoder(fileName, (streamContext**) mScs,mNumStreams,mChannels,(AVFormatContext*)mFormatContext, mStreamIndex);
mDecoders.push_back(decoder);
return decoder;
}
/// subclasses need to override this if they cannot always seek.
/// seeking will be enabled once this is true.
bool ODFFmpegDecoder::SeekingAllowed()
{
return false;
/*
if(ODFFMPEG_SEEKING_TEST_UNKNOWN != mSeekingAllowedStatus)
return mSeekingAllowedStatus == ODFFMPEG_SEEKING_TEST_SUCCESS;
//we can seek if the following checks pass:
//-sample rate is less than the reciprocal of the time_base of the seeking stream.
//-a seek test has been made and dts updates as expected.
//we want to clone this to run a seek test.
AVFormatContext* ic = (AVFormatContext*)mFormatContext;
bool audioStreamExists = false;
AVStream* st;
//test the audio stream(s)
for (unsigned int i = 0; i < ic->nb_streams; i++)
{
if (ic->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
{
audioStreamExists = true;
st = ic->streams[i];
if(st->duration <= 0 || st->codec->sample_rate <= 0)
goto test_failed;
//if the time base reciprocal is less than the sample rate it means we can't accurately represent a sample with the timestamp in av.
float time_base_inv = ((float)st->time_base.den/st->time_base.num);
if(time_base_inv < st->codec->sample_rate)
goto test_failed;
}
}
if(!audioStreamExists)
goto test_failed;
//TODO: now try a seek and see if dts/pts (decode/presentation timestamp) is updated as we expected it to be.
//This should be done using a new AVFormatContext clone so that we don't ruin the file pointer if we fail.
// url_fseek(mFormatContext->pb,0,SEEK_SET);
AVFormatContext* tempContext;
int err;
err = ufile_fopen_input(&tempContext, mFName);
if (err < 0)
{
goto test_failed;
}
err = av_find_stream_info(tempContext);
if (err < 0)
{
goto test_failed;
}
if(av_seek_frame(tempContext, st->index, 0, 0) >= 0) {
mSeekingAllowedStatus = ODFFMPEG_SEEKING_TEST_SUCCESS;
if (tempContext) av_close_input_file(tempContext);
return SeekingAllowed();
}
if (tempContext) av_close_input_file(tempContext);
test_failed:
mSeekingAllowedStatus = ODFFMPEG_SEEKING_TEST_FAILED;
return SeekingAllowed();
*/
}
//------ ODDecodeFFmpegFileDecoder
ODFFmpegDecoder::ODFFmpegDecoder(const wxString & fileName, streamContext** scs,int numStreams,WaveTrack*** channels, AVFormatContext* formatContext, int streamIndex)
:ODFileDecoder(fileName),
//mSamplesDone(0),
mNumStreams(numStreams),
mScs(scs),
mFormatContext(formatContext),
mNumSamplesInCache(0),
mCurrentLen(0),
mSeekingAllowedStatus(ODFFMPEG_SEEKING_TEST_UNKNOWN),
mStreamIndex(streamIndex)
{
PickFFmpegLibs();
//do a shallow copy of the 2d array.
mChannels = new WaveTrack **[mNumStreams];
for (int s = 0; s < mNumStreams; s++)
{
mChannels[s] = new WaveTrack *[mScs[s]->m_stream->codec->channels];
int c;
for (c = 0; c < mScs[s]->m_stream->codec->channels; c++)
{
mChannels[s][c] = channels[s][c];
}
}
// get the current stream start time.
int64_t stream_delay = 0;
if (mScs[streamIndex]->m_stream->start_time != int64_t(AV_NOPTS_VALUE) &&
mScs[streamIndex]->m_stream->start_time > 0) {
stream_delay = mScs[streamIndex]->m_stream->start_time;
}
mCurrentPos = double(stream_delay) / AV_TIME_BASE;
//TODO: add a ref counter to scs? This will be necessary if we want to allow copy and paste of not-yet decoded
//ODDecodeBlockFiles that point to FFmpeg files.
}
//we have taken ownership, so delete the ffmpeg stuff allocated in ImportFFmpeg that was given to us.
ODFFmpegDecoder::~ODFFmpegDecoder()
{
if (FFmpegLibsInst->ValidLibsLoaded())
{
if (mFormatContext) av_close_input_file(mFormatContext);
av_log_set_callback(av_log_default_callback);
}
for (int i = 0; i < mNumStreams; i++)
{
if (mScs[i]->m_decodedAudioSamples != NULL)
av_free(mScs[i]->m_decodedAudioSamples);
delete mScs[i];
}
free(mScs);
//delete our caches.
while(mDecodeCache.size())
{
free(mDecodeCache[0]->samplePtr);
delete mDecodeCache[0];
mDecodeCache.erase(mDecodeCache.begin());
}
//free the channel pointer arrays
for (int s = 0; s < mNumStreams; s++)
{
delete[] mChannels[s];
}
delete[] mChannels;
DropFFmpegLibs();
}
//we read the file from left to right, so in some cases it makes more sense not to seek and just carry on the decode if the gap is small enough.
//this value controls this amount. this should be a value that is much larger than the payload for a single packet, and around block file size around 1-10 secs.
#define kDecodeSampleAllowance 400000
//number of jump backwards seeks
#define kMaxSeekRewindAttempts 8
int ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)
{
format = mScs[mStreamIndex]->m_osamplefmt;
data = NewSamples(len, format);
samplePtr bufStart = data;
streamContext* sc = NULL;
int nChannels;
// printf("start %llu len %llu\n", start, len);
//TODO update this to work with seek - this only works linearly now.
if(mCurrentPos > start && mCurrentPos <= start+len + kDecodeSampleAllowance)
{
//this next call takes data, start and len as reference variables and updates them to reflect the new area that is needed.
FillDataFromCache(bufStart, format, start,len,channel);
}
bool seeking = false;
//look at the decoding timestamp and see if the next sample that will be decoded is not the next sample we need.
if(len && (mCurrentPos > start + len || mCurrentPos + kDecodeSampleAllowance < start ) && SeekingAllowed()) {
sc = mScs[mStreamIndex];
AVStream* st = sc->m_stream;
int stindex = -1;
uint64_t targetts;
//printf("attempting seek to %llu\n", start);
//we have to find the index for this stream.
for (unsigned int i = 0; i < mFormatContext->nb_streams; i++) {
if (mFormatContext->streams[i] == sc->m_stream )
stindex =i;
}
if(stindex >=0) {
int numAttempts = 0;
//reset mCurrentPos to a bogus value
mCurrentPos = start+len +1;
while(numAttempts++ < kMaxSeekRewindAttempts && mCurrentPos > start) {
//we want to move slightly before the start of the block file, but not too far ahead
targetts = (start-kDecodeSampleAllowance*numAttempts/kMaxSeekRewindAttempts) * ((double)st->time_base.den/(st->time_base.num * st->codec->sample_rate ));
if(targetts<0)
targetts=0;
//printf("attempting seek to %llu, attempts %d\n", targetts, numAttempts);
if(av_seek_frame(mFormatContext,stindex,targetts,0) >= 0){
//find out the dts we've seekd to.
sampleCount actualDecodeStart = 0.5 + st->codec->sample_rate * st->cur_dts * ((double)st->time_base.num/st->time_base.den); //this is mostly safe because den is usually 1 or low number but check for high values.
mCurrentPos = actualDecodeStart;
seeking = true;
//if the seek was past our desired position, rewind a bit.
//printf("seek ok to %llu samps, float: %f\n",actualDecodeStart,actualDecodeStartDouble);
} else {
printf("seek failed");
break;
}
}
if(mCurrentPos>start){
mSeekingAllowedStatus = (bool)ODFFMPEG_SEEKING_TEST_FAILED;
// url_fseek(mFormatContext->pb,sc->m_pkt.pos,SEEK_SET);
printf("seek fail, reverting to previous pos\n");
return -1;
}
}
}
bool firstpass = true;
//we decode up to the end of the blockfile
while (len>0 && (mCurrentPos < start+len) && (sc = ReadNextFrame()) != NULL)
{
// ReadNextFrame returns 1 if stream is not to be imported
if (sc != (streamContext*)1)
{
nChannels = sc->m_stream->codec->channels < sc->m_initialchannels ? sc->m_stream->codec->channels : sc->m_initialchannels;
//find out the dts we've seekd to. can't use the stream->cur_dts because it is faulty. also note that until we do the first seek, pkt.dts can be false and will change for the same samples after the initial seek.
sampleCount actualDecodeStart = mCurrentPos;
// we need adjacent samples, so don't use dts most of the time which will leave gaps between frames
// for some formats
// The only other case for inserting silence is for initial offset and ImportFFmpeg.cpp does this for us
if (seeking) {
actualDecodeStart = 0.52 + (sc->m_stream->codec->sample_rate * sc->m_pkt.dts
* ((double)sc->m_stream->time_base.num / sc->m_stream->time_base.den));
//this is mostly safe because den is usually 1 or low number but check for high values.
//hack to get rounding to work to neareset frame size since dts isn't exact
if (sc->m_stream->codec->frame_size) {
actualDecodeStart = ((actualDecodeStart + sc->m_stream->codec->frame_size/2) / sc->m_stream->codec->frame_size) * sc->m_stream->codec->frame_size;
}
// reset for the next one
seeking = false;
}
if(actualDecodeStart != mCurrentPos)
printf("ts not matching - now:%llu , last:%llu, lastlen:%llu, start %llu, len %llu\n",actualDecodeStart, mCurrentPos, mCurrentLen, start, len);
//if we've skipped over some samples, fill the gap with silence. This could happen often in the beginning of the file.
if(actualDecodeStart>start && firstpass) {
// find the number of samples for the leading silence
int amt = actualDecodeStart - start;
FFMpegDecodeCache* cache = new FFMpegDecodeCache;
//printf("skipping/zeroing %i samples. - now:%llu (%f), last:%llu, lastlen:%llu, start %llu, len %llu\n",amt,actualDecodeStart, actualDecodeStartdouble, mCurrentPos, mCurrentLen, start, len);
//put it in the cache so the other channels can use it.
cache->numChannels = sc->m_stream->codec->channels;
cache->len = amt;
cache->start=start;
// 8 bit and 16 bit audio output from ffmpeg means
// 16 bit int out.
// 32 bit int, float, double mean float out.
if (format == int16Sample)
cache->samplefmt = SAMPLE_FMT_S16;
else
cache->samplefmt = SAMPLE_FMT_FLT;
cache->samplePtr = (uint8_t*) malloc(amt * cache->numChannels * SAMPLE_SIZE(format));
memset(cache->samplePtr, 0, amt * cache->numChannels * SAMPLE_SIZE(format));
InsertCache(cache);
}
firstpass=false;
mCurrentPos = actualDecodeStart;
//decode the entire packet (unused bits get saved in cache, so as long as cache size limit is bigger than the
//largest packet size, we're ok.
while (sc->m_pktRemainingSiz > 0)
//Fill the cache with decoded samples
if (DecodeFrame(sc,false) < 0)
break;
// Cleanup after frame decoding
if (sc->m_pktValid)
{
av_free_packet(&sc->m_pkt);
sc->m_pktValid = 0;
}
}
}
// Flush the decoders if we're done.
if((!sc || sc == (streamContext*) 1)&& len>0)
{
for (int i = 0; i < mNumStreams; i++)
{
if (DecodeFrame(mScs[i], true) == 0)
{
if (mScs[i]->m_pktValid)
{
av_free_packet(&mScs[i]->m_pkt);
mScs[i]->m_pktValid = 0;
}
}
}
}
//this next call takes data, start and len as reference variables and updates them to reflect the new area that is needed.
FillDataFromCache(bufStart, format, start, len, channel);
// CHECK: not sure if we need this. In any case it has to be updated for the new float case (not just int16)
//if for some reason we couldn't get the samples, fill them with silence
/*
int16_t* outBuf = (int16_t*) bufStart;
for(int i=0;i<len;i++)
outBuf[i]=0;
*/
return 1;
}
//puts the actual audio samples into the blockfile's data array
// the minimum amount of cache entries necessary to warrant a binary search.
#define kODFFmpegSearchThreshold 10
///returns the number of samples filled in from start.
//also updates data and len to reflect new unfilled area - start is unmodified.
int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleFormat outFormat, sampleCount &start, sampleCount& len, unsigned int channel)
{
if(mDecodeCache.size() <= 0)
return 0;
int samplesFilled=0;
//do a search for the best position to start at.
//Guess that the array is evenly spaced from end to end - (dictionary sort)
//assumes the array is sorted.
//all we need for this to work is a location in the cache array
//that has a start time of less than our start sample, but try to get closer with binary search
int searchStart = 0;
int searchEnd = mDecodeCache.size();
int guess;
if(searchEnd>kODFFmpegSearchThreshold)
{
//first just guess that the cache is contiguous and we can just use math to figure it out like a dictionary
//by guessing where our hit will be.
while(searchStart+1<searchEnd)
{
guess = (searchStart+searchEnd)/2;//find a midpoint. //searchStart+ (searchEnd-searchStart)* ((float)start - mDecodeCache[searchStart]->start )/mDecodeCache[searchEnd]->start;
//we want guess to point at the first index that hits even if there are duplicate start times (which can happen)
if(mDecodeCache[guess]->start+mDecodeCache[guess]->len >= start)
searchEnd = --guess;
else
searchStart = guess;
}
}
//this is a sorted array
for(int i=searchStart; i < (int)mDecodeCache.size(); i++)
{
//check for a cache hit - be careful to include the first/last sample an nothing more.
//we only accept cache hits that touch either end - no piecing out of the middle.
//this way the amount to be decoded remains set.
if(start < mDecodeCache[i]->start+mDecodeCache[i]->len &&
start + len > mDecodeCache[i]->start)
{
uint8_t* outBuf;
outBuf = (uint8_t*)data;
//reject buffers that would split us into two pieces because we don't have
//a method of dealing with this yet, and it won't happen very often.
if(start<mDecodeCache[i]->start && start+len > mDecodeCache[i]->start+mDecodeCache[i]->len)
continue;
int samplesHit;
int hitStartInCache;
int hitStartInRequest;
int nChannels = mDecodeCache[i]->numChannels;
samplesHit = FFMIN(start+len,mDecodeCache[i]->start+mDecodeCache[i]->len)
- FFMAX(mDecodeCache[i]->start,start);
//find the start of the hit relative to the cache buffer start.
hitStartInCache = FFMAX(0,start-mDecodeCache[i]->start);
//we also need to find out which end was hit - if it is the tail only we need to update from a later index.
hitStartInRequest = start <mDecodeCache[i]->start?len - samplesHit: 0;
sampleCount outIndex,inIndex;
for(int j=0;j<samplesHit;j++)
{
outIndex = hitStartInRequest + j;
inIndex = (hitStartInCache + j) * nChannels + channel;
switch (mDecodeCache[i]->samplefmt)
{
case SAMPLE_FMT_U8:
//printf("u8 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
((int16_t *)outBuf)[outIndex] = (int16_t) (((uint8_t*)mDecodeCache[i]->samplePtr)[inIndex] - 0x80) << 8;
break;
case SAMPLE_FMT_S16:
//printf("u16 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
((int16_t *)outBuf)[outIndex] = ((int16_t*)mDecodeCache[i]->samplePtr)[inIndex];
break;
case SAMPLE_FMT_S32:
//printf("s32 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
((float *)outBuf)[outIndex] = (float) ((int32_t*)mDecodeCache[i]->samplePtr)[inIndex] * (1.0 / (1 << 31));
break;
case SAMPLE_FMT_FLT:
//printf("f in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
((float *)outBuf)[outIndex] = (float) ((float*)mDecodeCache[i]->samplePtr)[inIndex];
break;
case SAMPLE_FMT_DBL:
//printf("dbl in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
((float *)outBuf)[outIndex] = (float) ((double*)mDecodeCache[i]->samplePtr)[inIndex];
break;
default:
printf("ODDecodeFFMPEG TASK unrecognized sample format\n");
return 1;
break;
}
}
//update the cursor
samplesFilled += samplesHit;
//update the input start/len params - if the end was hit we can take off just len.
//otherwise, we can assume only the front of the request buffer was hit since we don't allow it to be split.
if(start < mDecodeCache[i]->start)
len-=samplesHit;
else
{
//we update data pointer too- but it is a typedef'd char* so be careful with the pointer math
data+= samplesHit * (SAMPLE_SIZE(outFormat) / sizeof(*data));
start+=samplesHit;
len -=samplesHit;
}
}
//if we've had our fill, leave. if we've passed the point which can have hits, leave.
if(len<=0 || mDecodeCache[i]->start > start+len)
break;
}
return samplesFilled;
}
//these next few look highly refactorable.
//get the right stream pointer.
streamContext* ODFFmpegDecoder::ReadNextFrame()
{
return import_ffmpeg_read_next_frame(mFormatContext, mScs, mNumStreams);
}
int ODFFmpegDecoder::DecodeFrame(streamContext *sc, bool flushing)
{
int ret = import_ffmpeg_decode_frame(sc, flushing);
if (ret == 0 && sc->m_frameValid) {
//stick it in the cache.
//TODO- consider growing/unioning a few cache buffers like WaveCache does.
//however we can't use wavecache as it isn't going to handle our stereo interleaved part, and isn't for samples
//However if other ODDecode tasks need this, we should do a new class for caching.
FFMpegDecodeCache* cache = new FFMpegDecodeCache;
//len is number of samples per channel
cache->numChannels = sc->m_stream->codec->channels;
cache->len = (sc->m_decodedAudioSamplesValidSiz / sc->m_samplesize) / cache->numChannels;
cache->start = mCurrentPos;
cache->samplePtr = (uint8_t*) malloc(sc->m_decodedAudioSamplesValidSiz);
cache->samplefmt = sc->m_samplefmt;
memcpy(cache->samplePtr, sc->m_decodedAudioSamples, sc->m_decodedAudioSamplesValidSiz);
InsertCache(cache);
}
return ret;
}
void ODFFmpegDecoder::InsertCache(FFMpegDecodeCache* cache) {
int searchStart = 0;
int searchEnd = mDecodeCache.size(); //size() is also a valid insert index.
int guess = 0;
//first just guess that the cache is contiguous and we can just use math to figure it out like a dictionary
//by guessing where our hit will be.
// printf("inserting cache start %llu, mCurrentPos %llu\n", cache->start, mCurrentPos);
while(searchStart<searchEnd)
{
guess = (searchStart+searchEnd)/2;//searchStart+ (searchEnd-searchStart)* ((float)cache->start - mDecodeCache[searchStart]->start )/mDecodeCache[searchEnd]->start;
//check greater than OR equals because we want to insert infront of old dupes.
if(mDecodeCache[guess]->start>= cache->start) {
// if(mDecodeCache[guess]->start == cache->start) {
// printf("dupe! start cache %llu start new cache %llu, mCurrentPos %llu\n",mDecodeCache[guess]->start, cache->start, mCurrentPos);
// }
searchEnd = guess;
}
else
searchStart = ++guess;
}
mCurrentLen = cache->len;
mCurrentPos=cache->start+cache->len;
mDecodeCache.insert(mDecodeCache.begin()+guess, cache);
// mDecodeCache.push_back(cache);
mNumSamplesInCache+=cache->len;
//if the cache is too big, drop some.
while(mNumSamplesInCache>kMaxSamplesInCache)
{
int dropindex;
//drop which ever index is further from our newly added one.
dropindex = (guess > mDecodeCache.size()/2) ? 0 : (mDecodeCache.size()-1);
mNumSamplesInCache-=mDecodeCache[dropindex]->len;
free(mDecodeCache[dropindex]->samplePtr);
delete mDecodeCache[dropindex];
mDecodeCache.erase(mDecodeCache.begin()+dropindex);
}
}
#endif