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 };