Browse Source

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.
tags/n4.1
Devin Heitmueller Kieran Kunhya 7 years ago
parent
commit
4241e44a3c
5 changed files with 76 additions and 17 deletions
  1. +20
    -17
      libavcodec/h264_sei.c
  2. +9
    -0
      libavcodec/h264_sei.h
  3. +38
    -0
      libavcodec/h264_slice.c
  4. +1
    -0
      libavutil/frame.c
  5. +8
    -0
      libavutil/frame.h

+ 20
- 17
libavcodec/h264_sei.c View File

@@ -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 */


+ 9
- 0
libavcodec/h264_sei.h View File

@@ -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 {


+ 38
- 0
libavcodec/h264_slice.c View File

@@ -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) {


+ 1
- 0
libavutil/frame.c View File

@@ -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


+ 8
- 0
libavutil/frame.h View File

@@ -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 {


Loading…
Cancel
Save