|
|
|
@ -56,6 +56,7 @@ typedef struct H264MetadataContext { |
|
|
|
|
int done_first_au; |
|
|
|
|
|
|
|
|
|
int aud; |
|
|
|
|
H264RawAUD aud_nal; |
|
|
|
|
|
|
|
|
|
AVRational sample_aspect_ratio; |
|
|
|
|
|
|
|
|
@ -91,6 +92,59 @@ typedef struct H264MetadataContext { |
|
|
|
|
} H264MetadataContext; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int h264_metadata_insert_aud(AVBSFContext *bsf, |
|
|
|
|
CodedBitstreamFragment *au) |
|
|
|
|
{ |
|
|
|
|
H264MetadataContext *ctx = bsf->priv_data; |
|
|
|
|
int primary_pic_type_mask = 0xff; |
|
|
|
|
int err, i, j; |
|
|
|
|
|
|
|
|
|
static const int primary_pic_type_table[] = { |
|
|
|
|
0x084, // 2, 7
|
|
|
|
|
0x0a5, // 0, 2, 5, 7
|
|
|
|
|
0x0e7, // 0, 1, 2, 5, 6, 7
|
|
|
|
|
0x210, // 4, 9
|
|
|
|
|
0x318, // 3, 4, 8, 9
|
|
|
|
|
0x294, // 2, 4, 7, 9
|
|
|
|
|
0x3bd, // 0, 2, 3, 4, 5, 7, 8, 9
|
|
|
|
|
0x3ff, // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < au->nb_units; i++) { |
|
|
|
|
if (au->units[i].type == H264_NAL_SLICE || |
|
|
|
|
au->units[i].type == H264_NAL_IDR_SLICE) { |
|
|
|
|
H264RawSlice *slice = au->units[i].content; |
|
|
|
|
for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) { |
|
|
|
|
if (!(primary_pic_type_table[j] & |
|
|
|
|
(1 << slice->header.slice_type))) |
|
|
|
|
primary_pic_type_mask &= ~(1 << j); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) |
|
|
|
|
if (primary_pic_type_mask & (1 << j)) |
|
|
|
|
break; |
|
|
|
|
if (j >= FF_ARRAY_ELEMS(primary_pic_type_table)) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "No usable primary_pic_type: " |
|
|
|
|
"invalid slice types?\n"); |
|
|
|
|
return AVERROR_INVALIDDATA; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ctx->aud_nal = (H264RawAUD) { |
|
|
|
|
.nal_unit_header.nal_unit_type = H264_NAL_AUD, |
|
|
|
|
.primary_pic_type = j, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
err = ff_cbs_insert_unit_content(au, 0, H264_NAL_AUD, |
|
|
|
|
&ctx->aud_nal, NULL); |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); |
|
|
|
|
return err; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int h264_metadata_update_sps(AVBSFContext *bsf, |
|
|
|
|
H264RawSPS *sps) |
|
|
|
|
{ |
|
|
|
@ -322,160 +376,51 @@ static int h264_metadata_update_side_data(AVBSFContext *bsf, AVPacket *pkt) |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) |
|
|
|
|
static int h264_metadata_handle_display_orientation(AVBSFContext *bsf, |
|
|
|
|
AVPacket *pkt, |
|
|
|
|
CodedBitstreamFragment *au, |
|
|
|
|
int seek_point) |
|
|
|
|
{ |
|
|
|
|
H264MetadataContext *ctx = bsf->priv_data; |
|
|
|
|
CodedBitstreamFragment *au = &ctx->access_unit; |
|
|
|
|
int err, i, j, has_sps; |
|
|
|
|
H264RawAUD aud; |
|
|
|
|
|
|
|
|
|
err = ff_bsf_get_packet_ref(bsf, pkt); |
|
|
|
|
if (err < 0) |
|
|
|
|
return err; |
|
|
|
|
|
|
|
|
|
err = h264_metadata_update_side_data(bsf, pkt); |
|
|
|
|
if (err < 0) |
|
|
|
|
goto fail; |
|
|
|
|
|
|
|
|
|
err = ff_cbs_read_packet(ctx->input, au, pkt); |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (au->nb_units == 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "No NAL units in packet.\n"); |
|
|
|
|
err = AVERROR_INVALIDDATA; |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If an AUD is present, it must be the first NAL unit.
|
|
|
|
|
if (au->units[0].type == H264_NAL_AUD) { |
|
|
|
|
if (ctx->aud == REMOVE) |
|
|
|
|
ff_cbs_delete_unit(au, 0); |
|
|
|
|
} else { |
|
|
|
|
if (ctx->aud == INSERT) { |
|
|
|
|
static const int primary_pic_type_table[] = { |
|
|
|
|
0x084, // 2, 7
|
|
|
|
|
0x0a5, // 0, 2, 5, 7
|
|
|
|
|
0x0e7, // 0, 1, 2, 5, 6, 7
|
|
|
|
|
0x210, // 4, 9
|
|
|
|
|
0x318, // 3, 4, 8, 9
|
|
|
|
|
0x294, // 2, 4, 7, 9
|
|
|
|
|
0x3bd, // 0, 2, 3, 4, 5, 7, 8, 9
|
|
|
|
|
0x3ff, // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
|
|
|
}; |
|
|
|
|
int primary_pic_type_mask = 0xff; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < au->nb_units; i++) { |
|
|
|
|
if (au->units[i].type == H264_NAL_SLICE || |
|
|
|
|
au->units[i].type == H264_NAL_IDR_SLICE) { |
|
|
|
|
H264RawSlice *slice = au->units[i].content; |
|
|
|
|
for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) { |
|
|
|
|
if (!(primary_pic_type_table[j] & |
|
|
|
|
(1 << slice->header.slice_type))) |
|
|
|
|
primary_pic_type_mask &= ~(1 << j); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) |
|
|
|
|
if (primary_pic_type_mask & (1 << j)) |
|
|
|
|
break; |
|
|
|
|
if (j >= FF_ARRAY_ELEMS(primary_pic_type_table)) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "No usable primary_pic_type: " |
|
|
|
|
"invalid slice types?\n"); |
|
|
|
|
err = AVERROR_INVALIDDATA; |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
aud = (H264RawAUD) { |
|
|
|
|
.nal_unit_header.nal_unit_type = H264_NAL_AUD, |
|
|
|
|
.primary_pic_type = j, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
err = ff_cbs_insert_unit_content(au, |
|
|
|
|
0, H264_NAL_AUD, &aud, NULL); |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
has_sps = 0; |
|
|
|
|
for (i = 0; i < au->nb_units; i++) { |
|
|
|
|
if (au->units[i].type == H264_NAL_SPS) { |
|
|
|
|
err = h264_metadata_update_sps(bsf, au->units[i].content); |
|
|
|
|
if (err < 0) |
|
|
|
|
goto fail; |
|
|
|
|
has_sps = 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Only insert the SEI in access units containing SPSs, and also
|
|
|
|
|
// unconditionally in the first access unit we ever see.
|
|
|
|
|
if (ctx->sei_user_data && (has_sps || !ctx->done_first_au)) { |
|
|
|
|
err = ff_cbs_sei_add_message(ctx->output, au, 1, |
|
|
|
|
SEI_TYPE_USER_DATA_UNREGISTERED, |
|
|
|
|
&ctx->sei_user_data_payload, NULL); |
|
|
|
|
SEIRawMessage *message; |
|
|
|
|
int err; |
|
|
|
|
|
|
|
|
|
message = NULL; |
|
|
|
|
while (ff_cbs_sei_find_message(ctx->output, au, |
|
|
|
|
SEI_TYPE_DISPLAY_ORIENTATION, |
|
|
|
|
&message) == 0) { |
|
|
|
|
H264RawSEIDisplayOrientation *disp = message->payload; |
|
|
|
|
int32_t *matrix; |
|
|
|
|
|
|
|
|
|
matrix = av_malloc(9 * sizeof(int32_t)); |
|
|
|
|
if (!matrix) |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
|
|
av_display_rotation_set(matrix, |
|
|
|
|
disp->anticlockwise_rotation * |
|
|
|
|
180.0 / 65536.0); |
|
|
|
|
av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip); |
|
|
|
|
|
|
|
|
|
// If there are multiple display orientation messages in an
|
|
|
|
|
// access unit, then the last one added to the packet (i.e.
|
|
|
|
|
// the first one in the access unit) will prevail.
|
|
|
|
|
err = av_packet_add_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX, |
|
|
|
|
(uint8_t*)matrix, |
|
|
|
|
9 * sizeof(int32_t)); |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI " |
|
|
|
|
"message to access unit.\n"); |
|
|
|
|
goto fail; |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted " |
|
|
|
|
"displaymatrix side data to packet.\n"); |
|
|
|
|
av_free(matrix); |
|
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ctx->delete_filler) { |
|
|
|
|
for (i = au->nb_units - 1; i >= 0; i--) { |
|
|
|
|
if (au->units[i].type == H264_NAL_FILLER_DATA) { |
|
|
|
|
ff_cbs_delete_unit(au, i); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ctx->display_orientation == REMOVE || |
|
|
|
|
ctx->display_orientation == INSERT) { |
|
|
|
|
ff_cbs_sei_delete_message_type(ctx->output, au, |
|
|
|
|
SEI_TYPE_FILLER_PAYLOAD); |
|
|
|
|
SEI_TYPE_DISPLAY_ORIENTATION); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ctx->display_orientation != PASS) { |
|
|
|
|
SEIRawMessage *message = NULL; |
|
|
|
|
while (ff_cbs_sei_find_message(ctx->output, au, |
|
|
|
|
SEI_TYPE_DISPLAY_ORIENTATION, |
|
|
|
|
&message) == 0) { |
|
|
|
|
H264RawSEIDisplayOrientation *disp = message->payload; |
|
|
|
|
int32_t *matrix; |
|
|
|
|
|
|
|
|
|
matrix = av_malloc(9 * sizeof(int32_t)); |
|
|
|
|
if (!matrix) { |
|
|
|
|
err = AVERROR(ENOMEM); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
av_display_rotation_set(matrix, |
|
|
|
|
disp->anticlockwise_rotation * |
|
|
|
|
180.0 / 65536.0); |
|
|
|
|
av_display_matrix_flip(matrix, disp->hor_flip, disp->ver_flip); |
|
|
|
|
|
|
|
|
|
// If there are multiple display orientation messages in an
|
|
|
|
|
// access unit, then the last one added to the packet (i.e.
|
|
|
|
|
// the first one in the access unit) will prevail.
|
|
|
|
|
err = av_packet_add_side_data(pkt, AV_PKT_DATA_DISPLAYMATRIX, |
|
|
|
|
(uint8_t*)matrix, |
|
|
|
|
9 * sizeof(int32_t)); |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to attach extracted " |
|
|
|
|
"displaymatrix side data to packet.\n"); |
|
|
|
|
av_free(matrix); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ctx->display_orientation == REMOVE || |
|
|
|
|
ctx->display_orientation == INSERT) { |
|
|
|
|
ff_cbs_sei_delete_message_type(ctx->output, au, |
|
|
|
|
SEI_TYPE_DISPLAY_ORIENTATION); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (ctx->display_orientation == INSERT) { |
|
|
|
|
H264RawSEIDisplayOrientation *disp = |
|
|
|
|
&ctx->display_orientation_payload; |
|
|
|
@ -516,7 +461,7 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (has_sps || !ctx->done_first_au) { |
|
|
|
|
if (seek_point) { |
|
|
|
|
if (!isnan(ctx->rotate)) { |
|
|
|
|
disp->anticlockwise_rotation = |
|
|
|
|
(uint16_t)rint((ctx->rotate >= 0.0 ? ctx->rotate |
|
|
|
@ -540,9 +485,98 @@ static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to add display orientation " |
|
|
|
|
"SEI message to access unit.\n"); |
|
|
|
|
return err; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *pkt) |
|
|
|
|
{ |
|
|
|
|
H264MetadataContext *ctx = bsf->priv_data; |
|
|
|
|
CodedBitstreamFragment *au = &ctx->access_unit; |
|
|
|
|
int err, i, has_sps, seek_point; |
|
|
|
|
|
|
|
|
|
err = ff_bsf_get_packet_ref(bsf, pkt); |
|
|
|
|
if (err < 0) |
|
|
|
|
return err; |
|
|
|
|
|
|
|
|
|
err = h264_metadata_update_side_data(bsf, pkt); |
|
|
|
|
if (err < 0) |
|
|
|
|
goto fail; |
|
|
|
|
|
|
|
|
|
err = ff_cbs_read_packet(ctx->input, au, pkt); |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (au->nb_units == 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "No NAL units in packet.\n"); |
|
|
|
|
err = AVERROR_INVALIDDATA; |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If an AUD is present, it must be the first NAL unit.
|
|
|
|
|
if (au->units[0].type == H264_NAL_AUD) { |
|
|
|
|
if (ctx->aud == REMOVE) |
|
|
|
|
ff_cbs_delete_unit(au, 0); |
|
|
|
|
} else { |
|
|
|
|
if (ctx->aud == INSERT) { |
|
|
|
|
err = h264_metadata_insert_aud(bsf, au); |
|
|
|
|
if (err < 0) |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
has_sps = 0; |
|
|
|
|
for (i = 0; i < au->nb_units; i++) { |
|
|
|
|
if (au->units[i].type == H264_NAL_SPS) { |
|
|
|
|
err = h264_metadata_update_sps(bsf, au->units[i].content); |
|
|
|
|
if (err < 0) |
|
|
|
|
goto fail; |
|
|
|
|
has_sps = 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// The current packet should be treated as a seek point for metadata
|
|
|
|
|
// insertion if any of:
|
|
|
|
|
// - It is the first packet in the stream.
|
|
|
|
|
// - It contains an SPS, indicating that a sequence might start here.
|
|
|
|
|
// - It is marked as containing a key frame.
|
|
|
|
|
seek_point = !ctx->done_first_au || has_sps || |
|
|
|
|
(pkt->flags & AV_PKT_FLAG_KEY); |
|
|
|
|
|
|
|
|
|
if (ctx->sei_user_data && seek_point) { |
|
|
|
|
err = ff_cbs_sei_add_message(ctx->output, au, 1, |
|
|
|
|
SEI_TYPE_USER_DATA_UNREGISTERED, |
|
|
|
|
&ctx->sei_user_data_payload, NULL); |
|
|
|
|
if (err < 0) { |
|
|
|
|
av_log(bsf, AV_LOG_ERROR, "Failed to add user data SEI " |
|
|
|
|
"message to access unit.\n"); |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ctx->delete_filler) { |
|
|
|
|
for (i = au->nb_units - 1; i >= 0; i--) { |
|
|
|
|
if (au->units[i].type == H264_NAL_FILLER_DATA) { |
|
|
|
|
ff_cbs_delete_unit(au, i); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ff_cbs_sei_delete_message_type(ctx->output, au, |
|
|
|
|
SEI_TYPE_FILLER_PAYLOAD); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ctx->display_orientation != PASS) { |
|
|
|
|
err = h264_metadata_handle_display_orientation(bsf, pkt, au, |
|
|
|
|
seek_point); |
|
|
|
|
if (err < 0) |
|
|
|
|
goto fail; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
err = ff_cbs_write_packet(ctx->output, pkt, au); |
|
|
|
|