From 5c3de04c28eccc41a6f7ee0f0e80bc9eeaed7bb6 Mon Sep 17 00:00:00 2001 From: Dmitry Vedenko Date: Mon, 8 Apr 2024 18:21:15 +0300 Subject: [PATCH] Do not use FFmpeg provide FIFO for the encoder FFmpeg has changed the FIFO API and the new API is not quite compatible with Audacity exporter --- modules/mod-ffmpeg/ExportFFmpeg.cpp | 26 ++--- .../lib-ffmpeg-support/AVUtilFunctions.h | 7 +- .../lib-ffmpeg-support/CMakeLists.txt | 6 +- .../lib-ffmpeg-support/FFmpegFunctions.cpp | 6 - .../lib-ffmpeg-support/FFmpegFunctions.h | 2 - .../lib-ffmpeg-support/FifoBuffer.cpp | 106 ++++++++++++++++++ .../lib-ffmpeg-support/FifoBuffer.h | 46 ++++++++ .../impl/avutil/AVUtilFunctionsLoader.cpp | 7 +- .../wrappers/AVFifoBufferWrapper.cpp | 36 ------ .../wrappers/AVFifoBufferWrapper.h | 36 ------ 10 files changed, 169 insertions(+), 109 deletions(-) create mode 100644 modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.cpp create mode 100644 modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.h delete mode 100644 modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.cpp delete mode 100644 modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.h diff --git a/modules/mod-ffmpeg/ExportFFmpeg.cpp b/modules/mod-ffmpeg/ExportFFmpeg.cpp index bcd1457e216a..b2ab7f8b6681 100644 --- a/modules/mod-ffmpeg/ExportFFmpeg.cpp +++ b/modules/mod-ffmpeg/ExportFFmpeg.cpp @@ -21,6 +21,7 @@ function. #include "../FFmpeg.h" #include "FFmpegFunctions.h" +#include "FifoBuffer.h" #include #include @@ -652,7 +653,7 @@ class FFmpegExporter final bool mSupportsUTF8{true}; // Smart pointer fields, their order is the reverse in which they are reset in FreeResources(): - std::unique_ptr mEncAudioFifo; // FIFO to write incoming audio samples into + std::unique_ptr mEncAudioFifo; // FIFO to write incoming audio samples into AVDataBuffer mEncAudioFifoOutBuf; // buffer to read _out_ of the FIFO into std::unique_ptr mEncFormatCtx; // libavformat's context for our output file std::unique_ptr mEncAudioCodecCtx; // the encoder for the output audio stream @@ -1289,7 +1290,7 @@ bool FFmpegExporter::InitCodecs(int sampleRate, // The encoder may require a minimum number of raw audio samples for each encoding but we can't // guarantee we'll get this minimum each time an audio frame is decoded from the input file so // we use a FIFO to store up incoming raw samples until we have enough for one call to the codec. - mEncAudioFifo = mFFmpeg->CreateFifoBuffer(mDefaultFrameSize); + mEncAudioFifo = std::make_unique(mDefaultFrameSize * mChannels * sizeof(int16_t)); mEncAudioFifoOutBufSize = 2*MaxAudioPacketSize; // Allocate a buffer to read OUT of the FIFO into. The FIFO maintains its own buffer internally. @@ -1471,8 +1472,8 @@ bool FFmpegExporter::Finalize() { std::unique_ptr pkt = mFFmpeg->CreateAVPacketWrapper(); - const int nFifoBytes = mFFmpeg->av_fifo_size( - mEncAudioFifo->GetWrappedValue()); // any bytes left in audio FIFO? + const auto nFifoBytes = + mEncAudioFifo->GetAvailable(); // any bytes left in audio FIFO? int encodeResult = 0; @@ -1500,7 +1501,7 @@ bool FFmpegExporter::Finalize() (mEncAudioCodecCtx->GetChannels() * sizeof(int16_t)); } - wxLogDebug(wxT("FFmpeg : Audio FIFO still contains %d bytes, writing %d sample frame ..."), + wxLogDebug(wxT("FFmpeg : Audio FIFO still contains %lld bytes, writing %d sample frame ..."), nFifoBytes, frame_size); // Fill audio buffer with zeroes. If codec tries to read the whole buffer, @@ -1509,7 +1510,7 @@ bool FFmpegExporter::Finalize() //const AVCodec *codec = mEncAudioCodecCtx->codec; // Pull the bytes out from the FIFO and feed them to the encoder. - if (mFFmpeg->av_fifo_generic_read(mEncAudioFifo->GetWrappedValue(), mEncAudioFifoOutBuf.data(), nFifoBytes, nullptr) == 0) + if (mEncAudioFifo->Read(mEncAudioFifoOutBuf.data(), nFifoBytes) == nFifoBytes) { encodeResult = EncodeAudio(*pkt, mEncAudioFifoOutBuf.data(), frame_size); } @@ -1556,13 +1557,9 @@ bool FFmpegExporter::EncodeAudioFrame(int16_t *pFrame, size_t numSamples) nBytesToWrite = frameSize; pRawSamples = (uint8_t*)pFrame; - if (mFFmpeg->av_fifo_realloc2(mEncAudioFifo->GetWrappedValue(), mFFmpeg->av_fifo_size(mEncAudioFifo->GetWrappedValue()) + frameSize) < 0) { - throw ExportErrorException("FFmpeg:905"); - } // Put the raw audio samples into the FIFO. - ret = mFFmpeg->av_fifo_generic_write( - mEncAudioFifo->GetWrappedValue(), pRawSamples, nBytesToWrite, nullptr); + ret = mEncAudioFifo->Write(pRawSamples, nBytesToWrite); if (ret != nBytesToWrite) { throw ExportErrorException("FFmpeg:913"); @@ -1573,11 +1570,10 @@ bool FFmpegExporter::EncodeAudioFrame(int16_t *pFrame, size_t numSamples) } // Read raw audio samples out of the FIFO in nAudioFrameSizeOut byte-sized groups to encode. - while (mFFmpeg->av_fifo_size(mEncAudioFifo->GetWrappedValue()) >= nAudioFrameSizeOut) + while (mEncAudioFifo->GetAvailable() >= nAudioFrameSizeOut) { - ret = mFFmpeg->av_fifo_generic_read( - mEncAudioFifo->GetWrappedValue(), mEncAudioFifoOutBuf.data(), - nAudioFrameSizeOut, nullptr); + mEncAudioFifo->Read( + mEncAudioFifoOutBuf.data(), nAudioFrameSizeOut); std::unique_ptr pkt = mFFmpeg->CreateAVPacketWrapper(); diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/AVUtilFunctions.h b/modules/mod-ffmpeg/lib-ffmpeg-support/AVUtilFunctions.h index 53a96d0d4296..7f54160fa944 100644 --- a/modules/mod-ffmpeg/lib-ffmpeg-support/AVUtilFunctions.h +++ b/modules/mod-ffmpeg/lib-ffmpeg-support/AVUtilFunctions.h @@ -30,12 +30,7 @@ struct FFMPEG_SUPPORT_API AVUtilFunctions int (*av_get_bytes_per_sample) (AVSampleFormatFwd sample_fmt) = nullptr; void (*av_log_set_callback) (void (*cb)(void*, int, const char*, va_list)) = nullptr; void (*av_log_default_callback) (void* ptr, int level, const char* fmt, va_list vl) = nullptr; - AVFifoBuffer* (*av_fifo_alloc) (unsigned int size) = nullptr; - int (*av_fifo_generic_read) (AVFifoBuffer *f, void *buf, int buf_size, void (*func)(void*, void*, int)) = nullptr; - int (*av_fifo_realloc2) (AVFifoBuffer *f, unsigned int size) = nullptr; - void (*av_fifo_free) (AVFifoBuffer *f) = nullptr; - int (*av_fifo_size) (const AVFifoBuffer *f) = nullptr; - int (*av_fifo_generic_write) (AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int)) = nullptr; + int64_t (*av_rescale_q) (int64_t a, AudacityAVRational bq, AudacityAVRational cq) = nullptr; AVFrame* (*av_frame_alloc) (void) = nullptr; void (*av_frame_free) (AVFrame **frame) = nullptr; diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/CMakeLists.txt b/modules/mod-ffmpeg/lib-ffmpeg-support/CMakeLists.txt index a6c40230216e..c421447c6d5e 100644 --- a/modules/mod-ffmpeg/lib-ffmpeg-support/CMakeLists.txt +++ b/modules/mod-ffmpeg/lib-ffmpeg-support/CMakeLists.txt @@ -11,14 +11,16 @@ if (${_OPT}use_ffmpeg) AVFormatFunctions.h AVUtilFunctions.h + FifoBuffer.cpp + FifoBuffer.h + + wrappers/AVChannelLayoutWrapper.h wrappers/AVCodecContextWrapper.cpp wrappers/AVCodecContextWrapper.h wrappers/AVCodecWrapper.cpp wrappers/AVCodecWrapper.h wrappers/AVDictionaryWrapper.cpp wrappers/AVDictionaryWrapper.h - wrappers/AVFifoBufferWrapper.cpp - wrappers/AVFifoBufferWrapper.h wrappers/AVFormatContextWrapper.cpp wrappers/AVFormatContextWrapper.h wrappers/AVFrameWrapper.cpp diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.cpp b/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.cpp index 64f25d398805..c5522b7f9ab2 100644 --- a/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.cpp +++ b/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.cpp @@ -457,12 +457,6 @@ const std::vector& FFmpegFunctions::GetCodecs() const return mCodecPointers; } -std::unique_ptr -FFmpegFunctions::CreateFifoBuffer(int size) const -{ - return std::make_unique(*this, size); -} - void FFmpegFunctions::FillCodecsList() { mCodecs.clear(); diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.h b/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.h index 6e74279eceaa..53aefff25c53 100644 --- a/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.h +++ b/modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.h @@ -119,8 +119,6 @@ struct FFMPEG_SUPPORT_API FFmpegFunctions : const std::vector& GetOutputFormats() const; const std::vector& GetCodecs() const; - std::unique_ptr CreateFifoBuffer(int size) const; - template AVDataBuffer CreateMemoryBuffer(int preallocatedSize) const { diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.cpp b/modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.cpp new file mode 100644 index 000000000000..07a3a2f72362 --- /dev/null +++ b/modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.cpp @@ -0,0 +1,106 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + FifoBuffer.h + + Dmitry Vedenko + +**********************************************************************/ + +#include "FifoBuffer.h" + +#include + +FifoBuffer::Page::Page(size_t size) + : Data(size) +{ +} + +void FifoBuffer::Page::Reset() +{ + WritePosition = 0; + ReadPosition = 0; +} + +FifoBuffer::FifoBuffer(int pageSize) + : mPageSize { pageSize } +{ +} + +int64_t FifoBuffer::Write(const void* dataPtr, int64_t size) +{ + const int8_t* data = static_cast(dataPtr); + + auto bytesLeft = size; + + while (bytesLeft > 0) + { + if ( + mActivePages.empty() || + mActivePages.back()->WritePosition == mPageSize) + { + if (mFreePages.empty()) + { + mAllocatedPages.emplace_back(mPageSize); + mFreePages.push_back(&mAllocatedPages.back()); + } + + mActivePages.push_back(mFreePages.back()); + mFreePages.pop_back(); + } + + auto& page = mActivePages.back(); + auto toWrite = std::min( + bytesLeft, static_cast(mPageSize - page->WritePosition)); + + std::copy(data, data + toWrite, page->Data.begin() + page->WritePosition); + page->WritePosition += toWrite; + mAvaliable += toWrite; + + data += toWrite; + bytesLeft -= toWrite; + } + + return size; +} + +int64_t FifoBuffer::Read(void* data, int64_t size) +{ + size = std::min(size, mAvaliable); + + int8_t* dataPtr = static_cast(data); + + int bytesRead = 0; + + while (size > 0) + { + auto& page = mActivePages.front(); + auto toRead = + std::min(size, static_cast(mPageSize - page->ReadPosition)); + + std::copy( + page->Data.begin() + page->ReadPosition, + page->Data.begin() + page->ReadPosition + toRead, dataPtr); + page->ReadPosition += toRead; + mAvaliable -= toRead; + + dataPtr += toRead; + size -= toRead; + bytesRead += toRead; + + if (page->ReadPosition == mPageSize) + { + page->Reset(); + mFreePages.push_back(page); + mActivePages.pop_front(); + } + } + + return bytesRead; +} + +int64_t FifoBuffer::GetAvailable() const +{ + return mAvaliable; +} diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.h b/modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.h new file mode 100644 index 000000000000..1f441796df22 --- /dev/null +++ b/modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.h @@ -0,0 +1,46 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + FifoBuffer.h + + Dmitry Vedenko + +**********************************************************************/ + +#pragma once + +#include +#include +#include + +class FFMPEG_SUPPORT_API FifoBuffer final +{ +public: + explicit FifoBuffer(int pageSize); + + int64_t Write(const void* data, int64_t size); + int64_t Read(void* data, int64_t size); + + int64_t GetAvailable() const; + +private: + struct Page final + { + explicit Page(size_t size); + + void Reset(); + + std::vector Data; + size_t WritePosition {}; + size_t ReadPosition {}; + }; + + std::deque mAllocatedPages; + + std::deque mActivePages; + std::vector mFreePages; + + int64_t mAvaliable {}; + int mPageSize {}; +}; diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/impl/avutil/AVUtilFunctionsLoader.cpp b/modules/mod-ffmpeg/lib-ffmpeg-support/impl/avutil/AVUtilFunctionsLoader.cpp index 1d7881c6f5ad..459d0d7b5a04 100644 --- a/modules/mod-ffmpeg/lib-ffmpeg-support/impl/avutil/AVUtilFunctionsLoader.cpp +++ b/modules/mod-ffmpeg/lib-ffmpeg-support/impl/avutil/AVUtilFunctionsLoader.cpp @@ -29,12 +29,7 @@ bool LoadAVUtilFunctions( RESOLVE(av_get_bytes_per_sample); RESOLVE(av_log_set_callback); RESOLVE(av_log_default_callback); - RESOLVE(av_fifo_alloc); - RESOLVE(av_fifo_generic_read); - RESOLVE(av_fifo_realloc2); - RESOLVE(av_fifo_free); - RESOLVE(av_fifo_size); - RESOLVE(av_fifo_generic_write); + RESOLVE(av_rescale_q); RESOLVE(av_frame_alloc); RESOLVE(av_frame_free); diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.cpp b/modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.cpp deleted file mode 100644 index 914a657ec740..000000000000 --- a/modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/********************************************************************** - - Audacity: A Digital Audio Editor - - AVFifoBufferWrapper.cpp - - Dmitry Vedenko - -**********************************************************************/ - -#include "AVFifoBufferWrapper.h" - -#include "FFmpegFunctions.h" - - -AVFifoBufferWrapper::AVFifoBufferWrapper( - const FFmpegFunctions& ffmpeg, int size) noexcept - : mFFmpeg(ffmpeg) -{ - mAVFifoBuffer = mFFmpeg.av_fifo_alloc(size); -} - -AVFifoBuffer* AVFifoBufferWrapper::GetWrappedValue() noexcept -{ - return mAVFifoBuffer; -} - -const AVFifoBuffer* AVFifoBufferWrapper::GetWrappedValue() const noexcept -{ - return mAVFifoBuffer; -} - -AVFifoBufferWrapper::~AVFifoBufferWrapper() -{ - mFFmpeg.av_fifo_free(mAVFifoBuffer); -} diff --git a/modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.h b/modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.h deleted file mode 100644 index 7e768ccff09e..000000000000 --- a/modules/mod-ffmpeg/lib-ffmpeg-support/wrappers/AVFifoBufferWrapper.h +++ /dev/null @@ -1,36 +0,0 @@ -/********************************************************************** - - Audacity: A Digital Audio Editor - - AVFifoBufferWrapper.h - - Dmitry Vedenko - -**********************************************************************/ - -#pragma once - -struct FFmpegFunctions; -typedef struct AVFifoBuffer AVFifoBuffer; - -class FFMPEG_SUPPORT_API AVFifoBufferWrapper -{ -public: - AVFifoBufferWrapper(const AVFifoBufferWrapper&) = delete; - AVFifoBufferWrapper& operator=(AVFifoBufferWrapper&) = delete; - - AVFifoBufferWrapper(AVFifoBufferWrapper&&) = delete; - AVFifoBufferWrapper& operator=(AVFifoBufferWrapper&&) = delete; - - AVFifoBufferWrapper( - const FFmpegFunctions& ffmpeg, int size) noexcept; - - AVFifoBuffer* GetWrappedValue() noexcept; - const AVFifoBuffer* GetWrappedValue() const noexcept; - - virtual ~AVFifoBufferWrapper(); - -protected: - const FFmpegFunctions& mFFmpeg; - AVFifoBuffer* mAVFifoBuffer { nullptr }; -};