Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6148 | serge | 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 |
||
20 | #include |
||
21 | |||
22 | #include "config.h" |
||
23 | |||
24 | #include "libavutil/avassert.h" |
||
25 | #include "libavutil/channel_layout.h" |
||
26 | #include "libavutil/common.h" |
||
27 | #include "libavutil/log.h" |
||
28 | #include "libavutil/mathematics.h" |
||
29 | #include "libavutil/opt.h" |
||
30 | #include "libavutil/samplefmt.h" |
||
31 | |||
32 | #include "audio.h" |
||
33 | #include "avfilter.h" |
||
34 | #include "internal.h" |
||
35 | |||
36 | typedef struct TrimContext { |
||
37 | const AVClass *class; |
||
38 | |||
39 | /* |
||
40 | * AVOptions |
||
41 | */ |
||
42 | int64_t duration; |
||
43 | int64_t start_time, end_time; |
||
44 | int64_t start_frame, end_frame; |
||
45 | |||
46 | double duration_dbl; |
||
47 | double start_time_dbl, end_time_dbl; |
||
48 | /* |
||
49 | * in the link timebase for video, |
||
50 | * in 1/samplerate for audio |
||
51 | */ |
||
52 | int64_t start_pts, end_pts; |
||
53 | int64_t start_sample, end_sample; |
||
54 | |||
55 | /* |
||
56 | * number of video frames that arrived on this filter so far |
||
57 | */ |
||
58 | int64_t nb_frames; |
||
59 | /* |
||
60 | * number of audio samples that arrived on this filter so far |
||
61 | */ |
||
62 | int64_t nb_samples; |
||
63 | /* |
||
64 | * timestamp of the first frame in the output, in the timebase units |
||
65 | */ |
||
66 | int64_t first_pts; |
||
67 | /* |
||
68 | * duration in the timebase units |
||
69 | */ |
||
70 | int64_t duration_tb; |
||
71 | |||
72 | int64_t next_pts; |
||
73 | |||
74 | int eof; |
||
75 | } TrimContext; |
||
76 | |||
77 | static av_cold int init(AVFilterContext *ctx) |
||
78 | { |
||
79 | TrimContext *s = ctx->priv; |
||
80 | |||
81 | s->first_pts = AV_NOPTS_VALUE; |
||
82 | |||
83 | return 0; |
||
84 | } |
||
85 | |||
86 | static int config_input(AVFilterLink *inlink) |
||
87 | { |
||
88 | AVFilterContext *ctx = inlink->dst; |
||
89 | TrimContext *s = ctx->priv; |
||
90 | AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ? |
||
91 | inlink->time_base : (AVRational){ 1, inlink->sample_rate }; |
||
92 | |||
93 | if (s->start_time_dbl != DBL_MAX) |
||
94 | s->start_time = s->start_time_dbl * 1e6; |
||
95 | if (s->end_time_dbl != DBL_MAX) |
||
96 | s->end_time = s->end_time_dbl * 1e6; |
||
97 | if (s->duration_dbl != 0) |
||
98 | s->duration = s->duration_dbl * 1e6; |
||
99 | |||
100 | if (s->start_time != INT64_MAX) { |
||
101 | int64_t start_pts = av_rescale_q(s->start_time, AV_TIME_BASE_Q, tb); |
||
102 | if (s->start_pts == AV_NOPTS_VALUE || start_pts < s->start_pts) |
||
103 | s->start_pts = start_pts; |
||
104 | } |
||
105 | if (s->end_time != INT64_MAX) { |
||
106 | int64_t end_pts = av_rescale_q(s->end_time, AV_TIME_BASE_Q, tb); |
||
107 | if (s->end_pts == AV_NOPTS_VALUE || end_pts > s->end_pts) |
||
108 | s->end_pts = end_pts; |
||
109 | } |
||
110 | if (s->duration) |
||
111 | s->duration_tb = av_rescale_q(s->duration, AV_TIME_BASE_Q, tb); |
||
112 | |||
113 | return 0; |
||
114 | } |
||
115 | |||
116 | static int config_output(AVFilterLink *outlink) |
||
117 | { |
||
118 | outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP; |
||
119 | return 0; |
||
120 | } |
||
121 | |||
122 | #define OFFSET(x) offsetof(TrimContext, x) |
||
123 | #define COMMON_OPTS \ |
||
124 | { "starti", "Timestamp of the first frame that " \ |
||
125 | "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ |
||
126 | { "endi", "Timestamp of the first frame that " \ |
||
127 | "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ |
||
128 | { "start_pts", "Timestamp of the first frame that should be " \ |
||
129 | " passed", OFFSET(start_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ |
||
130 | { "end_pts", "Timestamp of the first frame that should be " \ |
||
131 | "dropped again", OFFSET(end_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ |
||
132 | { "durationi", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, |
||
133 | |||
134 | #define COMPAT_OPTS \ |
||
135 | { "start", "Timestamp in seconds of the first frame that " \ |
||
136 | "should be passed", OFFSET(start_time_dbl),AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS }, \ |
||
137 | { "end", "Timestamp in seconds of the first frame that " \ |
||
138 | "should be dropped again", OFFSET(end_time_dbl), AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS }, \ |
||
139 | { "duration", "Maximum duration of the output in seconds", OFFSET(duration_dbl), AV_OPT_TYPE_DOUBLE, { .dbl = 0 }, 0, DBL_MAX, FLAGS }, |
||
140 | |||
141 | |||
142 | #if CONFIG_TRIM_FILTER |
||
143 | static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame) |
||
144 | { |
||
145 | AVFilterContext *ctx = inlink->dst; |
||
146 | TrimContext *s = ctx->priv; |
||
147 | int drop; |
||
148 | |||
149 | /* drop everything if EOF has already been returned */ |
||
150 | if (s->eof) { |
||
151 | av_frame_free(&frame); |
||
152 | return 0; |
||
153 | } |
||
154 | |||
155 | if (s->start_frame >= 0 || s->start_pts != AV_NOPTS_VALUE) { |
||
156 | drop = 1; |
||
157 | if (s->start_frame >= 0 && s->nb_frames >= s->start_frame) |
||
158 | drop = 0; |
||
159 | if (s->start_pts != AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE && |
||
160 | frame->pts >= s->start_pts) |
||
161 | drop = 0; |
||
162 | if (drop) |
||
163 | goto drop; |
||
164 | } |
||
165 | |||
166 | if (s->first_pts == AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE) |
||
167 | s->first_pts = frame->pts; |
||
168 | |||
169 | if (s->end_frame != INT64_MAX || s->end_pts != AV_NOPTS_VALUE || s->duration_tb) { |
||
170 | drop = 1; |
||
171 | |||
172 | if (s->end_frame != INT64_MAX && s->nb_frames < s->end_frame) |
||
173 | drop = 0; |
||
174 | if (s->end_pts != AV_NOPTS_VALUE && frame->pts != AV_NOPTS_VALUE && |
||
175 | frame->pts < s->end_pts) |
||
176 | drop = 0; |
||
177 | if (s->duration_tb && frame->pts != AV_NOPTS_VALUE && |
||
178 | frame->pts - s->first_pts < s->duration_tb) |
||
179 | drop = 0; |
||
180 | |||
181 | if (drop) { |
||
182 | s->eof = inlink->closed = 1; |
||
183 | goto drop; |
||
184 | } |
||
185 | } |
||
186 | |||
187 | s->nb_frames++; |
||
188 | |||
189 | return ff_filter_frame(ctx->outputs[0], frame); |
||
190 | |||
191 | drop: |
||
192 | s->nb_frames++; |
||
193 | av_frame_free(&frame); |
||
194 | return 0; |
||
195 | } |
||
196 | |||
197 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM |
||
198 | static const AVOption trim_options[] = { |
||
199 | COMMON_OPTS |
||
200 | { "start_frame", "Number of the first frame that should be passed " |
||
201 | "to the output", OFFSET(start_frame), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, |
||
202 | { "end_frame", "Number of the first frame that should be dropped " |
||
203 | "again", OFFSET(end_frame), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS }, |
||
204 | COMPAT_OPTS |
||
205 | { NULL } |
||
206 | }; |
||
207 | #undef FLAGS |
||
208 | |||
209 | AVFILTER_DEFINE_CLASS(trim); |
||
210 | |||
211 | static const AVFilterPad trim_inputs[] = { |
||
212 | { |
||
213 | .name = "default", |
||
214 | .type = AVMEDIA_TYPE_VIDEO, |
||
215 | .filter_frame = trim_filter_frame, |
||
216 | .config_props = config_input, |
||
217 | }, |
||
218 | { NULL } |
||
219 | }; |
||
220 | |||
221 | static const AVFilterPad trim_outputs[] = { |
||
222 | { |
||
223 | .name = "default", |
||
224 | .type = AVMEDIA_TYPE_VIDEO, |
||
225 | .config_props = config_output, |
||
226 | }, |
||
227 | { NULL } |
||
228 | }; |
||
229 | |||
230 | AVFilter avfilter_vf_trim = { |
||
231 | .name = "trim", |
||
232 | .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), |
||
233 | .init = init, |
||
234 | .priv_size = sizeof(TrimContext), |
||
235 | .priv_class = &trim_class, |
||
236 | .inputs = trim_inputs, |
||
237 | .outputs = trim_outputs, |
||
238 | }; |
||
239 | #endif // CONFIG_TRIM_FILTER |
||
240 | |||
241 | #if CONFIG_ATRIM_FILTER |
||
242 | static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame) |
||
243 | { |
||
244 | AVFilterContext *ctx = inlink->dst; |
||
245 | TrimContext *s = ctx->priv; |
||
246 | int64_t start_sample, end_sample = frame->nb_samples; |
||
247 | int64_t pts; |
||
248 | int drop; |
||
249 | |||
250 | /* drop everything if EOF has already been returned */ |
||
251 | if (s->eof) { |
||
252 | av_frame_free(&frame); |
||
253 | return 0; |
||
254 | } |
||
255 | |||
256 | if (frame->pts != AV_NOPTS_VALUE) |
||
257 | pts = av_rescale_q(frame->pts, inlink->time_base, |
||
258 | (AVRational){ 1, inlink->sample_rate }); |
||
259 | else |
||
260 | pts = s->next_pts; |
||
261 | s->next_pts = pts + frame->nb_samples; |
||
262 | |||
263 | /* check if at least a part of the frame is after the start time */ |
||
264 | if (s->start_sample < 0 && s->start_pts == AV_NOPTS_VALUE) { |
||
265 | start_sample = 0; |
||
266 | } else { |
||
267 | drop = 1; |
||
268 | start_sample = frame->nb_samples; |
||
269 | |||
270 | if (s->start_sample >= 0 && |
||
271 | s->nb_samples + frame->nb_samples > s->start_sample) { |
||
272 | drop = 0; |
||
273 | start_sample = FFMIN(start_sample, s->start_sample - s->nb_samples); |
||
274 | } |
||
275 | |||
276 | if (s->start_pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && |
||
277 | pts + frame->nb_samples > s->start_pts) { |
||
278 | drop = 0; |
||
279 | start_sample = FFMIN(start_sample, s->start_pts - pts); |
||
280 | } |
||
281 | |||
282 | if (drop) |
||
283 | goto drop; |
||
284 | } |
||
285 | |||
286 | if (s->first_pts == AV_NOPTS_VALUE) |
||
287 | s->first_pts = pts + start_sample; |
||
288 | |||
289 | /* check if at least a part of the frame is before the end time */ |
||
290 | if (s->end_sample == INT64_MAX && s->end_pts == AV_NOPTS_VALUE && !s->duration_tb) { |
||
291 | end_sample = frame->nb_samples; |
||
292 | } else { |
||
293 | drop = 1; |
||
294 | end_sample = 0; |
||
295 | |||
296 | if (s->end_sample != INT64_MAX && |
||
297 | s->nb_samples < s->end_sample) { |
||
298 | drop = 0; |
||
299 | end_sample = FFMAX(end_sample, s->end_sample - s->nb_samples); |
||
300 | } |
||
301 | |||
302 | if (s->end_pts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && |
||
303 | pts < s->end_pts) { |
||
304 | drop = 0; |
||
305 | end_sample = FFMAX(end_sample, s->end_pts - pts); |
||
306 | } |
||
307 | |||
308 | if (s->duration_tb && pts - s->first_pts < s->duration_tb) { |
||
309 | drop = 0; |
||
310 | end_sample = FFMAX(end_sample, s->first_pts + s->duration_tb - pts); |
||
311 | } |
||
312 | |||
313 | if (drop) { |
||
314 | s->eof = inlink->closed = 1; |
||
315 | goto drop; |
||
316 | } |
||
317 | } |
||
318 | |||
319 | s->nb_samples += frame->nb_samples; |
||
320 | start_sample = FFMAX(0, start_sample); |
||
321 | end_sample = FFMIN(frame->nb_samples, end_sample); |
||
322 | av_assert0(start_sample < end_sample || (start_sample == end_sample && !frame->nb_samples)); |
||
323 | |||
324 | if (start_sample) { |
||
325 | AVFrame *out = ff_get_audio_buffer(ctx->outputs[0], end_sample - start_sample); |
||
326 | if (!out) { |
||
327 | av_frame_free(&frame); |
||
328 | return AVERROR(ENOMEM); |
||
329 | } |
||
330 | |||
331 | av_frame_copy_props(out, frame); |
||
332 | av_samples_copy(out->extended_data, frame->extended_data, 0, start_sample, |
||
333 | out->nb_samples, inlink->channels, |
||
334 | frame->format); |
||
335 | if (out->pts != AV_NOPTS_VALUE) |
||
336 | out->pts += av_rescale_q(start_sample, (AVRational){ 1, out->sample_rate }, |
||
337 | inlink->time_base); |
||
338 | |||
339 | av_frame_free(&frame); |
||
340 | frame = out; |
||
341 | } else |
||
342 | frame->nb_samples = end_sample; |
||
343 | |||
344 | return ff_filter_frame(ctx->outputs[0], frame); |
||
345 | |||
346 | drop: |
||
347 | s->nb_samples += frame->nb_samples; |
||
348 | av_frame_free(&frame); |
||
349 | return 0; |
||
350 | } |
||
351 | |||
352 | #define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM |
||
353 | static const AVOption atrim_options[] = { |
||
354 | COMMON_OPTS |
||
355 | { "start_sample", "Number of the first audio sample that should be " |
||
356 | "passed to the output", OFFSET(start_sample), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, |
||
357 | { "end_sample", "Number of the first audio sample that should be " |
||
358 | "dropped again", OFFSET(end_sample), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS }, |
||
359 | COMPAT_OPTS |
||
360 | { NULL } |
||
361 | }; |
||
362 | #undef FLAGS |
||
363 | |||
364 | AVFILTER_DEFINE_CLASS(atrim); |
||
365 | |||
366 | static const AVFilterPad atrim_inputs[] = { |
||
367 | { |
||
368 | .name = "default", |
||
369 | .type = AVMEDIA_TYPE_AUDIO, |
||
370 | .filter_frame = atrim_filter_frame, |
||
371 | .config_props = config_input, |
||
372 | }, |
||
373 | { NULL } |
||
374 | }; |
||
375 | |||
376 | static const AVFilterPad atrim_outputs[] = { |
||
377 | { |
||
378 | .name = "default", |
||
379 | .type = AVMEDIA_TYPE_AUDIO, |
||
380 | .config_props = config_output, |
||
381 | }, |
||
382 | { NULL } |
||
383 | }; |
||
384 | |||
385 | AVFilter avfilter_af_atrim = { |
||
386 | .name = "atrim", |
||
387 | .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), |
||
388 | .init = init, |
||
389 | .priv_size = sizeof(TrimContext), |
||
390 | .priv_class = &atrim_class, |
||
391 | .inputs = atrim_inputs, |
||
392 | .outputs = atrim_outputs, |
||
393 | }; |
||
394 | #endif // CONFIG_ATRIM_FILTER>>>>>>>>> |