summaryrefslogtreecommitdiff
path: root/audio/muxer.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/muxer.c')
-rw-r--r--audio/muxer.c235
1 files changed, 235 insertions, 0 deletions
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 <asm-generic/errno-base.h>
+#include <assert.h>
+#include <libavcodec/avcodec.h>
+#include <libavutil/error.h>
+#include <libavutil/frame.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#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);
+}