00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00026 #include "libavutil/avassert.h"
00027 #include "libavutil/avstring.h"
00028 #include "libavutil/channel_layout.h"
00029 #include "libavutil/opt.h"
00030 #include "avfilter.h"
00031 #define FF_BUFQUEUE_SIZE 256
00032 #include "bufferqueue.h"
00033 #include "internal.h"
00034 #include "video.h"
00035 #include "audio.h"
00036
00037 #define TYPE_ALL 2
00038
00039 typedef struct {
00040 const AVClass *class;
00041 unsigned nb_streams[TYPE_ALL];
00042 unsigned nb_segments;
00043 unsigned cur_idx;
00044 int64_t delta_ts;
00045 unsigned nb_in_active;
00046 unsigned unsafe;
00047 struct concat_in {
00048 int64_t pts;
00049 int64_t nb_frames;
00050 unsigned eof;
00051 struct FFBufQueue queue;
00052 } *in;
00053 } ConcatContext;
00054
00055 #define OFFSET(x) offsetof(ConcatContext, x)
00056 #define A AV_OPT_FLAG_AUDIO_PARAM
00057 #define F AV_OPT_FLAG_FILTERING_PARAM
00058 #define V AV_OPT_FLAG_VIDEO_PARAM
00059
00060 static const AVOption concat_options[] = {
00061 { "n", "specify the number of segments", OFFSET(nb_segments),
00062 AV_OPT_TYPE_INT, { .i64 = 2 }, 2, INT_MAX, V|A|F},
00063 { "v", "specify the number of video streams",
00064 OFFSET(nb_streams[AVMEDIA_TYPE_VIDEO]),
00065 AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, V|F },
00066 { "a", "specify the number of audio streams",
00067 OFFSET(nb_streams[AVMEDIA_TYPE_AUDIO]),
00068 AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A|F},
00069 { "unsafe", "enable unsafe mode",
00070 OFFSET(unsafe),
00071 AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A|A|F},
00072 { 0 }
00073 };
00074
00075 AVFILTER_DEFINE_CLASS(concat);
00076
00077 static int query_formats(AVFilterContext *ctx)
00078 {
00079 ConcatContext *cat = ctx->priv;
00080 unsigned type, nb_str, idx0 = 0, idx, str, seg;
00081 AVFilterFormats *formats, *rates;
00082 AVFilterChannelLayouts *layouts;
00083
00084 for (type = 0; type < TYPE_ALL; type++) {
00085 nb_str = cat->nb_streams[type];
00086 for (str = 0; str < nb_str; str++) {
00087 idx = idx0;
00088
00089
00090 formats = ff_all_formats(type);
00091 if (!formats)
00092 return AVERROR(ENOMEM);
00093 ff_formats_ref(formats, &ctx->outputs[idx]->in_formats);
00094 if (type == AVMEDIA_TYPE_AUDIO) {
00095 rates = ff_all_samplerates();
00096 if (!rates)
00097 return AVERROR(ENOMEM);
00098 ff_formats_ref(rates, &ctx->outputs[idx]->in_samplerates);
00099 layouts = ff_all_channel_layouts();
00100 if (!layouts)
00101 return AVERROR(ENOMEM);
00102 ff_channel_layouts_ref(layouts, &ctx->outputs[idx]->in_channel_layouts);
00103 }
00104
00105
00106 for (seg = 0; seg < cat->nb_segments; seg++) {
00107 ff_formats_ref(formats, &ctx->inputs[idx]->out_formats);
00108 if (type == AVMEDIA_TYPE_AUDIO) {
00109 ff_formats_ref(rates, &ctx->inputs[idx]->out_samplerates);
00110 ff_channel_layouts_ref(layouts, &ctx->inputs[idx]->out_channel_layouts);
00111 }
00112 idx += ctx->nb_outputs;
00113 }
00114
00115 idx0++;
00116 }
00117 }
00118 return 0;
00119 }
00120
00121 static int config_output(AVFilterLink *outlink)
00122 {
00123 AVFilterContext *ctx = outlink->src;
00124 ConcatContext *cat = ctx->priv;
00125 unsigned out_no = FF_OUTLINK_IDX(outlink);
00126 unsigned in_no = out_no, seg;
00127 AVFilterLink *inlink = ctx->inputs[in_no];
00128
00129
00130 outlink->time_base = AV_TIME_BASE_Q;
00131 outlink->w = inlink->w;
00132 outlink->h = inlink->h;
00133 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
00134 outlink->format = inlink->format;
00135 for (seg = 1; seg < cat->nb_segments; seg++) {
00136 inlink = ctx->inputs[in_no += ctx->nb_outputs];
00137
00138 if (outlink->w != inlink->w ||
00139 outlink->h != inlink->h ||
00140 outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num ||
00141 outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) {
00142 av_log(ctx, AV_LOG_ERROR, "Input link %s parameters "
00143 "(size %dx%d, SAR %d:%d) do not match the corresponding "
00144 "output link %s parameters (%dx%d, SAR %d:%d)\n",
00145 ctx->input_pads[in_no].name, inlink->w, inlink->h,
00146 inlink->sample_aspect_ratio.num,
00147 inlink->sample_aspect_ratio.den,
00148 ctx->input_pads[out_no].name, outlink->w, outlink->h,
00149 outlink->sample_aspect_ratio.num,
00150 outlink->sample_aspect_ratio.den);
00151 if (!cat->unsafe)
00152 return AVERROR(EINVAL);
00153 }
00154 }
00155
00156 return 0;
00157 }
00158
00159 static void push_frame(AVFilterContext *ctx, unsigned in_no,
00160 AVFilterBufferRef *buf)
00161 {
00162 ConcatContext *cat = ctx->priv;
00163 unsigned out_no = in_no % ctx->nb_outputs;
00164 AVFilterLink * inlink = ctx-> inputs[ in_no];
00165 AVFilterLink *outlink = ctx->outputs[out_no];
00166 struct concat_in *in = &cat->in[in_no];
00167
00168 buf->pts = av_rescale_q(buf->pts, inlink->time_base, outlink->time_base);
00169 in->pts = buf->pts;
00170 in->nb_frames++;
00171
00172 if (inlink->sample_rate)
00173
00174 in->pts += av_rescale_q(buf->audio->nb_samples,
00175 (AVRational){ 1, inlink->sample_rate },
00176 outlink->time_base);
00177 else if (in->nb_frames >= 2)
00178
00179 in->pts = av_rescale(in->pts, in->nb_frames, in->nb_frames - 1);
00180
00181 buf->pts += cat->delta_ts;
00182 ff_filter_frame(outlink, buf);
00183 }
00184
00185 static void process_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
00186 {
00187 AVFilterContext *ctx = inlink->dst;
00188 ConcatContext *cat = ctx->priv;
00189 unsigned in_no = FF_INLINK_IDX(inlink);
00190
00191 if (in_no < cat->cur_idx) {
00192 av_log(ctx, AV_LOG_ERROR, "Frame after EOF on input %s\n",
00193 ctx->input_pads[in_no].name);
00194 avfilter_unref_buffer(buf);
00195 } else if (in_no >= cat->cur_idx + ctx->nb_outputs) {
00196 ff_bufqueue_add(ctx, &cat->in[in_no].queue, buf);
00197 } else {
00198 push_frame(ctx, in_no, buf);
00199 }
00200 }
00201
00202 static AVFilterBufferRef *get_video_buffer(AVFilterLink *inlink, int perms,
00203 int w, int h)
00204 {
00205 AVFilterContext *ctx = inlink->dst;
00206 unsigned in_no = FF_INLINK_IDX(inlink);
00207 AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
00208
00209 return ff_get_video_buffer(outlink, perms, w, h);
00210 }
00211
00212 static AVFilterBufferRef *get_audio_buffer(AVFilterLink *inlink, int perms,
00213 int nb_samples)
00214 {
00215 AVFilterContext *ctx = inlink->dst;
00216 unsigned in_no = FF_INLINK_IDX(inlink);
00217 AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
00218
00219 return ff_get_audio_buffer(outlink, perms, nb_samples);
00220 }
00221
00222 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf)
00223 {
00224 process_frame(inlink, buf);
00225 return 0;
00226 }
00227
00228 static void close_input(AVFilterContext *ctx, unsigned in_no)
00229 {
00230 ConcatContext *cat = ctx->priv;
00231
00232 cat->in[in_no].eof = 1;
00233 cat->nb_in_active--;
00234 av_log(ctx, AV_LOG_VERBOSE, "EOF on %s, %d streams left in segment.\n",
00235 ctx->input_pads[in_no].name, cat->nb_in_active);
00236 }
00237
00238 static void find_next_delta_ts(AVFilterContext *ctx)
00239 {
00240 ConcatContext *cat = ctx->priv;
00241 unsigned i = cat->cur_idx;
00242 unsigned imax = i + ctx->nb_outputs;
00243 int64_t pts;
00244
00245 pts = cat->in[i++].pts;
00246 for (; i < imax; i++)
00247 pts = FFMAX(pts, cat->in[i].pts);
00248 cat->delta_ts += pts;
00249 }
00250
00251 static void send_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no)
00252 {
00253 ConcatContext *cat = ctx->priv;
00254 AVFilterLink *outlink = ctx->outputs[out_no];
00255 int64_t base_pts = cat->in[in_no].pts + cat->delta_ts;
00256 int64_t nb_samples, sent = 0;
00257 int frame_nb_samples;
00258 AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };
00259 AVFilterBufferRef *buf;
00260 int nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout);
00261
00262 if (!rate_tb.den)
00263 return;
00264 nb_samples = av_rescale_q(cat->delta_ts - base_pts,
00265 outlink->time_base, rate_tb);
00266 frame_nb_samples = FFMAX(9600, rate_tb.den / 5);
00267 while (nb_samples) {
00268 frame_nb_samples = FFMIN(frame_nb_samples, nb_samples);
00269 buf = ff_get_audio_buffer(outlink, AV_PERM_WRITE, frame_nb_samples);
00270 if (!buf)
00271 return;
00272 av_samples_set_silence(buf->extended_data, 0, frame_nb_samples,
00273 nb_channels, outlink->format);
00274 buf->pts = base_pts + av_rescale_q(sent, rate_tb, outlink->time_base);
00275 ff_filter_frame(outlink, buf);
00276 sent += frame_nb_samples;
00277 nb_samples -= frame_nb_samples;
00278 }
00279 }
00280
00281 static void flush_segment(AVFilterContext *ctx)
00282 {
00283 ConcatContext *cat = ctx->priv;
00284 unsigned str, str_max;
00285
00286 find_next_delta_ts(ctx);
00287 cat->cur_idx += ctx->nb_outputs;
00288 cat->nb_in_active = ctx->nb_outputs;
00289 av_log(ctx, AV_LOG_VERBOSE, "Segment finished at pts=%"PRId64"\n",
00290 cat->delta_ts);
00291
00292 if (cat->cur_idx < ctx->nb_inputs) {
00293
00294 str = cat->nb_streams[AVMEDIA_TYPE_VIDEO];
00295 str_max = str + cat->nb_streams[AVMEDIA_TYPE_AUDIO];
00296 for (; str < str_max; str++)
00297 send_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str);
00298
00299
00300 str_max = cat->cur_idx + ctx->nb_outputs;
00301 for (str = cat->cur_idx; str < str_max; str++)
00302 while (cat->in[str].queue.available)
00303 push_frame(ctx, str, ff_bufqueue_get(&cat->in[str].queue));
00304 }
00305 }
00306
00307 static int request_frame(AVFilterLink *outlink)
00308 {
00309 AVFilterContext *ctx = outlink->src;
00310 ConcatContext *cat = ctx->priv;
00311 unsigned out_no = FF_OUTLINK_IDX(outlink);
00312 unsigned in_no = out_no + cat->cur_idx;
00313 unsigned str, str_max;
00314 int ret;
00315
00316 while (1) {
00317 if (in_no >= ctx->nb_inputs)
00318 return AVERROR_EOF;
00319 if (!cat->in[in_no].eof) {
00320 ret = ff_request_frame(ctx->inputs[in_no]);
00321 if (ret != AVERROR_EOF)
00322 return ret;
00323 close_input(ctx, in_no);
00324 }
00325
00326
00327 str_max = cat->cur_idx + ctx->nb_outputs - 1;
00328 for (str = cat->cur_idx; cat->nb_in_active;
00329 str = str == str_max ? cat->cur_idx : str + 1) {
00330 if (cat->in[str].eof)
00331 continue;
00332 ret = ff_request_frame(ctx->inputs[str]);
00333 if (ret == AVERROR_EOF)
00334 close_input(ctx, str);
00335 else if (ret < 0)
00336 return ret;
00337 }
00338 flush_segment(ctx);
00339 in_no += ctx->nb_outputs;
00340 }
00341 }
00342
00343 static av_cold int init(AVFilterContext *ctx, const char *args)
00344 {
00345 ConcatContext *cat = ctx->priv;
00346 int ret;
00347 unsigned seg, type, str;
00348
00349 cat->class = &concat_class;
00350 av_opt_set_defaults(cat);
00351 ret = av_set_options_string(cat, args, "=", ":");
00352 if (ret < 0) {
00353 av_log(ctx, AV_LOG_ERROR, "Error parsing options: '%s'\n", args);
00354 return ret;
00355 }
00356
00357
00358 for (seg = 0; seg < cat->nb_segments; seg++) {
00359 for (type = 0; type < TYPE_ALL; type++) {
00360 for (str = 0; str < cat->nb_streams[type]; str++) {
00361 AVFilterPad pad = {
00362 .type = type,
00363 .min_perms = AV_PERM_READ | AV_PERM_PRESERVE,
00364 .get_video_buffer = get_video_buffer,
00365 .get_audio_buffer = get_audio_buffer,
00366 .filter_frame = filter_frame,
00367 };
00368 pad.name = av_asprintf("in%d:%c%d", seg, "va"[type], str);
00369 ff_insert_inpad(ctx, ctx->nb_inputs, &pad);
00370 }
00371 }
00372 }
00373
00374 for (type = 0; type < TYPE_ALL; type++) {
00375 for (str = 0; str < cat->nb_streams[type]; str++) {
00376 AVFilterPad pad = {
00377 .type = type,
00378 .config_props = config_output,
00379 .request_frame = request_frame,
00380 };
00381 pad.name = av_asprintf("out:%c%d", "va"[type], str);
00382 ff_insert_outpad(ctx, ctx->nb_outputs, &pad);
00383 }
00384 }
00385
00386 cat->in = av_calloc(ctx->nb_inputs, sizeof(*cat->in));
00387 if (!cat->in)
00388 return AVERROR(ENOMEM);
00389 cat->nb_in_active = ctx->nb_outputs;
00390 return 0;
00391 }
00392
00393 static av_cold void uninit(AVFilterContext *ctx)
00394 {
00395 ConcatContext *cat = ctx->priv;
00396 unsigned i;
00397
00398 for (i = 0; i < ctx->nb_inputs; i++) {
00399 av_freep(&ctx->input_pads[i].name);
00400 ff_bufqueue_discard_all(&cat->in[i].queue);
00401 }
00402 for (i = 0; i < ctx->nb_outputs; i++)
00403 av_freep(&ctx->output_pads[i].name);
00404 av_free(cat->in);
00405 }
00406
00407 AVFilter avfilter_avf_concat = {
00408 .name = "concat",
00409 .description = NULL_IF_CONFIG_SMALL("Concatenate audio and video streams."),
00410 .init = init,
00411 .uninit = uninit,
00412 .query_formats = query_formats,
00413 .priv_size = sizeof(ConcatContext),
00414 .inputs = NULL,
00415 .outputs = NULL,
00416 .priv_class = &concat_class,
00417 };