• 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和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的库和解码器等,在一个程序中只需要注册一次即可,没必要每个视频类都注册一次。

    //一个软件中只需要初始化一次就行
    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
        }
    }
    

    第三步:设置参数

    //在打开码流前指定各种参数比如:探测时间/超时时间/最大延时等
    //设置缓存大小,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);
    

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

    第五步:解码图像

    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
    2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
    3. 个人主页:https://blog.csdn.net/feiyangqingyun
    4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
  • 相关阅读:
    .Net程序员之Python基础教程学习----字典的使用 [Third Day]
    .Net程序员之Python基础教程学习----字符串的使用 [Second Day]
    .Net程序员之Python基础教程学习----列表和元组 [First Day]
    SQL--实现分页查询
    .Net程序员Python之道---Python基础
    C#基础----Linq之List<T>篇
    C#基础--基于POP3协议的邮件接收和基于STMP的邮件发送
    C#基础---事件的使用
    C#基础---委托的使用
    各种坑死爹的
  • 原文地址:https://www.cnblogs.com/feiyangqingyun/p/12767587.html
Copyright © 2020-2023  润新知