mirror of
https://github.com/xenia-project/FFmpeg.git
synced 2024-11-24 20:19:55 +00:00
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.
This commit is contained in:
parent
92c25963e8
commit
4241e44a3c
@ -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 */
|
||||
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 */
|
||||
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 */
|
||||
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 */
|
||||
h->tc_seconds = get_bits(gb, 6);
|
||||
if (get_bits(gb, 1)) { /* minutes_flag */
|
||||
skip_bits(gb, 6); /* minutes_value 0..59 */
|
||||
h->tc_minutes = get_bits(gb, 6);
|
||||
if (get_bits(gb, 1)) /* hours_flag */
|
||||
skip_bits(gb, 5); /* hours_value 0..23 */
|
||||
h->tc_minutes = get_bits(gb, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sps->time_offset_length > 0)
|
||||
skip_bits(gb,
|
||||
sps->time_offset_length); /* time_offset */
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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…
Reference in New Issue
Block a user