FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/bsf/setts.c
Date: 2026-03-07 22:03:05
Exec Total Coverage
Lines: 85 107 79.4%
Functions: 3 3 100.0%
Branches: 46 86 53.5%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2021 Paul B Mahol
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
8 * License 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /**
22 * @file
23 * Change the PTS/DTS timestamps.
24 */
25
26 #include "libavutil/opt.h"
27 #include "libavutil/eval.h"
28
29 #include "bsf.h"
30 #include "bsf_internal.h"
31
32 static const char *const var_names[] = {
33 "N", ///< frame number (starting at zero)
34 "TS",
35 "POS", ///< original position in the file of the frame
36 "PREV_INPTS", ///< previous input PTS
37 "PREV_INDTS", ///< previous input DTS
38 "PREV_INDURATION", ///< previous input duration
39 "PREV_OUTPTS", ///< previous output PTS
40 "PREV_OUTDTS", ///< previous output DTS
41 "PREV_OUTDURATION", ///< previous output duration
42 "NEXT_PTS", ///< next input PTS
43 "NEXT_DTS", ///< next input DTS
44 "NEXT_DURATION", ///< next input duration
45 "PTS", ///< original PTS in the file of the frame
46 "DTS", ///< original DTS in the file of the frame
47 "DURATION", ///< original duration in the file of the frame
48 "STARTPTS", ///< PTS at start of movie
49 "STARTDTS", ///< DTS at start of movie
50 "TB", ///< input timebase of the stream
51 "TB_OUT", ///< output timebase of the stream
52 "SR", ///< sample rate of the stream
53 "NOPTS", ///< The AV_NOPTS_VALUE constant
54 NULL
55 };
56
57 enum var_name {
58 VAR_N,
59 VAR_TS,
60 VAR_POS,
61 VAR_PREV_INPTS,
62 VAR_PREV_INDTS,
63 VAR_PREV_INDUR,
64 VAR_PREV_OUTPTS,
65 VAR_PREV_OUTDTS,
66 VAR_PREV_OUTDUR,
67 VAR_NEXT_PTS,
68 VAR_NEXT_DTS,
69 VAR_NEXT_DUR,
70 VAR_PTS,
71 VAR_DTS,
72 VAR_DURATION,
73 VAR_STARTPTS,
74 VAR_STARTDTS,
75 VAR_TB,
76 VAR_TB_OUT,
77 VAR_SR,
78 VAR_NOPTS,
79 VAR_VARS_NB
80 };
81
82 typedef struct SetTSContext {
83 const AVClass *class;
84
85 char *ts_str;
86 char *pts_str;
87 char *dts_str;
88 char *duration_str;
89
90 AVRational time_base;
91 int user_outtb;
92 int prescale;
93
94 int64_t frame_number;
95
96 double var_values[VAR_VARS_NB];
97
98 AVExpr *ts_expr;
99 AVExpr *pts_expr;
100 AVExpr *dts_expr;
101 AVExpr *duration_expr;
102
103 AVPacket *prev_inpkt;
104 AVPacket *prev_outpkt;
105 AVPacket *cur_pkt;
106 } SetTSContext;
107
108 6 static int setts_init(AVBSFContext *ctx)
109 {
110 6 SetTSContext *s = ctx->priv_data;
111 int ret;
112
113 6 s->prev_inpkt = av_packet_alloc();
114 6 s->prev_outpkt = av_packet_alloc();
115 6 s->cur_pkt = av_packet_alloc();
116
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
6 if (!s->prev_inpkt || !s->prev_outpkt || !s->cur_pkt)
117 return AVERROR(ENOMEM);
118
119
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if ((ret = av_expr_parse(&s->ts_expr, s->ts_str,
120 var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
121 av_log(ctx, AV_LOG_ERROR, "Error while parsing ts expression '%s'\n", s->ts_str);
122 return ret;
123 }
124
125
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if ((ret = av_expr_parse(&s->duration_expr, s->duration_str,
126 var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
127 av_log(ctx, AV_LOG_ERROR, "Error while parsing duration expression '%s'\n", s->duration_str);
128 return ret;
129 }
130
131
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 if (s->pts_str) {
132
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if ((ret = av_expr_parse(&s->pts_expr, s->pts_str,
133 var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
134 av_log(ctx, AV_LOG_ERROR, "Error while parsing pts expression '%s'\n", s->pts_str);
135 return ret;
136 }
137 }
138
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (s->dts_str) {
140 if ((ret = av_expr_parse(&s->dts_expr, s->dts_str,
141 var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
142 av_log(ctx, AV_LOG_ERROR, "Error while parsing dts expression '%s'\n", s->dts_str);
143 return ret;
144 }
145 }
146
147
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 if (s->time_base.num > 0 && s->time_base.den > 0) {
148 ctx->time_base_out = s->time_base;
149 s->user_outtb = 1;
150
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 } else if (s->time_base.num || !s->time_base.den) {
151 av_log(ctx, AV_LOG_ERROR, "Invalid value %d/%d specified for output timebase\n", s->time_base.num, s->time_base.den);
152 return AVERROR_INVALIDDATA;
153 } else
154 6 s->prescale = 0;
155
156 6 s->frame_number= 0;
157 6 s->var_values[VAR_STARTPTS] = AV_NOPTS_VALUE;
158 6 s->var_values[VAR_STARTDTS] = AV_NOPTS_VALUE;
159 6 s->var_values[VAR_NOPTS] = AV_NOPTS_VALUE;
160
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 s->var_values[VAR_TB_OUT]= ctx->time_base_out.den ? av_q2d(ctx->time_base_out) : 0;
161 6 s->var_values[VAR_SR] = ctx->par_in->sample_rate;
162
163
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6 if (s->user_outtb && s->prescale)
164 s->var_values[VAR_TB] = av_q2d(ctx->time_base_out);
165 else
166
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 s->var_values[VAR_TB] = ctx->time_base_in.den ? av_q2d(ctx->time_base_in) : 0;
167
168 6 return 0;
169 }
170
171 #define PRESCALED(x) ( s->prescale ? av_rescale_q(x, ctx->time_base_in, ctx->time_base_out) : x )
172
173 212 static int setts_filter(AVBSFContext *ctx, AVPacket *pkt)
174 {
175 212 SetTSContext *s = ctx->priv_data;
176 int64_t new_ts, new_pts, new_dts, new_duration;
177 int ret;
178
179 212 ret = ff_bsf_get_packet_ref(ctx, pkt);
180
6/6
✓ Branch 0 taken 110 times.
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 94 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 8 times.
212 if (ret < 0 && (ret != AVERROR_EOF || !s->cur_pkt->data))
181 102 return ret;
182
183
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 102 times.
110 if (!s->cur_pkt->data) {
184 8 av_packet_move_ref(s->cur_pkt, pkt);
185 8 return AVERROR(EAGAIN);
186 }
187
188
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 71 times.
102 if (s->var_values[VAR_STARTPTS] == AV_NOPTS_VALUE)
189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 s->var_values[VAR_STARTPTS] = PRESCALED(s->cur_pkt->pts);
190
191
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 96 times.
102 if (s->var_values[VAR_STARTDTS] == AV_NOPTS_VALUE)
192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 s->var_values[VAR_STARTDTS] = PRESCALED(s->cur_pkt->dts);
193
194 102 s->var_values[VAR_N] = s->frame_number++;
195
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_TS] = PRESCALED(s->cur_pkt->dts);
196 102 s->var_values[VAR_POS] = s->cur_pkt->pos;
197
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_PTS] = PRESCALED(s->cur_pkt->pts);
198
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_DTS] = PRESCALED(s->cur_pkt->dts);
199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_DURATION] = PRESCALED(s->cur_pkt->duration);
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_PREV_INPTS] = PRESCALED(s->prev_inpkt->pts);
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_PREV_INDTS] = PRESCALED(s->prev_inpkt->dts);
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_PREV_INDUR] = PRESCALED(s->prev_inpkt->duration);
203 102 s->var_values[VAR_PREV_OUTPTS] = s->prev_outpkt->pts;
204 102 s->var_values[VAR_PREV_OUTDTS] = s->prev_outpkt->dts;
205 102 s->var_values[VAR_PREV_OUTDUR] = s->prev_outpkt->duration;
206
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_NEXT_PTS] = PRESCALED(pkt->pts);
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_NEXT_DTS] = PRESCALED(pkt->dts);
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 s->var_values[VAR_NEXT_DUR] = PRESCALED(pkt->duration);
209
210 102 new_ts = llrint(av_expr_eval(s->ts_expr, s->var_values, NULL));
211 102 new_duration = llrint(av_expr_eval(s->duration_expr, s->var_values, NULL));
212
213
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 52 times.
102 if (s->pts_str) {
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 s->var_values[VAR_TS] = PRESCALED(s->cur_pkt->pts);
215 50 new_pts = llrint(av_expr_eval(s->pts_expr, s->var_values, NULL));
216 } else {
217 52 new_pts = new_ts;
218 }
219
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (s->dts_str) {
221 s->var_values[VAR_TS] = PRESCALED(s->cur_pkt->dts);
222 new_dts = llrint(av_expr_eval(s->dts_expr, s->var_values, NULL));
223 } else {
224 102 new_dts = new_ts;
225 }
226
227 102 av_packet_unref(s->prev_inpkt);
228 102 av_packet_unref(s->prev_outpkt);
229 102 av_packet_move_ref(s->prev_inpkt, s->cur_pkt);
230 102 av_packet_move_ref(s->cur_pkt, pkt);
231
232 102 ret = av_packet_ref(pkt, s->prev_inpkt);
233
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (ret < 0)
234 return ret;
235
236
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
102 if (s->user_outtb && !s->prescale) {
237 new_pts = av_rescale_q(new_pts, ctx->time_base_in, ctx->time_base_out);
238 new_dts = av_rescale_q(new_dts, ctx->time_base_in, ctx->time_base_out);
239 new_duration = av_rescale_q(new_duration, ctx->time_base_in, ctx->time_base_out);
240 }
241
242 102 pkt->pts = new_pts;
243 102 pkt->dts = new_dts;
244 102 pkt->duration = new_duration;
245
246 102 ret = av_packet_ref(s->prev_outpkt, pkt);
247
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
102 if (ret < 0)
248 av_packet_unref(pkt);
249
250 102 return ret;
251 }
252
253 6 static void setts_close(AVBSFContext *bsf)
254 {
255 6 SetTSContext *s = bsf->priv_data;
256
257 6 av_packet_free(&s->prev_inpkt);
258 6 av_packet_free(&s->prev_outpkt);
259 6 av_packet_free(&s->cur_pkt);
260
261 6 av_expr_free(s->ts_expr);
262 6 s->ts_expr = NULL;
263 6 av_expr_free(s->pts_expr);
264 6 s->pts_expr = NULL;
265 6 av_expr_free(s->dts_expr);
266 6 s->dts_expr = NULL;
267 6 av_expr_free(s->duration_expr);
268 6 s->duration_expr = NULL;
269 6 }
270
271 #define OFFSET(x) offsetof(SetTSContext, x)
272 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_SUBTITLE_PARAM|AV_OPT_FLAG_BSF_PARAM)
273
274 static const AVOption options[] = {
275 { "ts", "set expression for packet PTS and DTS", OFFSET(ts_str), AV_OPT_TYPE_STRING, {.str="TS"}, 0, 0, FLAGS },
276 { "pts", "set expression for packet PTS", OFFSET(pts_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
277 { "dts", "set expression for packet DTS", OFFSET(dts_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
278 { "duration", "set expression for packet duration", OFFSET(duration_str), AV_OPT_TYPE_STRING, {.str="DURATION"}, 0, 0, FLAGS },
279 { "time_base", "set output timebase", OFFSET(time_base), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS },
280 { "prescale", "convert to output timebase before evaluation", OFFSET(prescale), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
281 { NULL },
282 };
283
284 static const AVClass setts_class = {
285 .class_name = "setts_bsf",
286 .item_name = av_default_item_name,
287 .option = options,
288 .version = LIBAVUTIL_VERSION_INT,
289 };
290
291 const FFBitStreamFilter ff_setts_bsf = {
292 .p.name = "setts",
293 .p.priv_class = &setts_class,
294 .priv_data_size = sizeof(SetTSContext),
295 .init = setts_init,
296 .close = setts_close,
297 .filter = setts_filter,
298 };
299