Subversion Repositories Kolibri OS

Rev

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