liudianwu |
2020-04-24 15:28 |
Qt开源作品1-视频流播放ffmpeg内核
## 一、前言 好久以前就写过这个工具,后来因为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"; } ```
## 三、效果图 [attachment=21531]
## 四、开源主页 **以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。** 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/) |
|