FFmpeg
trim.c
Go to the documentation of this file.
1 /*
2  * This file is part of FFmpeg.
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 #include <stdint.h>
20 
21 #include "config.h"
22 #include "config_components.h"
23 
25 #include "libavutil/common.h"
26 #include "libavutil/log.h"
27 #include "libavutil/mathematics.h"
28 #include "libavutil/opt.h"
29 #include "libavutil/samplefmt.h"
30 
31 #include "audio.h"
32 #include "avfilter.h"
33 #include "internal.h"
34 #include "filters.h"
35 #include "video.h"
36 
37 typedef struct TrimContext {
38  const AVClass *class;
39 
40  /*
41  * AVOptions
42  */
43  int64_t duration;
44  int64_t start_time, end_time;
46  /*
47  * in the link timebase for video,
48  * in 1/samplerate for audio
49  */
50  int64_t start_pts, end_pts;
52 
53  /*
54  * number of video frames that arrived on this filter so far
55  */
56  int64_t nb_frames;
57  /*
58  * number of audio samples that arrived on this filter so far
59  */
60  int64_t nb_samples;
61  /*
62  * timestamp of the first frame in the output, in the timebase units
63  */
64  int64_t first_pts;
65  /*
66  * duration in the timebase units
67  */
68  int64_t duration_tb;
69 
70  int64_t next_pts;
71 
72  int eof;
73 
75 } TrimContext;
76 
78 {
79  TrimContext *s = ctx->priv;
80 
81  s->first_pts = AV_NOPTS_VALUE;
82 
83  return 0;
84 }
85 
86 #if CONFIG_TRIM_FILTER
87 static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame)
88 {
89  AVFilterContext *ctx = inlink->dst;
90  TrimContext *s = ctx->priv;
91  int drop;
92 
93  /* drop everything if EOF has already been returned */
94  if (s->eof) {
96  return 0;
97  }
98 
99  if (s->start_frame >= 0 || s->start_pts != AV_NOPTS_VALUE) {
100  drop = 1;
101  if (s->start_frame >= 0 && s->nb_frames >= s->start_frame)
102  drop = 0;
103  if (s->start_pts != AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE &&
104  frame->pts >= s->start_pts)
105  drop = 0;
106  if (drop)
107  goto drop;
108  }
109 
110  if (s->first_pts == AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE)
111  s->first_pts = frame->pts;
112 
113  if (s->end_frame != INT64_MAX || s->end_pts != AV_NOPTS_VALUE || s->duration_tb) {
114  drop = 1;
115 
116  if (s->end_frame != INT64_MAX && s->nb_frames < s->end_frame)
117  drop = 0;
118  if (s->end_pts != AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE &&
119  frame->pts < s->end_pts)
120  drop = 0;
121  if (s->duration_tb && frame->pts != AV_NOPTS_VALUE &&
122  frame->pts - s->first_pts < s->duration_tb)
123  drop = 0;
124 
125  if (drop) {
126  s->eof = 1;
128  ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, frame->pts);
129  goto drop;
130  }
131  }
132 
133  s->nb_frames++;
134 
135  return ff_filter_frame(ctx->outputs[0], frame);
136 
137 drop:
138  if (!s->eof)
139  ff_filter_set_ready(ctx, 100);
140  s->nb_frames++;
142  return 0;
143 }
144 #endif // CONFIG_TRIM_FILTER
145 
146 #if CONFIG_ATRIM_FILTER
147 static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame)
148 {
149  AVFilterContext *ctx = inlink->dst;
150  TrimContext *s = ctx->priv;
151  int64_t start_sample, end_sample;
152  int64_t pts;
153  int drop;
154 
155  /* drop everything if EOF has already been returned */
156  if (s->eof) {
158  return 0;
159  }
160 
161  if (frame->pts != AV_NOPTS_VALUE)
162  pts = av_rescale_q(frame->pts, inlink->time_base,
163  (AVRational){ 1, inlink->sample_rate });
164  else
165  pts = s->next_pts;
166  s->next_pts = pts + frame->nb_samples;
167 
168  /* check if at least a part of the frame is after the start time */
169  if (s->start_sample < 0 && s->start_pts == AV_NOPTS_VALUE) {
170  start_sample = 0;
171  } else {
172  drop = 1;
173  start_sample = frame->nb_samples;
174 
175  if (s->start_sample >= 0 &&
176  s->nb_samples + frame->nb_samples > s->start_sample) {
177  drop = 0;
178  start_sample = FFMIN(start_sample, s->start_sample - s->nb_samples);
179  }
180 
181  if (s->start_pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE &&
182  pts + frame->nb_samples > s->start_pts) {
183  drop = 0;
184  start_sample = FFMIN(start_sample, s->start_pts - pts);
185  }
186 
187  if (drop)
188  goto drop;
189  }
190 
191  if (s->first_pts == AV_NOPTS_VALUE)
192  s->first_pts = pts + start_sample;
193 
194  /* check if at least a part of the frame is before the end time */
195  if (s->end_sample == INT64_MAX && s->end_pts == AV_NOPTS_VALUE && !s->duration_tb) {
196  end_sample = frame->nb_samples;
197  } else {
198  drop = 1;
199  end_sample = 0;
200 
201  if (s->end_sample != INT64_MAX &&
202  s->nb_samples < s->end_sample) {
203  drop = 0;
204  end_sample = FFMAX(end_sample, s->end_sample - s->nb_samples);
205  }
206 
207  if (s->end_pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE &&
208  pts < s->end_pts) {
209  drop = 0;
210  end_sample = FFMAX(end_sample, s->end_pts - pts);
211  }
212 
213  if (s->duration_tb && pts - s->first_pts < s->duration_tb) {
214  drop = 0;
215  end_sample = FFMAX(end_sample, s->first_pts + s->duration_tb - pts);
216  }
217 
218  if (drop) {
219  s->eof = 1;
221  ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, frame->pts);
222  goto drop;
223  }
224  }
225 
226  s->nb_samples += frame->nb_samples;
227  start_sample = FFMAX(0, start_sample);
228  end_sample = FFMIN(frame->nb_samples, end_sample);
229  if (start_sample >= end_sample || !frame->nb_samples)
230  goto drop;
231 
232  if (start_sample) {
233  AVFrame *out = ff_get_audio_buffer(ctx->outputs[0], end_sample - start_sample);
234  if (!out) {
236  return AVERROR(ENOMEM);
237  }
238 
240  av_samples_copy(out->extended_data, frame->extended_data, 0, start_sample,
241  out->nb_samples, inlink->ch_layout.nb_channels,
242  frame->format);
243  if (out->pts != AV_NOPTS_VALUE)
244  out->pts += av_rescale_q(start_sample, (AVRational){ 1, out->sample_rate },
245  inlink->time_base);
246 
248  frame = out;
249  } else
250  frame->nb_samples = end_sample;
251 
252  return ff_filter_frame(ctx->outputs[0], frame);
253 
254 drop:
255  if (!s->eof)
256  ff_filter_set_ready(ctx, 100);
257  s->nb_samples += frame->nb_samples;
259  return 0;
260 }
261 #endif // CONFIG_ATRIM_FILTER
262 
264 {
265  AVFilterContext *ctx = inlink->dst;
266  TrimContext *s = ctx->priv;
267  AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ?
268  inlink->time_base : (AVRational){ 1, inlink->sample_rate };
269 
270 #if CONFIG_TRIM_FILTER
271  if (inlink->type == AVMEDIA_TYPE_VIDEO)
272  s->filter_frame = trim_filter_frame;
273 #endif
274 #if CONFIG_ATRIM_FILTER
275  if (inlink->type == AVMEDIA_TYPE_AUDIO)
276  s->filter_frame = atrim_filter_frame;
277 #endif
278  if (s->start_time != INT64_MAX) {
279  int64_t start_pts = av_rescale_q(s->start_time, AV_TIME_BASE_Q, tb);
280  if (s->start_pts == AV_NOPTS_VALUE || start_pts < s->start_pts)
281  s->start_pts = start_pts;
282  }
283  if (s->end_time != INT64_MAX) {
284  int64_t end_pts = av_rescale_q(s->end_time, AV_TIME_BASE_Q, tb);
285  if (s->end_pts == AV_NOPTS_VALUE || end_pts > s->end_pts)
286  s->end_pts = end_pts;
287  }
288  if (s->duration)
289  s->duration_tb = av_rescale_q(s->duration, AV_TIME_BASE_Q, tb);
290 
291  return 0;
292 }
293 
295 {
296  TrimContext *s = ctx->priv;
297  AVFilterLink *inlink = ctx->inputs[0];
298  AVFilterLink *outlink = ctx->outputs[0];
299 
301 
302  if (!s->eof && ff_inlink_queued_frames(inlink)) {
303  AVFrame *frame = NULL;
304  int ret;
305 
307  if (ret < 0)
308  return ret;
309  if (ret > 0)
310  return s->filter_frame(inlink, frame);
311  }
312 
315 
316  return FFERROR_NOT_READY;
317 }
318 
319 #define OFFSET(x) offsetof(TrimContext, x)
320 #define COMMON_OPTS \
321  { "start", "Timestamp of the first frame that " \
322  "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \
323  { "starti", "Timestamp of the first frame that " \
324  "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \
325  { "end", "Timestamp of the first frame that " \
326  "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \
327  { "endi", "Timestamp of the first frame that " \
328  "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \
329  { "start_pts", "Timestamp of the first frame that should be " \
330  " passed", OFFSET(start_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \
331  { "end_pts", "Timestamp of the first frame that should be " \
332  "dropped again", OFFSET(end_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \
333  { "duration", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, \
334  { "durationi", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS },
335 
336 
337 #if CONFIG_TRIM_FILTER
338 
339 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
340 static const AVOption trim_options[] = {
342  { "start_frame", "Number of the first frame that should be passed "
343  "to the output", OFFSET(start_frame), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS },
344  { "end_frame", "Number of the first frame that should be dropped "
345  "again", OFFSET(end_frame), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS },
346  { NULL }
347 };
348 #undef FLAGS
349 
351 
352 static const AVFilterPad trim_inputs[] = {
353  {
354  .name = "default",
355  .type = AVMEDIA_TYPE_VIDEO,
356  .config_props = config_input,
357  },
358 };
359 
360 const AVFilter ff_vf_trim = {
361  .name = "trim",
362  .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
363  .init = init,
364  .activate = activate,
365  .priv_size = sizeof(TrimContext),
366  .priv_class = &trim_class,
367  FILTER_INPUTS(trim_inputs),
369 };
370 #endif // CONFIG_TRIM_FILTER
371 
372 #if CONFIG_ATRIM_FILTER
373 
374 #define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
375 static const AVOption atrim_options[] = {
377  { "start_sample", "Number of the first audio sample that should be "
378  "passed to the output", OFFSET(start_sample), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS },
379  { "end_sample", "Number of the first audio sample that should be "
380  "dropped again", OFFSET(end_sample), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS },
381  { NULL }
382 };
383 #undef FLAGS
384 
385 AVFILTER_DEFINE_CLASS(atrim);
386 
387 static const AVFilterPad atrim_inputs[] = {
388  {
389  .name = "default",
390  .type = AVMEDIA_TYPE_AUDIO,
391  .config_props = config_input,
392  },
393 };
394 
395 const AVFilter ff_af_atrim = {
396  .name = "atrim",
397  .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."),
398  .init = init,
399  .activate = activate,
400  .priv_size = sizeof(TrimContext),
401  .priv_class = &atrim_class,
403  FILTER_INPUTS(atrim_inputs),
405 };
406 #endif // CONFIG_ATRIM_FILTER
av_samples_copy
int av_samples_copy(uint8_t *const *dst, uint8_t *const *src, int dst_offset, int src_offset, int nb_samples, int nb_channels, enum AVSampleFormat sample_fmt)
Copy samples from src to dst.
Definition: samplefmt.c:222
ff_get_audio_buffer
AVFrame * ff_get_audio_buffer(AVFilterLink *link, int nb_samples)
Request an audio samples buffer with a specific set of permissions.
Definition: audio.c:97
TrimContext::eof
int eof
Definition: trim.c:72
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
out
FILE * out
Definition: movenc.c:55
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1015
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:57
FFERROR_NOT_READY
return FFERROR_NOT_READY
Definition: filter_design.txt:204
AV_TIME_BASE_Q
#define AV_TIME_BASE_Q
Internal time base represented as fractional value.
Definition: avutil.h:264
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:160
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:374
TrimContext::duration_tb
int64_t duration_tb
Definition: trim.c:68
AVOption
AVOption.
Definition: opt.h:346
TrimContext::nb_samples
int64_t nb_samples
Definition: trim.c:60
TrimContext::start_sample
int64_t start_sample
Definition: trim.c:51
FLAGS
#define FLAGS
Definition: cmdutils.c:581
mathematics.h
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:170
video.h
FF_FILTER_FORWARD_STATUS_BACK
#define FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink)
Forward the status on an output link to an input link.
Definition: filters.h:199
TrimContext::filter_frame
int(* filter_frame)(AVFilterLink *inlink, AVFrame *frame)
Definition: trim.c:74
ff_inlink_consume_frame
int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe)
Take a frame from the link's FIFO and update the link's stats.
Definition: avfilter.c:1442
activate
static int activate(AVFilterContext *ctx)
Definition: trim.c:294
samplefmt.h
pts
static int64_t pts
Definition: transcode_aac.c:644
TrimContext::start_pts
int64_t start_pts
Definition: trim.c:50
AVFilterPad
A filter pad used for either input or output.
Definition: internal.h:33
av_cold
#define av_cold
Definition: attributes.h:90
ff_video_default_filterpad
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition: video.c:37
COMMON_OPTS
#define COMMON_OPTS
Definition: trim.c:320
ff_outlink_set_status
static void ff_outlink_set_status(AVFilterLink *link, int status, int64_t pts)
Set the status field of a link from the source filter.
Definition: filters.h:189
s
#define s(width, name)
Definition: cbs_vp9.c:198
AVMEDIA_TYPE_AUDIO
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
init
static av_cold int init(AVFilterContext *ctx)
Definition: trim.c:77
AV_OPT_TYPE_INT64
@ AV_OPT_TYPE_INT64
Definition: opt.h:236
filters.h
ctx
AVFormatContext * ctx
Definition: movenc.c:49
av_rescale_q
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
Rescale a 64-bit integer by 2 rational numbers.
Definition: mathematics.c:142
OFFSET
#define OFFSET(x)
Definition: trim.c:319
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: internal.h:182
ff_af_atrim
const AVFilter ff_af_atrim
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:66
NULL
#define NULL
Definition: coverity.c:32
av_frame_copy_props
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:709
AVRational
Rational number (pair of numerator and denominator).
Definition: rational.h:58
TrimContext::duration
int64_t duration
Definition: trim.c:43
ff_audio_default_filterpad
const AVFilterPad ff_audio_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_AUDIO.
Definition: audio.c:33
ff_inlink_queued_frames
size_t ff_inlink_queued_frames(AVFilterLink *link)
Get the number of frames available on the link.
Definition: avfilter.c:1405
TrimContext::next_pts
int64_t next_pts
Definition: trim.c:70
ff_inlink_set_status
void ff_inlink_set_status(AVFilterLink *link, int status)
Set the status on an input link.
Definition: avfilter.c:1577
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
AV_NOPTS_VALUE
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:248
FF_FILTER_FORWARD_WANTED
FF_FILTER_FORWARD_WANTED(outlink, inlink)
TrimContext::end_frame
int64_t end_frame
Definition: trim.c:45
internal.h
AVFILTER_DEFINE_CLASS
#define AVFILTER_DEFINE_CLASS(fname)
Definition: internal.h:323
log.h
common.h
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
TrimContext::end_time
int64_t end_time
Definition: trim.c:44
tb
#define tb
Definition: regdef.h:68
AVFilterPad::name
const char * name
Pad name.
Definition: internal.h:39
config_input
static int config_input(AVFilterLink *inlink)
Definition: trim.c:263
ff_vf_trim
const AVFilter ff_vf_trim
AVFilter
Filter definition.
Definition: avfilter.h:166
ret
ret
Definition: filter_design.txt:187
frame
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 the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:264
TrimContext::end_sample
int64_t end_sample
Definition: trim.c:51
TrimContext::start_frame
int64_t start_frame
Definition: trim.c:45
TrimContext::nb_frames
int64_t nb_frames
Definition: trim.c:56
TrimContext::start_time
int64_t start_time
Definition: trim.c:44
channel_layout.h
avfilter.h
AVFILTER_FLAG_METADATA_ONLY
#define AVFILTER_FLAG_METADATA_ONLY
The filter is a "metadata" filter - it does not modify the frame data in any way.
Definition: avfilter.h:133
AVFilterContext
An instance of a filter.
Definition: avfilter.h:407
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
audio.h
FF_FILTER_FORWARD_STATUS
FF_FILTER_FORWARD_STATUS(inlink, outlink)
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: internal.h:183
TrimContext
Definition: trim.c:37
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:474
int
int
Definition: ffmpeg_filter.c:424
TrimContext::first_pts
int64_t first_pts
Definition: trim.c:64
TrimContext::end_pts
int64_t end_pts
Definition: trim.c:50
ff_filter_set_ready
void ff_filter_set_ready(AVFilterContext *filter, unsigned priority)
Mark a filter ready and schedule it for activation.
Definition: avfilter.c:235