全球优质服务器购买

为用户应用推荐适合的服务器,针对需求定制,将质量做到更好

香港服务器

香港CN2优化带宽,国内访问快

CPU:I3-2120(2核心4线程)

内存:4G DDR3内存

硬盘:1T HDD

带宽:10M优化、10M国际

IP数:1个

价格:699/月

美国服务器

美国洛杉矶高性价比服务器

CPU:I3-2120(2核心4线程)

内存:4G DDR3内存

硬盘:1T HDD

带宽:30M优化/100M普通

IP数:1个(10G防护)

价格:499/月

香港站群服务器

香港多IP站群服务器租用

CPU:E3-1230V2(4核

内存:8G DDR3内存

硬盘:240G SSD/1T SATA

带宽:10M优化

IP数:125个IP(1/2C)

价格:1099/月

美国站群服务器

美国多IP站群服务器租用

CPU:E3-1230V2(4核)

内存:16G DDR3内存

硬盘:1T HDD/240G SSD

带宽:30M优化/100M普通

IP数:125个IP(1/2C)

价格:999/月

b'
\n
\n

在上一篇 ​​SkeyeRTMPClient拉取RTMP流扩展支持HEVC(H.265)解决方案​​​ 中关于HEVCDecoderConfigurationRecord结构解析的讲解存在一些表述上不清楚的地方,本文为之续篇,重点对HEVC格式的MetaData结构的解析进行讲解。\n在SkeyeRTMPPusher扩展支持H265的解决方案讲述时。我们对Metadata结构进行过详解,大家可以回顾一下这篇文章​​​RTMP推送扩展支持HEVC(H265)之Metadata结构填写详解​​,重点来了,因为正常情况下,我们只需要从MetaData中取出对我们解码有用的数据头(即VPS,SPS和PPS),所以我们对HEVCDecoderConfigurationRecord填充的MetaData其他数据并不关心,但是,在解析时,我们需要对该结构所有数据都解析出来,以保证能准确无误的获取到我们所需要的数据头信息。

再次回顾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;

而事实上,该结构如果直接填入到MetaData中是不正确的,我们看ffmpeg中hevc.c文件中的实现,该结构详细声明如下:

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

从上代码段我们可以看出,以general_constraint_indicator_flags这个参数为例,结构体声明位宽64,而实际位宽是48,,所以结构体声明的参数位宽和实际位宽可能是不对等的,这就将导致解析MetaData时发生错位,从而解析发生错误,从而,我们从新认识HEVCDecoderConfigurationRecord,并声明其结构如下:

// RTMP扩展支持HEVC(H.265) [4/18/2019 SwordTwelve]
typedef struct _Parser_HEVCDecoderConfigurationRecord {
uint8_t configurationVersion;
uint8_t general_profile_space:2;
uint8_t general_tier_flag:1;
uint8_t general_profile_idc:5;
uint32_t general_profile_compatibility_flags;//6
uint8_t general_constraint_indicator_flags[6];//12
uint8_t general_level_idc;
uint8_t reserved1:4;// bit(4) reserved = ‘1111’b;
uint8_t min_spatial_segmentation_idc_L:4;//12位之低4位
uint8_t min_spatial_segmentation_idc_H;//12位之高8位
uint8_t reserved2:6;//bit(6) reserved = ‘111111’b;
uint8_t parallelismType:2;
uint8_t reserved3:6;//bit(6) reserved = ‘111111’b;
uint8_t chromaFormat:2;
uint8_t reserved4:5;//bit(5) reserved = ‘11111’b;
uint8_t bitDepthLumaMinus8:3;
uint8_t reserved5:5;//bit(5) reserved = ‘11111’b;
uint8_t bitDepthChromaMinus8:3;
uint16_t avgFrameRate;
uint8_t constantFrameRate:2;
uint8_t numTemporalLayers:3;
uint8_t temporalIdNested:1;
uint8_t lengthSizeMinusOne:2;
uint8_t numOfArrays;
//Parser_HVCCNALUnitArray *array;
} Parser_HEVCDecoderConfigurationRecord;

现在位宽已经对齐,我们可以直接从MetaData里面讲该结构拷贝出来,从而获取到正确的参数和值,如下代码所示:

Parser_HEVCDecoderConfigurationRecord *decoder_header = (Parser_HEVCDecoderConfigurationRecord*)((char*)parser_config);
parser_offset += sizeof(Parser_HEVCDecoderConfigurationRecord);
int nNumOfArrays = decoder_header->numOfArrays;
for (int i=0; i<nNumOfArrays; i++)
{
......
}

同理,我们从MetaData中拷贝出nal单元数据头也是需要考虑这个问题,这里我们声明Parser_HVCCNALUnitArray结构如下:

// RTMP扩展支持HEVC(H.265) [4/18/2019 SwordTwlve]
typedef struct _Parser_HVCCNALUnitArray {
uint8_t NAL_unit_type;
// uint16_t numNalus;
// uint16_t *nalUnitLength;
// uint8_t **nalUnit;
} Parser_HVCCNALUnitArray;

如上代码所示,为了保证字节对齐,我们只保留NAL_unit_type参数,通过字节拷贝运行,计算出nalu的数量numNalus,然后再从nalunit数组中取得我们需要的头信息。

Parser_HVCCNALUnitArray* pNALUnit = (Parser_HVCCNALUnitArray*)((char *)parser_config+parser_offset);
parser_offset += sizeof(Parser_HVCCNALUnitArray);
int numNalus = ntohs(*(unsigned short *)((char *)parser_config + parser_offset));
parser_offset += 2;
for (int nI=0; nI<numNalus; nI++)
{
......
}

欢迎大家下载SkeyePlayer测试播放支持H265的RTMP流:\n​​https://gitee.com/visual-opening/skeyplayer​​​\n​​​https://gitee.com/visual-opening/skeyeexspeedplayer​


\n
\n \n
\n
\n\n \n \n \n\n
\n
\n '
内容来源于网络如有侵权请私信删除

推荐文章