[FFmpeg-devel] [PATCH 3/3] h264/pic_timing: support multiple timecodes

joshdk at ob-encoder.com joshdk at ob-encoder.com
Tue Oct 9 16:32:04 EEST 2018


From: Josh de Kock <joshdk at obe.tv>

---
 libavcodec/h264_sei.c     | 21 ++++++++------
 libavcodec/h264_sei.h     | 28 +++++++++++++------
 libavcodec/h264_slice.c   | 59 ++++++++++++++++++++++-----------------
 libavfilter/vf_showinfo.c | 10 +++++++
 4 files changed, 75 insertions(+), 43 deletions(-)

diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c
index 275224eabe..d4eb9c0dab 100644
--- a/libavcodec/h264_sei.c
+++ b/libavcodec/h264_sei.c
@@ -84,8 +84,10 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
             return AVERROR_INVALIDDATA;
 
         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 */
+                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);
@@ -95,20 +97,21 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb,
                 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 */
+                    tc->dropframe = 1;
+                tc->frame = get_bits(gb, 8);         /* n_frames */
                 if (full_timestamp_flag) {
-                    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 */
+                    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 */
                 } else {
+                    tc->seconds = tc->minutes = tc->hours = tc->full = 0;
                     if (get_bits(gb, 1)) {             /* seconds_flag */
-                        h->tc_seconds = get_bits(gb, 6);
+                        tc->seconds = get_bits(gb, 6);
                         if (get_bits(gb, 1)) {         /* minutes_flag */
-                            h->tc_minutes = get_bits(gb, 6);
+                            tc->minutes = get_bits(gb, 6);
                             if (get_bits(gb, 1))       /* hours_flag */
-                                h->tc_minutes = get_bits(gb, 5);
+                                tc->hours = get_bits(gb, 5);
                         }
                     }
                 }
diff --git a/libavcodec/h264_sei.h b/libavcodec/h264_sei.h
index 3b8806be0a..a75c3aa175 100644
--- a/libavcodec/h264_sei.h
+++ b/libavcodec/h264_sei.h
@@ -67,6 +67,17 @@ typedef enum {
     H264_SEI_FPA_TYPE_2D                  = 6,
 } H264_SEI_FpaType;
 
+typedef struct H264SEITimeCode {
+    /* When not continuously receiving full timecodes, we have to reference
+       the previous timecode received */
+    int full;
+    int frame;
+    int seconds;
+    int minutes;
+    int hours;
+    int dropframe;
+} H264SEITimeCode;
+
 typedef struct H264SEIPictureTiming {
     int present;
     H264_SEI_PicStructType pic_struct;
@@ -88,14 +99,15 @@ typedef struct H264SEIPictureTiming {
      */
     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;
+    /**
+     * Maximum three timecodes in a pic_timing SEI.
+     */
+    H264SEITimeCode timecode[3];
+
+    /**
+     * Number of timecode in use
+     */
+    int timecode_cnt;
 } H264SEIPictureTiming;
 
 typedef struct H264SEIAFD {
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 973f5761ef..aaf0006a32 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1287,42 +1287,49 @@ static int h264_export_frame_props(H264Context *h)
         h->avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS;
     }
 
-    if (h->sei.picture_timing.fulltc_received) {
+    if (h->sei.picture_timing.timecode_cnt > 0) {
         uint32_t tc = 0;
-        uint32_t frames;
+        uint32_t *tc_sd;
 
         AVFrameSideData *tcside = av_frame_new_side_data(cur->f,
                                                          AV_FRAME_DATA_S12M_TIMECODE,
-                                                         sizeof(uint32_t));
+                                                         sizeof(uint32_t)*4);
         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);
+        tc_sd = (uint32_t*)tcside->data;
+        tc_sd[0] = h->sei.picture_timing.timecode_cnt;
+
+        for (int i = 0; i < tc_sd[0]; i++) {
+            uint32_t frames;
+
+            /* 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.timecode[i].frame / 2;
+                if (h->sei.picture_timing.timecode[i].frame % 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.timecode[i].frame;
             }
-        } 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);
+            tc |= h->sei.picture_timing.timecode[i].dropframe << 30;
+            tc |= (frames / 10) << 28;
+            tc |= (frames % 10) << 24;
+            tc |= (h->sei.picture_timing.timecode[i].seconds / 10) << 20;
+            tc |= (h->sei.picture_timing.timecode[i].seconds % 10) << 16;
+            tc |= (h->sei.picture_timing.timecode[i].minutes / 10) << 12;
+            tc |= (h->sei.picture_timing.timecode[i].minutes % 10) << 8;
+            tc |= (h->sei.picture_timing.timecode[i].hours / 10) << 4;
+            tc |= (h->sei.picture_timing.timecode[i].hours % 10);
 
-        memcpy(tcside->data, &tc, sizeof(uint32_t));
-        h->sei.picture_timing.fulltc_received = 0;
+            tc_sd[i + 1] = tc;
+        }
+        h->sei.picture_timing.timecode_cnt = 0;
     }
 
     if (h->sei.alternative_transfer.present &&
diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c
index d1d1415c0b..689b5399db 100644
--- a/libavfilter/vf_showinfo.c
+++ b/libavfilter/vf_showinfo.c
@@ -32,6 +32,7 @@
 #include "libavutil/spherical.h"
 #include "libavutil/stereo3d.h"
 #include "libavutil/timestamp.h"
+#include "libavutil/timecode.h"
 
 #include "avfilter.h"
 #include "internal.h"
@@ -174,6 +175,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
         case AV_FRAME_DATA_STEREO3D:
             dump_stereo3d(ctx, sd);
             break;
+        case AV_FRAME_DATA_S12M_TIMECODE: {
+            uint32_t *tc = (uint32_t*)sd->data;
+            for (int j = 1; j < tc[0]; j++) {
+                char tcbuf[AV_TIMECODE_STR_SIZE];
+                av_timecode_make_smpte_tc_string(tcbuf, tc[j], 0);
+                av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0] - 1 ? ", " : "");
+            }
+            break;
+        }
         case AV_FRAME_DATA_DISPLAYMATRIX:
             av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees",
                    av_display_rotation_get((int32_t *)sd->data));
-- 
2.17.1



More information about the ffmpeg-devel mailing list