Я сделал тестовое приложение для тестирования 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;
}