Skip to content

Commit

Permalink
Do not use FFmpeg provide FIFO for the encoder
Browse files Browse the repository at this point in the history
FFmpeg has changed the FIFO API and the new API is not quite compatible
with Audacity exporter
  • Loading branch information
crsib committed Apr 8, 2024
1 parent 435ede5 commit 5c3de04
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 109 deletions.
26 changes: 11 additions & 15 deletions modules/mod-ffmpeg/ExportFFmpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function.

#include "../FFmpeg.h"
#include "FFmpegFunctions.h"
#include "FifoBuffer.h"

#include <wx/app.h>
#include <wx/log.h>
Expand Down Expand Up @@ -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<AVFifoBufferWrapper> mEncAudioFifo; // FIFO to write incoming audio samples into
std::unique_ptr<FifoBuffer> mEncAudioFifo; // FIFO to write incoming audio samples into
AVDataBuffer<int16_t> mEncAudioFifoOutBuf; // buffer to read _out_ of the FIFO into
std::unique_ptr<AVFormatContextWrapper> mEncFormatCtx; // libavformat's context for our output file
std::unique_ptr<AVCodecContextWrapper> mEncAudioCodecCtx; // the encoder for the output audio stream
Expand Down Expand Up @@ -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<FifoBuffer>(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.
Expand Down Expand Up @@ -1471,8 +1472,8 @@ bool FFmpegExporter::Finalize()
{
std::unique_ptr<AVPacketWrapper> 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;

Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down Expand Up @@ -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");
Expand All @@ -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<AVPacketWrapper> pkt = mFFmpeg->CreateAVPacketWrapper();

Expand Down
7 changes: 1 addition & 6 deletions modules/mod-ffmpeg/lib-ffmpeg-support/AVUtilFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 4 additions & 2 deletions modules/mod-ffmpeg/lib-ffmpeg-support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 0 additions & 6 deletions modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,6 @@ const std::vector<const AVCodecWrapper*>& FFmpegFunctions::GetCodecs() const
return mCodecPointers;
}

std::unique_ptr<AVFifoBufferWrapper>
FFmpegFunctions::CreateFifoBuffer(int size) const
{
return std::make_unique<AVFifoBufferWrapper>(*this, size);
}

void FFmpegFunctions::FillCodecsList()
{
mCodecs.clear();
Expand Down
2 changes: 0 additions & 2 deletions modules/mod-ffmpeg/lib-ffmpeg-support/FFmpegFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ struct FFMPEG_SUPPORT_API FFmpegFunctions :
const std::vector<const AVOutputFormatWrapper*>& GetOutputFormats() const;
const std::vector<const AVCodecWrapper*>& GetCodecs() const;

std::unique_ptr<AVFifoBufferWrapper> CreateFifoBuffer(int size) const;

template<typename T>
AVDataBuffer<T> CreateMemoryBuffer(int preallocatedSize) const
{
Expand Down
106 changes: 106 additions & 0 deletions modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**********************************************************************
Audacity: A Digital Audio Editor
FifoBuffer.h
Dmitry Vedenko
**********************************************************************/

#include "FifoBuffer.h"

#include <algorithm>

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<const int8_t*>(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<int64_t>(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<int8_t*>(data);

int bytesRead = 0;

while (size > 0)
{
auto& page = mActivePages.front();
auto toRead =
std::min(size, static_cast<int64_t>(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;
}
46 changes: 46 additions & 0 deletions modules/mod-ffmpeg/lib-ffmpeg-support/FifoBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**********************************************************************
Audacity: A Digital Audio Editor
FifoBuffer.h
Dmitry Vedenko
**********************************************************************/

#pragma once

#include <cstdint>
#include <deque>
#include <vector>

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<char> Data;
size_t WritePosition {};
size_t ReadPosition {};
};

std::deque<Page> mAllocatedPages;

std::deque<Page*> mActivePages;
std::vector<Page*> mFreePages;

int64_t mAvaliable {};
int mPageSize {};
};
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

This file was deleted.

This file was deleted.

0 comments on commit 5c3de04

Please sign in to comment.