獲取海康威視實時流用平台支持的RTMP推流組件libEasyRTMP向服務器推流數據的處理方法


背景分析

RTMP是Real Time Messaging Protocol(實時消息傳輸協議)的首字母縮寫,該協議基於TCP,是一個協議族,包括RTMP基本協議及RTMP/RTMPS/RTMPE等多種變種。RTMP是一種設計用來進行實時數據通信的網絡下ieyi,主要用來在Flash/AIR平台和支持RTMP協議的流媒體/交互服務器之間進行音視頻和數據通信。RTMP推流,就是將直播內容推送到服務器的過程。

EasyRTMP是結合了多種音視頻緩存及網絡技術的一個rtmp直播推流端,包括:圓形緩沖區(circular buffer)、智能丟幀、自動重連、rtmp協議等等多種技術,能夠非常有效地適應各種平台(Windows、Linux、ARM、Android、iOS),各種網絡環境(有線、wifi、4G),以及各種情況下的直播恢復(服務器重啟、網絡重啟、硬件設備重啟)。

獲取海康威視實時流用libEasyRTMP向服務器推流數據處理

1、根據SDK接入方式先進行設備登錄

NET_DVR_Login_V30(DeviceIP,m_nLoginPort,cUserName,cPassword,&devInfo)

2、啟動實時預覽

NET_DVR_RealPlay_V40(m_lUserID[nI],&struPlayInfo,fRealDataCallBack, this)

3、在fRealDataCallBack回調函數中處理視頻流數據(包括復合流和音視頻分開的視頻流數據),類型定義為NET_DVR_STREAMDATA。

詳細處理過程:
判斷回調數據類型(NET_DVR_STREAMDATA ——> 海康PS數據Demux成音視頻ES數據(GetH246FromPS) ——> 對關鍵幀數據做緩存和處理(bIsKeyFrame ) ——> 進入轉推RTMP緩存隊列(WriteH264DataToChace)
case NET_DVR_STREAMDATA:

{
BOOL inData=PlayM4_InputData(nPort,pBuffer,dwBufSize);
	while (!inData)
	{
	Sleep(10);

inData=PlayM4_InputData(nPort,pBuffer,dwBufSize);
	OutputDebugString("PlayM4_InputData failed \n");	
	}	
	//PS流數據解析處理		
	{
		int nI = 0;
		int nCacheSize = 0;
		nI = m_mFrameCacheLenth[lRealHandle];
		//直接--提取H264數據
		BOOL bVideo = FALSE;
		BOOL bPatialData = FALSE;
		bPatialData = GetH246FromPS(pBuffer,dwBufSize, &m_pFrameCache[lRealHandle][nI].pCacheBuffer, 
		m_pFrameCache[lRealHandle][nI].nCacheBufLenth, bVideo);

		if (bVideo)
		{
			if (bPatialData)//部分包數據
			{
				//緩存數據
				m_pFrameCache[lRealHandle][nI].lTimeStamp = clock();
				m_mFrameCacheLenth[lRealHandle]++;
			} 
			else//包頭
			{
				int i = 0;
				if(m_mFrameCacheLenth[lRealHandle]>0)
				{
Long  lH264DataLenth = m_mFrameCacheLenth[lRealHandle]*MAX_PACK_SIZE;
					BYTE* pH264Nal =  NULL;
					pH264Nal = new BYTE[lH264DataLenth];
					memset(pH264Nal, 0x00, lH264DataLenth);
					BYTE* pTempBuffer = pH264Nal;
					int nTempBufLenth = 0;
					for (i=0; i</*MAX_FRAME_LENTH*/m_mFrameCacheLenth[lRealHandle]; i++)
					{						
if(m_pFrameCache[lRealHandle][i].pCacheBuffer!=NULL&&m_pFrameCache[lRealHandle][i].nCacheBufLenth>0)
								{
						memcpy(pTempBuffer, m_pFrameCache[i].pCacheBuffer, m_pFrameCache[i].nCacheBufLenth);
							// 										
pTempBuffer = pTempBuffer + m_pFrameCache[i].nCacheBufLenth;

							memcpy(pH264Nal+nTempBufLenth, m_pFrameCache[lRealHandle][i].pCacheBuffer, 
								m_pFrameCache[lRealHandle][i].nCacheBufLenth);
							nTempBufLenth += m_pFrameCache[lRealHandle][i].nCacheBufLenth;
						}
						if (m_pFrameCache[lRealHandle][i].pCacheBuffer)
						{
							delete [](m_pFrameCache[lRealHandle][i].pCacheBuffer);
					m_pFrameCache[lRealHandle][i].pCacheBuffer = NULL;
						}	
					m_pFrameCache[lRealHandle][i].nCacheBufLenth = 0; 
					}
					if(m_bRtmpRunning&& pH264Nal && nTempBufLenth>0)
					{
						BOOL bIsKeyFrame = FALSE;
						//查找是否為關鍵幀
						if(pH264Nal[4]==0x67)
						{
							bIsKeyFrame = TRUE;
						}
						long lTimeStamp = clock();
						WriteH264DataToChace(lRealHandle, pH264Nal, nTempBufLenth, bIsKeyFrame, lTimeStamp);
					}

					if (pH264Nal)
					{
						delete []pH264Nal;
						pH264Nal = NULL;
					}
					m_mFrameCacheLenth[lRealHandle] = 0;
				}
			}
		}
	}

//從PS流中取H264數據
//返回值:是否為數據包 
BOOL CDecCallBack_DemoDlg::GetH246FromPS(IN BYTE* pBuffer, IN int nBufLenth, BYTE** pH264, int& nH264Lenth, BOOL& bVideo)
{
	if (!pBuffer || nBufLenth<=0)
	{
		return FALSE;
	}
	BYTE* pH264Buffer = NULL;
	int nHerderLen = 0;
	if( pBuffer
		&& pBuffer[0]==0x00
		&& pBuffer[1]==0x00 
		&& pBuffer[2]==0x01
		&& pBuffer[3]==0xE0)//E==視頻數據(此處E0標識為視頻)
	{
		bVideo = TRUE;
		nHerderLen = 9 + (int)pBuffer[8];//9個為固定的數據包頭長度,pBuffer[8]為填充頭部分的長度
		pH264Buffer = pBuffer+nHerderLen;
		if (*pH264 == NULL)
		{
			*pH264 = new BYTE[nBufLenth];
		}
		if (*pH264&&pH264Buffer&&(nBufLenth-nHerderLen)>0)
		{	
			memcpy(*pH264, pH264Buffer, (nBufLenth-nHerderLen));
		}	
		nH264Lenth = nBufLenth-nHerderLen;
		return TRUE;
	}	
	else if(pBuffer 
		&& pBuffer[0]==0x00
		&& pBuffer[1]==0x00
		&& pBuffer[2]==0x01
		&& pBuffer[3]==0xC0) //C==音頻數據?
	{
		*pH264 = NULL;
		nH264Lenth = 0;
		bVideo = FALSE;
	}
	else if(pBuffer 
		&& pBuffer[0]==0x00
		&& pBuffer[1]==0x00
		&& pBuffer[2]==0x01
		&& pBuffer[3]==0xBA)//視頻流數據包 包頭
	{
		bVideo = TRUE;
		*pH264 = NULL;
		nH264Lenth = 0;
		return FALSE;
	}
	return FALSE;
}

//進入轉推RTMP緩存隊列
//H264數據寫入寫文件緩存隊列
int CDecCallBack_DemoDlg::WriteH264DataToChace(int nDevId, BYTE* pBuffer, int nBufSize, BOOL bIsKeyFrame, long lTimeStamp)
{
	if (!pBuffer || nBufSize<=0 || lTimeStamp<0)
	{
		return -1;
	}

	BOOL bKeyFrame  = bIsKeyFrame;
	int nDeviceType = nDevId+1;
	if (m_RtmpHandle && m_bRtmpRunning)
	{
		//H264推送RTMP
		EASY_AV_Frame	avFrame;
		memset(&avFrame, 0x00, sizeof(EASY_AV_Frame));
		avFrame.pBuffer = (unsigned char*)pBuffer;
		avFrame.u32AVFrameLen = nBufSize;
		avFrame.u32VFrameType = (bKeyFrame)?EASY_SDK_VIDEO_FRAME_I:EASY_SDK_VIDEO_FRAME_P;
		avFrame.u32AVFrameFlag = EASY_SDK_VIDEO_FRAME_FLAG;
		avFrame.u32TimestampSec = lTimeStamp/1000000;
		avFrame.u32TimestampUsec = (lTimeStamp%1000000);
		//EnterCriticalSection(&m_cs);
		EasyRTMP_SendPacket(m_RtmpHandle, &avFrame);	
	}
	return 1;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM