From 7f2de1bf7f2d6c407d2be05473e3a8b8e39704b2 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Tue, 31 Jan 2017 15:48:04 +0200 Subject: [PATCH 1/8] SoundSourceFFmpeg: Added 'ClosableAVCodecContextPtr' to SoundSourceFFmpeg for post 3.0 API and wrapped needed AVStream API deprications. --- src/sources/soundsourceffmpeg.cpp | 144 ++++++++++++++++++++++++++---- src/sources/soundsourceffmpeg.h | 47 +++++++++- 2 files changed, 172 insertions(+), 19 deletions(-) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index 8163ecdea05..104e6093c3f 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -28,9 +28,25 @@ void initFFmpegLib() { // More than 2 channels are currently not supported const SINT kMaxChannelCount = 2; +// +// FFMPEG 3.1 obsolettes AVSteam->codec to favor AVStream->codepar +// This change is little bit harmfull if you want to have backward +// compability but we try to keep it +inline +#if LIBAVFORMAT_CHANGE_AVSTREAM +AVCodecParameters *getCodecStructPtr(AVStream* pStream) { + return pStream->codecpar; +} +#else +AVCodecContext *getCodecStructPtr(AVStream* pStream) { + return pStream->codec; +} +#endif + + inline AVMediaType getMediaTypeOfStream(AVStream* pStream) { - return pStream->codec->codec_type; + return getCodecStructPtr(pStream)->codec_type; } AVStream* findFirstAudioStream(AVFormatContext* pFormatCtx) { @@ -51,17 +67,17 @@ AVStream* findFirstAudioStream(AVFormatContext* pFormatCtx) { inline AVCodec* findDecoderForStream(AVStream* pStream) { - return avcodec_find_decoder(pStream->codec->codec_id); + return avcodec_find_decoder(getCodecStructPtr(pStream)->codec_id); } inline SINT getChannelCountOfStream(AVStream* pStream) { - return pStream->codec->channels; + return getCodecStructPtr(pStream)->channels; } inline SINT getSamplingRateOfStream(AVStream* pStream) { - return pStream->codec->sample_rate; + return getCodecStructPtr(pStream)->sample_rate; } inline @@ -107,7 +123,11 @@ bool getFrameCountOfStream(AVStream* pStream, SINT* pFrameCount) { inline AVSampleFormat getSampleFormatOfStream(AVStream* pStream) { - return pStream->codec->sample_fmt; +#if LIBAVFORMAT_CHANGE_AVSTREAM + return (AVSampleFormat)getCodecStructPtr(pStream)->format; +#else + return getCodecStructPtr(pStream)->sample_fmt; +#endif } } // anonymous namespace @@ -230,16 +250,10 @@ void SoundSourceFFmpeg::ClosableInputAVFormatContextPtr::close() { //static SoundSource::OpenResult SoundSourceFFmpeg::openAudioStream( - AVStream* pAudioStream) { - DEBUG_ASSERT(pAudioStream != nullptr); - AVCodec* pDecoder = findDecoderForStream(pAudioStream); - if (pDecoder == nullptr) { - qWarning() << "[SoundSourceFFmpeg]" - << "Failed to find a decoder for stream" - << pAudioStream->index; - return SoundSource::OpenResult::ABORTED; - } - const int avcodec_open2_result = avcodec_open2(pAudioStream->codec, pDecoder, nullptr); + AVCodecContext* pCodecContext, AVCodec *pDecoder) { + DEBUG_ASSERT(pCodecContext != nullptr); + + const int avcodec_open2_result = avcodec_open2(pCodecContext, pDecoder, nullptr); if (avcodec_open2_result < 0) { qWarning() << "[SoundSourceFFmpeg]" << "avcodec_open2() failed and returned" @@ -260,6 +274,7 @@ void SoundSourceFFmpeg::ClosableAVStreamPtr::take(AVStream** ppClosableStream) { void SoundSourceFFmpeg::ClosableAVStreamPtr::close() { if (m_pClosableStream != nullptr) { +#if ! LIBAVFORMAT_CHANGE_AVSTREAM const int avcodec_close_result = avcodec_close(m_pClosableStream->codec); if (avcodec_close_result != 0) { qWarning() << "[SoundSourceFFmpeg]" @@ -267,10 +282,29 @@ void SoundSourceFFmpeg::ClosableAVStreamPtr::close() { << avcodec_close_result; // ignore error and continue } +#endif m_pClosableStream = nullptr; } } +#if LIBAVFORMAT_CHANGE_AVSTREAM +void SoundSourceFFmpeg::ClosableAVCodecContextPtr::take(AVCodecContext** ppClosableContext) { + DEBUG_ASSERT(ppClosableContext != nullptr); + if (m_pClosableContext != *ppClosableContext) { + close(); + m_pClosableContext = *ppClosableContext; + *ppClosableContext = nullptr; + } +} + +void SoundSourceFFmpeg::ClosableAVCodecContextPtr::close() { + if (m_pClosableContext != nullptr) { + avcodec_free_context(&m_pClosableContext); + m_pClosableContext = nullptr; + } +} +#endif + SoundSourceFFmpeg::SoundSourceFFmpeg(const QUrl& url) : SoundSource(url), m_pResample(nullptr), @@ -321,10 +355,52 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(const AudioSourceConfig& /*au << "No audio stream found"; return OpenResult::ABORTED; } - const OpenResult openAudioStreamResult = openAudioStream(pAudioStream); + + // Find codec to decode stream or pass out + AVCodec* pDecoder = findDecoderForStream(pAudioStream); + if (pDecoder == nullptr) { + qWarning() << "[SoundSourceFFmpeg]" + << "Failed to find a decoder for stream" + << pAudioStream->index; + return SoundSource::OpenResult::ABORTED; + } + +#if LIBAVFORMAT_CHANGE_AVSTREAM + AVCodecContext *pCodecContext = avcodec_alloc_context3(pDecoder); + + if (pCodecContext == nullptr) { + qWarning() << "[SoundSourceFFmpeg]" + << "Failed to allocate codec context" + << pAudioStream->index; + return SoundSource::OpenResult::ABORTED; + } + + // Add stream parameters to context + if(avcodec_parameters_to_context(pCodecContext,pAudioStream->codecpar)) { + qWarning() << "[SoundSourceFFmpeg]" + << "Failed to find to set Code parameter for AVCodecContext" + << pAudioStream->index; + return SoundSource::OpenResult::ABORTED; + } + + // Se timebase correct + av_codec_set_pkt_timebase(pCodecContext, pAudioStream->time_base); + + // Make sure that Codecs are identical or avcodec_open2 fails. + pCodecContext->codec_id = pDecoder->id; + + const OpenResult openAudioStreamResult = openAudioStream(pCodecContext, pDecoder); + + m_pAudioContext.take(&pCodecContext); +#else + const OpenResult openAudioStreamResult = openAudioStream(pAudioStream->codec, pDecoder); +#endif + + if (openAudioStreamResult != OpenResult::SUCCEEDED) { return openAudioStreamResult; // early exit on any error } + // Now set the member, because the audio stream has been opened // successfully and needs to be closed eventually. m_pAudioStream.take(&pAudioStream); @@ -352,7 +428,7 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(const AudioSourceConfig& /*au } SINT frameCount = getFrameCount(); - if (getFrameCountOfStream(m_pAudioStream, &frameCount) && isValidFrameCount(frameCount)) { + if (getFrameCountOfStream(m_pAudioStream, &frameCount) && isValidFrameCount(frameCount) == false) { qWarning() << "[SoundSourceFFmpeg]" << "Stream has invalid number of frames:" << frameCount; @@ -363,7 +439,11 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(const AudioSourceConfig& /*au setSamplingRate(samplingRate); setFrameCount(frameCount); +#if LIBAVFORMAT_CHANGE_AVSTREAM + m_pResample = std::make_unique(m_pAudioContext); +#else m_pResample = std::make_unique(m_pAudioStream->codec); +#endif m_pResample->openMixxx(getSampleFormatOfStream(m_pAudioStream), AV_SAMPLE_FMT_FLT); return OpenResult::SUCCEEDED; @@ -380,6 +460,9 @@ void SoundSourceFFmpeg::close() { free(l_SRmJmp); } +#if LIBAVFORMAT_CHANGE_AVSTREAM + m_pAudioContext.close(); +#endif m_pAudioStream.close(); m_pInputFormatContext.close(); } @@ -401,7 +484,9 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { l_SPacket.size = 0; AVFrame *l_pFrame = nullptr; bool l_bStop = false; +#if ! LIBAVFORMAT_CHANGE_AVSTREAM int l_iFrameFinished = 0; +#endif struct ffmpegCacheObject *l_SObj = nullptr; struct ffmpegCacheObject *l_SRmObj = nullptr; qint64 l_lLastPacketPos = -1; @@ -474,11 +559,34 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { m_SStoredJumpPoint = nullptr; } +#if LIBAVFORMAT_CHANGE_AVSTREAM + // AVERROR(EAGAIN) means that we need to feed more + // That we can decode Frame or Packet + do { + do { + l_iRet = avcodec_send_packet(m_pAudioContext, &l_SPacket); + } while(l_iRet == AVERROR(EAGAIN)); + + if(l_iRet == AVERROR_EOF || l_iRet == AVERROR(EINVAL)) { + qDebug() << "SoundSourceFFmpeg::readFramesToCache: Warning can't decode frame!"; + break; + } + + l_iRet = avcodec_receive_frame(m_pAudioContext, l_pFrame); + + if(l_iRet == AVERROR_EOF || l_iRet == AVERROR(EINVAL)) { + qDebug() << "SoundSourceFFmpeg::readFramesToCache: Warning can't decode frame!"; + break; + } + } while(l_iRet == AVERROR(EAGAIN)); + + if (l_iRet == AVERROR_EOF || l_iRet < 0) { +#else // Decode audio bytes (These can be S16P or FloatP [P is Planar]) l_iRet = avcodec_decode_audio4(m_pAudioStream->codec,l_pFrame,&l_iFrameFinished, &l_SPacket); - if (l_iRet <= 0) { +#endif // An error or EOF occurred,index break out and return what // we have so far. qDebug() << "EOF!"; diff --git a/src/sources/soundsourceffmpeg.h b/src/sources/soundsourceffmpeg.h index 8e3617826c2..2e2d140aace 100644 --- a/src/sources/soundsourceffmpeg.h +++ b/src/sources/soundsourceffmpeg.h @@ -22,6 +22,9 @@ extern "C" { #include "util/memory.h" // std::unique_ptr<> + std::make_unique() +#define LIBAVFORMAT_CHANGE_AVSTREAM \ + (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 0)) + // forward declaration class EncoderFfmpegResample; @@ -99,7 +102,7 @@ class SoundSourceFFmpeg : public SoundSource { }; ClosableInputAVFormatContextPtr m_pInputFormatContext; - static OpenResult openAudioStream(AVStream* pAudioStream); + static OpenResult openAudioStream(AVCodecContext* pCodecContext, AVCodec *pDecoder); // Takes ownership of an opened (audio) stream and ensures that // the corresponding AVStream is closed, either explicitly or @@ -137,6 +140,48 @@ class SoundSourceFFmpeg : public SoundSource { }; ClosableAVStreamPtr m_pAudioStream; + +#if LIBAVFORMAT_CHANGE_AVSTREAM + // Takes ownership of an opened (audio) codec context and ensures that + // the corresponding AVCodecContext is closed, either explicitly or + // implicitly by the destructor. The wrapper can only be moved, + // copying is disabled. + // + // This is prior new API changes made in FFMmpeg 3.1 + // before that we can use AVStream->codec to access AVCodecContext + class ClosableAVCodecContextPtr final { + public: + explicit ClosableAVCodecContextPtr(AVCodecContext* pClosableContext = nullptr) + : m_pClosableContext(pClosableContext) { + } + explicit ClosableAVCodecContextPtr(const ClosableAVCodecContextPtr&) = delete; + explicit ClosableAVCodecContextPtr(ClosableAVCodecContextPtr&& that) + : m_pClosableContext(that.m_pClosableContext) { + that.m_pClosableContext = nullptr; + } + ~ClosableAVCodecContextPtr() { + close(); + } + + void take(AVCodecContext** ppClosableContext); + void close(); + + friend void swap(ClosableAVCodecContextPtr& lhs, ClosableAVCodecContextPtr& rhs) { + std::swap(lhs.m_pClosableContext, rhs.m_pClosableContext); + } + + ClosableAVCodecContextPtr& operator=(const ClosableAVCodecContextPtr&) = delete; + ClosableAVCodecContextPtr& operator=(ClosableAVCodecContextPtr&& that) = delete; + + AVCodecContext* operator->() { return m_pClosableContext; } + operator AVCodecContext*() { return m_pClosableContext; } + + private: + AVCodecContext* m_pClosableContext; + }; + ClosableAVCodecContextPtr m_pAudioContext; +#endif + std::unique_ptr m_pResample; SINT m_currentMixxxFrameIndex; From ef262f3060ed36b6476cc0a71c2780b293e64149 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Tue, 31 Jan 2017 15:55:05 +0200 Subject: [PATCH 2/8] SoundSourceFFmpeg: Fix M4A getting stuck when there is embedded cover art --- src/sources/soundsourceffmpeg.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index 104e6093c3f..96379ff790c 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -562,6 +562,9 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { #if LIBAVFORMAT_CHANGE_AVSTREAM // AVERROR(EAGAIN) means that we need to feed more // That we can decode Frame or Packet + + quint8 l_iRepeat = 5; + do { do { l_iRet = avcodec_send_packet(m_pAudioContext, &l_SPacket); @@ -578,7 +581,16 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { qDebug() << "SoundSourceFFmpeg::readFramesToCache: Warning can't decode frame!"; break; } - } while(l_iRet == AVERROR(EAGAIN)); + + // Sometimes (Espescially with m4a with Cover art) + // This gets stuck.. then we have to bail out + // not hang here more than 5 times.. + if( l_iRet == AVERROR(EAGAIN) ) { + l_iRepeat --; + } else { + l_iRepeat = 5; + } + } while(l_iRet == AVERROR(EAGAIN) && l_iRepeat); if (l_iRet == AVERROR_EOF || l_iRet < 0) { #else From 6252e2acb6bf0360603de8fe0a91cbcd018fa27c Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Tue, 31 Jan 2017 16:03:44 +0200 Subject: [PATCH 3/8] SoundSourceFFmpeg: Separated pre-3.1 and after 3.1 API to own classes which are choosed within compiling. --- src/sources/soundsourceffmpeg.cpp | 30 +++----------- src/sources/soundsourceffmpeg.h | 69 +++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index 96379ff790c..edf25233b1e 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -28,25 +28,9 @@ void initFFmpegLib() { // More than 2 channels are currently not supported const SINT kMaxChannelCount = 2; -// -// FFMPEG 3.1 obsolettes AVSteam->codec to favor AVStream->codepar -// This change is little bit harmfull if you want to have backward -// compability but we try to keep it -inline -#if LIBAVFORMAT_CHANGE_AVSTREAM -AVCodecParameters *getCodecStructPtr(AVStream* pStream) { - return pStream->codecpar; -} -#else -AVCodecContext *getCodecStructPtr(AVStream* pStream) { - return pStream->codec; -} -#endif - - inline AVMediaType getMediaTypeOfStream(AVStream* pStream) { - return getCodecStructPtr(pStream)->codec_type; + return m_pAVStreamWrapper.getMediaTypeOfStream(pStream); } AVStream* findFirstAudioStream(AVFormatContext* pFormatCtx) { @@ -67,17 +51,17 @@ AVStream* findFirstAudioStream(AVFormatContext* pFormatCtx) { inline AVCodec* findDecoderForStream(AVStream* pStream) { - return avcodec_find_decoder(getCodecStructPtr(pStream)->codec_id); + return m_pAVStreamWrapper.findDecoderForStream(pStream); } inline SINT getChannelCountOfStream(AVStream* pStream) { - return getCodecStructPtr(pStream)->channels; + return m_pAVStreamWrapper.getChannelCountOfStream(pStream); } inline SINT getSamplingRateOfStream(AVStream* pStream) { - return getCodecStructPtr(pStream)->sample_rate; + return m_pAVStreamWrapper.getSamplingRateOfStream(pStream); } inline @@ -123,11 +107,7 @@ bool getFrameCountOfStream(AVStream* pStream, SINT* pFrameCount) { inline AVSampleFormat getSampleFormatOfStream(AVStream* pStream) { -#if LIBAVFORMAT_CHANGE_AVSTREAM - return (AVSampleFormat)getCodecStructPtr(pStream)->format; -#else - return getCodecStructPtr(pStream)->sample_fmt; -#endif + return m_pAVStreamWrapper.getSampleFormatOfStream(pStream); } } // anonymous namespace diff --git a/src/sources/soundsourceffmpeg.h b/src/sources/soundsourceffmpeg.h index 2e2d140aace..df175e18404 100644 --- a/src/sources/soundsourceffmpeg.h +++ b/src/sources/soundsourceffmpeg.h @@ -28,6 +28,75 @@ extern "C" { // forward declaration class EncoderFfmpegResample; +namespace { + + // Because 3.1 changed API how to access thigs in AVStream + // we'll separate this logic in own wrapper class + class AVStreamWrapper { + public: + virtual AVMediaType getMediaTypeOfStream(AVStream* pStream) = 0; + virtual AVCodec* findDecoderForStream(AVStream* pStream) = 0; + virtual SINT getChannelCountOfStream(AVStream* pStream) = 0; + virtual SINT getSamplingRateOfStream(AVStream* pStream) = 0; + virtual AVSampleFormat getSampleFormatOfStream(AVStream* pStream) = 0; + }; + + // Implement classes for version befor 3.1 and after that +#if LIBAVFORMAT_CHANGE_AVSTREAM + // This is after version 3.1 + class AVStreamWrapperImpl : public AVStreamWrapper { + public: + AVMediaType getMediaTypeOfStream(AVStream* pStream) { + return pStream->codecpar->codec_type; + } + + AVCodec* findDecoderForStream(AVStream* pStream) { + return avcodec_find_decoder(pStream->codecpar->codec_id); + } + + SINT getChannelCountOfStream(AVStream* pStream) { + return pStream->codecpar->channels; + } + + SINT getSamplingRateOfStream(AVStream* pStream) { + return pStream->codecpar->sample_rate; + } + + AVSampleFormat getSampleFormatOfStream(AVStream* pStream) { + return (AVSampleFormat)pStream->codecpar->format; + } + }; +#else + class AVStreamWrapperImpl : public AVStreamWrapper { + public: + AVMediaType getMediaTypeOfStream(AVStream* pStream) { + return pStream->codec->codec_type; + } + + AVCodec* findDecoderForStream(AVStream* pStream) { + return avcodec_find_decoder(pStream->codec->codec_id); + } + + SINT getChannelCountOfStream(AVStream* pStream) { + return pStream->codec->channels; + } + + SINT getSamplingRateOfStream(AVStream* pStream) { + return pStream->codec->sample_rate; + } + + AVSampleFormat getSampleFormatOfStream(AVStream* pStream) { + return pStream->codec->sample_fmt; + } + }; +#endif + + //AVStreamWrapperImpl *m_pAVStreamWrapper = new AVStreamWrapperImpl(); + AVStreamWrapperImpl m_pAVStreamWrapper; + +} + + namespace mixxx { struct ffmpegLocationObject { From 4d7a5685a28e7f74c490bb67b03e0d3d3d08d07e Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Fri, 10 Feb 2017 10:21:55 +0200 Subject: [PATCH 4/8] SoundSourceFFmpeg: Changed #ifdef 'LIBAVFORMAT_CHANGE_AVSTREAM' to more definative 'AVSTREAM_FROM_API_VERSION_3_1' --- src/sources/soundsourceffmpeg.cpp | 14 +++++++------- src/sources/soundsourceffmpeg.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index edf25233b1e..6e72584d765 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -254,7 +254,7 @@ void SoundSourceFFmpeg::ClosableAVStreamPtr::take(AVStream** ppClosableStream) { void SoundSourceFFmpeg::ClosableAVStreamPtr::close() { if (m_pClosableStream != nullptr) { -#if ! LIBAVFORMAT_CHANGE_AVSTREAM +#if ! AVSTREAM_FROM_API_VERSION_3_1 const int avcodec_close_result = avcodec_close(m_pClosableStream->codec); if (avcodec_close_result != 0) { qWarning() << "[SoundSourceFFmpeg]" @@ -267,7 +267,7 @@ void SoundSourceFFmpeg::ClosableAVStreamPtr::close() { } } -#if LIBAVFORMAT_CHANGE_AVSTREAM +#if AVSTREAM_FROM_API_VERSION_3_1 void SoundSourceFFmpeg::ClosableAVCodecContextPtr::take(AVCodecContext** ppClosableContext) { DEBUG_ASSERT(ppClosableContext != nullptr); if (m_pClosableContext != *ppClosableContext) { @@ -345,7 +345,7 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(const AudioSourceConfig& /*au return SoundSource::OpenResult::ABORTED; } -#if LIBAVFORMAT_CHANGE_AVSTREAM +#if AVSTREAM_FROM_API_VERSION_3_1 AVCodecContext *pCodecContext = avcodec_alloc_context3(pDecoder); if (pCodecContext == nullptr) { @@ -419,7 +419,7 @@ SoundSource::OpenResult SoundSourceFFmpeg::tryOpen(const AudioSourceConfig& /*au setSamplingRate(samplingRate); setFrameCount(frameCount); -#if LIBAVFORMAT_CHANGE_AVSTREAM +#if AVSTREAM_FROM_API_VERSION_3_1 m_pResample = std::make_unique(m_pAudioContext); #else m_pResample = std::make_unique(m_pAudioStream->codec); @@ -440,7 +440,7 @@ void SoundSourceFFmpeg::close() { free(l_SRmJmp); } -#if LIBAVFORMAT_CHANGE_AVSTREAM +#if AVSTREAM_FROM_API_VERSION_3_1 m_pAudioContext.close(); #endif m_pAudioStream.close(); @@ -464,7 +464,7 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { l_SPacket.size = 0; AVFrame *l_pFrame = nullptr; bool l_bStop = false; -#if ! LIBAVFORMAT_CHANGE_AVSTREAM +#if ! AVSTREAM_FROM_API_VERSION_3_1 int l_iFrameFinished = 0; #endif struct ffmpegCacheObject *l_SObj = nullptr; @@ -539,7 +539,7 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { m_SStoredJumpPoint = nullptr; } -#if LIBAVFORMAT_CHANGE_AVSTREAM +#if AVSTREAM_FROM_API_VERSION_3_1 // AVERROR(EAGAIN) means that we need to feed more // That we can decode Frame or Packet diff --git a/src/sources/soundsourceffmpeg.h b/src/sources/soundsourceffmpeg.h index df175e18404..30724a009cb 100644 --- a/src/sources/soundsourceffmpeg.h +++ b/src/sources/soundsourceffmpeg.h @@ -22,7 +22,7 @@ extern "C" { #include "util/memory.h" // std::unique_ptr<> + std::make_unique() -#define LIBAVFORMAT_CHANGE_AVSTREAM \ +#define AVSTREAM_FROM_API_VERSION_3_1 \ (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 0)) // forward declaration @@ -42,7 +42,7 @@ namespace { }; // Implement classes for version befor 3.1 and after that -#if LIBAVFORMAT_CHANGE_AVSTREAM +#if AVSTREAM_FROM_API_VERSION_3_1 // This is after version 3.1 class AVStreamWrapperImpl : public AVStreamWrapper { public: @@ -210,7 +210,7 @@ class SoundSourceFFmpeg : public SoundSource { ClosableAVStreamPtr m_pAudioStream; -#if LIBAVFORMAT_CHANGE_AVSTREAM +#if AVSTREAM_FROM_API_VERSION_3_1 // Takes ownership of an opened (audio) codec context and ensures that // the corresponding AVCodecContext is closed, either explicitly or // implicitly by the destructor. The wrapper can only be moved, From 026f6ba153fa55ed5f2052d24f87681e6f9b7b47 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Sun, 26 Mar 2017 11:04:35 +0300 Subject: [PATCH 5/8] SoundSourceFFmpeg: Corrected the situation when you have codec Demuxer but don't have codec decoder. This happens for example in Wavpack on Ubuntu 14.04 AVConv --- src/sources/soundsourceffmpeg.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index 6e72584d765..befab0c39be 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -737,7 +737,10 @@ bool SoundSourceFFmpeg::getBytesFromCache(CSAMPLE* buffer, SINT offset, // If cache is empty then retun without crash. if (m_SCache.isEmpty()) { qDebug() << "SoundSourceFFmpeg::getBytesFromCache: Cache is empty can't return bytes"; - memset(l_pBuffer, 0x00, l_lLeft); + if(l_pBuffer != nullptr) + { + memset(l_pBuffer, 0x00, l_lLeft); + } return false; } @@ -835,7 +838,10 @@ bool SoundSourceFFmpeg::getBytesFromCache(CSAMPLE* buffer, SINT offset, } else { qDebug() << "SoundSourceFFmpeg::getBytesFromCache: Buffer run out. Shouldn't happen!"; - memset(l_pBuffer, 0x00, l_lLeft); + if(l_pBuffer != nullptr) + { + memset(l_pBuffer, 0x00, l_lLeft); + } return false; } } From 8af8ae2db67217822c7819b82ecaaf4cbe3daf36 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Wed, 29 Mar 2017 17:53:36 +0300 Subject: [PATCH 6/8] SoundSourceFFmpeg: Corrected skipping situation where incoming buffer if null. Now there is no nasty error in this situation and it's handled as it should --- src/sources/soundsourceffmpeg.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index befab0c39be..b1423104ba5 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -976,6 +976,12 @@ SINT SoundSourceFFmpeg::seekSampleFrame(SINT frameIndex) { SINT SoundSourceFFmpeg::readSampleFrames(SINT numberOfFrames, CSAMPLE* sampleBuffer) { + if (sampleBuffer == nullptr) { + // They are trying to make us skip + // we don't want to do that so we lie + return numberOfFrames; + } + if (m_SCache.size() == 0) { // Make sure we always start at beginning and cache have some // material that we can consume. From 5a1c86b2e6802501de37b40ff33e7df7b14ea37e Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Wed, 29 Mar 2017 19:42:31 +0300 Subject: [PATCH 7/8] SoundSourceFFmpeg: Some codecs like Wavepack doesn't update position and it leads to situation where soundsource do not cache packages. Fixes reading not testcase --- src/sources/soundsourceffmpeg.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index b1423104ba5..d0869ef96d0 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -512,8 +512,17 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { // Are we on correct audio stream. Currently we are always // Using first audio stream but in future there should be // possibility to choose which to use + // If Pos is -1 it meand FFmpeg/AVConv doesn't know it + // So then we use pts instead if (l_SPacket.stream_index == m_pAudioStream->index && - l_SPacket.pos >= 0) { + (l_SPacket.pos >= 0 || l_SPacket.pos == -1)) { + + // Codecs like Wavpack does it like this + // They work but you can say about position nothing + if (l_SPacket.pos == -1) + { + l_SPacket.pos = l_SPacket.pts; + } if (m_lStoredSeekPoint > 0) { struct ffmpegLocationObject *l_STestObj = nullptr; if (m_SJumpPoints.size() > 0) { From 78bd025d8062084fa09ad7b559e86d4f5e727136 Mon Sep 17 00:00:00 2001 From: Tuukka Pasanen Date: Sun, 2 Apr 2017 19:55:24 +0300 Subject: [PATCH 8/8] SoundSourceFFmpeg: Correctly handling new API when you have image in MP4 file and same time correct skipping samples to correct point --- src/sources/soundsourceffmpeg.cpp | 59 +++++++++++++------------------ 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/src/sources/soundsourceffmpeg.cpp b/src/sources/soundsourceffmpeg.cpp index d0869ef96d0..e18f37ecf88 100644 --- a/src/sources/soundsourceffmpeg.cpp +++ b/src/sources/soundsourceffmpeg.cpp @@ -549,37 +549,31 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { } #if AVSTREAM_FROM_API_VERSION_3_1 + l_iRet = avcodec_send_packet(m_pAudioContext, &l_SPacket); + // AVERROR(EAGAIN) means that we need to feed more // That we can decode Frame or Packet - - quint8 l_iRepeat = 5; - - do { - do { - l_iRet = avcodec_send_packet(m_pAudioContext, &l_SPacket); - } while(l_iRet == AVERROR(EAGAIN)); - - if(l_iRet == AVERROR_EOF || l_iRet == AVERROR(EINVAL)) { - qDebug() << "SoundSourceFFmpeg::readFramesToCache: Warning can't decode frame!"; - break; - } + if (l_iRet == AVERROR(EAGAIN)) { + qDebug() << "SoundSourceFFmpeg::readFramesToCache: Need more packets to decode!"; + continue; + } - l_iRet = avcodec_receive_frame(m_pAudioContext, l_pFrame); + if(l_iRet == AVERROR_EOF || l_iRet == AVERROR(EINVAL)) { + qDebug() << "SoundSourceFFmpeg::readFramesToCache: Warning can't decode frame!"; + } - if(l_iRet == AVERROR_EOF || l_iRet == AVERROR(EINVAL)) { - qDebug() << "SoundSourceFFmpeg::readFramesToCache: Warning can't decode frame!"; - break; - } - - // Sometimes (Espescially with m4a with Cover art) - // This gets stuck.. then we have to bail out - // not hang here more than 5 times.. - if( l_iRet == AVERROR(EAGAIN) ) { - l_iRepeat --; - } else { - l_iRepeat = 5; - } - } while(l_iRet == AVERROR(EAGAIN) && l_iRepeat); + l_iRet = avcodec_receive_frame(m_pAudioContext, l_pFrame); + + // AVERROR(EAGAIN) means that we need to feed more + // That we can decode Frame or Packet + if (l_iRet == AVERROR(EAGAIN)) { + qDebug() << "SoundSourceFFmpeg::readFramesToCache: Need more packets to decode!"; + continue; + } + + if(l_iRet == AVERROR_EOF || l_iRet == AVERROR(EINVAL)) { + qDebug() << "SoundSourceFFmpeg::readFramesToCache: Warning can't decode frame!"; + } if (l_iRet == AVERROR_EOF || l_iRet < 0) { #else @@ -590,7 +584,7 @@ bool SoundSourceFFmpeg::readFramesToCache(unsigned int count, SINT offset) { #endif // An error or EOF occurred,index break out and return what // we have so far. - qDebug() << "EOF!"; + qDebug() << "SoundSourceFFmpeg::readFramesToCache: EOF or uncoverable error!"; l_bStop = true; continue; } else { @@ -987,7 +981,7 @@ SINT SoundSourceFFmpeg::readSampleFrames(SINT numberOfFrames, if (sampleBuffer == nullptr) { // They are trying to make us skip - // we don't want to do that so we lie + seekSampleFrame(m_currentMixxxFrameIndex + numberOfFrames); return numberOfFrames; } @@ -1000,12 +994,7 @@ SINT SoundSourceFFmpeg::readSampleFrames(SINT numberOfFrames, getBytesFromCache(sampleBuffer, m_currentMixxxFrameIndex, numberOfFrames); - // As this is also Hack - // If we don't seek like we don't on analyzer.. keep - // place in mind.. - if (m_bIsSeeked == false) { - m_currentMixxxFrameIndex += numberOfFrames; - } + m_currentMixxxFrameIndex += numberOfFrames; m_bIsSeeked = false;