Неправильное преобразование фильтра libav в новую частоту дискретизации - PullRequest
0 голосов
/ 15 января 2019

Я сделал тестовое приложение для тестирования FilterGraph в LibAV. Я хотел бы использовать его (среди прочих фильтров) для преобразования любой данной звуковой дорожки в 16-битную стерео PCM со знаком (s16) с частотой дискретизации 48 кГц. В настоящее время мой тестовый звуковой файл находится здесь: https://commons.wikimedia.org/wiki/File:Median_test.ogg Преобразование в порядке, когда я беру файлы с частотой дискретизации 44,100 Гц и заставляю afromat возвращать эту точную частоту дискретизации. Однако при попытке преобразования в частоту 48000 Гц появляется всплывающее окно.

Глядя на сгенерированную форму волны, кажется, что она довольно грубо преобразует аудиофайл, разнося волновую форму с течением времени и оставляя сгенерированные пробелы с нулями.

Насколько мне известно, и документация aformat должна создавать resample по мере необходимости для обработки ситуаций такого типа , как указано здесь .

Код, который я использую, показан ниже. Принимая ../test.ogg и выводя ../out_filter.raw как 16-битный PCM при 48000 Гц.

#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/channel_layout.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

#define BUFFER_SIZE 4096

FILE *fh;

int read_packet(void *opaque, uint8_t *buf, int buff_size) {
    return (int) fread(buf, 1, (size_t) buff_size, fh);
}

int perform_decoding() {
#define out_rate 48000
#define out_layout AV_CH_LAYOUT_STEREO
#define out_format AV_SAMPLE_FMT_S16

    AVFormatContext *ctx = avformat_alloc_context();
    uint8_t *buffer = av_malloc(BUFFER_SIZE);
    AVIOContext *aioctx = avio_alloc_context(
            buffer,       // Buffer
            BUFFER_SIZE,  // Buffer size
            0,            // write_flag
            NULL,         // opaque
            read_packet,  // read_packet
            NULL,         // write_packet
            NULL          // seek
    );
    ctx->pb = aioctx;
    avformat_open_input(&ctx, "stream", NULL, NULL);
    if (avformat_find_stream_info(ctx, NULL) < 0) {
        fprintf(stderr, "Resampler has read the stream info.\n");
        return -1;
    }

    int audio_stream_index = -1;
    AVStream *stream = NULL;
    for (int i = 0; i < ctx->nb_streams; i++) {
        stream = ctx->streams[i];
        if (stream->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            audio_stream_index = i;
            break;
        }
    }
    if (audio_stream_index == -1) {
        fprintf(stderr, "Could not find the audio stream.\n");
        return -1;
    }

    AVCodecContext *cctx = stream->codec;
    AVCodecParameters *params = stream->codecpar;
#define in_rate params->sample_rate
#define in_layout params->channel_layout
#define in_format params->format

    AVCodec *codec = avcodec_find_decoder(cctx->codec_id);
    if (codec == NULL) {
        fprintf(stderr, "Codec not found.\n");
        return -1;
    }

    if (avcodec_open2(cctx, codec, NULL) < 0) {
        fprintf(stderr, "error: avcodec_open2()\n");
        return -1;
    }

    AVPacket packet;
    av_init_packet(&packet);
    packet.data = NULL;
    packet.size = 0;

    AVFrame *frame = av_frame_alloc();

    FILE *out = fopen("../out_filter.raw", "wb");


    /// { FILTER
    AVFilterGraph *filter_graph = NULL;
    filter_graph = avfilter_graph_alloc();
    if (!filter_graph) {
        av_log(NULL, AV_LOG_ERROR, "unable to create filter graph: out of memory\n");
        return -1;
    }

    AVFilterContext *abuffer_ctx = NULL;
    AVFilterContext *volume_ctx = NULL;
    AVFilterContext *aformat_ctx = NULL;
    AVFilterContext *abuffersink_ctx = NULL;
    const AVFilter *abuffer = avfilter_get_by_name("abuffer");
    const AVFilter *volume = avfilter_get_by_name("volume");
    const AVFilter *aformat = avfilter_get_by_name("aformat");
    const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");

    int err;
    char strbuf[512];
    // create abuffer filter
    AVCodecContext *avctx = cctx;
    AVRational time_base = cctx->time_base;
    snprintf(strbuf, sizeof(strbuf),
             "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"
                     PRIx64,
             time_base.num, time_base.den, in_rate,
             av_get_sample_fmt_name(avctx->sample_fmt),
             avctx->channel_layout);
    fprintf(stderr, "abuffer: %s\n", strbuf);
    err = avfilter_graph_create_filter(&abuffer_ctx, abuffer,
                                       NULL, strbuf, NULL, filter_graph);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "error initializing abuffer filter\n");
        return err;
    }
    // create volume filter
    double vol = 0.2;
    snprintf(strbuf, sizeof(strbuf), "volume=%f", vol);
    fprintf(stderr, "volume: %s\n", strbuf);
    err = avfilter_graph_create_filter(&volume_ctx, volume, NULL, strbuf, NULL, filter_graph);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "error initializing volume filter\n");
        return err;
    }
    // create aformat filter
    snprintf(strbuf, sizeof(strbuf),
             "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
             av_get_sample_fmt_name(out_format), out_rate, (uint64_t) out_layout);
    fprintf(stderr, "aformat: %s\n", strbuf);
    err = avfilter_graph_create_filter(&aformat_ctx, aformat, NULL, strbuf, NULL, filter_graph);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "unable to create aformat filter\n");
        return err;
    }
    // create abuffersink filter
    err = avfilter_graph_create_filter(&abuffersink_ctx, abuffersink, NULL, NULL, NULL, filter_graph);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "unable to create aformat filter\n");
        return err;
    }

    // connect inputs and outputs
    if (err >= 0) err = avfilter_link(abuffer_ctx, 0, volume_ctx, 0);
    if (err >= 0) err = avfilter_link(volume_ctx, 0, aformat_ctx, 0);
    if (err >= 0) err = avfilter_link(aformat_ctx, 0, abuffersink_ctx, 0);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "error connecting filters\n");
        return err;
    }
    err = avfilter_graph_config(filter_graph, NULL);
    if (err < 0) {
        av_log(NULL, AV_LOG_ERROR, "error configuring the filter graph\n");
        return err;
    }
    /// } FILTER

    while (av_read_frame(ctx, &packet) >= 0) { /// Fetch a packet
        if (packet.stream_index != audio_stream_index) {
            continue;
        }

        /// Decode packet to frame V1
        avcodec_send_packet(cctx, &packet); /// Toss the packet into the codec context
        av_frame_unref(frame);
        avcodec_receive_frame(cctx, frame); /// Pull out a frame
        if (frame->nb_samples == 0) {
            av_packet_unref(&packet);
            continue;
        }

        AVFrame *oframe = NULL;

        oframe = av_frame_alloc();
        if (!oframe) {
            av_log(NULL, AV_LOG_ERROR, "error allocating oframe\n");
            return 1;
        }

        /// Push the audio data from decoded frame into the filtergraph
        err = av_buffersrc_write_frame(abuffer_ctx, frame);
        if (err < 0) {
            av_log(NULL, AV_LOG_ERROR, "error writing frame to buffersrc\n");
            return -1;
        }
        /// Pull filtered audio from the filtergraph
        for (;;) {
            err = av_buffersink_get_frame(abuffersink_ctx, oframe);
            if (err == AVERROR_EOF || err == AVERROR(EAGAIN))
                break;
            if (err < 0) {
                av_log(NULL, AV_LOG_ERROR, "error reading buffer from buffersink\n");
                return -1;
            }
            int nb_channels = av_get_channel_layout_nb_channels(frame->channel_layout);
            int bytes_per_sample = av_get_bytes_per_sample(frame->format);
            int data_size = frame->nb_samples * nb_channels * bytes_per_sample;
            fwrite(oframe->data[0], 1, (size_t) data_size, out);
        }

        av_free_packet(&packet);
    }

    return 0;
}

int main() {
    avcodec_register_all();
    av_register_all();
    avformat_network_init();
    avfilter_register_all();

    fh = fopen("../test.ogg", "rb");
    if (fh == NULL) {
        printf("Cannot open the file.\n");
        return -1;
    }
    int ret = perform_decoding();
    if (ret != 0) {
        return ret;
    }
    printf("Finished!");
    return 0;
}

1 Ответ

0 голосов
/ 16 января 2019

Фрагмент, показанный ниже, по ошибке использовал входной кадр, чтобы определить байты для копирования из нового преобразованного кадра. Этот кусок кода использовал входной кадр с именем frame вместо выходного кадра с именем oframe.

int nb_channels = av_get_channel_layout_nb_channels(frame->channel_layout);
int bytes_per_sample = av_get_bytes_per_sample(frame->format);
int data_size = frame->nb_samples * nb_channels * bytes_per_sample;
fwrite(oframe->data[0], 1, (size_t) data_size, out);

Исправленная версия показана ниже:

int nb_channels = av_get_channel_layout_nb_channels(oframe->channel_layout);
int bytes_per_sample = av_get_bytes_per_sample(oframe->format);
int data_size = oframe->nb_samples * nb_channels * bytes_per_sample;
fwrite(oframe->data[0], 1, (size_t) data_size, out);

Заикание было вызвано неправильным копированием байтов.

...