天天看點

EasyNVR錄影機網頁直播中,推流元件EasyRTMP推送RTMP擴充支援HEVC(H.265)的方案

衆所周知,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,詳見下圖:

EasyNVR錄影機網頁直播中,推流元件EasyRTMP推送RTMP擴充支援HEVC(H.265)的方案

然後,我們在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伺服器,能夠完美應用于各種行業的直播需求,手機直播、桌面直播、錄影機直播、課堂直播等等方面!