Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PR] setts bsf: output tb changes (PR #21431)
@ 2026-01-11 13:24 Gyan Doshi via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Gyan Doshi via ffmpeg-devel @ 2026-01-11 13:24 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Gyan Doshi

PR #21431 opened by Gyan Doshi (GyanD)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21431
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21431.patch

Rescale time fields wrt output tb and add option for adjustment order


>From eff1d612e28164067333d07e75bb3e6b0884dec9 Mon Sep 17 00:00:00 2001
From: Gyan Doshi <ffmpeg@gyani.pro>
Date: Sun, 11 Jan 2026 16:48:32 +0530
Subject: [PATCH 1/2] avcodec/bsf/setts: rescale TS when changing TB

The setts bsf has an option to change TB. However the filter only
changed the TB and did not rescale the ts and duration, so it
effectively and silently stretched or squeezed the stream.

The pts, dts and duration are now rescaled to maintain temporal fidelity.
---
 libavcodec/bsf/setts.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/libavcodec/bsf/setts.c b/libavcodec/bsf/setts.c
index 9c27b24a39..86d6aafdf8 100644
--- a/libavcodec/bsf/setts.c
+++ b/libavcodec/bsf/setts.c
@@ -219,6 +219,12 @@ static int setts_filter(AVBSFContext *ctx, AVPacket *pkt)
     if (ret < 0)
         return ret;
 
+    if (ctx->time_base_out.den) {
+        new_pts = av_rescale_q(new_pts, ctx->time_base_in, ctx->time_base_out);
+        new_dts = av_rescale_q(new_dts, ctx->time_base_in, ctx->time_base_out);
+        new_duration = av_rescale_q(new_duration, ctx->time_base_in, ctx->time_base_out);
+    }
+
     pkt->pts = new_pts;
     pkt->dts = new_dts;
     pkt->duration = new_duration;
-- 
2.49.1


>From 8c31e09029d7538fe51bf37d7e1955c93a98a5c8 Mon Sep 17 00:00:00 2001
From: Gyan Doshi <ffmpeg@gyani.pro>
Date: Sun, 11 Jan 2026 18:05:33 +0530
Subject: [PATCH 2/2] avcodec/bsf/setts: add option prescale

When prescale is enabled, time fields are converted to the output
timebase before expression evaluation. This allows option specification
even if the input timebase is unknown.
---
 doc/bitstream_filters.texi |  7 ++---
 libavcodec/bsf/setts.c     | 57 +++++++++++++++++++++++++-------------
 2 files changed, 41 insertions(+), 23 deletions(-)

diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index fb31ca7380..549ca9ec45 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -913,6 +913,8 @@ Set expressions for PTS, DTS or both.
 Set expression for duration.
 @item time_base
 Set output time base.
+@item prescale
+Set whether to convert time fields to output time base before evaluation of expressions. Defaults to 0.
 @end table
 
 The expressions are evaluated through the eval API and can contain the following
@@ -973,10 +975,7 @@ The next input PTS.
 The next input duration.
 
 @item TB
-The timebase of stream packet belongs.
-
-@item TB_OUT
-The output timebase.
+The timebase in which time fields are denominated. The output timebase if prescale is enabled, else the input timebase.
 
 @item SR
 The sample rate of stream packet belongs.
diff --git a/libavcodec/bsf/setts.c b/libavcodec/bsf/setts.c
index 86d6aafdf8..cbaec01f35 100644
--- a/libavcodec/bsf/setts.c
+++ b/libavcodec/bsf/setts.c
@@ -48,7 +48,6 @@ static const char *const var_names[] = {
     "STARTPTS",    ///< PTS at start of movie
     "STARTDTS",    ///< DTS at start of movie
     "TB",          ///< input timebase of the stream
-    "TB_OUT",      ///< output timebase of the stream
     "SR",          ///< sample rate of the stream
     "NOPTS",       ///< The AV_NOPTS_VALUE constant
     NULL
@@ -73,7 +72,6 @@ enum var_name {
     VAR_STARTPTS,
     VAR_STARTDTS,
     VAR_TB,
-    VAR_TB_OUT,
     VAR_SR,
     VAR_NOPTS,
     VAR_VARS_NB
@@ -88,6 +86,7 @@ typedef struct SetTSContext {
     char *duration_str;
 
     AVRational time_base;
+    int prescale;
 
     int64_t frame_number;
 
@@ -144,15 +143,23 @@ static int setts_init(AVBSFContext *ctx)
 
     if (s->time_base.num > 0 && s->time_base.den > 0)
         ctx->time_base_out = s->time_base;
+    else if (s->time_base.num || s->time_base.den) {
+        av_log(ctx, AV_LOG_ERROR, "Invalid value %d/%d specified for output timebase\n", s->time_base.num, s->time_base.den);
+        return AVERROR_INVALIDDATA;
+    } else
+        s->prescale = 0;
 
     s->frame_number= 0;
     s->var_values[VAR_STARTPTS] = AV_NOPTS_VALUE;
     s->var_values[VAR_STARTDTS] = AV_NOPTS_VALUE;
     s->var_values[VAR_NOPTS] = AV_NOPTS_VALUE;
-    s->var_values[VAR_TB]    = ctx->time_base_in.den ? av_q2d(ctx->time_base_in) : 0;
-    s->var_values[VAR_TB_OUT]= ctx->time_base_out.den ? av_q2d(ctx->time_base_out) : 0;
     s->var_values[VAR_SR]    = ctx->par_in->sample_rate;
 
+    if (s->prescale)
+        s->var_values[VAR_TB] = av_q2d(ctx->time_base_out);
+    else
+        s->var_values[VAR_TB] = ctx->time_base_in.den ? av_q2d(ctx->time_base_in) : 0;
+
     return 0;
 }
 
@@ -160,6 +167,7 @@ static int setts_filter(AVBSFContext *ctx, AVPacket *pkt)
 {
     SetTSContext *s = ctx->priv_data;
     int64_t new_ts, new_pts, new_dts, new_duration;
+    int64_t prev_pts, prev_dts, prev_duration, cur_pts, cur_dts, cur_duration, next_pts, next_dts, next_duration;
     int ret;
 
     ret = ff_bsf_get_packet_ref(ctx, pkt);
@@ -171,40 +179,50 @@ static int setts_filter(AVBSFContext *ctx, AVPacket *pkt)
          return AVERROR(EAGAIN);
     }
 
+    prev_pts      = s->prescale ? av_rescale_q(s->prev_inpkt->pts, ctx->time_base_in, ctx->time_base_out) : s->prev_inpkt->pts;
+    prev_dts      = s->prescale ? av_rescale_q(s->prev_inpkt->dts, ctx->time_base_in, ctx->time_base_out) : s->prev_inpkt->dts;
+    prev_duration = s->prescale ? av_rescale_q(s->prev_inpkt->duration, ctx->time_base_in, ctx->time_base_out) : s->prev_inpkt->duration;
+    cur_pts       = s->prescale ? av_rescale_q(s->cur_pkt->pts, ctx->time_base_in, ctx->time_base_out) : s->cur_pkt->pts;
+    cur_dts       = s->prescale ? av_rescale_q(s->cur_pkt->dts, ctx->time_base_in, ctx->time_base_out) : s->cur_pkt->dts;
+    cur_duration  = s->prescale ? av_rescale_q(s->cur_pkt->duration, ctx->time_base_in, ctx->time_base_out) : s->cur_pkt->duration;
+    next_pts      = s->prescale ? av_rescale_q(pkt->pts, ctx->time_base_in, ctx->time_base_out) : pkt->pts;
+    next_dts      = s->prescale ? av_rescale_q(pkt->dts, ctx->time_base_in, ctx->time_base_out) : pkt->dts;
+    next_duration = s->prescale ? av_rescale_q(pkt->duration, ctx->time_base_in, ctx->time_base_out) : pkt->duration;
+
     if (s->var_values[VAR_STARTPTS] == AV_NOPTS_VALUE)
-        s->var_values[VAR_STARTPTS] = s->cur_pkt->pts;
+        s->var_values[VAR_STARTPTS] = cur_pts;
 
     if (s->var_values[VAR_STARTDTS] == AV_NOPTS_VALUE)
-        s->var_values[VAR_STARTDTS] = s->cur_pkt->dts;
+        s->var_values[VAR_STARTDTS] = cur_dts;
 
     s->var_values[VAR_N]           = s->frame_number++;
-    s->var_values[VAR_TS]          = s->cur_pkt->dts;
+    s->var_values[VAR_TS]          = cur_dts;
     s->var_values[VAR_POS]         = s->cur_pkt->pos;
-    s->var_values[VAR_PTS]         = s->cur_pkt->pts;
-    s->var_values[VAR_DTS]         = s->cur_pkt->dts;
-    s->var_values[VAR_DURATION]    = s->cur_pkt->duration;
-    s->var_values[VAR_PREV_INPTS]  = s->prev_inpkt->pts;
-    s->var_values[VAR_PREV_INDTS]  = s->prev_inpkt->dts;
-    s->var_values[VAR_PREV_INDUR]  = s->prev_inpkt->duration;
+    s->var_values[VAR_PTS]         = cur_pts;
+    s->var_values[VAR_DTS]         = cur_dts;
+    s->var_values[VAR_DURATION]    = cur_duration;
+    s->var_values[VAR_PREV_INPTS]  = prev_pts;
+    s->var_values[VAR_PREV_INDTS]  = prev_dts;
+    s->var_values[VAR_PREV_INDUR]  = prev_duration;
     s->var_values[VAR_PREV_OUTPTS] = s->prev_outpkt->pts;
     s->var_values[VAR_PREV_OUTDTS] = s->prev_outpkt->dts;
     s->var_values[VAR_PREV_OUTDUR] = s->prev_outpkt->duration;
-    s->var_values[VAR_NEXT_PTS]    = pkt->pts;
-    s->var_values[VAR_NEXT_DTS]    = pkt->dts;
-    s->var_values[VAR_NEXT_DUR]    = pkt->duration;
+    s->var_values[VAR_NEXT_PTS]    = next_pts;
+    s->var_values[VAR_NEXT_DTS]    = next_dts;
+    s->var_values[VAR_NEXT_DUR]    = next_duration;
 
     new_ts = llrint(av_expr_eval(s->ts_expr, s->var_values, NULL));
     new_duration = llrint(av_expr_eval(s->duration_expr, s->var_values, NULL));
 
     if (s->pts_str) {
-        s->var_values[VAR_TS] = s->cur_pkt->pts;
+        s->var_values[VAR_TS] = cur_pts;
         new_pts = llrint(av_expr_eval(s->pts_expr, s->var_values, NULL));
     } else {
         new_pts = new_ts;
     }
 
     if (s->dts_str) {
-        s->var_values[VAR_TS] = s->cur_pkt->dts;
+        s->var_values[VAR_TS] = cur_dts;
         new_dts = llrint(av_expr_eval(s->dts_expr, s->var_values, NULL));
     } else {
         new_dts = new_ts;
@@ -219,7 +237,7 @@ static int setts_filter(AVBSFContext *ctx, AVPacket *pkt)
     if (ret < 0)
         return ret;
 
-    if (ctx->time_base_out.den) {
+    if (ctx->time_base_out.den && !s->prescale) {
         new_pts = av_rescale_q(new_pts, ctx->time_base_in, ctx->time_base_out);
         new_dts = av_rescale_q(new_dts, ctx->time_base_in, ctx->time_base_out);
         new_duration = av_rescale_q(new_duration, ctx->time_base_in, ctx->time_base_out);
@@ -263,6 +281,7 @@ static const AVOption options[] = {
     { "dts", "set expression for packet DTS", OFFSET(dts_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
     { "duration", "set expression for packet duration", OFFSET(duration_str), AV_OPT_TYPE_STRING, {.str="DURATION"}, 0, 0, FLAGS },
     { "time_base", "set output timebase", OFFSET(time_base), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS },
+    { "prescale",  "convert to output timebase before evaluation", OFFSET(prescale), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
     { NULL },
 };
 
-- 
2.49.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-01-11 13:25 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-11 13:24 [FFmpeg-devel] [PR] setts bsf: output tb changes (PR #21431) Gyan Doshi via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git