You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-11-23 21:54:53 +02:00
avformat/hevc: don't write NALUs with nuh_layer_id > 0 in hvcC boxes
Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
@@ -40,12 +40,15 @@ enum {
|
|||||||
NB_ARRAYS
|
NB_ARRAYS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FLAG_ARRAY_COMPLETENESS (1 << 0)
|
||||||
|
#define FLAG_IS_NALFF (1 << 1)
|
||||||
|
|
||||||
typedef struct HVCCNALUnitArray {
|
typedef struct HVCCNALUnitArray {
|
||||||
uint8_t array_completeness;
|
uint8_t array_completeness;
|
||||||
uint8_t NAL_unit_type;
|
uint8_t NAL_unit_type;
|
||||||
uint16_t numNalus;
|
uint16_t numNalus;
|
||||||
uint16_t *nalUnitLength;
|
uint16_t *nalUnitLength;
|
||||||
uint8_t **nalUnit;
|
const uint8_t **nalUnit;
|
||||||
} HVCCNALUnitArray;
|
} HVCCNALUnitArray;
|
||||||
|
|
||||||
typedef struct HEVCDecoderConfigurationRecord {
|
typedef struct HEVCDecoderConfigurationRecord {
|
||||||
@@ -654,24 +657,26 @@ static int hvcc_parse_pps(GetBitContext *gb,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nal_unit_parse_header(GetBitContext *gb, uint8_t *nal_type)
|
static void nal_unit_parse_header(GetBitContext *gb, uint8_t *nal_type,
|
||||||
|
uint8_t *nuh_layer_id)
|
||||||
{
|
{
|
||||||
skip_bits1(gb); // forbidden_zero_bit
|
skip_bits1(gb); // forbidden_zero_bit
|
||||||
|
|
||||||
*nal_type = get_bits(gb, 6);
|
*nal_type = get_bits(gb, 6);
|
||||||
|
*nuh_layer_id = get_bits(gb, 6);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* nuh_layer_id u(6)
|
|
||||||
* nuh_temporal_id_plus1 u(3)
|
* nuh_temporal_id_plus1 u(3)
|
||||||
*/
|
*/
|
||||||
skip_bits(gb, 9);
|
skip_bits(gb, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hvcc_array_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size,
|
static int hvcc_array_add_nal_unit(const uint8_t *nal_buf, uint32_t nal_size,
|
||||||
uint8_t nal_type, int ps_array_completeness,
|
uint8_t nal_type, int flags,
|
||||||
HVCCNALUnitArray *array)
|
HVCCNALUnitArray *array)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
int ps_array_completeness = !!(flags & FLAG_ARRAY_COMPLETENESS);
|
||||||
uint16_t numNalus = array->numNalus;
|
uint16_t numNalus = array->numNalus;
|
||||||
|
|
||||||
ret = av_reallocp_array(&array->nalUnit, numNalus + 1, sizeof(uint8_t*));
|
ret = av_reallocp_array(&array->nalUnit, numNalus + 1, sizeof(uint8_t*));
|
||||||
@@ -699,14 +704,14 @@ static int hvcc_array_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size,
|
static int hvcc_add_nal_unit(const uint8_t *nal_buf, uint32_t nal_size,
|
||||||
int ps_array_completeness,
|
|
||||||
HEVCDecoderConfigurationRecord *hvcc,
|
HEVCDecoderConfigurationRecord *hvcc,
|
||||||
unsigned array_idx)
|
int flags, unsigned array_idx)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int is_nalff = !!(flags & FLAG_IS_NALFF);
|
||||||
GetBitContext gbc;
|
GetBitContext gbc;
|
||||||
uint8_t nal_type;
|
uint8_t nal_type, nuh_layer_id;
|
||||||
uint8_t *rbsp_buf;
|
uint8_t *rbsp_buf;
|
||||||
uint32_t rbsp_size;
|
uint32_t rbsp_size;
|
||||||
|
|
||||||
@@ -720,7 +725,9 @@ static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
nal_unit_parse_header(&gbc, &nal_type);
|
nal_unit_parse_header(&gbc, &nal_type, &nuh_layer_id);
|
||||||
|
if (nuh_layer_id > 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: only 'declarative' SEI messages are allowed in
|
* Note: only 'declarative' SEI messages are allowed in
|
||||||
@@ -728,12 +735,17 @@ static int hvcc_add_nal_unit(uint8_t *nal_buf, uint32_t nal_size,
|
|||||||
* and non-declarative SEI messages discarded?
|
* and non-declarative SEI messages discarded?
|
||||||
*/
|
*/
|
||||||
ret = hvcc_array_add_nal_unit(nal_buf, nal_size, nal_type,
|
ret = hvcc_array_add_nal_unit(nal_buf, nal_size, nal_type,
|
||||||
ps_array_completeness,
|
flags,
|
||||||
&hvcc->arrays[array_idx]);
|
&hvcc->arrays[array_idx]);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto end;
|
goto end;
|
||||||
if (hvcc->arrays[array_idx].numNalus == 1)
|
if (hvcc->arrays[array_idx].numNalus == 1)
|
||||||
hvcc->numOfArrays++;
|
hvcc->numOfArrays++;
|
||||||
|
|
||||||
|
/* Don't parse parameter sets. We already have the needed information*/
|
||||||
|
if (is_nalff)
|
||||||
|
goto end;
|
||||||
|
|
||||||
if (nal_type == HEVC_NAL_VPS)
|
if (nal_type == HEVC_NAL_VPS)
|
||||||
ret = hvcc_parse_vps(&gbc, hvcc);
|
ret = hvcc_parse_vps(&gbc, hvcc);
|
||||||
else if (nal_type == HEVC_NAL_SPS)
|
else if (nal_type == HEVC_NAL_SPS)
|
||||||
@@ -1041,20 +1053,100 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hvcc_parse_nal_unit(const uint8_t *buf, uint32_t len, int type,
|
||||||
|
HEVCDecoderConfigurationRecord *hvcc,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < FF_ARRAY_ELEMS(hvcc->arrays); i++) {
|
||||||
|
static const uint8_t array_idx_to_type[] =
|
||||||
|
{ HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS,
|
||||||
|
HEVC_NAL_SEI_PREFIX, HEVC_NAL_SEI_SUFFIX };
|
||||||
|
|
||||||
|
if (type == array_idx_to_type[i]) {
|
||||||
|
int ret = hvcc_add_nal_unit(buf, len, hvcc, flags, i);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data,
|
int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data,
|
||||||
int size, int ps_array_completeness)
|
int size, int ps_array_completeness)
|
||||||
{
|
{
|
||||||
HEVCDecoderConfigurationRecord hvcc;
|
HEVCDecoderConfigurationRecord hvcc;
|
||||||
uint8_t *buf, *end, *start;
|
uint8_t *buf, *end, *start = NULL;
|
||||||
|
int flags = !!ps_array_completeness * FLAG_ARRAY_COMPLETENESS;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (size < 6) {
|
if (size < 6) {
|
||||||
/* We can't write a valid hvcC from the provided data */
|
/* We can't write a valid hvcC from the provided data */
|
||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
} else if (*data == 1) {
|
} else if (*data == 1) {
|
||||||
/* Data is already hvcC-formatted */
|
/* Data is already hvcC-formatted. Parse the arrays to skip any NALU
|
||||||
avio_write(pb, data, size);
|
with nuh_layer_id > 0 */
|
||||||
return 0;
|
GetBitContext gbc;
|
||||||
|
int num_arrays;
|
||||||
|
|
||||||
|
if (size < 23)
|
||||||
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
|
ret = init_get_bits8(&gbc, data, size);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
hvcc_init(&hvcc);
|
||||||
|
skip_bits(&gbc, 8); // hvcc.configurationVersion
|
||||||
|
hvcc.general_profile_space = get_bits(&gbc, 2);
|
||||||
|
hvcc.general_tier_flag = get_bits1(&gbc);
|
||||||
|
hvcc.general_profile_idc = get_bits(&gbc, 5);
|
||||||
|
hvcc.general_profile_compatibility_flags = get_bits_long(&gbc, 32);
|
||||||
|
hvcc.general_constraint_indicator_flags = get_bits64(&gbc, 48);
|
||||||
|
hvcc.general_level_idc = get_bits(&gbc, 8);
|
||||||
|
skip_bits(&gbc, 4); // reserved
|
||||||
|
hvcc.min_spatial_segmentation_idc = get_bits(&gbc, 12);
|
||||||
|
skip_bits(&gbc, 6); // reserved
|
||||||
|
hvcc.parallelismType = get_bits(&gbc, 2);
|
||||||
|
skip_bits(&gbc, 6); // reserved
|
||||||
|
hvcc.chromaFormat = get_bits(&gbc, 2);
|
||||||
|
skip_bits(&gbc, 5); // reserved
|
||||||
|
hvcc.bitDepthLumaMinus8 = get_bits(&gbc, 3);
|
||||||
|
skip_bits(&gbc, 5); // reserved
|
||||||
|
hvcc.bitDepthChromaMinus8 = get_bits(&gbc, 3);
|
||||||
|
hvcc.avgFrameRate = get_bits(&gbc, 16);
|
||||||
|
hvcc.constantFrameRate = get_bits(&gbc, 2);
|
||||||
|
hvcc.numTemporalLayers = get_bits(&gbc, 3);
|
||||||
|
hvcc.temporalIdNested = get_bits1(&gbc);
|
||||||
|
hvcc.lengthSizeMinusOne = get_bits(&gbc, 2);
|
||||||
|
|
||||||
|
flags |= FLAG_IS_NALFF;
|
||||||
|
|
||||||
|
num_arrays = get_bits(&gbc, 8);
|
||||||
|
for (int i = 0; i < num_arrays; i++) {
|
||||||
|
int type, num_nalus;
|
||||||
|
|
||||||
|
skip_bits(&gbc, 2);
|
||||||
|
type = get_bits(&gbc, 6);
|
||||||
|
num_nalus = get_bits(&gbc, 16);
|
||||||
|
for (int j = 0; j < num_nalus; j++) {
|
||||||
|
int len = get_bits(&gbc, 16);
|
||||||
|
|
||||||
|
if (len > (get_bits_left(&gbc) / 8))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
ret = hvcc_parse_nal_unit(data + get_bits_count(&gbc) / 8,
|
||||||
|
len, type, &hvcc, flags);
|
||||||
|
if (ret < 0)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
skip_bits_long(&gbc, len * 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = hvcc_write(pb, &hvcc);
|
||||||
|
goto end;
|
||||||
} else if (!(AV_RB24(data) == 1 || AV_RB32(data) == 1)) {
|
} else if (!(AV_RB24(data) == 1 || AV_RB32(data) == 1)) {
|
||||||
/* Not a valid Annex B start code prefix */
|
/* Not a valid Annex B start code prefix */
|
||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
@@ -1075,19 +1167,9 @@ int ff_isom_write_hvcc(AVIOContext *pb, const uint8_t *data,
|
|||||||
|
|
||||||
buf += 4;
|
buf += 4;
|
||||||
|
|
||||||
for (unsigned i = 0; i < FF_ARRAY_ELEMS(hvcc.arrays); i++) {
|
ret = hvcc_parse_nal_unit(buf, len, type, &hvcc, flags);
|
||||||
static const uint8_t array_idx_to_type[] =
|
|
||||||
{ HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS,
|
|
||||||
HEVC_NAL_SEI_PREFIX, HEVC_NAL_SEI_SUFFIX };
|
|
||||||
|
|
||||||
if (type == array_idx_to_type[i]) {
|
|
||||||
ret = hvcc_add_nal_unit(buf, len, ps_array_completeness,
|
|
||||||
&hvcc, i);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto end;
|
goto end;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf += len;
|
buf += len;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ int ff_hevc_annexb2mp4_buf(const uint8_t *buf_in, uint8_t **buf_out,
|
|||||||
int *size, int filter_ps, int *ps_count);
|
int *size, int filter_ps, int *ps_count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes HEVC extradata (parameter sets, declarative SEI NAL units) to the
|
* Writes HEVC extradata (parameter sets and declarative SEI NAL units with
|
||||||
|
* nuh_layer_id == 0, as a HEVCDecoderConfigurationRecord) to the
|
||||||
* provided AVIOContext.
|
* provided AVIOContext.
|
||||||
*
|
*
|
||||||
* If the extradata is Annex B format, it gets converted to hvcC format before
|
* If the extradata is Annex B format, it gets converted to hvcC format before
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
0da54607064548fa1aae5695751f189c *tests/data/fate/enhanced-flv-hevc.flv
|
565cf155790db391137f81f619448477 *tests/data/fate/enhanced-flv-hevc.flv
|
||||||
3603038 tests/data/fate/enhanced-flv-hevc.flv
|
3603038 tests/data/fate/enhanced-flv-hevc.flv
|
||||||
#extradata 0: 551, 0xa18acf66
|
#extradata 0: 551, 0xb1ddcd66
|
||||||
#extradata 1: 2, 0x00340022
|
#extradata 1: 2, 0x00340022
|
||||||
#tb 0: 1/1000
|
#tb 0: 1/1000
|
||||||
#media_type 0: video
|
#media_type 0: video
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
0730145aa317d800cb4bde0e3a38bb8d *tests/data/fate/matroska-dovi-write-config8.matroska
|
3bd4b07d5af6153516e4c0e66a71c8c9 *tests/data/fate/matroska-dovi-write-config8.matroska
|
||||||
3600607 tests/data/fate/matroska-dovi-write-config8.matroska
|
3600607 tests/data/fate/matroska-dovi-write-config8.matroska
|
||||||
#extradata 0: 551, 0xa18acf66
|
#extradata 0: 551, 0xb1ddcd66
|
||||||
#extradata 1: 2, 0x00340022
|
#extradata 1: 2, 0x00340022
|
||||||
#tb 0: 1/1000
|
#tb 0: 1/1000
|
||||||
#media_type 0: video
|
#media_type 0: video
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
39cf3df5fc3a9c50ab71a294f45663fe *tests/data/lavf-fate/lavf.hevc.flv
|
c9e8b5df15135d21bd2781558f32f269 *tests/data/lavf-fate/lavf.hevc.flv
|
||||||
11819 tests/data/lavf-fate/lavf.hevc.flv
|
11819 tests/data/lavf-fate/lavf.hevc.flv
|
||||||
tests/data/lavf-fate/lavf.hevc.flv CRC=0xd29da885
|
tests/data/lavf-fate/lavf.hevc.flv CRC=0xd29da885
|
||||||
|
|||||||
Reference in New Issue
Block a user