mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-30 15:18:42 +02:00
integrate OD FFmpeg to work with multiple formats, including wav
also move streamContext to ffmpeg.h to allow for refactoring of import code
This commit is contained in:
parent
8cb2a38fbe
commit
816b58bc08
130
src/FFmpeg.cpp
130
src/FFmpeg.cpp
@ -420,6 +420,136 @@ fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
streamContext *import_ffmpeg_read_next_frame(AVFormatContext* formatContext,
|
||||
streamContext** streams,
|
||||
unsigned int numStreams)
|
||||
{
|
||||
streamContext *sc = NULL;
|
||||
AVPacket pkt;
|
||||
|
||||
if (av_read_frame(formatContext,&pkt) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find a stream to which this frame belongs to
|
||||
for (int i = 0; i < numStreams; i++)
|
||||
{
|
||||
if (streams[i]->m_stream->index == pkt.stream_index)
|
||||
sc = streams[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)
|
||||
{
|
||||
av_free_packet(&pkt);
|
||||
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 import_ffmpeg_decode_frame(streamContext *sc, bool flushing)
|
||||
{
|
||||
int nBytesDecoded;
|
||||
wxUint8 *pDecode = sc->m_pktDataPtr;
|
||||
int nDecodeSiz = sc->m_pktRemainingSiz;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sc->m_samplefmt = sc->m_codecCtx->sample_fmt;
|
||||
sc->m_samplesize = av_get_bits_per_sample_fmt(sc->m_samplefmt) / 8;
|
||||
|
||||
unsigned int newsize = FFMAX(sc->m_pkt.size * sc->m_samplesize, AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
||||
// Reallocate the audio sample buffer if it's smaller than the frame size.
|
||||
if (newsize > sc->m_decodedAudioSamplesSiz )
|
||||
{
|
||||
if (sc->m_decodedAudioSamples)
|
||||
{
|
||||
av_free(sc->m_decodedAudioSamples);
|
||||
}
|
||||
|
||||
sc->m_decodedAudioSamples = (uint8_t *) av_malloc(newsize);
|
||||
sc->m_decodedAudioSamplesSiz = newsize;
|
||||
|
||||
if (sc->m_decodedAudioSamples == NULL)
|
||||
{
|
||||
//Can't allocate bytes
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sc->m_decodedAudioSamplesValidSiz = sc->m_decodedAudioSamplesSiz;
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52, 25, 0)
|
||||
// avcodec_decode_audio3() expects the size of the output buffer as the 3rd parameter but
|
||||
// also returns the number of bytes it decoded in the same parameter.
|
||||
AVPacket avpkt;
|
||||
av_init_packet(&avpkt);
|
||||
avpkt.data = pDecode;
|
||||
avpkt.size = nDecodeSiz;
|
||||
nBytesDecoded =
|
||||
avcodec_decode_audio3(sc->m_codecCtx,
|
||||
(int16_t *)sc->m_decodedAudioSamples, // out
|
||||
&sc->m_decodedAudioSamplesValidSiz, // in/out
|
||||
&avpkt); // in
|
||||
#else
|
||||
// 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.
|
||||
nBytesDecoded =
|
||||
avcodec_decode_audio2(sc->m_codecCtx,
|
||||
(int16_t *) sc->m_decodedAudioSamples, // out
|
||||
&sc->m_decodedAudioSamplesValidSiz, // in/out
|
||||
pDecode, // in
|
||||
nDecodeSiz); // in
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************/
|
||||
|
||||
class FFmpegNotFoundDialog;
|
||||
|
12
src/FFmpeg.h
12
src/FFmpeg.h
@ -105,6 +105,7 @@ void FFmpegStartup();
|
||||
|
||||
bool LoadFFmpeg(bool showerror);
|
||||
|
||||
|
||||
/// If Audacity failed to load libav*, this dialog
|
||||
/// shows up and tells user about that. It will pop-up
|
||||
/// again and again until it is disabled.
|
||||
@ -330,8 +331,6 @@ void DropFFmpegLibs();
|
||||
int ufile_fopen(AVIOContext **s, const wxString & name, int flags);
|
||||
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
|
||||
@ -359,7 +358,14 @@ typedef struct _streamContext
|
||||
sampleFormat m_osamplefmt; // output sample format
|
||||
|
||||
} streamContext;
|
||||
#endif
|
||||
|
||||
// common utility functions
|
||||
// utility calls that are shared with ImportFFmpeg and ODDecodeFFmpegTask
|
||||
streamContext *import_ffmpeg_read_next_frame(AVFormatContext* formatContext,
|
||||
streamContext** streams,
|
||||
unsigned int numStreams);
|
||||
|
||||
int import_ffmpeg_decode_frame(streamContext *sc, bool flushing);
|
||||
|
||||
extern "C" {
|
||||
// A little explanation of what's going on here.
|
||||
|
@ -46,8 +46,10 @@ ODDecodeBlockFile::ODDecodeBlockFile(wxFileName baseFileName,wxFileName audioFil
|
||||
mAliasStart(aliasStart),
|
||||
mAliasChannel(aliasChannel)
|
||||
{
|
||||
mDecoder = NULL;
|
||||
mDataAvailable=false;
|
||||
mAudioFileName = audioFileName;
|
||||
mFormat = int16Sample;
|
||||
}
|
||||
|
||||
/// Create the memory structure to refer to the given block file
|
||||
@ -60,8 +62,10 @@ ODDecodeBlockFile::ODDecodeBlockFile(wxFileName existingFile, wxFileName audioFi
|
||||
mAliasStart(aliasStart),
|
||||
mAliasChannel(aliasChannel)
|
||||
{
|
||||
mDecoder = NULL;
|
||||
mDataAvailable=dataAvailable;
|
||||
mAudioFileName = audioFileName;
|
||||
mFormat = int16Sample;
|
||||
}
|
||||
|
||||
|
||||
@ -324,8 +328,10 @@ int ODDecodeBlockFile::WriteODDecodeBlockFile()
|
||||
ret = mDecoder->Decode(sampleData, mFormat, mAliasStart, mLen, mAliasChannel);
|
||||
|
||||
mDecoderMutex.Unlock();
|
||||
if(ret < 0)
|
||||
if(ret < 0) {
|
||||
printf("ODDecodeBlockFile Decode failure\n");
|
||||
return ret; //failure
|
||||
}
|
||||
|
||||
//the summary is also calculated here.
|
||||
mFileNameMutex.Lock();
|
||||
|
@ -162,36 +162,7 @@ static const wxChar *exts[] =
|
||||
extern FFmpegLibs *FFmpegLibsInst;
|
||||
|
||||
class FFmpegImportFileHandle;
|
||||
//moving from ImportFFmpeg.cpp to FFMpeg.h so other cpp files can use this struct.
|
||||
#ifndef 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?
|
||||
uint8_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.
|
||||
|
||||
int m_samplesize; // input sample size in bytes
|
||||
SampleFormat m_samplefmt; // input sample format
|
||||
|
||||
int m_osamplesize; // output sample size in bytes
|
||||
sampleFormat m_osamplefmt; // output sample format
|
||||
|
||||
} streamContext;
|
||||
#endif
|
||||
/// A representative of FFmpeg loader in
|
||||
/// the Audacity import plugin list
|
||||
class FFmpegImportPlugin : public ImportPlugin
|
||||
@ -730,128 +701,12 @@ int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
|
||||
|
||||
streamContext *FFmpegImportFileHandle::ReadNextFrame()
|
||||
{
|
||||
streamContext *sc = NULL;
|
||||
AVPacket pkt;
|
||||
|
||||
if (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)
|
||||
{
|
||||
av_free_packet(&pkt);
|
||||
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;
|
||||
return import_ffmpeg_read_next_frame(mFormatContext, mScs, mNumStreams);
|
||||
}
|
||||
|
||||
int FFmpegImportFileHandle::DecodeFrame(streamContext *sc, bool flushing)
|
||||
{
|
||||
int nBytesDecoded;
|
||||
wxUint8 *pDecode = sc->m_pktDataPtr;
|
||||
int nDecodeSiz = sc->m_pktRemainingSiz;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sc->m_samplefmt = sc->m_codecCtx->sample_fmt;
|
||||
sc->m_samplesize = av_get_bits_per_sample_fmt(sc->m_samplefmt) / 8;
|
||||
|
||||
unsigned int newsize = FFMAX(sc->m_pkt.size * sc->m_samplesize, AVCODEC_MAX_AUDIO_FRAME_SIZE);
|
||||
// Reallocate the audio sample buffer if it's smaller than the frame size.
|
||||
if (newsize > sc->m_decodedAudioSamplesSiz )
|
||||
{
|
||||
if (sc->m_decodedAudioSamples)
|
||||
{
|
||||
av_free(sc->m_decodedAudioSamples);
|
||||
}
|
||||
|
||||
sc->m_decodedAudioSamples = (uint8_t *) av_malloc(newsize);
|
||||
sc->m_decodedAudioSamplesSiz = newsize;
|
||||
|
||||
if (sc->m_decodedAudioSamples == NULL)
|
||||
{
|
||||
//Can't allocate bytes
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sc->m_decodedAudioSamplesValidSiz = sc->m_decodedAudioSamplesSiz;
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52, 25, 0)
|
||||
// avcodec_decode_audio3() expects the size of the output buffer as the 3rd parameter but
|
||||
// also returns the number of bytes it decoded in the same parameter.
|
||||
AVPacket avpkt;
|
||||
av_init_packet(&avpkt);
|
||||
avpkt.data = pDecode;
|
||||
avpkt.size = nDecodeSiz;
|
||||
nBytesDecoded =
|
||||
avcodec_decode_audio3(sc->m_codecCtx,
|
||||
(int16_t *)sc->m_decodedAudioSamples, // out
|
||||
&sc->m_decodedAudioSamplesValidSiz, // in/out
|
||||
&avpkt); // in
|
||||
#else
|
||||
// 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.
|
||||
nBytesDecoded =
|
||||
avcodec_decode_audio2(sc->m_codecCtx,
|
||||
(int16_t *) sc->m_decodedAudioSamples, // out
|
||||
&sc->m_decodedAudioSamplesValidSiz, // in/out
|
||||
pDecode, // in
|
||||
nDecodeSiz); // in
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
return import_ffmpeg_decode_frame(sc, flushing);
|
||||
}
|
||||
|
||||
int FFmpegImportFileHandle::WriteData(streamContext *sc)
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "../Audacity.h" // needed before FFmpeg.h
|
||||
#include "../FFmpeg.h" // which brings in avcodec.h, avformat.h
|
||||
#include "../import/ImportFFmpeg.h"
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
// Include your minimal set of headers here, or wx.h
|
||||
#include <wx/window.h>
|
||||
@ -32,10 +34,12 @@ extern FFmpegLibs *FFmpegLibsInst;
|
||||
//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
|
||||
uint8_t* samplePtr;//interleaved samples
|
||||
sampleCount start;
|
||||
sampleCount len;
|
||||
int numChannels;
|
||||
SampleFormat samplefmt; // input (from libav) sample format
|
||||
|
||||
} FFMpegDecodeCache;
|
||||
|
||||
|
||||
@ -70,7 +74,7 @@ private:
|
||||
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);
|
||||
int FillDataFromCache(samplePtr & data, sampleFormat outFormat, sampleCount & start, sampleCount& len, unsigned int channel);
|
||||
///REFACTORABLE CODE FROM IMPORT FFMPEG
|
||||
///! Reads next audio frame
|
||||
///\return pointer to the stream context structure to which the frame belongs to or NULL on error, or 1 if stream is not to be imported.
|
||||
@ -262,8 +266,7 @@ ODFFmpegDecoder::~ODFFmpegDecoder()
|
||||
#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;
|
||||
format = mScs[mStreamIndex]->m_osamplefmt;
|
||||
|
||||
data = NewSamples(len, format);
|
||||
samplePtr bufStart = data;
|
||||
@ -276,7 +279,7 @@ int ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
|
||||
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);
|
||||
FillDataFromCache(bufStart, format, start,len,channel);
|
||||
}
|
||||
|
||||
|
||||
@ -287,7 +290,7 @@ int ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
|
||||
int stindex = -1;
|
||||
uint64_t targetts;
|
||||
|
||||
printf("attempting seek to %llu\n", start);
|
||||
//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 )
|
||||
@ -310,7 +313,7 @@ int ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
|
||||
|
||||
mCurrentPos = actualDecodeStart;
|
||||
//if the seek was past our desired position, rewind a bit.
|
||||
printf("seek ok to %llu samps, float: %f\n",actualDecodeStart,actualDecodeStartDouble);
|
||||
//printf("seek ok to %llu samps, float: %f\n",actualDecodeStart,actualDecodeStartDouble);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
@ -336,21 +339,35 @@ int ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
|
||||
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
|
||||
if (sc->m_stream->codec->frame_size) {
|
||||
actualDecodeStart = ((actualDecodeStart + sc->m_stream->codec->frame_size/2) / sc->m_stream->codec->frame_size) * sc->m_stream->codec->frame_size;
|
||||
}
|
||||
|
||||
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);
|
||||
//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) {
|
||||
// find the number of samples for the leading silence
|
||||
int amt = actualDecodeStart - start;
|
||||
FFMpegDecodeCache* cache = new FFMpegDecodeCache;
|
||||
|
||||
printf("skipping/zeroing %i samples. - now:%llu (%f), last:%llu, lastlen:%llu, start %llu, len %llu\n",amt,actualDecodeStart, actualDecodeStartdouble, mCurrentPos, mCurrentLen, start, len);
|
||||
//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));
|
||||
// 8 bit and 16 bit audio output from ffmpeg means
|
||||
// 16 bit int out.
|
||||
// 32 bit int, float, double mean float out.
|
||||
if (format == int16Sample)
|
||||
cache->samplefmt = SAMPLE_FMT_S16;
|
||||
else
|
||||
cache->samplefmt = SAMPLE_FMT_FLT;
|
||||
|
||||
cache->samplePtr = (uint8_t*) malloc(amt * cache->numChannels * SAMPLE_SIZE(format));
|
||||
|
||||
memset(cache->samplePtr, 0, amt * cache->numChannels * SAMPLE_SIZE(format));
|
||||
|
||||
InsertCache(cache);
|
||||
}
|
||||
@ -389,7 +406,7 @@ int ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
|
||||
}
|
||||
|
||||
//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);
|
||||
FillDataFromCache(bufStart, format, start, len, channel);
|
||||
|
||||
//if for some reason we couldn't get the samples, fill them with silence
|
||||
int16_t* outBuf = (int16_t*) bufStart;
|
||||
@ -404,11 +421,10 @@ int ODFFmpegDecoder::Decode(samplePtr & data, sampleFormat & format, sampleCount
|
||||
#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, sampleFormat outFormat, sampleCount &start, sampleCount& len, unsigned int channel)
|
||||
{
|
||||
if(mDecodeCache.size() <= 0)
|
||||
return 0;
|
||||
|
||||
int samplesFilled=0;
|
||||
|
||||
//do a search for the best position to start at.
|
||||
@ -444,9 +460,8 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount &start, sam
|
||||
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;
|
||||
uint8_t* outBuf;
|
||||
outBuf = (uint8_t*)data;
|
||||
//reject buffers that would split us into two pieces because we don't have
|
||||
//a method of dealing with this yet, and it won't happen very often.
|
||||
if(start<mDecodeCache[i]->start && start+len > mDecodeCache[i]->start+mDecodeCache[i]->len)
|
||||
@ -462,9 +477,43 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount &start, sam
|
||||
hitStartInCache = FFMAX(0,start-mDecodeCache[i]->start);
|
||||
//we also need to find out which end was hit - if it is the tail only we need to update from a later index.
|
||||
hitStartInRequest = start <mDecodeCache[i]->start?len - samplesHit: 0;
|
||||
sampleCount outIndex,inIndex;
|
||||
for(int j=0;j<samplesHit;j++)
|
||||
{
|
||||
outBuf[j+hitStartInRequest]=mDecodeCache[i]->samplePtr[(hitStartInCache+j)*nChannels+channel];
|
||||
outIndex = hitStartInRequest + j;
|
||||
inIndex = (hitStartInCache + j) * nChannels + channel;
|
||||
switch (mDecodeCache[i]->samplefmt)
|
||||
{
|
||||
case SAMPLE_FMT_U8:
|
||||
//printf("u8 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
|
||||
((int16_t *)outBuf)[outIndex] = (int16_t) (((uint8_t*)mDecodeCache[i]->samplePtr)[inIndex] - 0x80) << 8;
|
||||
break;
|
||||
|
||||
case SAMPLE_FMT_S16:
|
||||
//printf("u16 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
|
||||
((int16_t *)outBuf)[outIndex] = ((int16_t*)mDecodeCache[i]->samplePtr)[inIndex];
|
||||
break;
|
||||
|
||||
case SAMPLE_FMT_S32:
|
||||
//printf("s32 in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
|
||||
((float *)outBuf)[outIndex] = (float) ((int32_t*)mDecodeCache[i]->samplePtr)[inIndex] * (1.0 / (1 << 31));
|
||||
break;
|
||||
|
||||
case SAMPLE_FMT_FLT:
|
||||
//printf("f in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
|
||||
((float *)outBuf)[outIndex] = (float) ((float*)mDecodeCache[i]->samplePtr)[inIndex];
|
||||
break;
|
||||
|
||||
case SAMPLE_FMT_DBL:
|
||||
//printf("dbl in %llu out %llu cachelen %llu outLen %llu\n", inIndex, outIndex, mDecodeCache[i]->len, len);
|
||||
((float *)outBuf)[outIndex] = (float) ((double*)mDecodeCache[i]->samplePtr)[inIndex];
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("ODDecodeFFMPEG TASK unrecognized sample format\n");
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//update the cursor
|
||||
samplesFilled += samplesHit;
|
||||
@ -476,7 +525,7 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount &start, sam
|
||||
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));
|
||||
data+= samplesHit * (SAMPLE_SIZE(outFormat) / sizeof(*data));
|
||||
start+=samplesHit;
|
||||
len -=samplesHit;
|
||||
}
|
||||
@ -493,130 +542,15 @@ int ODFFmpegDecoder::FillDataFromCache(samplePtr & data, sampleCount &start, sam
|
||||
//get the right stream pointer.
|
||||
streamContext* ODFFmpegDecoder::ReadNextFrame()
|
||||
{
|
||||
streamContext *sc = NULL;
|
||||
AVPacket pkt;
|
||||
|
||||
if (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)
|
||||
{
|
||||
av_free_packet(&pkt);
|
||||
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;
|
||||
return import_ffmpeg_read_next_frame(mFormatContext, mScs, mNumStreams);
|
||||
}
|
||||
|
||||
|
||||
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 = (uint8_t*)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;
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52, 25, 0)
|
||||
// avcodec_decode_audio3() expects the size of the output buffer as the 3rd parameter but
|
||||
// also returns the number of bytes it decoded in the same parameter.
|
||||
AVPacket avpkt;
|
||||
av_init_packet(&avpkt);
|
||||
avpkt.data = pDecode;
|
||||
avpkt.size = nDecodeSiz;
|
||||
nBytesDecoded =
|
||||
avcodec_decode_audio3(sc->m_codecCtx,
|
||||
(int16_t *)sc->m_decodedAudioSamples, // out
|
||||
&sc->m_decodedAudioSamplesValidSiz, // in/out
|
||||
&avpkt); // in
|
||||
#else
|
||||
// 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.
|
||||
nBytesDecoded =
|
||||
avcodec_decode_audio2(sc->m_codecCtx,
|
||||
(int16_t *) sc->m_decodedAudioSamples, // out
|
||||
&sc->m_decodedAudioSamplesValidSiz, // in/out
|
||||
pDecode, // in
|
||||
nDecodeSiz); // in
|
||||
#endif
|
||||
|
||||
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;
|
||||
int ret = import_ffmpeg_decode_frame(sc, flushing);
|
||||
|
||||
if (ret == 0 && sc->m_frameValid) {
|
||||
//stick it in the cache.
|
||||
//TODO- consider growing/unioning a few cache buffers like WaveCache does.
|
||||
//however we can't use wavecache as it isn't going to handle our stereo interleaved part, and isn't for samples
|
||||
@ -624,10 +558,11 @@ int ODFFmpegDecoder::DecodeFrame(streamContext *sc, bool flushing)
|
||||
FFMpegDecodeCache* cache = new FFMpegDecodeCache;
|
||||
//len is number of samples per channel
|
||||
cache->numChannels = sc->m_stream->codec->channels;
|
||||
// Here we convert to 16 bit stero frame length
|
||||
cache->len = (sc->m_decodedAudioSamplesValidSiz/sizeof(int16_t) ) / cache->numChannels;
|
||||
|
||||
cache->len = (sc->m_decodedAudioSamplesValidSiz / sc->m_samplesize) / cache->numChannels;
|
||||
cache->start = mCurrentPos;
|
||||
cache->samplePtr = (int16_t*) malloc(sc->m_decodedAudioSamplesValidSiz);
|
||||
cache->samplePtr = (uint8_t*) malloc(sc->m_decodedAudioSamplesValidSiz);
|
||||
cache->samplefmt = sc->m_samplefmt;
|
||||
memcpy(cache->samplePtr, sc->m_decodedAudioSamples, sc->m_decodedAudioSamplesValidSiz);
|
||||
|
||||
InsertCache(cache);
|
||||
|
Loading…
x
Reference in New Issue
Block a user