美文网首页
QT如何获取VLC每一帧并渲染到Qwidget

QT如何获取VLC每一帧并渲染到Qwidget

作者: q2nAmor | 来源:发表于2021-01-14 13:11 被阅读0次

    1、依赖下载

    1.1 首先下载VLC依赖动态库和静态库,官网下载(VLC-3.0.4,64位),如需下载32位去 win32 目录下载即可。

    VLC官网下载

    http://download.videolan.org/pub/videolan/vlc/3.0.4/win64/

    另外我上传到了csdn:方便大家下载:

    https://download.csdn.net/download/u012534831/10776378

    1.2 下载完成后解压,拷贝出其中的 plugins 文件夹,libvlc.dll 文件,libvlccore.dll 文件,和sdk文件夹下的 include 和 lib 文件夹,总共5项。

    image

    1.3 然后将 plugins 文件夹和 libvlc.dll 和 libvlccore.dll 文件拷贝到exe的同级目录,同时将 include 和 lib 文件夹拷贝到 cpp 代码的同级目录。

    image

    2、链接配置

    2.1 打开VS2015,右键项目,打开属性页,选择C/C++ 标签,选择常规, 附加包含目录,添加一项 .\include

    include附加包含目录配置

    2.2 切换到链接器标签,选择常规,附加库目录,添加一项 .\lib

    lib附加库目录配置

    3、代码展示

    渲染思路: 将回调的每一帧转为 QImage,然后将 QImage 放入 list 末尾,然后使用槽函数通知 paintEvent 从 list 的头部取出一帧图像,使用 drawPixmap 方法进行绘制,绘制完成后从list头部移除渲染过的 image 图像。

    // 头文件 
    
    #pragma once
    #include <QtWidgets/QWidget>
    #include <QPaintEvent>
    
    class Vedio : public QWidget
    {
        Q_OBJECT
    
    public:
        Vedio(QWidget *parent = Q_NULLPTR);
        void updatePicture(const QImage &image);
        static Vedio *pThis;  //声明 pThis 对象方便我们在静态函数中调用成员函数
    protected:
        virtual void paintEvent(QPaintEvent *event);
    signals:
        void showImage();
    
    private:
        std::list<QImage> lists;
    };
    
    

    正常情况下我们播放 VLC 视频只需要调用 libvlc_media_player_set_hwnd(mp, (void *)this->winId()); 传入窗口句柄即可播放视频,但是当需要获取每一帧自己去渲染的时候,此方法就失效了,需要通过 libvlc_video_set_callbacks(mp, lock, unlock, display, ctx); 方法在 VLC 提供的三个回调方法中处理数据,手动去渲染每一帧,这三个回调方法为 lock,unlock 和 display 方法。

    // cpp文件
    
    #include "vedio.h"
    #include <include/vlc/vlc.h>  
    #pragma comment(lib, "libvlc.lib")
    #pragma comment(lib, "libvlccore.lib")
    #include <QPixmap>
    #include <QImage>
    #include <QPainter>
    #include <QMutex>
    using namespace std;
    
    // 定义输出视频的分辨率
    #define VIDEO_WIDTH   1920
    #define VIDEO_HEIGHT  1280
    
    
    struct context {
        QMutex mutex;
        uchar *pixels;
    };
    
    static void *lock(void *opaque, void **planes)
    {
        struct context *ctx = (context *)opaque;
        ctx->mutex.lock();
        *planes = ctx->pixels;
        return NULL;
    }
    
    static void unlock(void *opaque, void *picture, void *const *planes)
    {
           struct context *ctx = (context *)opaque;
           unsigned char *data = (unsigned char *)*planes; // planes即为帧数据
           QImage image(data, VIDEO_WIDTH, VIDEO_HEIGHT, QImage::Format_RGBA8888);// 指定生成的图片格式为 RGBA 4通道
           Vedio::pThis->updatePicture(image);  // 
           ctx->mutex.unlock();
    }
    
    static void display(void *opaque, void *picture)
    {
        (void)opaque;
    }
    Vedio* Vedio::pThis = nullptr;
    
    Vedio::Vedio(QWidget *parent)
        : QWidget(parent)
    {
        pThis = this;
        //
        connect(this, SIGNAL(showImage()), this, SLOT(update()));
    
        libvlc_instance_t * inst;
        libvlc_media_player_t *mp;
        libvlc_media_t *m;
    
        context *ctx = new context;
        ctx->pixels = new uchar[VIDEO_WIDTH * VIDEO_HEIGHT * 4]; // 申请大小也为4通道的像素
        memset(ctx->pixels, 0, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
    
        libvlc_time_t length;
        int width;
        int height;
        inst = libvlc_new(0, NULL);
        m = libvlc_media_new_path(inst, u8"C:\\Users\\HiWin10\\Desktop\\4K.mp4");
        mp = libvlc_media_player_new_from_media(m);
        //libvlc_media_player_set_hwnd(mp, (void *)this->winId());
        libvlc_video_set_callbacks(mp, lock, unlock, display, ctx);
        libvlc_video_set_format(mp, "RGBA", VIDEO_WIDTH, VIDEO_HEIGHT, VIDEO_WIDTH * 4);
        libvlc_media_release(m);
        libvlc_media_player_play(mp);
    }
    
    void Vedio::updatePicture(const QImage &image)
    {
        lists.push_back(image);
        emit showImage();
    }
    
    
    void Vedio::paintEvent(QPaintEvent *event)
    {
        if (lists.empty())
        {
            return;
        }
        QPainter painter(this);
        painter.drawPixmap(this->rect(), QPixmap::fromImage(lists.front()));
        lists.pop_front();
    }
    

    经过上面的步骤我们已经完成了整个视频的渲染,由于 paintEvent 方法使用的是cpu计算,且我们不停的生成的是 QImage 对象,因此对 CPU 负荷较高。

    其实在绘制这块我们可以使用 Opencv 或者 openGL 去绘制,利用 GPU 减轻 CPU 计算负荷。

    相关文章

      网友评论

          本文标题:QT如何获取VLC每一帧并渲染到Qwidget

          本文链接:https://www.haomeiwen.com/subject/ohhvaktx.html