h265parse: forward time codes
authorMathieu Duponchelle <mathieu@centricular.com>
Sat, 30 Mar 2019 00:17:08 +0000 (01:17 +0100)
committerMathieu Duponchelle <mduponchelle1@gmail.com>
Mon, 1 Apr 2019 10:02:33 +0000 (10:02 +0000)
This transforms time code SEIs into GstVideoTimeCodeMeta

gst-libs/gst/codecparsers/gsth265parser.c
gst-libs/gst/codecparsers/gsth265parser.h
gst/videoparsers/gsth265parse.c
gst/videoparsers/gsth265parse.h

index d44a045..16fce00 100644 (file)
@@ -1124,6 +1124,64 @@ error:
   return GST_H265_PARSER_ERROR;
 }
 
+static GstH265ParserResult
+gst_h265_parser_parse_time_code (GstH265Parser * parser,
+    GstH265TimeCode * tc, NalReader * nr)
+{
+  guint i;
+
+  GST_DEBUG ("parsing \"Time code\"");
+
+  READ_UINT8 (nr, tc->num_clock_ts, 2);
+
+  for (i = 0; i < tc->num_clock_ts; i++) {
+    READ_UINT8 (nr, tc->clock_timestamp_flag[i], 1);
+    if (tc->clock_timestamp_flag[i]) {
+      READ_UINT8 (nr, tc->units_field_based_flag[i], 1);
+      READ_UINT8 (nr, tc->counting_type[i], 5);
+      READ_UINT8 (nr, tc->full_timestamp_flag[i], 1);
+      READ_UINT8 (nr, tc->discontinuity_flag[i], 1);
+      READ_UINT8 (nr, tc->cnt_dropped_flag[i], 1);
+      READ_UINT16 (nr, tc->n_frames[i], 9);
+
+      if (tc->full_timestamp_flag[i]) {
+        tc->seconds_flag[i] = TRUE;
+        READ_UINT8 (nr, tc->seconds_value[i], 6);
+
+        tc->minutes_flag[i] = TRUE;
+        READ_UINT8 (nr, tc->minutes_value[i], 6);
+
+        tc->hours_flag[i] = TRUE;
+        READ_UINT8 (nr, tc->hours_value[i], 5);
+      } else {
+        READ_UINT8 (nr, tc->seconds_flag[i], 1);
+        if (tc->seconds_flag[i]) {
+          READ_UINT8 (nr, tc->seconds_value[i], 6);
+          READ_UINT8 (nr, tc->minutes_flag[i], 1);
+          if (tc->minutes_flag[i]) {
+            READ_UINT8 (nr, tc->minutes_value[i], 6);
+            READ_UINT8 (nr, tc->hours_flag[i], 1);
+            if (tc->hours_flag[i]) {
+              READ_UINT8 (nr, tc->hours_value[i], 5);
+            }
+          }
+        }
+      }
+    }
+
+    READ_UINT8 (nr, tc->time_offset_length[i], 5);
+
+    if (tc->time_offset_length[i] > 0)
+      READ_UINT32 (nr, tc->time_offset_value[i], tc->time_offset_length[i]);
+  }
+
+  return GST_H265_PARSER_OK;
+
+error:
+  GST_WARNING ("error parsing \"Time code\"");
+  return GST_H265_PARSER_ERROR;
+}
+
 /******** API *************/
 
 /**
@@ -2312,6 +2370,10 @@ gst_h265_parser_parse_sei_message (GstH265Parser * parser,
         res = gst_h265_parser_parse_recovery_point (parser,
             &sei->payload.recovery_point, nr);
         break;
+      case GST_H265_SEI_TIME_CODE:
+        res = gst_h265_parser_parse_time_code (parser,
+            &sei->payload.time_code, nr);
+        break;
       default:
         /* Just consume payloadSize bytes, which does not account for
            emulation prevention bytes */
index b4d1901..db996fc 100644 (file)
@@ -221,6 +221,7 @@ typedef enum
  * @GST_H265_SEI_BUF_PERIOD: Buffering Period SEI Message
  * @GST_H265_SEI_PIC_TIMING: Picture Timing SEI Message
  * @GST_H265_SEI_RECOVERY_POINT: Recovery Point SEI Message (D.3.8)
+ * @GST_H265_SEI_TIME_CODE: Time code SEI message (D.2.27) (Since 1.16)
  * ...
  *
  * The type of SEI message.
@@ -230,6 +231,7 @@ typedef enum
   GST_H265_SEI_BUF_PERIOD = 0,
   GST_H265_SEI_PIC_TIMING = 1,
   GST_H265_SEI_RECOVERY_POINT = 6,
+  GST_H265_SEI_TIME_CODE = 136,
       /* and more...  */
 } GstH265SEIPayloadType;
 
@@ -316,6 +318,7 @@ typedef struct _GstH265SliceHdr                 GstH265SliceHdr;
 typedef struct _GstH265PicTiming                GstH265PicTiming;
 typedef struct _GstH265BufferingPeriod          GstH265BufferingPeriod;
 typedef struct _GstH265RecoveryPoint            GstH265RecoveryPoint;
+typedef struct _GstH265TimeCode                 GstH265TimeCode;
 typedef struct _GstH265SEIMessage               GstH265SEIMessage;
 
 /**
@@ -1073,6 +1076,36 @@ struct _GstH265RecoveryPoint
   guint8 broken_link_flag;
 };
 
+/**
+ * GstH265TimeCode:
+ * The time code SEI message provides time code information similar to that
+ * defined by SMPTE ST 12-1 (2014) for field(s) or frame(s) of the current
+ * picture.
+ *
+ * D.2.27
+ *
+ * Since: 1.16
+ */
+struct _GstH265TimeCode
+{
+  guint8 num_clock_ts;
+  guint8 clock_timestamp_flag[3];
+  guint8 units_field_based_flag[3];
+  guint8 counting_type[3];
+  guint8 full_timestamp_flag[3];
+  guint8 discontinuity_flag[3];
+  guint8 cnt_dropped_flag[3];
+  guint16 n_frames[3];
+  guint8 seconds_flag[3];
+  guint8 seconds_value[3];
+  guint8 minutes_flag[3];
+  guint8 minutes_value[3];
+  guint8 hours_flag[3];
+  guint8 hours_value[3];
+  guint8 time_offset_length[3];
+  guint32 time_offset_value[3];
+};
+
 struct _GstH265SEIMessage
 {
   GstH265SEIPayloadType payloadType;
@@ -1081,6 +1114,7 @@ struct _GstH265SEIMessage
     GstH265BufferingPeriod buffering_period;
     GstH265PicTiming pic_timing;
     GstH265RecoveryPoint recovery_point;
+    GstH265TimeCode time_code;
     /* ... could implement more */
   } payload;
 };
index c7fd8bf..b16f20d 100644 (file)
@@ -544,9 +544,14 @@ gst_h265_parse_process_sei (GstH265Parse * h265parse, GstH265NalUnit * nalu)
             sei.payload.recovery_point.broken_link_flag);
         h265parse->keyframe = TRUE;
         break;
-
-      case GST_H265_SEI_BUF_PERIOD:
+      case GST_H265_SEI_TIME_CODE:
+        memcpy (&h265parse->time_code, &sei.payload.time_code,
+            sizeof (GstH265TimeCode));
+        break;
       case GST_H265_SEI_PIC_TIMING:
+        h265parse->sei_pic_struct = sei.payload.pic_timing.pic_struct;
+        break;
+      case GST_H265_SEI_BUF_PERIOD:
         /* FIXME */
         break;
     }
@@ -1245,8 +1250,8 @@ gst_h265_parse_make_codec_data (GstH265Parse * h265parse)
   }
   data[6] |=
       (pft->progressive_source_flag << 7) | (pft->interlaced_source_flag << 6) |
-      (pft->non_packed_constraint_flag << 5) | (pft->
-      frame_only_constraint_flag << 4);
+      (pft->
+      non_packed_constraint_flag << 5) | (pft->frame_only_constraint_flag << 4);
   data[12] = pft->level_idc;
   /* min_spatial_segmentation_idc */
   GST_WRITE_UINT16_BE (data + 13, min_spatial_segmentation_idc);
@@ -1701,9 +1706,13 @@ gst_h265_parse_update_src_caps (GstH265Parse * h265parse, GstCaps * caps)
 
       /* but not necessarily or reliably this */
       if (fps_num > 0 && fps_den > 0) {
+        GstStructure *s2;
         GST_INFO_OBJECT (h265parse, "setting framerate in caps");
         gst_caps_set_simple (caps, "framerate",
             GST_TYPE_FRACTION, fps_num, fps_den, NULL);
+        s2 = gst_caps_get_structure (caps, 0);
+        gst_structure_get_fraction (s2, "framerate", &h265parse->parsed_fps_n,
+            &h265parse->parsed_fps_d);
         gst_base_parse_set_frame_rate (GST_BASE_PARSE (h265parse),
             fps_num, fps_den, 0, 0);
         latency = gst_util_uint64_scale (GST_SECOND, fps_den, fps_num);
@@ -2213,6 +2222,76 @@ gst_h265_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
     }
   }
 
+  {
+    guint i = 0;
+
+    for (i = 0; i < h265parse->time_code.num_clock_ts; i++) {
+      GstVideoTimeCodeFlags flags = 0;
+      gint field_count = -1;
+
+      if (!h265parse->time_code.clock_timestamp_flag[i])
+        break;
+
+      h265parse->time_code.clock_timestamp_flag[i] = 0;
+
+      /* Table D.2 */
+      switch (h265parse->sei_pic_struct) {
+        case GST_H265_SEI_PIC_STRUCT_FRAME:
+        case GST_H265_SEI_PIC_STRUCT_TOP_FIELD:
+        case GST_H265_SEI_PIC_STRUCT_BOTTOM_FIELD:
+          field_count = h265parse->sei_pic_struct;
+          break;
+        case GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM:
+        case GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_PREVIOUS_BOTTOM:
+        case GST_H265_SEI_PIC_STRUCT_TOP_PAIRED_NEXT_BOTTOM:
+          field_count = i + 1;
+          break;
+        case GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP:
+        case GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_PREVIOUS_TOP:
+        case GST_H265_SEI_PIC_STRUCT_BOTTOM_PAIRED_NEXT_TOP:
+          field_count = 2 - i;
+          break;
+        case GST_H265_SEI_PIC_STRUCT_TOP_BOTTOM_TOP:
+          field_count = i % 2 ? 2 : 1;
+          break;
+        case GST_H265_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM:
+          field_count = i % 2 ? 1 : 2;
+          break;
+        case GST_H265_SEI_PIC_STRUCT_FRAME_DOUBLING:
+        case GST_H265_SEI_PIC_STRUCT_FRAME_TRIPLING:
+          field_count = 0;
+          break;
+      }
+
+      if (field_count == -1) {
+        GST_WARNING_OBJECT (parse,
+            "failed to determine field count for timecode");
+        field_count = 0;
+      }
+
+      /* Dropping of the two lowest (value 0 and 1) n_frames[ i ] counts when
+       * seconds_value[ i ] is equal to 0 and minutes_value[ i ] is not an integer
+       * multiple of 10 */
+      if (h265parse->time_code.counting_type[i] == 4)
+        flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
+
+      if (h265parse->sei_pic_struct != GST_H265_SEI_PIC_STRUCT_FRAME)
+        flags |= GST_VIDEO_TIME_CODE_FLAGS_INTERLACED;
+
+      gst_buffer_add_video_time_code_meta_full (buffer,
+          h265parse->parsed_fps_n,
+          h265parse->parsed_fps_d,
+          NULL,
+          flags,
+          h265parse->time_code.hours_flag[i] ? h265parse->time_code.
+          hours_value[i] : 0,
+          h265parse->time_code.minutes_flag[i] ? h265parse->time_code.
+          minutes_value[i] : 0,
+          h265parse->time_code.seconds_flag[i] ? h265parse->time_code.
+          seconds_value[i] : 0, h265parse->time_code.n_frames[i], field_count);
+    }
+  }
+
   gst_h265_parse_reset_frame (h265parse);
 
   return GST_FLOW_OK;
index d27aac3..f523649 100644 (file)
@@ -52,6 +52,7 @@ struct _GstH265Parse
   gint fps_num, fps_den;
   gint upstream_par_n, upstream_par_d;
   gint parsed_par_n, parsed_par_d;
+  gint parsed_fps_n, parsed_fps_d;
   /* current codec_data in output caps, if any */
   GstBuffer *codec_data;
   /* input codec_data, if any */
@@ -86,6 +87,12 @@ struct _GstH265Parse
   GstBuffer *sps_nals[GST_H265_MAX_SPS_COUNT];
   GstBuffer *pps_nals[GST_H265_MAX_PPS_COUNT];
 
+  /* Infos we need to keep track of */
+  guint8 sei_pic_struct;
+
+  /* Collected TimeCode SEI */
+  GstH265TimeCode time_code;
+
   gboolean discont;
 
   /* frame parsing */