61 m_last_error =
"AudioEncodeContext::open called on unopened mux";
65 if (codec_id == AV_CODEC_ID_NONE)
68 if (codec_id == AV_CODEC_ID_NONE) {
73 const AVCodec* codec = avcodec_find_encoder(codec_id);
75 m_last_error = std::string(
"avcodec_find_encoder failed for codec ")
76 + avcodec_get_name(codec_id);
86 AVSampleFormat enc_fmt = AV_SAMPLE_FMT_FLTP;
88 const AVSampleFormat* fmts =
nullptr;
90 if (avcodec_get_supported_config(
nullptr, codec, AV_CODEC_CONFIG_SAMPLE_FORMAT,
91 0,
reinterpret_cast<const void**
>(&fmts), &n_fmts)
93 && fmts && n_fmts > 0) {
95 for (
int i = 0; i < n_fmts; ++i) {
96 if (fmts[i] == AV_SAMPLE_FMT_S16) {
97 enc_fmt = AV_SAMPLE_FMT_S16;
100 if (fmts[i] == AV_SAMPLE_FMT_FLTP) {
101 enc_fmt = AV_SAMPLE_FMT_FLTP;
108 uint32_t enc_rate = sample_rate;
110 const int* rates =
nullptr;
112 if (avcodec_get_supported_config(
nullptr, codec, AV_CODEC_CONFIG_SAMPLE_RATE,
113 0,
reinterpret_cast<const void**
>(&rates), &n_rates)
115 && rates && n_rates > 0) {
117 for (
int i = 0; i < n_rates; ++i) {
118 if (
static_cast<uint32_t
>(rates[i]) == sample_rate) {
124 enc_rate =
static_cast<uint32_t
>(rates[0]);
129 static_cast<int>(channels));
132 codec_context->time_base = { .num = 1, .den =
static_cast<int>(enc_rate) };
151 m_last_error =
"avcodec_parameters_from_context failed";
314 : av_audio_fifo_size(
fifo);
316 AVFrame* frame = av_frame_alloc();
322 frame->nb_samples = frame_size;
325 av_channel_layout_copy(&frame->ch_layout, &
codec_context->ch_layout);
327 if (av_frame_get_buffer(frame, 0) < 0) {
329 av_frame_free(&frame);
333 if (av_frame_make_writable(frame) < 0) {
335 av_frame_free(&frame);
339 int available = av_audio_fifo_size(
fifo);
340 int to_read = std::min(available, frame_size);
342 int read = av_audio_fifo_read(
fifo,
343 reinterpret_cast<void**
>(frame->data), to_read);
345 if (read < to_read) {
346 m_last_error =
"av_audio_fifo_read returned fewer samples than expected";
347 av_frame_free(&frame);
351 if (pad_to_frame_size && read < frame_size) {
352 int pad_samples = frame_size - read;
353 int bytes_per_sample = av_get_bytes_per_sample(
codec_context->sample_fmt);
354 bool is_planar = av_sample_fmt_is_planar(
codec_context->sample_fmt);
355 int planes = is_planar ?
static_cast<int>(
m_channels) : 1;
356 int samples_per_plane = is_planar ? pad_samples : pad_samples *
static_cast<int>(
m_channels);
358 for (
int p = 0; p < planes; ++p) {
360 frame->data[p] +
static_cast<ptrdiff_t
>(read) * bytes_per_sample * (is_planar ? 1 :
static_cast<int>(
m_channels)),
362 static_cast<size_t>(samples_per_plane) *
static_cast<size_t>(bytes_per_sample));
364 frame->nb_samples = frame_size;
366 frame->nb_samples = read;
370 m_pts += frame->nb_samples;
373 av_frame_free(&frame);
376 char errbuf[AV_ERROR_MAX_STRING_SIZE];
377 av_strerror(ret, errbuf,
sizeof(errbuf));
378 m_last_error = std::string(
"avcodec_send_frame failed: ") + errbuf;
bool is_valid() const
True if codec, resampler, and FIFO are all ready.
bool drain_packets(FFmpegMuxContext &mux)
Drain all packets currently available from the encoder into mux.
void close()
Release all owned resources.
bool drain(FFmpegMuxContext &mux)
Flush the FIFO remainder and encoder internal delay to the mux.
bool send_fifo_frame(FFmpegMuxContext &mux, bool pad_to_frame_size)
Pull one frame from the FIFO and send it to the encoder.
bool open(FFmpegMuxContext &mux, uint32_t sample_rate, uint32_t channels, AVCodecID codec_id)
Open the encoder and register an audio stream in the mux context.
AVCodecContext * codec_context
Owned; freed in destructor.
SwrContext * swr_context
Owned; freed in destructor.
AVAudioFifo * fifo
Owned; freed in destructor.
bool encode_frames(std::span< const double > interleaved, uint32_t num_frames, FFmpegMuxContext &mux)
Encode a block of interleaved double-precision PCM frames.
AVStream * stream
Owned by mux; pointer cached here.
static void init_ffmpeg()
Initialise FFmpeg logging level once per process.
bool is_open() const
True if the context is open and ready to accept streams / packets.
AVStream * add_stream()
Allocate a new AVStream inside this context.
const std::string & last_error() const
bool write_packet(AVPacket *pkt)
Submit one encoded packet for interleaved writing.
AVFormatContext * format_context
Owned; freed in close().
RAII owner of a single AVFormatContext on the write path.