#ifndef DECODEVEDIO_H #define DECODEVEDIO_H #include #include #include #include #include #include #include "RingQueue/RingQueue.hpp" // #include "threadcontroller.h" extern "C" { // #include #include // #include // #include } /** * 使用方式: * 1. 初始化FFmpeg:initFFmpeg() * 2. 开启解码线程:startDecodeVedio() * 3. 获取一帧图像:getOneImage() * 4. 停止解码线程:stopDecodeVedio() * 5. 在初始化完成后,未进入第二步的之前,可以获取视频的宽高信息,也可以设置宽高信息 * */ class DecodeVedio : public QObject { Q_OBJECT enum class DecodeState { NONE = 0, DecodeRun, /* 解码运行中 */ DecodePause, /* 暂停解码 */ DecodeSeek, /* 跳转中 */ DecodeStop, /* 停止解码,但是并没有退出解码线程 */ DecodeExit /* 退出解码 */ }; public: explicit DecodeVedio(QThread* thread, QObject* parent = nullptr); ~DecodeVedio(); /* 打开视频,同时初始化解码器) */ void openVedio(const QString& fileName); /* 开始解码视频,开始前先打开视频文件 */ void startDecodeVedio(); /* 停止解码视频,也是停止线程 */ void stopDecodeVedio(); /* 获取解码状态 */ bool isDecoding() { return m_threadRuning; } /* 设置当前播放位置,单位ms */ void setCurrentPos(qint64 pos); /* 获取当前播放位置,单位ms */ qint64 getCurrentPos(); /* 获取视频时长 */ qint64 getDuration(); qint64 getTotalFrame() { return m_totalFrame; } /* 获取一帧图像 */ QImage* getOneImage(); /* 获取一帧图像,直到有图像为止,可以设置超时时间 */ QImage* getOneImageUntilHave(int timeOut = -1); /* 获取帧数 */ int getFPS() const { return m_fps; } /* 设置帧数 */ void setFPS(int fps) { m_fps = fps; } /* 获取图像宽度 */ QSize getSrcVideoSize() const {return m_srcSize; } /* 获取解码器名称(编码格式) */ QString getDecoderName() const { return m_decoderName; } /* 获取硬件解码器名称列表 */ QStringList getHWDecoderList() const { return m_listDecoderName; } /* 查找硬件解码器 */ static void findHWDecoder(QStringList& listDecoderName); signals: void signal_oneImage(); /* 一帧图像信号 */ void signal_playCompleted(); /* 播放完成信号 */ void signal_startDecode(); /* 开始解码信号 */ private: /* 查找硬件解码器 */ void findHWDecoder(); /* 初始化硬件解码器 */ void initHWDecoder(const AVCodec* codec); /* 拷贝数据,从GPU显存拷贝到内存中 */ bool copyDataFromGPU(AVFrame* pFrameHW, AVFrame* pFrameSRC); /* 软解码线程 */ void threadDecodeUsingCPU(); /* 硬件解码线程 */ void threadDecodeUsingGPU(); /* 退出线程 */ void exitThread(); /* 暂停解码 */ void pauseDecode(); /* 继续解码 */ void continueDecode(); /* 将AVRational转换为double */ qreal rationalToDouble(AVRational* rational); /* 释放所有资源 */ void freeAll(); private slots: void do_startDecodeVedio(); /* 开启解码 */ private: QThread* m_thread = nullptr; /* 解码线程 */ /* 线程状态 */ std::atomic_bool m_threadRuning = false; /* 解码线程是运行标志 */ std::atomic_bool m_initFFmpeg = false; /* ffmpeg初始化标志 */ std::atomic_bool m_pauseDecode = false; /* 暂停解码 */ std::atomic_bool m_decodeStatus = false; /* 解码状态,这里主要是检测是否暂停解码 */ std::atomic_bool m_isSeek = false; /* 是否在跳转中 */ std::atomic_bool m_flushDecoder = false; /* 刷新解码器 */ std::atomic m_decodeState = DecodeState::NONE; /* 视频解码相关变量信息 */ QString m_fileName; /* 解码的视频文件名称 */ AVFormatContext *m_pFormatContext = nullptr; /* 格式上下文,贯穿全局 */ AVCodecContext *m_pCodecContext = nullptr; /* 解码器上下文 */ AVPacket* m_packet = nullptr; /* 存储解码前的数据,一个数据包 */ AVFrame* m_pFrameSRC = nullptr; /* 存储解码后的一帧数据原始视频编码 */ AVFrame* m_pFrameHW = nullptr; /* 存储解码后的一帧数据,硬件解码 */ struct SwsContext *m_sws_ctx = nullptr; /* 视频转换上下文 */ uint8_t *m_buffer = nullptr; /* 存储解码后的一帧数据,RGB格式 */ int m_videoStream = -1; /* 记录视频流是第几个流 */ bool m_supportHWDecoder = false; /* 是否使用硬件解码 */ AVBufferRef* m_hw_device_ctx = nullptr; /* 对数据缓冲区的引用 */ QList m_listHWDeviceType; /* 保存当前环境支持的硬件解码器 */ QStringList m_listDecoderName; /* 硬件解码器列表名称 */ /* 视频相关信息 */ QSize m_srcSize; /* 原始视频分辨率大小 */ qint64 m_totalFrame = 0; /* 视频总帧数 */ int m_fps = 0; /* 每秒的帧数 */ qint64 m_duration = 0; /* 视频时长,单位毫秒 */ qint64 m_startPos = 0; /* 开始播放的位置,摄像机视频的位置不是从0开始的,需要在初始化的时候取出这个值 */ std::atomic m_pts = 0; /* 当前帧显示时间,也就是当前的进度时间 */ qint64 m_targetPos = -1; /* 跳转的目标播放位置 */ qint64 m_currentFrame = 0; /* 当前已播放的帧数 */ QString m_decoderName; /* 解码器名称 */ RingQueue m_queueImage; /* 环形队列,存储生成的图像 */ }; #endif /* DECODEVEDIO_H */