• 2110阅读
  • 3回复

Qt音视频开发33-ffmpeg安卓版 [复制链接]

上一主题 下一主题
离线liudianwu
 

图酷模式  只看楼主 正序阅读 楼主  发表于: 2020-10-13
## 一、前言
一直都想搞个安卓版本的视频监控程序,很早以前弄过一个,采用的是早期的ffmpeg2的lib文件,对于现在众多的网络流媒体格式,支持有限,而且新的Qt编写安卓程序,结构上也变动了,新的安卓系统权限要求也和以前处理不一样了,现在需要动态授权,以前是直接配置文件写好需要哪些权限就行,所以近期特意全部重写了一遍安卓版本的视频监控程序,内核还是采用的ffmpeg,换成了最新的ffmpeg4版本,在ubuntu系统上用安卓编译器编译了对应的lib文件,然后放到win上编写Qt+安卓程序。

为了统一管理一个版本的ffmpeg视频监控内核程序,直接在现有的程序上修改,增加了动态申请权限的代码,同时对应的进度条和复选框等指示器放大,方便选中,通道画面限定默认4个,可以自行增加画面数量,一开始测试保存截图和视频文件的时候,发现死活不成功,后面动态权限申请成功以后,把把成功,这里搞了很久,原来对不一样的安卓系统处理要求不一样。新时代新技术层出不穷,迭代真是非常的快,想要保持活力看来还得不断的学习及了解新技术新知识。一个有前途的程序员每天60%的时间要拿出来学习研究,40%用来应付工作,这样每两年工资可翻一倍,而且最要命的是程序员也是要有一些天赋的,并非所有人都适合,大部分程序员把时间都扔在了业务逻辑之中,最后被淘汰!这也是35岁中年危机的主要原因。事实上有实力的程序员越老越值钱,扯远了!

## 二、功能特点
1. 多线程实时播放视频流+本地视频+USB摄像头等。
2. 支持windows+linux+mac,支持ffmpeg3和ffmpeg4,支持32位和64位。
3. 多线程显示图像,不卡主界面
4. 自动重连网络摄像头。
5. 可设置边框大小即偏移量和边框颜色。
6. 可设置是否绘制OSD标签即标签文本或图片和标签位置。
7. 可设置两种OSD位置和风格。
8. 可设置是否保存到文件以及文件名。
9. 可直接拖曳文件到ffmpegwidget控件播放。
10. 支持h265视频流+rtmp等常见视频流。
11. 可暂停播放和继续播放。
12. 支持存储单个视频文件和定时存储视频文件。
13. 自定义顶部悬浮条,发送单击信号通知,可设置是否启用。
14. 可设置画面拉伸填充或者等比例填充。
15. 可设置解码是速度优先、质量优先、均衡处理。
16. 可对视频进行截图(原始图片)和截屏。
17. 录像文件存储支持裸流和MP4文件。
18. 音视频完美同步,采用外部时钟同步策略。
19. 支持seek定位播放位置。
20. 支持qsv、dxva2、d3d11va等硬解码。
21. 支持opengl绘制视频数据,极低CPU占用。
22. 支持安卓和嵌入式linux,交叉编译即可。

## 三、效果图




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

## 五、核心代码
```cpp
#include "head.h"
#include "frmtab.h"

//动态设置权限
bool checkPermission(const QString &permission)
{
#ifdef Q_OS_ANDROID
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
    QtAndroid::PermissionResult result = QtAndroid::checkPermission(permission);
    if (result == QtAndroid::PermissionResult::Denied) {
        QtAndroid::requestPermissionsSync(QStringList() << permission);
        result = QtAndroid::checkPermission(permission);
        if (result == QtAndroid::PermissionResult::Denied) {
            return false;
        }
    }
#endif
#endif
    return true;
}

void initStyle()
{
    //复选框单选框滑块等指示器大小
    QStringList list;
    int rbtnWidth = 20;
    int ckWidth = 18;
    list.append(QString("QRadioButton::indicator{width:%1px;height:%1px;}").arg(rbtnWidth));
    list.append(QString("QCheckBox::indicator,QGroupBox::indicator,QTreeWidget::indicator,QListWidget::indicator{width:%1px;height:%1px;}").arg(ckWidth));

    QString normalColor = "#E8EDF2";
    QString grooveColor = "#1ABC9C";
    QString handleColor = "#1ABC9C";
    int sliderHeight = 12;
    int sliderRadius = sliderHeight / 2;
    int handleWidth = (sliderHeight * 3) / 2 + (sliderHeight / 5);
    int handleRadius = handleWidth / 2;
    int handleOffset = handleRadius / 2;

    list.append(QString("QSlider::horizontal{min-height:%1px;}").arg(sliderHeight * 2));
    list.append(QString("QSlider::groove:horizontal{background:%1;height:%2px;border-radius:%3px;}")
                .arg(normalColor).arg(sliderHeight).arg(sliderRadius));
    list.append(QString("QSlider::add-page:horizontal{background:%1;height:%2px;border-radius:%3px;}")
                .arg(normalColor).arg(sliderHeight).arg(sliderRadius));
    list.append(QString("QSlider::sub-page:horizontal{background:%1;height:%2px;border-radius:%3px;}")
                .arg(grooveColor).arg(sliderHeight).arg(sliderRadius));
    list.append(QString("QSlider::handle:horizontal{width:%2px;margin-top:-%3px;margin-bottom:-%3px;border-radius:%4px;"
                        "background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #FFFFFF,stop:0.8 %1);}")
                .arg(handleColor).arg(handleWidth).arg(handleOffset).arg(handleRadius));

    //偏移一个像素
    handleWidth = handleWidth + 1;
    list.append(QString("QSlider::vertical{min-width:%1px;}").arg(sliderHeight * 2));
    list.append(QString("QSlider::groove:vertical{background:%1;width:%2px;border-radius:%3px;}")
                .arg(normalColor).arg(sliderHeight).arg(sliderRadius));
    list.append(QString("QSlider::add-page:vertical{background:%1;width:%2px;border-radius:%3px;}")
                .arg(grooveColor).arg(sliderHeight).arg(sliderRadius));
    list.append(QString("QSlider::sub-page:vertical{background:%1;width:%2px;border-radius:%3px;}")
                .arg(normalColor).arg(sliderHeight).arg(sliderRadius));
    list.append(QString("QSlider::handle:vertical{height:%2px;margin-left:-%3px;margin-right:-%3px;border-radius:%4px;"
                        "background:qradialgradient(spread:pad,cx:0.5,cy:0.5,radius:0.5,fx:0.5,fy:0.5,stop:0.6 #FFFFFF,stop:0.8 %1);}")
                .arg(handleColor).arg(handleWidth).arg(handleOffset).arg(handleRadius));

    qApp->setStyleSheet(list.join(""));
}

int main(int argc, char *argv[])
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#if (QT_VERSION > QT_VERSION_CHECK(5,4,0))
    //设置opengl模式 AA_UseDesktopOpenGL AA_UseSoftwareOpenGL AA_UseOpenGLES
    //QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
    QApplication a(argc, argv);

    App::setFont();
    App::setCode();
    App::ConfigFile = AppPath + "/video_ffmpeg.ini";
    App::readConfig();

    frmTab w;
#ifdef Q_OS_ANDROID
    //请求权限
    checkPermission("android.permission.READ_EXTERNAL_STORAGE");
    checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");

    QString strDir = AppPath + "/snap";
    QDir dir(strDir);
    if (!dir.exists()) {
        dir.mkpath(strDir);
    }

    initStyle();
    w.showMaximized();
#else
    w.resize(1100, 700);
    w.setWindowTitle(QString("qt+ffmpeg 示例 %1 %2").arg(App::Version).arg(App::TitleFlag));
    w.show();
    App::setFormInCenter(&w);
#endif

    return a.exec();
}

```
4条评分好评度+1贡献值+1金钱+10威望+1
20091001753 好评度 +1 - 2020-10-13
20091001753 贡献值 +1 - 2020-10-13
20091001753 威望 +1 - 2020-10-13
20091001753 金钱 +10 - 2020-10-13
欢迎关注微信公众号:Qt实战 (各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发)QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线huaiqishizu

只看该作者 3楼 发表于: 2020-10-14
回 liudianwu 的帖子
liudianwu:qcamera就支持,不需要ffmpeg。下面这个就是纯Qt写的手机app,支持前置后置摄像头切换。
[图片]
 (2020-10-14 08:20) 

666
离线liudianwu

只看该作者 2楼 发表于: 2020-10-14
回 huaiqishizu 的帖子
huaiqishizu:
大佬,这个支持手机摄像头吗

qcamera就支持,不需要ffmpeg。下面这个就是纯Qt写的手机app,支持前置后置摄像头切换。

欢迎关注微信公众号:Qt实战 (各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发)QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线huaiqishizu

只看该作者 1楼 发表于: 2020-10-13
大佬,这个支持手机摄像头吗
快速回复
限100 字节
 
上一个 下一个