DecodeVedio.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #ifndef DECODEVEDIO_H
  2. #define DECODEVEDIO_H
  3. #include <QObject>
  4. #include <QQueue>
  5. #include <QTimer>
  6. #include <QMutex>
  7. #include <QWaitCondition>
  8. #include <QImage>
  9. #include "RingQueue/RingQueue.hpp"
  10. #include "FrameFormat.h"
  11. extern "C"
  12. {
  13. #include <libavcodec/avcodec.h>
  14. #include <libavformat/avformat.h>
  15. // #include <libswscale/swscale.h>
  16. // #include <libavutil/imgutils.h>
  17. }
  18. /**
  19. * 使用方式:
  20. * 1. 初始化FFmpeg:initFFmpeg()
  21. * 2. 开启解码线程:startDecodeVedio()
  22. * 3. 获取一帧图像:getOneImage()
  23. * 4. 停止解码线程:stopDecodeVedio()
  24. * 5. 在初始化完成后,未进入第二步的之前,可以获取视频的宽高信息,也可以设置宽高信息
  25. *
  26. */
  27. class DecodeVedio : public QObject
  28. {
  29. Q_OBJECT
  30. enum class DecodeState
  31. {
  32. NONE = 0,
  33. DecodeRun, /* 解码运行中 */
  34. DecodePause, /* 暂停解码 */
  35. DecodeSeek, /* 跳转中 */
  36. DecodeStop, /* 停止解码,但是并没有退出解码线程 */
  37. DecodeExit /* 退出解码 */
  38. };
  39. public:
  40. explicit DecodeVedio(QThread* thread, QObject* parent = nullptr);
  41. ~DecodeVedio();
  42. /* 打开视频,同时初始化解码器) */
  43. bool openVedio(const QString& fileName);
  44. /* 开始解码视频,开始前先打开视频文件 */
  45. void startDecodeVedio();
  46. /* 停止解码视频,也是停止线程 */
  47. void stopDecodeVedio();
  48. /* 获取解码状态 */
  49. bool isDecoding() { return m_threadRuning; }
  50. /* 设置当前播放位置,单位ms */
  51. void setCurrentPos(qint64 pos);
  52. /* 获取当前播放位置,单位ms */
  53. qint64 getCurrentPos();
  54. /* 获取视频时长 */
  55. qint64 getDuration();
  56. qint64 getTotalFrame() { return m_totalFrame; }
  57. /* 获取一帧图像 */
  58. FrameBase* getOneImage();
  59. /* 获取一帧图像,直到有图像为止,可以设置超时时间 */
  60. FrameBase* getOneImageUntilHave(int timeOut = -1);
  61. /* 获取帧数 */
  62. int getFPS() const { return m_fps; }
  63. /* 设置帧数 */
  64. void setFPS(int fps) { m_fps = fps; }
  65. /* 获取图像宽度 */
  66. QSize getSrcVideoSize() const {return m_srcSize; }
  67. /* 获取解码器名称(编码格式) */
  68. QString getDecoderName() const { return m_decoderName; }
  69. /* 获取硬件解码器名称列表 */
  70. QStringList getHWDecoderList() const { return m_listDecoderName; }
  71. /* 查找硬件解码器 */
  72. static void findHWDecoder(QStringList& listDecoderName);
  73. signals:
  74. void signal_oneImage(); /* 一帧图像信号 */
  75. void signal_playCompleted(); /* 播放完成信号 */
  76. void signal_startDecode(); /* 开始解码信号 */
  77. private:
  78. /* 清空环形队列 */
  79. void clearQueueFrame();
  80. /* 删除一帧图片 */
  81. void deleteOneImage();
  82. /*----------------------- 解码相关的函数 ------------------------*/
  83. /* 查找硬件解码器 */
  84. void findHWDecoder();
  85. /* 初始化硬件解码器 */
  86. void initHWDecoder(const AVCodec* codec);
  87. /* 拷贝数据,从GPU显存拷贝到内存中 */
  88. bool copyDataFromGPU(AVFrame* pFrameHW, AVFrame* pFrameSRC);
  89. /* 软解码线程 */
  90. void threadDecodeUsingCPU();
  91. /* 硬件解码线程 */
  92. void threadDecodeUsingGPU();
  93. /* 对生成的图像进行格式转换,并添加到环形队列中 */
  94. inline void convertImageFormatAndPushToQueue(AVFrame* pFrame);
  95. /* 将yuv420p的uint8_t数组缓存转换成Image_YUV420P结构体 */
  96. inline void convertToYUV420P(AVFrame* pFrame, Image_YUV420P* image);
  97. /* 转换成RGBA格式 */
  98. inline void convertToRGBA(AVFrame* pFrame, QImage* image);
  99. /* 退出线程 */
  100. void exitThread();
  101. /* 暂停解码 */
  102. void pauseDecode();
  103. /* 继续解码 */
  104. void continueDecode();
  105. /* 将AVRational转换为double */
  106. qreal rationalToDouble(AVRational* rational);
  107. /* 释放所有资源 */
  108. void freeAll();
  109. /* 打印出错误信息 */
  110. void printErrorStr(int errRet);
  111. private slots:
  112. void do_startDecodeVedio(); /* 开启解码 */
  113. private:
  114. QThread* m_thread = nullptr; /* 解码线程 */
  115. /* 线程状态 */
  116. std::atomic_bool m_threadRuning = false; /* 解码线程是运行标志 */
  117. std::atomic_bool m_initFFmpeg = false; /* ffmpeg初始化标志 */
  118. std::atomic_bool m_pauseDecode = false; /* 暂停解码 */
  119. std::atomic_bool m_decodeStatus = false; /* 解码状态,这里主要是检测是否暂停解码 */
  120. std::atomic_bool m_isSeek = false; /* 是否在跳转中 */
  121. std::atomic_bool m_flushDecoder = false; /* 刷新解码器 */
  122. std::atomic<DecodeState> m_decodeState = DecodeState::NONE;
  123. /* 视频解码相关变量信息 */
  124. QString m_fileName; /* 解码的视频文件名称 */
  125. AVFormatContext *m_pFormatContext = nullptr; /* 格式上下文,贯穿全局 */
  126. AVCodecContext *m_pCodecContext = nullptr; /* 解码器上下文 */
  127. AVPacket* m_packet = nullptr; /* 存储解码前的数据,一个数据包 */
  128. AVFrame* m_pFrameSRC = nullptr; /* 存储解码后的一帧数据原始视频编码 */
  129. AVFrame* m_pFrameHW = nullptr; /* 存储解码后的一帧数据,硬件解码 */
  130. struct SwsContext *m_sws_ctx = nullptr; /* 视频转换上下文 */
  131. uint8_t *m_buffer = nullptr; /* 存储解码后的一帧数据,RGB格式 */
  132. int m_videoStream = -1; /* 记录视频流是第几个流 */
  133. bool m_supportHWDecoder = false; /* 是否使用硬件解码 */
  134. AVBufferRef* m_hw_device_ctx = nullptr; /* 对数据缓冲区的引用 */
  135. QList<int> m_listHWDeviceType; /* 保存当前环境支持的硬件解码器 */
  136. QStringList m_listDecoderName; /* 硬件解码器列表名称 */
  137. /* 视频相关信息 */
  138. QSize m_srcSize; /* 原始视频分辨率大小 */
  139. qint64 m_totalFrame = 0; /* 视频总帧数 */
  140. int m_fps = 0; /* 每秒的帧数 */
  141. qint64 m_duration = 0; /* 视频时长,单位毫秒 */
  142. qint64 m_startPos = 0; /* 开始播放的位置,摄像机视频的位置不是从0开始的,需要在初始化的时候取出这个值 */
  143. std::atomic<qint64> m_pts = 0; /* 当前帧显示时间,也就是当前的进度时间 */
  144. qint64 m_targetPos = -1; /* 跳转的目标播放位置 */
  145. qint64 m_currentFrame = 0; /* 当前已播放的帧数 */
  146. QString m_decoderName; /* 解码器名称 */
  147. // EFrameFormat m_frameFormat = EFrameFormat::Frame_YUV420P; /* 视频帧格式,默认是YUV420P */
  148. RingQueue<FrameBase*> m_queueFrame; /* 环形队列,存储生成的图像 */
  149. };
  150. #endif /* DECODEVEDIO_H */