diff options
author | Devin Heitmueller <dheitmueller@ltnglobal.com> | 2018-06-29 14:57:13 -0400 |
---|---|---|
committer | Kieran Kunhya <kierank@obe.tv> | 2018-10-23 15:46:30 +0100 |
commit | 4241e44a3c0193d182d3d614e7b4977c00c0225c (patch) | |
tree | 6162e908a5b8480a31ebd6f002c4520f529b38be /libavcodec | |
parent | 92c25963e8b68c47055b813334eaf76599936a90 (diff) | |
download | ffmpeg-4241e44a3c0193d182d3d614e7b4977c00c0225c.tar.gz |
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.
Diffstat (limited to 'libavcodec')
-rw-r--r-- | libavcodec/h264_sei.c | 37 | ||||
-rw-r--r-- | libavcodec/h264_sei.h | 9 | ||||
-rw-r--r-- | libavcodec/h264_slice.c | 38 |
3 files changed, 67 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) { |