• Qt音视频开发18-海康sdk回调


    一、前言

    海康sdk显示实时视频流除了支持句柄方式以外,也支持回调的方式拿到每一张图片自己绘制处理,当然回调除了拿到视频数据,其实音频数据也一块拿到了,自行调用音频设备播放就行,关于海康sdk回调这块,还着实折腾了一阵子才搞定,可能最开始没有参照提供的demo以及没有彻底的搜索吧,只是单单看sdk的文档折腾来折腾去的,搞了一星期居然没搞定,后面找到了正确的办法才发现,原来就差一点点一丢丢呢,这又让我联想到很多事情,包括生活中的事情,不都是如此么?当你铆足了劲,试验搞了各种办法,快要精疲力尽放弃的时候,其实此时离成功就差一步了,真的就差那么一丢丢,处理生活中的很多事情也是如此,所以很多时候如果方向对了,坚持过努力过,还不行的话,再努力一把估计就ok了。

    折腾了很久总结失败在哪里,调用NET_DVR_RealPlay_V40设置回调函数也是对的,回调函数里面也进去了,调用PlayM4_SetDecCallBackMend设置解码回调函数也是对的(这地方也着实折腾了一阵子,没想到还要用播放MP4的形式来处理),最后发现问题出在解码后的数据,数据也都是拿到了,默认是yv12的数据,如果需要转成image的话就需要做个转换,这个转换网上找了一堆的函数来测试,都失败了,后面找到一个yv12转rgb888格式的,终于可以了,我勒个去。

    海康sdk回调流程:

    1. 调用NET_DVR_RealPlay_V40设置回调处理函数。
    2. 在回调处理函数RealDataCallBack中依次处理打开、播放、解码。
    3. 调用PlayM4_GetPort获取播放库未使用的通道号。
    4. 调用PlayM4_OpenStream打开视频流。
    5. 调用PlayM4_SetDecCallBackMend设置解码回调函数,只解码不显示。
    6. 调用PlayM4_Play播放视频流。
    7. 调用PlayM4_InputData循环解码数据。
    8. 在解码回调函数DecCallBack中分别处理音视频数据。
    9. 调用自己封装的yv12ToRGB888函数将数据转成QImage。

    关于回调函数请注意以下几点:

    1. 回调函数必须有关键词 CALLBACK。
    2. 回调函数本身必须是全局函数或者静态函数,不可定义为某个特定的类的成员函数。
    3. 回调函数并不由开发者直接调用执行,只是使用系统接口API函数作为起点。
    4. 回调函数通常作为参数传递给系统API,由该API来调用。
    5. 回调函数可能被系统API调用一次,也可能被循环调用多次。

    二、功能特点

    1. 支持播放视频流和本地MP4文件。
    2. 支持句柄和回调两种模式。
    3. 多线程显示图像,不卡主界面。
    4. 自动重连网络摄像头。
    5. 可设置边框大小即偏移量和边框颜色。
    6. 可设置是否绘制OSD标签即标签文本或图片和标签位置。
    7. 可设置两种OSD位置和风格。
    8. 可设置是否保存到文件以及文件名。
    9. 可直接拖曳文件到haikangwidget控件播放。
    10. 支持h264/h265视频流。
    11. 可暂停播放和继续播放。
    12. 支持存储单个视频文件和定时存储视频文件。
    13. 自定义顶部悬浮条,发送单击信号通知,可设置是否启用。
    14. 可设置画面拉伸填充或者等比例填充。
    15. 可设置解码是速度优先、质量优先、均衡处理。
    16. 可对视频进行截图(原始图片)和截屏(视频窗体)。
    17. 录像文件存储为MP4文件。
    18. 支持焦距控制、云台控制。
    19. 可定制功能。

    三、效果图

    四、相关站点

    1. 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
    2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
    3. 个人主页:https://blog.csdn.net/feiyangqingyun
    4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
    5. 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

    五、核心代码

    //yv12转RGB888
    static bool yv12ToRGB888(const unsigned char *yv12, unsigned char *rgb888, int width, int height)
    {
        if ((width < 1) || (height < 1) || (yv12 == NULL) || (rgb888 == NULL)) {
            return false;
        }
    
        int len = width * height;
        unsigned char const *yData = yv12;
        unsigned char const *vData = &yData[len];
        unsigned char const *uData = &vData[len >> 2];
    
        int rgb[3];
        int yIdx, uIdx, vIdx, idx;
    
        for (int i = 0; i < height; ++i) {
            for (int j = 0; j < width; ++j) {
                yIdx = i * width + j;
                vIdx = (i / 2) * (width / 2) + (j / 2);
                uIdx = vIdx;
    
                rgb[0] = static_cast<int>(yData[yIdx] + 1.370705 * (vData[uIdx] - 128));
                rgb[1] = static_cast<int>(yData[yIdx] - 0.698001 * (uData[uIdx] - 128) - 0.703125 * (vData[vIdx] - 128));
                rgb[2] = static_cast<int>(yData[yIdx] + 1.732446 * (uData[vIdx] - 128));
    
                for (int k = 0; k < 3; ++k) {
                    idx = (i * width + j) * 3 + k;
                    if ((rgb[k] >= 0) && (rgb[k] <= 255)) {
                        rgb888[idx] = static_cast<unsigned char>(rgb[k]);
                    } else {
                        rgb888[idx] = (rgb[k] < 0) ? (0) : (255);
                    }
                }
            }
        }
        return true;
    }
    
    //解码回调 视频为YUV420P数据(YV12),音频为PCM数据
    static void CALLBACK DecCallBack(qport nPort, char *pBuf, qport nSize, FRAME_INFO *pFrameInfo, quser luser, quser nReserved2)
    {
        HaiKangThread *thread = (HaiKangThread *)luser;
        long frameType = pFrameInfo->nType;
    
        //视频数据是 T_YV12 音频数据是 T_AUDIO16
        if (frameType == T_YV12) {
            //qDebug() << TIMEMS << width << height << thread;
            int width = pFrameInfo->nWidth;
            int height = pFrameInfo->nHeight;
            QImage image(width, height, QImage::Format_RGB888);
            if (yv12ToRGB888((unsigned char *)pBuf, image.bits(), width, height)) {
                thread->setImage(image);
            }
        } else if (frameType == T_AUDIO16) {
            //qDebug() << TIMEMS << "T_AUDIO16" << thread;
        }
    }
    
    static void CALLBACK RealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *dwUser)
    {
        //每个类都对应自己的 port
        HaiKangThread *thread = (HaiKangThread *)dwUser;
        qport nPort = thread->port;
    
        DWORD dRet;
        switch (dwDataType) {
            case NET_DVR_SYSHEAD:
                //获取播放库未使用的通道号
                if (!PlayM4_GetPort(&nPort)) {
                    break;
                }
    
                if (dwBufSize > 0) {
                    thread->port = nPort;
                    if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 1024)) {
                        dRet = PlayM4_GetLastError(nPort);
                        break;
                    }
    
                    //设置解码回调函数 只解码不显示
                    if (!PlayM4_SetDecCallBackMend(nPort, DecCallBack, (quser)dwUser)) {
                        dRet = PlayM4_GetLastError(nPort);
                        break;
                    }
    
                    //打开视频解码
                    if (!PlayM4_Play(nPort, NULL)) {
                        dRet = PlayM4_GetLastError(nPort);
                        break;
                    }
    
                    //打开音频解码, 需要码流是复合流
                    if (!PlayM4_PlaySound(nPort)) {
                        dRet = PlayM4_GetLastError(nPort);
                        break;
                    }
                }
                break;
    
            case NET_DVR_STREAMDATA:
                //解码数据
                if (dwBufSize > 0 && nPort != -1) {
                    BOOL inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
                    while (!inData) {
                        sleep(10);
                        inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
                    }
                }
                break;
        }
    }
    
  • 相关阅读:
    JSOIWC2019游记
    基础网络流题单
    【题解】Luogu P2472 [SCOI2007]蜥蜴
    【题解】Luogu P2057 [SHOI2007]善意的投票
    凸包略解
    【题解】Luogu P4324 [JSOI2016]扭动的回文串
    【题解】Luogu P4054 [JSOI2009]计数问题
    kruscal重构树略解
    【题解】bzoj 4478 [Jsoi2013]侦探jyy
    【题解】4465 [Jsoi2013]游戏中的学问
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/13563081.html
Copyright © 2020-2023  润新知