From 4241e44a3c0193d182d3d614e7b4977c00c0225c Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 29 Jun 2018 14:57:13 -0400 Subject: lavc/h264: create AVFrame side data from H.264 timecodes 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 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 17 deletions(-) (limited to 'libavcodec') 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) { -- cgit v1.2.3