C++ 使用海康威视SDK将视频推流到rtmp服务器

2023-11-18

研究FFmpeg有两三年了,一直没写过这方面的文章,今天记一下。

由于工作关系,需要将化工企业内部的视频发布到一个部署在公网的视频服务器,然后由相关人员浏览。由于是化工企业,企业严禁外部的机器直接访问视频网络,最多提供一个跳板机。因此,两年多前,针对这种情况,基于FFmpeg研发了一个推流系统。

随着接入视频数量的增加,发现不能单纯的使用RTSP协议获取硬盘录像机视频数据了,海康威视的硬盘录像机,最多允许5个用户同时访问,如果使用RTSP的话,每路RTSP都相当于一个访问用户,因此,需要使用海康卫视的SDK将视频流转为FFmpeg帧。

其实海康威视在开发文档中给出了相应示例,这里我贴一下我写的这部分代码:

 

初始化程序,连接硬盘录像机:

void HKVideo::initInput() {
    inputSuccess = false;
    NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };
    NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = { 0 };
    while (true)
    {
        //---------------------------------------
    // 初始化
        NET_DVR_Init();
        //设置连接时间与重连时间
        NET_DVR_SetConnectTime(2000, 1);
        NET_DVR_SetReconnect(2000, true);

        //---------------------------------------
        //设置异常消息回调函数
        NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);

        //---------------------------------------
        // 注册设备


        //登录参数,包括设备地址、登录用户、密码等
        
        struLoginInfo.bUseAsynLogin = 0; //同步登录方式
        strcpy(struLoginInfo.sDeviceAddress, ip.c_str()); //设备IP地址
        struLoginInfo.wPort = port; //设备服务端口
        strcpy(struLoginInfo.sUserName, user.c_str()); //设备登录用户名
        strcpy(struLoginInfo.sPassword, psd.c_str()); //设备登录密码

        //设备信息, 输出参数
        

        lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
        printf("Login  code: %d\n", NET_DVR_GetLastError());
        if (lUserID < 0)
        {
            
            NET_DVR_Cleanup();
            Sleep(3000);
            continue;
        }
        else {
            printf("lUserID:%d\n", lUserID);
            break;
        }
        
    }
    int size = rtsp_json["channel"].size();
    for (size_t i = 0; i < size; i++)
    {
        int lChannel = rtsp_json["channel"][i].asInt();
        HKVideoPush* push = new HKVideoPush;
        stringstream stream;
        stream << rtmp;
        stream << "_";
        stream << lChannel;
        string rtmp0;
        stream >> rtmp0;
        push->init(rtmp0);
        printf("Channel:%s\n",push->rtmp);
        //---------------------------------------
        //启动预览并设置回调数据流
        //HWND  h = (HWND)cvGetWindowHandle("Mywindow");
        push->struPlayInfo.hPlayWnd = NULL;         //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空
        push->struPlayInfo.lChannel = lChannel;       //预览通道号
        push->struPlayInfo.dwStreamType = 0;       //0-主码流,1-子码流,2-码流3,3-码流4,以此类推
        push->struPlayInfo.dwLinkMode = 0;       //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
        push->struPlayInfo.bBlocked = 1;       //0- 非阻塞取流,1- 阻塞取流

        push->lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &push->struPlayInfo, g_RealDataCallBack_V30, push);
        if (push->lRealPlayHandle < 0)
        {
            printf("NET_DVR_RealPlay_V40 error, %d\n", NET_DVR_GetLastError());
            NET_DVR_Logout(lUserID);
            NET_DVR_Cleanup();
        }
        HKVideos[lChannel] = push;

        HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartHKPushThread, (PVOID)push, 1, 0); //创建子线程
        ResumeThread(h);  //启动子线程
    }
    inputSuccess = true;
    
};

 

 回调函数:

void HKVideoPush::AVFrame2AVPacket(unsigned char* pYUV, int srcWidth, int srcHeight) {
//关键函数,将海康威视视频帧转为FFmpeg帧
    AVFrame* avframe_tmp = av_frame_alloc();//申请一个新的帧
    AVPacket* pkt = av_packet_alloc();//申请一个新的视频包
    avframe_tmp->format = AV_PIX_FMT_YUV420P;
    avframe_tmp->width = srcWidth;
    avframe_tmp->height = srcHeight;
    
    avpicture_fill((AVPicture*)avframe_tmp, pYUV, AV_PIX_FMT_YUV420P, srcWidth, srcHeight);//将海康威视帧数据填充到FFmpeg视频帧中
    //下面这一步很重要,如果没有下面三行代码,rtmp看到的视频颜色会失常
    uint8_t* ptmp = avframe_tmp->data[1];
    avframe_tmp->data[1] = avframe_tmp->data[2];
    avframe_tmp->data[2] = ptmp;

    this->vpts += 1;
    this->lock();//锁定,
    avframe_tmp->pts = this->vpts;
    int ret = avcodec_send_frame(this->vc, avframe_tmp);
    
    if (ret < 0) {
        printf("Error avcodec_send_frame:%d\n", ret);
        av_frame_free(&avframe_tmp);
        this->unlock();
        return;
    }
    av_frame_free(&avframe_tmp);
    ret = avcodec_receive_packet(this->vc, pkt);
    if (ret < 0)
    {
        printf("Error avcodec_receive_packet:%d\n", ret);
        this->unlock();
        return;
    }
    //this->lock();
    this->Pkts.push_back(pkt);//将最终的到的视频包,推入一个vector中,其他线程会从vector中取出包,推流给rtmp
    this->unlock();//解锁
};

void CALLBACK DecCBFun(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, long nReserved1, long nReserved2)
{
    long lFrameType = pFrameInfo->nType;
    if (lFrameType == T_YV12)
    {
        //将海康威视包转为FFmpeg帧
        AVFrame2AVPacket((unsigned char*)pBuf, pFrameInfo->nWidth, pFrameInfo->nHeight);

    }
}

void CALLBACK g_RealDataCallBack_V30(LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, void* dwUser)
{
    HKVideoPush* push = (HKVideoPush*)dwUser;
    int dRet = 0;
    switch (dwDataType)
    {
    case NET_DVR_SYSHEAD: //系统头
        //if (push->lPort >= 0)
        //{
        //    break;  //该通道取流之前已经获取到句柄,后续接口不需要再调用
        //}

        if (push->lPort >= 0)
        {
            break;  //该通道取流之前已经获取到句柄,后续接口不需要再调用
        }

        if (!PlayM4_GetPort(&push->lPort))  //获取播放库未使用的通道号
        {
            break;
        }
        //m_iPort = lPort; //第一次回调的是系统头,将获取的播放库port号赋值给全局port,下次回调数据时即使用此port号播放
        if (dwBufSize > 0)
        {

            if (!PlayM4_OpenStream(push->lPort, pBuffer, dwBufSize, 1600 * 1600))
            {
                dRet = PlayM4_GetLastError(push->lPort);
                break;
            }
            //设置解码回调函数 仅仅解码不显示
            if (!PlayM4_SetDecCallBack(push->lPort, DecCBFun))
            {
                dRet = PlayM4_GetLastError(push->lPort);
                break;
            }

            //设置解码回调函数 解码且显示
            //if (!PlayM4_SetDecCallBackEx(nPort,DecCBFun,NULL,NULL))
            //{
            //  dRet=PlayM4_GetLastError(nPort);
            //  break;
            //}

            //打开视频解码
            if (!PlayM4_Play(push->lPort, push->hWnd))
            {
                dRet = PlayM4_GetLastError(push->lPort);
                break;
            }


        }
        break;
    case NET_DVR_STREAMDATA:   //码流数据
        if (dwBufSize > 0 && push->lPort != -1)
        {
            BOOL inData = PlayM4_InputData(push->lPort, pBuffer, dwBufSize);
            while (!inData)
            {
                inData = PlayM4_InputData(push->lPort, pBuffer, dwBufSize);
                OutputDebugString("PlayM4_InputData failed \n");

            }
        }
        break;
    default: //其他数据
        if (dwBufSize > 0)
        {

        }
        break;
    }
}
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void* pUser)
{
    printf("异常回调%X\n", dwType);
    switch (dwType)
    {
    case EXCEPTION_RECONNECT:    //预览时重连
        break;
    default:
        break;
    }

}

 

 总结:

缺点:这种方式,会造成大量计算,每一帧都会经过计算,我在实际项目使用的时候,使用了一个CPU为J1900的工控机,读取一个硬盘录像机的5路摄像头,CPU占用率达到90% 以上。主要耗费CPU的就是AVFrame2AVPacket函数,它需要大量计算

优点:节省带宽。。。因为海康卫视的SDK解出来的视频包很小,子码流时每路最多200Kb/s(要知道我用RTSP时,子码流每路800Kb)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 使用海康威视SDK将视频推流到rtmp服务器 的相关文章

  • C++ 模板中的名称查找

    我有一些 C 代码 如果没有 fpermissive 选项 就无法再编译 这是我无法分享的专有代码 但我认为我已经能够提取一个简单的测试用例来演示该问题 这是 g 的输出 template eg cpp In instantiation o
  • C 中的复合语句表达式

    下面的代码不起作用 int i void 999 100 添加括号就可以了 为什么 int i void 999 100 还有另一种方法可以完成此类分配 int i void 999 100 是什么让他们与众不同 在这份声明中 int i
  • 如何“杀死”Pthread?

    我正在学习 Pthreads 并且想知道杀死这样一个对象的最佳方法是什么 在寻找类似的问题后 我无法找到 明确 的答案 但请随时向我指出任何相关问题 我正在使用一个小型客户端服务器应用程序 其中服务器主线程正在侦听套接字上的客户端连接 每次
  • 在桌面应用程序中,类库的连接字符串存储在哪里?我可以在app.config中使用吗?

    我是桌面应用程序开发的新手 目前正在使用分层架构 用户界面 DAL BLL 构建桌面应用程序 在 Web 开发中 我曾经将连接字符串存储在 web config 中 我的类库从那里访问它 请指导我在桌面应用程序中如何以及在何处存储 DAL
  • 将列表(对象)转换为列表(字符串)

    有没有办法转换List of Object to a List of String 在 c 或 vb net 中而不迭代所有项目 幕后迭代很好 我只想要简洁的代码 Update 最好的方法可能就是进行新的选择 myList Select f
  • C++ 中的 Java ArrayList [重复]

    这个问题在这里已经有答案了 在Java中我可以做 List
  • 枚举器上的 [[maybe_unused]]

    查看规格 maybe unused http en cppreference com w cpp language attributes 它指出 出现在类 typedef 变量 非静态数据成员 函数 枚举或枚举器的声明中 如果编译器对未使用
  • 套接字:监听积压并接受

    listen sock backlog 在我看来 参数backlog限制连接数量 这是我的测试代码 server initialize the sockaddr of server server sin family AF INET ser
  • 如何用C++解析复杂的字符串?

    我试图弄清楚如何使用 解析这个字符串sstream 和C 其格式为 string int int 我需要能够将包含 IP 地址的字符串的第一部分分配给 std string 以下是该字符串的示例 std string 127 0 0 1 1
  • 简单的文档管理系统和API [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • C# 的空条件委托调用线程安全吗? [复制]

    这个问题在这里已经有答案了 这就是我一直以来编写事件引发者的方式 例如属性更改 public event PropertyChangedEventHandler PropertyChanged private void RaisePrope
  • 使用互斥锁来阻止临界区外部的执行

    我不确定我的术语是否正确 但这里是 我有一个由多个线程使用的函数来写入数据 在注释中使用伪代码来说明我想要的内容 these are initiated in the constructor int data std atomic
  • 使用 Ffmpeg 编辑视频元数据

    我想更改视频元数据 原始视频信息 ffmpeg i video mp4 Metadata major brand mp42 minor version 0 compatible brands isomavc1mp42 creation ti
  • C 的“char”使用什么字符集? [复制]

    这个问题在这里已经有答案了 简单的问题 我最近开始用 C 编程 有一个简单的问题 C 编程语言在其 char 类型中使用什么字符集 例如 ASCII 还是取决于软件 操作系统 char 本质上是 1 个字节 主要在所有操作系统上 所以默认情
  • 如何使用 libpq 获取双精度值?

    The examples http www postgresql org docs 9 3 interactive libpq example htmllibpq 文档中展示了如何通过将整数值转换为主机字节序表示来获取整数值 我很好奇必须做
  • Qt:将拖放委托给子级的最佳方式

    我在 QWidget 上使用拖放 我重新实现了 DragEnterEvent dragLeaveEvent dragMoveEvent 和 dropEvent 效果很好 在我的 QWidget 中 我有其他 QWidget 子级 我希望它们
  • C 中什么函数可以替换字符串中的子字符串?

    给定一个 char 字符串 我想查找所有出现的子字符串并将其替换为备用字符串 我没有看到任何简单的函数可以实现这一点
  • 需要使用 openssl 加密和解密文件的示例 C 代码

    我正在用 Linux C 编写代码 我需要使用以下命令来加密和解密文件 openssl 目前 我使用系统命令 des3 e nosalt k 0123456789012345 in inp file out out file 进行加密 使用
  • win32 API 和 .NET 框架之间的选择

    我必须开发一个适用于 Windows 的应用程序 该应用程序将能够通过网络摄像头识别手势来控制鼠标 我将使用 vc 2008 进行开发 但我很困惑是使用 NET 框架还是核心 win32 API 性能对于我的应用程序非常重要 根据 Ivor
  • 如何将 Metro 应用部署到桌面?

    我正在尝试将我的 C 应用程序部署到我的 Windows 8 Metro 桌面 我可以在 bin 文件夹中看到部署的文件 但是当我尝试打开它们时 出现以下错误 该应用程序只能在 AppContainer 的上下文中运行 我检查了属性上下文菜

随机推荐

  • cmake 解决错误:Cannot specify link libraries for target

    最近研究cmake来配置Qt的编译方法 写好了CMakeLists txt通过编译后却无法链接成功 由于用的是mac osx 还以为是不同系统链接库出了问题 检查他给出的路径 变量 QT LIBRARIES 的内容 为 Volumes De
  • 敬请各位付费专栏的订阅者花点时间移步帮忙做个调查,谢谢!

    老猿有2个付费专栏 一个是使用PyQt开发图形界面Python应用 一个是moviepy音视频开发专栏 由于CSDN付费专栏订阅是不区分专栏的 老猿无法区分是因为哪个专栏得到大家认可的 因此敬请大家配合做个调查 非常感谢 大家调查回复时 根
  • openslide对.svs切成tile,并显示的记录

    仅作为记录 大佬请跳过 文章目录 直接上代码 参考 直接上代码 有 svs图和相应的python包 openslide matplotlib 后可直接运行 import openslide import matplotlib pyplot
  • 算法题记录【华为od】服务中心的最佳位置

    题目描述 思路分析 在我的理解就是查找均值 代码解析 let input1 2 input2 0 10 10 20 20 30 30 40 40 50 sum 0 res result let len input2 length input
  • 测试常见bug

    一 某公司发现 价值100元的商品 在该公司网上商城被以0 01元买走了很多 攻城狮们火速定位 问题原因很快被找到了 原来是购买商品接口的bug 该接口需要3个参数 商品id 商品单价 购买数量 而服务器根据接口传过来的商品单价 0 01元
  • 中国省份城市0-N编号

    1 中国省份0 N编号 上海 1 云南 2 内蒙古 3 北京 4 台湾 5 吉林 6 四川 7 天津 8 宁夏 9 安徽 10 山东 11 山西 12 广东 13 广西 14 新疆 15 江苏 16 江西 17 河北 18 河南 19 浙江
  • 热修复——Bugly让热修复变得如此简单

    一 简述 在上一篇 热修复 Tinker的集成与使用 中 根据Tinker官方Wiki集成了Tinker 但那仅仅只是本地集成 有一个重要的问题没有解决 那就是补丁从服务器下发到用户手机上 如果你团队中的后台开发人员实力够强 那么完全可以自
  • 蓝以中老师《高等代数》第01章:代数学的经典课题,笔记

    蓝以中老师 高等代数 第01章 代数学的经典课题 笔记 如下
  • vue 全局loading的思路和方法

    Vue 全局 loading 的实现思路一般是在 Vue 实例中添加一个 loading 组件 通过控制该组件的显示和隐藏来实现全局 loading 的效果 具体思路如下 创建一个全局的 Vue 组件 Loading 该组件用于显示 loa
  • 使用Aspect切面实现系统日志并存入数据库

    使用Aspect切面实现系统日志并存入数据库 1 pom xml中 加入Maven依赖
  • 标准C++库用法

    本文中提到的函数库有
  • JOIN与INNER JOIN区别

    一 指代不同 1 JOIN 用于根据两个或多个表中的列之间的关系 从这些表中查询数据 2 INNER JOIN 组合两个表中的记录 只要在公共字段之中有相符的值 二 特点不同 1 JOIN 每个主键的值都是唯一的 这样做的目的是在不重复每个
  • JS求任意字符串中出现最多的字符以及出现的次数

    随意定义一个字符串 var str 111iiiw2shhfel000 定义函数 function num str 定义一个空对象 因为这边要求出现最多次数 以及出现的字符 这边使用对象的方式再合适不过了 键值对的形式 var obj 求出
  • 在idea使用本地jetty

    参考 https www jetbrains com idea help run debug configuration jetty server html背景 web开发当中 我觉得服务层的代码尽量用单元测试来测 这样可减少启动web容器
  • HTTP代理编程:Python实用技巧与代码实例

    今天我要与大家分享一些关于HTTP代理编程的实用技巧和Python代码实例 作为一名HTTP代理产品供应商 希望通过这篇文章 帮助你们掌握一些高效且实用的编程技巧 提高开发和使用HTTP代理产品的能力 一 使用Python的requests
  • 小数转化为二进制

    小数转换为二进制方法 a 0 125 10 0 125 10 转化为二进制方法 取每次结果的小数乘以2得到b 取b的整数位 如果b为1 0结束计算得到结果 0 125 2 0 25 gt 取整数部分 0 0 25 2 0 5 gt 0 0
  • 【C#】.Net Framework框架下使用SQLike以及基本概念

    2023年 第32周 第2篇文章 给自己一个目标 然后坚持总会有收货 不信你试试 在C 的 NET Framework框架下 有很多轻量级数据库选择 比如 SQLike就是其中一款 一起来了解SQLike的简单使用吧 目录 一 轻量级数据库
  • 关于VUE 配置文件config详解内容

    const path require path module exports 区分打包环境与开发环境 process env NODE ENV production 打包环境 process env NODE ENV development
  • 【知识蒸馏】知识蒸馏(Knowledge Distillation)技术详解

    参考论文 Knowledge Distillation A Survey 1 前言 近年来 深度学习在学术界和工业界取得了巨大的成功 根本原因在于其可拓展性和编码大规模数据的能力 但是 深度学习的主要挑战在于 受限制于资源容量 深度神经模型
  • C++ 使用海康威视SDK将视频推流到rtmp服务器

    研究FFmpeg有两三年了 一直没写过这方面的文章 今天记一下 由于工作关系 需要将化工企业内部的视频发布到一个部署在公网的视频服务器 然后由相关人员浏览 由于是化工企业 企业严禁外部的机器直接访问视频网络 最多提供一个跳板机 因此 两年多