Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Number of encoded frames does not match number of decoded frames #155

Open
j0sh opened this issue Sep 17, 2019 · 0 comments
Open

Number of encoded frames does not match number of decoded frames #155

j0sh opened this issue Sep 17, 2019 · 0 comments

Comments

@j0sh
Copy link
Collaborator

j0sh commented Sep 17, 2019

Under particular conditions, the number of encoded frames does not match number of decoded frames.

  • Reproducible case that encodes 61 "frames" (61 frames encoded and 61 packets muxed) but only decodes 60, as shown below.
  • FFProbe mirrors our numbers: it reads the same 61 packets in the bitstream, but only decodes 60 frames.
  • This only seems to happen with the MP4 muxer. The mpegts muxer works as expected.
  • Possible that this is a separate slice / field / etc but this doesn't seem likely.
  • If this is indeed a non-display frame of some sort : is there a way to detect this?

    lpms/ffmpeg/ffmpeg_test.go

    Lines 1117 to 1209 in d722afc

    func TestTranscoder_MismatchedEncodeDecode(t *testing.T) {
    // Encoded frame count does not match decoded frame count for mp4
    // Note this is not an issue for mpegts! (this is sanity checked)
    run, dir := setupTest(t)
    defer os.RemoveAll(dir)
    p144p60fps := P144p30fps16x9
    p144p60fps.Framerate = 60
    cmd := `
    set -eux
    cd $0
    # prepare 1-second input
    cp "$1/../transcoder/test.ts" inp.ts
    ffmpeg -loglevel warning -i inp.ts -c:a copy -c:v copy -t 1 test.ts
    `
    run(cmd)
    in := &TranscodeOptionsIn{Fname: dir + "/test.ts"}
    out := []TranscodeOptions{TranscodeOptions{Oname: dir + "/out.mp4", Profile: p144p60fps}}
    res, err := Transcode3(in, out)
    if err != nil {
    t.Error(err)
    }
    in.Fname = dir + "/out.mp4"
    res2, err := Transcode3(in, nil)
    if err != nil {
    t.Error(err)
    }
    // TODO Ideally these two should match. As far as I can tell it is due
    // to timestamp rounding around EOF. Note this does not happen with
    // mpegts formatted output!
    if res2.Decoded.Frames != 60 || res.Encoded[0].Frames != 61 {
    t.Error("Did not get expected frame counts: is this fixed?!? ",
    res2.Decoded.Frames, res.Encoded[0].Frames)
    }
    // Interestingly enough, ffmpeg shows that we have 61 packets but 60 frames.
    // That indicates we are receving, encoding and muxing 61 *things* but one
    // of them isn't a complete frame.
    cmd = `
    ffprobe -count_frames -show_packets -show_streams -select_streams v out.mp4 2>&1 > mp4.out
    grep nb_read_frames=60 mp4.out
    grep nb_read_packets=61 mp4.out
    `
    run(cmd)
    // Sanity check mpegts works as expected
    in.Fname = dir + "/test.ts"
    out[0].Oname = dir + "/out.ts"
    res, err = Transcode3(in, out)
    if err != nil {
    t.Error(err)
    }
    in.Fname = dir + "/out.ts"
    res2, err = Transcode3(in, nil)
    if err != nil {
    t.Error(err)
    }
    if res2.Decoded.Frames != 61 || res.Encoded[0].Frames != 61 {
    t.Error("Did not get expected frame counts for mpegts ",
    res2.Decoded.Frames, res.Encoded[0].Frames)
    }
    // Sanity check we still get the same results with multiple outputs?
    in.Fname = dir + "/test.ts"
    out = []TranscodeOptions{
    TranscodeOptions{Oname: dir + "/out2.ts", Profile: p144p60fps},
    TranscodeOptions{Oname: dir + "/out2.mp4", Profile: p144p60fps},
    }
    res, err = Transcode3(in, out)
    if err != nil {
    t.Error(err)
    }
    in.Fname = dir + "/out2.ts"
    res2, err = Transcode3(in, nil)
    if err != nil {
    t.Error(err)
    }
    in.Fname = dir + "/out2.mp4"
    res3, err := Transcode3(in, nil)
    if err != nil {
    t.Error(err)
    }
    // first output is mpegts
    if res2.Decoded.Frames != 61 || res.Encoded[0].Frames != 61 {
    t.Error("Sanity check of mpegts failed ", res2.Decoded.Frames, res.Encoded[0].Frames)
    }
    if res3.Decoded.Frames != 60 || res.Encoded[1].Frames != 61 {
    t.Error("Sanity check of mp4 failed ", res2.Decoded.Frames, res.Encoded[1].Frames)
    }
    }
jailuthra added a commit that referenced this issue May 6, 2020
Earlier we relied on getting a sentinel (pts -1) packet back to know
when the decoder is flushed. This does not work for short segments as
the decoder buffers packets internally.

Now we maintain the difference between packets sent - frames received
and mark decoder as flushed when this becomes 0 (usually).

Sometimes the decoder might not return all frames (see #155), so we give
up after sending SENTINEL_MAX packets and not getting any valid frame
back.
jailuthra added a commit that referenced this issue May 6, 2020
Earlier we relied on getting a sentinel (pts -1) packet back to know
when the decoder is flushed. This does not work for short segments as
the decoder buffers packets internally.

Now we maintain the difference between packets sent - frames received
and mark decoder as flushed when this becomes 0 (usually).

Sometimes the decoder might not return all frames (see #155), so we give
up after sending SENTINEL_MAX packets and not getting any valid frame
back.
jailuthra added a commit that referenced this issue May 15, 2020
Earlier we relied on getting a sentinel (pts -1) packet back to know
when the decoder is flushed. This does not work for short segments as
the decoder buffers packets internally.

Now we maintain the difference between packets sent - frames received
and mark decoder as flushed when this becomes 0 (usually).

Sometimes the decoder might not return all frames (see #155), so we give
up after sending SENTINEL_MAX packets and not getting any valid frame
back.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant