首页 技术 正文
技术 2022年11月15日
0 收藏 999 点赞 3,563 浏览 4093 个字

在上一篇方案《EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)》我们介绍了将海康安防摄像机进行互联网直播的整体方案流程,其中有一个流程“数据处理与分析”我们当时没有展开详述,今天我们将海康HCNetSDK实时预览回调接口数据处理的过程,尤其是在RealDataCallback中对AVData的处理过程:

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;//TRACE("m_mFrameCacheLenth==%d\r\n", pDemoDlg->m_mFrameCacheLenth);// 最大缓存数据个数设为pDemoDlg->m_mFrameCacheLenth,程序会过程中报错,Why? [5/6/2014-13:19:51 Dingshuai]
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;
}
// 缓存数据个数 清0
m_mFrameCacheLenth[lRealHandle] = 0;
}
}
}
}
}

通过对上述代码的分析,整个视频流回调过程还是比较简单的:判断回调数据类型(NET_DVR_STREAMDATA)–》海康PS数据Demux成音视频ES数据(GetH246FromPS)–》对关键帧数据做缓存和处理(bIsKeyFrame )–》进入转推RTMP缓存队列(WriteH264DataToChace)

海康PS流解析:

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推流:

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

经过上述步骤,基本完成了对一路SDK数据回调的处理,如果需要一个完整的、系统的、中间件级别的流转服务,那还需要一套完整的控制机制和配套接口以及前端界面,就类似于EasyNVR一样。

本文中所述详细代码见:https://github.com/EasyDSS/EasyRTMP/tree/master/EasyRTMP_HIK

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,492
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,907
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,740
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,494
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,132
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,295