1/* 2 * Copyright (c) 2012 Andrey Utkin 3 * Copyright (c) 2012 Stefano Sabatini 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * Filter that changes number of samples on single output operation 25 */ 26 27#include "libavutil/audio_fifo.h" 28#include "libavutil/avassert.h" 29#include "libavutil/channel_layout.h" 30#include "libavutil/opt.h" 31#include "avfilter.h" 32#include "audio.h" 33#include "internal.h" 34#include "formats.h" 35 36typedef struct { 37 const AVClass *class; 38 int nb_out_samples; ///< how many samples to output 39 AVAudioFifo *fifo; ///< samples are queued here 40 int64_t next_out_pts; 41 int pad; 42} ASNSContext; 43 44#define OFFSET(x) offsetof(ASNSContext, x) 45#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM 46 47static const AVOption asetnsamples_options[] = { 48 { "nb_out_samples", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS }, 49 { "n", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS }, 50 { "pad", "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS }, 51 { "p", "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS }, 52 { NULL } 53}; 54 55AVFILTER_DEFINE_CLASS(asetnsamples); 56 57static av_cold int init(AVFilterContext *ctx) 58{ 59 ASNSContext *asns = ctx->priv; 60 61 asns->next_out_pts = AV_NOPTS_VALUE; 62 av_log(ctx, AV_LOG_VERBOSE, "nb_out_samples:%d pad:%d\n", asns->nb_out_samples, asns->pad); 63 64 return 0; 65} 66 67static av_cold void uninit(AVFilterContext *ctx) 68{ 69 ASNSContext *asns = ctx->priv; 70 av_audio_fifo_free(asns->fifo); 71} 72 73static int config_props_output(AVFilterLink *outlink) 74{ 75 ASNSContext *asns = outlink->src->priv; 76 77 asns->fifo = av_audio_fifo_alloc(outlink->format, outlink->channels, asns->nb_out_samples); 78 if (!asns->fifo) 79 return AVERROR(ENOMEM); 80 outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP; 81 82 return 0; 83} 84 85static int push_samples(AVFilterLink *outlink) 86{ 87 ASNSContext *asns = outlink->src->priv; 88 AVFrame *outsamples = NULL; 89 int ret, nb_out_samples, nb_pad_samples; 90 91 if (asns->pad) { 92 nb_out_samples = av_audio_fifo_size(asns->fifo) ? asns->nb_out_samples : 0; 93 nb_pad_samples = nb_out_samples - FFMIN(nb_out_samples, av_audio_fifo_size(asns->fifo)); 94 } else { 95 nb_out_samples = FFMIN(asns->nb_out_samples, av_audio_fifo_size(asns->fifo)); 96 nb_pad_samples = 0; 97 } 98 99 if (!nb_out_samples) 100 return 0; 101 102 outsamples = ff_get_audio_buffer(outlink, nb_out_samples); 103 if (!outsamples) 104 return AVERROR(ENOMEM); 105 106 av_audio_fifo_read(asns->fifo, 107 (void **)outsamples->extended_data, nb_out_samples); 108 109 if (nb_pad_samples) 110 av_samples_set_silence(outsamples->extended_data, nb_out_samples - nb_pad_samples, 111 nb_pad_samples, outlink->channels, 112 outlink->format); 113 outsamples->nb_samples = nb_out_samples; 114 outsamples->channel_layout = outlink->channel_layout; 115 outsamples->sample_rate = outlink->sample_rate; 116 outsamples->pts = asns->next_out_pts; 117 118 if (asns->next_out_pts != AV_NOPTS_VALUE) 119 asns->next_out_pts += av_rescale_q(nb_out_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); 120 121 ret = ff_filter_frame(outlink, outsamples); 122 if (ret < 0) 123 return ret; 124 return nb_out_samples; 125} 126 127static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) 128{ 129 AVFilterContext *ctx = inlink->dst; 130 ASNSContext *asns = ctx->priv; 131 AVFilterLink *outlink = ctx->outputs[0]; 132 int ret; 133 int nb_samples = insamples->nb_samples; 134 135 if (av_audio_fifo_space(asns->fifo) < nb_samples) { 136 av_log(ctx, AV_LOG_DEBUG, "No space for %d samples, stretching audio fifo\n", nb_samples); 137 ret = av_audio_fifo_realloc(asns->fifo, av_audio_fifo_size(asns->fifo) + nb_samples); 138 if (ret < 0) { 139 av_log(ctx, AV_LOG_ERROR, 140 "Stretching audio fifo failed, discarded %d samples\n", nb_samples); 141 return -1; 142 } 143 } 144 av_audio_fifo_write(asns->fifo, (void **)insamples->extended_data, nb_samples); 145 if (asns->next_out_pts == AV_NOPTS_VALUE) 146 asns->next_out_pts = insamples->pts; 147 av_frame_free(&insamples); 148 149 while (av_audio_fifo_size(asns->fifo) >= asns->nb_out_samples) 150 push_samples(outlink); 151 return 0; 152} 153 154static int request_frame(AVFilterLink *outlink) 155{ 156 AVFilterLink *inlink = outlink->src->inputs[0]; 157 int ret; 158 159 ret = ff_request_frame(inlink); 160 if (ret == AVERROR_EOF) { 161 ret = push_samples(outlink); 162 return ret < 0 ? ret : ret > 0 ? 0 : AVERROR_EOF; 163 } 164 165 return ret; 166} 167 168static const AVFilterPad asetnsamples_inputs[] = { 169 { 170 .name = "default", 171 .type = AVMEDIA_TYPE_AUDIO, 172 .filter_frame = filter_frame, 173 }, 174 { NULL } 175}; 176 177static const AVFilterPad asetnsamples_outputs[] = { 178 { 179 .name = "default", 180 .type = AVMEDIA_TYPE_AUDIO, 181 .request_frame = request_frame, 182 .config_props = config_props_output, 183 }, 184 { NULL } 185}; 186 187AVFilter ff_af_asetnsamples = { 188 .name = "asetnsamples", 189 .description = NULL_IF_CONFIG_SMALL("Set the number of samples for each output audio frames."), 190 .priv_size = sizeof(ASNSContext), 191 .priv_class = &asetnsamples_class, 192 .init = init, 193 .uninit = uninit, 194 .inputs = asetnsamples_inputs, 195 .outputs = asetnsamples_outputs, 196}; 197