blob: 171c16aaaa3ca815c87971eca421bd655269374c
1 | /* |
2 | * Copyright (c) 2015 Nicolas George |
3 | * |
4 | * This file is part of FFmpeg. |
5 | * |
6 | * FFmpeg is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public License |
8 | * as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * FFmpeg is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public License |
17 | * along with FFmpeg; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | */ |
20 | |
21 | #include "libavutil/opt.h" |
22 | #include "libavutil/time.h" |
23 | #include "avfilter.h" |
24 | #include "internal.h" |
25 | |
26 | typedef struct RealtimeContext { |
27 | const AVClass *class; |
28 | int64_t delta; |
29 | int64_t limit; |
30 | unsigned inited; |
31 | } RealtimeContext; |
32 | |
33 | static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
34 | { |
35 | AVFilterContext *ctx = inlink->dst; |
36 | RealtimeContext *s = ctx->priv; |
37 | |
38 | if (frame->pts != AV_NOPTS_VALUE) { |
39 | int64_t pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q); |
40 | int64_t now = av_gettime_relative(); |
41 | int64_t sleep = pts - now + s->delta; |
42 | if (!s->inited) { |
43 | s->inited = 1; |
44 | sleep = 0; |
45 | s->delta = now - pts; |
46 | } |
47 | if (sleep > s->limit || sleep < -s->limit) { |
48 | av_log(ctx, AV_LOG_WARNING, |
49 | "time discontinuity detected: %"PRIi64" us, resetting\n", |
50 | sleep); |
51 | sleep = 0; |
52 | s->delta = now - pts; |
53 | } |
54 | if (sleep > 0) { |
55 | av_log(ctx, AV_LOG_DEBUG, "sleeping %"PRIi64" us\n", sleep); |
56 | for (; sleep > 600000000; sleep -= 600000000) |
57 | av_usleep(600000000); |
58 | av_usleep(sleep); |
59 | } |
60 | } |
61 | return ff_filter_frame(inlink->dst->outputs[0], frame); |
62 | } |
63 | |
64 | #define OFFSET(x) offsetof(RealtimeContext, x) |
65 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM |
66 | static const AVOption options[] = { |
67 | { "limit", "sleep time limit", OFFSET(limit), AV_OPT_TYPE_DURATION, { .i64 = 2000000 }, 0, INT64_MAX, FLAGS }, |
68 | { NULL } |
69 | }; |
70 | |
71 | #if CONFIG_REALTIME_FILTER |
72 | #define realtime_options options |
73 | AVFILTER_DEFINE_CLASS(realtime); |
74 | |
75 | static const AVFilterPad avfilter_vf_realtime_inputs[] = { |
76 | { |
77 | .name = "default", |
78 | .type = AVMEDIA_TYPE_VIDEO, |
79 | .filter_frame = filter_frame, |
80 | }, |
81 | { NULL } |
82 | }; |
83 | |
84 | static const AVFilterPad avfilter_vf_realtime_outputs[] = { |
85 | { |
86 | .name = "default", |
87 | .type = AVMEDIA_TYPE_VIDEO, |
88 | }, |
89 | { NULL } |
90 | }; |
91 | |
92 | AVFilter ff_vf_realtime = { |
93 | .name = "realtime", |
94 | .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."), |
95 | .priv_size = sizeof(RealtimeContext), |
96 | .priv_class = &realtime_class, |
97 | .inputs = avfilter_vf_realtime_inputs, |
98 | .outputs = avfilter_vf_realtime_outputs, |
99 | }; |
100 | #endif /* CONFIG_REALTIME_FILTER */ |
101 | |
102 | #if CONFIG_AREALTIME_FILTER |
103 | |
104 | #define arealtime_options options |
105 | AVFILTER_DEFINE_CLASS(arealtime); |
106 | |
107 | static const AVFilterPad arealtime_inputs[] = { |
108 | { |
109 | .name = "default", |
110 | .type = AVMEDIA_TYPE_AUDIO, |
111 | .filter_frame = filter_frame, |
112 | }, |
113 | { NULL } |
114 | }; |
115 | |
116 | static const AVFilterPad arealtime_outputs[] = { |
117 | { |
118 | .name = "default", |
119 | .type = AVMEDIA_TYPE_AUDIO, |
120 | }, |
121 | { NULL } |
122 | }; |
123 | |
124 | AVFilter ff_af_arealtime = { |
125 | .name = "arealtime", |
126 | .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."), |
127 | .priv_size = sizeof(RealtimeContext), |
128 | .priv_class = &arealtime_class, |
129 | .inputs = arealtime_inputs, |
130 | .outputs = arealtime_outputs, |
131 | }; |
132 | #endif /* CONFIG_AREALTIME_FILTER */ |
133 |