Hi, just a gentle reminder about this patch. It's been 7 days without a review.
It's a simple libavfilter example program. Coincidentally it happens to be affected by a libavfilter bug<https://bugzilla.libav.org/show_bug.cgi?id=560> for sample rates != 44100. On Mon, Sep 16, 2013 at 2:48 PM, Andrew Kelley <[email protected]> wrote: > Note: this example program is affected by this libavfilter bug: > https://bugzilla.libav.org/show_bug.cgi?id=560 > > > On Mon, Sep 16, 2013 at 2:42 PM, Andrew Kelley <[email protected]>wrote: > >> It sets up a filter chain, decodes audio into the buffersrc, >> and plays audio from the buffersink using libao. >> --- >> libavfilter/api-example.c | 321 >> ++++++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 321 insertions(+) >> create mode 100644 libavfilter/api-example.c >> >> diff --git a/libavfilter/api-example.c b/libavfilter/api-example.c >> new file mode 100644 >> index 0000000..df5073f >> --- /dev/null >> +++ b/libavfilter/api-example.c >> @@ -0,0 +1,321 @@ >> +/* >> + * 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 <libavformat/avformat.h> >> +#include <libavfilter/avfilter.h> >> +#include <libavfilter/buffersink.h> >> +#include <libavfilter/buffersrc.h> >> +#include <libavutil/samplefmt.h> >> +#include <libavutil/opt.h> >> +#include <libavutil/channel_layout.h> >> + >> +#include <ao/ao.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) >> +{ >> + // 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; >> + } >> + >> + 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"); >> + >> + int err; >> + // create abuffer filter >> + AVCodecContext *avctx = audio_st->codec; >> + AVRational time_base = audio_st->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, 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 >> + double vol = 0.90; >> + 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) >> +{ >> + AVPacket pkt_temp_; >> + memset(&pkt_temp_, 0, sizeof(pkt_temp_)); >> + AVPacket *pkt_temp = &pkt_temp_; >> + >> + *pkt_temp = *pkt; >> + >> + int len1, got_frame; >> + int new_packet = 1; >> + 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 >> + int 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 (;;) { >> + int 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[]) >> +{ >> + 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(); >> + >> + ao_sample_format fmt; >> + memset(&fmt, 0, sizeof(fmt)); >> + 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; >> + } >> + >> + AVFormatContext *ic = NULL; >> + char *filename = argv[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 (int i = 0; i < ic->nb_streams; i++) >> + ic->streams[i]->discard = AVDISCARD_ALL; >> + >> + AVCodec *decoder = NULL; >> + int 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; >> + } >> + >> + AVStream *audio_st = ic->streams[audio_stream_index]; >> + audio_st->discard = AVDISCARD_DEFAULT; >> + >> + AVCodecContext *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; >> + } >> + >> + AVPacket audio_pkt; >> + memset(&audio_pkt, 0, sizeof(audio_pkt)); >> + AVPacket *pkt = &audio_pkt; >> + AVFrame *frame = avcodec_alloc_frame(); >> + >> + oframe = av_frame_alloc(); >> + if (!oframe) { >> + av_log(NULL, AV_LOG_ERROR, "error allocating oframe\n"); >> + return 1; >> + } >> + >> + int eof = 0; >> + 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; >> + } >> + int 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.8.1.2 >> >> > _______________________________________________ libav-devel mailing list [email protected] https://lists.libav.org/mailman/listinfo/libav-devel
