libavfilter/af_astreamsync.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Nicolas George <nicolas.george@normalesup.org>
00003  *
00004  * This file is part of FFmpeg.
00005  *
00006  * FFmpeg is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * FFmpeg is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with FFmpeg; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019  */
00020 
00026 #include "libavutil/eval.h"
00027 #include "avfilter.h"
00028 #include "internal.h"
00029 
00030 #define QUEUE_SIZE 16
00031 
00032 static const char * const var_names[] = {
00033     "b1", "b2",
00034     "s1", "s2",
00035     "t1", "t2",
00036     NULL
00037 };
00038 
00039 enum var_name {
00040     VAR_B1, VAR_B2,
00041     VAR_S1, VAR_S2,
00042     VAR_T1, VAR_T2,
00043     VAR_NB
00044 };
00045 
00046 typedef struct {
00047     AVExpr *expr;
00048     double var_values[VAR_NB];
00049     struct buf_queue {
00050         AVFilterBufferRef *buf[QUEUE_SIZE];
00051         unsigned tail, nb;
00052         /* buf[tail] is the oldest,
00053            buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
00054     } queue[2];
00055     int req[2];
00056     int next_out;
00057     int eof; /* bitmask, one bit for each stream */
00058 } AStreamSyncContext;
00059 
00060 static const char *default_expr = "t1-t2";
00061 
00062 static av_cold int init(AVFilterContext *ctx, const char *args0, void *opaque)
00063 {
00064     AStreamSyncContext *as = ctx->priv;
00065     const char *expr = args0 ? args0 : default_expr;
00066     int r, i;
00067 
00068     r = av_expr_parse(&as->expr, expr, var_names,
00069                       NULL, NULL, NULL, NULL, 0, ctx);
00070     if (r < 0) {
00071         av_log(ctx, AV_LOG_ERROR, "Error in expression \"%s\"\n", expr);
00072         return r;
00073     }
00074     for (i = 0; i < 42; i++)
00075         av_expr_eval(as->expr, as->var_values, NULL); /* exercize prng */
00076     return 0;
00077 }
00078 
00079 static int query_formats(AVFilterContext *ctx)
00080 {
00081     int i;
00082     AVFilterFormats *formats;
00083 
00084     for (i = 0; i < 2; i++) {
00085         formats = ctx->inputs[i]->in_formats;
00086         avfilter_formats_ref(formats, &ctx->inputs[i]->out_formats);
00087         avfilter_formats_ref(formats, &ctx->outputs[i]->in_formats);
00088         formats = ctx->inputs[i]->in_packing;
00089         avfilter_formats_ref(formats, &ctx->inputs[i]->out_packing);
00090         avfilter_formats_ref(formats, &ctx->outputs[i]->in_packing);
00091         formats = ctx->inputs[i]->in_chlayouts;
00092         avfilter_formats_ref(formats, &ctx->inputs[i]->out_chlayouts);
00093         avfilter_formats_ref(formats, &ctx->outputs[i]->in_chlayouts);
00094     }
00095     return 0;
00096 }
00097 
00098 static int config_output(AVFilterLink *outlink)
00099 {
00100     AVFilterContext *ctx = outlink->src;
00101     int id = outlink == ctx->outputs[1];
00102 
00103     outlink->sample_rate = ctx->inputs[id]->sample_rate;
00104     outlink->time_base   = ctx->inputs[id]->time_base;
00105     return 0;
00106 }
00107 
00108 static void send_out(AVFilterContext *ctx, int out_id)
00109 {
00110     AStreamSyncContext *as = ctx->priv;
00111     struct buf_queue *queue = &as->queue[out_id];
00112     AVFilterBufferRef *buf = queue->buf[queue->tail];
00113 
00114     queue->buf[queue->tail] = NULL;
00115     as->var_values[VAR_B1 + out_id]++;
00116     as->var_values[VAR_S1 + out_id] += buf->audio->nb_samples;
00117     if (buf->pts != AV_NOPTS_VALUE)
00118         as->var_values[VAR_T1 + out_id] =
00119             av_q2d(ctx->outputs[out_id]->time_base) * buf->pts;
00120     as->var_values[VAR_T1 + out_id] += buf->audio->nb_samples /
00121                                    (double)ctx->inputs[out_id]->sample_rate;
00122     avfilter_filter_samples(ctx->outputs[out_id], buf);
00123     queue->nb--;
00124     queue->tail = (queue->tail + 1) % QUEUE_SIZE;
00125     if (as->req[out_id])
00126         as->req[out_id]--;
00127 }
00128 
00129 static void send_next(AVFilterContext *ctx)
00130 {
00131     AStreamSyncContext *as = ctx->priv;
00132     int i;
00133 
00134     while (1) {
00135         if (!as->queue[as->next_out].nb)
00136             break;
00137         send_out(ctx, as->next_out);
00138         if (!as->eof)
00139             as->next_out = av_expr_eval(as->expr, as->var_values, NULL) >= 0;
00140     }
00141     for (i = 0; i < 2; i++)
00142         if (as->queue[i].nb == QUEUE_SIZE)
00143             send_out(ctx, i);
00144 }
00145 
00146 static int request_frame(AVFilterLink *outlink)
00147 {
00148     AVFilterContext *ctx = outlink->src;
00149     AStreamSyncContext *as = ctx->priv;
00150     int id = outlink == ctx->outputs[1];
00151 
00152     as->req[id]++;
00153     while (as->req[id] && !(as->eof & (1 << id))) {
00154         if (as->queue[as->next_out].nb) {
00155             send_next(ctx);
00156         } else {
00157             as->eof |= 1 << as->next_out;
00158             avfilter_request_frame(ctx->inputs[as->next_out]);
00159             if (as->eof & (1 << as->next_out))
00160                 as->next_out = !as->next_out;
00161         }
00162     }
00163     return 0;
00164 }
00165 
00166 static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples)
00167 {
00168     AVFilterContext *ctx = inlink->dst;
00169     AStreamSyncContext *as = ctx->priv;
00170     int id = inlink == ctx->inputs[1];
00171 
00172     as->queue[id].buf[(as->queue[id].tail + as->queue[id].nb++) % QUEUE_SIZE] =
00173         insamples;
00174     as->eof &= ~(1 << id);
00175     send_next(ctx);
00176 }
00177 
00178 AVFilter avfilter_af_astreamsync = {
00179     .name          = "astreamsync",
00180     .description   = NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
00181                                           "in a configurable order."),
00182     .priv_size     = sizeof(AStreamSyncContext),
00183     .init          = init,
00184     .query_formats = query_formats,
00185 
00186     .inputs    = (const AVFilterPad[]) {
00187         { .name             = "in1",
00188           .type             = AVMEDIA_TYPE_AUDIO,
00189           .filter_samples   = filter_samples,
00190           .min_perms        = AV_PERM_READ, },
00191         { .name             = "in2",
00192           .type             = AVMEDIA_TYPE_AUDIO,
00193           .filter_samples   = filter_samples,
00194           .min_perms        = AV_PERM_READ, },
00195         { .name = NULL }
00196     },
00197     .outputs   = (const AVFilterPad[]) {
00198         { .name             = "out1",
00199           .type             = AVMEDIA_TYPE_AUDIO,
00200           .config_props     = config_output,
00201           .request_frame    = request_frame, },
00202         { .name             = "out2",
00203           .type             = AVMEDIA_TYPE_AUDIO,
00204           .config_props     = config_output,
00205           .request_frame    = request_frame, },
00206         { .name = NULL }
00207     },
00208 };