2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "libavutil/avassert.h"
26 #include "libavutil/channel_layout.h"
27 #include "libavutil/common.h"
28 #include "libavutil/log.h"
29 #include "libavutil/mathematics.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/samplefmt.h"
37 typedef struct TrimContext
{
44 int64_t start_time
, end_time
;
45 int64_t start_frame
, end_frame
;
48 double start_time_dbl
, end_time_dbl
;
50 * in the link timebase for video,
51 * in 1/samplerate for audio
53 int64_t start_pts
, end_pts
;
54 int64_t start_sample
, end_sample
;
57 * number of video frames that arrived on this filter so far
61 * number of audio samples that arrived on this filter so far
65 * timestamp of the first frame in the output, in the timebase units
69 * duration in the timebase units
78 static av_cold
int init(AVFilterContext
*ctx
)
80 TrimContext
*s
= ctx
->priv
;
82 s
->first_pts
= AV_NOPTS_VALUE
;
87 static int config_input(AVFilterLink
*inlink
)
89 AVFilterContext
*ctx
= inlink
->dst
;
90 TrimContext
*s
= ctx
->priv
;
91 AVRational tb
= (inlink
->type
== AVMEDIA_TYPE_VIDEO
) ?
92 inlink
->time_base
: (AVRational
){ 1, inlink
->sample_rate
};
94 if (s
->start_time_dbl
!= DBL_MAX
)
95 s
->start_time
= s
->start_time_dbl
* 1e6
;
96 if (s
->end_time_dbl
!= DBL_MAX
)
97 s
->end_time
= s
->end_time_dbl
* 1e6
;
98 if (s
->duration_dbl
!= 0)
99 s
->duration
= s
->duration_dbl
* 1e6
;
101 if (s
->start_time
!= INT64_MAX
) {
102 int64_t start_pts
= av_rescale_q(s
->start_time
, AV_TIME_BASE_Q
, tb
);
103 if (s
->start_pts
== AV_NOPTS_VALUE
|| start_pts
< s
->start_pts
)
104 s
->start_pts
= start_pts
;
106 if (s
->end_time
!= INT64_MAX
) {
107 int64_t end_pts
= av_rescale_q(s
->end_time
, AV_TIME_BASE_Q
, tb
);
108 if (s
->end_pts
== AV_NOPTS_VALUE
|| end_pts
> s
->end_pts
)
109 s
->end_pts
= end_pts
;
112 s
->duration_tb
= av_rescale_q(s
->duration
, AV_TIME_BASE_Q
, tb
);
117 static int config_output(AVFilterLink
*outlink
)
119 outlink
->flags
|= FF_LINK_FLAG_REQUEST_LOOP
;
123 #define OFFSET(x) offsetof(TrimContext, x)
124 #define COMMON_OPTS \
125 { "starti", "Timestamp of the first frame that " \
126 "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \
127 { "endi", "Timestamp of the first frame that " \
128 "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \
129 { "start_pts", "Timestamp of the first frame that should be " \
130 " passed", OFFSET(start_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \
131 { "end_pts", "Timestamp of the first frame that should be " \
132 "dropped again", OFFSET(end_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \
133 { "durationi", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS },
135 #define COMPAT_OPTS \
136 { "start", "Timestamp in seconds of the first frame that " \
137 "should be passed", OFFSET(start_time_dbl),AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS }, \
138 { "end", "Timestamp in seconds of the first frame that " \
139 "should be dropped again", OFFSET(end_time_dbl), AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS }, \
140 { "duration", "Maximum duration of the output in seconds", OFFSET(duration_dbl), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, DBL_MAX, FLAGS },
143 #if CONFIG_TRIM_FILTER
144 static int trim_filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
146 AVFilterContext
*ctx
= inlink
->dst
;
147 TrimContext
*s
= ctx
->priv
;
150 /* drop everything if EOF has already been returned */
152 av_frame_free(&frame
);
156 if (s
->start_frame
>= 0 || s
->start_pts
!= AV_NOPTS_VALUE
) {
158 if (s
->start_frame
>= 0 && s
->nb_frames
>= s
->start_frame
)
160 if (s
->start_pts
!= AV_NOPTS_VALUE
&& frame
->pts
!= AV_NOPTS_VALUE
&&
161 frame
->pts
>= s
->start_pts
)
167 if (s
->first_pts
== AV_NOPTS_VALUE
&& frame
->pts
!= AV_NOPTS_VALUE
)
168 s
->first_pts
= frame
->pts
;
170 if (s
->end_frame
!= INT64_MAX
|| s
->end_pts
!= AV_NOPTS_VALUE
|| s
->duration_tb
) {
173 if (s
->end_frame
!= INT64_MAX
&& s
->nb_frames
< s
->end_frame
)
175 if (s
->end_pts
!= AV_NOPTS_VALUE
&& frame
->pts
!= AV_NOPTS_VALUE
&&
176 frame
->pts
< s
->end_pts
)
178 if (s
->duration_tb
&& frame
->pts
!= AV_NOPTS_VALUE
&&
179 frame
->pts
- s
->first_pts
< s
->duration_tb
)
183 s
->eof
= inlink
->closed
= 1;
190 return ff_filter_frame(ctx
->outputs
[0], frame
);
194 av_frame_free(&frame
);
198 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
199 static const AVOption trim_options
[] = {
201 { "start_frame", "Number of the first frame that should be passed "
202 "to the output", OFFSET(start_frame
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, FLAGS
},
203 { "end_frame", "Number of the first frame that should be dropped "
204 "again", OFFSET(end_frame
), AV_OPT_TYPE_INT64
, { .i64
= INT64_MAX
}, 0, INT64_MAX
, FLAGS
},
210 AVFILTER_DEFINE_CLASS(trim
);
212 static const AVFilterPad trim_inputs
[] = {
215 .type
= AVMEDIA_TYPE_VIDEO
,
216 .filter_frame
= trim_filter_frame
,
217 .config_props
= config_input
,
222 static const AVFilterPad trim_outputs
[] = {
225 .type
= AVMEDIA_TYPE_VIDEO
,
226 .config_props
= config_output
,
231 AVFilter ff_vf_trim
= {
233 .description
= NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
235 .priv_size
= sizeof(TrimContext
),
236 .priv_class
= &trim_class
,
237 .inputs
= trim_inputs
,
238 .outputs
= trim_outputs
,
240 #endif // CONFIG_TRIM_FILTER
242 #if CONFIG_ATRIM_FILTER
243 static int atrim_filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
245 AVFilterContext
*ctx
= inlink
->dst
;
246 TrimContext
*s
= ctx
->priv
;
247 int64_t start_sample
, end_sample
;
251 /* drop everything if EOF has already been returned */
253 av_frame_free(&frame
);
257 if (frame
->pts
!= AV_NOPTS_VALUE
)
258 pts
= av_rescale_q(frame
->pts
, inlink
->time_base
,
259 (AVRational
){ 1, inlink
->sample_rate
});
262 s
->next_pts
= pts
+ frame
->nb_samples
;
264 /* check if at least a part of the frame is after the start time */
265 if (s
->start_sample
< 0 && s
->start_pts
== AV_NOPTS_VALUE
) {
269 start_sample
= frame
->nb_samples
;
271 if (s
->start_sample
>= 0 &&
272 s
->nb_samples
+ frame
->nb_samples
> s
->start_sample
) {
274 start_sample
= FFMIN(start_sample
, s
->start_sample
- s
->nb_samples
);
277 if (s
->start_pts
!= AV_NOPTS_VALUE
&& pts
!= AV_NOPTS_VALUE
&&
278 pts
+ frame
->nb_samples
> s
->start_pts
) {
280 start_sample
= FFMIN(start_sample
, s
->start_pts
- pts
);
287 if (s
->first_pts
== AV_NOPTS_VALUE
)
288 s
->first_pts
= pts
+ start_sample
;
290 /* check if at least a part of the frame is before the end time */
291 if (s
->end_sample
== INT64_MAX
&& s
->end_pts
== AV_NOPTS_VALUE
&& !s
->duration_tb
) {
292 end_sample
= frame
->nb_samples
;
297 if (s
->end_sample
!= INT64_MAX
&&
298 s
->nb_samples
< s
->end_sample
) {
300 end_sample
= FFMAX(end_sample
, s
->end_sample
- s
->nb_samples
);
303 if (s
->end_pts
!= AV_NOPTS_VALUE
&& pts
!= AV_NOPTS_VALUE
&&
306 end_sample
= FFMAX(end_sample
, s
->end_pts
- pts
);
309 if (s
->duration_tb
&& pts
- s
->first_pts
< s
->duration_tb
) {
311 end_sample
= FFMAX(end_sample
, s
->first_pts
+ s
->duration_tb
- pts
);
315 s
->eof
= inlink
->closed
= 1;
320 s
->nb_samples
+= frame
->nb_samples
;
321 start_sample
= FFMAX(0, start_sample
);
322 end_sample
= FFMIN(frame
->nb_samples
, end_sample
);
323 av_assert0(start_sample
< end_sample
|| (start_sample
== end_sample
&& !frame
->nb_samples
));
326 AVFrame
*out
= ff_get_audio_buffer(ctx
->outputs
[0], end_sample
- start_sample
);
328 av_frame_free(&frame
);
329 return AVERROR(ENOMEM
);
332 av_frame_copy_props(out
, frame
);
333 av_samples_copy(out
->extended_data
, frame
->extended_data
, 0, start_sample
,
334 out
->nb_samples
, inlink
->channels
,
336 if (out
->pts
!= AV_NOPTS_VALUE
)
337 out
->pts
+= av_rescale_q(start_sample
, (AVRational
){ 1, out
->sample_rate
},
340 av_frame_free(&frame
);
343 frame
->nb_samples
= end_sample
;
345 return ff_filter_frame(ctx
->outputs
[0], frame
);
348 s
->nb_samples
+= frame
->nb_samples
;
349 av_frame_free(&frame
);
353 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
354 static const AVOption atrim_options
[] = {
356 { "start_sample", "Number of the first audio sample that should be "
357 "passed to the output", OFFSET(start_sample
), AV_OPT_TYPE_INT64
, { .i64
= -1 }, -1, INT64_MAX
, FLAGS
},
358 { "end_sample", "Number of the first audio sample that should be "
359 "dropped again", OFFSET(end_sample
), AV_OPT_TYPE_INT64
, { .i64
= INT64_MAX
}, 0, INT64_MAX
, FLAGS
},
365 AVFILTER_DEFINE_CLASS(atrim
);
367 static const AVFilterPad atrim_inputs
[] = {
370 .type
= AVMEDIA_TYPE_AUDIO
,
371 .filter_frame
= atrim_filter_frame
,
372 .config_props
= config_input
,
377 static const AVFilterPad atrim_outputs
[] = {
380 .type
= AVMEDIA_TYPE_AUDIO
,
381 .config_props
= config_output
,
386 AVFilter ff_af_atrim
= {
388 .description
= NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
390 .priv_size
= sizeof(TrimContext
),
391 .priv_class
= &atrim_class
,
392 .inputs
= atrim_inputs
,
393 .outputs
= atrim_outputs
,
395 #endif // CONFIG_ATRIM_FILTER