diff mbox

[FFmpeg-devel] lavf/mov.c: Guess video codec delay based on PTS while parsing MOV header.

Message ID 20171120162840.9779-1-isasi@google.com
State Superseded
Headers show

Commit Message

Sasi Inguva Nov. 20, 2017, 4:28 p.m. UTC
Signed-off-by: Sasi Inguva <isasi@google.com>
---
 libavformat/mov.c                | 55 ++++++++++++++++++++++++++++++++++++++++
 tests/fate/mov.mak               |  7 +++++
 tests/ref/fate/mov-guess-delay-1 |  3 +++
 tests/ref/fate/mov-guess-delay-2 |  3 +++
 tests/ref/fate/mov-guess-delay-3 |  3 +++
 5 files changed, 71 insertions(+)
 create mode 100644 tests/ref/fate/mov-guess-delay-1
 create mode 100644 tests/ref/fate/mov-guess-delay-2
 create mode 100644 tests/ref/fate/mov-guess-delay-3

Comments

Sasi Inguva Nov. 20, 2017, 4:33 p.m. UTC | #1
Attaching the fate sample

On Mon, Nov 20, 2017 at 8:28 AM, Sasi Inguva <isasi@google.com> wrote:

> Signed-off-by: Sasi Inguva <isasi@google.com>
> ---
>  libavformat/mov.c                | 55 ++++++++++++++++++++++++++++++
> ++++++++++
>  tests/fate/mov.mak               |  7 +++++
>  tests/ref/fate/mov-guess-delay-1 |  3 +++
>  tests/ref/fate/mov-guess-delay-2 |  3 +++
>  tests/ref/fate/mov-guess-delay-3 |  3 +++
>  5 files changed, 71 insertions(+)
>  create mode 100644 tests/ref/fate/mov-guess-delay-1
>  create mode 100644 tests/ref/fate/mov-guess-delay-2
>  create mode 100644 tests/ref/fate/mov-guess-delay-3
>
> diff --git a/libavformat/mov.c b/libavformat/mov.c
> index fd170baa57..d1bd0c4f29 100644
> --- a/libavformat/mov.c
> +++ b/libavformat/mov.c
> @@ -3213,6 +3213,59 @@ static int64_t add_ctts_entry(MOVStts** ctts_data,
> unsigned int* ctts_count, uns
>      return *ctts_count;
>  }
>
> +static void mov_guess_video_delay(MOVContext *c, AVStream* st) {
> +    MOVStreamContext *msc = st->priv_data;
> +    int ind;
> +    int ctts_ind = 0;
> +    int ctts_sample = 0;
> +    int64_t curr_pts = AV_NOPTS_VALUE;
> +    int64_t min_prev_pts = AV_NOPTS_VALUE;
> +    int64_t prev_max_pts = AV_NOPTS_VALUE;
> +    int num_steps = 0;
> +
> +    if (st->codecpar->video_delay <= 0 && msc->ctts_data &&
> +        (st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO ||
> +         st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
> +         st->codecpar->codec_id == AV_CODEC_ID_VC1 ||
> +         st->codecpar->codec_id == AV_CODEC_ID_H263 ||
> +         st->codecpar->codec_id == AV_CODEC_ID_H264 ||
> +         st->codecpar->codec_id == AV_CODEC_ID_HEVC)) {
> +        st->codecpar->video_delay = 0;
> +        for(ind = 0; ind < st->nb_index_entries && ctts_ind <
> msc->ctts_count; ++ind) {
> +            curr_pts = st->index_entries[ind].timestamp +
> msc->ctts_data[ctts_ind].duration;
> +
> +            // Everytime we encounter a new max_pts we reset num_steps
> and compute again.
> +            if (curr_pts > prev_max_pts) {
> +                st->codecpar->video_delay = FFMIN(FFMAX(st->codecpar->video_delay,
> num_steps), 16);
> +                num_steps = 0;
> +                prev_max_pts = curr_pts;
> +                min_prev_pts = curr_pts;
> +            } else {
> +                // Compute delay as the length of the path from max PTS
> to min PTS.
> +                // Frames: I0 I1 B0 B1 B2
> +                // PTS:     0  4  1  2  3 -> num_steps = delay = 1 (4->1)
> +                //
> +                // Frames: I0 I1 B1 B0 B2
> +                // PTS:     0  4  2  1  3 -> num_steps = delay = 2 (4->2,
> 2->1)
> +                if (min_prev_pts != AV_NOPTS_VALUE) {
> +                    if (curr_pts < min_prev_pts) {
> +                        ++num_steps;
> +                        min_prev_pts = curr_pts;
> +                    }
> +                }
> +            }
> +
> +            ctts_sample++;
> +            if (ctts_sample == msc->ctts_data[ctts_ind].count) {
> +                ctts_ind++;
> +                ctts_sample = 0;
> +            }
> +        }
> +        av_log(c->fc, AV_LOG_DEBUG, "Setting codecpar->delay to %d for
> stream st: %d\n",
> +               st->codecpar->video_delay, st->index);
> +    }
> +}
> +
>  static void mov_current_sample_inc(MOVStreamContext *sc)
>  {
>      sc->current_sample++;
> @@ -3846,6 +3899,8 @@ static void mov_build_index(MOVContext *mov,
> AVStream *st)
>          // Fix index according to edit lists.
>          mov_fix_index(mov, st);
>      }
> +
> +    mov_guess_video_delay(mov, st);
>  }
>
>  static int test_same_origin(const char *src, const char *ref) {
> diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
> index 76f66ff498..6e79f0aba8 100644
> --- a/tests/fate/mov.mak
> +++ b/tests/fate/mov.mak
> @@ -11,6 +11,9 @@ FATE_MOV = fate-mov-3elist \
>             fate-mov-440hz-10ms \
>             fate-mov-ibi-elst-starts-b \
>             fate-mov-elst-ends-betn-b-and-i \
> +           fate-mov-guess-delay-1 \
> +           fate-mov-guess-delay-2 \
> +           fate-mov-guess-delay-3 \
>
>  FATE_MOV_FFPROBE = fate-mov-aac-2048-priming \
>                     fate-mov-zombie \
> @@ -72,3 +75,7 @@ fate-mov-spherical-mono: CMD = run
> ffprobe$(PROGSSUF)$(EXESUF) -show_entries str
>  fate-mov-gpmf-remux: CMD = md5 -i $(TARGET_SAMPLES)/mov/fake-gp-media-with-real-gpmf.mp4
> -map 0 -c copy -fflags +bitexact -f mp4
>  fate-mov-gpmf-remux: CMP = oneline
>  fate-mov-gpmf-remux: REF = 8f48e435ee1f6b7e173ea756141eabf3
> +
> +fate-mov-guess-delay-1: CMD = run ffprobe$(PROGSSUF)$(EXESUF)
> -show_entries stream=has_b_frames -select_streams v
> $(TARGET_SAMPLES)/h264/h264_3bf_nopyramid_nobsrestriction.mp4
> +fate-mov-guess-delay-2: CMD = run ffprobe$(PROGSSUF)$(EXESUF)
> -show_entries stream=has_b_frames -select_streams v
> $(TARGET_SAMPLES)/h264/h264_3bf_pyramid_nobsrestriction.mp4
> +fate-mov-guess-delay-3: CMD = run ffprobe$(PROGSSUF)$(EXESUF)
> -show_entries stream=has_b_frames -select_streams v
> $(TARGET_SAMPLES)/h264/h264_4bf_pyramid_nobsrestriction.mp4
> \ No newline at end of file
> diff --git a/tests/ref/fate/mov-guess-delay-1
> b/tests/ref/fate/mov-guess-delay-1
> new file mode 100644
> index 0000000000..96cb67be0c
> --- /dev/null
> +++ b/tests/ref/fate/mov-guess-delay-1
> @@ -0,0 +1,3 @@
> +[STREAM]
> +has_b_frames=1
> +[/STREAM]
> diff --git a/tests/ref/fate/mov-guess-delay-2
> b/tests/ref/fate/mov-guess-delay-2
> new file mode 100644
> index 0000000000..248de1c3ea
> --- /dev/null
> +++ b/tests/ref/fate/mov-guess-delay-2
> @@ -0,0 +1,3 @@
> +[STREAM]
> +has_b_frames=2
> +[/STREAM]
> diff --git a/tests/ref/fate/mov-guess-delay-3
> b/tests/ref/fate/mov-guess-delay-3
> new file mode 100644
> index 0000000000..248de1c3ea
> --- /dev/null
> +++ b/tests/ref/fate/mov-guess-delay-3
> @@ -0,0 +1,3 @@
> +[STREAM]
> +has_b_frames=2
> +[/STREAM]
> --
> 2.15.0.448.gf294e3d99a-goog
>
>
diff mbox

Patch

diff --git a/libavformat/mov.c b/libavformat/mov.c
index fd170baa57..d1bd0c4f29 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -3213,6 +3213,59 @@  static int64_t add_ctts_entry(MOVStts** ctts_data, unsigned int* ctts_count, uns
     return *ctts_count;
 }
 
+static void mov_guess_video_delay(MOVContext *c, AVStream* st) {
+    MOVStreamContext *msc = st->priv_data;
+    int ind;
+    int ctts_ind = 0;
+    int ctts_sample = 0;
+    int64_t curr_pts = AV_NOPTS_VALUE;
+    int64_t min_prev_pts = AV_NOPTS_VALUE;
+    int64_t prev_max_pts = AV_NOPTS_VALUE;
+    int num_steps = 0;
+
+    if (st->codecpar->video_delay <= 0 && msc->ctts_data &&
+        (st->codecpar->codec_id == AV_CODEC_ID_MPEG2VIDEO ||
+         st->codecpar->codec_id == AV_CODEC_ID_MPEG4 ||
+         st->codecpar->codec_id == AV_CODEC_ID_VC1 ||
+         st->codecpar->codec_id == AV_CODEC_ID_H263 ||
+         st->codecpar->codec_id == AV_CODEC_ID_H264 ||
+         st->codecpar->codec_id == AV_CODEC_ID_HEVC)) {
+        st->codecpar->video_delay = 0;
+        for(ind = 0; ind < st->nb_index_entries && ctts_ind < msc->ctts_count; ++ind) {
+            curr_pts = st->index_entries[ind].timestamp + msc->ctts_data[ctts_ind].duration;
+
+            // Everytime we encounter a new max_pts we reset num_steps and compute again.
+            if (curr_pts > prev_max_pts) {
+                st->codecpar->video_delay = FFMIN(FFMAX(st->codecpar->video_delay, num_steps), 16);
+                num_steps = 0;
+                prev_max_pts = curr_pts;
+                min_prev_pts = curr_pts;
+            } else {
+                // Compute delay as the length of the path from max PTS to min PTS.
+                // Frames: I0 I1 B0 B1 B2
+                // PTS:     0  4  1  2  3 -> num_steps = delay = 1 (4->1)
+                //
+                // Frames: I0 I1 B1 B0 B2
+                // PTS:     0  4  2  1  3 -> num_steps = delay = 2 (4->2, 2->1)
+                if (min_prev_pts != AV_NOPTS_VALUE) {
+                    if (curr_pts < min_prev_pts) {
+                        ++num_steps;
+                        min_prev_pts = curr_pts;
+                    }
+                }
+            }
+
+            ctts_sample++;
+            if (ctts_sample == msc->ctts_data[ctts_ind].count) {
+                ctts_ind++;
+                ctts_sample = 0;
+            }
+        }
+        av_log(c->fc, AV_LOG_DEBUG, "Setting codecpar->delay to %d for stream st: %d\n",
+               st->codecpar->video_delay, st->index);
+    }
+}
+
 static void mov_current_sample_inc(MOVStreamContext *sc)
 {
     sc->current_sample++;
@@ -3846,6 +3899,8 @@  static void mov_build_index(MOVContext *mov, AVStream *st)
         // Fix index according to edit lists.
         mov_fix_index(mov, st);
     }
+
+    mov_guess_video_delay(mov, st);
 }
 
 static int test_same_origin(const char *src, const char *ref) {
diff --git a/tests/fate/mov.mak b/tests/fate/mov.mak
index 76f66ff498..6e79f0aba8 100644
--- a/tests/fate/mov.mak
+++ b/tests/fate/mov.mak
@@ -11,6 +11,9 @@  FATE_MOV = fate-mov-3elist \
            fate-mov-440hz-10ms \
            fate-mov-ibi-elst-starts-b \
            fate-mov-elst-ends-betn-b-and-i \
+           fate-mov-guess-delay-1 \
+           fate-mov-guess-delay-2 \
+           fate-mov-guess-delay-3 \
 
 FATE_MOV_FFPROBE = fate-mov-aac-2048-priming \
                    fate-mov-zombie \
@@ -72,3 +75,7 @@  fate-mov-spherical-mono: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries str
 fate-mov-gpmf-remux: CMD = md5 -i $(TARGET_SAMPLES)/mov/fake-gp-media-with-real-gpmf.mp4 -map 0 -c copy -fflags +bitexact -f mp4
 fate-mov-gpmf-remux: CMP = oneline
 fate-mov-gpmf-remux: REF = 8f48e435ee1f6b7e173ea756141eabf3
+
+fate-mov-guess-delay-1: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream=has_b_frames -select_streams v $(TARGET_SAMPLES)/h264/h264_3bf_nopyramid_nobsrestriction.mp4
+fate-mov-guess-delay-2: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream=has_b_frames -select_streams v $(TARGET_SAMPLES)/h264/h264_3bf_pyramid_nobsrestriction.mp4
+fate-mov-guess-delay-3: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -show_entries stream=has_b_frames -select_streams v $(TARGET_SAMPLES)/h264/h264_4bf_pyramid_nobsrestriction.mp4
\ No newline at end of file
diff --git a/tests/ref/fate/mov-guess-delay-1 b/tests/ref/fate/mov-guess-delay-1
new file mode 100644
index 0000000000..96cb67be0c
--- /dev/null
+++ b/tests/ref/fate/mov-guess-delay-1
@@ -0,0 +1,3 @@ 
+[STREAM]
+has_b_frames=1
+[/STREAM]
diff --git a/tests/ref/fate/mov-guess-delay-2 b/tests/ref/fate/mov-guess-delay-2
new file mode 100644
index 0000000000..248de1c3ea
--- /dev/null
+++ b/tests/ref/fate/mov-guess-delay-2
@@ -0,0 +1,3 @@ 
+[STREAM]
+has_b_frames=2
+[/STREAM]
diff --git a/tests/ref/fate/mov-guess-delay-3 b/tests/ref/fate/mov-guess-delay-3
new file mode 100644
index 0000000000..248de1c3ea
--- /dev/null
+++ b/tests/ref/fate/mov-guess-delay-3
@@ -0,0 +1,3 @@ 
+[STREAM]
+has_b_frames=2
+[/STREAM]