衆所周知,RTMP标準協定實際是不支援HEVC(H.265)編碼格式的,同樣,現行的H5标準裡面,也沒有對H.265的描述,是以,在很大程度上,H5網頁浏覽器是無法接入HEVC(H.265)的,但是,在很多原生用戶端中,對HEVC(H.265)的需求是非常大的,那麼,像EasyNVR中,如果需要接入HEVC(H.265)流的時候,我們應該如何支援呢?
不久前剛實作EasyRTMP擴充支援h265推送,當時在網上也查找了很多資料,發現都不盡詳細,而官方也沒有更新對HEVC(H265,後文統稱HEVC)tag的支援,反正是走了不少彎路,當然,在廣大網友以及ffmpeg代碼的幫助下我最終實作了通過EasyRTMP推送HEVC視訊幀資料到EasyDSS,這裡我将把實作過程詳細的記錄下來,供廣大網友參考。
首先, RTMP頭部資訊封裝并沒有定義HEVC,我們采用CDN聯盟的HEVC(H.265)擴充标準,将HEVC的VideoTagHeader定義為12,詳見下圖:
然後,我們在H.264封裝的基礎上進行改進,以支援HEVC頭部的封裝,而HEVC頭有
SPS PPS VPS,我們參考ffmpeg的HEVCDecoderConfigurationRecord結構對metadata進行封裝,該結構體代碼如下:
typedef struct HVCCNALUnitArray {
uint8_t array_completeness;
uint8_t NAL_unit_type;
uint16_t numNalus;
uint16_t *nalUnitLength;
uint8_t **nalUnit;
} HVCCNALUnitArray;
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;
參考ffmeg對該結構進行初始化如下:
static void hvcc_init(HEVCDecoderConfigurationRecord *hvcc)
{
memset(hvcc, 0, sizeof(HEVCDecoderConfigurationRecord));
hvcc->configurationVersion = 1;
hvcc->lengthSizeMinusOne = 3; // 4 bytes
/*
* The following fields have all their valid bits set by default,
* the ProfileTierLevel parsing code will unset them when needed.
*/
hvcc->general_profile_compatibility_flags = 0xffffffff;
hvcc->general_constraint_indicator_flags = 0xffffffffffff;
/*
* Initialize this field with an invalid value which can be used to detect
* whether we didn't see any VUI (in which case it should be reset to zero).
*/
hvcc->min_spatial_segmentation_idc = MAX_SPATIAL_SEGMENTATION + 1;
}
需要注意的是,該結構其他參數我們其實可以不特别關心,我們隻需要在HVCCNALUnitArray數組中把HEVC的VPS,SPS和PPS資訊填入即可。
最後,填寫好Metadata資訊後,我們還需要在發送幀資料的時候做一下修改,将I幀的tag頭改成12,P幀tag不變,設定成1即可,如下代碼所示:
int i = 0;
if(bIsKeyFrame)
{
//body[i++] = 0x17;// 2:Pframe 7:AVC
body[i++] = (m_metadata.nVideoCodec == FLV_CODECID_HEVC) ? 0x1C:0x17;// 1:Iframe 7:AVC 12:HEVC
if (m_bWaitingKeyFrame)
{
m_bWaitingKeyFrame = false;
}
}
else
{
//body[i++] = 0x27;// 2:Pframe 7:AVC
body[i++] = (m_metadata.nVideoCodec == FLV_CODECID_HEVC) ? 0x2C:0x27;// 1:Iframe 7:AVC 12:HEVC
}
body[i++] = 0x01;// AVC NALU
body[i++] = 0x00;
body[i++] = 0x00;
body[i++] = 0x00;
// NALU size
body[i++] = size>>24;
body[i++] = size>>16;
body[i++] = size>>8;
body[i++] = size&0xff;
EasyRTMP是TSINGSEE青犀開放平台(http://open.tsingsee.com)一套調用簡單、功能完善、運作高效穩定的RTMP功能元件,經過多年實戰和線上運作打造,支援RTMP推送斷線重連、環形緩沖、智能丢幀、網絡事件回調,支援Windows、Linux、arm(hisiv100/hisiv200/hisiv300/hisiv400/etc…)、Android、iOS平台,支援市面上絕大部分的RTMP流媒體伺服器,包括Red5、Ngnix_rtmp、crtmpserver等主流RTMP伺服器,能夠完美應用于各種行業的直播需求,手機直播、桌面直播、錄影機直播、課堂直播等等方面!