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 {