mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-13 22:21:11 +02:00
added on-demand support for non-seeking loading of FFmpeg files. This feature is not on, controlled by ifdefs via the #define EXPERIMENTAL_OD_FFMPEG in Experimental.h
This commit is contained in:
@@ -83,6 +83,8 @@
|
|||||||
// have not been fully imported in builds without FLAC support, so disabled for
|
// have not been fully imported in builds without FLAC support, so disabled for
|
||||||
// 2.0 release
|
// 2.0 release
|
||||||
//#define EXPERIMENTAL_OD_FLAC
|
//#define EXPERIMENTAL_OD_FLAC
|
||||||
|
// similarly for FFmpeg:
|
||||||
|
//#define EXPERIMENTAL_OD_FFMPEG
|
||||||
|
|
||||||
// Philip Van Baren 01 July 2009
|
// Philip Van Baren 01 July 2009
|
||||||
// Replace RealFFT() and PowerSpectrum function to use (faster) RealFFTf function
|
// Replace RealFFT() and PowerSpectrum function to use (faster) RealFFTf function
|
||||||
|
@@ -153,7 +153,15 @@ void av_log_wx_callback(void* ptr, int level, const char* fmt, va_list vl)
|
|||||||
case 2: cpt = wxT("Debug"); break;
|
case 2: cpt = wxT("Debug"); break;
|
||||||
default: cpt = wxT("Log"); break;
|
default: cpt = wxT("Log"); break;
|
||||||
}
|
}
|
||||||
wxLogMessage(wxT("%s: %s"),cpt.c_str(),printstring.c_str());
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
//if the decoding happens thru OD then this gets called from a non main thread, which means wxLogMessage
|
||||||
|
//will crash.
|
||||||
|
//TODO:find some workaround for the log. perhaps use ODManager as a bridge. for now just print
|
||||||
|
if(!wxThread::IsMain())
|
||||||
|
printf("%s: %s\n",cpt.c_str(),printstring.c_str());
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
wxLogMessage(wxT("%s: %s"),cpt.c_str(),printstring.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
//======================= Unicode aware uri protocol for FFmpeg
|
//======================= Unicode aware uri protocol for FFmpeg
|
||||||
|
26
src/FFmpeg.h
26
src/FFmpeg.h
@@ -57,6 +57,8 @@ extern "C" {
|
|||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
|
|
||||||
|
#include "Experimental.h"
|
||||||
|
|
||||||
// if you needed them, any other audacity header files would go here
|
// if you needed them, any other audacity header files would go here
|
||||||
|
|
||||||
/* These defines apply whether or not ffmpeg is available */
|
/* These defines apply whether or not ffmpeg is available */
|
||||||
@@ -397,6 +399,30 @@ void DropFFmpegLibs();
|
|||||||
int ufile_fopen(ByteIOContext **s, const wxString & name, int flags);
|
int ufile_fopen(ByteIOContext **s, const wxString & name, int flags);
|
||||||
int ufile_fopen_input(AVFormatContext **ic_ptr, wxString & name);
|
int ufile_fopen_input(AVFormatContext **ic_ptr, wxString & name);
|
||||||
|
|
||||||
|
//moving from ImportFFmpeg.cpp to FFMpeg.h so other cpp files can use this struct.
|
||||||
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
typedef struct _streamContext
|
||||||
|
{
|
||||||
|
bool m_use; // TRUE = this stream will be loaded into Audacity
|
||||||
|
AVStream *m_stream; // an AVStream *
|
||||||
|
AVCodecContext *m_codecCtx; // pointer to m_stream->codec
|
||||||
|
|
||||||
|
AVPacket m_pkt; // the last AVPacket we read for this stream
|
||||||
|
int m_pktValid; // is m_pkt valid?
|
||||||
|
uint8_t *m_pktDataPtr; // pointer into m_pkt.data
|
||||||
|
int m_pktRemainingSiz;
|
||||||
|
|
||||||
|
int64_t m_pts; // the current presentation time of the input stream
|
||||||
|
int64_t m_ptsOffset; // packets associated with stream are relative to this
|
||||||
|
|
||||||
|
int m_frameValid; // is m_decodedVideoFrame/m_decodedAudioSamples valid?
|
||||||
|
int16_t *m_decodedAudioSamples; // decoded audio samples stored here
|
||||||
|
unsigned int m_decodedAudioSamplesSiz; // current size of m_decodedAudioSamples
|
||||||
|
int m_decodedAudioSamplesValidSiz; // # valid bytes in m_decodedAudioSamples
|
||||||
|
int m_initialchannels; // number of channels allocated when we begin the importing. Assumes that number of channels doesn't change on the fly.
|
||||||
|
} streamContext;
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // USE_FFMPEG
|
#endif // USE_FFMPEG
|
||||||
#endif // __AUDACITY_FFMPEG__
|
#endif // __AUDACITY_FFMPEG__
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@ Licensed under the GNU General Public License v2 or later
|
|||||||
|
|
||||||
#include "../Audacity.h" // needed before FFmpeg.h
|
#include "../Audacity.h" // needed before FFmpeg.h
|
||||||
#include "../FFmpeg.h" // which brings in avcodec.h, avformat.h
|
#include "../FFmpeg.h" // which brings in avcodec.h, avformat.h
|
||||||
|
#include "../ondemand/ODManager.h"
|
||||||
#ifndef WX_PRECOMP
|
#ifndef WX_PRECOMP
|
||||||
// Include your minimal set of headers here, or wx.h
|
// Include your minimal set of headers here, or wx.h
|
||||||
#include <wx/window.h>
|
#include <wx/window.h>
|
||||||
@@ -153,10 +154,16 @@ static const wxChar *exts[] =
|
|||||||
#include "../WaveTrack.h"
|
#include "../WaveTrack.h"
|
||||||
#include "ImportPlugin.h"
|
#include "ImportPlugin.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
#include "ODDecodeFFMpegTask.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
extern FFmpegLibs *FFmpegLibsInst;
|
extern FFmpegLibs *FFmpegLibsInst;
|
||||||
|
|
||||||
class FFmpegImportFileHandle;
|
class FFmpegImportFileHandle;
|
||||||
|
//moving from ImportFFmpeg.cpp to FFMpeg.h so other cpp files can use this struct.
|
||||||
|
#ifndef EXPERIMENTAL_OD_FFMPEG
|
||||||
typedef struct _streamContext
|
typedef struct _streamContext
|
||||||
{
|
{
|
||||||
bool m_use; // TRUE = this stream will be loaded into Audacity
|
bool m_use; // TRUE = this stream will be loaded into Audacity
|
||||||
@@ -177,7 +184,7 @@ typedef struct _streamContext
|
|||||||
int m_decodedAudioSamplesValidSiz; // # valid bytes in m_decodedAudioSamples
|
int m_decodedAudioSamplesValidSiz; // # valid bytes in m_decodedAudioSamples
|
||||||
int m_initialchannels; // number of channels allocated when we begin the importing. Assumes that number of channels doesn't change on the fly.
|
int m_initialchannels; // number of channels allocated when we begin the importing. Assumes that number of channels doesn't change on the fly.
|
||||||
} streamContext;
|
} streamContext;
|
||||||
|
#endif
|
||||||
/// A representative of FFmpeg loader in
|
/// A representative of FFmpeg loader in
|
||||||
/// the Audacity import plugin list
|
/// the Audacity import plugin list
|
||||||
class FFmpegImportPlugin : public ImportPlugin
|
class FFmpegImportPlugin : public ImportPlugin
|
||||||
@@ -278,6 +285,10 @@ private:
|
|||||||
bool mStopped; //!< True if importing was stopped by user
|
bool mStopped; //!< True if importing was stopped by user
|
||||||
wxString mName;
|
wxString mName;
|
||||||
WaveTrack ***mChannels; //!< 2-dimentional array of WaveTrack's. First dimention - streams, second - channels of a stream. Length is mNumStreams
|
WaveTrack ***mChannels; //!< 2-dimentional array of WaveTrack's. First dimention - streams, second - channels of a stream. Length is mNumStreams
|
||||||
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
bool mUsingOD;
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -533,11 +544,68 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the heart of the importing process
|
// This is the heart of the importing process
|
||||||
streamContext *sc = NULL;
|
streamContext *sc = NULL;
|
||||||
// The result of Import() to be returend. It will be something other than zero if user canceled or some error appears.
|
// The result of Import() to be returend. It will be something other than zero if user canceled or some error appears.
|
||||||
int res = eProgressSuccess;
|
int res = eProgressSuccess;
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
mUsingOD = true; //TODO: for now just use it - later use a prefs
|
||||||
|
//at this point we know the file is good and that we have to load the number of channels in mScs[s]->m_stream->codec->channels;
|
||||||
|
//so for OD loading we create the tracks and releasee the modal lock after starting the ODTask.
|
||||||
|
std::vector<ODDecodeFFmpegTask*> tasks;
|
||||||
|
//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);
|
||||||
|
ODFileDecoder* odDecoder = (ODFileDecoder*)odTask->CreateFileDecoder(mFilename);
|
||||||
|
|
||||||
|
//each stream has different duration. We need to know it if seeking is to be allowed.
|
||||||
|
sampleCount sampleDuration = 0;
|
||||||
|
if (mScs[s]->m_stream->duration > 0)
|
||||||
|
sampleDuration = ((sampleCount)mScs[s]->m_stream->duration * mScs[s]->m_stream->time_base.num) *mScs[s]->m_stream->codec->sample_rate / mScs[s]->m_stream->time_base.den;
|
||||||
|
else
|
||||||
|
sampleDuration = ((sampleCount)mFormatContext->duration *mScs[s]->m_stream->codec->sample_rate) / AV_TIME_BASE;
|
||||||
|
|
||||||
|
// printf(" OD duration samples %qi, sr %d, secs %d\n",sampleDuration, (int)mScs[s]->m_stream->codec->sample_rate,(int)sampleDuration/mScs[s]->m_stream->codec->sample_rate);
|
||||||
|
|
||||||
|
//for each wavetrack within the stream add coded blockfiles
|
||||||
|
for (int c = 0; c < mScs[s]->m_stream->codec->channels; c++)
|
||||||
|
{
|
||||||
|
WaveTrack *t = mChannels[s][c];
|
||||||
|
odTask->AddWaveTrack(t);
|
||||||
|
|
||||||
|
sampleCount maxBlockSize = t->GetMaxBlockSize();
|
||||||
|
//use the maximum blockfile size to divide the sections (about 11secs per blockfile at 44.1khz)
|
||||||
|
for (sampleCount i = 0; i < sampleDuration; i += maxBlockSize)
|
||||||
|
{
|
||||||
|
sampleCount blockLen = maxBlockSize;
|
||||||
|
if (i + blockLen > sampleDuration)
|
||||||
|
blockLen = sampleDuration - i;
|
||||||
|
|
||||||
|
t->AppendCoded(mFilename, i, blockLen, c,ODTask::eODFFMPEG);
|
||||||
|
|
||||||
|
// This only works well for single streams since we assume
|
||||||
|
// each stream is of the same duration and channels
|
||||||
|
res = mProgress->Update(i+sampleDuration*c+ sampleDuration*mScs[s]->m_stream->codec->channels*s,
|
||||||
|
sampleDuration*mScs[s]->m_stream->codec->channels*mNumStreams);
|
||||||
|
if (res != eProgressSuccess)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.push_back(odTask);
|
||||||
|
}
|
||||||
|
//Now we add the tasks and let them run, or delete them if the user cancelled
|
||||||
|
for(int i=0;i<tasks.size();i++)
|
||||||
|
{
|
||||||
|
if(res==eProgressSuccess)
|
||||||
|
ODManager::Instance()->AddNewTask(tasks[i]);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete tasks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else //ifndef EXPERIMENTAL_OD_FFMPEG
|
||||||
// Read next frame.
|
// Read next frame.
|
||||||
while ((sc = ReadNextFrame()) != NULL && (res == eProgressSuccess))
|
while ((sc = ReadNextFrame()) != NULL && (res == eProgressSuccess))
|
||||||
{
|
{
|
||||||
@@ -589,6 +657,7 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif //EXPERIMENTAL_OD_FFMPEG
|
||||||
|
|
||||||
// Something bad happened - destroy everything!
|
// Something bad happened - destroy everything!
|
||||||
if (res == eProgressCancelled || res == eProgressFailed)
|
if (res == eProgressCancelled || res == eProgressFailed)
|
||||||
@@ -836,6 +905,11 @@ void FFmpegImportFileHandle::WriteMetadata(AVFormatContext *avf,Tags *tags)
|
|||||||
|
|
||||||
FFmpegImportFileHandle::~FFmpegImportFileHandle()
|
FFmpegImportFileHandle::~FFmpegImportFileHandle()
|
||||||
{
|
{
|
||||||
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
//ODDecodeFFmpegTask takes ownership and deltes it there.
|
||||||
|
if(!mUsingOD)
|
||||||
|
{
|
||||||
|
#endif
|
||||||
if (FFmpegLibsInst->ValidLibsLoaded())
|
if (FFmpegLibsInst->ValidLibsLoaded())
|
||||||
{
|
{
|
||||||
if (mFormatContext) FFmpegLibsInst->av_close_input_file(mFormatContext);
|
if (mFormatContext) FFmpegLibsInst->av_close_input_file(mFormatContext);
|
||||||
@@ -850,6 +924,10 @@ FFmpegImportFileHandle::~FFmpegImportFileHandle()
|
|||||||
delete mScs[i];
|
delete mScs[i];
|
||||||
}
|
}
|
||||||
free(mScs);
|
free(mScs);
|
||||||
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
}//mUsingOD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
delete mStreamInfo;
|
delete mStreamInfo;
|
||||||
|
|
||||||
|
569
src/ondemand/ODDecodeFFmpegTask.cpp
Normal file
569
src/ondemand/ODDecodeFFmpegTask.cpp
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
#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 kMaxSamplesInCache 4410000
|
||||||
|
|
||||||
|
//struct for caching the decoded samples to be used over multiple blockfiles
|
||||||
|
typedef struct _FFmpegDecodeCache
|
||||||
|
{
|
||||||
|
int16_t* samplePtr;//interleaved samples - currently ffmpeg only uses 16 bit int
|
||||||
|
sampleCount start;
|
||||||
|
sampleCount len;
|
||||||
|
int numChannels;
|
||||||
|
} 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);
|
||||||
|
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 void 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(){}
|
||||||
|
|
||||||
|
private:
|
||||||
|
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.
|
||||||
|
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);
|
||||||
|
|
||||||
|
///! 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
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//------ ODDecodeFFmpegTask definitions
|
||||||
|
ODDecodeFFmpegTask::ODDecodeFFmpegTask(void* scs,int numStreams, WaveTrack*** channels, void* formatContext)
|
||||||
|
{
|
||||||
|
mScs=scs;
|
||||||
|
mNumStreams=numStreams;
|
||||||
|
mChannels=channels;
|
||||||
|
mFormatContext = formatContext;
|
||||||
|
//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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
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 ODDecodeFFmpegTask::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------ ODDecodeFFmpegFileDecoder
|
||||||
|
ODFFmpegDecoder::ODFFmpegDecoder(const wxString & fileName, streamContext** scs,int numStreams,WaveTrack*** channels, AVFormatContext* formatContext)
|
||||||
|
:ODFileDecoder(fileName),
|
||||||
|
//mSamplesDone(0),
|
||||||
|
mNumStreams(numStreams),
|
||||||
|
mScs(scs),
|
||||||
|
mFormatContext(formatContext),
|
||||||
|
mCurrentPos(0),
|
||||||
|
mNumSamplesInCache(0)
|
||||||
|
{
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//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) FFmpegLibsInst->av_close_input_file(mFormatContext);
|
||||||
|
FFmpegLibsInst->av_log_set_callback(FFmpegLibsInst->av_log_default_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < mNumStreams; i++)
|
||||||
|
{
|
||||||
|
if (mScs[i]->m_decodedAudioSamples != NULL)
|
||||||
|
FFmpegLibsInst->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();
|
||||||
|
}
|
||||||
|
|
||||||
|
void 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;
|
||||||
|
|
||||||
|
data = NewSamples(len, format);
|
||||||
|
samplePtr bufStart = data;
|
||||||
|
streamContext* sc = NULL;
|
||||||
|
|
||||||
|
sampleCount origLen=len;
|
||||||
|
|
||||||
|
int nChannels;
|
||||||
|
|
||||||
|
//TODO update this to work with seek - this only works linearly now.
|
||||||
|
if(mCurrentPos > start)
|
||||||
|
{
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
//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)
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
#if FFMPEG_STABLE
|
||||||
|
av_free_packet(&sc->m_pkt);
|
||||||
|
#else
|
||||||
|
FFmpegLibsInst->av_free_packet(&sc->m_pkt);
|
||||||
|
#endif
|
||||||
|
sc->m_pktValid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Flush the decoders if we're done.
|
||||||
|
if(!sc && len>0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < mNumStreams; i++)
|
||||||
|
{
|
||||||
|
if (DecodeFrame(mScs[i], true) == 0)
|
||||||
|
{
|
||||||
|
WriteData(mScs[i],bufStart,len,channel);
|
||||||
|
|
||||||
|
if (mScs[i]->m_pktValid)
|
||||||
|
{
|
||||||
|
#if FFMPEG_STABLE
|
||||||
|
av_free_packet(&mScs[i]->m_pkt);
|
||||||
|
#else
|
||||||
|
FFmpegLibsInst->av_free_packet(&mScs[i]->m_pkt);
|
||||||
|
#endif
|
||||||
|
mScs[i]->m_pktValid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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()-1;
|
||||||
|
int mid;
|
||||||
|
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-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.
|
||||||
|
|
||||||
|
if(mDecodeCache[guess]->start>start)
|
||||||
|
searchEnd = guess;
|
||||||
|
else
|
||||||
|
searchStart = guess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//most recent caches are at the end of the vector, so start there.
|
||||||
|
for(int i=searchStart;i<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)
|
||||||
|
{
|
||||||
|
//ffmpeg only uses 16 bit ints
|
||||||
|
int16_t* outBuf;
|
||||||
|
outBuf = (int16_t*)data;
|
||||||
|
//for debug
|
||||||
|
FFMpegDecodeCache* cache = mDecodeCache[i];
|
||||||
|
//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;
|
||||||
|
for(int j=0;j<samplesHit;j++)
|
||||||
|
{
|
||||||
|
outBuf[j+hitStartInRequest]=mDecodeCache[i]->samplePtr[(hitStartInCache+j)*nChannels+channel];
|
||||||
|
}
|
||||||
|
//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* (sizeof(int16_t)/sizeof(*data));
|
||||||
|
start+=samplesHit;
|
||||||
|
len -=samplesHit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if we've had our fill, leave
|
||||||
|
if(len<=0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return samplesFilled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//these next few look highly refactorable.
|
||||||
|
//get the right stream pointer.
|
||||||
|
streamContext* ODFFmpegDecoder::ReadNextFrame()
|
||||||
|
{
|
||||||
|
streamContext *sc = NULL;
|
||||||
|
AVPacket pkt;
|
||||||
|
|
||||||
|
if (FFmpegLibsInst->av_read_frame(mFormatContext,&pkt) < 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find a stream to which this frame belongs to
|
||||||
|
for (int i = 0; i < mNumStreams; i++)
|
||||||
|
{
|
||||||
|
if (mScs[i]->m_stream->index == pkt.stream_index)
|
||||||
|
sc = mScs[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Off-stream packet. Don't panic, just skip it.
|
||||||
|
// When not all streams are selected for import this will happen very often.
|
||||||
|
if (sc == NULL)
|
||||||
|
{
|
||||||
|
#if FFMPEG_STABLE
|
||||||
|
av_free_packet(&pkt);
|
||||||
|
#else
|
||||||
|
FFmpegLibsInst->av_free_packet(&pkt);
|
||||||
|
#endif
|
||||||
|
return (streamContext*)1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the frame to the stream context
|
||||||
|
memcpy(&sc->m_pkt, &pkt, sizeof(AVPacket));
|
||||||
|
|
||||||
|
sc->m_pktValid = 1;
|
||||||
|
sc->m_pktDataPtr = pkt.data;
|
||||||
|
sc->m_pktRemainingSiz = pkt.size;
|
||||||
|
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ODFFmpegDecoder::DecodeFrame(streamContext *sc, bool flushing)
|
||||||
|
{
|
||||||
|
int nBytesDecoded;
|
||||||
|
wxUint8 *pDecode = sc->m_pktDataPtr;
|
||||||
|
int nDecodeSiz = sc->m_pktRemainingSiz;
|
||||||
|
|
||||||
|
//check to see if the sc has already been decoded in our sample range
|
||||||
|
|
||||||
|
sc->m_frameValid = 0;
|
||||||
|
|
||||||
|
if (flushing)
|
||||||
|
{
|
||||||
|
// If we're flushing the decoders we don't actually have any new data to decode.
|
||||||
|
pDecode = NULL;
|
||||||
|
nDecodeSiz = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!sc->m_pktValid || (sc->m_pktRemainingSiz <= 0))
|
||||||
|
{
|
||||||
|
//No more data
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reallocate the audio sample buffer if it's smaller than the frame size.
|
||||||
|
if (!flushing)
|
||||||
|
{
|
||||||
|
// av_fast_realloc() will only reallocate the buffer if m_decodedAudioSamplesSiz is
|
||||||
|
// smaller than third parameter. It also returns new size in m_decodedAudioSamplesSiz
|
||||||
|
//\warning { for some reason using the following macro call right in the function call
|
||||||
|
// causes Audacity to crash in some unknown place. With "newsize" it works fine }
|
||||||
|
int newsize = FFMAX(sc->m_pkt.size*sizeof(*sc->m_decodedAudioSamples), AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
||||||
|
sc->m_decodedAudioSamples = (int16_t*)FFmpegLibsInst->av_fast_realloc(sc->m_decodedAudioSamples,
|
||||||
|
&sc->m_decodedAudioSamplesSiz,
|
||||||
|
newsize
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sc->m_decodedAudioSamples == NULL)
|
||||||
|
{
|
||||||
|
//Can't allocate bytes
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// avcodec_decode_audio2() expects the size of the output buffer as the 3rd parameter but
|
||||||
|
// also returns the number of bytes it decoded in the same parameter.
|
||||||
|
sc->m_decodedAudioSamplesValidSiz = sc->m_decodedAudioSamplesSiz;
|
||||||
|
nBytesDecoded = FFmpegLibsInst->avcodec_decode_audio2(sc->m_codecCtx,
|
||||||
|
sc->m_decodedAudioSamples, // out
|
||||||
|
&sc->m_decodedAudioSamplesValidSiz, // in/out
|
||||||
|
pDecode, nDecodeSiz); // in
|
||||||
|
|
||||||
|
if (nBytesDecoded < 0)
|
||||||
|
{
|
||||||
|
// Decoding failed. Don't stop.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may not have read all of the data from this packet. If so, the user can call again.
|
||||||
|
// Whether or not they do depends on if m_pktRemainingSiz == 0 (they can check).
|
||||||
|
sc->m_pktDataPtr += nBytesDecoded;
|
||||||
|
sc->m_pktRemainingSiz -= nBytesDecoded;
|
||||||
|
|
||||||
|
// At this point it's normally safe to assume that we've read some samples. However, the MPEG
|
||||||
|
// audio decoder is broken. If this is the case then we just return with m_frameValid == 0
|
||||||
|
// but m_pktRemainingSiz perhaps != 0, so the user can call again.
|
||||||
|
if (sc->m_decodedAudioSamplesValidSiz > 0)
|
||||||
|
{
|
||||||
|
sc->m_frameValid = 1;
|
||||||
|
|
||||||
|
//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/sizeof(int16_t) )/cache->numChannels;
|
||||||
|
cache->start=mCurrentPos;
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
if (mScs[i] == sc)
|
||||||
|
{
|
||||||
|
streamid = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Stream is not found. This should not really happen
|
||||||
|
if (streamid == -1)
|
||||||
|
{
|
||||||
|
return 0;//mchinen:changed from 1 so we can return size
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
51
src/ondemand/ODDecodeFFmpegTask.h
Normal file
51
src/ondemand/ODDecodeFFmpegTask.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* ODDecodeFFmpegTask.h
|
||||||
|
* Audacity
|
||||||
|
*
|
||||||
|
* Created by apple on 3/8/10.
|
||||||
|
* Copyright 2010 __MyCompanyName__. All rights reserved.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "../Experimental.h"
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_OD_FFMPEG
|
||||||
|
|
||||||
|
#ifndef __ODDECODEFFMPEGTASK__
|
||||||
|
#define __ODDECODEFFMPEGTASK__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "ODDecodeTask.h"
|
||||||
|
#include "ODTaskThread.h"
|
||||||
|
|
||||||
|
class ODFileDecoder;
|
||||||
|
class WaveTrack;
|
||||||
|
/// A class representing a modular task to be used with the On-Demand structures.
|
||||||
|
class ODDecodeFFmpegTask:public ODDecodeTask
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructs an ODTask
|
||||||
|
ODDecodeFFmpegTask(void* scs,int numStreams, WaveTrack*** channels, void* formatContext);
|
||||||
|
virtual ~ODDecodeFFmpegTask();
|
||||||
|
|
||||||
|
virtual ODTask* Clone();
|
||||||
|
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
|
||||||
|
virtual ODFileDecoder* CreateFileDecoder(const wxString & fileName);
|
||||||
|
|
||||||
|
///Lets other classes know that this class handles the ffmpeg type
|
||||||
|
///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;
|
||||||
|
};
|
||||||
|
#endif //__ODDECODEFFMPEGTASK__
|
||||||
|
|
||||||
|
#endif //EXPERIMENTAL_OD_FFMPEG
|
@@ -219,6 +219,17 @@ void ODDecodeTask::OrderBlockFiles(std::vector<ODDecodeBlockFile*> &unorderedBlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///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.
|
||||||
|
void ODDecodeTask::DemandTrackUpdate(WaveTrack* track, double seconds)
|
||||||
|
{
|
||||||
|
//only update if the subclass says we can seek.
|
||||||
|
if(SeekingAllowed())
|
||||||
|
ODTask::DemandTrackUpdate(track,seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///there could be the ODBlockFiles of several FLACs in one track (after copy and pasting)
|
///there could be the ODBlockFiles of several FLACs in one track (after copy and pasting)
|
||||||
///so we keep a list of decoders that keep track of the file names, etc, and check the blocks against them.
|
///so we keep a list of decoders that keep track of the file names, etc, and check the blocks against them.
|
||||||
///Blocks that have IsDataAvailable()==false are blockfiles to be decoded. if BlockFile::GetDecodeType()==ODDecodeTask::GetODType() then
|
///Blocks that have IsDataAvailable()==false are blockfiles to be decoded. if BlockFile::GetDecodeType()==ODDecodeTask::GetODType() then
|
||||||
@@ -230,7 +241,11 @@ ODFileDecoder* ODDecodeTask::GetOrCreateMatchingFileDecoder(ODDecodeBlockFile* b
|
|||||||
//see if the filename matches any of our decoders, if so, return it.
|
//see if the filename matches any of our decoders, if so, return it.
|
||||||
for(int i=0;i<(int)mDecoders.size();i++)
|
for(int i=0;i<(int)mDecoders.size();i++)
|
||||||
{
|
{
|
||||||
if(mDecoders[i]->GetFileName()==blockFile->GetAudioFileName().GetFullPath())
|
//we check filename and decode type, since two types of ODDecoders might work with the same filetype
|
||||||
|
//e.g., FFmpeg and LibMad import both do MP3s. TODO: is this necessary? in theory we filter this when
|
||||||
|
//updating our list of blockfiles.
|
||||||
|
if(mDecoders[i]->GetFileName()==blockFile->GetAudioFileName().GetFullPath() &&
|
||||||
|
GetODType() == blockFile->GetDecodeType() )
|
||||||
{
|
{
|
||||||
ret = mDecoders[i];
|
ret = mDecoders[i];
|
||||||
break;
|
break;
|
||||||
|
@@ -48,6 +48,14 @@ class ODDecodeTask:public ODTask
|
|||||||
|
|
||||||
virtual ODTask* Clone()=0;
|
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;}
|
||||||
|
|
||||||
|
///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.
|
||||||
|
virtual void DemandTrackUpdate(WaveTrack* track, double seconds);
|
||||||
|
|
||||||
///Return the task name
|
///Return the task name
|
||||||
virtual const char* GetTaskName(){return "ODDecodeTask";}
|
virtual const char* GetTaskName(){return "ODDecodeTask";}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user