From c1cb78d574c0429aa5e3ff3a2b3886e4bc153212 Mon Sep 17 00:00:00 2001 From: bbergeron Date: Wed, 3 Apr 2024 17:32:01 -0400 Subject: Reset Git repo and use a pseudonym to sign commits I used to sign my commits with my real name and my personal email address, which I wanted scrubbed off the "B." pseudosphere. Re-creating a new git repository was safer than simpler than re-writing the history (although the latter could've also worked but, oh well). --- audio/muxer.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 audio/muxer.c (limited to 'audio/muxer.c') diff --git a/audio/muxer.c b/audio/muxer.c new file mode 100644 index 0000000..3860476 --- /dev/null +++ b/audio/muxer.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "muxer.h" +#include "encoder.h" +#include "decoder.h" +#include "resampler.h" +#include "stream.h" + +#define NANOSEC (uint64_t)1000000000 + +struct AudioMuxer { + struct Stream stream; + struct Encoder encoder; + struct Decoder decoder; + struct Resampler resampler; + AVFrame *frame_in; + AVFrame *frame_out; + AVPacket *packet; + + /* The amount of output samples that went out of this muxer. Starts at 0 and + * keep on increasing. + */ + unsigned int nb_samples_out; +}; + +int64_t audiomuxer_flush(AudioMuxer *muxer); + +uint64_t audiomuxer_get_runtime(const AudioMuxer *muxer) +{ + return (muxer->nb_samples_out * NANOSEC) / muxer->resampler.out_sample_rate; +} + +int audiomuxer_init(AudioMuxer **pmuxer, AVFormatContext *s, const struct AudioMuxerOpt *opts) +{ + int err; + AudioMuxer *muxer; + AVFrame *frame_out; + AVFrame *frame_in; + AVPacket *pkt; + + *pmuxer = NULL; + if ((muxer = malloc(sizeof(AudioMuxer))) == NULL) + return AVERROR(ENOMEM); + + muxer->decoder.avctx = NULL; + muxer->encoder.avctx = NULL; + muxer->resampler.swr = NULL; + + err = stream_init( + &muxer->stream, + s, + opts->codec_id, + opts->sample_rate, + opts->sample_format, + &opts->ch_layout, + opts->opts_muxer); + if (err) goto error; + + err = encoder_init_for_stream(&muxer->encoder, muxer->stream.av_stream, opts->opts_encoder); + if (err) goto error; + + err = resampler_init_for_encoder(&muxer->resampler, &muxer->encoder); + if (err) goto error; + + if ((frame_out = av_frame_alloc()) == NULL) + goto enomem; + if ((frame_in = av_frame_alloc()) == NULL) + goto enomem; + if ((pkt = av_packet_alloc()) == NULL) + goto enomem; + + + frame_out->format = muxer->encoder.avctx->sample_fmt; + frame_out->nb_samples = muxer->encoder.avctx->frame_size; + frame_out->ch_layout = muxer->encoder.avctx->ch_layout; + frame_out->sample_rate = muxer->encoder.avctx->sample_rate; + if ((err = av_frame_get_buffer(frame_out, 0))) + goto error; + + muxer->packet = pkt; + muxer->frame_out = frame_out; + muxer->frame_in = frame_in; + *pmuxer = muxer; + return 0; + +enomem: + err = AVERROR(ENOMEM); +error: + audiomuxer_free(muxer); + return err; +} + +int audiomuxer_conf (AudioMuxer *muxer, const AVStream *input) +{ + int err, src_rate, dst_rate; + const AVCodec *codec; + SwrContext *resampler; + + src_rate = input->codecpar->sample_rate; + + err = decoder_init_for_stream(&muxer->decoder, input, NULL); // TODO: Pass options // TODO: Pass options + if (err) { + fprintf(stderr, "Failed to configure decoder: %s\n", av_err2str(err)); + return err; + } + + err = resampler_conf_for_decoder(&muxer->resampler, &muxer->decoder); + if (err) { + fprintf(stderr, "Failed to configure resampler: %s\n", av_err2str(err)); + return err; + } + + + muxer->frame_in->format = muxer->decoder.avctx->sample_fmt; + muxer->frame_in->nb_samples = muxer->decoder.avctx->frame_size; + muxer->frame_in->ch_layout = muxer->decoder.avctx->ch_layout; + muxer->frame_in->sample_rate = muxer->decoder.avctx->sample_rate; + if ((err = av_frame_get_buffer(muxer->frame_in, 0))) + return err; + + return 0; +} + +int audiomuxer_send_packet (AudioMuxer *muxer, const AVPacket *pkt_in) +{ + int err, duration; + AVFrame *frame_in; + + frame_in = muxer->frame_in; + + if ((err = decoder_send(&muxer->decoder, pkt_in)) < 0) + return err; + + /* Since a packet might contains several frames, we call decoder_convert + * repeatedly until we're out of data. + */ + while ((err = decoder_convert(&muxer->decoder, frame_in)) != AVERROR(EAGAIN)) { + /* Errors other than EAGAIN should be repported */ + if (err) { + fprintf(stderr, "Failed to decode frame: %s\n", av_err2str(err)); + return err; + } + + /* Immediately send frame to resampler and postpone silent injection */ + if ((err = resampler_send_frame(&muxer->resampler, frame_in)) < 0) { + fprintf(stderr, "Failed to send data to resampler: %s\n", av_err2str(err)); + return err; + } + + /* Flush frame if possible */ + if ((err = audiomuxer_flush(muxer)) < 0) + return err; + } + + return 0; +} + +int audiomuxer_send_silence (AudioMuxer *muxer, unsigned int microsec) +{ + int err; + if ((err = resampler_send_empty(&muxer->resampler, microsec))) + return err; + + return audiomuxer_flush(muxer); +} + + +int64_t audiomuxer_flush(AudioMuxer *muxer) +{ + int err; + AVPacket *pkt_out; + AVFrame *frame_out; + + frame_out = muxer->frame_out; + pkt_out = muxer->packet; + + if ((err = resampler_convert(&muxer->resampler, frame_out))) { + if (err == AVERROR(EAGAIN)) return 0; + fprintf(stderr, "Failed to resample: %s\n", av_err2str(err)); + return err; + } + + if ((err = encoder_send(&muxer->encoder, frame_out))) { + fprintf(stderr, "Failed to send frame to encoder: %s\n", av_err2str(err)); + return err; + } + + if ((err = encoder_convert(&muxer->encoder, pkt_out))) { + if (err == AVERROR(EAGAIN)) return 0; + fprintf(stderr, "Failed to encode packet: %s\n", av_err2str(err)); + return err; + } + + /* pkt_out->duration is the "Duration of this packet in AVStream->time_base + * units, 0 if unknown." In practice, the timebase appears to be 1 / + * sample_rate, so, in other words, pkt_out->duration is the amount of + * samples stored in that packet. + * + * TODO: This behaviour is however undocumented, and asserts should be put + * in place to catch potential regressions. + * + * NOTE: duration is usually 1152 with a default MP3 audio stream. + */ + assert(pkt_out->duration > 0); + muxer->nb_samples_out += pkt_out->duration; + + /* Finally send packet to stream. */ + if ((err = stream_send(&muxer->stream, pkt_out)) < 0) { + fprintf(stderr, "Failed stream packet: %s\n", av_err2str(err)); + return err; + } + + return 0; +} + + +void audiomuxer_free (AudioMuxer *muxer) +{ + if (muxer == NULL) return; + + /* Flush decoder */ + avcodec_send_packet(muxer->decoder.avctx, NULL); + + encoder_free(&muxer->encoder); + decoder_free(&muxer->decoder); + resampler_free(&muxer->resampler); + + free(muxer); +} -- cgit v1.2.3