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

Qt开源作品1-视频流播放ffmpeg内核

4
回复
5445
查看
[复制链接]
累计签到:7 天
连续签到:1 天
来源: 2020-4-24 15:38:44 显示全部楼层 |阅读模式
## 一、前言
好久以前就写过这个工具,后来因为Qt版本的不断升级以及ffmpeg也经历过好多次的迭代,可能从官网下载的ffmpeg搭配原来的代码不能正确编译,因为很多api已经变了,所以这次特意抽空全部整理重写一遍,只求最精简最好用,同时兼容了ffmpeg3和ffmpeg4,并且同时支持32位的库和64位的库,这样任何小白拿过去直接编译就能用。

1. 多线程实时绘制
2. 同时解码视频流和音频流
3. 支持任意Qt版本任意系统任意编译器
4. 解码和窗体分离,拓展性强
5. 可选ffmpeg3和ffmpeg4两个版本
6. 可选32位和64位的ffmpeg库
7. 注释绝对详细,包你满意

## 二、代码思路
第一步:引入ffmpeg的头文件
```c++
//必须加以下内容,否则编译不能通过,为了兼容C和C99标准
#ifndef INT64_C
#define INT64_C
#define UINT64_C
#endif

//引入ffmpeg头文件
extern "C" {
#include "libavutil/opt.h"
#include "libavutil/time.h"
#include "libavutil/frame.h"
#include "libavutil/pixdesc.h"
#include "libavutil/avassert.h"
#include "libavutil/imgutils.h"
#include "libavutil/ffversion.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"

#ifdef ffmpegdevice
#include "libavdevice/avdevice.h"
#endif

#ifndef gcc45
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_qsv.h"
#endif
}
```

第二步:注册ffmpeg的库
这里发现很多人每个类都注册一次,搞得内存每次增加很多,不是不可以,而是没有必要,其实ffmpeg的库和解码器等,在一个程序中只需要注册一次即可,没必要每个视频类都注册一次。
```c++
//一个软件中只需要初始化一次就行
void FFmpegThread::initlib()
{
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    static bool isInit = false;
    if (!isInit) {
        //注册库中所有可用的文件格式和解码器
        av_register_all();
        //注册所有设备,主要用于本地摄像机播放支持
#ifdef ffmpegdevice
        avdevice_register_all();
#endif
        //初始化网络流格式,使用网络流时必须先执行
        avformat_network_init();

        isInit = true;
        qDebug() << TIMEMS << "init ffmpeg lib ok" << " version:" << FFMPEG_VERSION;
#if 0
        //输出所有支持的解码器名称
        QStringList listCodeName;
        AVCodec *code = av_codec_next(NULL);
        while (code != NULL) {
            listCodeName << code->name;
            code = code->next;
        }

        qDebug() << TIMEMS << listCodeName;
#endif
    }
}
```

第三步:设置参数
```c++
//在打开码流前指定各种参数比如:探测时间/超时时间/最大延时等
//设置缓存大小,1080p可将值调大
av_dict_set(&options, "buffer_size", "8192000", 0);
//以tcp方式打开,如果以udp方式打开将tcp替换为udp
av_dict_set(&options, "rtsp_transport", "tcp", 0);
//设置超时断开连接时间,单位微秒,3000000表示3秒
av_dict_set(&options, "stimeout", "3000000", 0);
//设置最大时延,单位微秒,1000000表示1秒
av_dict_set(&options, "max_delay", "1000000", 0);
//自动开启线程数
av_dict_set(&options, "threads", "auto", 0);
```

第四步:打开视频流
具体代码比较多,详细代码请自行开源主页下载。

第五步:解码图像
```c++
void FFmpegThread::run()
{
    //计时
    QTime time;
    while (!stopped) {
        //根据标志位执行初始化操作
        if (isPlay) {
            this->init();
            isPlay = false;
            continue;
        }

        time.restart();
        if (av_read_frame(avFormatContext, avPacket) >= 0) {
            //判断当前包是视频还是音频
            int packetSize = avPacket->size;
            int index = avPacket->stream_index;
            if (index == videoStreamIndex) {
                //解码视频流
                avcodec_decode_video2(videoCodec, avFrame2, &frameFinish, avPacket);

                if (frameFinish) {
                    //将数据转成一张图片
                    sws_scale(swsContext, (const uint8_t *const *)avFrame2->data, avFrame2->linesize, 0, videoHeight, avFrame3->data, avFrame3->linesize);

                    //以下两种方法都可以
                    //QImage image(avFrame3->data[0], videoWidth, videoHeight, QImage::Format_RGB32);
                    QImage image((uchar *)buffer, videoWidth, videoHeight, QImage::Format_RGB32);
                    if (!image.isNull()) {
                        emit receiveImage(image);
                    }

                    msleep(1);
                }
            } else if (index == audioStreamIndex) {
                //解码音频流,这里暂不处理,以后交给sdl播放
            }
        }

        av_packet_unref(avPacket);
        av_freep(avPacket);
        msleep(1);
    }

    //线程结束后释放资源
    free();
    stopped = false;
    isPlay = false;
    qDebug() << TIMEMS << "stop ffmpeg thread";
}
```

## 三、效果图



## 四、开源主页
**以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。**
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/)

本帖子中包含更多资源

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

x
回复

使用道具 举报

累计签到:256 天
连续签到:1 天
2020-4-27 11:25:43 显示全部楼层
典武大佬的开源代码 占坑学习 ヾ(◍°∇°◍)ノ゙
回复 支持 反对

使用道具 举报

累计签到:256 天
连续签到:1 天
2020-4-27 11:30:02 显示全部楼层
还请问一下,我有两张QImage图片对象,现在要拼起来。A作为C的奇数行,B作为C的奇数行。
for(int i = 0; i<FramemImage.height(); ++i){//组合
            RGBBuffer.append((char*)FramemImage.scanLine(i), FramemImage.width());
            RGBBuffer.append((char*)FieldmImage.scanLine(i), FramemImage.width());
        }
qDebug() << "rgb leng:" << RGBBuffer.size();
FramemImage = QImage((uchar *)RGBBuffer.data(),  FramemImage.width(), FramemImage.height()*2, QImage::Format_RGB32);
然后直接崩溃了 - -

点评

建议拿到图片后重新painter  详情 回复 发表于 2020-4-30 09:15
回复 支持 反对

使用道具 举报

累计签到:7 天
连续签到:1 天
2020-4-30 09:15:46 显示全部楼层
z55716368 发表于 2020-4-27 11:30
还请问一下,我有两张QImage图片对象,现在要拼起来。A作为C的奇数行,B作为C的奇数行。
for(int i = 0; i ...

建议拿到图片后重新painter

点评

我也试过用painter。但是我现在是需要组合两张图像,这种的话有什么好的处理方法吗?  详情 回复 发表于 2020-4-30 15:02
回复 支持 反对

使用道具 举报

累计签到:256 天
连续签到:1 天
2020-4-30 15:02:46 显示全部楼层
liudianwu 发表于 2020-4-30 09:15
建议拿到图片后重新painter

我也试过用painter。但是我现在是需要组合两张图像,这种的话有什么好的处理方法吗?
回复 支持 反对

使用道具 举报

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

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