格式定义
格式中定义相关字段的值,可以直接从HEVC的SPS序列帧中解析得到,按照此格式组合成HEVC的序列头,其buf[0]=0x1C,表示关键帧,编码格式为HEVC,buf[1]=0表示HEVC的序列帧,buf[2-4]=0x000000,buf[5]=1,之后的编码信息赋值按照从H265的SPS中解析出的值赋值即可,其中numOfArrays表示后面数组的个数,一般为0x03,表示包含VPS/SPS/PPS,后面数组的封装方式示意如下(《ISO-14496-15 AVC file format》中有详细说明):
// The CodecPrivate syntax shall follow the
// syntax of HEVCDecoderConfigurationRecord
// defined in ISO/IEC 14496-15.
//
// The number zero (0) shall be written to
// the configurationVersion variable until
// official finalization of 14496-15, 3rd ed.
//
// After its finalization, this field and the
// following CodecPrivate structure shall
// follow the definition of the
// HEVCDecoderConfigurationRecord in 14496-15.
unsigned int(8) configurationVersion;
unsigned int(2) general_profile_space;
unsigned int(1) general_tier_flag;
unsigned int(5) general_profile_idc;
unsigned int(32) general_profile_compatibility_flags;
unsigned int(48) general_constraint_indicator_flags;
unsigned int(8) general_level_idc;
bit(4) reserved = ‘1111’b;
unsigned int(12) min_spatial_segmentation_idc;
bit(6) reserved = ‘111111’b;
unsigned int(2) parallelismType;
bit(6) reserved = ‘111111’b;
unsigned int(2) chromaFormat;
bit(5) reserved = ‘11111’b;
unsigned int(3) bitDepthLumaMinus8;
bit(5) reserved = ‘11111’b;
unsigned int(3) bitDepthChromaMinus8;
bit(16) avgFrameRate;
bit(2) constantFrameRate;
bit(3) numTemporalLayers;
bit(1) temporalIdNested;
unsigned int(2) lengthSizeMinusOne;
unsigned int(8) numOfArrays;
for (j=0; j < numOfArrays; j++) {
bit(1) array_completeness;
unsigned int(1) reserved = 0;
unsigned int(6) NAL_unit_type;
unsigned int(16) numNalus;
for (i=0; i< numNalus; i++) {
unsigned int(16) nalUnitLength;
bit(8*nalUnitLength) nalUnit;
}
}
typedef struct HVCCNALUnitArray {
uint8_t array_completeness;
uint8_t NAL_unit_type;
uint16_t numNalus;
uint16_t *nalUnitLength;
uint8_t **nalUnit;
} HVCCNALUnitArray;
HVCCNALUnitArray在定义格式中,前面的三个字段,占用同一个字节,所以写入的过程中需要注意
HEVCDecoderConfigurationRecord结构体
typedef struct HEVCDecoderConfigurationRecord {
uint8_t configurationVersion;
uint8_t general_profile_space;
uint8_t general_tier_flag;
uint8_t general_profile_idc;
uint32_t general_profile_compatibility_flags;
uint64_t general_constraint_indicator_flags;
uint8_t general_level_idc;
uint16_t min_spatial_segmentation_idc;
uint8_t parallelismType;
uint8_t chromaFormat;
uint8_t bitDepthLumaMinus8;
uint8_t bitDepthChromaMinus8;
uint16_t avgFrameRate;
uint8_t constantFrameRate;
uint8_t numTemporalLayers;
uint8_t temporalIdNested;
uint8_t lengthSizeMinusOne;
uint8_t numOfArrays;
HVCCNALUnitArray *array;
} HEVCDecoderConfigurationRecord;
hvcc_write
static int hvcc_write(AVIOContext *pb, HEVCDecoderConfigurationRecord *hvcc)
{
uint8_t i;
uint16_t j, vps_count = 0, sps_count = 0, pps_count = 0;
/*
* We only support writing HEVCDecoderConfigurationRecord version 1.
*/
hvcc->configurationVersion = 1;
/*
* If min_spatial_segmentation_idc is invalid, reset to 0 (unspecified).
*/
if (hvcc->min_spatial_segmentation_idc > MAX_SPATIAL_SEGMENTATION)
hvcc->min_spatial_segmentation_idc = 0;
/*
* parallelismType indicates the type of parallelism that is used to meet
* the restrictions imposed by min_spatial_segmentation_idc when the value
* of min_spatial_segmentation_idc is greater than 0.
*/
if (!hvcc->min_spatial_segmentation_idc)
hvcc->parallelismType = 0;
/*
* It's unclear how to properly compute these fields, so
* let's always set them to values meaning 'unspecified'.
*/
hvcc->avgFrameRate = 0;
hvcc->constantFrameRate = 0;
av_log(NULL, AV_LOG_TRACE, "configurationVersion: %"PRIu8"\n",
hvcc->configurationVersion);
av_log(NULL, AV_LOG_TRACE, "general_profile_space: %"PRIu8"\n",
hvcc->general_profile_space);
av_log(NULL, AV_LOG_TRACE, "general_tier_flag: %"PRIu8"\n",
hvcc->general_tier_flag);
av_log(NULL, AV_LOG_TRACE, "general_profile_idc: %"PRIu8"\n",
hvcc->general_profile_idc);
av_log(NULL, AV_LOG_TRACE, "general_profile_compatibility_flags: 0x%08"PRIx32"\n",
hvcc->general_profile_compatibility_flags);
av_log(NULL, AV_LOG_TRACE, "general_constraint_indicator_flags: 0x%012"PRIx64"\n",
hvcc->general_constraint_indicator_flags);
av_log(NULL, AV_LOG_TRACE, "general_level_idc: %"PRIu8"\n",
hvcc->general_level_idc);
av_log(NULL, AV_LOG_TRACE, "min_spatial_segmentation_idc: %"PRIu16"\n",
hvcc->min_spatial_segmentation_idc);
av_log(NULL, AV_LOG_TRACE, "parallelismType: %"PRIu8"\n",
hvcc->parallelismType);
av_log(NULL, AV_LOG_TRACE, "chromaFormat: %"PRIu8"\n",
hvcc->chromaFormat);
av_log(NULL, AV_LOG_TRACE, "bitDepthLumaMinus8: %"PRIu8"\n",
hvcc->bitDepthLumaMinus8);
av_log(NULL, AV_LOG_TRACE, "bitDepthChromaMinus8: %"PRIu8"\n",
hvcc->bitDepthChromaMinus8);
av_log(NULL, AV_LOG_TRACE, "avgFrameRate: %"PRIu16"\n",
hvcc->avgFrameRate);
av_log(NULL, AV_LOG_TRACE, "constantFrameRate: %"PRIu8"\n",
hvcc->constantFrameRate);
av_log(NULL, AV_LOG_TRACE, "numTemporalLayers: %"PRIu8"\n",
hvcc->numTemporalLayers);
av_log(NULL, AV_LOG_TRACE, "temporalIdNested: %"PRIu8"\n",
hvcc->temporalIdNested);
av_log(NULL, AV_LOG_TRACE, "lengthSizeMinusOne: %"PRIu8"\n",
hvcc->lengthSizeMinusOne);
av_log(NULL, AV_LOG_TRACE, "numOfArrays: %"PRIu8"\n",
hvcc->numOfArrays);
for (i = 0; i < hvcc->numOfArrays; i++) {
av_log(NULL, AV_LOG_TRACE, "array_completeness[%"PRIu8"]: %"PRIu8"\n",
i, hvcc->array[i].array_completeness);
av_log(NULL, AV_LOG_TRACE, "NAL_unit_type[%"PRIu8"]: %"PRIu8"\n",
i, hvcc->array[i].NAL_unit_type);
av_log(NULL, AV_LOG_TRACE, "numNalus[%"PRIu8"]: %"PRIu16"\n",
i, hvcc->array[i].numNalus);
for (j = 0; j < hvcc->array[i].numNalus; j++)
av_log(NULL, AV_LOG_TRACE,
"nalUnitLength[%"PRIu8"][%"PRIu16"]: %"PRIu16"\n",
i, j, hvcc->array[i].nalUnitLength[j]);
}
/*
* We need at least one of each: VPS, SPS and PPS.
*/
for (i = 0; i < hvcc->numOfArrays; i++)
switch (hvcc->array[i].NAL_unit_type) {
case HEVC_NAL_VPS:
vps_count += hvcc->array[i].numNalus;
break;
case HEVC_NAL_SPS:
sps_count += hvcc->array[i].numNalus;
break;
case HEVC_NAL_PPS:
pps_count += hvcc->array[i].numNalus;
break;
default:
break;
}
if (!vps_count || vps_count > HEVC_MAX_VPS_COUNT ||
!sps_count || sps_count > HEVC_MAX_SPS_COUNT ||
!pps_count || pps_count > HEVC_MAX_PPS_COUNT)
return AVERROR_INVALIDDATA;
/* unsigned int(8) configurationVersion = 1; */
avio_w8(pb, hvcc->configurationVersion);
/*
* unsigned int(2) general_profile_space;
* unsigned int(1) general_tier_flag;
* unsigned int(5) general_profile_idc;
*/
avio_w8(pb, hvcc->general_profile_space << 6 |
hvcc->general_tier_flag << 5 |
hvcc->general_profile_idc);
/* unsigned int(32) general_profile_compatibility_flags; */
avio_wb32(pb, hvcc->general_profile_compatibility_flags);
/* unsigned int(48) general_constraint_indicator_flags; */
avio_wb32(pb, hvcc->general_constraint_indicator_flags >> 16);
avio_wb16(pb, hvcc->general_constraint_indicator_flags);
/* unsigned int(8) general_level_idc; */
avio_w8(pb, hvcc->general_level_idc);
/*
* bit(4) reserved = ‘1111’b;
* unsigned int(12) min_spatial_segmentation_idc;
*/
avio_wb16(pb, hvcc->min_spatial_segmentation_idc | 0xf000);
/*
* bit(6) reserved = ‘111111’b;
* unsigned int(2) parallelismType;
*/
avio_w8(pb, hvcc->parallelismType | 0xfc);
/*
* bit(6) reserved = ‘111111’b;
* unsigned int(2) chromaFormat;
*/
avio_w8(pb, hvcc->chromaFormat | 0xfc);
/*
* bit(5) reserved = ‘11111’b;
* unsigned int(3) bitDepthLumaMinus8;
*/
avio_w8(pb, hvcc->bitDepthLumaMinus8 | 0xf8);
/*
* bit(5) reserved = ‘11111’b;
* unsigned int(3) bitDepthChromaMinus8;
*/
avio_w8(pb, hvcc->bitDepthChromaMinus8 | 0xf8);
/* bit(16) avgFrameRate; */
avio_wb16(pb, hvcc->avgFrameRate);
/*
* bit(2) constantFrameRate;
* bit(3) numTemporalLayers;
* bit(1) temporalIdNested;
* unsigned int(2) lengthSizeMinusOne;
*/
avio_w8(pb, hvcc->constantFrameRate << 6 |
hvcc->numTemporalLayers << 3 |
hvcc->temporalIdNested << 2 |
hvcc->lengthSizeMinusOne);
/* unsigned int(8) numOfArrays; */
avio_w8(pb, hvcc->numOfArrays);
for (i = 0; i < hvcc->numOfArrays; i++) {
/*
* bit(1) array_completeness;
* unsigned int(1) reserved = 0;
* unsigned int(6) NAL_unit_type;
*/
avio_w8(pb, hvcc->array[i].array_completeness << 7 |
hvcc->array[i].NAL_unit_type & 0x3f);
/* unsigned int(16) numNalus; */
avio_wb16(pb, hvcc->array[i].numNalus);
for (j = 0; j < hvcc->array[i].numNalus; j++) {
/* unsigned int(16) nalUnitLength; */
avio_wb16(pb, hvcc->array[i].nalUnitLength[j]);
/* bit(8*nalUnitLength) nalUnit; */
avio_write(pb, hvcc->array[i].nalUnit[j],
hvcc->array[i].nalUnitLength[j]);
}
}
return 0;
}