[FFmpeg-devel] [PATCH v2 1/4] lavc/h264: create AVFrame side data from H.264 timecodes
joshdk at ob-encoder.com
joshdk at ob-encoder.com
Fri Oct 12 16:38:38 EEST 2018
From: Devin Heitmueller <dheitmueller at ltnglobal.com>
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 | 37 ++++++++++++++++++++-----------------
libavcodec/h264_sei.h | 9 +++++++++
libavcodec/h264_slice.c | 38 ++++++++++++++++++++++++++++++++++++++
libavutil/frame.c | 1 +
libavutil/frame.h | 8 ++++++++
5 files changed, 76 insertions(+), 17 deletions(-)
diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c
index 43593d34d2..275224eabe 100644
--- a/libavcodec/h264_sei.c
+++ b/libavcodec/h264_sei.c
@@ -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 */
diff --git a/libavcodec/h264_sei.h b/libavcodec/h264_sei.h
index 5b7c8ef9d8..3b8806be0a 100644
--- a/libavcodec/h264_sei.h
+++ b/libavcodec/h264_sei.h
@@ -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 {
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index d09cee4b13..f5415ba595 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -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) {
diff --git a/libavutil/frame.c b/libavutil/frame.c
index 4460325a9b..92626dccf2 100644
--- a/libavutil/frame.c
+++ b/libavutil/frame.c
@@ -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
diff --git a/libavutil/frame.h b/libavutil/frame.h
index 9d57d6ce66..e2a292980f 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -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 {
--
2.17.1
More information about the ffmpeg-devel
mailing list