PR #25356 opened by sepro
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25356
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25356.patch

# Summary of changes

The timeline started at the PTS of the first video keyframe, and every packet 
presented before it was discarded. A GOP can begin with B-frames that are 
presented before its I-frame, for example a closed GOP coded as I2 B0 B1. On 
some discs, these frames and the audio presented with them were lost at the 
start of titles and menus.

When the first keyframe is presented within the first VOBU, start the timeline 
at that VOBU's vobu_s_ptm instead.

Here is an example output from master showing the issue:
```
$ ffprobe -v trace -f dvdvideo -title 7 -i DVD03.iso 2>&1 | grep -E "NAV 
packet|first_pts=|Discarding frame @ st=0"
[dvdvideo @ 0x5631aecbccc0] NAV packet: s_ptm=126889200 e_ptm=126932400 
scr=126861174 lbn=601474 vobu_duration=43200 nav_pts=0
[dvdvideo @ 0x5631aecbccc0] st=0 pts=0 dts=-10800 pts_offset=0 
first_pts=126896400
[dvdvideo @ 0x5631aecbccc0] Discarding frame @ st=0 pts=-7200 dts=-7200 
is_key=0 st_mapped=1
[dvdvideo @ 0x5631aecbccc0] Discarding frame @ st=0 pts=-3600 dts=-3600 
is_key=0 st_mapped=1
[dvdvideo @ 0x5631aecbccc0] st=0 pts=10800 dts=0 pts_offset=0 
first_pts=126896400
[dvdvideo @ 0x5631aecbccc0] st=0 pts=3600 dts=3600 pts_offset=0 
first_pts=126896400
[dvdvideo @ 0x5631aecbccc0] st=0 pts=7200 dts=7200 pts_offset=0 
first_pts=126896400
...
```

The two B-frames presented before the I-frame are discarded because first_pts 
is the PTS of the I-frame rather than the start PTM of the VOBU.

After applying the patch, the output is:
```
[dvdvideo @ 0x15faa900] NAV packet: s_ptm=126889200 e_ptm=126932400 
scr=126861174 lbn=601474 vobu_duration=43200 nav_pts=0
[dvdvideo @ 0x15faa900] st=0 pts=7200 dts=-3600 pts_offset=0 first_pts=126889200
[dvdvideo @ 0x15faa900] st=0 pts=0 dts=0 pts_offset=0 first_pts=126889200
[dvdvideo @ 0x15faa900] st=0 pts=3600 dts=3600 pts_offset=0 first_pts=126889200
[dvdvideo @ 0x15faa900] st=0 pts=18000 dts=7200 pts_offset=0 first_pts=126889200
[dvdvideo @ 0x15faa900] st=0 pts=10800 dts=10800 pts_offset=0 
first_pts=126889200
[dvdvideo @ 0x15faa900] st=0 pts=14400 dts=14400 pts_offset=0 
first_pts=126889200
...
```

I did not upload any samples, as the affected discs are commercial DVDs. If 
this is required, I can privately share them.


>From 04017f9d2efcb83aab554dbdb043c852050981c7 Mon Sep 17 00:00:00 2001
From: sepro <[email protected]>
Date: Tue, 20 Oct 2026 17:58:50 +0200
Subject: [PATCH] avformat/dvdvideodec: start the timeline at the first VOBU's
 start PTM

The timeline started at the PTS of the first video keyframe, and every
packet presented before it was discarded. A GOP can begin with B-frames
that are presented before its I-frame, for example a closed GOP coded as
I2 B0 B1. On some discs, these frames and the audio presented with them
were lost at the start of titles and menus.

When the first keyframe is presented within the first VOBU, start the
timeline at that VOBU's vobu_s_ptm instead.

Signed-off-by: sepro <[email protected]>
---
 libavformat/dvdvideodec.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/libavformat/dvdvideodec.c b/libavformat/dvdvideodec.c
index cf4c7962df7..d07f5bb756f 100644
--- a/libavformat/dvdvideodec.c
+++ b/libavformat/dvdvideodec.c
@@ -121,6 +121,8 @@ typedef struct DVDVideoPlaybackState {
     int                         ptt;                /* ID of the chapter we 
are in now */
     uint32_t                    vobu_duration;      /* duration of the current 
VOBU */
     uint32_t                    vobu_e_ptm;         /* end PTM of the current 
VOBU */
+    uint32_t                    ps_start_s_ptm;     /* start PTM of the first 
VOBU */
+    uint32_t                    ps_start_e_ptm;     /* end PTM of the first 
VOBU */
     int                         vtsn;               /* ID of the active VTS 
(video title set) */
     uint64_t                    *pgc_pg_times_est;  /* PG start times as 
reported by IFO */
     pgc_t                       *pgc;               /* handle to the active 
PGC */
@@ -164,7 +166,7 @@ typedef struct DVDVideoDemuxContext {
     ifo_handle_t                *vts_ifo;           /* handle to the active 
VTS (VTS_nn_n.IFO) */
 
     /* playback control */
-    int64_t                     first_pts;          /* the PTS of the first 
video keyframe */
+    int64_t                     first_pts;          /* PTS of the timeline 
origin */
     int                         nb_angles;          /* number of angles in the 
current title */
     int                         play_started;       /* signal that playback 
has started */
     DVDVideoPlaybackState       play_state;         /* the active playback 
state */
@@ -482,8 +484,10 @@ static int dvdvideo_menu_next_ps_block(AVFormatContext *s, 
DVDVideoPlaybackState
                 state->ptm_offset  += state->vobu_e_ptm - 
pci.pci_gi.vobu_s_ptm;
             }
         } else {
-            state->in_pgc        = 1;
-            state->in_ps         = 1;
+            state->in_pgc         = 1;
+            state->in_ps          = 1;
+            state->ps_start_s_ptm = pci.pci_gi.vobu_s_ptm;
+            state->ps_start_e_ptm = pci.pci_gi.vobu_e_ptm;
         }
 
         state->vobu_e_ptm        = pci.pci_gi.vobu_e_ptm;
@@ -799,7 +803,9 @@ static int dvdvideo_play_next_ps_block(AVFormatContext *s, 
DVDVideoPlaybackState
 
                     av_log(s, AV_LOG_DEBUG, "navigation: locked to program 
stream\n");
 
-                    state->in_ps = 1;
+                    state->in_ps          = 1;
+                    state->ps_start_s_ptm = e_pci->pci_gi.vobu_s_ptm;
+                    state->ps_start_e_ptm = e_pci->pci_gi.vobu_e_ptm;
                 } else {
                     if (state->vobu_e_ptm != e_pci->pci_gi.vobu_s_ptm) {
                         state->ptm_discont  = 1;
@@ -1682,7 +1688,14 @@ static int dvdvideo_read_packet(AVFormatContext *s, 
AVPacket *pkt)
         if (st_subdemux->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || !is_key)
             goto discard;
 
-        c->first_pts = pkt->pts;
+        /* the GOP may begin with B-frames that are presented before the 
keyframe
+         * (e.g. I2 B0 B1), so begin at the start PTM of the VOBU if the 
keyframe
+         * is presented within it, and at the keyframe otherwise */
+        if (c->play_state.ps_start_s_ptm <= pkt->pts && pkt->pts < 
c->play_state.ps_start_e_ptm)
+            c->first_pts = c->play_state.ps_start_s_ptm;
+        else
+            c->first_pts = pkt->pts;
+
         c->play_started = 1;
     }
 
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to