From: Andrew Kelley <[email protected]> It sets up a filter chain, decodes audio into the buffersrc, and plays audio from the buffersink using libao.
Signed-off-by: Diego Biurrun <[email protected]> --- All my previous fixes are squashed into this, plus cleanup of mixed declarations and statements. libavfilter/Makefile | 1 + libavfilter/api-example.c | 324 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 325 insertions(+) create mode 100644 libavfilter/api-example.c diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 96fa8c0..2d89cf7 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -94,5 +94,6 @@ OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o OBJS-$(HAVE_THREADS) += pthread.o +EXAMPLES = api TOOLS = graph2dot TESTPROGS = filtfmts diff --git a/libavfilter/api-example.c b/libavfilter/api-example.c new file mode 100644 index 0000000..e9adc25 --- /dev/null +++ b/libavfilter/api-example.c @@ -0,0 +1,324 @@ +/* + * copyright (c) 2013 Andrew Kelley + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * libavfilter API use example. + * + * @example libavfilter/api-example.c + * This example will read a file, decode the main audio stream, + * pass it through a simple filter chain, and then send the samples + * to the default sound device with libao. + * + * The filter chain it uses is: + * (decoded samples) -> abuffer -> volume -> aformat -> abuffersink -> (device) + * + * abuffer: this provides the endpoint where you can feed the decoded samples. + * volume: in this example we hardcode it to 0.90 + * aformat: this converts the samples to the samplefreq, channel layout, + * and sample format required by the audio device. + * abuffersink: this provides the endpoint where you can read the samples after + * they have passed through the filter chain. + */ + +#include <ao/ao.h> + +#include <libavutil/channel_layout.h> +#include <libavutil/opt.h> +#include <libavutil/samplefmt.h> +#include <libavformat/avformat.h> +#include <libavfilter/avfilter.h> +#include <libavfilter/buffersink.h> +#include <libavfilter/buffersrc.h> + +static ao_device *device = NULL; + +static char strbuf[512]; +static AVFilterGraph *filter_graph = NULL; +static AVFilterContext *abuffer_ctx = NULL; +static AVFilterContext *volume_ctx = NULL; +static AVFilterContext *aformat_ctx = NULL; +static AVFilterContext *abuffersink_ctx = NULL; + +static AVFrame *oframe = NULL; + +static int init_filter_graph(AVFormatContext *ic, AVStream *audio_st) +{ + int err; + double vol = 0.90; + AVFilter *abuffer = avfilter_get_by_name("abuffer"); + AVFilter *volume = avfilter_get_by_name("volume"); + AVFilter *aformat = avfilter_get_by_name("aformat"); + AVFilter *abuffersink = avfilter_get_by_name("abuffersink"); + AVCodecContext *avctx = audio_st->codec; + AVRational time_base = audio_st->time_base; + + // create new graph + 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; + } + + // create abuffer filter + snprintf(strbuf, sizeof(strbuf), + "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%"PRIx64, + time_base.num, time_base.den, avctx->sample_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 + 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(AV_SAMPLE_FMT_S16), 44100, + (uint64_t)AV_CH_LAYOUT_STEREO); + 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; + } + return 0; +} + +static int audio_decode_frame(AVFormatContext *ic, AVStream *audio_st, + AVPacket *pkt, AVFrame *frame) +{ + int err, len1, got_frame, new_packet = 1; + AVPacket pkt_temp_ = { 0 }; + AVPacket *pkt_temp = &pkt_temp_; + + *pkt_temp = *pkt; + + while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet)) { + avcodec_get_frame_defaults(frame); + new_packet = 0; + + len1 = avcodec_decode_audio4(audio_st->codec, frame, + &got_frame, pkt_temp); + if (len1 < 0) { + // if error we skip the frame + pkt_temp->size = 0; + return -1; + } + + pkt_temp->data += len1; + pkt_temp->size -= len1; + + if (!got_frame) { + // stop sending empty packets if the decoder is finished + if (!pkt_temp->data && + audio_st->codec->codec->capabilities & CODEC_CAP_DELAY) { + return 0; + } + continue; + } + + // 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; + } + ao_play(device, (void *)oframe->data[0], oframe->linesize[0]); + } + return 0; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int audio_stream_index, eof = 0, err, i; + ao_sample_format fmt = { 0 }; + AVCodecContext *avctx; + AVFormatContext *ic = NULL; + AVCodec *decoder = NULL; + AVStream *audio_st; + AVPacket audio_pkt = { 0 }; + AVPacket *pkt = &audio_pkt; + AVFrame *frame; + char *filename = argv[1]; + + if (argc < 2) { + fprintf(stderr, "Usage: %s file\n", argv[0]); + return 1; + } + + ao_initialize(); + avcodec_register_all(); + av_register_all(); + avformat_network_init(); + avfilter_register_all(); + + fmt.bits = 16; + fmt.channels = 2; + fmt.rate = 44100; + fmt.byte_format = AO_FMT_NATIVE; + device = ao_open_live(ao_default_driver_id(), &fmt, NULL); + if (!device) { + av_log(NULL, AV_LOG_ERROR, "opening audio device\n"); + return 1; + } + + if (avformat_open_input(&ic, filename, NULL, NULL) < 0) { + av_log(NULL, AV_LOG_ERROR, "error opening %s\n", filename); + return 1; + } + + if (avformat_find_stream_info(ic, NULL) < 0) { + av_log(NULL, AV_LOG_ERROR, + "%s: could not find codec parameters\n", filename); + return 1; + } + + // set all streams to discard. in a few lines here we will find the audio + // stream and cancel discarding it + for (i = 0; i < ic->nb_streams; i++) + ic->streams[i]->discard = AVDISCARD_ALL; + + audio_stream_index = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, + &decoder, 0); + + if (audio_stream_index < 0) { + av_log(NULL, AV_LOG_ERROR, "%s: no audio stream found\n", ic->filename); + return 1; + } + + if (!decoder) { + av_log(NULL, AV_LOG_ERROR, "%s: no decoder found\n", ic->filename); + return 1; + } + + audio_st = ic->streams[audio_stream_index]; + audio_st->discard = AVDISCARD_DEFAULT; + + avctx = audio_st->codec; + + if (avcodec_open2(avctx, decoder, NULL) < 0) { + av_log(NULL, AV_LOG_ERROR, "Unable to open decoder\n"); + return 1; + } + + if (!avctx->channel_layout) + avctx->channel_layout = av_get_default_channel_layout(avctx->channels); + if (!avctx->channel_layout) { + av_log(NULL, AV_LOG_ERROR, "Unable to guess channel layout\n"); + return 1; + } + + if (init_filter_graph(ic, audio_st) < 0) { + av_log(NULL, AV_LOG_ERROR, "Unable to init filter graph\n"); + return 1; + } + + frame = avcodec_alloc_frame(); + oframe = av_frame_alloc(); + if (!oframe) { + av_log(NULL, AV_LOG_ERROR, "error allocating oframe\n"); + return 1; + } + + for (;;) { + if (eof) { + if (avctx->codec->capabilities & CODEC_CAP_DELAY) { + av_init_packet(pkt); + pkt->data = NULL; + pkt->size = 0; + pkt->stream_index = audio_stream_index; + if (audio_decode_frame(ic, audio_st, pkt, frame) > 0) { + // keep flushing + continue; + } + } + break; + } + err = av_read_frame(ic, pkt); + if (err < 0) { + if (err != AVERROR_EOF) + av_log(NULL, AV_LOG_WARNING, "Error reading frames\n"); + eof = 1; + continue; + } + if (pkt->stream_index != audio_stream_index) { + av_free_packet(pkt); + continue; + } + audio_decode_frame(ic, audio_st, pkt, frame); + av_free_packet(pkt); + } + + avformat_network_deinit(); + ao_close(device); + ao_shutdown(); + + return 0; +} -- 1.7.9.5 _______________________________________________ libav-devel mailing list [email protected] https://lists.libav.org/mailman/listinfo/libav-devel
