[FFmpeg-devel] [PATCH] h264_sei: parse the picture timing SEIs correctly

Anton Khirnov anton at khirnov.net
Mon Mar 23 18:10:38 EET 2020


Those SEIs refer to the currently active SPS. However, since the SEI
NALUs precede the coded picture data in the bitstream, the active SPS is
in general not known when we are decoding the SEI.

Therefore, store the content of the picture timing SEIs and actually
parse it when the active SPS is known.
---
Thank you, stupid bug found and fixed by simplifying the code.
---
 libavcodec/h264_parser.c |  9 +++++
 libavcodec/h264_sei.c    | 82 +++++++++++++++++++++++-----------------
 libavcodec/h264_sei.h    | 11 ++++++
 libavcodec/h264_slice.c  | 10 +++++
 4 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c
index 5f9a9c46ef..ec1cbc6a66 100644
--- a/libavcodec/h264_parser.c
+++ b/libavcodec/h264_parser.c
@@ -481,6 +481,15 @@ static inline int parse_nal_units(AVCodecParserContext *s,
                 }
             }
 
+            if (p->sei.picture_timing.present) {
+                ret = ff_h264_sei_process_picture_timing(&p->sei.picture_timing,
+                                                         sps, avctx);
+                if (ret < 0) {
+                    av_log(avctx, AV_LOG_ERROR, "Error processing the picture timing SEI\n");
+                    p->sei.picture_timing.present = 0;
+                }
+            }
+
             if (sps->pic_struct_present_flag && p->sei.picture_timing.present) {
                 switch (p->sei.picture_timing.pic_struct) {
                 case H264_SEI_PIC_STRUCT_TOP_FIELD:
diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c
index 32d13985f3..870dd90717 100644
--- a/libavcodec/h264_sei.c
+++ b/libavcodec/h264_sei.c
@@ -54,30 +54,22 @@ void ff_h264_sei_uninit(H264SEIContext *h)
     av_buffer_unref(&h->a53_caption.buf_ref);
 }
 
-static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
-                                 const H264ParamSets *ps, void *logctx)
+int ff_h264_sei_process_picture_timing(H264SEIPictureTiming *h, const SPS *sps,
+                                       void *logctx)
 {
-    int i;
-    const SPS *sps = ps->sps;
-
-    for (i = 0; i<MAX_SPS_COUNT; i++)
-        if ((!sps || !sps->log2_max_frame_num) && ps->sps_list[i])
-            sps = (const SPS *)ps->sps_list[i]->data;
+    GetBitContext gb;
 
-    if (!sps) {
-        av_log(logctx, AV_LOG_ERROR, "SPS unavailable in decode_picture_timing\n");
-        return AVERROR_PS_NOT_FOUND;
-    }
+    init_get_bits(&gb, h->payload, h->payload_size_bits);
 
     if (sps->nal_hrd_parameters_present_flag ||
         sps->vcl_hrd_parameters_present_flag) {
-        h->cpb_removal_delay = get_bits_long(gb, sps->cpb_removal_delay_length);
-        h->dpb_output_delay  = get_bits_long(gb, sps->dpb_output_delay_length);
+        h->cpb_removal_delay = get_bits_long(&gb, sps->cpb_removal_delay_length);
+        h->dpb_output_delay  = get_bits_long(&gb, sps->dpb_output_delay_length);
     }
     if (sps->pic_struct_present_flag) {
         unsigned int i, num_clock_ts;
 
-        h->pic_struct = get_bits(gb, 4);
+        h->pic_struct = get_bits(&gb, 4);
         h->ct_type    = 0;
 
         if (h->pic_struct > H264_SEI_PIC_STRUCT_FRAME_TRIPLING)
@@ -86,38 +78,38 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
         num_clock_ts = sei_num_clock_ts_table[h->pic_struct];
         h->timecode_cnt = 0;
         for (i = 0; i < num_clock_ts; i++) {
-            if (get_bits(gb, 1)) {                      /* clock_timestamp_flag */
+            if (get_bits(&gb, 1)) {                      /* clock_timestamp_flag */
                 H264SEITimeCode *tc = &h->timecode[h->timecode_cnt++];
                 unsigned int full_timestamp_flag;
                 unsigned int counting_type, cnt_dropped_flag;
-                h->ct_type |= 1 << get_bits(gb, 2);
-                skip_bits(gb, 1);                       /* nuit_field_based_flag */
-                counting_type = get_bits(gb, 5);        /* counting_type */
-                full_timestamp_flag = get_bits(gb, 1);
-                skip_bits(gb, 1);                       /* discontinuity_flag */
-                cnt_dropped_flag = get_bits(gb, 1);      /* cnt_dropped_flag */
+                h->ct_type |= 1 << get_bits(&gb, 2);
+                skip_bits(&gb, 1);                       /* nuit_field_based_flag */
+                counting_type = get_bits(&gb, 5);        /* counting_type */
+                full_timestamp_flag = get_bits(&gb, 1);
+                skip_bits(&gb, 1);                       /* discontinuity_flag */
+                cnt_dropped_flag = get_bits(&gb, 1);      /* cnt_dropped_flag */
                 if (cnt_dropped_flag && counting_type > 1 && counting_type < 7)
                     tc->dropframe = 1;
-                tc->frame = get_bits(gb, 8);         /* n_frames */
+                tc->frame = get_bits(&gb, 8);         /* n_frames */
                 if (full_timestamp_flag) {
                     tc->full = 1;
-                    tc->seconds = get_bits(gb, 6); /* seconds_value 0..59 */
-                    tc->minutes = get_bits(gb, 6); /* minutes_value 0..59 */
-                    tc->hours = get_bits(gb, 5);   /* hours_value 0..23 */
+                    tc->seconds = get_bits(&gb, 6); /* seconds_value 0..59 */
+                    tc->minutes = get_bits(&gb, 6); /* minutes_value 0..59 */
+                    tc->hours = get_bits(&gb, 5);   /* hours_value 0..23 */
                 } else {
                     tc->seconds = tc->minutes = tc->hours = tc->full = 0;
-                    if (get_bits(gb, 1)) {             /* seconds_flag */
-                        tc->seconds = get_bits(gb, 6);
-                        if (get_bits(gb, 1)) {         /* minutes_flag */
-                            tc->minutes = get_bits(gb, 6);
-                            if (get_bits(gb, 1))       /* hours_flag */
-                                tc->hours = get_bits(gb, 5);
+                    if (get_bits(&gb, 1)) {             /* seconds_flag */
+                        tc->seconds = get_bits(&gb, 6);
+                        if (get_bits(&gb, 1)) {         /* minutes_flag */
+                            tc->minutes = get_bits(&gb, 6);
+                            if (get_bits(&gb, 1))       /* hours_flag */
+                                tc->hours = get_bits(&gb, 5);
                         }
                     }
                 }
 
                 if (sps->time_offset_length > 0)
-                    skip_bits(gb,
+                    skip_bits(&gb,
                               sps->time_offset_length); /* time_offset */
             }
         }
@@ -126,6 +118,28 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
                h->ct_type, h->pic_struct);
     }
 
+    return 0;
+}
+
+static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
+                                 void *logctx)
+{
+    int index     = get_bits_count(gb);
+    int size_bits = get_bits_left(gb);
+    int size      = (size_bits + 7) / 8;
+
+    if (index & 7) {
+        av_log(logctx, AV_LOG_ERROR, "Unaligned SEI payload\n");
+        return AVERROR_INVALIDDATA;
+    }
+    if (size > sizeof(h->payload)) {
+        av_log(logctx, AV_LOG_ERROR, "Picture timing SEI payload too large\n");
+        return AVERROR_INVALIDDATA;
+    }
+    memcpy(h->payload, gb->buffer + index / 8, size);
+
+    h->payload_size_bits = size_bits;
+
     h->present = 1;
     return 0;
 }
@@ -436,7 +450,7 @@ int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb,
 
         switch (type) {
         case H264_SEI_TYPE_PIC_TIMING: // Picture timing SEI
-            ret = decode_picture_timing(&h->picture_timing, &gb_payload, ps, logctx);
+            ret = decode_picture_timing(&h->picture_timing, &gb_payload, logctx);
             break;
         case H264_SEI_TYPE_USER_DATA_REGISTERED:
             ret = decode_registered_user_data(h, &gb_payload, logctx, size);
diff --git a/libavcodec/h264_sei.h b/libavcodec/h264_sei.h
index a75c3aa175..f07a5055c3 100644
--- a/libavcodec/h264_sei.h
+++ b/libavcodec/h264_sei.h
@@ -20,6 +20,7 @@
 #define AVCODEC_H264_SEI_H
 
 #include "get_bits.h"
+#include "h264_ps.h"
 
 /**
  * SEI message types
@@ -79,6 +80,10 @@ typedef struct H264SEITimeCode {
 } H264SEITimeCode;
 
 typedef struct H264SEIPictureTiming {
+    // maximum size of pic_timing according to the spec should be 274 bits
+    uint8_t payload[40];
+    int     payload_size_bits;
+
     int present;
     H264_SEI_PicStructType pic_struct;
 
@@ -202,4 +207,10 @@ void ff_h264_sei_uninit(H264SEIContext *h);
  */
 const char *ff_h264_sei_stereo_mode(const H264SEIFramePacking *h);
 
+/**
+ * Parse the contents of a picture timing message given an active SPS.
+ */
+int ff_h264_sei_process_picture_timing(H264SEIPictureTiming *h, const SPS *sps,
+                                       void *logctx);
+
 #endif /* AVCODEC_H264_SEI_H */
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index af30f6267b..c6072738d7 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1134,6 +1134,16 @@ static int h264_export_frame_props(H264Context *h)
     /* Signal interlacing information externally. */
     /* Prioritize picture timing SEI information over used
      * decoding process if it exists. */
+    if (h->sei.picture_timing.present) {
+        int ret = ff_h264_sei_process_picture_timing(&h->sei.picture_timing, sps,
+                                                     h->avctx);
+        if (ret < 0) {
+            av_log(h->avctx, AV_LOG_ERROR, "Error processing a picture timing SEI\n");
+            if (h->avctx->err_recognition & AV_EF_EXPLODE)
+                return ret;
+            h->sei.picture_timing.present = 0;
+        }
+    }
 
     if (sps->pic_struct_present_flag && h->sei.picture_timing.present) {
         H264SEIPictureTiming *pt = &h->sei.picture_timing;
-- 
2.25.1



More information about the ffmpeg-devel mailing list