| @@ -0,0 +1,366 @@ |
| | 1 | /* |
| | 2 | * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram |
| | 3 | * Copyright (c) 2011 Mina Nagy Zaki |
| | 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 | * memory buffer source for audio |
| | 25 | */ |
| | 26 | |
| | 27 | #include "libavutil/audioconvert.h" |
| | 28 | #include "libavutil/fifo.h" |
| | 29 | #include "asrc_abuffer.h" |
| | 30 | #include "internal.h" |
| | 31 | |
| | 32 | typedef struct { |
| | 33 | // Audio format of incoming buffers |
| | 34 | int sample_rate; |
| | 35 | unsigned int sample_format; |
| | 36 | int64_t channel_layout; |
| | 37 | int packing_format; |
| | 38 | |
| | 39 | // FIFO buffer of audio buffer ref pointers |
| | 40 | AVFifoBuffer *fifo; |
| | 41 | |
| | 42 | // Normalization filters |
| | 43 | AVFilterContext *aconvert; |
| | 44 | AVFilterContext *aresample; |
| | 45 | } ABufferSourceContext; |
| | 46 | |
| | 47 | #define FIFO_SIZE 8 |
| | 48 | |
| | 49 | static void buf_free(AVFilterBuffer *ptr) |
| | 50 | { |
| | 51 | av_free(ptr); |
| | 52 | return; |
| | 53 | } |
| | 54 | |
| | 55 | static void set_link_source(AVFilterContext *src, AVFilterLink *link) |
| | 56 | { |
| | 57 | link->src = src; |
| | 58 | link->srcpad = &(src->output_pads[0]); |
| | 59 | src->outputs[0] = link; |
| | 60 | } |
| | 61 | |
| | 62 | static int reconfigure_filter(ABufferSourceContext *abuffer, AVFilterContext *filt_ctx) |
| | 63 | { |
| | 64 | int ret; |
| | 65 | AVFilterLink * const inlink = filt_ctx->inputs[0]; |
| | 66 | AVFilterLink * const outlink = filt_ctx->outputs[0]; |
| | 67 | |
| | 68 | inlink->format = abuffer->sample_format; |
| | 69 | inlink->channel_layout = abuffer->channel_layout; |
| | 70 | inlink->planar = abuffer->packing_format; |
| | 71 | inlink->sample_rate = abuffer->sample_rate; |
| | 72 | |
| | 73 | filt_ctx->filter->uninit(filt_ctx); |
| | 74 | memset(filt_ctx->priv, 0, filt_ctx->filter->priv_size); |
| | 75 | if ((ret = filt_ctx->filter->init(filt_ctx, NULL , NULL)) < 0) |
| | 76 | return ret; |
| | 77 | if ((ret = inlink->srcpad->config_props(inlink)) < 0) |
| | 78 | return ret; |
| | 79 | return outlink->srcpad->config_props(outlink); |
| | 80 | } |
| | 81 | |
| | 82 | static int insert_filter(ABufferSourceContext *abuffer, |
| | 83 | AVFilterLink *link, AVFilterContext **filt_ctx, |
| | 84 | const char *filt_name) |
| | 85 | { |
| | 86 | int ret; |
| | 87 | |
| | 88 | if ((ret = avfilter_open(filt_ctx, avfilter_get_by_name(filt_name), NULL)) < 0) |
| | 89 | return ret; |
| | 90 | |
| | 91 | link->src->outputs[0] = NULL; |
| | 92 | if ((ret = avfilter_link(link->src, 0, *filt_ctx, 0)) < 0) { |
| | 93 | link->src->outputs[0] = link; |
| | 94 | return ret; |
| | 95 | } |
| | 96 | |
| | 97 | set_link_source(*filt_ctx, link); |
| | 98 | |
| | 99 | if ((ret = reconfigure_filter(abuffer, *filt_ctx)) < 0) { |
| | 100 | avfilter_free(*filt_ctx); |
| | 101 | return ret; |
| | 102 | } |
| | 103 | |
| | 104 | return 0; |
| | 105 | } |
| | 106 | |
| | 107 | static void remove_filter(AVFilterContext **filt_ctx) |
| | 108 | { |
| | 109 | AVFilterLink *outlink = (*filt_ctx)->outputs[0]; |
| | 110 | AVFilterContext *src = (*filt_ctx)->inputs[0]->src; |
| | 111 | |
| | 112 | (*filt_ctx)->outputs[0] = NULL; |
| | 113 | avfilter_free(*filt_ctx); |
| | 114 | *filt_ctx = NULL; |
| | 115 | |
| | 116 | set_link_source(src, outlink); |
| | 117 | } |
| | 118 | |
| | 119 | static inline void log_input_change(void *ctx, AVFilterLink *link, AVFilterBufferRef *ref) |
| | 120 | { |
| | 121 | char old_layout_str[16], new_layout_str[16]; |
| | 122 | av_get_channel_layout_string(old_layout_str, sizeof(old_layout_str), |
| | 123 | -1, link->channel_layout); |
| | 124 | av_get_channel_layout_string(new_layout_str, sizeof(new_layout_str), |
| | 125 | -1, ref->audio->channel_layout); |
| | 126 | av_log(ctx, AV_LOG_INFO, |
| | 127 | "Audio input format changed: " |
| | 128 | "%s:%s:%"PRId64" -> %s:%s:%u, normalizing\n", |
| | 129 | av_get_sample_fmt_name(link->format), |
| | 130 | old_layout_str, link->sample_rate, |
| | 131 | av_get_sample_fmt_name(ref->format), |
| | 132 | new_layout_str, ref->audio->sample_rate); |
| | 133 | } |
| | 134 | |
| | 135 | int av_asrc_buffer_add_audio_buffer_ref(AVFilterContext *ctx, |
| | 136 | AVFilterBufferRef *samplesref, |
| | 137 | int av_unused flags) |
| | 138 | { |
| | 139 | ABufferSourceContext *abuffer = ctx->priv; |
| | 140 | AVFilterLink *link; |
| | 141 | int ret, logged = 0; |
| | 142 | |
| | 143 | if (av_fifo_space(abuffer->fifo) < sizeof(samplesref)) { |
| | 144 | av_log(ctx, AV_LOG_ERROR, |
| | 145 | "Buffering limit reached. Please consume some available frames " |
| | 146 | "before adding new ones.\n"); |
| | 147 | return AVERROR(EINVAL); |
| | 148 | } |
| | 149 | |
| | 150 | // Normalize input |
| | 151 | |
| | 152 | link = ctx->outputs[0]; |
| | 153 | if (samplesref->audio->sample_rate != link->sample_rate) { |
| | 154 | |
| | 155 | log_input_change(ctx, link, samplesref); |
| | 156 | logged = 1; |
| | 157 | |
| | 158 | abuffer->sample_rate = samplesref->audio->sample_rate; |
| | 159 | |
| | 160 | if (!abuffer->aresample) { |
| | 161 | ret = insert_filter(abuffer, link, &abuffer->aresample, "aresample"); |
| | 162 | if (ret < 0) return ret; |
| | 163 | } else { |
| | 164 | link = abuffer->aresample->outputs[0]; |
| | 165 | if (samplesref->audio->sample_rate == link->sample_rate) |
| | 166 | remove_filter(&abuffer->aresample); |
| | 167 | else |
| | 168 | if ((ret = reconfigure_filter(abuffer, abuffer->aresample)) < 0) |
| | 169 | return ret; |
| | 170 | } |
| | 171 | } |
| | 172 | |
| | 173 | link = ctx->outputs[0]; |
| | 174 | if (samplesref->format != link->format || |
| | 175 | samplesref->audio->channel_layout != link->channel_layout || |
| | 176 | samplesref->audio->planar != link->planar) { |
| | 177 | |
| | 178 | if (!logged) log_input_change(ctx, link, samplesref); |
| | 179 | |
| | 180 | abuffer->sample_format = samplesref->format; |
| | 181 | abuffer->channel_layout = samplesref->audio->channel_layout; |
| | 182 | abuffer->packing_format = samplesref->audio->planar; |
| | 183 | |
| | 184 | if (!abuffer->aconvert) { |
| | 185 | ret = insert_filter(abuffer, link, &abuffer->aconvert, "aconvert"); |
| | 186 | if (ret < 0) return ret; |
| | 187 | } else { |
| | 188 | link = abuffer->aconvert->outputs[0]; |
| | 189 | if (samplesref->format == link->format && |
| | 190 | samplesref->audio->channel_layout == link->channel_layout && |
| | 191 | samplesref->audio->planar == link->planar |
| | 192 | ) |
| | 193 | remove_filter(&abuffer->aconvert); |
| | 194 | else |
| | 195 | if ((ret = reconfigure_filter(abuffer, abuffer->aconvert)) < 0) |
| | 196 | return ret; |
| | 197 | } |
| | 198 | } |
| | 199 | |
| | 200 | if (sizeof(samplesref) != av_fifo_generic_write(abuffer->fifo, &samplesref, |
| | 201 | sizeof(samplesref), NULL)) { |
| | 202 | av_log(ctx, AV_LOG_ERROR, "Error while writing to FIFO\n"); |
| | 203 | return AVERROR(EINVAL); |
| | 204 | } |
| | 205 | |
| | 206 | return 0; |
| | 207 | } |
| | 208 | |
| | 209 | int av_asrc_buffer_add_samples(AVFilterContext *ctx, |
| | 210 | uint8_t *data[8], int linesize[8], |
| | 211 | int nb_samples, int sample_rate, |
| | 212 | int sample_fmt, int64_t channel_layout, int planar, |
| | 213 | int64_t pts, int av_unused flags) |
| | 214 | { |
| | 215 | AVFilterBufferRef *samplesref; |
| | 216 | |
| | 217 | samplesref = avfilter_get_audio_buffer_ref_from_arrays( |
| | 218 | data, linesize, AV_PERM_WRITE, |
| | 219 | nb_samples, |
| | 220 | sample_fmt, channel_layout, planar); |
| | 221 | if (!samplesref) |
| | 222 | return AVERROR(ENOMEM); |
| | 223 | |
| | 224 | samplesref->buf->free = buf_free; |
| | 225 | samplesref->pts = pts; |
| | 226 | samplesref->audio->sample_rate = sample_rate; |
| | 227 | |
| | 228 | return av_asrc_buffer_add_audio_buffer_ref(ctx, samplesref, 0); |
| | 229 | } |
| | 230 | |
| | 231 | int av_asrc_buffer_add_buffer(AVFilterContext *ctx, |
| | 232 | uint8_t *buf, int buf_size, int sample_rate, |
| | 233 | int sample_fmt, int64_t channel_layout, int planar, |
| | 234 | int64_t pts, int av_unused flags) |
| | 235 | { |
| | 236 | uint8_t *data[8]; |
| | 237 | int linesize[8]; |
| | 238 | int nb_channels = av_get_channel_layout_nb_channels(channel_layout), |
| | 239 | nb_samples = buf_size / nb_channels / av_get_bytes_per_sample(sample_fmt); |
| | 240 | |
| | 241 | av_samples_fill_arrays(data, linesize, |
| | 242 | buf, nb_channels, nb_samples, |
| | 243 | sample_fmt, planar, 16); |
| | 244 | |
| | 245 | return av_asrc_buffer_add_samples(ctx, |
| | 246 | data, linesize, nb_samples, |
| | 247 | sample_rate, |
| | 248 | sample_fmt, channel_layout, planar, |
| | 249 | pts, flags); |
| | 250 | } |
| | 251 | |
| | 252 | static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) |
| | 253 | { |
| | 254 | ABufferSourceContext *abuffer = ctx->priv; |
| | 255 | char *arg = NULL, *ptr, chlayout_str[16]; |
| | 256 | int ret; |
| | 257 | |
| | 258 | arg = strtok_r(args, ":", &ptr); |
| | 259 | |
| | 260 | #define ADD_FORMAT(fmt_name) \ |
| | 261 | if (!arg) \ |
| | 262 | goto arg_fail; \ |
| | 263 | if ((ret = ff_parse_##fmt_name(&abuffer->fmt_name, arg, ctx)) < 0) \ |
| | 264 | return ret; \ |
| | 265 | if (*args) \ |
| | 266 | arg = strtok_r(NULL, ":", &ptr) |
| | 267 | |
| | 268 | ADD_FORMAT(sample_rate); |
| | 269 | ADD_FORMAT(sample_format); |
| | 270 | ADD_FORMAT(channel_layout); |
| | 271 | ADD_FORMAT(packing_format); |
| | 272 | |
| | 273 | abuffer->fifo = av_fifo_alloc(FIFO_SIZE*sizeof(AVFilterBufferRef*)); |
| | 274 | if (!abuffer->fifo) { |
| | 275 | av_log(ctx, AV_LOG_ERROR, "Failed to allocate fifo, filter init failed.\n"); |
| | 276 | return AVERROR(ENOMEM); |
| | 277 | } |
| | 278 | |
| | 279 | av_get_channel_layout_string(chlayout_str, sizeof(chlayout_str), |
| | 280 | -1, abuffer->channel_layout); |
| | 281 | av_log(ctx, AV_LOG_INFO, "format:%s layout:%s rate:%d\n", |
| | 282 | av_get_sample_fmt_name(abuffer->sample_format), chlayout_str, |
| | 283 | abuffer->sample_rate); |
| | 284 | |
| | 285 | return 0; |
| | 286 | |
| | 287 | arg_fail: |
| | 288 | av_log(ctx, AV_LOG_ERROR, "Invalid arguments, must be of the form " |
| | 289 | "sample_rate:sample_fmt:channel_layout:packing\n"); |
| | 290 | return AVERROR(EINVAL); |
| | 291 | } |
| | 292 | |
| | 293 | static av_cold void uninit(AVFilterContext *ctx) |
| | 294 | { |
| | 295 | ABufferSourceContext *abuffer = ctx->priv; |
| | 296 | av_fifo_free(abuffer->fifo); |
| | 297 | } |
| | 298 | |
| | 299 | static int query_formats(AVFilterContext *ctx) |
| | 300 | { |
| | 301 | ABufferSourceContext *abuffer = ctx->priv; |
| | 302 | AVFilterFormats *formats; |
| | 303 | |
| | 304 | formats = NULL; |
| | 305 | avfilter_add_format(&formats, abuffer->sample_format); |
| | 306 | avfilter_set_common_sample_formats(ctx, formats); |
| | 307 | |
| | 308 | formats = NULL; |
| | 309 | avfilter_add_format(&formats, abuffer->channel_layout); |
| | 310 | avfilter_set_common_channel_layouts(ctx, formats); |
| | 311 | |
| | 312 | formats = NULL; |
| | 313 | avfilter_add_format(&formats, abuffer->packing_format); |
| | 314 | avfilter_set_common_packing_formats(ctx, formats); |
| | 315 | |
| | 316 | return 0; |
| | 317 | } |
| | 318 | |
| | 319 | static int config_output(AVFilterLink *outlink) |
| | 320 | { |
| | 321 | ABufferSourceContext *abuffer = outlink->src->priv; |
| | 322 | outlink->sample_rate = abuffer->sample_rate; |
| | 323 | return 0; |
| | 324 | } |
| | 325 | |
| | 326 | static int request_frame(AVFilterLink *outlink) |
| | 327 | { |
| | 328 | ABufferSourceContext *abuffer = outlink->src->priv; |
| | 329 | AVFilterBufferRef *samplesref; |
| | 330 | |
| | 331 | if (!av_fifo_size(abuffer->fifo)) { |
| | 332 | av_log(outlink->src, AV_LOG_ERROR, |
| | 333 | "request_frame() called with no available frames!\n"); |
| | 334 | return AVERROR(EINVAL); |
| | 335 | } |
| | 336 | |
| | 337 | av_fifo_generic_read(abuffer->fifo, &samplesref, sizeof(samplesref), NULL); |
| | 338 | avfilter_filter_samples(outlink, avfilter_ref_buffer(samplesref, ~0)); |
| | 339 | avfilter_unref_buffer(samplesref); |
| | 340 | |
| | 341 | return 0; |
| | 342 | } |
| | 343 | |
| | 344 | static int poll_frame(AVFilterLink *outlink) |
| | 345 | { |
| | 346 | ABufferSourceContext *abuffer = outlink->src->priv; |
| | 347 | return av_fifo_size(abuffer->fifo)/sizeof(AVFilterBufferRef*); |
| | 348 | } |
| | 349 | |
| | 350 | AVFilter avfilter_asrc_abuffer = { |
| | 351 | .name = "abuffer", |
| | 352 | .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."), |
| | 353 | .priv_size = sizeof(ABufferSourceContext), |
| | 354 | .query_formats = query_formats, |
| | 355 | |
| | 356 | .init = init, |
| | 357 | .uninit = uninit, |
| | 358 | |
| | 359 | .inputs = (AVFilterPad[]) {{ .name = NULL }}, |
| | 360 | .outputs = (AVFilterPad[]) {{ .name = "default", |
| | 361 | .type = AVMEDIA_TYPE_AUDIO, |
| | 362 | .request_frame = request_frame, |
| | 363 | .poll_frame = poll_frame, |
| | 364 | .config_props = config_output, }, |
| | 365 | { .name = NULL}}, |
| | 366 | }; |