1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-01 00:19:27 +02:00

Add seek support to OD-based FFmpeg import.

Also some minor refactoring.
This feature works with mp3 right now - may work on other formats, but that will be a seperate commit.
It is not enabled so most of the changes won't even be compiled, and those that do won't be run.
To enable it uncommment the EXPERIMENTAL_ODFFMPEG def in Experimental.h
This commit is contained in:
mchinen 2010-07-05 14:38:47 +00:00
parent 4d244e93ae
commit 5a5957b422
9 changed files with 262 additions and 136 deletions

View File

@ -311,28 +311,29 @@ bool ODDecodeBlockFile::IsDataAvailable()
/// Write the summary to disk, using the derived ReadData() to get the data
/// Here, the decoder ODTask associated with this file must fetch the samples with
/// the ODDecodeTask::Decode() method.
void ODDecodeBlockFile::WriteODDecodeBlockFile()
int ODDecodeBlockFile::WriteODDecodeBlockFile()
{
// To build the summary data, call ReadData (implemented by the
// derived classes) to get the sample data
samplePtr sampleData;// = NewSamples(mLen, floatSample);
int ret;
//use the decoder here.
mDecoderMutex.Lock();
if(!mDecoder)
{
mDecoderMutex.Unlock();
return;
return -1;
}
//sampleData and mFormat are set by the decoder.
mDecoder->Decode(sampleData, mFormat, mAliasStart, mLen, mAliasChannel);
ret = mDecoder->Decode(sampleData, mFormat, mAliasStart, mLen, mAliasChannel);
mDecoderMutex.Unlock();
if(ret < 0)
return ret; //failure
//the summary is also calculated here.
mFileNameMutex.Lock();
@ -352,6 +353,8 @@ void ODDecodeBlockFile::WriteODDecodeBlockFile()
mDataAvailableMutex.Lock();
mDataAvailable=true;
mDataAvailableMutex.Unlock();
return ret;
}
///sets the file name the summary info will be saved in. threadsafe.

View File

@ -89,9 +89,9 @@ class ODDecodeBlockFile : public SimpleBlockFile
virtual void Recover(void);
///A public interface to WriteSummary
void DoWriteBlockFile(){WriteODDecodeBlockFile();}
int DoWriteBlockFile(){return WriteODDecodeBlockFile();}
void WriteODDecodeBlockFile();
int WriteODDecodeBlockFile();
///Sets the value that indicates where the first sample in this block corresponds to the global sequence/clip. Only for display use.
void SetStart(sampleCount startSample){mStart = startSample;}

View File

@ -557,7 +557,7 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
//append blockfiles to each stream and add an individual ODDecodeTask for each one.
for (int s = 0; s < mNumStreams; s++)
{
ODDecodeFFmpegTask* odTask=new ODDecodeFFmpegTask(mScs,mNumStreams,mChannels,mFormatContext);
ODDecodeFFmpegTask* odTask=new ODDecodeFFmpegTask(mScs,mNumStreams,mChannels,mFormatContext, s);
ODFileDecoder* odDecoder = (ODFileDecoder*)odTask->CreateFileDecoder(mFilename);
//each stream has different duration. We need to know it if seeking is to be allowed.

View File

@ -23,6 +23,11 @@
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
@ -44,7 +49,7 @@ 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);
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.
@ -54,14 +59,19 @@ public:
///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 void Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel);
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:
int FillDataFromCache(samplePtr & data, sampleCount start, sampleCount& len, unsigned int channel);
void InsertCache(FFMpegDecodeCache* cache);
//puts the actual audio samples into the blockfile's data array
int FillDataFromCache(samplePtr & data, 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.
@ -73,29 +83,28 @@ private:
///\return 0 on success, -1 if it can't decode any further
int DecodeFrame(streamContext *sc, bool flushing);
///! Writes decoded data into WaveTracks. Called by DecodeFrame
///\param sc - stream context
///\return 0 on success, 1 on error or interruption
// also, updates the data and len inputs to reflect the result.
int WriteData(streamContext *sc,samplePtr & data, sampleCount& len, unsigned int channel);
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;
int mCurrentPos; //the index of the next sample to be decoded
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)
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);
@ -109,7 +118,7 @@ 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);
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.
@ -122,7 +131,7 @@ ODTask* ODDecodeFFmpegTask::Clone()
ODFileDecoder* ODDecodeFFmpegTask::CreateFileDecoder(const wxString & fileName)
{
// Open the file for import
ODFFmpegDecoder *decoder = new ODFFmpegDecoder(fileName, (streamContext**) mScs,mNumStreams,mChannels,(AVFormatContext*)mFormatContext);
ODFFmpegDecoder *decoder = new ODFFmpegDecoder(fileName, (streamContext**) mScs,mNumStreams,mChannels,(AVFormatContext*)mFormatContext, mStreamIndex);
mDecoders.push_back(decoder);
return decoder;
@ -131,22 +140,67 @@ ODFileDecoder* ODDecodeFFmpegTask::CreateFileDecoder(const wxString & fileName)
/// subclasses need to override this if they cannot always seek.
/// seeking will be enabled once this is true.
bool ODDecodeFFmpegTask::SeekingAllowed()
bool ODFFmpegDecoder::SeekingAllowed()
{
//TODO: find out when we can seek - we can make an educated guess if the first 5% of the frames were of fixed size.
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.
streamContext** scs = (streamContext** )mScs;
//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.
// FFmpegLibsInst->url_fseek(mFormatContext->pb,0,SEEK_SET);
if(FFmpegLibsInst->av_seek_frame(mFormatContext,st->index,0,0) >= 0) {
mSeekingAllowedStatus = ODFFMPEG_SEEKING_TEST_SUCCESS;
return SeekingAllowed();
}
test_failed:
mSeekingAllowedStatus = ODFFMPEG_SEEKING_TEST_FAILED;
return SeekingAllowed();
}
//------ ODDecodeFFmpegFileDecoder
ODFFmpegDecoder::ODFFmpegDecoder(const wxString & fileName, streamContext** scs,int numStreams,WaveTrack*** channels, AVFormatContext* formatContext)
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),
mCurrentPos(0)
mCurrentPos(0),
mCurrentLen(0),
mSeekingAllowedStatus(ODFFMPEG_SEEKING_TEST_UNKNOWN),
mStreamIndex(streamIndex)
{
PickFFmpegLibs();
@ -202,9 +256,13 @@ ODFFmpegDecoder::~ODFFmpegDecoder()
DropFFmpegLibs();
}
void ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)
//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)
{
//it looks like the code in importFFmpeg.cpp only imports 16 bit int - need to see why.
format = int16Sample;
@ -214,35 +272,98 @@ void ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCoun
int nChannels;
//TODO update this to work with seek - this only works linearly now.
if(mCurrentPos > start)
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,start,len,channel);
}
// mDecodeBuffer=data;
while (len>0 && (sc = ReadNextFrame()) != NULL)
//look at the decoding timestamp and see if the next sample that will be decoded is not the next sample we need.
if(len && SeekingAllowed() && (mCurrentPos > start + len || mCurrentPos + kDecodeSampleAllowance < start )) {
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;
if(FFmpegLibsInst->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.
double actualDecodeStartDouble = 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;
//if the seek was past our desired position, rewind a bit.
printf("seek ok to %llu samps, float: %f\n",actualDecodeStart,actualDecodeStartDouble);
} else
break;
}
if(mCurrentPos>start){
mSeekingAllowedStatus = ODFFMPEG_SEEKING_TEST_FAILED;
// FFmpegLibsInst->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 = 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.
double actualDecodeStartdouble = 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
actualDecodeStart = ((actualDecodeStart + sc->m_stream->codec->frame_size/2) / sc->m_stream->codec->frame_size) * sc->m_stream->codec->frame_size;
if(actualDecodeStart != mCurrentPos)
printf("ts not matching - now:%llu (%f), last:%llu, lastlen:%llu, start %llu, len %llu\n",actualDecodeStart, actualDecodeStartdouble, 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) {
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;
cache->samplePtr = (int16_t*) malloc(amt * cache->numChannels * sizeof(int16_t));
memset(cache->samplePtr, 0, amt * cache->numChannels * sizeof(int16_t));
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)// && (res == eProgressSuccess || res == eProgressStopped))
{
//if(mCurrentPos < start+origLen)
while (sc->m_pktRemainingSiz > 0)
//Fill the cache with decoded samples
if (DecodeFrame(sc,false) < 0)
break;
// If something useable was decoded - write it to the buffer
//note that bufStart and len are updated by the WriteData function.
if (sc->m_frameValid && len > 0)
WriteData(sc,bufStart,len,channel);
}
// Cleanup after frame decoding
if (sc->m_pktValid)
{
@ -255,8 +376,7 @@ void ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCoun
}
}
}
// Flush the decoders if we're done.
if(!sc && len>0)
{
@ -264,8 +384,6 @@ void ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCoun
{
if (DecodeFrame(mScs[i], true) == 0)
{
WriteData(mScs[i],bufStart,len,channel);
if (mScs[i]->m_pktValid)
{
#if FFMPEG_STABLE
@ -278,21 +396,28 @@ void ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCoun
}
}
}
//this next call takes data, start and len as reference variables and updates them to reflect the new area that is needed.
FillDataFromCache(bufStart,start,len,channel);
//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, sampleCount start, sampleCount& len, unsigned int channel)
int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, 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.
@ -301,7 +426,7 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount start, samp
//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()-1;
int searchEnd = mDecodeCache.size();
int guess;
if(searchEnd>kODFFmpegSearchThreshold)
{
@ -309,20 +434,17 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount start, samp
//by guessing where our hit will be.
while(searchStart+1<searchEnd)
{
guess = searchStart+ (searchEnd-searchStart)* ((float)start - mDecodeCache[searchStart]->start )/mDecodeCache[searchEnd]->start;
if(guess == searchEnd)
guess--; //scoot on down to guarantee at max a final condition with spacing of one
else if(guess == searchStart)
guess = (searchStart+searchEnd)/2;//find a midpoint.
guess = (searchStart+searchEnd)/2;//find a midpoint. //searchStart+ (searchEnd-searchStart)* ((float)start - mDecodeCache[searchStart]->start )/mDecodeCache[searchEnd]->start;
if(mDecodeCache[guess]->start>start)
searchEnd = guess;
//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;
}
}
//most recent caches are at the end of the vector, so start there.
//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.
@ -334,13 +456,11 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount start, samp
//ffmpeg only uses 16 bit ints
int16_t* outBuf;
outBuf = (int16_t*)data;
//for debug
//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;
@ -370,8 +490,8 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount start, samp
len -=samplesHit;
}
}
//if we've had our fill, leave
if(len<=0)
//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;
@ -504,62 +624,51 @@ int ODFFmpegDecoder::DecodeFrame(streamContext *sc, bool flushing)
cache->samplePtr = (int16_t*) malloc(sc->m_decodedAudioSamplesValidSiz);
memcpy(cache->samplePtr,sc->m_decodedAudioSamples,sc->m_decodedAudioSamplesValidSiz);
//TODO:this WILL NOT work with seeking..
mCurrentPos+=cache->len;
mDecodeCache.push_back(cache);
mNumSamplesInCache+=cache->len;
//if the cache is too big, drop some
while(mNumSamplesInCache>kMaxSamplesInCache)
{
mNumSamplesInCache-=mDecodeCache[0]->len;
free(mDecodeCache[0]->samplePtr);
delete mDecodeCache[0];
mDecodeCache.erase(mDecodeCache.begin());
}
InsertCache(cache);
}
return 0;
}
int ODFFmpegDecoder::WriteData(streamContext *sc,samplePtr & data, sampleCount &len, unsigned int channel)
{
// Find the stream index in mScs array
int streamid = -1;
for (int i = 0; i < mNumStreams; i++)
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)
{
if (mScs[i] == sc)
{
streamid = i;
break;
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;
}
// Stream is not found. This should not really happen
if (streamid == -1)
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)
{
return 0;//mchinen:changed from 1 so we can return size
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);
}
int nChannels = sc->m_stream->codec->channels < sc->m_initialchannels ? sc->m_stream->codec->channels : sc->m_initialchannels;
// Separate the channels
int index = 0;
//get the number of samples per channel
int decodedLen = (sc->m_decodedAudioSamplesValidSiz/sizeof(int16_t))/sc->m_stream->codec->channels;
//cast to 16 bit int.
int16_t* outbuf = (int16_t*)data;
while (index < decodedLen && index<len)
{
//this is interleaved
outbuf[index]= sc->m_decodedAudioSamples[index*nChannels +channel];
index++;
}
len-=index;
//we update data pointer too- but it is a typedef'd char* so be careful with the pointer math
data+= index* (sizeof(int16_t)/sizeof(*data));
return index;
}
#endif

View File

@ -25,7 +25,7 @@ class ODDecodeFFmpegTask:public ODDecodeTask
public:
/// Constructs an ODTask
ODDecodeFFmpegTask(void* scs,int numStreams, WaveTrack*** channels, void* formatContext);
ODDecodeFFmpegTask(void* scs,int numStreams, WaveTrack*** channels, void* formatContext, int streamIndex);
virtual ~ODDecodeFFmpegTask();
virtual ODTask* Clone();
@ -36,15 +36,12 @@ class ODDecodeFFmpegTask:public ODDecodeTask
///Subclasses should override to return respective type.
virtual unsigned int GetODType(){return eODFFMPEG;}
/// overridden because we cannot always seek - this depends on the file and our confidence which is
/// computed by this function.
virtual bool SeekingAllowed();
protected:
WaveTrack*** mChannels;
int mNumStreams;
void* mScs;
void* mFormatContext;
int mStreamIndex;
};
#endif //__ODDECODEFFMPEGTASK__

View File

@ -166,7 +166,7 @@ FLAC__StreamDecoderWriteStatus ODFLACFile::write_callback(const FLAC__Frame *fra
///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.
void ODFlacDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)
int ODFlacDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)
{
//we need to lock this so the target stays fixed over the seek/write callback.
@ -191,7 +191,7 @@ void ODFlacDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
if(!mFile->seek_absolute(start))
{
mFlacFileLock.Unlock();
return;
return -1;
}
while(mDecodeBufferWritePosition<mDecodeBufferLen)
@ -204,6 +204,7 @@ void ODFlacDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
}
//insert into blockfile and
//calculate summary happen in ODDecodeBlockFile::WriteODDecodeBlockFile, where this method is also called.
return 1;
}
///Read header. Subclasses must override. Probably should save the info somewhere.

View File

@ -106,7 +106,7 @@ public:
///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 void Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel);
virtual int Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel);
///Read header. Subclasses must override. Probably should save the info somewhere.

View File

@ -49,6 +49,8 @@ void ODDecodeTask::DoSomeInternal()
{
bf = mBlockFiles[0];
int ret = 1;
//first check to see if the ref count is at least 2. It should have one
//from when we added it to this instance's mBlockFiles array, and one from
//the Wavetrack/sequence. If it doesn't it has been deleted and we should forget it.
@ -63,13 +65,15 @@ void ODDecodeTask::DoSomeInternal()
if(!decoder->IsInitialized())
decoder->Init();
bf->SetODFileDecoder(decoder);
bf->DoWriteBlockFile();
ret = bf->DoWriteBlockFile();
bf->UnlockRead();
success = true;
blockStartSample = bf->GetStart();
blockEndSample = blockStartSample + bf->GetLength();
mComputedBlockFiles++;
if(ret >= 0) {
success = true;
blockStartSample = bf->GetStart();
blockEndSample = blockStartSample + bf->GetLength();
mComputedBlockFiles++;
}
}
else
{
@ -78,21 +82,23 @@ void ODDecodeTask::DoSomeInternal()
mMaxBlockFiles--;
}
//Release the refcount we placed on it.
bf->Deref();
//take it out of the array - we are done with it.
mBlockFiles.erase(mBlockFiles.begin());
//Release the refcount we placed on it if we are successful
if(ret >= 0 ) {
bf->Deref();
//take it out of the array - we are done with it.
mBlockFiles.erase(mBlockFiles.begin());
//upddate the gui for all associated blocks. It doesn't matter that we're hitting more wavetracks then we should
//because this loop runs a number of times equal to the number of tracks, they probably are getting processed in
//the next iteration at the same sample window.
mWaveTrackMutex.Lock();
for(size_t i=0;i<mWaveTracks.size();i++)
{
if(success && mWaveTracks[i])
mWaveTracks[i]->AddInvalidRegion(blockStartSample,blockEndSample);
//upddate the gui for all associated blocks. It doesn't matter that we're hitting more wavetracks then we should
//because this loop runs a number of times equal to the number of tracks, they probably are getting processed in
//the next iteration at the same sample window.
mWaveTrackMutex.Lock();
for(size_t i=0;i<mWaveTracks.size();i++)
{
if(success && mWaveTracks[i])
mWaveTracks[i]->AddInvalidRegion(blockStartSample,blockEndSample);
}
mWaveTrackMutex.Unlock();
}
mWaveTrackMutex.Unlock();
}
//update percentage complete.
@ -106,6 +112,15 @@ void ODDecodeTask::CalculatePercentComplete()
mPercentCompleteMutex.Unlock();
}
bool ODDecodeTask::SeekingAllowed()
{
for(int i=0;i<mDecoders.size();i++) {
if(!mDecoders[i]->SeekingAllowed())
return false;
}
return true;
}
///by default creates the order of the wavetrack to load.
void ODDecodeTask::Update()
{

View File

@ -48,9 +48,7 @@ class ODDecodeTask:public ODTask
virtual ODTask* Clone()=0;
/// subclasses need to override this if they cannot always seek.
/// seeking will be enabled once this is true.
virtual bool SeekingAllowed(){return true;}
virtual bool SeekingAllowed();
///changes the tasks associated with this Waveform to process the task from a different point in the track
///this is overridden from ODTask because certain classes don't allow users to seek sometimes, or not at all.
@ -113,13 +111,16 @@ public:
virtual bool ReadHeader()=0;
virtual bool Init(){return ReadHeader();}
virtual bool SeekingAllowed(){return true;}
///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 should be created by the ODFileDecoder implementing this method.
///It should set the format parameter so that the client code can deal with it.
///This class should call ReadHeader() first, so it knows the length, and can prepare
///the file object if it needs to.
virtual void Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)=0;
///returns negative value for failure, 0 or positive value for success.
virtual int Decode(samplePtr & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)=0;
wxString GetFileName(){return mFName;}