| @@ -0,0 +1,288 @@ |
| | 1 | /* |
| | 2 | * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org> |
| | 3 | * |
| | 4 | * This file is part of FFmpeg. |
| | 5 | * |
| | 6 | * FFmpeg is free software; you can redistribute it and/or |
| | 7 | * modify it under the terms of the GNU Lesser General Public |
| | 8 | * License as published by the Free Software Foundation; either |
| | 9 | * version 2.1 of the License, or (at your option) any later version. |
| | 10 | * |
| | 11 | * FFmpeg is distributed in the hope that it will be useful, |
| | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| | 14 | * GNU General Public License for more details. |
| | 15 | * |
| | 16 | * You should have received a copy of the GNU Lesser General Public |
| | 17 | * License along with FFmpeg; if not, write to the Free Software |
| | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| | 19 | */ |
| | 20 | |
| | 21 | /** |
| | 22 | * @file |
| | 23 | * Audio merging filter |
| | 24 | */ |
| | 25 | |
| | 26 | #include "libswresample/swresample.h" // only for SWR_CH_MAX |
| | 27 | #include "avfilter.h" |
| | 28 | #include "internal.h" |
| | 29 | |
| | 30 | #define QUEUE_SIZE 16 |
| | 31 | |
| | 32 | typedef struct { |
| | 33 | int nb_in_ch[2]; /**< number of channels for each input */ |
| | 34 | int route[SWR_CH_MAX]; /**< channels routing, see copy_samples */ |
| | 35 | int bps; |
| | 36 | struct amerge_queue { |
| | 37 | AVFilterBufferRef *buf[QUEUE_SIZE]; |
| | 38 | int nb_buf, nb_samples, pos; |
| | 39 | } queue[2]; |
| | 40 | } AMergeContext; |
| | 41 | |
| | 42 | static av_cold void uninit(AVFilterContext *ctx) |
| | 43 | { |
| | 44 | AMergeContext *am = ctx->priv; |
| | 45 | int i, j; |
| | 46 | |
| | 47 | for (i = 0; i < 2; i++) |
| | 48 | for (j = 0; j < am->queue[i].nb_buf; j++) |
| | 49 | avfilter_unref_buffer(am->queue[i].buf[j]); |
| | 50 | } |
| | 51 | |
| | 52 | static int query_formats(AVFilterContext *ctx) |
| | 53 | { |
| | 54 | AMergeContext *am = ctx->priv; |
| | 55 | int64_t inlayout[2], outlayout; |
| | 56 | const int packing_fmts[] = { AVFILTER_PACKED, -1 }; |
| | 57 | AVFilterFormats *formats; |
| | 58 | int i; |
| | 59 | |
| | 60 | for (i = 0; i < 2; i++) { |
| | 61 | if (!ctx->inputs[i]->in_chlayouts || |
| | 62 | !ctx->inputs[i]->in_chlayouts->format_count) { |
| | 63 | av_log(ctx, AV_LOG_ERROR, |
| | 64 | "No channel layout for input %d\n", i + 1); |
| | 65 | return AVERROR(EINVAL); |
| | 66 | } |
| | 67 | inlayout[i] = ctx->inputs[i]->in_chlayouts->formats[0]; |
| | 68 | if (ctx->inputs[i]->in_chlayouts->format_count > 1) { |
| | 69 | char buf[256]; |
| | 70 | av_get_channel_layout_string(buf, sizeof(buf), 0, inlayout[i]); |
| | 71 | av_log(ctx, AV_LOG_INFO, "Using \"%s\" for input %d\n", buf, i + 1); |
| | 72 | } |
| | 73 | am->nb_in_ch[i] = av_get_channel_layout_nb_channels(inlayout[i]); |
| | 74 | } |
| | 75 | if (am->nb_in_ch[0] + am->nb_in_ch[1] > SWR_CH_MAX) { |
| | 76 | av_log(ctx, AV_LOG_ERROR, "Too many channels (max %d)\n", SWR_CH_MAX); |
| | 77 | return AVERROR(EINVAL); |
| | 78 | } |
| | 79 | if (inlayout[0] & inlayout[1]) { |
| | 80 | av_log(ctx, AV_LOG_WARNING, |
| | 81 | "Inputs overlap: output layout will be meaningless\n"); |
| | 82 | for (i = 0; i < am->nb_in_ch[0] + am->nb_in_ch[1]; i++) |
| | 83 | am->route[i] = i; |
| | 84 | outlayout = av_get_default_channel_layout(am->nb_in_ch[0] + |
| | 85 | am->nb_in_ch[1]); |
| | 86 | if (!outlayout) |
| | 87 | outlayout = ((int64_t)1 << (am->nb_in_ch[0] + am->nb_in_ch[1])) - 1; |
| | 88 | } else { |
| | 89 | int *route[2] = { am->route, am->route + am->nb_in_ch[0] }; |
| | 90 | int c, out_ch_number = 0; |
| | 91 | |
| | 92 | outlayout = inlayout[0] | inlayout[1]; |
| | 93 | for (c = 0; c < 64; c++) |
| | 94 | for (i = 0; i < 2; i++) |
| | 95 | if ((inlayout[i] >> c) & 1) |
| | 96 | *(route[i]++) = out_ch_number++; |
| | 97 | } |
| | 98 | formats = avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO); |
| | 99 | avfilter_set_common_sample_formats(ctx, formats); |
| | 100 | formats = avfilter_make_format_list(packing_fmts); |
| | 101 | avfilter_set_common_packing_formats(ctx, formats); |
| | 102 | for (i = 0; i < 2; i++) { |
| | 103 | formats = NULL; |
| | 104 | avfilter_add_format(&formats, inlayout[i]); |
| | 105 | avfilter_formats_ref(formats, &ctx->inputs[i]->out_chlayouts); |
| | 106 | } |
| | 107 | formats = NULL; |
| | 108 | avfilter_add_format(&formats, outlayout); |
| | 109 | avfilter_formats_ref(formats, &ctx->outputs[0]->in_chlayouts); |
| | 110 | return 0; |
| | 111 | } |
| | 112 | |
| | 113 | static int config_output(AVFilterLink *outlink) |
| | 114 | { |
| | 115 | AVFilterContext *ctx = outlink->src; |
| | 116 | AMergeContext *am = ctx->priv; |
| | 117 | int64_t layout; |
| | 118 | char name[3][256]; |
| | 119 | int i; |
| | 120 | |
| | 121 | if (ctx->inputs[0]->sample_rate != ctx->inputs[1]->sample_rate) { |
| | 122 | av_log(ctx, AV_LOG_ERROR, |
| | 123 | "Inputs must have the same sample rate " |
| | 124 | "(%"PRIi64" vs %"PRIi64")\n", |
| | 125 | ctx->inputs[0]->sample_rate, ctx->inputs[1]->sample_rate); |
| | 126 | return AVERROR(EINVAL); |
| | 127 | } |
| | 128 | am->bps = av_get_bytes_per_sample(ctx->outputs[0]->format); |
| | 129 | outlink->sample_rate = ctx->inputs[0]->sample_rate; |
| | 130 | outlink->time_base = ctx->inputs[0]->time_base; |
| | 131 | for (i = 0; i < 3; i++) { |
| | 132 | layout = (i < 2 ? ctx->inputs[i] : ctx->outputs[0])->channel_layout; |
| | 133 | av_get_channel_layout_string(name[i], sizeof(name[i]), -1, layout); |
| | 134 | } |
| | 135 | av_log(ctx, AV_LOG_INFO, |
| | 136 | "in1:%s + in2:%s -> out:%s\n", name[0], name[1], name[2]); |
| | 137 | return 0; |
| | 138 | } |
| | 139 | |
| | 140 | static int request_frame(AVFilterLink *outlink) |
| | 141 | { |
| | 142 | AVFilterContext *ctx = outlink->src; |
| | 143 | AMergeContext *am = ctx->priv; |
| | 144 | int i; |
| | 145 | |
| | 146 | for (i = 0; i < 2; i++) |
| | 147 | if (!am->queue[i].nb_samples) |
| | 148 | avfilter_request_frame(ctx->inputs[i]); |
| | 149 | return 0; |
| | 150 | } |
| | 151 | |
| | 152 | /** |
| | 153 | * Copy samples from two input streams to one output stream. |
| | 154 | * @param nb_in_ch number of channels in each input stream |
| | 155 | * @param route routing values; |
| | 156 | * input channel i goes to output channel route[i]; |
| | 157 | * i < nb_in_ch[0] are the channels from the first output; |
| | 158 | * i >= nb_in_ch[0] are the channels from the second output |
| | 159 | * @param ins pointer to the samples of each inputs, in packed format; |
| | 160 | * will be left at the end of the copied samples |
| | 161 | * @param outs pointer to the samples of the output, in packet format; |
| | 162 | * must point to a buffer big enough; |
| | 163 | * will be left at the end of the copied samples |
| | 164 | * @param ns number of samples to copy |
| | 165 | * @param bps bytes per sample |
| | 166 | */ |
| | 167 | static inline void copy_samples(int nb_in_ch[2], int *route, uint8_t *ins[2], |
| | 168 | uint8_t **outs, int ns, int bps) |
| | 169 | { |
| | 170 | int *route_cur; |
| | 171 | int i, c; |
| | 172 | |
| | 173 | while (ns--) { |
| | 174 | route_cur = route; |
| | 175 | for (i = 0; i < 2; i++) { |
| | 176 | for (c = 0; c < nb_in_ch[i]; c++) { |
| | 177 | memcpy((*outs) + bps * *(route_cur++), ins[i], bps); |
| | 178 | ins[i] += bps; |
| | 179 | } |
| | 180 | } |
| | 181 | *outs += (nb_in_ch[0] + nb_in_ch[1]) * bps; |
| | 182 | } |
| | 183 | } |
| | 184 | |
| | 185 | static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples) |
| | 186 | { |
| | 187 | AVFilterContext *ctx = inlink->dst; |
| | 188 | AMergeContext *am = ctx->priv; |
| | 189 | int input_number = inlink == ctx->inputs[1]; |
| | 190 | struct amerge_queue *inq = &am->queue[input_number]; |
| | 191 | int nb_samples, ns, i; |
| | 192 | AVFilterBufferRef *outbuf, **inbuf[2]; |
| | 193 | uint8_t *ins[2], *outs; |
| | 194 | |
| | 195 | if (inq->nb_buf == QUEUE_SIZE) { |
| | 196 | av_log(ctx, AV_LOG_ERROR, "Packet queue overflow; dropped\n"); |
| | 197 | avfilter_unref_buffer(insamples); |
| | 198 | return; |
| | 199 | } |
| | 200 | inq->buf[inq->nb_buf++] = avfilter_ref_buffer(insamples, AV_PERM_READ | |
| | 201 | AV_PERM_PRESERVE); |
| | 202 | inq->nb_samples += insamples->audio->nb_samples; |
| | 203 | avfilter_unref_buffer(insamples); |
| | 204 | if (!am->queue[!input_number].nb_samples) |
| | 205 | return; |
| | 206 | |
| | 207 | nb_samples = FFMIN(am->queue[0].nb_samples, |
| | 208 | am->queue[1].nb_samples); |
| | 209 | outbuf = avfilter_get_audio_buffer(ctx->outputs[0], AV_PERM_WRITE, |
| | 210 | nb_samples); |
| | 211 | outs = outbuf->data[0]; |
| | 212 | for (i = 0; i < 2; i++) { |
| | 213 | inbuf[i] = am->queue[i].buf; |
| | 214 | ins[i] = (*inbuf[i])->data[0] + |
| | 215 | am->queue[i].pos * am->nb_in_ch[i] * am->bps; |
| | 216 | } |
| | 217 | while (nb_samples) { |
| | 218 | ns = nb_samples; |
| | 219 | for (i = 0; i < 2; i++) |
| | 220 | ns = FFMIN(ns, (*inbuf[i])->audio->nb_samples - am->queue[i].pos); |
| | 221 | /* Unroll the most common sample formats: speed +~350% for the loop, |
| | 222 | +~13% overall (including two common decoders) */ |
| | 223 | switch (am->bps) { |
| | 224 | case 1: |
| | 225 | copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, 1); |
| | 226 | break; |
| | 227 | case 2: |
| | 228 | copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, 2); |
| | 229 | break; |
| | 230 | case 4: |
| | 231 | copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, 4); |
| | 232 | break; |
| | 233 | default: |
| | 234 | copy_samples(am->nb_in_ch, am->route, ins, &outs, ns, am->bps); |
| | 235 | break; |
| | 236 | } |
| | 237 | |
| | 238 | nb_samples -= ns; |
| | 239 | for (i = 0; i < 2; i++) { |
| | 240 | am->queue[i].nb_samples -= ns; |
| | 241 | am->queue[i].pos += ns; |
| | 242 | if (am->queue[i].pos == (*inbuf[i])->audio->nb_samples) { |
| | 243 | am->queue[i].pos = 0; |
| | 244 | avfilter_unref_buffer(*inbuf[i]); |
| | 245 | *inbuf[i] = NULL; |
| | 246 | inbuf[i]++; |
| | 247 | ins[i] = *inbuf[i] ? (*inbuf[i])->data[0] : NULL; |
| | 248 | } |
| | 249 | } |
| | 250 | } |
| | 251 | for (i = 0; i < 2; i++) { |
| | 252 | int nbufused = inbuf[i] - am->queue[i].buf; |
| | 253 | if (nbufused) { |
| | 254 | am->queue[i].nb_buf -= nbufused; |
| | 255 | memmove(am->queue[i].buf, inbuf[i], |
| | 256 | am->queue[i].nb_buf * sizeof(**inbuf)); |
| | 257 | } |
| | 258 | } |
| | 259 | avfilter_filter_samples(ctx->outputs[0], outbuf); |
| | 260 | } |
| | 261 | |
| | 262 | AVFilter avfilter_af_amerge = { |
| | 263 | .name = "amerge", |
| | 264 | .description = NULL_IF_CONFIG_SMALL("Merge two audio streams into " |
| | 265 | "a single multi-channel stream."), |
| | 266 | .priv_size = sizeof(AMergeContext), |
| | 267 | .uninit = uninit, |
| | 268 | .query_formats = query_formats, |
| | 269 | |
| | 270 | .inputs = (const AVFilterPad[]) { |
| | 271 | { .name = "in1", |
| | 272 | .type = AVMEDIA_TYPE_AUDIO, |
| | 273 | .filter_samples = filter_samples, |
| | 274 | .min_perms = AV_PERM_READ, }, |
| | 275 | { .name = "in2", |
| | 276 | .type = AVMEDIA_TYPE_AUDIO, |
| | 277 | .filter_samples = filter_samples, |
| | 278 | .min_perms = AV_PERM_READ, }, |
| | 279 | { .name = NULL } |
| | 280 | }, |
| | 281 | .outputs = (const AVFilterPad[]) { |
| | 282 | { .name = "default", |
| | 283 | .type = AVMEDIA_TYPE_AUDIO, |
| | 284 | .config_props = config_output, |
| | 285 | .request_frame = request_frame, }, |
| | 286 | { .name = NULL } |
| | 287 | }, |
| | 288 | }; |