FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
af_astreamsync.c
Go to the documentation of this file.
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 Lesser 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  * Stream (de)synchronization filter
24  */
25 
26 #include "libavutil/eval.h"
27 #include "libavutil/opt.h"
28 #include "avfilter.h"
29 #include "audio.h"
30 #include "internal.h"
31 
32 #define QUEUE_SIZE 16
33 
34 static const char * const var_names[] = {
35  "b1", "b2",
36  "s1", "s2",
37  "t1", "t2",
38  NULL
39 };
40 
41 enum var_name {
46 };
47 
48 typedef struct {
49  const AVClass *class;
51  char *expr_str;
52  double var_values[VAR_NB];
53  struct buf_queue {
55  unsigned tail, nb;
56  /* buf[tail] is the oldest,
57  buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
58  } queue[2];
59  int req[2];
60  int next_out;
61  int eof; /* bitmask, one bit for each stream */
63 
64 #define OFFSET(x) offsetof(AStreamSyncContext, x)
65 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
66 static const AVOption astreamsync_options[] = {
67  { "expr", "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
68  { "e", "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
69  { NULL }
70 };
71 
72 AVFILTER_DEFINE_CLASS(astreamsync);
73 
74 static av_cold int init(AVFilterContext *ctx)
75 {
76  AStreamSyncContext *as = ctx->priv;
77  int r, i;
78 
79  r = av_expr_parse(&as->expr, as->expr_str, var_names,
80  NULL, NULL, NULL, NULL, 0, ctx);
81  if (r < 0) {
82  av_log(ctx, AV_LOG_ERROR, "Error in expression \"%s\"\n", as->expr_str);
83  return r;
84  }
85  for (i = 0; i < 42; i++)
86  av_expr_eval(as->expr, as->var_values, NULL); /* exercize prng */
87  return 0;
88 }
89 
91 {
92  int i;
95 
96  for (i = 0; i < 2; i++) {
97  formats = ctx->inputs[i]->in_formats;
98  ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
99  ff_formats_ref(formats, &ctx->outputs[i]->in_formats);
100  rates = ff_all_samplerates();
101  ff_formats_ref(rates, &ctx->inputs[i]->out_samplerates);
102  ff_formats_ref(rates, &ctx->outputs[i]->in_samplerates);
103  layouts = ctx->inputs[i]->in_channel_layouts;
106  }
107  return 0;
108 }
109 
110 static int config_output(AVFilterLink *outlink)
111 {
112  AVFilterContext *ctx = outlink->src;
113  int id = outlink == ctx->outputs[1];
114 
115  outlink->sample_rate = ctx->inputs[id]->sample_rate;
116  outlink->time_base = ctx->inputs[id]->time_base;
117  return 0;
118 }
119 
120 static int send_out(AVFilterContext *ctx, int out_id)
121 {
122  AStreamSyncContext *as = ctx->priv;
123  struct buf_queue *queue = &as->queue[out_id];
124  AVFrame *buf = queue->buf[queue->tail];
125  int ret;
126 
127  queue->buf[queue->tail] = NULL;
128  as->var_values[VAR_B1 + out_id]++;
129  as->var_values[VAR_S1 + out_id] += buf->nb_samples;
130  if (buf->pts != AV_NOPTS_VALUE)
131  as->var_values[VAR_T1 + out_id] =
132  av_q2d(ctx->outputs[out_id]->time_base) * buf->pts;
133  as->var_values[VAR_T1 + out_id] += buf->nb_samples /
134  (double)ctx->inputs[out_id]->sample_rate;
135  ret = ff_filter_frame(ctx->outputs[out_id], buf);
136  queue->nb--;
137  queue->tail = (queue->tail + 1) % QUEUE_SIZE;
138  if (as->req[out_id])
139  as->req[out_id]--;
140  return ret;
141 }
142 
143 static void send_next(AVFilterContext *ctx)
144 {
145  AStreamSyncContext *as = ctx->priv;
146  int i;
147 
148  while (1) {
149  if (!as->queue[as->next_out].nb)
150  break;
151  send_out(ctx, as->next_out);
152  if (!as->eof)
153  as->next_out = av_expr_eval(as->expr, as->var_values, NULL) >= 0;
154  }
155  for (i = 0; i < 2; i++)
156  if (as->queue[i].nb == QUEUE_SIZE)
157  send_out(ctx, i);
158 }
159 
160 static int request_frame(AVFilterLink *outlink)
161 {
162  AVFilterContext *ctx = outlink->src;
163  AStreamSyncContext *as = ctx->priv;
164  int id = outlink == ctx->outputs[1];
165 
166  as->req[id]++;
167  while (as->req[id] && !(as->eof & (1 << id))) {
168  if (as->queue[as->next_out].nb) {
169  send_next(ctx);
170  } else {
171  as->eof |= 1 << as->next_out;
172  ff_request_frame(ctx->inputs[as->next_out]);
173  if (as->eof & (1 << as->next_out))
174  as->next_out = !as->next_out;
175  }
176  }
177  return 0;
178 }
179 
180 static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
181 {
182  AVFilterContext *ctx = inlink->dst;
183  AStreamSyncContext *as = ctx->priv;
184  int id = inlink == ctx->inputs[1];
185 
186  as->queue[id].buf[(as->queue[id].tail + as->queue[id].nb++) % QUEUE_SIZE] =
187  insamples;
188  as->eof &= ~(1 << id);
189  send_next(ctx);
190  return 0;
191 }
192 
193 static av_cold void uninit(AVFilterContext *ctx)
194 {
195  AStreamSyncContext *as = ctx->priv;
196 
197  av_expr_free(as->expr);
198  as->expr = NULL;
199 }
200 
201 static const AVFilterPad astreamsync_inputs[] = {
202  {
203  .name = "in1",
204  .type = AVMEDIA_TYPE_AUDIO,
205  .filter_frame = filter_frame,
206  },{
207  .name = "in2",
208  .type = AVMEDIA_TYPE_AUDIO,
209  .filter_frame = filter_frame,
210  },
211  { NULL }
212 };
213 
215  {
216  .name = "out1",
217  .type = AVMEDIA_TYPE_AUDIO,
218  .config_props = config_output,
219  .request_frame = request_frame,
220  },{
221  .name = "out2",
222  .type = AVMEDIA_TYPE_AUDIO,
223  .config_props = config_output,
224  .request_frame = request_frame,
225  },
226  { NULL }
227 };
228 
230  .name = "astreamsync",
231  .description = NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
232  "in a configurable order."),
233  .priv_size = sizeof(AStreamSyncContext),
234  .init = init,
235  .uninit = uninit,
237  .inputs = astreamsync_inputs,
238  .outputs = astreamsync_outputs,
239  .priv_class = &astreamsync_class,
240 };