From 76a8c3b522bf1000b327e8aea28710716c057cb9 Mon Sep 17 00:00:00 2001 From: Josh de Kock Date: Fri, 12 Oct 2018 13:38:26 +0100 Subject: [PATCH] h264/pic_timing: support multiple timecodes --- libavcodec/h264_sei.c | 21 ++++++++------ libavcodec/h264_sei.h | 28 +++++++++++++------ libavcodec/h264_slice.c | 61 +++++++++++++++++++++++------------------ 3 files changed, 66 insertions(+), 44 deletions(-) diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c index 275224eabe..d4eb9c0dab 100644 --- a/libavcodec/h264_sei.c +++ b/libavcodec/h264_sei.c @@ -84,8 +84,10 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb, return AVERROR_INVALIDDATA; num_clock_ts = sei_num_clock_ts_table[h->pic_struct]; + h->timecode_cnt = 0; for (i = 0; i < num_clock_ts; i++) { if (get_bits(gb, 1)) { /* clock_timestamp_flag */ + H264SEITimeCode *tc = &h->timecode[h->timecode_cnt++]; unsigned int full_timestamp_flag; unsigned int counting_type, cnt_dropped_flag; h->ct_type |= 1 << get_bits(gb, 2); @@ -95,20 +97,21 @@ static int decode_picture_timing(H264SEIPictureTiming *h, GetBitContext *gb, 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 */ + tc->dropframe = 1; + tc->frame = get_bits(gb, 8); /* n_frames */ if (full_timestamp_flag) { - 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 */ + tc->full = 1; + tc->seconds = get_bits(gb, 6); /* seconds_value 0..59 */ + tc->minutes = get_bits(gb, 6); /* minutes_value 0..59 */ + tc->hours = get_bits(gb, 5); /* hours_value 0..23 */ } else { + tc->seconds = tc->minutes = tc->hours = tc->full = 0; if (get_bits(gb, 1)) { /* seconds_flag */ - h->tc_seconds = get_bits(gb, 6); + tc->seconds = get_bits(gb, 6); if (get_bits(gb, 1)) { /* minutes_flag */ - h->tc_minutes = get_bits(gb, 6); + tc->minutes = get_bits(gb, 6); if (get_bits(gb, 1)) /* hours_flag */ - h->tc_minutes = get_bits(gb, 5); + tc->hours = get_bits(gb, 5); } } } diff --git a/libavcodec/h264_sei.h b/libavcodec/h264_sei.h index 3b8806be0a..a75c3aa175 100644 --- a/libavcodec/h264_sei.h +++ b/libavcodec/h264_sei.h @@ -67,6 +67,17 @@ typedef enum { H264_SEI_FPA_TYPE_2D = 6, } H264_SEI_FpaType; +typedef struct H264SEITimeCode { + /* When not continuously receiving full timecodes, we have to reference + the previous timecode received */ + int full; + int frame; + int seconds; + int minutes; + int hours; + int dropframe; +} H264SEITimeCode; + typedef struct H264SEIPictureTiming { int present; H264_SEI_PicStructType pic_struct; @@ -88,14 +99,15 @@ typedef struct H264SEIPictureTiming { */ 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; + /** + * Maximum three timecodes in a pic_timing SEI. + */ + H264SEITimeCode timecode[3]; + + /** + * Number of timecode in use + */ + int timecode_cnt; } H264SEIPictureTiming; typedef struct H264SEIAFD { diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index f5415ba595..2e158745a0 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -1287,42 +1287,49 @@ static int h264_export_frame_props(H264Context *h) h->avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; } - if (h->sei.picture_timing.fulltc_received) { + if (h->sei.picture_timing.timecode_cnt > 0) { uint32_t tc = 0; - uint32_t frames; + uint32_t *tc_sd; AVFrameSideData *tcside = av_frame_new_side_data(cur->f, AV_FRAME_DATA_S12M_TIMECODE, - sizeof(uint32_t)); + sizeof(uint32_t)*4); 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); + tc_sd = (uint32_t*)tcside->data; + tc_sd[0] = h->sei.picture_timing.timecode_cnt; + + for (int i = 0; i < tc_sd[0]; i++) { + uint32_t frames; + + /* 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.timecode[i].frame / 2; + if (h->sei.picture_timing.timecode[i].frame % 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.timecode[i].frame; } - } else { - frames = h->sei.picture_timing.tc_frames; + + tc |= h->sei.picture_timing.timecode[i].dropframe << 30; + tc |= (frames / 10) << 28; + tc |= (frames % 10) << 24; + tc |= (h->sei.picture_timing.timecode[i].seconds / 10) << 20; + tc |= (h->sei.picture_timing.timecode[i].seconds % 10) << 16; + tc |= (h->sei.picture_timing.timecode[i].minutes / 10) << 12; + tc |= (h->sei.picture_timing.timecode[i].minutes % 10) << 8; + tc |= (h->sei.picture_timing.timecode[i].hours / 10) << 4; + tc |= (h->sei.picture_timing.timecode[i].hours % 10); + + tc_sd[i + 1] = tc; } - - 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; + h->sei.picture_timing.timecode_cnt = 0; } if (h->sei.alternative_transfer.present &&