From a0b01b39ff31896363c98d9fc310b453e49a05a7 Mon Sep 17 00:00:00 2001 From: Daniel Morandini Date: Thu, 2 Nov 2023 11:08:54 +0100 Subject: [PATCH] Refactor output feormat handling --- c_src/decoder.c | 44 +++++++++++++++++++-------------------- c_src/decoder.h | 20 ++++++++++-------- c_src/libav.c | 14 ++++++------- test/avx/decoder_test.exs | 22 ++++++++++++++++++++ 4 files changed, 61 insertions(+), 39 deletions(-) diff --git a/c_src/decoder.c b/c_src/decoder.c index bcd48f8..a12b020 100644 --- a/c_src/decoder.c +++ b/c_src/decoder.c @@ -18,31 +18,19 @@ int is_planar(enum AVSampleFormat fmt) { } } -int alloc_resampler(Decoder *ctx, FormatOpts output_opts) { - AVChannelLayout *output_layout; +int alloc_resampler(Decoder *ctx) { AVCodecContext *codec_ctx; int errn; codec_ctx = ctx->codec_ctx; - output_layout = malloc(sizeof(AVChannelLayout)); - av_channel_layout_copy(output_layout, &ctx->codec_ctx->ch_layout); - output_layout->nb_channels = output_opts.channels; - - ctx->output_ch_layout = output_layout; - - output_opts.sample_format = - av_get_packed_sample_fmt(output_opts.sample_format); - - if ((errn = swr_alloc_set_opts2( - &(ctx->resampler_ctx), output_layout, output_opts.sample_format, - output_opts.sample_rate, &codec_ctx->ch_layout, - codec_ctx->sample_fmt, codec_ctx->sample_rate, 0, NULL)) != 0) + if ((errn = swr_alloc_set_opts2(&(ctx->resampler_ctx), ctx->output.ch_layout, + ctx->output.sample_format, + ctx->output.sample_rate, + &codec_ctx->ch_layout, codec_ctx->sample_fmt, + codec_ctx->sample_rate, 0, NULL)) != 0) return errn; - ctx->output_fmt = malloc(sizeof(FormatOpts)); - *ctx->output_fmt = output_opts; - return swr_init(ctx->resampler_ctx); } @@ -65,7 +53,16 @@ int decoder_alloc(Decoder **ctx, DecoderOpts opts) { ictx = (Decoder *)malloc(sizeof(Decoder)); ictx->codec_ctx = codec_ctx; - if ((errn = alloc_resampler(ictx, opts.output_opts)) < 0) + ictx->output.ch_layout = malloc(sizeof(AVChannelLayout)); + av_channel_layout_copy(ictx->output.ch_layout, &codec_ctx->ch_layout); + ictx->output.ch_layout->nb_channels = opts.output.nb_channels; + ictx->output.sample_rate = opts.output.sample_rate; + // Planar formats are not supported as they require a different procedure to + // lay down the plain bits contained in their frames. + ictx->output.sample_format = + av_get_packed_sample_fmt(opts.output.sample_format); + + if ((errn = alloc_resampler(ictx)) < 0) return errn; *ctx = ictx; @@ -88,9 +85,9 @@ int decoder_read_frame(Decoder *ctx, AVFrame *frame) { AVFrame *resampled_frame; resampled_frame = av_frame_alloc(); resampled_frame->nb_samples = frame->nb_samples; - resampled_frame->ch_layout = *ctx->output_ch_layout; - resampled_frame->sample_rate = frame->sample_rate; - resampled_frame->format = ctx->output_fmt->sample_format; + resampled_frame->ch_layout = *ctx->output.ch_layout; + resampled_frame->sample_rate = ctx->output.sample_rate; + resampled_frame->format = ctx->output.sample_format; if ((errn = av_frame_get_buffer(resampled_frame, 0)) != 0) return errn; @@ -110,9 +107,10 @@ int decoder_read_frame(Decoder *ctx, AVFrame *frame) { int decoder_free(Decoder **ctx) { avcodec_free_context(&(*ctx)->codec_ctx); + if ((*ctx)->resampler_ctx) { swr_free(&(*ctx)->resampler_ctx); - free((*ctx)->output_fmt); + av_channel_layout_uninit((*ctx)->output.ch_layout); } free(*ctx); diff --git a/c_src/decoder.h b/c_src/decoder.h index 107574a..7c21731 100644 --- a/c_src/decoder.h +++ b/c_src/decoder.h @@ -1,20 +1,18 @@ #include "libavcodec/codec_par.h" +#include "libavutil/channel_layout.h" #include "libavutil/samplefmt.h" #include "libswresample/swresample.h" #include -typedef struct { - int sample_rate; - int channels; - enum AVSampleFormat sample_format; -} FormatOpts; - typedef struct { AVCodecContext *codec_ctx; SwrContext *resampler_ctx; - FormatOpts *output_fmt; - AVChannelLayout *output_ch_layout; + struct { + int sample_rate; + enum AVSampleFormat sample_format; + AVChannelLayout *ch_layout; + } output; } Decoder; typedef struct { @@ -22,7 +20,11 @@ typedef struct { AVCodecParameters *params; AVRational timebase; - FormatOpts output_opts; + struct { + int sample_rate; + int nb_channels; + enum AVSampleFormat sample_format; + } output; } DecoderOpts; int decoder_alloc(Decoder **ctx, DecoderOpts opts); diff --git a/c_src/libav.c b/c_src/libav.c index c20fcd7..f1d2d30 100644 --- a/c_src/libav.c +++ b/c_src/libav.c @@ -229,6 +229,7 @@ ERL_NIF_TERM enif_decoder_alloc(ErlNifEnv *env, int argc, Decoder *ctx; DecoderOpts *opts; int buf_size = 256; + int nb_channels; char *buf; int errn; @@ -252,15 +253,15 @@ ERL_NIF_TERM enif_decoder_alloc(ErlNifEnv *env, int argc, // Output opts enif_get_map_value(env, argv[1], enif_make_atom(env, "sample_rate"), &tmp); - enif_get_int(env, tmp, &opts->output_opts.sample_rate); + enif_get_int(env, tmp, &opts->output.sample_rate); enif_get_map_value(env, argv[1], enif_make_atom(env, "channels"), &tmp); - enif_get_int(env, tmp, &opts->output_opts.channels); + enif_get_int(env, tmp, &opts->output.nb_channels); enif_get_map_value(env, argv[1], enif_make_atom(env, "sample_format"), &tmp); buf = malloc(buf_size); enif_get_string(env, tmp, buf, buf_size, ERL_NIF_LATIN1); - opts->output_opts.sample_format = av_get_sample_fmt(buf); + opts->output.sample_format = av_get_sample_fmt(buf); if ((errn = decoder_alloc(&ctx, *opts)) < 0) return enif_make_av_error(env, errn); @@ -298,14 +299,13 @@ ERL_NIF_TERM enif_decoder_stream_format(ErlNifEnv *env, int argc, if (ctx->codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { enif_make_map_put(env, map, enif_make_atom(env, "channels"), - enif_make_int(env, ctx->codec_ctx->ch_layout.nb_channels), + enif_make_int(env, ctx->output.ch_layout->nb_channels), &map); enif_make_map_put(env, map, enif_make_atom(env, "sample_rate"), - enif_make_int(env, ctx->codec_ctx->sample_rate), &map); + enif_make_int(env, ctx->output.sample_rate), &map); enif_make_map_put( env, map, enif_make_atom(env, "sample_format"), - enif_make_string(env, - av_get_sample_fmt_name(ctx->output_fmt->sample_format), + enif_make_string(env, av_get_sample_fmt_name(ctx->output.sample_format), ERL_NIF_UTF8), &map); } diff --git a/test/avx/decoder_test.exs b/test/avx/decoder_test.exs index c441fda..79d9974 100644 --- a/test/avx/decoder_test.exs +++ b/test/avx/decoder_test.exs @@ -34,6 +34,28 @@ defmodule AVx.DecoderTest do end end + test "decodes into a different format" do + {:ok, demuxer} = Demuxer.new_from_file(List.first(@inputs)) + [aac] = Demuxer.read_streams(demuxer) + packets = Demuxer.consume_packets(demuxer, [aac.stream_index]) + + output_format = %{ + sample_rate: 128_000, + channels: 1, + sample_format: "s16" + } + + {:ok, decoder} = Decoder.new(aac, output_format) + assert ^output_format = Decoder.stream_format(decoder) + + frame_count = + packets + |> Decoder.decode_frames(decoder) + |> Enum.count() + + assert frame_count == 564 + end + defp assert_frames(packets, decoder, expected_frames, ext) do count = packets