Merge remote-tracking branch 'qatar/master'
[ffmpeg.git] / libavfilter / trim.c
index 2b57540..04e82da 100644 (file)
@@ -1,18 +1,18 @@
 /*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
  *
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
@@ -40,9 +40,12 @@ typedef struct TrimContext {
     /*
      * AVOptions
      */
-    double duration;
-    double start_time, end_time;
+    int64_t duration;
+    int64_t start_time, end_time;
     int64_t start_frame, end_frame;
+
+    double duration_dbl;
+    double start_time_dbl, end_time_dbl;
     /*
      * in the link timebase for video,
      * in 1/samplerate for audio
@@ -70,10 +73,9 @@ typedef struct TrimContext {
     int64_t next_pts;
 
     int eof;
-    int got_output;
 } TrimContext;
 
-static int init(AVFilterContext *ctx)
+static av_cold int init(AVFilterContext *ctx)
 {
     TrimContext *s = ctx->priv;
 
@@ -89,52 +91,53 @@ static int config_input(AVFilterLink *inlink)
     AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ?
                      inlink->time_base : (AVRational){ 1, inlink->sample_rate };
 
-    if (s->start_time != DBL_MAX) {
-        int64_t start_pts = lrintf(s->start_time / av_q2d(tb));
+    if (s->start_time_dbl != DBL_MAX)
+        s->start_time = s->start_time_dbl * 1e6;
+    if (s->end_time_dbl != DBL_MAX)
+        s->end_time = s->end_time_dbl * 1e6;
+    if (s->duration_dbl != 0)
+        s->duration = s->duration_dbl * 1e6;
+
+    if (s->start_time != INT64_MAX) {
+        int64_t start_pts = av_rescale_q(s->start_time, AV_TIME_BASE_Q, tb);
         if (s->start_pts == AV_NOPTS_VALUE || start_pts < s->start_pts)
             s->start_pts = start_pts;
     }
-    if (s->end_time != DBL_MAX) {
-        int64_t end_pts = lrintf(s->end_time / av_q2d(tb));
+    if (s->end_time != INT64_MAX) {
+        int64_t end_pts = av_rescale_q(s->end_time, AV_TIME_BASE_Q, tb);
         if (s->end_pts == AV_NOPTS_VALUE || end_pts > s->end_pts)
             s->end_pts = end_pts;
     }
     if (s->duration)
-        s->duration_tb = lrintf(s->duration / av_q2d(tb));
+        s->duration_tb = av_rescale_q(s->duration, AV_TIME_BASE_Q, tb);
 
     return 0;
 }
 
-static int request_frame(AVFilterLink *outlink)
+static int config_output(AVFilterLink *outlink)
 {
-    AVFilterContext *ctx = outlink->src;
-    TrimContext       *s = ctx->priv;
-    int ret;
-
-    s->got_output = 0;
-    while (!s->got_output) {
-        if (s->eof)
-            return AVERROR_EOF;
-
-        ret = ff_request_frame(ctx->inputs[0]);
-        if (ret < 0)
-            return ret;
-    }
-
+    outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
     return 0;
 }
 
 #define OFFSET(x) offsetof(TrimContext, x)
 #define COMMON_OPTS                                                                                                                                                         \
-    { "start",       "Timestamp in seconds of the first frame that "                                                                                                        \
-        "should be passed",                                              OFFSET(start_time),  AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX },       -DBL_MAX, DBL_MAX,     FLAGS }, \
-    { "end",         "Timestamp in seconds of the first frame that "                                                                                                        \
-        "should be dropped again",                                       OFFSET(end_time),    AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX },       -DBL_MAX, DBL_MAX,     FLAGS }, \
+    { "starti",      "Timestamp of the first frame that "                                                                                                        \
+        "should be passed",                                              OFFSET(start_time),  AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX },    INT64_MIN, INT64_MAX, FLAGS }, \
+    { "endi",        "Timestamp of the first frame that "                                                                                                        \
+        "should be dropped again",                                       OFFSET(end_time),    AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX },    INT64_MIN, INT64_MAX, FLAGS }, \
     { "start_pts",   "Timestamp of the first frame that should be "                                                                                                         \
        " passed",                                                        OFFSET(start_pts),   AV_OPT_TYPE_INT64,  { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \
     { "end_pts",     "Timestamp of the first frame that should be "                                                                                                         \
         "dropped again",                                                 OFFSET(end_pts),     AV_OPT_TYPE_INT64,  { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \
-    { "duration",    "Maximum duration of the output in seconds",        OFFSET(duration),    AV_OPT_TYPE_DOUBLE, { .dbl = 0 },                      0,   DBL_MAX, FLAGS },
+    { "durationi",   "Maximum duration of the output",                   OFFSET(duration),    AV_OPT_TYPE_DURATION, { .i64 = 0 },                    0, INT64_MAX, FLAGS },
+
+#define COMPAT_OPTS \
+    { "start",       "Timestamp in seconds of the first frame that "                                                                                                        \
+        "should be passed",                                              OFFSET(start_time_dbl),AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX },       -DBL_MAX, DBL_MAX,     FLAGS }, \
+    { "end",         "Timestamp in seconds of the first frame that "                                                                                                        \
+        "should be dropped again",                                       OFFSET(end_time_dbl),  AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX },       -DBL_MAX, DBL_MAX,     FLAGS }, \
+    { "duration",    "Maximum duration of the output in seconds",        OFFSET(duration_dbl),  AV_OPT_TYPE_DOUBLE, { .dbl = 0 },                      0,   DBL_MAX, FLAGS },
 
 
 #if CONFIG_TRIM_FILTER
@@ -177,13 +180,12 @@ static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame)
             drop = 0;
 
         if (drop) {
-            s->eof = 1;
+            s->eof = inlink->closed = 1;
             goto drop;
         }
     }
 
     s->nb_frames++;
-    s->got_output = 1;
 
     return ff_filter_frame(ctx->outputs[0], frame);
 
@@ -193,23 +195,19 @@ drop:
     return 0;
 }
 
-#define FLAGS AV_OPT_FLAG_VIDEO_PARAM
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
 static const AVOption trim_options[] = {
     COMMON_OPTS
     { "start_frame", "Number of the first frame that should be passed "
         "to the output",                                                 OFFSET(start_frame), AV_OPT_TYPE_INT64,  { .i64 = -1 },       -1, INT64_MAX, FLAGS },
     { "end_frame",   "Number of the first frame that should be dropped "
         "again",                                                         OFFSET(end_frame),   AV_OPT_TYPE_INT64,  { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS },
-    { NULL },
+    COMPAT_OPTS
+    { NULL }
 };
 #undef FLAGS
 
-static const AVClass trim_class = {
-    .class_name = "trim",
-    .item_name  = av_default_item_name,
-    .option     = trim_options,
-    .version    = LIBAVUTIL_VERSION_INT,
-};
+AVFILTER_DEFINE_CLASS(trim);
 
 static const AVFilterPad trim_inputs[] = {
     {
@@ -223,9 +221,9 @@ static const AVFilterPad trim_inputs[] = {
 
 static const AVFilterPad trim_outputs[] = {
     {
-        .name          = "default",
-        .type          = AVMEDIA_TYPE_VIDEO,
-        .request_frame = request_frame,
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_output,
     },
     { NULL }
 };
@@ -233,12 +231,9 @@ static const AVFilterPad trim_outputs[] = {
 AVFilter ff_vf_trim = {
     .name        = "trim",
     .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
-
     .init        = init,
-
     .priv_size   = sizeof(TrimContext),
     .priv_class  = &trim_class,
-
     .inputs      = trim_inputs,
     .outputs     = trim_outputs,
 };
@@ -317,7 +312,7 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame)
         }
 
         if (drop) {
-            s->eof = 1;
+            s->eof = inlink->closed = 1;
             goto drop;
         }
     }
@@ -325,7 +320,7 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame)
     s->nb_samples += frame->nb_samples;
     start_sample   = FFMAX(0, start_sample);
     end_sample     = FFMIN(frame->nb_samples, end_sample);
-    av_assert0(start_sample < end_sample);
+    av_assert0(start_sample < end_sample || (start_sample == end_sample && !frame->nb_samples));
 
     if (start_sample) {
         AVFrame *out = ff_get_audio_buffer(ctx->outputs[0], end_sample - start_sample);
@@ -336,7 +331,7 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame)
 
         av_frame_copy_props(out, frame);
         av_samples_copy(out->extended_data, frame->extended_data, 0, start_sample,
-                        out->nb_samples, av_get_channel_layout_nb_channels(frame->channel_layout),
+                        out->nb_samples, inlink->channels,
                         frame->format);
         if (out->pts != AV_NOPTS_VALUE)
             out->pts += av_rescale_q(start_sample, (AVRational){ 1, out->sample_rate },
@@ -347,7 +342,6 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame)
     } else
         frame->nb_samples = end_sample;
 
-    s->got_output = 1;
     return ff_filter_frame(ctx->outputs[0], frame);
 
 drop:
@@ -356,23 +350,19 @@ drop:
     return 0;
 }
 
-#define FLAGS AV_OPT_FLAG_AUDIO_PARAM
+#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
 static const AVOption atrim_options[] = {
     COMMON_OPTS
     { "start_sample", "Number of the first audio sample that should be "
         "passed to the output",                                          OFFSET(start_sample), AV_OPT_TYPE_INT64,  { .i64 = -1 },       -1, INT64_MAX, FLAGS },
     { "end_sample",   "Number of the first audio sample that should be "
         "dropped again",                                                 OFFSET(end_sample),   AV_OPT_TYPE_INT64,  { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS },
-    { NULL },
+    COMPAT_OPTS
+    { NULL }
 };
 #undef FLAGS
 
-static const AVClass atrim_class = {
-    .class_name = "atrim",
-    .item_name  = av_default_item_name,
-    .option     = atrim_options,
-    .version    = LIBAVUTIL_VERSION_INT,
-};
+AVFILTER_DEFINE_CLASS(atrim);
 
 static const AVFilterPad atrim_inputs[] = {
     {
@@ -386,9 +376,9 @@ static const AVFilterPad atrim_inputs[] = {
 
 static const AVFilterPad atrim_outputs[] = {
     {
-        .name          = "default",
-        .type          = AVMEDIA_TYPE_AUDIO,
-        .request_frame = request_frame,
+        .name         = "default",
+        .type         = AVMEDIA_TYPE_AUDIO,
+        .config_props = config_output,
     },
     { NULL }
 };
@@ -396,12 +386,9 @@ static const AVFilterPad atrim_outputs[] = {
 AVFilter ff_af_atrim = {
     .name        = "atrim",
     .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
-
     .init        = init,
-
     .priv_size   = sizeof(TrimContext),
     .priv_class  = &atrim_class,
-
     .inputs      = atrim_inputs,
     .outputs     = atrim_outputs,
 };