找回密码
 立即注册
收起左侧

历时十年/RK3588视频监控系统/RKMPP硬解和GPU绘制/网络摄像头...

0
回复
434
查看
[复制链接]
累计签到:7 天
连续签到:1 天
来源: 2025-3-14 09:37:05 显示全部楼层 |阅读模式

## 一、前言说明
现在国产嵌入式板子,越来越多人用瑞星微的多媒体芯片,硬解硬编用RKMPP,早期的ffmpeg并没有集成RKMPP,这种情况一般是需要厂家主动按照ffmpeg的框架来编写对应代码开放出来,然后用户编译ffmpeg的时候加入到编解码中,ffmpeg整体框架的设计非常牛逼,上层设计在设计之初就考虑到可能的自定义编解码这块,方便新的硬件厂家加入,如今全宇宙主要的音视频框架就两个,一个是ffmpeg,一个是gstream,学习资料主要是ffmpeg多一些,gstream学习成本比较高,所以目前主要的人群学习ffmpeg居多。

最早监控系统开发就已经考虑好了模块化的方式,采用一个个core组件模块来实现,这样一旦某个组件模块更新了代码,只需要替换这个组件的代码就行,一般就是一个pri带一个目录,同理监控内核ffmpeg模块也是如此,于是抽空将对RKMPP硬解的支持也加入了其中,这其中的关键并不是使用硬解解码部分,后面捣鼓下来发现关键是要编译一个支持RKMPP硬解的ffmpeg库就行(https://github.com/nyanmisaka/ffmpeg-rockchip),把这个库替换掉,然后硬解名称选择RKMPP即可,整个硬解流程和之前的dxva2/d3d11va一模一样,然后解码后的数据发给opengl绘制,数据格式一般是NV12。打完收工非常完美。

## 二、效果图


## 三、相关代码
```cpp
#include "devicevideosmart.h"
#include "qthelper.h"
#include "appevent.h"
#include "videowidgetx.h"

DeviceVideoSmart:eviceVideoSmart(VideoWidget *videoWidget, QObject *parent) : QThread(parent)
{
    stopped = false;
    isPause = false;
    this->videoWidget = videoWidget;

    //关联播放开始和完成信号
    connect(videoWidget, SIGNAL(sig_receivePlayStart(int)), this, SLOT(receivePlayStart(int)));
    connect(videoWidget, SIGNAL(sig_receivePlayFinsh()), this, SLOT(receivePlayFinsh()));
    //关联实时码率信号将实时码率作为文字水印贴到视频上
    connect(videoWidget, SIGNAL(sig_receiveKbps(qreal, int)), this, SLOT(receiveKbps(qreal, int)));

    //需要注册下这个类型
    qRegisterMetaType<QList<QRect> >("QList<QRect>");
}

//定义三个标志位控制启用哪些测试
static bool testImage = false;
static bool testGraph = true;
static bool testOsd = true;
static int sleepTime = 50;

void DeviceVideoSmart::run()
{
    while (!stopped) {
        if (isPause || AppConfig:olling || !AppConfig::VideoSmart || !videoWidget || !videoWidget->getIsRunning()) {
            msleep(10);
            continue;
        }

        //定义区域集合
        QList<QRect> rects;
        if (testImage) {
            //获取一张图片用于人工智能处理
            QImage image = videoWidget->getImage();
            //qDebug() << TIMEMS << videoWidget->getVideoThread()->getFlag() << image.size();

            //人工智能处理算法放在这里进行运算
            //yolo->take(image);
        }

        //这里演示图形区域信息
        if (testGraph) {
            //限定下区域
            int w = videoWidget->getVideoWidth();
            int h = videoWidget->getVideoHeight();
            int width = w - 100;
            int height = h - 100;
            int wx = w / 50;
            int hx = h / 50;

            //随机生成矩形
            for (int i = 0; i < 30; ++i) {
                int x = rand() % width;
                int y = rand() % height;
                int w = wx + rand() % 40;
                int h = hx + rand() % 30;
                rects << QRect(x, y, w, h);
            }

            //异步执行
            QMetaObject::invokeMethod(this, "receiveRects", Q_ARG(QList<QRect>, rects));
        }

        //这里演示文字标签信息
        if (testOsd) {
            int currentCount = rand() % 60;
            int maxCount = rand() % 60 + 30;
            //qDebug() << TIMEMS << currentCount << currentThreadId();
            QMetaObject::invokeMethod(this, "receiveData", Q_ARG(QString, "currentCount"), Q_ARG(QVariant, currentCount));
            QMetaObject::invokeMethod(this, "receiveData", Q_ARG(QString, "maxCount"), Q_ARG(QVariant, maxCount));
        }

        //必须要有这个延时休息一下
        msleep(sleepTime);
    }

    stopped = false;
    isPause = false;
}

QColor DeviceVideoSmart::getColor()
{
    //取随机颜色
    static QStringList colors = QColor::colorNames();
    return colors.at(rand() % colors.count());
}

void DeviceVideoSmart::receivePlayStart(int time)
{
    //没开启对应功能则不用继续
    if (!AppConfig::VideoSmart) {
        return;
    }

    //字体大小按照视频宽度比例来
    fontSize = videoWidget->getVideoWidth() / 30;
    //设置时间水印
    this->receiveData("dateTime", 0);
    //设置启用视频帧数据锁
#ifdef ffmpeg
    QMetaObject::invokeMethod(videoWidget->getVideoThread(), "setLockData", Q_ARG(bool, true));
#endif

    //播放开始启动线程
    stopped = false;
    isPause = false;
    this->start();
}

void DeviceVideoSmart::receivePlayFinsh()
{
    //播放结束停止线程
    this->stop();
}

void DeviceVideoSmart::receiveKbps(qreal kbps, int frameRate)
{
    OsdInfo osd;
    osd.name = "kbps";
    osd.color = "#FF0000";
    osd.fontSize = fontSize;
    osd.position = OsdPosition_RightTop;
    osd.text = QString("%1 kbps\n%2 fps").arg((int)kbps).arg(frameRate);
    videoWidget->setOsd(osd);
}

void DeviceVideoSmart::receiveRects(const QList<QRect> &rects)
{
    int borderWidth = videoWidget->getVideoWidth() / 500;
    borderWidth = borderWidth < 1 ? 1 : borderWidth;

    int count = rects.count();
    for (int i = 0; i < count; ++i) {
        GraphInfo graph;
        graph.name = QString("graph%1").arg(i);
        graph.borderColor = getColor();
        graph.borderWidth = borderWidth;
        graph.rect = rects.at(i);
        videoWidget->setGraph(graph);
    }
}

void DeviceVideoSmart::receiveData(const QString &type, const QVariant &data)
{
    OsdInfo osd;
    osd.name = type;
    if (type == "dateTime") {
        osd.fontSize = fontSize;
        osd.color = "#FFFFFF";
        osd.format = OsdFormat_DateTime;
        osd.position = OsdPosition_LeftTop;
        videoWidget->setOsd(osd);
    } else if (type == "currentCount") {
        osd.fontSize = fontSize;
        osd.text = QString("当前人数: %1").arg(data.toInt());
        osd.color = getColor();//"#D64D54";
        osd.format = OsdFormat_Text;
        osd.position = OsdPosition_LeftBottom;
        videoWidget->setOsd(osd);
    } else if (type == "maxCount") {
        osd.fontSize = fontSize;
        osd.text = QString("限定人数: %1").arg(data.toInt());
        osd.color = getColor(); "#FDCD72";
        osd.format = OsdFormat_Text;
        osd.position = OsdPosition_RightBottom;
        videoWidget->setOsd(osd);
    }
}

void DeviceVideoSmart::stop()
{
    stopped = true;
    this->wait();
}

void DeviceVideoSmart::pause()
{
    isPause = true;
}

void DeviceVideoSmart::next()
{
    isPause = false;
}
```

## 四、相关地址
1. 国内站点:[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun)
2. 国际站点:[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun)
3. 个人作品:[https://blog.csdn.net/feiyangqingyun/article/details/97565652](https://blog.csdn.net/feiyangqingyun/article/details/97565652)
4. 文件地址:[https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g](https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g) 提取码:01jf 文件名:bin_video_system。

## 五、功能特点
### 5.1 软件模块
1. 视频监控模块,各种停靠小窗体子模块,包括设备列表、图文警情、窗口信息、云台控制、预置巡航、视频轮询、设备控制、悬浮地图、网页浏览等。
2. 视频回放模块,包括本地回放、网络回放、远程回放、图片回放、视频上传等。
3. 电子地图模块,包括图片地图、设备地图、设备移动、轨迹回放等。
4. 日志查询模块,包括本地日志、设备日志等。
5. 系统设置模块,包括系统设置(基本设置、视频参数、数据库设置、颜色配置、功能激活等)、录像机管理、摄像机管理、轮询配置、录像计划、用户管理、其他设置等。

### 5.2 基础功能
1. 支持各种音视频流(rtsp、rtmp、http、srt、ws等)、音视频文件(mp4、rmvb、avi等)、本地设备(本地摄像头、麦克风、桌面)。
2. 支持多画面切换,包括1、4、6、8、9、13、16、25、36、64画面切换。
3. 支持全屏切换,多种切换方式包括鼠标右键菜单、工具栏按钮、快捷键(alt+enter全屏,esc退出全屏)。
4. 支持视频轮询,包括1、4、9、16画面轮询,可设置轮询分组(轮询预案)、轮询间隔、码流类型等。
5. 支持onvif协议,包括设备搜索、云台控制、预置位管理、设备控制(图片参数、校对时间、系统重启、抓拍图片、OSD配置、网络配置等)。
6. 支持权限管理,不同的用户可以对应不同的模块权限,比如删除日志、关闭系统等。
7. 数据库支持多种,包括sqlite、mysql、sqlserver、postgresql、oracle、人大金仓等。
8. 支持本地设备采集比如本地桌面和摄像头,支持设置分辨率、帧率等参数,支持多屏幕。
9. 所有停靠模块都自动生成对应的菜单用来控制显示和隐藏,在标题栏右键可以弹出。
10. 支持显示所有模块、隐藏所有模块、复位普通布局、复位全屏布局。
11. 支持图片地图和网页地图上双击设备图标弹出实时预览。
12. 摄像机节点拖曳到对应窗体播放视频,同时支持拖曳本地文件直接播放。
13. 设备树双击分组打开对应分组下的所有视频,双击设备子节点直接打开对应设备视频流。自动加载最后展开的节点。
14. 设备树支持自定义配置,可以添加分组、删除分组、修改分组,任意层级设置。
15. 设备树可以开启是否放大字体显示、是否显示主码流子码流节点、是否隐藏空组(没有设备的分组自动隐藏)。
16. 删除视频支持鼠标右键删除、悬浮条关闭删除、拖曳到视频监控面板外删除等多种方式。
17. 图片地图上设备按钮可自由拖动,自动保存位置信息。地图上可以鼠标单击获取经纬度信息,用来更新设备位置。
18. 视频监控面板窗体中任意通道支持拖曳交换,瞬间响应。
19. 网页地图支持视图切换、运动轨迹显示、设备点位,鼠标按下获取经纬度等。
20. 双击节点、拖曳节点、拖曳窗体交换位置等操作,均自动更新保存最后的播放地址,下次软件打开自动应用。
21. 右下角音量条控件,失去焦点自动隐藏,音量条带静音图标,自动记忆最后的音量及静音状态。
22. 支持视频截图,可指定单个或者对所有通道截图,底部小工具栏也有截图按钮,每个视频控件悬浮条也有抓拍按钮。
23. 支持辅屏预览,可以打开多个,在多个屏幕分别打开64通道,按需显示视频。
24. 支持超时自动隐藏鼠标指针、自动全屏机制。
25. 支持onvif云台控制,可上下左右移动云台摄像机,包括复位和焦距调整等。
26. 支持onvif预置位,可以添加、删除、修改预置位,可以调用起始位。
27. 支持OSD增删改查,可以通过onvif协议添加及修改OSD信息。
28. 支持onvif图像参数设置,包括明亮度、对比度、饱和度、尖锐度等。
29. 支持onvif其他操作,包括抓图、网络设置、校时、重启、事件订阅等。
30. 支持任意onvif摄像机,包括但不限于海康、大华、宇视、天地伟业、华为等。
31. 可保存视频,可通过录像计划存储,也可在悬浮条手动切换开始录像和停止录像。
32. 可设置视频流通信方式tcp或udp,可设置视频解码是速度优先、质量优先、均衡处理、最快速度等。
33. 可设置软件中文名称、英文名称、LOGO图标等。
34. 存储的视频文件支持导出到指定目录,支持批量上传到服务器。
35. 完善的录像计划设置,支持每个通道7 * 24小时每半小时设置是否存储录像。
36. 音视频同步显示以及音视频同步存储到MP4文件。

### 5.3 特色功能
1. 主界面采用停靠窗体模式,各种组件以小模块的形式加入,可自定义任意模块加入。
2. 停靠模块可拖动任意位置嵌入和悬浮,支持最大化全屏,支持多屏幕。
3. 双重布局文件存储机制,正常模式、全屏模式都对应不同的布局方案,自动切换和保存,比如全屏模式可以突出几个模块透明显示在指定位置,更具科幻感现代化。
4. 原创onvif协议机制,采用底层协议解析(udp广播搜索+http请求执行命令)更轻量易懂易学习拓展,不依赖任何第三方组件比如gsoap。
5. 原创数据导入、导出、打印机制,跨平台不依赖任何组件,瞬间导出数据。
6. 内置多个原创组件,宇宙超值超级牛逼,包括数据导入导出组件(导出到xls、pdf、打印)、数据库组件(数据库管理线程、自动清理数据线程、万能分页、数据请求等)、地图组件、视频监控组件、文件多线程收发组件、onvif通信组件、通用浏览器内核组件等。
7. 自定义信息框、错误框、询问框、右下角提示框(包含多种格式)等。
8. 精美换肤,高达20套皮肤样式随意更换,所有样式全部统一,包括菜单等。
9. 选中通道对应设备树节点高亮,选中通道节点对应视频控件高亮,方便查看当前通道信息。
10. 视频控件悬浮条可以自行增加多个按钮,监控界面底部小工具栏也可自行增加按钮。
11. 双击摄像机节点自动播放视频,双击节点自动依次添加视频,会自动跳到下一个,双击父节点自动添加该节点下的所有视频。可选主码流、子码流。
12. 录像机管理、摄像机管理,可添加删除修改导入导出打印信息,立即应用新的设备信息生成树状列表,不需重启。
13. 摄像机搜索支持一键搜索和批量添加,支持onvif的NVR一键添加子设备,可以手动设置开始地址和数量一键生成摄像机信息。
14. 可选多种内核自由切换,ffmpeg、vlc、mpv等,均可在pro中设置。推荐用ffmpeg,跨平台最多,默认提供好了linux和mac平台上编译好的库。
15. 支持windows、linux、macos等系统硬解码,还支持嵌入式linux RKMPP硬解码,可设置硬解码类型(dxva2、d3d11va、vaapi、vdpau等)。
16. 各种模块可以勾选是否激活,方便根据实际需求搭配各种组合,比如隐藏电子地图模块,隐藏远程回放模块只保留本地回放等。
17. 尽最大化可能,将常用的功能封装接口,全局静态函数调用,极其容易使用,提供各种使用示例,方便用户二开。
18. 默认采用opengl绘制视频,超低的CPU资源占用,支持yuyv和nv12两种格式绘制,性能爆表。
19. 标签和图形信息支持三种绘制方式,绘制到遮罩层、绘制到图片、源头绘制(对应信息可以存储到文件)。
20. 包括但不限于视频监控内核组件的所有功能,可参阅说明书**能介绍 [视频监控内核](###8.1 视频监控内核)。
21. 高度可定制化,用户可以很方便的在此基础上衍生自己的功能,比如增加自定义模块,增加运行模式、机器人监控、无人机监控、挖掘机监控、广播监控等。
22. 支持xp、win7、win10、win11、linux、mac、各种国产系统(UOS、中标麒麟、银河麒麟等)、嵌入式linux等系统。
23. 注释完整,项目结构清晰,超级详细完整的使用开发手册,精确到每个代码文件的功能说明,不断持续迭代版本。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了