1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-25 16:48:44 +02:00

Make EXPERIMENTAL_OD_FFMPEG compilable, and change some memory management

This commit is contained in:
Paul Licameli 2016-04-02 00:09:54 -04:00
commit a67cfe9e5c
4 changed files with 149 additions and 124 deletions

View File

@ -16,6 +16,8 @@ Describes shared object that is used to access FFmpeg libraries.
#if !defined(__AUDACITY_FFMPEG__) #if !defined(__AUDACITY_FFMPEG__)
#define __AUDACITY_FFMPEG__ #define __AUDACITY_FFMPEG__
#include "MemoryX.h"
// TODO: Determine whether the libav* headers come from the FFmpeg or libav // TODO: Determine whether the libav* headers come from the FFmpeg or libav
// project and set IS_FFMPEG_PROJECT depending on it. // project and set IS_FFMPEG_PROJECT depending on it.
#define IS_FFMPEG_PROJECT 1 #define IS_FFMPEG_PROJECT 1
@ -395,7 +397,7 @@ int ufile_fopen(AVIOContext **s, const wxString & name, int flags);
int ufile_fopen_input(AVFormatContext **ic_ptr, wxString & name); int ufile_fopen_input(AVFormatContext **ic_ptr, wxString & name);
int ufile_close(AVIOContext *pb); int ufile_close(AVIOContext *pb);
typedef struct _streamContext struct streamContext
{ {
bool m_use; // TRUE = this stream will be loaded into Audacity bool m_use; // TRUE = this stream will be loaded into Audacity
AVStream *m_stream; // an AVStream * AVStream *m_stream; // an AVStream *
@ -421,7 +423,12 @@ typedef struct _streamContext
int m_osamplesize; // output sample size in bytes int m_osamplesize; // output sample size in bytes
sampleFormat m_osamplefmt; // output sample format sampleFormat m_osamplefmt; // output sample format
} streamContext; streamContext() { memset(this, 0, sizeof(*this)); }
~streamContext();
};
using Scs = ArrayOf<std::unique_ptr<streamContext>>;
using ScsPtr = std::shared_ptr<Scs>;
// common utility functions // common utility functions
// utility calls that are shared with ImportFFmpeg and ODDecodeFFmpegTask // utility calls that are shared with ImportFFmpeg and ODDecodeFFmpegTask
@ -853,6 +860,14 @@ extern "C" {
(linesize, nb_channels, nb_samples, sample_fmt, align) (linesize, nb_channels, nb_samples, sample_fmt, align)
); );
}; };
inline streamContext::~streamContext()
{
if (m_decodedAudioSamples)
av_free(m_decodedAudioSamples);
}
#endif #endif
#endif // USE_FFMPEG #endif // USE_FFMPEG

View File

@ -32,6 +32,7 @@ Licensed under the GNU General Public License v2 or later
#endif #endif
#include "../Experimental.h" #include "../Experimental.h"
#include "../MemoryX.h"
#define DESC _("FFmpeg-compatible files") #define DESC _("FFmpeg-compatible files")
@ -256,14 +257,14 @@ public:
void SetStreamUsage(wxInt32 StreamID, bool Use) void SetStreamUsage(wxInt32 StreamID, bool Use)
{ {
if (StreamID < mNumStreams) if (StreamID < mNumStreams)
mScs[StreamID]->m_use = Use; mScs->get()[StreamID]->m_use = Use;
} }
private: private:
AVFormatContext *mFormatContext; //!< Format description, also contains metadata and some useful info AVFormatContext *mFormatContext; //!< Format description, also contains metadata and some useful info
int mNumStreams; //!< mNumstreams is less or equal to mFormatContext->nb_streams int mNumStreams; //!< mNumstreams is less or equal to mFormatContext->nb_streams
streamContext **mScs; //!< Array of pointers to stream contexts. Length is mNumStreams. ScsPtr mScs; //!< Points to array of pointers to stream contexts, which may be shared with a decoder task.
wxArrayString *mStreamInfo; //!< Array of stream descriptions. Length is mNumStreams wxArrayString *mStreamInfo; //!< Array of stream descriptions. Length is mNumStreams
wxInt64 mProgressPos; //!< Current timestamp, file position or whatever is used as first argument for Update() wxInt64 mProgressPos; //!< Current timestamp, file position or whatever is used as first argument for Update()
@ -382,15 +383,14 @@ bool FFmpegImportFileHandle::InitCodecs()
{ {
// Allocate the array of pointers to hold stream contexts pointers // Allocate the array of pointers to hold stream contexts pointers
// Some of the allocated space may be unused (corresponds to video, subtitle, or undecodeable audio streams) // Some of the allocated space may be unused (corresponds to video, subtitle, or undecodeable audio streams)
mScs = (streamContext**)malloc(sizeof(streamContext**)*mFormatContext->nb_streams); mScs = std::make_shared<Scs>(mFormatContext->nb_streams);
// Fill the stream contexts // Fill the stream contexts
for (unsigned int i = 0; i < mFormatContext->nb_streams; i++) for (unsigned int i = 0; i < mFormatContext->nb_streams; i++)
{ {
if (mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) if (mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{ {
//Create a context //Create a context
streamContext *sc = new streamContext; auto sc = std::make_unique<streamContext>();
memset(sc,0,sizeof(*sc));
sc->m_stream = mFormatContext->streams[i]; sc->m_stream = mFormatContext->streams[i];
sc->m_codecCtx = sc->m_stream->codec; sc->m_codecCtx = sc->m_stream->codec;
@ -400,14 +400,12 @@ bool FFmpegImportFileHandle::InitCodecs()
{ {
wxLogError(wxT("FFmpeg : avcodec_find_decoder() failed. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name); wxLogError(wxT("FFmpeg : avcodec_find_decoder() failed. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name);
//FFmpeg can't decode this stream, skip it //FFmpeg can't decode this stream, skip it
delete sc;
continue; continue;
} }
if (codec->type != sc->m_codecCtx->codec_type) if (codec->type != sc->m_codecCtx->codec_type)
{ {
wxLogError(wxT("FFmpeg : Codec type mismatch, skipping. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name); wxLogError(wxT("FFmpeg : Codec type mismatch, skipping. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name);
//Non-audio codec reported as audio? Nevertheless, we don't need THIS. //Non-audio codec reported as audio? Nevertheless, we don't need THIS.
delete sc;
continue; continue;
} }
@ -415,7 +413,6 @@ bool FFmpegImportFileHandle::InitCodecs()
{ {
wxLogError(wxT("FFmpeg : avcodec_open() failed. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name); wxLogError(wxT("FFmpeg : avcodec_open() failed. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name);
//Can't open decoder - skip this stream //Can't open decoder - skip this stream
delete sc;
continue; continue;
} }
@ -440,7 +437,7 @@ bool FFmpegImportFileHandle::InitCodecs()
} }
strinfo.Printf(_("Index[%02x] Codec[%s], Language[%s], Bitrate[%s], Channels[%d], Duration[%d]"),sc->m_stream->id,codec->name,lang.c_str(),bitrate.c_str(),sc->m_stream->codec->channels, duration); strinfo.Printf(_("Index[%02x] Codec[%s], Language[%s], Bitrate[%s], Channels[%d], Duration[%d]"),sc->m_stream->id,codec->name,lang.c_str(),bitrate.c_str(),sc->m_stream->codec->channels, duration);
mStreamInfo->Add(strinfo); mStreamInfo->Add(strinfo);
mScs[mNumStreams++] = sc; mScs->get()[mNumStreams++] = std::move(sc);
} }
//for video and unknown streams do nothing //for video and unknown streams do nothing
} }
@ -469,14 +466,14 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
CreateProgress(); CreateProgress();
// Remove stream contexts which are not marked for importing and adjust mScs and mNumStreams accordingly // Remove stream contexts which are not marked for importing and adjust mScs and mNumStreams accordingly
const auto scs = mScs->get();
for (int i = 0; i < mNumStreams;) for (int i = 0; i < mNumStreams;)
{ {
if (!mScs[i]->m_use) if (!scs[i]->m_use)
{ {
delete mScs[i];
for (int j = i; j < mNumStreams - 1; j++) for (int j = i; j < mNumStreams - 1; j++)
{ {
mScs[j] = mScs[j+1]; scs[j] = std::move(scs[j+1]);
} }
mNumStreams--; mNumStreams--;
} }
@ -490,32 +487,33 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
{ {
++s; ++s;
switch (mScs[s]->m_stream->codec->sample_fmt) auto sc = scs[s].get();
switch (sc->m_stream->codec->sample_fmt)
{ {
case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_U8P:
case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S16P:
mScs[s]->m_osamplesize = sizeof(int16_t); sc->m_osamplesize = sizeof(int16_t);
mScs[s]->m_osamplefmt = int16Sample; sc->m_osamplefmt = int16Sample;
break; break;
default: default:
mScs[s]->m_osamplesize = sizeof(float); sc->m_osamplesize = sizeof(float);
mScs[s]->m_osamplefmt = floatSample; sc->m_osamplefmt = floatSample;
break; break;
} }
// There is a possibility that number of channels will change over time, but we do not have WaveTracks for NEW channels. Remember the number of channels and stick to it. // There is a possibility that number of channels will change over time, but we do not have WaveTracks for NEW channels. Remember the number of channels and stick to it.
mScs[s]->m_initialchannels = mScs[s]->m_stream->codec->channels; sc->m_initialchannels = sc->m_stream->codec->channels;
stream.resize(mScs[s]->m_stream->codec->channels); stream.resize(sc->m_stream->codec->channels);
int c = -1; int c = -1;
for (auto &channel : stream) for (auto &channel : stream)
{ {
++c; ++c;
channel = trackFactory->NewWaveTrack(mScs[s]->m_osamplefmt, mScs[s]->m_stream->codec->sample_rate); channel = trackFactory->NewWaveTrack(sc->m_osamplefmt, sc->m_stream->codec->sample_rate);
if (mScs[s]->m_stream->codec->channels == 2) if (sc->m_stream->codec->channels == 2)
{ {
switch (c) switch (c)
{ {
@ -544,10 +542,11 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
++s; ++s;
int64_t stream_delay = 0; int64_t stream_delay = 0;
if (mScs[s]->m_stream->start_time != int64_t(AV_NOPTS_VALUE) && mScs[s]->m_stream->start_time > 0) auto sc = scs[s].get();
if (sc->m_stream->start_time != int64_t(AV_NOPTS_VALUE) && sc->m_stream->start_time > 0)
{ {
stream_delay = mScs[s]->m_stream->start_time; stream_delay = sc->m_stream->start_time;
wxLogDebug(wxT("Stream %d start_time = %lld, that would be %f milliseconds."), s, (long long) mScs[s]->m_stream->start_time, double(mScs[s]->m_stream->start_time)/AV_TIME_BASE*1000); wxLogDebug(wxT("Stream %d start_time = %lld, that would be %f milliseconds."), s, (long long) sc->m_stream->start_time, double(sc->m_stream->start_time)/AV_TIME_BASE*1000);
} }
if (stream_delay != 0) if (stream_delay != 0)
{ {
@ -573,22 +572,26 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
if (mUsingOD) { if (mUsingOD) {
std::vector<ODDecodeFFmpegTask*> tasks; std::vector<ODDecodeFFmpegTask*> tasks;
//append blockfiles to each stream and add an individual ODDecodeTask for each one. //append blockfiles to each stream and add an individual ODDecodeTask for each one.
for (int s = 0; s < mNumStreams; s++) { s = -1;
ODDecodeFFmpegTask* odTask=new ODDecodeFFmpegTask(mScs,mNumStreams,mChannels,mFormatContext, s); for (const auto &stream : mChannels) {
++s;
ODDecodeFFmpegTask* odTask =
new ODDecodeFFmpegTask(mScs, ODDecodeFFmpegTask::FromList(mChannels), mFormatContext, s);
odTask->CreateFileDecoder(mFilename); odTask->CreateFileDecoder(mFilename);
//each stream has different duration. We need to know it if seeking is to be allowed. //each stream has different duration. We need to know it if seeking is to be allowed.
sampleCount sampleDuration = 0; sampleCount sampleDuration = 0;
if (mScs[s]->m_stream->duration > 0) auto sc = scs[s].get();
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; if (sc->m_stream->duration > 0)
sampleDuration = ((sampleCount)sc->m_stream->duration * sc->m_stream->time_base.num), sc->m_stream->codec->sample_rate / sc->m_stream->time_base.den;
else else
sampleDuration = ((sampleCount)mFormatContext->duration *mScs[s]->m_stream->codec->sample_rate) / AV_TIME_BASE; sampleDuration = ((sampleCount)mFormatContext->duration *sc->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); // printf(" OD duration samples %qi, sr %d, secs %d\n",sampleDuration, (int)sc->m_stream->codec->sample_rate, (int)sampleDuration/sc->m_stream->codec->sample_rate);
//for each wavetrack within the stream add coded blockfiles //for each wavetrack within the stream add coded blockfiles
for (int c = 0; c < mScs[s]->m_stream->codec->channels; c++) { for (int c = 0; c < sc->m_stream->codec->channels; c++) {
WaveTrack *t = mChannels[s][c]; WaveTrack *t = stream[c].get();
odTask->AddWaveTrack(t); odTask->AddWaveTrack(t);
sampleCount maxBlockSize = t->GetMaxBlockSize(); sampleCount maxBlockSize = t->GetMaxBlockSize();
@ -598,12 +601,12 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
if (i + blockLen > sampleDuration) if (i + blockLen > sampleDuration)
blockLen = sampleDuration - i; blockLen = sampleDuration - i;
t->AppendCoded(mFilename, i, blockLen, c,ODTask::eODFFMPEG); t->AppendCoded(mFilename, i, blockLen, c, ODTask::eODFFMPEG);
// This only works well for single streams since we assume // This only works well for single streams since we assume
// each stream is of the same duration and channels // each stream is of the same duration and channels
res = mProgress->Update(i+sampleDuration*c+ sampleDuration*mScs[s]->m_stream->codec->channels*s, res = mProgress->Update(i+sampleDuration*c+ sampleDuration*sc->m_stream->codec->channels*s,
sampleDuration*mScs[s]->m_stream->codec->channels*mNumStreams); sampleDuration*sc->m_stream->codec->channels*mNumStreams);
if (res != eProgressSuccess) if (res != eProgressSuccess)
break; break;
} }
@ -621,10 +624,9 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
} }
} else { } else {
#endif #endif
streamContext *sc = NULL;
// Read next frame. // Read next frame.
while ((sc = ReadNextFrame()) != NULL && (res == eProgressSuccess)) for (streamContext *sc; (sc = ReadNextFrame()) != NULL && (res == eProgressSuccess);)
{ {
// ReadNextFrame returns 1 if stream is not to be imported // ReadNextFrame returns 1 if stream is not to be imported
if (sc != (streamContext*)1) if (sc != (streamContext*)1)
@ -654,14 +656,15 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
{ {
for (int i = 0; i < mNumStreams; i++) for (int i = 0; i < mNumStreams; i++)
{ {
if (DecodeFrame(mScs[i], true) == 0) auto sc = scs[i].get();
if (DecodeFrame(sc, true) == 0)
{ {
WriteData(mScs[i]); WriteData(sc);
if (mScs[i]->m_pktValid) if (sc->m_pktValid)
{ {
av_free_packet(&mScs[i]->m_pkt); av_free_packet(&sc->m_pkt);
mScs[i]->m_pktValid = 0; sc->m_pktValid = 0;
} }
} }
} }
@ -693,7 +696,11 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
streamContext *FFmpegImportFileHandle::ReadNextFrame() streamContext *FFmpegImportFileHandle::ReadNextFrame()
{ {
return import_ffmpeg_read_next_frame(mFormatContext, mScs, mNumStreams); // Get pointer to array of contiguous unique_ptrs
auto scs = mScs->get();
// This reinterpret_cast to array of plain pointers is innocent
return import_ffmpeg_read_next_frame
(mFormatContext, reinterpret_cast<streamContext**>(scs), mNumStreams);
} }
int FFmpegImportFileHandle::DecodeFrame(streamContext *sc, bool flushing) int FFmpegImportFileHandle::DecodeFrame(streamContext *sc, bool flushing)
@ -706,9 +713,10 @@ int FFmpegImportFileHandle::WriteData(streamContext *sc)
// Find the stream index in mScs array // Find the stream index in mScs array
int streamid = -1; int streamid = -1;
auto iter = mChannels.begin(); auto iter = mChannels.begin();
auto scs = mScs->get();
for (int i = 0; i < mNumStreams; ++iter, ++i) for (int i = 0; i < mNumStreams; ++iter, ++i)
{ {
if (mScs[i] == sc) if (scs[i].get() == sc)
{ {
streamid = i; streamid = i;
break; break;
@ -858,14 +866,6 @@ FFmpegImportFileHandle::~FFmpegImportFileHandle()
av_log_set_callback(av_log_default_callback); 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);
#ifdef EXPERIMENTAL_OD_FFMPEG #ifdef EXPERIMENTAL_OD_FFMPEG
}//mUsingOD }//mUsingOD
#endif #endif

View File

@ -23,6 +23,9 @@
#ifdef USE_FFMPEG #ifdef USE_FFMPEG
#ifdef EXPERIMENTAL_OD_FFMPEG #ifdef EXPERIMENTAL_OD_FFMPEG
#include <algorithm>
#include <functional>
#include "../FFmpeg.h" // which brings in avcodec.h, avformat.h #include "../FFmpeg.h" // which brings in avcodec.h, avformat.h
#include "../import/ImportFFmpeg.h" #include "../import/ImportFFmpeg.h"
@ -54,11 +57,13 @@ typedef struct _FFmpegDecodeCache
///class to decode a particular file (one per file). Saves info such as filename and length (after the header is read.) ///class to decode a particular file (one per file). Saves info such as filename and length (after the header is read.)
class ODFFmpegDecoder final : public ODFileDecoder class ODFFmpegDecoder final : public ODFileDecoder
{ {
public: public:
///This should handle unicode converted to UTF-8 on mac/linux, but OD TODO:check on windows ///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); ODFFmpegDecoder(const wxString & fileName,
const ScsPtr &scs,
ODDecodeFFmpegTask::Streams &&channels,
AVFormatContext* formatContext,
int streamIndex);
virtual ~ODFFmpegDecoder(); virtual ~ODFFmpegDecoder();
///Decodes the samples for this blockfile from the real file into a float buffer. ///Decodes the samples for this blockfile from the real file into a float buffer.
@ -92,9 +97,8 @@ private:
///\return 0 on success, -1 if it can't decode any further ///\return 0 on success, -1 if it can't decode any further
int DecodeFrame(streamContext *sc, bool flushing); int DecodeFrame(streamContext *sc, bool flushing);
int mNumStreams; ScsPtr mScs; //!< Pointer to array of pointers to stream contexts.
streamContext **mScs; //!< Array of pointers to stream contexts. Length is mNumStreams. ODDecodeFFmpegTask::Streams mChannels;
WaveTrack*** mChannels;
AVFormatContext *mFormatContext; //!< Format description, also contains metadata and some useful info AVFormatContext *mFormatContext; //!< Format description, also contains metadata and some useful info
std::vector<FFMpegDecodeCache*> mDecodeCache; std::vector<FFMpegDecodeCache*> mDecodeCache;
int mNumSamplesInCache; int mNumSamplesInCache;
@ -105,33 +109,44 @@ private:
int mStreamIndex; int mStreamIndex;
}; };
auto ODDecodeFFmpegTask::FromList(const std::list<TrackHolders> &channels) -> Streams
{
Streams streams;
streams.reserve(channels.size());
using namespace std;
transform(channels.begin(), channels.end(), back_inserter(streams),
[](const TrackHolders &holders) {
Channels channels;
channels.reserve(holders.size());
transform(holders.begin(), holders.end(), back_inserter(channels),
mem_fun_ref(&TrackHolders::value_type::get)
);
return channels;
}
);
return streams;
}
//------ ODDecodeFFmpegTask definitions //------ ODDecodeFFmpegTask definitions
ODDecodeFFmpegTask::ODDecodeFFmpegTask(void* scs,int numStreams, WaveTrack*** channels, void* formatContext, int streamIndex) ODDecodeFFmpegTask::ODDecodeFFmpegTask(const ScsPtr &scs, Streams &&channels, void* formatContext, int streamIndex)
: mChannels(std::move(channels))
{ {
mScs=scs; mScs=scs;
mNumStreams=numStreams;
mChannels=channels;
mFormatContext = formatContext; mFormatContext = formatContext;
mStreamIndex = streamIndex; 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() ODDecodeFFmpegTask::~ODDecodeFFmpegTask()
{ {
} }
ODTask* ODDecodeFFmpegTask::Clone() ODTask *ODDecodeFFmpegTask::Clone()
{ {
//we need to create copies of mScs. It would be better to use a reference counter system. auto clone = std::make_unique<ODDecodeFFmpegTask>(mScs, Streams{ mChannels }, mFormatContext, mStreamIndex);
ODDecodeFFmpegTask* clone = new ODDecodeFFmpegTask((void*)mScs,mNumStreams,mChannels,mFormatContext,mStreamIndex);
clone->mDemandSample=GetDemandSample(); clone->mDemandSample=GetDemandSample();
//the decoders and blockfiles should not be copied. They are created as the task runs. //the decoders and blockfiles should not be copied. They are created as the task runs.
return clone; return clone.release();
} }
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles. ///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
@ -140,7 +155,9 @@ ODTask* ODDecodeFFmpegTask::Clone()
ODFileDecoder* ODDecodeFFmpegTask::CreateFileDecoder(const wxString & fileName) ODFileDecoder* ODDecodeFFmpegTask::CreateFileDecoder(const wxString & fileName)
{ {
// Open the file for import // Open the file for import
ODFFmpegDecoder *decoder = new ODFFmpegDecoder(fileName, (streamContext**) mScs,mNumStreams,mChannels,(AVFormatContext*)mFormatContext, mStreamIndex); ODFFmpegDecoder *decoder =
new ODFFmpegDecoder(fileName, mScs, ODDecodeFFmpegTask::Streams{ mChannels },
(AVFormatContext*)mFormatContext, mStreamIndex);
mDecoders.push_back(decoder); mDecoders.push_back(decoder);
return decoder; return decoder;
@ -219,10 +236,13 @@ test_failed:
//------ ODDecodeFFmpegFileDecoder //------ ODDecodeFFmpegFileDecoder
ODFFmpegDecoder::ODFFmpegDecoder(const wxString & fileName, streamContext** scs,int numStreams,WaveTrack*** channels, AVFormatContext* formatContext, int streamIndex) ODFFmpegDecoder::ODFFmpegDecoder(const wxString & fileName,
const ScsPtr &scs,
ODDecodeFFmpegTask::Streams &&channels,
AVFormatContext* formatContext,
int streamIndex)
:ODFileDecoder(fileName), :ODFileDecoder(fileName),
//mSamplesDone(0), //mSamplesDone(0),
mNumStreams(numStreams),
mScs(scs), mScs(scs),
mFormatContext(formatContext), mFormatContext(formatContext),
mNumSamplesInCache(0), mNumSamplesInCache(0),
@ -233,22 +253,14 @@ mStreamIndex(streamIndex)
PickFFmpegLibs(); PickFFmpegLibs();
//do a shallow copy of the 2d array. //do a shallow copy of the 2d array.
mChannels = new WaveTrack **[mNumStreams]; mChannels = std::move(channels);
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. // get the current stream start time.
int64_t stream_delay = 0; int64_t stream_delay = 0;
if (mScs[streamIndex]->m_stream->start_time != int64_t(AV_NOPTS_VALUE) && const auto sc = mScs->get()[streamIndex].get();
mScs[streamIndex]->m_stream->start_time > 0) { if (sc->m_stream->start_time != int64_t(AV_NOPTS_VALUE) &&
stream_delay = mScs[streamIndex]->m_stream->start_time; sc->m_stream->start_time > 0) {
stream_delay = sc->m_stream->start_time;
} }
mCurrentPos = double(stream_delay) / AV_TIME_BASE; mCurrentPos = double(stream_delay) / AV_TIME_BASE;
@ -265,15 +277,6 @@ ODFFmpegDecoder::~ODFFmpegDecoder()
av_log_set_callback(av_log_default_callback); 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. //DELETE our caches.
while(mDecodeCache.size()) while(mDecodeCache.size())
{ {
@ -282,12 +285,6 @@ ODFFmpegDecoder::~ODFFmpegDecoder()
mDecodeCache.erase(mDecodeCache.begin()); mDecodeCache.erase(mDecodeCache.begin());
} }
//free the channel pointer arrays
for (int s = 0; s < mNumStreams; s++)
{
delete[] mChannels[s];
}
delete[] mChannels;
DropFFmpegLibs(); DropFFmpegLibs();
} }
@ -298,7 +295,9 @@ ODFFmpegDecoder::~ODFFmpegDecoder()
#define kMaxSeekRewindAttempts 8 #define kMaxSeekRewindAttempts 8
int ODFFmpegDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel) int ODFFmpegDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCount start, sampleCount len, unsigned int channel)
{ {
format = mScs[mStreamIndex]->m_osamplefmt; auto scs = mScs->get();
auto sci = scs[mStreamIndex].get();
format = sci->m_osamplefmt;
data.Allocate(len, format); data.Allocate(len, format);
samplePtr bufStart = data.ptr(); samplePtr bufStart = data.ptr();
@ -315,7 +314,7 @@ int ODFFmpegDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCo
bool seeking = false; 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. //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()) { if(len && (mCurrentPos > start + len || mCurrentPos + kDecodeSampleAllowance < start ) && SeekingAllowed()) {
sc = mScs[mStreamIndex]; sc = sci;
AVStream* st = sc->m_stream; AVStream* st = sc->m_stream;
int stindex = -1; int stindex = -1;
uint64_t targetts; uint64_t targetts;
@ -404,9 +403,9 @@ int ODFFmpegDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCo
// 16 bit int out. // 16 bit int out.
// 32 bit int, float, double mean float out. // 32 bit int, float, double mean float out.
if (format == int16Sample) if (format == int16Sample)
cache->samplefmt = SAMPLE_FMT_S16; cache->samplefmt = AV_SAMPLE_FMT_S16;
else else
cache->samplefmt = SAMPLE_FMT_FLT; cache->samplefmt = AV_SAMPLE_FMT_FLT;
cache->samplePtr = (uint8_t*) malloc(amt * cache->numChannels * SAMPLE_SIZE(format)); cache->samplePtr = (uint8_t*) malloc(amt * cache->numChannels * SAMPLE_SIZE(format));
@ -435,14 +434,15 @@ int ODFFmpegDecoder::Decode(SampleBuffer & data, sampleFormat & format, sampleCo
// Flush the decoders if we're done. // Flush the decoders if we're done.
if((!sc || sc == (streamContext*) 1)&& len>0) if((!sc || sc == (streamContext*) 1)&& len>0)
{ {
for (int i = 0; i < mNumStreams; i++) for (int i = 0; i < mChannels.size(); i++)
{ {
if (DecodeFrame(mScs[i], true) == 0) sc = scs[i].get();
if (DecodeFrame(sc, true) == 0)
{ {
if (mScs[i]->m_pktValid) if (sc->m_pktValid)
{ {
av_free_packet(&mScs[i]->m_pkt); av_free_packet(&sc->m_pkt);
mScs[i]->m_pktValid = 0; sc->m_pktValid = 0;
} }
} }
} }
@ -530,27 +530,27 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleFormat outFormat,
inIndex = (hitStartInCache + j) * nChannels + channel; inIndex = (hitStartInCache + j) * nChannels + channel;
switch (mDecodeCache[i]->samplefmt) switch (mDecodeCache[i]->samplefmt)
{ {
case SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8:
//printf("u8 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); //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; ((int16_t *)outBuf)[outIndex] = (int16_t) (((uint8_t*)mDecodeCache[i]->samplePtr)[inIndex] - 0x80) << 8;
break; break;
case SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16:
//printf("u16 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); //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]; ((int16_t *)outBuf)[outIndex] = ((int16_t*)mDecodeCache[i]->samplePtr)[inIndex];
break; break;
case SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32:
//printf("s32 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); //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)); ((float *)outBuf)[outIndex] = (float) ((int32_t*)mDecodeCache[i]->samplePtr)[inIndex] * (1.0 / (1 << 31));
break; break;
case SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLT:
//printf("f in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); //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]; ((float *)outBuf)[outIndex] = (float) ((float*)mDecodeCache[i]->samplePtr)[inIndex];
break; break;
case SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBL:
//printf("dbl in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len); //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]; ((float *)outBuf)[outIndex] = (float) ((double*)mDecodeCache[i]->samplePtr)[inIndex];
break; break;
@ -588,7 +588,11 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleFormat outFormat,
//get the right stream pointer. //get the right stream pointer.
streamContext* ODFFmpegDecoder::ReadNextFrame() streamContext* ODFFmpegDecoder::ReadNextFrame()
{ {
return import_ffmpeg_read_next_frame(mFormatContext, mScs, mNumStreams); // Get pointer to array of contiguous unique_ptrs
auto scs = mScs->get();
// This reinterpret_cast to array of plain pointers is innocent
return import_ffmpeg_read_next_frame
(mFormatContext, reinterpret_cast<streamContext**>(scs), mChannels.size());
} }

View File

@ -10,6 +10,7 @@
******************************************************************/ ******************************************************************/
#include "../Experimental.h" #include "../Experimental.h"
#include "../MemoryX.h"
#ifdef EXPERIMENTAL_OD_FFMPEG #ifdef EXPERIMENTAL_OD_FFMPEG
@ -25,13 +26,17 @@ class WaveTrack;
/// A class representing a modular task to be used with the On-Demand structures. /// A class representing a modular task to be used with the On-Demand structures.
class ODDecodeFFmpegTask final : public ODDecodeTask class ODDecodeFFmpegTask final : public ODDecodeTask
{ {
public: public:
using Channels = std::vector < WaveTrack* >;
using Streams = std::vector < Channels >;
static Streams FromList(const std::list<TrackHolders> &channels);
/// Constructs an ODTask /// Constructs an ODTask
ODDecodeFFmpegTask(void* scs,int numStreams, WaveTrack*** channels, void* formatContext, int streamIndex); ODDecodeFFmpegTask(const ScsPtr &scs, Streams &&channels, void* formatContext, int streamIndex);
virtual ~ODDecodeFFmpegTask(); virtual ~ODDecodeFFmpegTask();
ODTask* Clone() override; ODTask *Clone() override;
///Creates an ODFileDecoder that decodes a file of filetype the subclass handles. ///Creates an ODFileDecoder that decodes a file of filetype the subclass handles.
ODFileDecoder* CreateFileDecoder(const wxString & fileName) override; ODFileDecoder* CreateFileDecoder(const wxString & fileName) override;
@ -39,10 +44,11 @@ class ODDecodeFFmpegTask final : public ODDecodeTask
///Subclasses should override to return respective type. ///Subclasses should override to return respective type.
unsigned int GetODType() override {return eODFFMPEG;} unsigned int GetODType() override {return eODFFMPEG;}
protected: protected:
WaveTrack*** mChannels; // non-owning pointers to WaveTracks:
int mNumStreams; Streams mChannels;
void* mScs;
ScsPtr mScs;
void* mFormatContext; void* mFormatContext;
int mStreamIndex; int mStreamIndex;
}; };