Skip to content

Instantly share code, notes, and snippets.

@namndev
Forked from tmm1/ffmpeg-timestamps.md
Created November 21, 2019 03:36
Show Gist options
  • Save namndev/f9ef28467cb979dcf184c2f59ccd8475 to your computer and use it in GitHub Desktop.
Save namndev/f9ef28467cb979dcf184c2f59ccd8475 to your computer and use it in GitHub Desktop.

I have a sample.mpg mpegts file with a video keyframe starting at 65738.512389

$ ffprobe -hide_banner -loglevel fatal sample.mpg -select_streams v -print_format json -show_packets -read_intervals "%+#1"
{
    "packets": [
        {
            "codec_type": "video",
            "stream_index": 0,
            "pts": 5916466115,
            "pts_time": "65738.512389",
            "dts": 5916454103,
            "dts_time": "65738.378922",
            "duration": 3003,
            "duration_time": "0.033367",
            "size": "185920",
            "pos": "65048",
            "flags": "K_",
            "side_data_list": [
                {
                    "side_data_type": "MPEGTS Stream ID"
                }
            ]
        }
    ]
}

If I remux with -copyts, I can get the exact same timestamp 65738.512389 in the new copy:

$ ffmpeg -hide_banner -loglevel fatal -copyts -i sample.mpg -map v -c copy -mpegts_copyts 1 -f mpegts -y /dev/stdout |
  ffprobe -hide_banner -loglevel fatal /dev/stdin -select_streams v -print_format json -show_packets -read_intervals "%+#1"
{
    "packets": [
        {
            "codec_type": "video",
            "stream_index": 0,
            "pts": 5916466115,
            "pts_time": "65738.512389",
            "dts": 5916454103,
            "dts_time": "65738.378922",
            "duration": 3003,
            "duration_time": "0.033367",
            "size": "185920",
            "pos": "564",
            "flags": "K_",
            "side_data_list": [
                {
                    "side_data_type": "MPEGTS Stream ID"
                }
            ]
        }
    ]
}

Now, if I transcode instead of the remux, the first timestamp decreases by 6ms for some reason to 65738.506167:

$ ffmpeg -hide_banner -loglevel fatal -copyts -i sample.mpg -map v -c mpeg2video -mpegts_copyts 1 -f mpegts -y /dev/stdout |
  ffprobe -hide_banner -loglevel fatal /dev/stdin -select_streams v -print_format json -show_packets -read_intervals "%+#1"
{
    "packets": [
        {
            "codec_type": "video",
            "stream_index": 0,
            "pts": 5916465555,
            "pts_time": "65738.506167",
            "dts": 5916462552,
            "dts_time": "65738.472800",
            "duration": 3003,
            "duration_time": "0.033367",
            "size": "82063",
            "pos": "564",
            "flags": "K_",
            "side_data_list": [
                {
                    "side_data_type": "MPEGTS Stream ID"
                }
            ]
        }
    ]
}

Using -debug_ts we can see the PTS wired through the demuxer and decoder is correct (5916466115):

demuxer -> ist_index:0 type:video next_dts:NOPTS next_dts_time:NOPTS next_pts:NOPTS next_pts_time:NOPTS pkt_pts:5916466115 pkt_pts_time:65738.5 pkt_dts:5916454103 pkt_dts_time:65738.4 off:0 off_time:0
demuxer+ffmpeg -> ist_index:0 type:video pkt_pts:5916466115 pkt_pts_time:65738.5 pkt_dts:5916454103 pkt_dts_time:65738.4 off:0 off_time:0
decoder -> ist_index:0 type:video frame_pts:5916466115 frame_pts_time:65738.5 best_effort_ts:5916466115 best_effort_ts_time:65738.5 keyframe:1 frame_type:1 time_base:1/90000

Then that's fed into the filter and encoder, but by the time it comes out of the encoder the timestamp has changed (to 5916465555):

filter -> pts:1970185 pts_time:65738.5 exact:1970185.186501 time_base:1001/30000
encoder <- type:video frame_pts:1970185 frame_pts_time:65738.5 time_base:1001/30000
encoder -> type:video pkt_pts:1970185 pkt_pts_time:65738.5 pkt_dts:1970184 pkt_dts_time:65738.5
encoder -> type:video pkt_pts:5916465555 pkt_pts_time:65738.5 pkt_dts:5916462552 pkt_dts_time:65738.5
muxer <- type:video pkt_pts:5916465555 pkt_pts_time:65738.5 pkt_dts:5916462552 pkt_dts_time:65738.5 size:82063

It appears the 6ms discrepency is caused by the loss of precision when converting to the encoder timebase and back:

> pts = 5916466115
=> 5916466115
> pts_time = pts / 90000.0
=> 65738.51238888889

> enc_pts = pts / 90000.0 / (1001.0 / 30000)
=> 1970185.1864801864
> enc_pts_time = enc_pts * 1001.0 / 30000
=> 65738.51238888888

> enc_pts_trunc = enc_pts.round
=> 1970185
> enc_pts_trunc_time = enc_pts_trunc * 1001.0 / 30000
=> 65738.50616666667

> enc_pts_trunc_time - enc_pts_time
=> -0.006222222204087302

Sure enough, if you use -enc_time_base -1 to ensure the input timebase is used, there is no loss of precision. Unfortunately the mpeg2video encoder doesn't accept 1/90k as a timebase, but most other encoders do:

$ ffmpeg -hide_banner -loglevel fatal -copyts -i sample.mpg -map v -c libx264 -enc_time_base -1 -mpegts_copyts 1 -f mpegts -y /dev/stdout |
  ffprobe -hide_banner -loglevel fatal /dev/stdin -select_streams v -print_format json -show_packets -read_intervals "%+#1"
{
    "packets": [
        {
            "codec_type": "video",
            "stream_index": 0,
            "pts": 5916466115,
            "pts_time": "65738.512389",
            "dts": 5916458608,
            "dts_time": "65738.428978",
            "size": "68467",
            "pos": "564",
            "flags": "K_",
            "side_data_list": [
                {
                    "side_data_type": "MPEGTS Stream ID"
                }
            ]
        }
    ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment