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 };
This structure describes decoded (raw) audio or video data.
Definition: frame.h:76
AVOption.
Definition: opt.h:251
enum AVCodecID id
Definition: mxfenc.c:89
static const AVFilterPad outputs[]
Definition: af_ashowinfo.c:117
external API header
int av_expr_parse(AVExpr **expr, const char *s, const char *const *const_names, const char *const *func1_names, double(*const *funcs1)(void *, double), const char *const *func2_names, double(*const *funcs2)(void *, double, double), int log_offset, void *log_ctx)
Parse an expression.
Definition: eval.c:640
static int config_output(AVFilterLink *outlink)
const char * name
Pad name.
#define OFFSET(x)
AVFilterLink ** inputs
array of pointers to input links
Definition: avfilter.h:532
it can be given away to ff_start_frame *A reference passed to ff_filter_frame(or the deprecated ff_start_frame) is given away and must no longer be used.*A reference created with avfilter_ref_buffer belongs to the code that created it.*A reference obtained with ff_get_video_buffer or ff_get_audio_buffer belongs to the code that requested it.*A reference given as return value by the get_video_buffer or get_audio_buffer method is given away and must no longer be used.Link reference fields---------------------The AVFilterLink structure has a few AVFilterBufferRef fields.The cur_buf and out_buf were used with the deprecated start_frame/draw_slice/end_frame API and should no longer be used.src_buf
#define av_cold
Definition: attributes.h:78
AVOptions.
int64_t pts
Presentation timestamp in time_base units (time when frame should be shown to user).
Definition: frame.h:159
Definition: eval.c:140
void ff_channel_layouts_ref(AVFilterChannelLayouts *f, AVFilterChannelLayouts **ref)
Add *ref as a new reference to f.
Definition: formats.c:427
static double av_q2d(AVRational a)
Convert rational to double.
Definition: rational.h:69
static const AVFilterPad astreamsync_outputs[]
#define FLAGS
static const int rates[]
A filter pad used for either input or output.
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
static const AVOption astreamsync_options[]
const char * r
Definition: vf_curves.c:94
void * priv
private data for use by the filter
Definition: avfilter.h:545
static int query_formats(AVFilterContext *ctx)
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:246
var_name
ret
Definition: avfilter.c:821
static av_cold void uninit(AVFilterContext *ctx)
A list of supported channel layouts.
Definition: formats.h:85
NULL
Definition: eval.c:55
#define QUEUE_SIZE
void ff_formats_ref(AVFilterFormats *f, AVFilterFormats **ref)
Add *ref as a new reference to formats.
Definition: formats.c:432
void av_expr_free(AVExpr *e)
Free a parsed expression previously created with av_expr_parse().
Definition: eval.c:302
double var_values[VAR_NB]
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:148
void * buf
Definition: avisynth_c.h:594
AVFrame * buf[QUEUE_SIZE]
struct AStreamSyncContext::buf_queue queue[2]
Describe the class of an AVClass context structure.
Definition: log.h:50
Filter definition.
Definition: avfilter.h:436
synthesis window for stochastic i
const char * name
filter name
Definition: avfilter.h:437
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
AVFilterLink ** outputs
array of pointers to output links
Definition: avfilter.h:539
enum MovChannelLayoutTag * layouts
Definition: mov_chan.c:434
AVFilterFormats * ff_all_samplerates(void)
Definition: formats.c:396
The official guide to swscale for confused that consecutive non overlapping rectangles of slice_bottom special converter These generally are unscaled converters of common formats
Definition: swscale.txt:33
static void send_next(AVFilterContext *ctx)
static const AVFilterPad astreamsync_inputs[]
static av_cold int init(AVFilterContext *ctx)
AVFILTER_DEFINE_CLASS(astreamsync)
double av_expr_eval(AVExpr *e, const double *const_values, void *opaque)
Evaluate a previously parsed expression.
Definition: eval.c:691
A list of supported formats for one end of a filter link.
Definition: formats.h:64
An instance of a filter.
Definition: avfilter.h:524
AVFilter avfilter_af_astreamsync
int ff_request_frame(AVFilterLink *link)
Request an input frame from the filter at the other end of the link.
Definition: avfilter.c:319
static int send_out(AVFilterContext *ctx, int out_id)
internal API functions
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several inputs
int nb_samples
number of audio samples (per channel) described by this frame
Definition: frame.h:127
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:190
static const char *const var_names[]
static int request_frame(AVFilterLink *outlink)
simple arithmetic expression evaluator