[FFmpeg-devel] [PATCH] H.264 timestamps

Ivan Schreter schreter
Tue Feb 17 17:47:28 CET 2009


Ivan Schreter wrote:
> Ivan Schreter wrote:
>> attached three patches to get/compute some timestamp-relevant 
>> information from H.264 streams as a prerequisite for correct H.264 
>> timestamp computation (comes later).
>>
> I've now finished the code for computation of PTS/DTS for H.264 
> correctly (according to H.264 standard). I'll post the timestamp code 
> later (it is not as complex as I thought), as it requires parser 
> patch, which has to be cleaned up and reviewed first (there are still 
> some deficiencies).

Here the first version of H.264 timestamp computation routine in 
h264_parser.c for pre-review (depends on parsing patches being reviewed 
in another ML thread and patches #1-#7 posted in last post in this thread):

+/**
+ * Compute timestamp of currently parsed frame.
+ *
+ * @param s parser context
+ */
+static inline void h264_compute_timestamp(AVCodecParserContext *s)
+{
+    H264Context *h = s->priv_data;
+    int cpb_removal_delay = h->sei_cpb_removal_delay;
+    int duration = 1;   // in ticks
+    int64_t pts;
+    int64_t in_dts = s->dts, in_pts = s->pts;
+
+    /*
+     * H.264 C.1.2 Timing of coded picture removal (equivalent to DTS):
+     *   Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000
+     *   Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n)
+     * where
+     *   Tc = num_units_in_tick / time_scale
+     */
+
+    if (h->sps.timing_info_present_flag) {
+        // update clock Tc
+        h->ts_tc = (AVRational) {h->sps.num_units_in_tick, 
h->sps.time_scale};
+        if(h->x264_build > 0 && h->x264_build < 44)
+            h->ts_tc.den *= 2;
+    }
+
+    if (h->ts_tc.den == 0) {
+        // cannot compute timestamps as no timing info present
+        return;
+    }
+
+    if (h->ts_trn_nb < 0) {
+        // set initial timestamp
+        if (h->sps.nal_hrd_parameters_present_flag || 
h->sps.vcl_hrd_parameters_present_flag) {
+            h->ts_trn_nb = h->initial_cpb_removal_delay[h->sched_sel_idx];
+        } else {
+            // no initial TS, use 0
+            h->ts_trn_nb = 0;
+        }
+        cpb_removal_delay = 0;
+    }
+
+    if (s->dts != AV_NOPTS_VALUE) {
+        /*
+         * Each H.264 buffering period starts own sequence of timestamps.
+         * These are then computed contiguously for following buffering
+         * periods using given formulas above. When decoding from the
+         * beginning, the timestamps actually match those of the container
+         * (e.g., MPEG-TS) almost perfectly (with only small offset of 
~1ms).
+         *
+         * However, after a seek, when we start decoding in the middle 
of the
+         * stream, we don't have a timestamp history. So H.264 would start
+         * generating different (offset) timestamps.
+         *
+         * We want to actually generate timestamps in sync with the
+         * container format. Therefore, if the container does provide a DTS
+         * for the frame, adjust Trn(nb) timestamp to match timing of the
+         * container. Timestamp deltas are then computed using above 
mentioned
+         * H.264 formula.
+         */
+        if (h->sei_buffering_period_present_flag) {
+            cpb_removal_delay = 0;
+            h->ts_trn_nb = s->dts;
+        } else if (cpb_removal_delay >= 0) {
+            h->ts_trn_nb = s->dts -
+                cpb_removal_delay * h->ts_tc.num * 90000 / h->ts_tc.den;
+        }
+    }
+
+    if (h->sps.pic_struct_present_flag) {
+        switch (h->sei_pic_struct) {
+            case SEI_PIC_STRUCT_TOP_FIELD:
+            case SEI_PIC_STRUCT_BOTTOM_FIELD:
+                duration = 1;
+                break;
+            case SEI_PIC_STRUCT_FRAME:
+            case SEI_PIC_STRUCT_TOP_BOTTOM:
+            case SEI_PIC_STRUCT_BOTTOM_TOP:
+                duration = 2;
+                break;
+            case SEI_PIC_STRUCT_TOP_BOTTOM_TOP:
+            case SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM:
+                duration = 3;
+                break;
+            case SEI_PIC_STRUCT_FRAME_DOUBLING:
+                duration = 4;
+                break;
+            case SEI_PIC_STRUCT_FRAME_TRIPLING:
+                duration = 6;
+                break;
+        }
+    } else {
+        duration = h->field_pic_flag ? 1 : 2;
+    }
+
+    if (cpb_removal_delay >= 0) {
+        h->cur_dts = h->ts_trn_nb +
+                cpb_removal_delay * h->ts_tc.num * 90000 / h->ts_tc.den;
+    } else {
+        // no removal delay specified, use best guess (add prev frame 
duration)
+        h->cur_dts += h->cur_duration;
+    }
+    pts = h->cur_dts +
+            h->sei_dpb_output_delay * h->ts_tc.num * 90000 / h->ts_tc.den;
+    h->cur_duration = duration * h->ts_tc.num * 90000 / h->ts_tc.den;
+
+    if (h->sei_buffering_period_present_flag) {
+        // set reference for next buffering period, _after_ computing the
+        // timestamp for current frame (TS of current frame is computed 
against
+        // initial timestamp of previous buffering period).
+        h->ts_trn_nb = h->cur_dts;
+    }
+
+    s->dts = h->cur_dts;
+    s->pts = pts;
+    // TODO: handle wrap bits and pass duration to lavf
+}

Michael: I'd like to hear especially your comments...

Regards,

Ivan





More information about the ffmpeg-devel mailing list