]> git.sesse.net Git - ffmpeg/commitdiff
lavc/h264: create AVFrame side data from H.264 timecodes
authorDevin Heitmueller <dheitmueller@ltnglobal.com>
Fri, 29 Jun 2018 18:57:13 +0000 (14:57 -0400)
committerKieran Kunhya <kierank@obe.tv>
Tue, 23 Oct 2018 14:46:30 +0000 (15:46 +0100)
Create SMPTE ST 12-1 timecodes based on H.264 SEI picture timing
info.

For framerates > 30 FPS, the field flag is used in conjunction with
pairs of frames which contain the same frame timestamp in S12M.
Ensure the field is properly set per the spec.

libavcodec/h264_sei.c
libavcodec/h264_sei.h
libavcodec/h264_slice.c
libavutil/frame.c
libavutil/frame.h

index 43593d34d2d3438372350afb4904fe6c0a34066c..275224eabe537f8e9fd739d3988fdafffd87fa00 100644 (file)
@@ -84,32 +84,35 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
             return AVERROR_INVALIDDATA;
 
         num_clock_ts = sei_num_clock_ts_table[h->pic_struct];
-
         for (i = 0; i < num_clock_ts; i++) {
-            if (get_bits(gb, 1)) {                /* clock_timestamp_flag */
+            if (get_bits(gb, 1)) {                      /* clock_timestamp_flag */
                 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 */
-                skip_bits(gb, 5);                 /* counting_type */
+                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 */
-                skip_bits(gb, 1);                 /* cnt_dropped_flag */
-                skip_bits(gb, 8);                 /* n_frames */
+                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)
+                    h->tc_dropframe = 1;
+                h->tc_frames = get_bits(gb, 8);         /* n_frames */
                 if (full_timestamp_flag) {
-                    skip_bits(gb, 6);             /* seconds_value 0..59 */
-                    skip_bits(gb, 6);             /* minutes_value 0..59 */
-                    skip_bits(gb, 5);             /* hours_value 0..23 */
+                    h->fulltc_received = 1;
+                    h->tc_seconds = get_bits(gb, 6); /* seconds_value 0..59 */
+                    h->tc_minutes = get_bits(gb, 6); /* minutes_value 0..59 */
+                    h->tc_hours = get_bits(gb, 5);   /* hours_value 0..23 */
                 } else {
-                    if (get_bits(gb, 1)) {        /* seconds_flag */
-                        skip_bits(gb, 6);         /* seconds_value range 0..59 */
-                        if (get_bits(gb, 1)) {    /* minutes_flag */
-                            skip_bits(gb, 6);     /* minutes_value 0..59 */
-                            if (get_bits(gb, 1))  /* hours_flag */
-                                skip_bits(gb, 5); /* hours_value 0..23 */
+                    if (get_bits(gb, 1)) {             /* seconds_flag */
+                        h->tc_seconds = get_bits(gb, 6);
+                        if (get_bits(gb, 1)) {         /* minutes_flag */
+                            h->tc_minutes = get_bits(gb, 6);
+                            if (get_bits(gb, 1))       /* hours_flag */
+                                h->tc_minutes = get_bits(gb, 5);
                         }
                     }
                 }
+
                 if (sps->time_offset_length > 0)
                     skip_bits(gb,
                               sps->time_offset_length); /* time_offset */
index 5b7c8ef9d8aa859a792c10566c8e5f35a903ea19..3b8806be0a9e9ec8a44f178bbc8b37679abd0495 100644 (file)
@@ -87,6 +87,15 @@ typedef struct H264SEIPictureTiming {
      * cpb_removal_delay in picture timing SEI message, see H.264 C.1.2
      */
     int cpb_removal_delay;
+
+    /* When not continuously receiving full timecodes, we have to reference
+       the previous timecode received */
+    int fulltc_received;
+    int tc_frames;
+    int tc_seconds;
+    int tc_minutes;
+    int tc_hours;
+    int tc_dropframe;
 } H264SEIPictureTiming;
 
 typedef struct H264SEIAFD {
index d09cee4b13fbb53161270c08aac7e975754b5791..f5415ba595d8c706563aa421ebd497e020fb4a68 100644 (file)
@@ -1287,6 +1287,44 @@ static int h264_export_frame_props(H264Context *h)
         h->avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS;
     }
 
+    if (h->sei.picture_timing.fulltc_received) {
+        uint32_t tc = 0;
+        uint32_t frames;
+
+        AVFrameSideData *tcside = av_frame_new_side_data(cur->f,
+                                                         AV_FRAME_DATA_S12M_TIMECODE,
+                                                         sizeof(uint32_t));
+        if (!tcside)
+            return AVERROR(ENOMEM);
+
+        /* For SMPTE 12-M timecodes, frame count is a special case if > 30 FPS.
+           See SMPTE ST 12-1:2014 Sec 12.1 for more info. */
+        if (av_cmp_q(h->avctx->framerate, (AVRational) {30, 1}) == 1) {
+            frames = h->sei.picture_timing.tc_frames / 2;
+            if (h->sei.picture_timing.tc_frames % 2 == 1) {
+                if (av_cmp_q(h->avctx->framerate, (AVRational) {50, 1}) == 0)
+                    tc |= (1 << 7);
+                else
+                    tc |= (1 << 23);
+            }
+        } else {
+            frames = h->sei.picture_timing.tc_frames;
+        }
+
+        tc |= h->sei.picture_timing.tc_dropframe << 30;
+        tc |= (frames / 10) << 28;
+        tc |= (frames % 10) << 24;
+        tc |= (h->sei.picture_timing.tc_seconds / 10) << 20;
+        tc |= (h->sei.picture_timing.tc_seconds % 10) << 16;
+        tc |= (h->sei.picture_timing.tc_minutes / 10) << 12;
+        tc |= (h->sei.picture_timing.tc_minutes % 10) << 8;
+        tc |= (h->sei.picture_timing.tc_hours / 10) << 4;
+        tc |= (h->sei.picture_timing.tc_hours % 10);
+
+        memcpy(tcside->data, &tc, sizeof(uint32_t));
+        h->sei.picture_timing.fulltc_received = 0;
+    }
+
     if (h->sei.alternative_transfer.present &&
         av_color_transfer_name(h->sei.alternative_transfer.preferred_transfer_characteristics) &&
         h->sei.alternative_transfer.preferred_transfer_characteristics != AVCOL_TRC_UNSPECIFIED) {
index 4460325a9bba8ff00049cc96457d68c955483ebd..92626dccf23c70cf3b4c1d4593b5a348a00dcec7 100644 (file)
@@ -831,6 +831,7 @@ const char *av_frame_side_data_name(enum AVFrameSideDataType type)
     case AV_FRAME_DATA_MASTERING_DISPLAY_METADATA:  return "Mastering display metadata";
     case AV_FRAME_DATA_CONTENT_LIGHT_LEVEL:         return "Content light level metadata";
     case AV_FRAME_DATA_GOP_TIMECODE:                return "GOP timecode";
+    case AV_FRAME_DATA_S12M_TIMECODE:               return "SMPTE 12-1 timecode";
     case AV_FRAME_DATA_SPHERICAL:                   return "Spherical Mapping";
     case AV_FRAME_DATA_ICC_PROFILE:                 return "ICC profile";
 #if FF_API_FRAME_QP
index 9d57d6ce66ff92420145caf55643b2ec34b63147..e2a292980f46d2189ed75e76652064f27f16afb0 100644 (file)
@@ -158,6 +158,14 @@ enum AVFrameSideDataType {
      */
     AV_FRAME_DATA_QP_TABLE_DATA,
 #endif
+
+    /**
+     * Timecode which conforms to SMPTE ST 12-1. The data is an array of 4 uint32_t
+     * where the first uint32_t describes how many (1-3) of the other timecodes are used.
+     * The timecode format is described in the av_timecode_get_smpte_from_framenum()
+     * function in libavutil/timecode.c.
+     */
+    AV_FRAME_DATA_S12M_TIMECODE,
 };
 
 enum AVActiveFormatDescription {