LCOV - code coverage report
Current view: top level - src/libavfilter - avf_concat.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 187 207 90.3 %
Date: 2017-03-25 17:02:41 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2012 Nicolas George
       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.
      14             :  * See the GNU Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public License
      17             :  * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
      18             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : /**
      22             :  * @file
      23             :  * concat audio-video filter
      24             :  */
      25             : 
      26             : #include "libavutil/avassert.h"
      27             : #include "libavutil/avstring.h"
      28             : #include "libavutil/channel_layout.h"
      29             : #include "libavutil/opt.h"
      30             : #include "avfilter.h"
      31             : #define FF_BUFQUEUE_SIZE 256
      32             : #include "bufferqueue.h"
      33             : #include "internal.h"
      34             : #include "video.h"
      35             : #include "audio.h"
      36             : 
      37             : #define TYPE_ALL 2
      38             : 
      39             : typedef struct {
      40             :     const AVClass *class;
      41             :     unsigned nb_streams[TYPE_ALL]; /**< number of out streams of each type */
      42             :     unsigned nb_segments;
      43             :     unsigned cur_idx; /**< index of the first input of current segment */
      44             :     int64_t delta_ts; /**< timestamp to add to produce output timestamps */
      45             :     unsigned nb_in_active; /**< number of active inputs in current segment */
      46             :     unsigned unsafe;
      47             :     struct concat_in {
      48             :         int64_t pts;
      49             :         int64_t nb_frames;
      50             :         unsigned eof;
      51             :         struct FFBufQueue queue;
      52             :     } *in;
      53             : } ConcatContext;
      54             : 
      55             : #define OFFSET(x) offsetof(ConcatContext, x)
      56             : #define A AV_OPT_FLAG_AUDIO_PARAM
      57             : #define F AV_OPT_FLAG_FILTERING_PARAM
      58             : #define V AV_OPT_FLAG_VIDEO_PARAM
      59             : 
      60             : static const AVOption concat_options[] = {
      61             :     { "n", "specify the number of segments", OFFSET(nb_segments),
      62             :       AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT_MAX, V|A|F},
      63             :     { "v", "specify the number of video streams",
      64             :       OFFSET(nb_streams[AVMEDIA_TYPE_VIDEO]),
      65             :       AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, V|F },
      66             :     { "a", "specify the number of audio streams",
      67             :       OFFSET(nb_streams[AVMEDIA_TYPE_AUDIO]),
      68             :       AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A|F},
      69             :     { "unsafe", "enable unsafe mode",
      70             :       OFFSET(unsafe),
      71             :       AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, V|A|F},
      72             :     { NULL }
      73             : };
      74             : 
      75             : AVFILTER_DEFINE_CLASS(concat);
      76             : 
      77           2 : static int query_formats(AVFilterContext *ctx)
      78             : {
      79           2 :     ConcatContext *cat = ctx->priv;
      80           2 :     unsigned type, nb_str, idx0 = 0, idx, str, seg;
      81           2 :     AVFilterFormats *formats, *rates = NULL;
      82           2 :     AVFilterChannelLayouts *layouts = NULL;
      83             :     int ret;
      84             : 
      85           6 :     for (type = 0; type < TYPE_ALL; type++) {
      86           4 :         nb_str = cat->nb_streams[type];
      87           7 :         for (str = 0; str < nb_str; str++) {
      88           3 :             idx = idx0;
      89             : 
      90             :             /* Set the output formats */
      91           3 :             formats = ff_all_formats(type);
      92           3 :             if ((ret = ff_formats_ref(formats, &ctx->outputs[idx]->in_formats)) < 0)
      93           0 :                 return ret;
      94             : 
      95           3 :             if (type == AVMEDIA_TYPE_AUDIO) {
      96           1 :                 rates = ff_all_samplerates();
      97           1 :                 if ((ret = ff_formats_ref(rates, &ctx->outputs[idx]->in_samplerates)) < 0)
      98           0 :                     return ret;
      99           1 :                 layouts = ff_all_channel_layouts();
     100           1 :                 if ((ret = ff_channel_layouts_ref(layouts, &ctx->outputs[idx]->in_channel_layouts)) < 0)
     101           0 :                     return ret;
     102             :             }
     103             : 
     104             :             /* Set the same formats for each corresponding input */
     105          11 :             for (seg = 0; seg < cat->nb_segments; seg++) {
     106           8 :                 if ((ret = ff_formats_ref(formats, &ctx->inputs[idx]->out_formats)) < 0)
     107           0 :                     return ret;
     108           8 :                 if (type == AVMEDIA_TYPE_AUDIO) {
     109           6 :                     if ((ret = ff_formats_ref(rates, &ctx->inputs[idx]->out_samplerates)) < 0 ||
     110           3 :                         (ret = ff_channel_layouts_ref(layouts, &ctx->inputs[idx]->out_channel_layouts)) < 0)
     111           0 :                         return ret;
     112             :                 }
     113           8 :                 idx += ctx->nb_outputs;
     114             :             }
     115             : 
     116           3 :             idx0++;
     117             :         }
     118             :     }
     119           2 :     return 0;
     120             : }
     121             : 
     122           3 : static int config_output(AVFilterLink *outlink)
     123             : {
     124           3 :     AVFilterContext *ctx = outlink->src;
     125           3 :     ConcatContext *cat   = ctx->priv;
     126           3 :     unsigned out_no = FF_OUTLINK_IDX(outlink);
     127           3 :     unsigned in_no  = out_no, seg;
     128           3 :     AVFilterLink *inlink = ctx->inputs[in_no];
     129             : 
     130             :     /* enhancement: find a common one */
     131           3 :     outlink->time_base           = AV_TIME_BASE_Q;
     132           3 :     outlink->w                   = inlink->w;
     133           3 :     outlink->h                   = inlink->h;
     134           3 :     outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
     135           3 :     outlink->format              = inlink->format;
     136           8 :     for (seg = 1; seg < cat->nb_segments; seg++) {
     137           5 :         inlink = ctx->inputs[in_no += ctx->nb_outputs];
     138           5 :         if (!outlink->sample_aspect_ratio.num)
     139           2 :             outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
     140             :         /* possible enhancement: unsafe mode, do not check */
     141           9 :         if (outlink->w                       != inlink->w                       ||
     142           8 :             outlink->h                       != inlink->h                       ||
     143           4 :             outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num &&
     144           4 :                                                 inlink->sample_aspect_ratio.num ||
     145           4 :             outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) {
     146           3 :             av_log(ctx, AV_LOG_ERROR, "Input link %s parameters "
     147             :                    "(size %dx%d, SAR %d:%d) do not match the corresponding "
     148             :                    "output link %s parameters (%dx%d, SAR %d:%d)\n",
     149           1 :                    ctx->input_pads[in_no].name, inlink->w, inlink->h,
     150             :                    inlink->sample_aspect_ratio.num,
     151             :                    inlink->sample_aspect_ratio.den,
     152           1 :                    ctx->input_pads[out_no].name, outlink->w, outlink->h,
     153             :                    outlink->sample_aspect_ratio.num,
     154             :                    outlink->sample_aspect_ratio.den);
     155           1 :             if (!cat->unsafe)
     156           0 :                 return AVERROR(EINVAL);
     157             :         }
     158             :     }
     159             : 
     160           3 :     return 0;
     161             : }
     162             : 
     163         205 : static int push_frame(AVFilterContext *ctx, unsigned in_no, AVFrame *buf)
     164             : {
     165         205 :     ConcatContext *cat = ctx->priv;
     166         205 :     unsigned out_no = in_no % ctx->nb_outputs;
     167         205 :     AVFilterLink * inlink = ctx-> inputs[ in_no];
     168         205 :     AVFilterLink *outlink = ctx->outputs[out_no];
     169         205 :     struct concat_in *in = &cat->in[in_no];
     170             : 
     171         205 :     buf->pts = av_rescale_q(buf->pts, inlink->time_base, outlink->time_base);
     172         205 :     in->pts = buf->pts;
     173         205 :     in->nb_frames++;
     174             :     /* add duration to input PTS */
     175         205 :     if (inlink->sample_rate)
     176             :         /* use number of audio samples */
     177         175 :         in->pts += av_rescale_q(buf->nb_samples,
     178             :                                 av_make_q(1, inlink->sample_rate),
     179             :                                 outlink->time_base);
     180          30 :     else if (in->nb_frames >= 2)
     181             :         /* use mean duration */
     182          25 :         in->pts = av_rescale(in->pts, in->nb_frames, in->nb_frames - 1);
     183             : 
     184         205 :     buf->pts += cat->delta_ts;
     185         205 :     return ff_filter_frame(outlink, buf);
     186             : }
     187             : 
     188         205 : static int process_frame(AVFilterLink *inlink, AVFrame *buf)
     189             : {
     190         205 :     AVFilterContext *ctx  = inlink->dst;
     191         205 :     ConcatContext *cat    = ctx->priv;
     192         205 :     unsigned in_no = FF_INLINK_IDX(inlink);
     193             : 
     194         205 :     if (in_no < cat->cur_idx) {
     195           0 :         av_log(ctx, AV_LOG_ERROR, "Frame after EOF on input %s\n",
     196           0 :                ctx->input_pads[in_no].name);
     197           0 :         av_frame_free(&buf);
     198         205 :     } else if (in_no >= cat->cur_idx + ctx->nb_outputs) {
     199           0 :         ff_bufqueue_add(ctx, &cat->in[in_no].queue, buf);
     200             :     } else {
     201         205 :         return push_frame(ctx, in_no, buf);
     202             :     }
     203           0 :     return 0;
     204             : }
     205             : 
     206          30 : static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
     207             : {
     208          30 :     AVFilterContext *ctx = inlink->dst;
     209          30 :     unsigned in_no = FF_INLINK_IDX(inlink);
     210          30 :     AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
     211             : 
     212          30 :     return ff_get_video_buffer(outlink, w, h);
     213             : }
     214             : 
     215         175 : static AVFrame *get_audio_buffer(AVFilterLink *inlink, int nb_samples)
     216             : {
     217         175 :     AVFilterContext *ctx = inlink->dst;
     218         175 :     unsigned in_no = FF_INLINK_IDX(inlink);
     219         175 :     AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
     220             : 
     221         175 :     return ff_get_audio_buffer(outlink, nb_samples);
     222             : }
     223             : 
     224         205 : static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     225             : {
     226         205 :     return process_frame(inlink, buf);
     227             : }
     228             : 
     229           8 : static void close_input(AVFilterContext *ctx, unsigned in_no)
     230             : {
     231           8 :     ConcatContext *cat = ctx->priv;
     232             : 
     233           8 :     cat->in[in_no].eof = 1;
     234           8 :     cat->nb_in_active--;
     235          16 :     av_log(ctx, AV_LOG_VERBOSE, "EOF on %s, %d streams left in segment.\n",
     236           8 :            ctx->input_pads[in_no].name, cat->nb_in_active);
     237           8 : }
     238             : 
     239           5 : static void find_next_delta_ts(AVFilterContext *ctx, int64_t *seg_delta)
     240             : {
     241           5 :     ConcatContext *cat = ctx->priv;
     242           5 :     unsigned i = cat->cur_idx;
     243           5 :     unsigned imax = i + ctx->nb_outputs;
     244             :     int64_t pts;
     245             : 
     246           5 :     pts = cat->in[i++].pts;
     247           8 :     for (; i < imax; i++)
     248           3 :         pts = FFMAX(pts, cat->in[i].pts);
     249           5 :     cat->delta_ts += pts;
     250           5 :     *seg_delta = pts;
     251           5 : }
     252             : 
     253           2 : static int send_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
     254             :                         int64_t seg_delta)
     255             : {
     256           2 :     ConcatContext *cat = ctx->priv;
     257           2 :     AVFilterLink *outlink = ctx->outputs[out_no];
     258           2 :     int64_t base_pts = cat->in[in_no].pts + cat->delta_ts - seg_delta;
     259           2 :     int64_t nb_samples, sent = 0;
     260             :     int frame_nb_samples, ret;
     261           2 :     AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };
     262             :     AVFrame *buf;
     263             : 
     264           2 :     if (!rate_tb.den)
     265           0 :         return AVERROR_BUG;
     266           2 :     nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
     267             :                               outlink->time_base, rate_tb);
     268           2 :     frame_nb_samples = FFMAX(9600, rate_tb.den / 5); /* arbitrary */
     269           9 :     while (nb_samples) {
     270           5 :         frame_nb_samples = FFMIN(frame_nb_samples, nb_samples);
     271           5 :         buf = ff_get_audio_buffer(outlink, frame_nb_samples);
     272           5 :         if (!buf)
     273           0 :             return AVERROR(ENOMEM);
     274           5 :         av_samples_set_silence(buf->extended_data, 0, frame_nb_samples,
     275           5 :                                outlink->channels, outlink->format);
     276           5 :         buf->pts = base_pts + av_rescale_q(sent, rate_tb, outlink->time_base);
     277           5 :         ret = ff_filter_frame(outlink, buf);
     278           5 :         if (ret < 0)
     279           0 :             return ret;
     280           5 :         sent       += frame_nb_samples;
     281           5 :         nb_samples -= frame_nb_samples;
     282             :     }
     283           2 :     return 0;
     284             : }
     285             : 
     286           5 : static int flush_segment(AVFilterContext *ctx)
     287             : {
     288             :     int ret;
     289           5 :     ConcatContext *cat = ctx->priv;
     290             :     unsigned str, str_max;
     291             :     int64_t seg_delta;
     292             : 
     293           5 :     find_next_delta_ts(ctx, &seg_delta);
     294           5 :     cat->cur_idx += ctx->nb_outputs;
     295           5 :     cat->nb_in_active = ctx->nb_outputs;
     296           5 :     av_log(ctx, AV_LOG_VERBOSE, "Segment finished at pts=%"PRId64"\n",
     297             :            cat->delta_ts);
     298             : 
     299           5 :     if (cat->cur_idx < ctx->nb_inputs) {
     300             :         /* pad audio streams with silence */
     301           3 :         str = cat->nb_streams[AVMEDIA_TYPE_VIDEO];
     302           3 :         str_max = str + cat->nb_streams[AVMEDIA_TYPE_AUDIO];
     303           5 :         for (; str < str_max; str++) {
     304           2 :             ret = send_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str,
     305             :                                seg_delta);
     306           2 :             if (ret < 0)
     307           0 :                 return ret;
     308             :         }
     309             :         /* flush queued buffers */
     310             :         /* possible enhancement: flush in PTS order */
     311           3 :         str_max = cat->cur_idx + ctx->nb_outputs;
     312           8 :         for (str = cat->cur_idx; str < str_max; str++) {
     313          10 :             while (cat->in[str].queue.available) {
     314           0 :                 ret = push_frame(ctx, str, ff_bufqueue_get(&cat->in[str].queue));
     315           0 :                 if (ret < 0)
     316           0 :                     return ret;
     317             :             }
     318             :         }
     319             :     }
     320           5 :     return 0;
     321             : }
     322             : 
     323         222 : static int request_frame(AVFilterLink *outlink)
     324             : {
     325         222 :     AVFilterContext *ctx = outlink->src;
     326         222 :     ConcatContext *cat   = ctx->priv;
     327         222 :     unsigned out_no = FF_OUTLINK_IDX(outlink);
     328         222 :     unsigned in_no  = out_no + cat->cur_idx;
     329             :     unsigned str, str_max;
     330             :     int ret;
     331             : 
     332             :     while (1) {
     333         232 :         if (in_no >= ctx->nb_inputs)
     334           2 :             return AVERROR_EOF;
     335         225 :         if (!cat->in[in_no].eof) {
     336         157 :             ret = ff_request_frame(ctx->inputs[in_no]);
     337         157 :             if (ret != AVERROR_EOF)
     338         151 :                 return ret;
     339           6 :             close_input(ctx, in_no);
     340             :         }
     341             :         /* cycle on all inputs to finish the segment */
     342             :         /* possible enhancement: request in PTS order */
     343          74 :         str_max = cat->cur_idx + ctx->nb_outputs - 1;
     344         215 :         for (str = cat->cur_idx; cat->nb_in_active;
     345          67 :              str = str == str_max ? cat->cur_idx : str + 1) {
     346         136 :             if (cat->in[str].eof)
     347          65 :                 continue;
     348          71 :             ret = ff_request_frame(ctx->inputs[str]);
     349          71 :             if (ret != AVERROR_EOF)
     350          69 :                 return ret;
     351           2 :             close_input(ctx, str);
     352             :         }
     353           5 :         ret = flush_segment(ctx);
     354           5 :         if (ret < 0)
     355           0 :             return ret;
     356           5 :         in_no += ctx->nb_outputs;
     357             :     }
     358             : }
     359             : 
     360           3 : static av_cold int init(AVFilterContext *ctx)
     361             : {
     362           3 :     ConcatContext *cat = ctx->priv;
     363             :     unsigned seg, type, str;
     364             : 
     365             :     /* create input pads */
     366          11 :     for (seg = 0; seg < cat->nb_segments; seg++) {
     367          24 :         for (type = 0; type < TYPE_ALL; type++) {
     368          30 :             for (str = 0; str < cat->nb_streams[type]; str++) {
     369          14 :                 AVFilterPad pad = {
     370             :                     .type             = type,
     371             :                     .get_video_buffer = get_video_buffer,
     372             :                     .get_audio_buffer = get_audio_buffer,
     373             :                     .filter_frame     = filter_frame,
     374             :                 };
     375          14 :                 pad.name = av_asprintf("in%d:%c%d", seg, "va"[type], str);
     376          14 :                 ff_insert_inpad(ctx, ctx->nb_inputs, &pad);
     377             :             }
     378             :         }
     379             :     }
     380             :     /* create output pads */
     381           9 :     for (type = 0; type < TYPE_ALL; type++) {
     382          11 :         for (str = 0; str < cat->nb_streams[type]; str++) {
     383           5 :             AVFilterPad pad = {
     384             :                 .type          = type,
     385             :                 .config_props  = config_output,
     386             :                 .request_frame = request_frame,
     387             :             };
     388           5 :             pad.name = av_asprintf("out:%c%d", "va"[type], str);
     389           5 :             ff_insert_outpad(ctx, ctx->nb_outputs, &pad);
     390             :         }
     391             :     }
     392             : 
     393           3 :     cat->in = av_calloc(ctx->nb_inputs, sizeof(*cat->in));
     394           3 :     if (!cat->in)
     395           0 :         return AVERROR(ENOMEM);
     396           3 :     cat->nb_in_active = ctx->nb_outputs;
     397           3 :     return 0;
     398             : }
     399             : 
     400           3 : static av_cold void uninit(AVFilterContext *ctx)
     401             : {
     402           3 :     ConcatContext *cat = ctx->priv;
     403             :     unsigned i;
     404             : 
     405          17 :     for (i = 0; i < ctx->nb_inputs; i++) {
     406          14 :         av_freep(&ctx->input_pads[i].name);
     407          14 :         ff_bufqueue_discard_all(&cat->in[i].queue);
     408             :     }
     409           8 :     for (i = 0; i < ctx->nb_outputs; i++)
     410           5 :         av_freep(&ctx->output_pads[i].name);
     411           3 :     av_freep(&cat->in);
     412           3 : }
     413             : 
     414             : AVFilter ff_avf_concat = {
     415             :     .name          = "concat",
     416             :     .description   = NULL_IF_CONFIG_SMALL("Concatenate audio and video streams."),
     417             :     .init          = init,
     418             :     .uninit        = uninit,
     419             :     .query_formats = query_formats,
     420             :     .priv_size     = sizeof(ConcatContext),
     421             :     .inputs        = NULL,
     422             :     .outputs       = NULL,
     423             :     .priv_class    = &concat_class,
     424             :     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS,
     425             : };

Generated by: LCOV version 1.13