基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备

发布时间: 2025-09-28 10:06:18

一、前言说明

这个功能是之前写的监控设备模拟器的衍生,同时集成在该项目中,就是单独写个竖版的app样式的界面,然后编译成安卓版本即可,默认就模拟一路设备,从设备地址下拉框中选择安卓摄像头即可,所有的底层功能使用的就是设备模拟器的,为何会有这个需求?之前的模拟器已经可以将任意的视频文件视频流,以及本地USB摄像头或者桌面采集,转成监控设备,通过onvif协议或者28181协议,和服务端平台端进行交互,当时设计的时候就已经考虑到了跨平台的场景,所以其实这些底层组件代码是完全支持安卓的,为了使得ffmpeg能够正常采集到安卓摄像头,当时还特意搞了很久,编译带avdevice的ffmpeg安卓库,网上几乎都是不带avdevice的,搞得之前还以为代码有问题,原来是对应的库没有带。

目前在手机上只是采集了摄像头的实时视频画面,音频这块还没有实现,在win上使用dshow采集麦克风,在linux上使用alsa采集麦克风,到安卓上目前没有发现有对应的实现,所以也挺鸡肋的,后期估计先采用Qt的麦克风采集,相当于单独音频交互,如果是ffmpeg推流rtsp的话,还要涉及到混流,也就是之前的视频流需要加上采集的音频流,混合后再一起推流。

 

三、相关地址

  1. 国内站点:https://gitee.com/feiyangqingyun
  2. 国际站点:https://github.com/feiyangqingyun
  3. 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 文件地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_simulate。

四、功能特点

  1. 标准onvif协议,支持设备搜索、获取参数、快照抓图等。
  2. 支持264/265/aac等标准视音频协议传输。
  3. 支持多路批量onvif设备模拟,每一路都独立的端口。
  4. 支持本地摄像头采集转成onvif,可选择不同的设备、分辨率、帧率等参数。
  5. 支持本地桌面采集转成onvif,可选择不同的屏幕、分辨率、帧率等参数。
  6. 支持各种视频文件和视频流转成onvif,可重新设置编码转换以及分辨率转换。
  7. 支持4K、8K等高清分辨率,不限制分辨率,非264/265会自动转码推流。
  8. 每一路都可以设置统一或者独立的用户验证信息,为空则表示不验证。
  9. 可以把任意内容接入到NVR以及视频监控系统,方便保存录像文件,以便回放可查。
  10. 也可作为压力测试工具,比如模拟几千路onvif设备,让集成平台软件做接入压力测试。
  11. 推出去的流不仅有rtsp格式,还支持rtmp、http、flv、ws-flv、webrtc等方式访问,可以直接网页查看。
  12. 在管理工具上可以看到每一路的推流状况以及分辨率信息,非常直观。
  13. 支持自动重连拉流,重连推流,保证7乘以24小时稳定运行。
  14. 可设置开机自启动运行和后台运行,不显示在任务栏,作为后台服务运行。
  15. 可批量添加文件、添加目录,自动将目录下的所有文件添加到模拟器。
  16. 多功能添加地址面板,可以选择本地设备和监控设备,本地设备会自动识别摄像头设备和桌面设备,监控设备可以选择不同厂家,自动填充对应rtsp格式,填入用户信息即可,可以批量递增添加监控设备。
  17. 可无缝上传到市面上所有的onvif协议设备,包括海康、大华、宇视、华为、天地伟业等,也支持ONVIF Device Manager国际onvif工具。
  18. 支持gb28181设备模拟,具备设备注册、设备注销、设备心跳、设备信息、设备配置、设备状态应答等。
  19. 支持模拟报警和位置上报等,方便平台侧显示对应设备的实时位置。
  20. 支持一键添加批量模拟28181设备,实时显示已注册和已注销状态。
  21. 支持将本地桌面、本地摄像头、任意视频文件、视频流文件、手机摄像头等转换成28181设备,添加到NVR或者国标软件平台。
  22. sip协议同时支持udp和tcp两种通信方式,视频点播同时支持udp/tcp主动/tcp被动三种方式,涵盖所有可能的场景需求。
  23. 无论是onvif设备模拟组件还是28181设备模拟组件,全部原创底层协议解析,纯Qt实现,跨任意平台。
  24. 代码结构框架非常清晰,注释详细,代码精简不繁琐,非常易于学习和移植,可以很容易拓展其他接口需求。
  25. 支持Qt4/Qt5/Qt6以及后续所有版本、所有编译器、所有开发环境。
  26. 支持windows、linux、mac、国产OS、嵌入式linux、RK3588、树莓派、香橙派等系统。

五、相关代码

#include"frmonvifapp.h"
#include"ui_frmonvifapp.h"
#include"qthelper.h"
#include"videoutil.h"
#include"urlutil.h"
#include"onvifdevicepush.h"
#include"onvifdeviceserver.h"

frmOnvifApp::frmOnvifApp(QWidget *parent):QWidget(parent),ui(new Ui::frmOnvifApp)
{
    ui->setupUi(this);
this->initForm();
this->initConfig();
}

frmOnvifApp::~frmOnvifApp()
{
    server->stop();
delete ui;
}

void frmOnvifApp::initForm()
{
//设置开启解码/需要解码后在这里显示
    OnvifDevicePush::disableDecode =false;

//实例化设备服务类并关联信号槽
    server =newOnvifDeviceServer(this);
connect(server,SIGNAL(pushImage(QString, QImage)),this,SLOT(pushImage(QString, QImage)));
    server->start();

    QStringList ips;
    ips <<QtHelper::getLocalIPs();
    ui->cboxServerIp->addItems(ips);

//不存在则取第一个
int index = ui->cboxServerIp->findText(AppConfig::OnvifServerIp);
if(index <0){
        QString ip = ips.first();
        AppConfig::OnvifServerIp = ip;
        AppConfig::OnvifServerHost = ip;
        AppConfig::OnvifPushHost = ip;
}
}

void frmOnvifApp::initConfig()
{
VideoUtil::loadMediaUrl(ui->cboxMediaUrl, AppConfig::OnvifMediaUrl, UrlUtil::Simple);
connect(ui->cboxMediaUrl->lineEdit(),SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->cboxServerIp->lineEdit()->setText(AppConfig::OnvifServerIp);
connect(ui->cboxServerIp->lineEdit(),SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));
connect(ui->cboxServerIp->lineEdit(),SIGNAL(textChanged(QString)), ui->txtServerHost,SLOT(setText(QString)));
connect(ui->cboxServerIp->lineEdit(),SIGNAL(textChanged(QString)), ui->txtPushHost,SLOT(setText(QString)));

    ui->txtServerHost->setText(AppConfig::OnvifServerHost);
connect(ui->txtServerHost,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtUserName->setText(AppConfig::OnvifUserName);
connect(ui->txtUserName,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtUserPwd->setText(AppConfig::OnvifUserPwd);
connect(ui->txtUserPwd,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtPushFlag->setText(AppConfig::OnvifPushFlag);
connect(ui->txtPushFlag,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtPushHost->setText(AppConfig::OnvifPushHost);
connect(ui->txtPushHost,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtPushPort->setText(QString::number(AppConfig::OnvifPushPort));
connect(ui->txtPushPort,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtListenPort->setText(QString::number(AppConfig::OnvifListenPort));
connect(ui->txtListenPort,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));
}

void frmOnvifApp::saveConfig()
{
    AppConfig::OnvifMediaUrl = ui->cboxMediaUrl->lineEdit()->text().trimmed();
    AppConfig::OnvifServerHost = ui->txtServerHost->text().trimmed();
    AppConfig::OnvifServerIp = ui->cboxServerIp->lineEdit()->text().trimmed();
    AppConfig::OnvifUserName = ui->txtUserName->text().trimmed();
    AppConfig::OnvifUserPwd = ui->txtUserPwd->text().trimmed();
    AppConfig::OnvifPushFlag = ui->txtPushFlag->text().trimmed();
    AppConfig::OnvifPushHost = ui->txtPushHost->text().trimmed();
    AppConfig::OnvifPushPort = ui->txtPushPort->text().toInt();
    AppConfig::OnvifListenPort = ui->txtListenPort->text().toInt();
AppConfig::writeConfig();
}

void frmOnvifApp::pushImage(const QString &,const QImage &image)
{
if(ui->btnStart->text()=="停止服务"){
        ui->labImage->setImage(image,true);
}else{
        ui->labImage->clear();
}
}

void frmOnvifApp::on_btnStart_clicked()
{
if(ui->btnStart->text()=="启动服务"){
        QString rtspUrl =QString("rtsp://%1:%2/stream/%3").arg(AppConfig::OnvifPushHost).arg(AppConfig::OnvifPushPort).arg(AppConfig::OnvifPushFlag);
        server->setPara(AppConfig::OnvifServerHost, AppConfig::OnvifServerIp, AppConfig::OnvifUserName, AppConfig::OnvifUserPwd);
bool ok = server->append(AppConfig::OnvifPushFlag, AppConfig::OnvifListenPort, AppConfig::OnvifMediaUrl, rtspUrl);
if(ok){
            ui->txtPushUrl->setText(rtspUrl);
            ui->frame->setEnabled(false);
            ui->btnStart->setText("停止服务");
}
}else{
        server->clear();
        ui->labImage->clear();
        ui->txtPushUrl->clear();
        ui->frame->setEnabled(true);
        ui->btnStart->setText("启动服务");
}
}

#include"frmgb28181app.h"
#include"ui_frmgb28181app.h"
#include"qthelper.h"
#include"videoutil.h"
#include"urlutil.h"
#include"gb28181deviceserver.h"
#include"gb28181devicepush.h"

frmGB28181App::frmGB28181App(QWidget *parent):QWidget(parent),ui(new Ui::frmGB28181App)
{
    ui->setupUi(this);
this->initForm();
this->initConfig();
}

frmGB28181App::~frmGB28181App()
{
    server->stop();
delete ui;
}

void frmGB28181App::initForm()
{
//设置开启解码/需要解码后在这里显示
    GB28181DevicePush::disableDecode =false;

//实例化设备服务类并关联信号槽
    server =newGB28181DeviceServer(this);
connect(server,SIGNAL(statusChanged(QString,int)),this,SLOT(statusChanged(QString,int)));
connect(server,SIGNAL(pushImage(QString, QImage)),this,SLOT(pushImage(QString, QImage)));
    server->start();

    QStringList ips;
    ips <<QtHelper::getLocalIPs();
    ui->cboxDeviceIp->addItems(ips);

//不存在则取第一个
int index = ui->cboxDeviceIp->findText(AppConfig::GB28181DeviceIp);
if(index <0){
        AppConfig::GB28181DeviceIp = ips.first();
}
}

void frmGB28181App::initConfig()
{
VideoUtil::loadMediaUrl(ui->cboxMediaUrl, AppConfig::GB28181MediaUrl, UrlUtil::Simple);
connect(ui->cboxMediaUrl->lineEdit(),SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->cboxSipProtocol->setCurrentIndex(ui->cboxSipProtocol->findText(AppConfig::GB28181SipProtocol));
connect(ui->cboxSipProtocol,SIGNAL(currentIndexChanged(int)),this,SLOT(saveConfig()));

    ui->txtServerId->setText(AppConfig::GB28181ServerId);
connect(ui->txtServerId,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtServerRealm->setText(AppConfig::GB28181ServerRealm);
connect(ui->txtServerRealm,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtServerIp->setText(AppConfig::GB28181ServerIp);
connect(ui->txtServerIp,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtServerPort->setText(QString::number(AppConfig::GB28181ServerPort));
connect(ui->txtServerPort,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtServerPwd->setText(AppConfig::GB28181ServerPwd);
connect(ui->txtServerPwd,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->cboxDeviceIp->lineEdit()->setText(AppConfig::GB28181DeviceIp);
connect(ui->cboxDeviceIp->lineEdit(),SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtDevicePort->setText(QString::number(AppConfig::GB28181DevicePort));
connect(ui->txtDevicePort,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtDeviceId->setText(AppConfig::GB28181DeviceId);
connect(ui->txtDeviceId,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtChannelId->setText(AppConfig::GB28181ChannelId);
connect(ui->txtChannelId,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtRegistInterval->setText(QString::number(AppConfig::GB28181RegistInterval));
connect(ui->txtRegistInterval,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtHeartInterval->setText(QString::number(AppConfig::GB28181HeartInterval));
connect(ui->txtHeartInterval,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));

    ui->txtHeartTimeout->setText(QString::number(AppConfig::GB28181HeartTimeout));
connect(ui->txtHeartTimeout,SIGNAL(textChanged(QString)),this,SLOT(saveConfig()));
}

void frmGB28181App::saveConfig()
{
    AppConfig::GB28181MediaUrl = ui->cboxMediaUrl->lineEdit()->text().trimmed();
    AppConfig::GB28181SipProtocol = ui->cboxSipProtocol->currentText();

    AppConfig::GB28181ServerId = ui->txtServerId->text().trimmed();
    AppConfig::GB28181ServerRealm = ui->txtServerRealm->text().trimmed();
    AppConfig::GB28181ServerIp = ui->txtServerIp->text().trimmed();
    AppConfig::GB28181ServerPort = ui->txtServerPort->text().toInt();
    AppConfig::GB28181ServerPwd = ui->txtServerPwd->text().trimmed();

    AppConfig::GB28181DeviceIp = ui->cboxDeviceIp->lineEdit()->text().trimmed();
    AppConfig::GB28181DevicePort = ui->txtDevicePort->text().toInt();
    AppConfig::GB28181DeviceId = ui->txtDeviceId->text().trimmed();
    AppConfig::GB28181ChannelId = ui->txtChannelId->text().trimmed();

    AppConfig::GB28181RegistInterval = ui->txtRegistInterval->text().toInt();
    AppConfig::GB28181HeartInterval = ui->txtHeartInterval->text().toInt();
    AppConfig::GB28181HeartTimeout = ui->txtHeartTimeout->text().toInt();
AppConfig::writeConfig();
}

void frmGB28181App::statusChanged(const QString &id,int status)
{
    QString text =(status ==1?"已注册":"已注销");
    ui->labImage->setText(text);
}

void frmGB28181App::pushImage(const QString &,const QImage &image)
{
if(ui->btnStart->text()=="停止服务"){
        ui->labImage->setImage(image,true);
}else{
        ui->labImage->clear();
}
}

void frmGB28181App::on_btnStart_clicked()
{
if(ui->btnStart->text()=="启动服务"){
        server->setPara(AppConfig::GB28181ServerId, AppConfig::GB28181ServerRealm, AppConfig::GB28181ServerIp, AppConfig::GB28181ServerPort, AppConfig::GB28181ServerPwd, AppConfig::GB28181RegistInterval, AppConfig::GB28181HeartInterval, AppConfig::GB28181HeartTimeout);
bool ok = server->append(AppConfig::GB28181SipProtocol, AppConfig::GB28181DeviceIp, AppConfig::GB28181DevicePort, AppConfig::GB28181DeviceId, AppConfig::GB28181ChannelId, AppConfig::GB28181MediaUrl);
if(ok){
            ui->frame->setEnabled(false);
            ui->btnStart->setText("停止服务");
}
}else{
        server->clear();
        ui->labImage->clear();
        ui->frame->setEnabled(true);
        ui->btnStart->setText("启动服务");
}
}

void frmGB28181App::on_btnUpdate_clicked()
{
    server->update();
    server->catalog();
}

支持红外夜视IRCUT的4G智能安全帽BW620Y,内部主板型号有,SAE01, asr8601 ,W20, k61v1_32_bsp_1gk62v1_64_bsp, soq_azw_05_Natv,soq_azw_01_NatvAZW003aqm6816,soq_azw_01_Natv,SC161, W25比传统的白光照明灯效果要好很多,可以避免图像中间大块的亮光斑,可以在雾天使用。https://www.bilibili.com/video/BV1594y1E7tB/

危险气体检测仪,通过4G/5G执法记录仪或者4G/5G智能安全帽上报到平台,https://www.besovideo.com/detail?t=1&i=273

智能安全帽~生命体征检测与危险气体检测一体化集成设计还是蓝牙无线外挂式方式好?https://www.besovideo.com/detail?t=1&i=279

智能安全帽~生命体征采集上报,https://www.besovideo.com/detail?t=2&i=506

智能安全帽,检测生命体征上报平台, https://v.douyin.com/e3oP4Yx/

AIoT万物智联,智能安全帽生产厂家,执法记录仪生产厂家,为大型国企央企提供移动视频类产品的ODM/OEM服务,单北斗定位智能安全帽智能头盔、智能头箍头盔记录仪独立北斗定位执法记录仪智能视频分析/边缘计算AI盒子车载视频监控/车载DVR/NVR、布控球智能眼镜智能手电、智能电子工牌、无人机4G补传系统等统一接入大型统一视频平台~融合通信可视指挥调度平台VMS/smarteye 。

什么是智能安全帽,如何选购智能安全帽,智能安全帽的主要功能,https://www.besovideo.com/detail?t=1&i=109

目前支持的AI智能算法、视频智能分析算法有哪些,https://www.besovideo.com/detail?t=1&i=297

视频演示1~单独北斗定位智能安全帽助力光伏新能源,  视频演示2~石油石化

海康、大华等5000路固定点摄像头走国标GB28181接入统一视频平台smarteye,支持eHome,ISUP, SIP,萤石云,CMSV6等

海康等IP摄像头走国标GB28181接入smarteye平台, https://www.besovideo.com/detail?t=1&i=244

国网+南网电力施工作业现场安全生产风险管控应用方案,https://www.besovideo.com/detail?t=1&i=50

轻危大型厂矿可视化监管系统方案(有内部作业视频数据安全保密的需求),https://www.besovideo.com/detail?t=1&i=258

小微型4G/WiFi执法记录仪、胸牌记录仪大全~智慧养老护工、银行机场客服、外勤、上门安装维修等,https://www.besovideo.com/detail?t=2&i=1784

超薄款4G工牌视频记录仪BH128,https://www.besovideo.com/detail?t=2&i=1575

4G/5G执法记录仪前端人脸识别、人脸比对使用说明,https://www.besovideo.com/detail?t=1&i=365

防抖执法记录仪、智能安全帽、头盔记录仪大全,https://www.besovideo.com/detail?t=2&i=1692

可拆卸智能安全帽、绑带式、粘贴式、贴合式、分体式智能头盔记录仪、智能头箍记录仪大全,https://www.besovideo.com/detail?t=2&i=1697

定位安全帽大全,人员定位考勤、电子围栏脱岗检查 all in smarteye,https://www.besovideo.com/detail?t=2&i=1843

支持南方电网WAPI无线网络的4G/5G执法记录仪、智能布控球、智能安全帽产品,https://www.besovideo.com/detail?t=1&i=404

各种布控球分类列表大全,https://www.besovideo.com/detail?t=1&i=339

北斗双频高精度定位智能工卡电子工牌大全,https://www.besovideo.com/detail?t=2&i=1689

多年来给各个行业客户定制的各种智能安全帽-头盔摄像头等,万物智联~各类智能安全帽/头盔摄像头统一接入优视多源融合统一视频可视指挥调度平台SmartEye,https://www.besovideo.com/detail?t=1&i=20

无人机4G/5G图传到远程指挥中心统一视频平台smarteye或者海康等国标大平台, https://www.besovideo.com/detail?t=2&i=1775

县级应急指挥调度系统建设, https://www.besovideo.com/detail?t=1&i=411

智慧铁路,铁路应急指挥通信系统建设,可视化作业现场风险管控系统应用方案,https://www.besovideo.com/detail?t=1&i=413

智慧港口可视化作业风险管控系统建设方案,https://www.besovideo.com/detail?t=1&i=414

石油石化可视化巡检系统安全风险管控系统应用方案,https://www.besovideo.com/detail?t=1&i=415

独立北斗定位4G智能安全帽在热电厂安全生产风险管控系统中的应用,https://www.besovideo.com/detail?t=1&i=508

车载视频监控产品,车载DVR/NVR、4G行车记录仪系列产品,https://www.besovideo.com/detail?t=2&i=1850

为什么武警、特警拉练演习的MESH自组网系统都不约而同的选择了smarteye平台?https://www.besovideo.com/detail?t=2&i=1215