Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions libavcodec/h264_sei.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,32 +84,38 @@ 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 */
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);
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)
tc->dropframe = 1;
tc->frame = 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 */
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 {
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 */
tc->seconds = tc->minutes = tc->hours = tc->full = 0;
if (get_bits(gb, 1)) { /* seconds_flag */
tc->seconds = get_bits(gb, 6);
if (get_bits(gb, 1)) { /* minutes_flag */
tc->minutes = get_bits(gb, 6);
if (get_bits(gb, 1)) /* hours_flag */
tc->hours = get_bits(gb, 5);
}
}
}

if (sps->time_offset_length > 0)
skip_bits(gb,
sps->time_offset_length); /* time_offset */
Expand Down
21 changes: 21 additions & 0 deletions libavcodec/h264_sei.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ typedef enum {
SEI_FPA_TYPE_2D = 6,
} 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;
SEI_PicStructType pic_struct;
Expand All @@ -83,6 +94,16 @@ typedef struct H264SEIPictureTiming {
* cpb_removal_delay in picture timing SEI message, see H.264 C.1.2
*/
int cpb_removal_delay;

/**
* Maximum three timecodes in a pic_timing SEI.
*/
H264SEITimeCode timecode[3];

/**
* Number of timecode in use
*/
int timecode_cnt;
} H264SEIPictureTiming;

typedef struct H264SEIAFD {
Expand Down
45 changes: 45 additions & 0 deletions libavcodec/h264_slice.c
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,51 @@ static int h264_export_frame_props(H264Context *h)
h->avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS;
}

if (h->sei.picture_timing.timecode_cnt > 0) {
uint32_t tc = 0;
uint32_t *tc_sd;

AVFrameSideData *tcside = av_frame_new_side_data(cur->f,
AV_FRAME_DATA_S12M_TIMECODE,
sizeof(uint32_t)*4);
if (!tcside)
return AVERROR(ENOMEM);

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;
}

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;
}
h->sei.picture_timing.timecode_cnt = 0;
}

return 0;
}

Expand Down
1 change: 1 addition & 0 deletions libavutil/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,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";
}
return NULL;
}
8 changes: 8 additions & 0 deletions libavutil/frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ enum AVFrameSideDataType {
* the form of the AVContentLightMetadata struct.
*/
AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,

/**
* 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 {
Expand Down