海康威视实时预览回调PS流用EasyRTMP向RTMP服务器推流中视频数据处理的代码
在上一篇方案《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_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;
}
经过上述步骤,基本完成了对一路SDK数据回调的处理,如果需要一个完整的、系统的、中间件级别的流转服务,那还需要一套完整的控制机制和配套接口以及前端界面,就类似于EasyNVR一样。
本文中所述详细代码见:https://github.com/EasyDSS/EasyRTMP/tree/master/EasyRTMP_HIK
海康威视实时预览回调PS流用EasyRTMP向RTMP服务器推流中视频数据处理的代码的更多相关文章
- 海康威视摄像机Java SDK拉流(二)开启关闭实时预览
本篇介绍海康威视摄像机通过SDK开启关闭实时预览接口 下篇介绍实时预览的回调函数及解码库 测试环境: 系统:Centos 7 SDK:设备网络SDK Linux64 实时预览模块流程: 图中虚线框部分 ...
- iOS - 创建可以在 InterfaceBuilder 中实时预览的自定义控件
一.需求实现一个前后带图标的输入框 这是一个简单的自定义控件,很容易想到自定义一个视图(UIView),然后前后的图标使用 UIImageView 或者 UIButton 显示,中间放一个 UITex ...
- Sublime写MarkDown实时预览
[TOC] Sublime写MarkDown实时预览 Sublime作为神器,实至名归. 首先 1.安装Sublime,并安装Package Control,这里不多说. 2.安装MarkDown P ...
- Sublime、Webstorm等在APICloud平台上全面支持WiFi真机同步和实时预览功能
APICloud工具插件包括APICloud Studio.Sublime Text和Webstorm全面为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同 ...
- MWeb 1.4 新功能介绍一:引入文件夹到 MWeb 中管理,支持 Octpress、Jekyll 等静态博客拖拽插入图片和实时预览
之前在 MWeb 中打开非文档库中的 Markdown 文档,如果文档中有引用到本机图片,是没办法在 MWeb 中显示出来和预览的.这是因为 Apple 规定在 Mac App Store(MAS) ...
- TypeWonder – 在任何网站上实时预览字体效果
TypeWonder 让网页字体的选择过程变得轻松愉快.它可以帮助您在任何网站上快速测试 Web 字体效果!输入网站网址,就能够即时预览的字体的实际效果,还可以从数百种字体中进行挑选,您还可以得到所需 ...
- APICloud全面支持WiFi真机同步和实时预览功能
APICloud工具插件包括APICloud Studio.Sublime Text和Webstorm全面为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同 ...
- 新增WiFi真机同步与实时预览功能 简化真机调试步骤
APICloud工具插件为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同步功能,更新增WiFi真机同步和WiFi真机实时预览两大功能,方便开发者在开发过程 ...
- UI实时预览最佳实践(转)
UI实时预览最佳实践 概要:Android中实时预览UI和编写UI的各种技巧.本文的例子都可以在结尾处的示例代码中看到并下载.如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以 ...
随机推荐
- BeautifulSoup模板简单应用-提取html指定数据(api_name/api_method/api_path,请求body/请求header/pagam参数)
from bs4 import BeautifulSoup import re import os.path import itertools name='newcrm' source_file_pa ...
- 深入理解RocketMQ的消费者组、队列、Broker,Topic
1.遇到的问题:上测试环境,上次描述的鸟问题又出现了,就是生产者发3条数据,我这边只能收到1条数据. 2.问题解决: (1)去控制台看我的消费者启动情况,貌似没什么问题 , (2)去测试服务器里看日志 ...
- [NgRx] NgRx Data Fetching Solution - How to Load Data Only If Needed
We have a reoslver, which everytime we want visit '/courses' route, it will be triggered, then api w ...
- win32线程栈溢出问题 (一)
一.什么是线程栈溢出 我们都知道,每一个win32线程都会开辟一个空间,用来临时存储线程执行时所调用的一系列函数的参数.返回地址和局部变量及其他上下文信息.这个空间就是线程的栈区.栈区的容量是有限的, ...
- 阿里开源线上应用调试利器 Arthas的背后
Arthas是一个功能非常强大的诊断工具,功能点很多,例如:jvm信息.线程信息.搜索类中的方法.跟踪代码执行.观测方法的入参和返回参数等等. 作为有追求的程序员,你不仅要知道它能做什么,更要思考它是 ...
- 关于Pi
- golang-切片
切片练习 package main import "fmt" /*func main() { arr := [10]int {1, 2, 3, 4, 5, 6, 7, 8, 9, ...
- KillTimer不能放在析构函数,可以放在DestroyWindow函数里
转自 https://www.cnblogs.com/huking/archive/2009/11/27/1612201.html KillTimer&析构函数 析构函数中不能用KillTim ...
- Python和多线程(multi-threading)。这是个好主意码?列举一些让Python代码以并行方式运行的方法。
Python并不支持真正意义上的多线程.Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意.Python中有一个被称为Global Interpreter ...
- [代码审计]PHP_Bugs题目总结(1)
0x00 简介 最近这几天看到了许多关于代码审计的ctf题,在电脑里也翻出来好长时间没看过的php_bugs,干脆最近把这个好好看看! 下载地址:https://github.com/bowu678/ ...