浏览代码

临时提交

Apple 3 天之前
父节点
当前提交
39f50b715c
共有 42 个文件被更改,包括 1461 次插入542 次删除
  1. 0 7
      .vscode/launch.json
  2. 2 2
      CMakeLists.txt
  3. 10 7
      demo/OpenGLWidgetLibrary/CMakeLists.txt
  4. 1 1
      demo/OpenGLWidgetLibrary/GLShader/ShaderBase.cpp
  5. 0 0
      demo/OpenGLWidgetLibrary/GLShader/ShaderBase.h
  6. 2 0
      demo/OpenGLWidgetLibrary/GLShader/ShaderRect.cpp
  7. 0 0
      demo/OpenGLWidgetLibrary/GLShader/ShaderRect.h
  8. 7 0
      demo/OpenGLWidgetLibrary/GLShader/ShaderYUV420.cpp
  9. 0 0
      demo/OpenGLWidgetLibrary/GLShader/ShaderYUV420.h
  10. 0 0
      demo/OpenGLWidgetLibrary/GLWidget/PlayerGLWidget.cpp
  11. 0 0
      demo/OpenGLWidgetLibrary/GLWidget/PlayerGLWidget.h
  12. 2 2
      demo/OpenGLWidgetLibrary/OpenGLWidgetAPI.cpp
  13. 0 0
      demo/OpenGLWidgetLibrary/OpenGLWidgetAPI.h
  14. 0 0
      demo/OpenGLWidgetLibrary/resShaderCode.qrc
  15. 0 0
      demo/OpenGLWidgetLibrary/shaderCode/fragmentShader.glsl
  16. 0 0
      demo/OpenGLWidgetLibrary/shaderCode/fragmentShaderRGBA.glsl
  17. 4 1
      demo/OpenGLWidgetLibrary/shaderCode/fragmentShaderYUV420.glsl
  18. 0 0
      demo/OpenGLWidgetLibrary/shaderCode/vertexShader.glsl
  19. 0 0
      demo/OpenGLWidgetLibrary/shaderCode/vertexShaderRGBA.glsl
  20. 0 0
      demo/OpenGLWidgetLibrary/shaderCode/vertexShaderYUV420.glsl
  21. 8 7
      demo/OpenGLWidgetTest/CMakeLists.txt
  22. 0 154
      demo/OpenGLWidgetTest/OpenGLWidgetAPI/OpenGLWidgetAPI.cpp
  23. 0 42
      demo/OpenGLWidgetTest/OpenGLWidgetAPI/OpenGLWidgetAPI.h
  24. 54 0
      demo/OpenGLWidgetTest/VideoPlayerAPI__/IVideoPlayer.h
  25. 84 0
      demo/OpenGLWidgetTest/VideoPlayerAPI__/VideoPlayerAPI.cpp
  26. 26 0
      demo/OpenGLWidgetTest/VideoPlayerAPI__/VideoPlayerAPI.h
  27. 49 45
      demo/OpenGLWidgetTest/widget.cpp
  28. 13 2
      demo/OpenGLWidgetTest/widget.h
  29. 58 2
      demo/OpenGLWidgetTest/widget.ui
  30. 6 4
      demo/VideoPlayer/CMakeLists.txt
  31. 1 1
      demo/VideoPlayerGL/GLWidget/PlayerGLWidget.cpp
  32. 115 0
      demo/VideoPlayerLibrary/CMakeLists.txt
  33. 257 153
      demo/VideoPlayerLibrary/VideoPlayer/DecodeVedio.cpp
  34. 22 7
      demo/VideoPlayerLibrary/VideoPlayer/DecodeVedio.h
  35. 61 0
      demo/VideoPlayerLibrary/VideoPlayer/FrameFormat.cpp
  36. 61 0
      demo/VideoPlayerLibrary/VideoPlayer/FrameFormat.h
  37. 3 0
      demo/VideoPlayerLibrary/VideoPlayer/IVideoPlayer.cpp
  38. 54 0
      demo/VideoPlayerLibrary/VideoPlayer/IVideoPlayer.h
  39. 129 77
      demo/VideoPlayerLibrary/VideoPlayer/VideoPlayer.cpp
  40. 47 28
      demo/VideoPlayerLibrary/VideoPlayer/VideoPlayer.h
  41. 323 0
      demo/VideoPlayerLibrary/VideoPlayerAPI/VideoPlayerAPI.cpp
  42. 62 0
      demo/VideoPlayerLibrary/VideoPlayerAPI/VideoPlayerAPI.h

+ 0 - 7
.vscode/launch.json

@@ -1,7 +0,0 @@
-{
-    // 使用 IntelliSense 了解相关属性。 
-    // 悬停以查看现有属性的描述。
-    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
-    "version": "0.2.0",
-    "configurations": []
-}

+ 2 - 2
CMakeLists.txt

@@ -196,8 +196,8 @@ file(GLOB GLOBAL_SRC
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/xlsx)
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/DesignerPattern)
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/ViewModel)
-add_subdirectory(${CMAKE_SOURCE_DIR}/demo/OpenGLWidget)
+add_subdirectory(${CMAKE_SOURCE_DIR}/demo/OpenGLWidgetLibrary)
 # add_subdirectory(${CMAKE_SOURCE_DIR}/demo/VideoPlayerGL)
 add_subdirectory(${CMAKE_SOURCE_DIR}/demo/OpenGLWidgetTest)
-
+add_subdirectory(${CMAKE_SOURCE_DIR}/demo/VideoPlayerLibrary)
 

+ 10 - 7
demo/OpenGLWidget/CMakeLists.txt → demo/OpenGLWidgetLibrary/CMakeLists.txt

@@ -27,9 +27,9 @@ add_library(${libName} SHARED
     ${LOCAL_SRC} 
 )
 
-# set_target_properties(${this_exe} PROPERTIES
-    
-# )
+set_target_properties(${libName} PROPERTIES
+    DEBUG_POSTFIX d
+)
 
 
 #添加头文件
@@ -57,6 +57,13 @@ target_link_libraries(${libName} PRIVATE
 
 )
 
+target_link_libraries(${libName} PRIVATE
+    # ${CURL_LIBRARY}
+    ${FFMPEG_LIBRARY}
+    ${spdlog_LIBRARY}
+
+)
+
 if(QT_VERSION_MAJOR EQUAL 5)
     target_link_libraries(${libName} PRIVATE
         Qt5::OpenGL
@@ -69,14 +76,10 @@ endif(QT_VERSION_MAJOR EQUAL 5)
 
 if(CMAKE_SYSTEM_NAME MATCHES "Windows")
     target_link_libraries(${libName} PRIVATE 
-        # ${CURL_LIBRARY}
-        # ${FFMPEG_LIBRARY}
-        ${spdlog_LIBRARY}
         opengl32
     )
 elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
     target_link_libraries(${libName} PRIVATE 
-        ${spdlog_LIBRARY}
         GL
     )
 endif()

+ 1 - 1
demo/OpenGLWidget/GLShader/ShaderBase.cpp → demo/OpenGLWidgetLibrary/GLShader/ShaderBase.cpp

@@ -174,7 +174,7 @@ int ShaderBase::createTexture(const QString uniformName, int textureUnit)
     QOpenGLTexture* texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
     /* 设置纹理参数 */
     texture->setWrapMode(QOpenGLTexture::Repeat); // S轴环绕方式
-    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); // 纹理缩小过滤方式
+    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); // 纹理缩小过滤方式:三线性过滤
     texture->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
     
 

+ 0 - 0
demo/OpenGLWidget/GLShader/ShaderBase.h → demo/OpenGLWidgetLibrary/GLShader/ShaderBase.h


+ 2 - 0
demo/OpenGLWidget/GLShader/ShaderRect.cpp → demo/OpenGLWidgetLibrary/GLShader/ShaderRect.cpp

@@ -144,6 +144,8 @@ bool ShaderRect::refreshFrameRGBA(const QImage& image, int textureUnit)
     }
     m_lastSize = image.size();
     texture->setData(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.bits()); // 设置纹理数据
+    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); // 纹理缩小过滤方式
+    texture->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
 
     texture->generateMipMaps(); // 生成多级渐远纹理
 

+ 0 - 0
demo/OpenGLWidget/GLShader/ShaderRect.h → demo/OpenGLWidgetLibrary/GLShader/ShaderRect.h


+ 7 - 0
demo/OpenGLWidget/GLShader/ShaderYUV420.cpp → demo/OpenGLWidgetLibrary/GLShader/ShaderYUV420.cpp

@@ -3,6 +3,7 @@
 
 #include <QOpenGLWidget>
 #include <QOpenGLFunctions_3_3_Core>
+#include <qopengltexture.h>
 
 #include "spdlog/spdlog.h"
 
@@ -219,14 +220,20 @@ bool ShaderYUV420::refreshFrameYUV420(const Image_YUV420P& yuvData)
     /* 设置Y分量纹理 */
     textureY->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvData.yData.constData());
     textureY->generateMipMaps(); // 生成多级渐远纹理
+    textureY->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
+    textureY->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
 
     /* 设置U分量纹理 */
     textureU->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvData.uData.constData());
     textureU->generateMipMaps(); // 生成多级渐远纹理
+    textureU->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
+    textureU->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
 
     /* 设置V分量纹理 */
     textureV->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvData.vData.constData());
     textureV->generateMipMaps(); // 生成多级渐远纹理
+    textureV->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
+    textureV->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
 
     printGLerror("ShaderYUV420::refreshFrameYUV420");
 

+ 0 - 0
demo/OpenGLWidget/GLShader/ShaderYUV420.h → demo/OpenGLWidgetLibrary/GLShader/ShaderYUV420.h


+ 0 - 0
demo/OpenGLWidget/GLWidget/PlayerGLWidget.cpp → demo/OpenGLWidgetLibrary/GLWidget/PlayerGLWidget.cpp


+ 0 - 0
demo/OpenGLWidget/GLWidget/PlayerGLWidget.h → demo/OpenGLWidgetLibrary/GLWidget/PlayerGLWidget.h


+ 2 - 2
demo/OpenGLWidget/OpenGLWidgetAPI.cpp → demo/OpenGLWidgetLibrary/OpenGLWidgetAPI.cpp

@@ -71,9 +71,9 @@ void refreshYUV420Image(QWidget* widget, Image_YUV420P* yuvData)
         return;
     }
     
-    if(yuvData == nullptr || !yuvData->isValid())
+    if(yuvData == nullptr)
     {
-        SPDLOG_WARN("refreshYUV420Image: yuvData is nullptr or invalid");
+        SPDLOG_WARN("refreshYUV420Image: yuvData is nullptr");
         return;
     }
     

+ 0 - 0
demo/OpenGLWidget/OpenGLWidgetAPI.h → demo/OpenGLWidgetLibrary/OpenGLWidgetAPI.h


+ 0 - 0
demo/OpenGLWidget/resShaderCode.qrc → demo/OpenGLWidgetLibrary/resShaderCode.qrc


+ 0 - 0
demo/OpenGLWidget/shaderCode/fragmentShader.glsl → demo/OpenGLWidgetLibrary/shaderCode/fragmentShader.glsl


+ 0 - 0
demo/OpenGLWidget/shaderCode/fragmentShaderRGBA.glsl → demo/OpenGLWidgetLibrary/shaderCode/fragmentShaderRGBA.glsl


+ 4 - 1
demo/OpenGLWidget/shaderCode/fragmentShaderYUV420.glsl → demo/OpenGLWidgetLibrary/shaderCode/fragmentShaderYUV420.glsl

@@ -11,11 +11,14 @@ uniform sampler2D textureV;
 
 void main()
 {
+    //在着色器里取出某个位置上的像素值,rgba四分量,单通道只有r有效
+    //y的范围是[0, 1],u和v的范围是[-0.5, 0.5]
     float y = texture(textureY, TexCoord).r;
+    //uv只有y分量的四分之一,映射到屏幕上的范围是[-0.5-0.5],所以需要减去0.5
     float u = texture(textureU, TexCoord).r - 0.5;
     float v = texture(textureV, TexCoord).r - 0.5;
     float r = y + 1.402 * v;
-    float g = y - 0.344 * u - 0.714 * v;
+    float g = y - 0.344136 * u - 0.714136 * v;
     float b = y + 1.772 * u;
     FragColor = vec4(r, g, b, 1.0);
 }

+ 0 - 0
demo/OpenGLWidget/shaderCode/vertexShader.glsl → demo/OpenGLWidgetLibrary/shaderCode/vertexShader.glsl


+ 0 - 0
demo/OpenGLWidget/shaderCode/vertexShaderRGBA.glsl → demo/OpenGLWidgetLibrary/shaderCode/vertexShaderRGBA.glsl


+ 0 - 0
demo/OpenGLWidget/shaderCode/vertexShaderYUV420.glsl → demo/OpenGLWidgetLibrary/shaderCode/vertexShaderYUV420.glsl


+ 8 - 7
demo/OpenGLWidgetTest/CMakeLists.txt

@@ -10,12 +10,13 @@ file(GLOB LOCAL_SRC
     ${CMAKE_CURRENT_SOURCE_DIR}/*.rc
     ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/*.ui
-    ${CMAKE_CURRENT_SOURCE_DIR}/OpenGLWidgetAPI/*.cpp
-    ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayer/*.cpp
+    # ${CMAKE_CURRENT_SOURCE_DIR}/OpenGLWidgetAPI/*.cpp
+    # ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayer/*.cpp
+    # ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayerAPI/*.cpp
 
     ${CMAKE_SOURCE_DIR}/External/module/Logs/*.cpp
     ${CMAKE_SOURCE_DIR}/External/module/ThreadPool/*.cpp
-    
+    ${VideoPlayer_SOURCE_DIR}/*.cpp
 )
 
 
@@ -36,8 +37,9 @@ add_executable(${this_exe}
 target_include_directories(${this_exe} PRIVATE
 
     ${CMAKE_CURRENT_SOURCE_DIR}
-    ${CMAKE_CURRENT_SOURCE_DIR}/OpenGLWidgetAPI
-    ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayer
+    # ${CMAKE_CURRENT_SOURCE_DIR}/OpenGLWidgetAPI
+    # ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayer
+    # ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayerAPI
 
     ${CMAKE_SOURCE_DIR}/External/common
     ${CMAKE_SOURCE_DIR}/External/module
@@ -46,7 +48,7 @@ target_include_directories(${this_exe} PRIVATE
     
     ${spdlog_INCLUDE_DIR}
     ${FFMPEG_INCLUDE_DIR}
-    
+    ${VideoPlayer_INCLUDE_DIR}
 )
 
 
@@ -59,7 +61,6 @@ target_link_libraries(${this_exe} PRIVATE
 )
 
 target_link_libraries(${this_exe} PRIVATE
-    # ${CURL_LIBRARY}
     ${FFMPEG_LIBRARY}
     ${spdlog_LIBRARY}
 

+ 0 - 154
demo/OpenGLWidgetTest/OpenGLWidgetAPI/OpenGLWidgetAPI.cpp

@@ -1,154 +0,0 @@
-#include "OpenGLWidgetAPI.h"
-
-#include <QApplication>
-#include <QLibrary>
-
-#include "spdlog/spdlog.h"
-
-Image_YUV420P::Image_YUV420P(Image_YUV420P&& other): width(other.width), height(other.height),
-          yData(std::move(other.yData)), uData(std::move(other.uData)), vData(std::move(other.vData))
-{}
-
-Image_YUV420P::Image_YUV420P(const Image_YUV420P& other)
-        : width(other.width), height(other.height),
-          yData(other.yData), uData(other.uData), vData(other.vData)
-{}
-
-Image_YUV420P& Image_YUV420P::operator=(Image_YUV420P&& other) 
-{
-    if (this != &other) {
-        width = other.width;
-        height = other.height;
-        yData = std::move(other.yData);
-        uData = std::move(other.uData);
-        vData = std::move(other.vData);
-    }
-    return *this;
-}
-
-Image_YUV420P& Image_YUV420P::operator=(const Image_YUV420P& other) 
-{
-    if (this != &other) {
-        width = other.width;
-        height = other.height;
-        yData = other.yData;
-        uData = other.uData;
-        vData = other.vData;
-    }
-    return *this;
-}
-
-bool Image_YUV420P::isValid() const 
-{
-    return width > 0 && height > 0 && !yData.isEmpty() && !uData.isEmpty() && !vData.isEmpty();
-}
-
-void Image_YUV420P::clear()
-{
-    width = 0;
-    height = 0;
-    yData.clear();
-    uData.clear();
-    vData.clear();
-}
-
-/* ----------------------------------------------------------------
- * OpenGLWidget动态库 API
- * ---------------------------------------------------------------- */
-
-using funcCreateOpenGLWidget = QWidget* (*)(QWidget*);
-using funcDestroyOpenGLWidget = void (*)(QWidget* widget);
-using funcRefreshRGBAImage = void (*)(QWidget* widget, QImage& image);
-using funcRefreshYUV420Image = void (*)(QWidget* widget, Image_YUV420P* yuvData);
-
-
-funcCreateOpenGLWidget pCreateOpenGLWidget = nullptr;
-funcDestroyOpenGLWidget pDestroyOpenGLWidget = nullptr;
-funcRefreshRGBAImage pRefreshRGBAImage = nullptr;
-funcRefreshYUV420Image pRefreshYUV420Image = nullptr;
-
-static std::atomic_bool isLibraryLoaded = false;
-/* 加载动态库 */
-bool loadOpenGLWidgetLibrary()
-{
-    if(isLibraryLoaded.load()) 
-    {
-        return true;
-    }
-
-    QString libPath = QApplication::applicationDirPath();
-#ifdef Q_OS_WIN
-    libPath += "/OpenGLWidget.dll";
-#elif defined(Q_OS_MACOS)
-    libPath += "/libOpenGLWidget.dylib";
-#elif defined(Q_OS_LINUX)
-    libPath += "/libOpenGLWidget.so";
-#endif
-    QLibrary lib(libPath);
-    if (!lib.load()) 
-    {
-        SPDLOG_ERROR("加载OpenGLWidget动态库失败: {}", libPath.toStdString());
-        SPDLOG_ERROR("错误信息: {}", lib.errorString().toStdString());
-        return false;
-    }
-
-    pCreateOpenGLWidget = reinterpret_cast<funcCreateOpenGLWidget>(lib.resolve("createOpenGLWidget"));
-    pDestroyOpenGLWidget = reinterpret_cast<funcDestroyOpenGLWidget>(lib.resolve("destroyOpenGLWidget"));
-    pRefreshRGBAImage = reinterpret_cast<funcRefreshRGBAImage>(lib.resolve("refreshRGBAImage"));
-    pRefreshYUV420Image = reinterpret_cast<funcRefreshYUV420Image>(lib.resolve("refreshYUV420Image"));
-
-    if(!pCreateOpenGLWidget || !pDestroyOpenGLWidget || 
-       !pRefreshRGBAImage || !pRefreshYUV420Image) 
-    {
-        SPDLOG_ERROR("加载OpenGLWidget动态库函数失败");
-        return false;
-    }
-
-    isLibraryLoaded = true;
-    return true;
-}
-
-/* 创建OpenGL窗口,返回QWidget指针 */
-QWidget* createOpenGLWidget(QWidget* parent)
-{
-    if(pCreateOpenGLWidget == nullptr) 
-    {
-        SPDLOG_ERROR("OpenGLWidget动态库函数未加载");
-        return nullptr;
-    }
-    return pCreateOpenGLWidget(parent);
-}
-
-/* 销毁OpenGL窗口 */
-void destroyOpenGLWidget(QWidget* widget)
-{
-    if(pDestroyOpenGLWidget == nullptr) 
-    {
-        SPDLOG_ERROR("OpenGLWidget动态库函数未加载");
-        return;
-    }
-    pDestroyOpenGLWidget(widget);
-}
-
-/* 刷新一帧RGBA图片 */
-void refreshRGBAImage(QWidget* widget, QImage& image)
-{
-    if(pRefreshRGBAImage == nullptr) 
-    {
-        SPDLOG_ERROR("OpenGLWidget动态库函数未加载");
-        return;
-    }
-    pRefreshRGBAImage(widget, image);
-}
-
-/* 刷新一帧YUV420图片 */
-void refreshYUV420Image(QWidget* widget, Image_YUV420P& yuvData)
-{
-    if(pRefreshYUV420Image == nullptr) 
-    {
-        SPDLOG_ERROR("OpenGLWidget动态库函数未加载");
-        return;
-    }
-    pRefreshYUV420Image(widget, &yuvData);
-}
-

+ 0 - 42
demo/OpenGLWidgetTest/OpenGLWidgetAPI/OpenGLWidgetAPI.h

@@ -1,42 +0,0 @@
-#ifndef _OPENGLWIDGETAPI_H_
-#define _OPENGLWIDGETAPI_H_
-
-#include <QWidget>
-#include <QString>
-
-/**
- * @brief YUV420图片格式
- * 
- */
-struct Image_YUV420P
-{
-    int width;          /* 宽度 */
-    int height;         /* 高度 */
-    QByteArray yData;   /* Y分量数据 */
-    QByteArray uData;   /* U分量数据 */
-    QByteArray vData;   /* V分量数据 */
-
-    Image_YUV420P() : width(0), height(0) {}
-    Image_YUV420P(Image_YUV420P&& other);
-    Image_YUV420P(const Image_YUV420P& other);
-    Image_YUV420P& operator=(Image_YUV420P&& other);
-    Image_YUV420P& operator=(const Image_YUV420P& other);
-    bool isValid() const;
-    void clear();
-};
-
-/* 加载动态库 */
-bool loadOpenGLWidgetLibrary();
-
-/* 创建OpenGL窗口,返回QWidget指针 */
-QWidget* createOpenGLWidget(QWidget* parent = nullptr);
-/* 销毁OpenGL窗口 */
-void destroyOpenGLWidget(QWidget* widget);
-
-/* 刷新一帧RGBA图片 */
-void refreshRGBAImage(QWidget* widget, QImage& image);
-/* 刷新一帧YUV420图片 */
-void refreshYUV420Image(QWidget* widget, Image_YUV420P& yuvData);
-
-
-#endif // _OPENGLWIDGETAPI_H_

+ 54 - 0
demo/OpenGLWidgetTest/VideoPlayerAPI__/IVideoPlayer.h

@@ -0,0 +1,54 @@
+#ifndef _IVIDEOPLAYER_H_
+#define _IVIDEOPLAYER_H_
+
+#include <QObject>
+
+class IVideoPlayer : public QObject
+{
+    Q_OBJECT
+public:
+
+    explicit IVideoPlayer() {}
+    virtual ~IVideoPlayer() {}
+
+    /* 打开播放视频 */
+    virtual bool openPlayVedio(const QString& fileName) = 0;
+    /* 播放视频 */
+    virtual bool play() = 0;
+    /* 暂停播放 */
+    virtual void pause() = 0;
+    /* 停止播放 */
+    virtual void stop() = 0;
+    /* 后退,单位ms */
+    virtual void backward(qint64 ms) = 0;
+    /* 前进,单位ms */
+    virtual void forward(qint64 ms) = 0;
+
+    /* 获取播放状态 */
+    virtual bool getPlayStatus() = 0;
+    /* 获取视频时长 */
+    virtual qint64 getDuration() = 0;
+    /* 获取当前播放位置 */
+    virtual qint64 getCurrentPos() = 0;
+    /* 设置当前播放位置 */
+    virtual void setCurrentPos(qint64 pos) = 0;
+
+    /* 设置播放视频窗口的大小 */
+    virtual void setPlayWidgetSize(int width,int height) = 0;
+    /* 设置播放窗口 */
+    virtual void setWidgetParent(QWidget* widget, bool isFull = false) = 0;
+    /* 获取播放窗口的指针 */
+    virtual QWidget* getPlayWidget() const = 0;
+    /* 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧 */
+    virtual void setPreviewImage(int num = 2) = 0;
+    /* 设置播放速度 */
+    virtual void setPlaySpeed(float speed) = 0;
+
+signals:
+    void signal_playCompleted();                    /* 播放完成信号 */
+    // void signal_refreshImage();                     /* 刷新图片信号 */
+
+};
+
+
+#endif /* _IVIDEOPLAYER_H_ */

+ 84 - 0
demo/OpenGLWidgetTest/VideoPlayerAPI__/VideoPlayerAPI.cpp

@@ -0,0 +1,84 @@
+#include "VideoPlayerAPI.h"
+
+#include <QApplication>
+#include <QLibrary>
+#include <qchar.h>
+#include <qlibrary.h>
+
+#include "spdlog/spdlog.h"
+
+/* 定义函数 */
+using FuncCreateVideoPlayer = IVideoPlayer* (*)();
+using FuncDestroyVideoPlayer = void (*)(IVideoPlayer* player);
+
+/* 创建函数指针 */
+FuncCreateVideoPlayer pCreateVideoPlayer = nullptr;
+FuncDestroyVideoPlayer pDestroyVideoPlayer = nullptr;
+
+
+/* 加载动态库 */
+bool loadVideoPlayerLibrary()
+{
+    QString libPath = QApplication::applicationDirPath();
+
+#ifdef QT_DEBUG
+    libPath += "/libVideoPlayerd"; // Debug版本
+#else
+    libPath += "/libVideoPlayer"; // Release版本
+#endif
+
+
+#if defined (Q_OS_LINUX)
+    libPath += ".so"; // 或者 .dll 或者 .dylib
+#elif defined (Q_OS_WIN)
+    libPath += ".dll"; // Windows下的动态库
+#elif defined (Q_OS_MACOS)
+    libPath += ".dylib"; // macOS下的动态库
+#endif
+
+    QLibrary lib(libPath);
+    if(!lib.load())
+    {
+        SPDLOG_ERROR("加载VideoPlayerAPI库失败: {}", lib.errorString().toStdString());
+        SPDLOG_ERROR("VideoPlayer动态库路径: {}", libPath.toStdString());
+        return false;
+    }
+
+    pCreateVideoPlayer = reinterpret_cast<FuncCreateVideoPlayer>(lib.resolve("createVideoPlayer"));
+    if(!pCreateVideoPlayer)
+    {
+        SPDLOG_ERROR("无法解析createVideoPlayer函数: {}", lib.errorString().toStdString());
+        return false;
+    }
+    pDestroyVideoPlayer = reinterpret_cast<FuncDestroyVideoPlayer>(lib.resolve("destroyVideoPlayer"));
+    if(!pDestroyVideoPlayer)
+    {
+        SPDLOG_ERROR("无法解析destroyVideoPlayer函数: {}", lib.errorString().toStdString());
+        return false;
+    }
+
+    return true;
+}
+
+/* 创建播放器 */
+IVideoPlayer* createPlayer()
+{
+    if(pCreateVideoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("createVideoPlayer函数指针为空,请先加载动态库");
+        return nullptr;
+    }
+    return pCreateVideoPlayer();
+}
+
+/* 销毁播放器 */
+void destroyPlayer(IVideoPlayer* player)
+{
+    if(pDestroyVideoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("destroyVideoPlayer函数指针为空,请先加载动态库");
+        return;
+    }
+    pDestroyVideoPlayer(player);
+}
+

+ 26 - 0
demo/OpenGLWidgetTest/VideoPlayerAPI__/VideoPlayerAPI.h

@@ -0,0 +1,26 @@
+#ifndef _VIDEOPLAYERAPI_H_
+#define _VIDEOPLAYERAPI_H_
+
+
+#include "IVideoPlayer.h"
+
+
+
+extern "C" {
+
+
+/* 加载动态库 */
+bool loadVideoPlayerLibrary();
+
+/* 创建播放器 */
+IVideoPlayer* createPlayer();
+/* 销毁播放器 */
+void destroyPlayer(IVideoPlayer* player);
+
+
+
+};
+
+
+
+#endif /* _VIDEOPLAYERAPI_H_ */

+ 49 - 45
demo/OpenGLWidgetTest/widget.cpp

@@ -1,12 +1,11 @@
 #include "widget.h"
 #include "./ui_widget.h"
 
-#include <QTimer>
+#include <QFileDialog>
 #include "spdlog/spdlog.h"
 
-#include "OpenGLWidgetAPI.h"
 
-#include "VideoPlayer.h"
+
 
 Widget::Widget(QWidget *parent)
     : QWidget(parent)
@@ -14,64 +13,69 @@ Widget::Widget(QWidget *parent)
 {
     ui->setupUi(this);
 
-    loadOpenGLWidgetLibrary(); // 加载OpenGL窗口库
-
-    // m_playerGLWidget = createOpenGLWidget(ui->widget_display);
-    // m_playerGLWidget->show(); // 显示OpenGL窗口
-    // m_playerGLWidget->setGeometry(0, 0, ui->widget_display->width(), ui->widget_display->height());
-    // m_playerGLWidget->setStyleSheet(R"(border-radius:10px;)");
-
-    // /* 设置背景颜色 */
-    // this->setAutoFillBackground(true);
-    // QPalette palette = m_playerGLWidget->palette();
-    // palette.setColor(QPalette::Window, Qt::black); // 设置背景颜色为黑色
-    // this->setPalette(palette);
-
-    
-    // connect(&m_timer, &QTimer::timeout, this, [&]() {
-    //     // SPDLOG_DEBUG("刷新一帧");
-    //     QImage image = QImage(":/image/1.jpg");
-    //     if(image.isNull()) {
-    //         SPDLOG_WARN("Image is null, cannot refresh.");
-    //         return;
-    //     }
-    //     refreshRGBAImage(m_playerGLWidget, image); // 显示一张测试图片
-    // });
-    // m_timer.setSingleShot(false);
-    // m_timer.start(10); // 60 FPS
-
-    playVdeio();
-    
+    /* 加载动态库 */
+    loadVideoPlayerLibrary();
+    /* 创建播放器,并设置窗口 */
+    m_player = createPlayer();
+    if(m_player == nullptr) {
+        SPDLOG_ERROR("创建播放器失败");
+        return;
+    }
+    m_player->setWidgetParent(ui->widget_display, true);
+    m_playerWidget = m_player->getPlayWidget();
+    if(m_playerWidget == nullptr) {
+        SPDLOG_ERROR("获取播放器窗口失败");
+        return;
+    }
 }
 
 Widget::~Widget()
 {
-
+    if(m_player != nullptr) {
+        /* 销毁播放器 */
+        destroyPlayer(m_player);
+        m_player = nullptr;
+    }
     delete ui;
 }
 
-void Widget::playVdeio()
+void Widget::on_pBtn_openFile_clicked()
 {
-    // QString videoFile = QApplication::applicationDirPath() + "/YPM.rmvb";
-    // QString videoFile = QApplication::applicationDirPath() + "/2024-07-11.mp4"; 
-    QString videoFile = QApplication::applicationDirPath() + "/4K烤鸭.mp4"; // 测试视频文件
+    // 打开文件对话框
+    QString fileName = QFileDialog::getOpenFileName(this, tr("打开视频文件"), "", tr("视频文件 (*.mp4 *.avi *.mkv *.flv *.mov)"));
+    if (fileName.isEmpty()) {
+        SPDLOG_WARN("未选择视频文件");
+        return;
+    }
+    m_player->openPlayVedio(fileName);
+    SPDLOG_INFO("打开视频文件: {}", fileName.toStdString());
+}
+
+void Widget::on_pBtn_play_clicked()
+{
+    m_player->play();
+}
 
-    VideoPlayer* player = new VideoPlayer(this);
-    player->setWidgetParent(ui->widget_display, true); // 设置播放窗口
-    if(!player->openPlayVedio(videoFile))
+void Widget::on_pBtn_playNetwork_clicked()
+{
+    QString url = ui->lineEdit->text();
+    if (url.isEmpty()) {
+        SPDLOG_WARN("未输入网络视频地址");
+        return;
+    }
+    if(!m_player->openPlayVedio(url))
     {
-        SPDLOG_ERROR("Failed to open video file: {}", videoFile.toStdString());
-        delete player; // 释放资源
+        SPDLOG_ERROR("打开网络视频失败: {}", url.toStdString());
         return;
     }
-
-    player->play(); // 播放视频
+    m_player->play();
+    SPDLOG_INFO("播放网络视频: {}", url.toStdString());
 }
 
 void Widget::resizeEvent(QResizeEvent *event) 
 {
-    if (m_playerGLWidget) {
-        m_playerGLWidget->setGeometry(0, 0, ui->widget_display->width(), ui->widget_display->height());
+    if (m_playerWidget) {
+        m_playerWidget->setGeometry(0, 0, ui->widget_display->width(), ui->widget_display->height());
     }
     QWidget::resizeEvent(event);
 }

+ 13 - 2
demo/OpenGLWidgetTest/widget.h

@@ -4,6 +4,10 @@
 #include <QWidget>
 #include <memory>
 #include <QTimer>
+#include <qwidget.h>
+
+#include "IVideoPlayer.h"
+#include "VideoPlayerAPI.h"
 
 QT_BEGIN_NAMESPACE
 namespace Ui { class Widget; }
@@ -17,7 +21,13 @@ public:
     Widget(QWidget *parent = nullptr);
     ~Widget();
 
-    void playVdeio();
+
+public slots:
+    void on_pBtn_openFile_clicked();
+
+    void on_pBtn_play_clicked();
+
+    void on_pBtn_playNetwork_clicked();
 
 private slots:
 
@@ -27,7 +37,8 @@ protected:
 private:
     Ui::Widget *ui;
 
-    QWidget *m_playerGLWidget = nullptr;
+    QWidget* m_playerWidget = nullptr;  // 播放器的显示窗口
+    IVideoPlayer *m_player = nullptr;   
 
     QTimer m_timer;
 

+ 58 - 2
demo/OpenGLWidgetTest/widget.ui

@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>1600</width>
-    <height>900</height>
+    <width>870</width>
+    <height>605</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -32,6 +32,62 @@
    <item>
     <widget class="QWidget" name="widget_display" native="true"/>
    </item>
+   <item>
+    <widget class="QWidget" name="widget_bottom" native="true">
+     <property name="maximumSize">
+      <size>
+       <width>16777215</width>
+       <height>60</height>
+      </size>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <widget class="QPushButton" name="pBtn_openFile">
+        <property name="text">
+         <string>打开视频</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="pBtn_play">
+        <property name="text">
+         <string>播放视频</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEdit">
+        <property name="minimumSize">
+         <size>
+          <width>240</width>
+          <height>0</height>
+         </size>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="pBtn_playNetwork">
+        <property name="text">
+         <string>播放网络视频</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>647</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
   </layout>
  </widget>
  <resources/>

+ 6 - 4
demo/VideoPlayer/CMakeLists.txt

@@ -1,6 +1,6 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.10)
 
-set(this_exe VideoPlayer)
+set(this_exe VideoPlayer1)
 
 
 #包含源文件
@@ -44,9 +44,10 @@ target_include_directories(${this_exe} PRIVATE
     ${CMAKE_SOURCE_DIR}/External/module/RingQueue
     ${CMAKE_SOURCE_DIR}/External/module/VideoPlayer
     
+    ${spdlog_INCLUDE_DIR}
     ${CURL_INCLUDE_DIR}
     ${FFMPEG_INCLUDE_DIR}
-    ${spdlog_INCLUDE_DIR}
+    
 )
 
 target_link_libraries(${this_exe} PRIVATE
@@ -61,9 +62,10 @@ target_link_libraries(${this_exe} PRIVATE
 target_link_libraries(${this_exe} PRIVATE 
     # fmt::fmt
     # spdlog::spdlog
+    ${spdlog_LIBRARY}
     ${CURL_LIBRARY}
     ${FFMPEG_LIBRARY}
-    ${spdlog_LIBRARY}
+    
 )
 
 if(CMAKE_CXX_COMPILER_VERSION LESS 9.0)

+ 1 - 1
demo/VideoPlayerGL/GLWidget/PlayerGLWidget.cpp

@@ -37,7 +37,7 @@ void PlayerGLWidget::showOneRGBAImage(const QImage& image)
 }
 
 /* 显示一张YUV420的图片 */
-void PlayerGLWidget::showOneYUV420Image(const Image_YUV420& yuvData)
+void PlayerGLWidget::showOneYUV420Image(const Image_YUV420P& yuvData)
 {
     if(!yuvData.isValid()) {
         SPDLOG_WARN("YUV420数据无效");

+ 115 - 0
demo/VideoPlayerLibrary/CMakeLists.txt

@@ -0,0 +1,115 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(libName VideoPlayer)
+
+
+
+#包含源文件
+file(GLOB LOCAL_SRC
+    ${CMAKE_CURRENT_SOURCE_DIR}/*.qrc
+    ${CMAKE_CURRENT_SOURCE_DIR}/*.rc
+    ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/*.ui
+    # ${CMAKE_CURRENT_SOURCE_DIR}/OpenGLWidgetAPI/*.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayer/*.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayerAPI/*.cpp
+
+    ${CMAKE_SOURCE_DIR}/External/module/Logs/*.cpp
+    # ${CMAKE_SOURCE_DIR}/External/module/ThreadPool/*.cpp
+    ${OpenGLWidget_SOURCE_DIR}/*.cpp
+)
+
+
+# 生成可执行程序
+
+add_library(${libName} SHARED
+    # WIN32
+    ${GLOBAL_SRC}
+    ${LOCAL_SRC} 
+)
+
+set_target_properties(${libName} PROPERTIES
+    DEBUG_POSTFIX d
+)
+
+
+#添加头文件
+target_include_directories(${libName} PRIVATE
+
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    # ${CMAKE_CURRENT_SOURCE_DIR}/OpenGLWidgetAPI
+    ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayer
+    ${CMAKE_CURRENT_SOURCE_DIR}/VideoPlayerAPI
+
+    ${CMAKE_SOURCE_DIR}/External/common
+    ${CMAKE_SOURCE_DIR}/External/module
+    # ${CMAKE_SOURCE_DIR}/External/module/ThreadPool
+    ${CMAKE_SOURCE_DIR}/External/module/RingQueue
+    
+    ${spdlog_INCLUDE_DIR}
+    ${FFMPEG_INCLUDE_DIR}
+    ${OpenGLWidget_INCLUDE_DIR}
+)
+
+
+target_link_libraries(${libName} PRIVATE
+    Qt${QT_VERSION_MAJOR}::Widgets
+    Qt${QT_VERSION_MAJOR}::Core
+    # Qt${QT_VERSION_MAJOR}::Network
+    # Qt${QT_VERSION_MAJOR}::Gui
+
+)
+
+target_link_libraries(${libName} PRIVATE
+    # ${CURL_LIBRARY}
+    ${FFMPEG_LIBRARY}
+    ${spdlog_LIBRARY}
+
+)
+
+if(QT_VERSION_MAJOR EQUAL 5)
+    target_link_libraries(${libName} PRIVATE
+        Qt5::OpenGL
+    )
+elseif(QT_VERSION_MAJOR EQUAL 6)
+    target_link_libraries(${libName} PRIVATE
+        Qt6::OpenGLWidgets
+    )
+endif(QT_VERSION_MAJOR EQUAL 5)
+
+if(CMAKE_SYSTEM_NAME MATCHES "Windows")
+    target_link_libraries(${libName} PRIVATE 
+        opengl32
+    )
+elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
+    target_link_libraries(${libName} PRIVATE 
+        GL
+    )
+endif()
+
+# if(CMAKE_CXX_COMPILER_VERSION LESS 9.0)
+#     target_link_libraries(${libName} PRIVATE
+#         stdc++fs
+#     )
+# endif()
+
+
+
+# target_link_libraries(${libName} PRIVATE
+#     GL
+#     GUL
+# )
+# message(STATUS "CURL_LIBRARY: ${CURL_LIBRARY}")
+
+
+# if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
+#     target_link_libraries(${libName} PRIVATE
+#         # debug spdlogd.lib
+#         # optimized spdlog.lib
+#     )
+# elseif(CMAKE_CXX_COMPILER_ID MATCHES GNU)
+#     target_link_libraries(${libName} PRIVATE
+#         # debug 
+#         # optimized ${SM_DLL}
+#     )
+# endif()

+ 257 - 153
demo/OpenGLWidgetTest/VideoPlayer/DecodeVedio.cpp → demo/VideoPlayerLibrary/VideoPlayer/DecodeVedio.cpp

@@ -1,17 +1,20 @@
 #include "DecodeVedio.h"
+#include "FrameFormat.h"
 #include "spdlog/spdlog.h"
-#include "FmtLog/fmtlog.h"
 
 #include <QThread>
 
 extern "C"
 {
-#include <libavcodec/avcodec.h>
-#include <libavformat/avformat.h>
+// #include <libavcodec/avcodec.h>
+// #include <libavformat/avformat.h>
 #include <libswscale/swscale.h>
 #include <libavutil/imgutils.h>
 }
 
+#include "OpenGLWidgetAPI.h"
+
+
 /*=================================================================================================
 * @brief  FFmpeg获取GPU硬件解码帧格式的回调函数
 ===================================================================================================*/
@@ -136,7 +139,7 @@ void DecodeVedio::setCurrentPos(qint64 pos)
     
     SPDLOG_DEBUG("跳转到:{}ms",pos);
     /*在环形队列中有已解码的视频帧数,需要去掉已有的帧数所占的时间 */
-    pos = pos - m_queueImage.QueueSize() * (1000 / m_fps);
+    pos = pos - m_queueFrame.QueueSize() * (1000 / m_fps);
     if(pos < 0) {
         pos = 0;
     }
@@ -156,16 +159,7 @@ void DecodeVedio::setCurrentPos(qint64 pos)
     m_flushDecoder.store(true);
     /* 清空环形队列中的视频 */
     SPDLOG_DEBUG("清空环形队列中的视频。");
-    QImage* image = 0;
-    while (m_queueImage.QueueSize() > 0)
-    {
-        image = nullptr;
-        m_queueImage.front_pop_noBlock(image);
-        if(image != nullptr)
-        {
-            delete image;
-        }
-    }
+    clearQueueFrame();
     
     /* 继续解码 */
     continueDecode();
@@ -188,18 +182,18 @@ qint64 DecodeVedio::getDuration()
  * @warning 传出这个指针后,队列就出队了,内存需要外面获取的实例释放
  * @return QImage* 一帧图像的指针
  */
-QImage* DecodeVedio::getOneImage()
+FrameBase* DecodeVedio::getOneImage()
 {
     if(!m_threadRuning)
     {
         return nullptr;
     }
-    QImage* image = nullptr;
-    if(!m_queueImage.front_pop_noBlock(image))
-    {
-        return nullptr;
-    }
-    return image;
+    // FrameBase* frame = nullptr;
+    // if(!m_queueFrame.front_pop_noBlock(frame))
+    // {
+    //     return nullptr;
+    // }
+    return m_queueFrame.front_pop_noBlock();
 }
 
 /**
@@ -208,7 +202,7 @@ QImage* DecodeVedio::getOneImage()
  * @param timeOut 设为-1是一直等待,设置正整数是等待的时间,单位ms
  * @return QImage* 
  */
-QImage* DecodeVedio::getOneImageUntilHave(int timeOut)
+FrameBase* DecodeVedio::getOneImageUntilHave(int timeOut)
 {
     if(!m_threadRuning)
     {
@@ -216,15 +210,15 @@ QImage* DecodeVedio::getOneImageUntilHave(int timeOut)
     }
     if(timeOut < 0)
     {
-        QImage* image = m_queueImage.front_pop();
-        return image;
+        auto frame = m_queueFrame.front_pop();
+        return frame;
     }
+    FrameBase* frame = nullptr;
     for(int i = 0; i < timeOut; i++)
     {
-        QImage* image = nullptr;
-        if(m_queueImage.front_pop_noBlock(image))
+        if(m_queueFrame.front_pop_noBlock(frame))
         {
-            return image;
+            return frame;
         }
         std::this_thread::sleep_for(std::chrono::milliseconds(1));
     }
@@ -248,6 +242,28 @@ void DecodeVedio::findHWDecoder(QStringList& listDecoderName)
     }
 }
 
+/* 清空环形队列 */
+void DecodeVedio::clearQueueFrame()
+{
+    while(m_queueFrame.QueueSize() > 0)
+    {
+        FrameBase* frame = nullptr;
+        m_queueFrame.front_pop_noBlock(frame);
+        deleteOneFrame(frame);
+    }
+    
+}
+
+/* 删除一个图片 */
+void DecodeVedio::deleteOneImage()
+{
+    if(m_queueFrame.QueueSize() > 0)
+    {
+        auto frame = m_queueFrame.front_pop_noBlock();
+        deleteOneFrame(frame);
+    }
+}
+
 
 /* 获取硬件解码器 */
 void DecodeVedio::findHWDecoder()
@@ -271,6 +287,8 @@ void DecodeVedio::findHWDecoder()
     }else {
         m_supportHWDecoder = true;
     }
+
+    m_supportHWDecoder = false;
 }
 
 
@@ -357,19 +375,9 @@ bool DecodeVedio::openVedio(const QString& fileName)
     }
 
     /* 清空队列 */
-    if(m_queueImage.QueueSize() > 0)
-    {
-        for(int i = 0; i < m_queueImage.QueueSize(); i++)
-        {
-            QImage* image = nullptr;
-            if (m_queueImage.front_pop_noBlock(image))
-            {
-                delete image;
-            }
-        }
-        m_queueImage.clearQueue();
-    }
-    m_queueImage.setQueueCapacity(30);
+    clearQueueFrame();
+    m_queueFrame.setQueueCapacity(30);
+    m_queueFrame.setDefaultValue(nullptr);
 
     SPDLOG_DEBUG("开始初始化FFMPEG");
     /* 设置网络缓冲区大小和错误恢复选项 */
@@ -549,9 +557,9 @@ void DecodeVedio::threadDecodeUsingCPU()
 {
     /******** 初始化局部变量 ********/
     bool isEnd = false;
-    int ret = 0;
-    int retFrame = 0;
-    int retPacket = 0;
+    // int ret = 0;
+    // int retFrame = 0;
+    // int retPacket = 0;
     m_pauseDecode = false;
     m_decodeStatus = true;
     m_decodeState.store(DecodeState::DecodeRun);
@@ -664,48 +672,52 @@ void DecodeVedio::threadDecodeUsingCPU()
             }
             // SPDLOG_DEBUG("当前帧的pts:{}", m_pts.load());
 
-            /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
-            if(m_sws_ctx == nullptr)
-            {
-                /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
-                m_sws_ctx = sws_getCachedContext(m_sws_ctx, 
-                                                m_pFrameSRC->width, m_pFrameSRC->height,    /* 原图像大小和格式 */
-                                                m_pCodecContext->pix_fmt,                   /* 输入图像的像素格式 */
-                                                m_srcSize.width(), m_srcSize.height(),      /* 目标图像的大小 */
-                                                AV_PIX_FMT_RGBA,                            /* 目标图像的格式 */
-                                                SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
-                                                nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
-                                                nullptr,                                    /* 输出图像的滤波器信息,不需要传NULL */
-                                                nullptr);                                   /* 特定缩放算法需要的参数,不需要传NULL */
-                if(m_sws_ctx == nullptr)
-                {
-                    SPDLOG_ERROR("创建SwsContext错误...");
-                    goto label_ThreadDecodeExit;
-                }
-                SPDLOG_INFO("创建SwsContext成功...");
-            }
-            /* 转换成RGBA格式 */
-            // uint8_t* data[1] = { m_buffer };
-            int lines[4];
-            /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
-            av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameSRC->width);
-
-            sws_scale(  m_sws_ctx,              /* 缩放的上下文 */
-                        m_pFrameSRC->data,      /* 源图像数组 */
-                        m_pFrameSRC->linesize,  /* 包含源图像每个平面步幅的数组 */
-                        0,                      /* 开始位置 */
-                        m_pFrameSRC->height,    /* 行数 */
-                        &m_buffer,                   /* 目标图像数组 */
-                        lines); /* 目标图像行数 */
-            if(m_buffer != nullptr)
-            {
-                /* 将数据拷贝到QImage中 */
-                auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
-                /* 如果队列满,线程会阻塞在这里 */
-                m_queueImage.push(image);
-                // av_frame_unref(m_pFrameRGB);
-                // SPDLOG_DEBUG("一帧视频入队");
-            }
+            // /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
+            // if(m_sws_ctx == nullptr)
+            // {
+            //     /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
+            //     m_sws_ctx = sws_getCachedContext(m_sws_ctx, 
+            //                                     m_pFrameSRC->width, m_pFrameSRC->height,    /* 原图像大小和格式 */
+            //                                     m_pCodecContext->pix_fmt,                   /* 输入图像的像素格式 */
+            //                                     m_srcSize.width(), m_srcSize.height(),      /* 目标图像的大小 */
+            //                                     AV_PIX_FMT_RGBA,                            /* 目标图像的格式 */
+            //                                     SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
+            //                                     nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
+            //                                     nullptr,                                    /* 输出图像的滤波器信息,不需要传NULL */
+            //                                     nullptr);                                   /* 特定缩放算法需要的参数,不需要传NULL */
+            //     if(m_sws_ctx == nullptr)
+            //     {
+            //         SPDLOG_ERROR("创建SwsContext错误...");
+            //         goto label_ThreadDecodeExit;
+            //     }
+            //     SPDLOG_INFO("创建SwsContext成功...");
+            // }
+            // /* 转换成RGBA格式 */
+            // // uint8_t* data[1] = { m_buffer };
+            // int lines[4];
+            // /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
+            // av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameSRC->width);
+
+            // sws_scale(  m_sws_ctx,              /* 转换上下文 */
+            //             m_pFrameSRC->data,      /* 源图像数据指针数组 */
+            //             m_pFrameSRC->linesize,  /* 源图像每行字节数数组 */
+            //             0,                      /* 源图像起始行(通常为0) */
+            //             m_pFrameSRC->height,    /* 处理的行数(通常为图像高度) */
+            //             &m_buffer,              /* 目标图像数据指针数组(packed格式只用[0]) */
+            //             lines);                 /* 目标图像每行字节数数组 */
+            // if(m_buffer != nullptr)
+            // {
+            //     /* 将数据拷贝到QImage中 */
+            //     auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
+            //     /* 如果队列满,线程会阻塞在这里 */
+            //     m_queueRGBA.push(image);
+            //     // av_frame_unref(m_pFrameRGB);
+            //     // SPDLOG_DEBUG("一帧视频入队");
+            // }
+
+            /* 对生成的图像进行格式转换,并添加到环形队列中 */
+            convertImageFormatAndPushToQueue(m_pFrameSRC);
+
             av_frame_unref(m_pFrameSRC);
             /* 如果在跳转过程中,直接退出,防止一个packet中有多个视频帧,再次阻塞在上面 */
             if(m_isSeek)
@@ -744,7 +756,7 @@ void DecodeVedio::exitThread()
     m_decodeState.store(DecodeState::DecodeRun);
     m_pauseDecode = false;
     /* 先退出可能阻塞住的解码线程 */
-    m_queueImage.exit();
+    m_queueFrame.exit();
 }
 
 /**
@@ -755,9 +767,9 @@ void DecodeVedio::threadDecodeUsingGPU()
 {
     /******** 初始化局部变量 ********/
     bool isEnd = false;
-    int ret = 0;
-    int retFrame = 0;
-    int retPacket = 0;
+    // int ret = 0;
+    // int retFrame = 0;
+    // int retPacket = 0;
     m_pauseDecode = false;
     m_decodeStatus = true;
     m_decodeState.store(DecodeState::DecodeRun);
@@ -879,47 +891,49 @@ void DecodeVedio::threadDecodeUsingGPU()
             // SPDLOG_DEBUG("当前帧的pts:{}", m_pts.load());
 
             /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
-            if(m_sws_ctx == nullptr)
-            {
-                /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
-                m_sws_ctx = sws_getCachedContext(m_sws_ctx, 
-                                                m_pFrameHW->width, m_pFrameHW->height,    /* 原图像大小和格式 */
-                                                m_pCodecContext->pix_fmt,                   /* 输入图像的像素格式 */
-                                                m_srcSize.width(), m_srcSize.height(),      /* 目标图像的大小 */
-                                                AV_PIX_FMT_RGBA,                            /* 目标图像的格式 */
-                                                SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
-                                                nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
-                                                nullptr,                                    /* 输出图像的滤波器信息,不需要传NULL */
-                                                nullptr);                                   /* 特定缩放算法需要的参数,不需要传NULL */
-                if(m_sws_ctx == nullptr)
-                {
-                    SPDLOG_ERROR("创建SwsContext错误...");
-                    goto label_ThreadDecodeExit;
-                }
-                SPDLOG_INFO("创建SwsContext成功...");
-            }
-            /* 转换成RGBA格式 */
-            // uint8_t* data[1] = { m_buffer };
-            int lines[4];
-            /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
-            av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameHW->width);
-
-            sws_scale(  m_sws_ctx,              /* 缩放的上下文 */
-                        m_pFrameHW->data,      /* 源图像数组 */
-                        m_pFrameHW->linesize,  /* 包含源图像每个平面步幅的数组 */
-                        0,                      /* 开始位置 */
-                        m_pFrameHW->height,    /* 行数 */
-                        &m_buffer,                   /* 目标图像数组 */
-                        lines); /* 目标图像行数 */
-            if(m_buffer != nullptr)
-            {
-                /* 将数据拷贝到QImage中 */
-                auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
-                /* 如果队列满,线程会阻塞在这里 */
-                m_queueImage.push(image);
-                // av_frame_unref(m_pFrameRGB);
-                // SPDLOG_DEBUG("一帧视频入队");
-            }
+            // if(m_sws_ctx == nullptr)
+            // {
+            //     /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
+            //     m_sws_ctx = sws_getCachedContext(m_sws_ctx, 
+            //                                     m_pFrameHW->width, m_pFrameHW->height,    /* 原图像大小和格式 */
+            //                                     m_pCodecContext->pix_fmt,                   /* 输入图像的像素格式 */
+            //                                     m_srcSize.width(), m_srcSize.height(),      /* 目标图像的大小 */
+            //                                     AV_PIX_FMT_RGBA,                            /* 目标图像的格式 */
+            //                                     SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
+            //                                     nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
+            //                                     nullptr,                                    /* 输出图像的滤波器信息,不需要传NULL */
+            //                                     nullptr);                                   /* 特定缩放算法需要的参数,不需要传NULL */
+            //     if(m_sws_ctx == nullptr)
+            //     {
+            //         SPDLOG_ERROR("创建SwsContext错误...");
+            //         goto label_ThreadDecodeExit;
+            //     }
+            //     SPDLOG_INFO("创建SwsContext成功...");
+            // }
+            // /* 转换成RGBA格式 */
+            // // uint8_t* data[1] = { m_buffer };
+            // int lines[4];
+            // /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
+            // av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, m_pFrameHW->width);
+
+            // sws_scale(  m_sws_ctx,              /* 缩放的上下文 */
+            //             m_pFrameHW->data,      /* 源图像数组 */
+            //             m_pFrameHW->linesize,  /* 包含源图像每个平面步幅的数组 */
+            //             0,                      /* 开始位置 */
+            //             m_pFrameHW->height,    /* 行数 */
+            //             &m_buffer,                   /* 目标图像数组 */
+            //             lines); /* 目标图像行数 */
+            // if(m_buffer != nullptr)
+            // {
+            //     /* 将数据拷贝到QImage中 */
+            //     auto image = new QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
+            //     /* 如果队列满,线程会阻塞在这里 */
+            //     m_queueRGBA.push(image);
+            //     // av_frame_unref(m_pFrameRGB);
+            //     // SPDLOG_DEBUG("一帧视频入队");
+            // }
+            convertImageFormatAndPushToQueue(m_pFrameHW);
+
             av_frame_unref(m_pFrameSRC);
             /* 如果在跳转过程中,直接退出,防止一个packet中有多个视频帧,再次阻塞在上面 */
             if(m_isSeek)
@@ -947,6 +961,105 @@ label_ThreadDecodeExit:
     m_threadRuning = false;
 }
 
+
+/**
+ * @brief 对生成的图像进行格式转换,并添加到环形队列中
+ *        1、YUVJ420P格式是全色YUV格式,范围是[0-255],而YUV420P格式是限色YUV格式,范围是[16-235]。
+ *        2、判断视频格式,如果是RGB类型的转换成RGBA格式,如果是yuv类型的,转换成YUVJ420P格式
+ * @param pFrame 
+ */
+void DecodeVedio::convertImageFormatAndPushToQueue(AVFrame* pFrame)
+{
+    /* 判断视频帧格式 */
+    if((pFrame->format == AV_PIX_FMT_YUV420P) || (pFrame->format == AV_PIX_FMT_YUVJ420P))
+    {
+        /* 是yuv420p格式, */
+        Frame_YUV420P* frame = new Frame_YUV420P();
+        convertToYUV420P(pFrame, frame->frameData);
+        m_queueFrame.push(frame);
+        return;
+    }
+
+    /* 判断视频格式,如果是RGB类型的转换成RGBA格式,如果是yuv类型的,转换成YUVJ420P格式 */
+    /* 不是420p,转换成RGBA格式 */
+    Frame_RGBA8888* frame = new Frame_RGBA8888();
+    convertToRGBA(pFrame, frame->frameData);
+    if(frame->frameData == nullptr)
+    {
+        SPDLOG_ERROR("转换成RGBA格式失败,可能是解码器不支持该格式: {}", av_get_pix_fmt_name((AVPixelFormat)pFrame->format));
+        delete frame;  /* 释放空间 */
+        return;
+    }
+    /* 如果队列满,线程会阻塞在这里 */
+    m_queueFrame.push(frame);
+}
+
+/* 将yuv420p的uint8_t数组缓存转换成Image_YUV420P结构体 */
+void DecodeVedio::convertToYUV420P(AVFrame* pFrame, Image_YUV420P* image)
+{
+    if(pFrame == nullptr || image == nullptr)
+    {
+        return;
+    }
+    if(pFrame->data[0] == nullptr || pFrame->data[1] == nullptr || pFrame->data[2] == nullptr)
+    {
+        return;
+    }
+
+    image->width = pFrame->width;
+    image->height = pFrame->height;
+    uint32_t ySzie = pFrame->width * pFrame->height;  /* Y分量的大小 */
+    image->yData.append(reinterpret_cast<const char*>(pFrame->data[0]), ySzie);
+    image->uData.append(reinterpret_cast<const char*>(pFrame->data[1]), ySzie / 4);
+    image->vData.append(reinterpret_cast<const char*>(pFrame->data[2]), ySzie / 4);
+}
+
+/* 转换成RGBA格式 */
+void DecodeVedio::convertToRGBA(AVFrame* pFrame, QImage* image)
+{
+    /* 转换解码后的帧格式,转换成RGBA格式,Qt可以识别 */
+    if(m_sws_ctx == nullptr)
+    {
+        /* 选择在这里创建,为了兼容硬件解码,硬件解码出来的格式可能和解码器传出的不一样 */
+        m_sws_ctx = sws_getCachedContext(m_sws_ctx, 
+                                        pFrame->width, pFrame->height,              /* 原图像大小和格式 */
+                                        m_pCodecContext->pix_fmt,                   /* 输入图像的像素格式 */
+                                        m_srcSize.width(), m_srcSize.height(),      /* 目标图像的大小 */
+                                        AV_PIX_FMT_RGBA,                            /* 目标图像的格式 */
+                                        SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
+                                        nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
+                                        nullptr,                                    /* 输出图像的滤波器信息,不需要传NULL */
+                                        nullptr);                                   /* 特定缩放算法需要的参数,不需要传NULL */
+        if(m_sws_ctx == nullptr)
+        {
+            SPDLOG_ERROR("创建SwsContext错误...");
+            /* 释放空间 */
+            av_packet_free(&m_packet);
+            m_decodeState.store(DecodeState::DecodeExit);
+            m_threadRuning = false;
+        }
+        SPDLOG_INFO("创建SwsContext成功...");
+    }
+    /* 转换成RGBA格式 */
+    // uint8_t* data[1] = { m_buffer };
+    int lines[4];
+    /* 使用像素格式pix_fmt和宽度填充图像的平面线条的大小(一行大小?) */
+    av_image_fill_linesizes(lines, AV_PIX_FMT_RGBA, pFrame->width);
+    sws_scale(  m_sws_ctx,              /* 转换上下文 */
+                pFrame->data,           /* 源图像数据指针数组 */
+                pFrame->linesize,       /* 源图像每行字节数数组 */
+                0,                      /* 源图像起始行(通常为0) */
+                pFrame->height,         /* 处理的行数(通常为图像高度) */
+                &m_buffer,              /* 目标图像数据指针数组(packed格式只用[0]) */
+                lines);                 /* 目标图像每行字节数数组 */
+    if(m_buffer != nullptr)
+    {
+        /* 将数据拷贝到QImage中 */
+        *image = QImage(m_buffer, m_srcSize.width(), m_srcSize.height(), QImage::Format_RGBA8888);
+    }
+    
+}
+
 /* 暂停解码,会阻塞到线程暂停为止 */
 void DecodeVedio::pauseDecode()
 {
@@ -963,19 +1076,8 @@ void DecodeVedio::pauseDecode()
     m_decodeState.store(DecodeState::DecodeRun);
     m_pauseDecode = true;
     /* 队列出队两张图片,防止解码线程阻塞到环形队列满上面 */
-    QImage* image = nullptr;
-    m_queueImage.front_pop_noBlock(image);
-    if(image != nullptr)
-    {
-        delete image;
-        image = nullptr;
-    }
-    m_queueImage.front_pop_noBlock(image);
-    if(image != nullptr)
-    {
-        delete image;
-        image = nullptr;
-    }
+    deleteOneImage();
+    deleteOneImage();
     /* 等待线程状态变为暂停为止 */
     while (m_decodeState.load() != DecodeState::DecodePause)
     {
@@ -1013,7 +1115,16 @@ void DecodeVedio::do_startDecodeVedio()
     m_threadRuning = true;
     m_pauseDecode = false;
     /* 进入解码,直到播放完成或者手动退出 */
-    threadDecodeUsingCPU();
+    if(m_supportHWDecoder)
+    {
+        SPDLOG_TRACE("使用硬件解码器进行解码...");
+        threadDecodeUsingGPU();
+    }else 
+    {
+        SPDLOG_INFO("使用CPU进行解码...");
+        threadDecodeUsingCPU();
+    }
+    
     SPDLOG_TRACE("Decode解码结束。");
 
 }
@@ -1060,15 +1171,8 @@ void DecodeVedio::freeAll()
         av_buffer_unref(&m_hw_device_ctx);
     }
 
-    for(int i = 0; i < m_queueImage.QueueSize(); i++)
-    {
-        QImage* image = nullptr;
-        if (m_queueImage.front_pop_noBlock(image))
-        {
-            delete image;
-        }
-    }
-    m_queueImage.clearQueue();
+    clearQueueFrame();
+    m_queueFrame.clearQueue();
 }
 
 

+ 22 - 7
demo/OpenGLWidgetTest/VideoPlayer/DecodeVedio.h → demo/VideoPlayerLibrary/VideoPlayer/DecodeVedio.h

@@ -9,12 +9,12 @@
 #include <QImage>
 
 #include "RingQueue/RingQueue.hpp"
+#include "FrameFormat.h"
 
-// #include "threadcontroller.h"
 
 extern "C"
 {
-// #include <libavcodec/avcodec.h>
+#include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
 // #include <libswscale/swscale.h>
 // #include <libavutil/imgutils.h>
@@ -22,6 +22,8 @@ extern "C"
 
 
 
+
+
 /**
  * 使用方式:
  *      1. 初始化FFmpeg:initFFmpeg()
@@ -69,9 +71,9 @@ public:
     
 
     /* 获取一帧图像 */
-    QImage* getOneImage();
+    FrameBase* getOneImage();
     /* 获取一帧图像,直到有图像为止,可以设置超时时间 */
-    QImage* getOneImageUntilHave(int timeOut = -1);
+    FrameBase* getOneImageUntilHave(int timeOut = -1);
 
     /* 获取帧数 */
     int getFPS() const { return m_fps; }
@@ -90,7 +92,14 @@ signals:
     void signal_oneImage();                         /* 一帧图像信号 */
     void signal_playCompleted();                    /* 播放完成信号 */
     void signal_startDecode();                      /* 开始解码信号 */
+    
 private:
+    /* 清空环形队列 */
+    void clearQueueFrame();
+    /* 删除一帧图片 */
+    void deleteOneImage();
+
+    /*----------------------- 解码相关的函数 ------------------------*/
     /* 查找硬件解码器 */
     void findHWDecoder();
     /* 初始化硬件解码器 */
@@ -99,9 +108,15 @@ private:
     bool copyDataFromGPU(AVFrame* pFrameHW, AVFrame* pFrameSRC);
                 
     /* 软解码线程 */       
-    void threadDecodeUsingCPU();     
+    void threadDecodeUsingCPU();
     /* 硬件解码线程 */
     void threadDecodeUsingGPU();
+    /* 对生成的图像进行格式转换,并添加到环形队列中 */
+    inline void convertImageFormatAndPushToQueue(AVFrame* pFrame);
+    /* 将yuv420p的uint8_t数组缓存转换成Image_YUV420P结构体 */
+    inline void convertToYUV420P(AVFrame* pFrame, Image_YUV420P* image);
+    /* 转换成RGBA格式 */
+    inline void convertToRGBA(AVFrame* pFrame, QImage* image);
     /* 退出线程 */      
     void exitThread();
     /* 暂停解码 */
@@ -157,8 +172,8 @@ private:
     qint64 m_currentFrame = 0;                      /* 当前已播放的帧数 */
     QString m_decoderName;                          /* 解码器名称 */
 
-    RingQueue<QImage*> m_queueImage;                /* 环形队列,存储生成的图像 */
-    
+    // EFrameFormat m_frameFormat = EFrameFormat::Frame_YUV420P; /* 视频帧格式,默认是YUV420P */
+    RingQueue<FrameBase*> m_queueFrame;             /* 环形队列,存储生成的图像 */
 };
 
 

+ 61 - 0
demo/VideoPlayerLibrary/VideoPlayer/FrameFormat.cpp

@@ -0,0 +1,61 @@
+#include "FrameFormat.h"
+
+#include "OpenGLWidgetAPI.h"
+
+#include "spdlog/spdlog.h"
+
+
+
+Frame_YUV420P::Frame_YUV420P() : FrameBase(EFrameFormat::Frame_YUV420P)
+{
+    frameData = new Image_YUV420P();
+}
+
+Frame_YUV420P::~Frame_YUV420P()
+{
+    if(frameData != nullptr)
+    {
+        delete frameData;
+        frameData = nullptr;
+    }
+}
+
+Frame_RGBA8888::Frame_RGBA8888() : FrameBase(EFrameFormat::Frame_RGBA8888)
+{
+    frameData = new QImage();
+}
+
+Frame_RGBA8888::~Frame_RGBA8888()
+{
+    if(frameData != nullptr)
+    {
+        delete frameData;
+        frameData = nullptr;
+    }
+}
+
+/* 删除一个图片 */
+bool deleteOneFrame(FrameBase* frame)
+{
+    if(frame == nullptr)
+    {
+        SPDLOG_DEBUG("传入的帧指针为空,无法删除");
+        return false;
+    }
+    if(frame->frameFormat() == EFrameFormat::Frame_YUV420P)
+    {
+        Frame_YUV420P* yuvFrame = static_cast<Frame_YUV420P*>(frame);
+        delete yuvFrame;
+    }else if(frame->frameFormat() == EFrameFormat::Frame_RGBA8888)
+    {
+        Frame_RGBA8888* rgbaFrame = static_cast<Frame_RGBA8888*>(frame);
+        delete rgbaFrame;
+    } else {
+        SPDLOG_WARN("未知的帧格式,无法删除。");
+        return false;
+    }
+
+    return true;
+}
+
+

+ 61 - 0
demo/VideoPlayerLibrary/VideoPlayer/FrameFormat.h

@@ -0,0 +1,61 @@
+#ifndef _FRAMEFORMAT_H_
+#define _FRAMEFORMAT_H_
+
+#include <QImage>
+
+
+struct Image_YUV420P;
+
+/**
+ * @brief 支持的帧格式枚举
+ * 
+ */
+enum class EFrameFormat
+{
+    Frame_YUV420P = 0,  /* YUV420P格式 */
+    Frame_RGBA8888,     /* RGBA格式 */
+};
+
+/**
+ * @brief 帧数据基础类
+ * 
+ */
+struct FrameBase
+{
+protected:
+    EFrameFormat format; /* 帧格式 */
+
+public:
+    FrameBase(EFrameFormat fmt = EFrameFormat::Frame_YUV420P) : format(fmt) { }
+    virtual ~FrameBase() { } /* 虚析构函数,确保派生类的析构函数被调用 */
+
+    EFrameFormat frameFormat() const { return format; } /* 获取帧格式 */
+};
+
+/**
+ * @brief YUV420P格式的帧数据结构
+ * 
+ */
+struct Frame_YUV420P : public FrameBase
+{
+    Image_YUV420P* frameData = nullptr;     /* YUV420P格式的图像数据 */
+
+    Frame_YUV420P();
+    ~Frame_YUV420P() override;
+};
+
+struct Frame_RGBA8888 : public FrameBase
+{
+    QImage* frameData = nullptr;            /* RGBA格式的图像数据 */
+
+    Frame_RGBA8888();
+    ~Frame_RGBA8888() override;
+};
+
+
+/* 删除一个图片 */
+bool deleteOneFrame(FrameBase* frame);
+
+
+
+#endif /* _FRAMEFORMAT_H_ */

+ 3 - 0
demo/VideoPlayerLibrary/VideoPlayer/IVideoPlayer.cpp

@@ -0,0 +1,3 @@
+#include "IVideoPlayer.h"
+
+

+ 54 - 0
demo/VideoPlayerLibrary/VideoPlayer/IVideoPlayer.h

@@ -0,0 +1,54 @@
+#ifndef _IVIDEOPLAYER_H_
+#define _IVIDEOPLAYER_H_
+
+#include <QObject>
+
+class IVideoPlayer : public QObject
+{
+    Q_OBJECT
+public:
+
+    explicit IVideoPlayer() {}
+    virtual ~IVideoPlayer() {}
+
+    /* 打开播放视频 */
+    virtual bool openPlayVedio(const QString& fileName) = 0;
+    /* 播放视频 */
+    virtual bool play() = 0;
+    /* 暂停播放 */
+    virtual void pause() = 0;
+    /* 停止播放 */
+    virtual void stop() = 0;
+    /* 后退,单位ms */
+    virtual void backward(qint64 ms) = 0;
+    /* 前进,单位ms */
+    virtual void forward(qint64 ms) = 0;
+
+    /* 获取播放状态 */
+    virtual bool getPlayStatus() = 0;
+    /* 获取视频时长 */
+    virtual qint64 getDuration() = 0;
+    /* 获取当前播放位置 */
+    virtual qint64 getCurrentPos() = 0;
+    /* 设置当前播放位置 */
+    virtual void setCurrentPos(qint64 pos) = 0;
+
+    /* 设置播放视频窗口的大小 */
+    virtual void setPlayWidgetSize(int width,int height) = 0;
+    /* 设置播放窗口 */
+    virtual void setWidgetParent(QWidget* widget, bool isFull = false) = 0;
+    /* 获取播放窗口的指针 */
+    virtual QWidget* getPlayWidget() const = 0;
+    /* 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧 */
+    virtual void setPreviewImage(int num = 2) = 0;
+    /* 设置播放速度 */
+    virtual void setPlaySpeed(float speed) = 0;
+
+signals:
+    void signal_playCompleted();                    /* 播放完成信号 */
+    // void signal_refreshImage();                     /* 刷新图片信号 */
+
+};
+
+
+#endif /* _IVIDEOPLAYER_H_ */

+ 129 - 77
demo/OpenGLWidgetTest/VideoPlayer/VideoPlayer.cpp → demo/VideoPlayerLibrary/VideoPlayer/VideoPlayer.cpp

@@ -7,12 +7,13 @@
 #include <QEventLoop>
 #include <QVBoxLayout>
 
+#include "FrameFormat.h"
 #include "OpenGLWidgetAPI.h"
 
 #include "spdlog/spdlog.h"
 
 
-VideoPlayer::VideoPlayer(QObject *parent) : QObject(parent)
+VideoPlayer::VideoPlayer()
 {
     // /* 初始化解码线程 */
     // m_threadDecode = new QThread(this);
@@ -104,13 +105,13 @@ bool VideoPlayer::openPlayVedio(const QString& fileName)
         return false;
     }
     m_fileName = fileName;
-    m_isOpenFile = true;
     m_isLocalFile = isLocalFile(fileName);
 
     if(!m_decodeVedio->openVedio(fileName))
     {
         return false;
     }
+    m_isOpenFile = true;
     /* 获取原始视频信息 */
     m_srcWidth = m_decodeVedio->getSrcVideoSize().width();
     m_srcHeight = m_decodeVedio->getSrcVideoSize().height();
@@ -211,11 +212,11 @@ void VideoPlayer::stop()
     m_isOpenFile = false;
     /* 绘制黑帧 */
     SPDLOG_DEBUG("绘制黑帧");
-    m_image = new QImage(m_nowWidth, m_nowHeight, QImage::Format_RGB32);
-    m_image->fill(Qt::black);
+    QImage image(m_nowWidth, m_nowHeight, QImage::Format_RGBA8888);
+    image.fill(Qt::black);
     // update();
     
-    refreshRGBAImage(m_playWidget, *m_image);
+    refreshRGBAImage(m_playWidget, image);
 }
 
 /* 后退,单位ms */
@@ -384,14 +385,14 @@ void VideoPlayer::setPlayWidgetSize(int width,int height)
  *  @arg true:独占widget,并设置一个layout,会随着传入的widget大小变化
  *  @arg false:不独占
  */
-void VideoPlayer::setWidgetParent(QWidget* widget, bool flag)
+void VideoPlayer::setWidgetParent(QWidget* widget, bool isFull)
 {
     if(widget == nullptr)
     {
         SPDLOG_WARN("传入的widget为空");
         return;
     }
-    if(flag)
+    if(isFull)
     {
         /* 设置布局 */
         QVBoxLayout* layout = new QVBoxLayout(widget);
@@ -429,14 +430,30 @@ void VideoPlayer::setPreviewImage(int num)
 void VideoPlayer::setFPS(int fps)
 {
     m_fps = fps;
-    if(m_decodeVedio != nullptr)
+    // if(m_decodeVedio != nullptr)
+    // {
+    //     m_decodeVedio->setFPS(fps);
+    // }
+    if(m_timerRefreshUI.isActive())
     {
-        m_decodeVedio->setFPS(fps);
+        m_timerRefreshUI.stop();
+        m_interval = qRound64(1000.0 / m_fps);
+        m_timerRefreshUI.start(m_interval);
+    }
+}
+
+/* 设置播放速度 */
+void VideoPlayer::setPlaySpeed(float speed)
+{
+    int fps = m_fps * speed;
+    if(fps <= 0)
+    {
+        fps = m_fps;  // 最小帧率为1
     }
     if(m_timerRefreshUI.isActive())
     {
         m_timerRefreshUI.stop();
-        m_interval = qRound64(1000.0 / m_fps);
+        m_interval = qRound64(1000.0 / fps);
         m_timerRefreshUI.start(m_interval);
     }
 }
@@ -480,24 +497,63 @@ void VideoPlayer::refreshOneUIUntilHave()
     if(m_decodeVedio != nullptr)
     {
         // SPDLOG_DEBUG("取出一帧图片...");
-        /* 删除上一帧图片 */
-        if(m_image != nullptr)
+        // /* 删除上一帧图片 */
+        // if(m_image != nullptr)
+        // {
+        //     delete m_image;
+        //     m_image = nullptr;
+        // }
+        // /* 如果没有图片,这个函数会阻塞 */
+        // m_image = m_decodeVedio->getOneImageUntilHave();
+        
+        // if(m_image)
+        // {
+        //     if(m_image->isNull())
+        //     {
+        //         SPDLOG_WARN("取出的图片为空...");
+        //         return;
+        //     }
+        //     // SPDLOG_DEBUG("绘制画面...");
+        //     refreshRGBAImage(m_playWidget, *m_image);
+        // }
+        refreshFrame(m_decodeVedio->getOneImageUntilHave());
+    }
+}
+
+/* 刷新一帧 */
+void VideoPlayer::refreshFrame(FrameBase* frame)
+{
+    if(frame == nullptr)
+    {
+        SPDLOG_WARN("刷新一帧,传入的帧为空");
+        return;
+    }
+    /* 删除上一帧图片 */
+    deleteOneFrame(m_image);
+    m_image = frame;
+    frame = nullptr;  // 防止外部继续使用这个帧
+
+    if(m_image->frameFormat() == EFrameFormat::Frame_RGBA8888)
+    {
+        // SPDLOG_INFO("刷新一帧,RGBA8888格式的图片");
+        auto rgbaImage = static_cast<Frame_RGBA8888*>(m_image);
+        if(rgbaImage != nullptr && rgbaImage->frameData != nullptr)
         {
-            delete m_image;
-            m_image = nullptr;
+            refreshRGBAImage(m_playWidget, *rgbaImage->frameData);
+        }else {
+            SPDLOG_WARN("刷新一帧,RGBA图片为空");
         }
-        /* 如果没有图片,这个函数会阻塞 */
-        m_image = m_decodeVedio->getOneImageUntilHave();
-        
-        if(m_image)
+    }
+    else if(m_image->frameFormat() == EFrameFormat::Frame_YUV420P)
+    {
+        // SPDLOG_INFO("刷新一帧,YUV420P格式的图片");
+        /* YUV420P格式的图片 */
+        Frame_YUV420P* yuvImage = static_cast<Frame_YUV420P*>(m_image);
+        if(yuvImage != nullptr && yuvImage->frameData != nullptr)
         {
-            if(m_image->isNull())
-            {
-                SPDLOG_WARN("取出的图片为空...");
-                return;
-            }
-            // SPDLOG_DEBUG("绘制画面...");
-            refreshRGBAImage(m_playWidget, *m_image);
+            refreshYUV420Image(m_playWidget, *yuvImage->frameData);
+        }else {
+            SPDLOG_WARN("刷新一帧,YUV420P图片为空");
         }
     }
 }
@@ -524,25 +580,13 @@ void VideoPlayer::do_refreshUI()
     if(m_decodeVedio != nullptr)
     {
         // SPDLOG_DEBUG("取出一帧图片...");
-        /* 删除上一帧图片 */
-        if(m_image != nullptr)
+        auto image = m_decodeVedio->getOneImage();
+        if(image == nullptr)
         {
-            delete m_image;
-            m_image = nullptr;
-        }
-        m_image = m_decodeVedio->getOneImage();
-        
-        if(m_image)
-        {
-            if(m_image->isNull())
-            {
-                SPDLOG_WARN("取出的图片为空...");
-                return;
-            }
-            // SPDLOG_DEBUG("绘制画面...");
-            refreshRGBAImage(m_playWidget, *m_image);
+            SPDLOG_DEBUG("没有取到图片,可能是解码器没有解码完毕,或者视频播放完毕");
+            return;
         }
-        // m_decodeVedio->wakeUpCondQueueNoEmpty();
+        refreshFrame(image);
     }
 }
 
@@ -560,24 +604,25 @@ void VideoPlayer::do_refreshSamImage()
         if(m_decodeVedio != nullptr)
         {
             // SPDLOG_DEBUG("取出一帧图片...");
-            /* 删除上一帧图片 */
-            if(m_image != nullptr)
-            {
-                delete m_image;
-                m_image = nullptr;
-            }
-            /* 等待图片,最多等待50ms */
-            m_image = m_decodeVedio->getOneImageUntilHave(100);
-            if(m_image)
-            {
-                if(m_image->isNull())
-                {
-                    SPDLOG_WARN("取出的图片为空...");
-                    return;
-                }
-                SPDLOG_DEBUG("绘制预览画面。");
-                refreshRGBAImage(m_playWidget, *m_image);
-            }
+            // /* 删除上一帧图片 */
+            // if(m_image != nullptr)
+            // {
+            //     delete m_image;
+            //     m_image = nullptr;
+            // }
+            // /* 等待图片,最多等待50ms */
+            // m_image = m_decodeVedio->getOneImageUntilHave(100);
+            // if(m_image)
+            // {
+            //     if(m_image->isNull())
+            //     {
+            //         SPDLOG_WARN("取出的图片为空...");
+            //         return;
+            //     }
+            //     SPDLOG_DEBUG("绘制预览画面。");
+            //     refreshRGBAImage(m_playWidget, *m_image);
+            // }
+            refreshFrame(m_decodeVedio->getOneImageUntilHave(100));
         }
     }
 }
@@ -592,27 +637,33 @@ void VideoPlayer::do_playCompleted()
     {
         if(m_decodeVedio != nullptr)
         {
-            QImage* image = nullptr;
-            image = m_decodeVedio->getOneImage();
+            // QImage* image = nullptr;
+            // image = m_decodeVedio->getOneImage();
+            // if(image == nullptr)
+            // {
+            //     break;
+            // }
+            // /* 删除上一帧图片 */
+            // if(m_image != nullptr)
+            // {
+            //     delete m_image;
+            //     m_image = nullptr;
+            // }
+            // m_image = image;
+            
+            // if(m_image->isNull())
+            // {
+            //     SPDLOG_WARN("取出的图片为空...");
+            //     return;
+            // }
+            // // SPDLOG_DEBUG("绘制画面...");
+            // refreshRGBAImage(m_playWidget, *m_image);
+            auto image = m_decodeVedio->getOneImage();
             if(image == nullptr)
             {
                 break;
             }
-            /* 删除上一帧图片 */
-            if(m_image != nullptr)
-            {
-                delete m_image;
-                m_image = nullptr;
-            }
-            m_image = image;
-            
-            if(m_image->isNull())
-            {
-                SPDLOG_WARN("取出的图片为空...");
-                return;
-            }
-            // SPDLOG_DEBUG("绘制画面...");
-            refreshRGBAImage(m_playWidget, *m_image);
+            refreshFrame(image);
         }
     }
     m_playStatus = false;
@@ -621,6 +672,7 @@ void VideoPlayer::do_playCompleted()
     //     /* 播放完成的回调函数 */
     //     m_funcPlayCB(this, 2, nullptr, 0, m_context);
     // }
+    emit signal_playCompleted();  // 播放完成信号
 }
 
 /* 判断是否是本地文件 */

+ 47 - 28
demo/OpenGLWidgetTest/VideoPlayer/VideoPlayer.h → demo/VideoPlayerLibrary/VideoPlayer/VideoPlayer.h

@@ -1,7 +1,9 @@
 #ifndef VideoPlayer_H
 #define VideoPlayer_H
 
-#include <QObject>
+#include "IVideoPlayer.h"
+#include "FrameFormat.h"
+// #include <QObject>
 #include <QThread>
 #include <QTimer>
 #include <QSemaphore>
@@ -9,41 +11,58 @@
 class DecodeVedio;
 class QWidget;
 
-class VideoPlayer : public QObject
+class VideoPlayer : public IVideoPlayer
 {
     Q_OBJECT
 public:
-    explicit VideoPlayer(QObject *parent = nullptr);
-    ~VideoPlayer();
-
-    bool openPlayVedio(const QString& fileName);    /* 打开播放视频 */
-    bool play();                                    /* 播放视频 */
-    void pause();                                   /* 暂停播放 */
-    void stop();                                    /* 停止播放 */
-    
-    void backward(qint64 ms);                       /* 后退,单位ms */
-    void forward(qint64 ms);                        /* 前进,单位ms */
-    bool getPlayStatus() { return m_playStatus; }   /* 获取播放状态 */
-    qint64 getDuration();                           /* 获取视频时长 */
-    qint64 getCurrentPos();                         /* 获取当前播放位置 */
-    void setCurrentPos(qint64 pos);                 /* 设置当前播放位置 */
-
-    void setPlayWidgetSize(int width,int height);   /* 设置播放视频窗口的大小 */
-    void setWidgetParent(QWidget* widget, bool flag = false); /* 设置播放窗口 */
-    void setPreviewImage(int num = 2);              /* 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧 */
-    void setFPS(int fps);                            /* 设置帧率 */
+    explicit VideoPlayer();
+    ~VideoPlayer() override;
+
+    /* 打开播放视频 */
+    bool openPlayVedio(const QString& fileName) override;
+    /* 播放视频 */
+    bool play() override;
+    /* 暂停播放 */
+    void pause() override;
+    /* 停止播放 */
+    void stop() override;
+    /* 后退,单位ms */
+    void backward(qint64 ms) override;
+    /* 前进,单位ms */
+    void forward(qint64 ms) override;
+
+    /* 获取播放状态 */
+    bool getPlayStatus() override { return m_playStatus; }
+    /* 获取视频时长 */
+    qint64 getDuration() override;
+    /* 获取当前播放位置 */
+    qint64 getCurrentPos() override;
+    /* 设置当前播放位置 */
+    void setCurrentPos(qint64 pos) override;
+
+    /* 设置播放视频窗口的大小 */
+    void setPlayWidgetSize(int width,int height) override;
+    /* 设置播放窗口 */
+    void setWidgetParent(QWidget* widget, bool isFull = false)  override;
+    /* 获取播放窗口的指针 */
+    QWidget* getPlayWidget() const override { return m_playWidget; }
+    /* 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧 */
+    void setPreviewImage(int num = 2) override;
+    /* 设置帧率,用于控制播放速度 */
+    void setFPS(int fps);
+    /* 设置播放速度 */
+    void setPlaySpeed(float speed) override;
 
     // void setPlayCallBack(std::function<Play_CallBack> playCallBack,void* context);  /* 设置播放回调函数 */
 signals:
-    void signal_playCompleted();                    /* 播放完成信号 */
+    // void signal_playCompleted();                    /* 播放完成信号 */
     void signal_refreshImage();                     /* 刷新图片信号 */
 
 protected:
-    // void paintEvent(QPaintEvent *event) override;
-    // void resizeEvent(QResizeEvent *event) override;
-    void refreshOneUIUntilHave();                   /* 刷新一张图片,直到有图片为止 */
-    /* 双击事件函数 */
-    // void mouseDoubleClickEvent(QMouseEvent *event) override;
+    /* 刷新一张图片,直到有图片为止 */
+    void refreshOneUIUntilHave();
+    /* 刷新一帧 */
+    inline void refreshFrame(FrameBase* frame);
 
 private slots:
     void do_refreshUI();                            /* 取出画面,刷新UI */
@@ -68,7 +87,7 @@ private:
 
     DecodeVedio* m_decodeVedio = nullptr;
     QThread* m_threadDecode = nullptr;              /* 解码器所在的线程 */
-    QImage* m_image = nullptr;                      /* 画面 */
+    FrameBase* m_image = nullptr;                   /* 一帧画面 */
     bool m_playStatus = false;                      /* 是否正在播放 */
     bool m_isOpenFile = false;                      /* 是否打开了视频文件,未打开视频文件也就是未初始化解码线程 */
     QSemaphore* m_semRefresh = nullptr;             /* 刷新信号量 */

+ 323 - 0
demo/VideoPlayerLibrary/VideoPlayerAPI/VideoPlayerAPI.cpp

@@ -0,0 +1,323 @@
+#include "VideoPlayerAPI.h"
+
+#include "spdlog/spdlog.h"
+
+
+/* 创建一个播放器 */
+Q_DECL_EXPORT QObject* createPlayer()
+{
+    return new VideoPlayer();
+}
+/* 销毁一个播放器 */
+Q_DECL_EXPORT void destroyPlayer(QObject* player)
+{
+    if(player != nullptr)
+    {
+        VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+        if(videoPlayer != nullptr)
+        {
+            delete videoPlayer;
+            videoPlayer = nullptr;
+            player = nullptr;  // 确保指针不再指向已删除的对象
+        }
+    } else
+    {
+        SPDLOG_WARN("destroyVideoPlayer: 传入的player为空指针");
+    }
+}
+
+/* 打开视频 */
+Q_DECL_EXPORT bool OpenVideo(QObject* player, const QString& fileName)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("OpenVideo: 传入的player为空指针");
+        return false;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("OpenVideo: 无法将player转换为VideoPlayer");
+        return false;
+    }
+
+    return videoPlayer->openPlayVedio(fileName);
+}
+
+/* 播放视频 */
+Q_DECL_EXPORT bool play(QObject* player)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("play: 传入的player为空指针");
+        return false;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("play: 无法将player转换为VideoPlayer");
+        return false;
+    }
+
+    return videoPlayer->play();
+}
+
+/* 暂停播放 */
+Q_DECL_EXPORT void pause(QObject* player)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("pause: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("pause: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->pause();
+}
+
+/* 停止播放 */
+Q_DECL_EXPORT void stop(QObject* player)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("stop: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("stop: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->stop();
+}
+
+/* 后退,单位ms */
+Q_DECL_EXPORT void backward(QObject* player, qint64 ms)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("backward: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("backward: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->backward(ms);
+}
+
+/* 前进,单位ms */
+Q_DECL_EXPORT void forward(QObject* player, qint64 ms)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("forward: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("forward: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->forward(ms);
+}
+
+/* 获取播放状态 */
+Q_DECL_EXPORT bool getPlayStatus(QObject* player)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("getPlayStatus: 传入的player为空指针");
+        return false;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("getPlayStatus: 无法将player转换为VideoPlayer");
+        return false;
+    }
+
+    return videoPlayer->getPlayStatus();
+}
+
+/* 获取视频时长 */
+Q_DECL_EXPORT qint64 getDuration(QObject* player)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("getDuration: 传入的player为空指针");
+        return -1;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("getDuration: 无法将player转换为VideoPlayer");
+        return -1;
+    }
+
+    return videoPlayer->getDuration();
+}
+
+/* 获取当前播放位置 */
+Q_DECL_EXPORT qint64 getCurrentPos(QObject* player)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("getCurrentPos: 传入的player为空指针");
+        return -1;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("getCurrentPos: 无法将player转换为VideoPlayer");
+        return -1;
+    }
+
+    return videoPlayer->getCurrentPos();
+}
+
+/* 设置当前播放位置 */
+Q_DECL_EXPORT void setCurrentPos(QObject* player, qint64 pos)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("setCurrentPos: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("setCurrentPos: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->setCurrentPos(pos);
+}
+
+/* 设置播放视频窗口的大小 */
+Q_DECL_EXPORT void setPlayWidgetSize(QObject* player, int width,int height)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("setPlayWidgetSize: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("setPlayWidgetSize: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->setPlayWidgetSize(width, height);
+}
+
+/* 设置播放窗口 */
+Q_DECL_EXPORT void setWidgetParent(QObject* player, QWidget* widget, bool isFull)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("setWidgetParent: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("setWidgetParent: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->setWidgetParent(widget, isFull);
+}
+
+/* 获取播放窗口的指针 */
+Q_DECL_EXPORT QWidget* getPlayWidget(QObject* player)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("getPlayWidget: 传入的player为空指针");
+        return nullptr;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("getPlayWidget: 无法将player转换为VideoPlayer");
+        return nullptr;
+    }
+
+    return videoPlayer->getPlayWidget();
+}
+
+
+/* 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧 */
+Q_DECL_EXPORT void setPreviewImage(QObject* player, int num)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("setPreviewImage: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("setPreviewImage: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->setPreviewImage(num);
+}
+
+/* 设置播放速度 */
+Q_DECL_EXPORT void setPlaySpeed(QObject* player, float speed)
+{
+    if(player == nullptr)
+    {
+        SPDLOG_ERROR("setPlaySpeed: 传入的player为空指针");
+        return;
+    }
+    VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+    if(videoPlayer == nullptr)
+    {
+        SPDLOG_ERROR("setPlaySpeed: 无法将player转换为VideoPlayer");
+        return;
+    }
+
+    videoPlayer->setPlaySpeed(speed);
+}
+
+
+/* 创建一个IVideoPlayer */
+Q_DECL_EXPORT IVideoPlayer* createVideoPlayer()
+{
+    return new VideoPlayer();
+}
+
+/* 销毁一个IVideoPlayer */
+Q_DECL_EXPORT void destroyVideoPlayer(IVideoPlayer* player)
+{
+    if(player != nullptr)
+    {
+        VideoPlayer* videoPlayer = qobject_cast<VideoPlayer*>(player);
+        if(videoPlayer != nullptr)
+        {
+            delete videoPlayer;
+            videoPlayer = nullptr;
+            player = nullptr;  // 确保指针不再指向已删除的对象
+        }
+    } else
+    {
+        SPDLOG_WARN("destroyVideoPlayer: 传入的player为空指针");
+    }
+}

+ 62 - 0
demo/VideoPlayerLibrary/VideoPlayerAPI/VideoPlayerAPI.h

@@ -0,0 +1,62 @@
+#ifndef _VIDEOPLAYERAPI_H_
+#define _VIDEOPLAYERAPI_H_
+
+#include "IVideoPlayer.h"
+#include "VideoPlayer.h"
+#include <QString>
+#include <qobject.h>
+
+class QWidget;
+
+extern "C" {
+
+/* 创建一个播放器 */
+Q_DECL_EXPORT QObject* createPlayer();
+/* 销毁一个播放器 */
+Q_DECL_EXPORT void destroyPlayer(QObject* player);
+
+/* 打开视频 */
+Q_DECL_EXPORT bool OpenVideo(QObject* player, const QString& fileName);
+/* 播放视频 */
+Q_DECL_EXPORT bool play(QObject* player);
+/* 暂停播放 */
+Q_DECL_EXPORT void pause(QObject* player);
+/* 停止播放 */
+Q_DECL_EXPORT void stop(QObject* player);
+/* 后退,单位ms */
+Q_DECL_EXPORT void backward(QObject* player, qint64 ms);
+/* 前进,单位ms */
+Q_DECL_EXPORT void forward(QObject* player, qint64 ms);
+
+/* 获取播放状态 */
+Q_DECL_EXPORT bool getPlayStatus(QObject* player);
+/* 获取视频时长 */
+Q_DECL_EXPORT qint64 getDuration(QObject* player);
+/* 获取当前播放位置 */
+Q_DECL_EXPORT qint64 getCurrentPos(QObject* player);
+/* 设置当前播放位置 */
+Q_DECL_EXPORT void setCurrentPos(QObject* player, qint64 pos);
+
+/* 设置播放视频窗口的大小 */
+Q_DECL_EXPORT void setPlayWidgetSize(QObject* player, int width,int height);
+/* 设置播放窗口 */
+Q_DECL_EXPORT void setWidgetParent(QObject* player, QWidget* widget, bool isFull);
+/* 获取播放窗口的指针 */
+Q_DECL_EXPORT QWidget* getPlayWidget(QObject* player);
+
+/* 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧 */
+Q_DECL_EXPORT void setPreviewImage(QObject* player, int num = 2);
+/* 设置播放速度 */
+Q_DECL_EXPORT void setPlaySpeed(QObject* player, float speed);
+
+
+/* 创建一个IVideoPlayer */
+Q_DECL_EXPORT IVideoPlayer* createVideoPlayer();
+/* 销毁一个IVideoPlayer */
+Q_DECL_EXPORT void destroyVideoPlayer(IVideoPlayer* player);
+
+};
+
+
+
+#endif /* _VIDEOPLAYERAPI_H_ */