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.tags/n4.1
| @@ -84,32 +84,35 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb, | |||||
| return AVERROR_INVALIDDATA; | return AVERROR_INVALIDDATA; | ||||
| num_clock_ts = sei_num_clock_ts_table[h->pic_struct]; | num_clock_ts = sei_num_clock_ts_table[h->pic_struct]; | ||||
| for (i = 0; i < num_clock_ts; i++) { | 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 full_timestamp_flag; | ||||
| unsigned int counting_type, cnt_dropped_flag; | |||||
| h->ct_type |= 1 << get_bits(gb, 2); | 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); | 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) { | 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 { | } 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) | if (sps->time_offset_length > 0) | ||||
| skip_bits(gb, | skip_bits(gb, | ||||
| sps->time_offset_length); /* time_offset */ | sps->time_offset_length); /* time_offset */ | ||||
| @@ -87,6 +87,15 @@ typedef struct H264SEIPictureTiming { | |||||
| * cpb_removal_delay in picture timing SEI message, see H.264 C.1.2 | * cpb_removal_delay in picture timing SEI message, see H.264 C.1.2 | ||||
| */ | */ | ||||
| int cpb_removal_delay; | 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; | } H264SEIPictureTiming; | ||||
| typedef struct H264SEIAFD { | typedef struct H264SEIAFD { | ||||
| @@ -1287,6 +1287,44 @@ static int h264_export_frame_props(H264Context *h) | |||||
| h->avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; | 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 && | if (h->sei.alternative_transfer.present && | ||||
| av_color_transfer_name(h->sei.alternative_transfer.preferred_transfer_characteristics) && | av_color_transfer_name(h->sei.alternative_transfer.preferred_transfer_characteristics) && | ||||
| h->sei.alternative_transfer.preferred_transfer_characteristics != AVCOL_TRC_UNSPECIFIED) { | h->sei.alternative_transfer.preferred_transfer_characteristics != AVCOL_TRC_UNSPECIFIED) { | ||||
| @@ -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_MASTERING_DISPLAY_METADATA: return "Mastering display metadata"; | ||||
| case AV_FRAME_DATA_CONTENT_LIGHT_LEVEL: return "Content light level 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_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_SPHERICAL: return "Spherical Mapping"; | ||||
| case AV_FRAME_DATA_ICC_PROFILE: return "ICC profile"; | case AV_FRAME_DATA_ICC_PROFILE: return "ICC profile"; | ||||
| #if FF_API_FRAME_QP | #if FF_API_FRAME_QP | ||||
| @@ -158,6 +158,14 @@ enum AVFrameSideDataType { | |||||
| */ | */ | ||||
| AV_FRAME_DATA_QP_TABLE_DATA, | AV_FRAME_DATA_QP_TABLE_DATA, | ||||
| #endif | #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 { | enum AVActiveFormatDescription { | ||||