SDL結合QWidget的簡單使用說明


SDL(Simple DirectMeida Layer)是一個簡單的封裝媒體庫,功能主要涉及了相關於OpenGL或者DirectX的顯卡硬件功能和一些鼠標,鍵盤等外設訪問。這里主要只說明一下它的渲染功能

因為Qt本身不支持YUV流媒體數據顯示,且QWidget默認是柵格式渲染(qml是默認GPU),用的是CPU,這樣如果渲染方面涉及了反走樣,優化等數據計算,對CPU的消耗是比較高的,這里我們期望能用GPU來負責處理渲染

GPU的處理就必須使用能調用顯卡的功能,通常來說在Win下是Opengl和D3D,但是要短時間熟悉使用還是比較麻煩的,這里就需要SDL了;SDL已經在下層封裝好了這些顯卡相關庫的使用,我們只需要按照他定義的更簡潔的一系列接口即可實現效果

這里以YV12為例,因為個人需求,我又加上了在渲染層上再渲染一張png圖片和一段文字的效果

首先在官網下載源代碼http://www.libsdl.org/download-2.0.php

目前最新的源碼版本是2.0.8,且主頁依然提供了1.0版本的下載。1.0的使用和接口跟2.0有很多不一樣的地方,這里建議還是直接用最新版

下載解壓后目錄如圖

可以看到SDL支持各大主流平台,這里因為是在Win上,直接進入VisualC目錄

解決方案已經存在,直接打開編譯即可。

成功后在輸出目錄找到

這就是我們需要的動態庫(另外還有一個SDL2main的庫,因為我們是通過Qt創建的窗口,所以這個不需要了,如果完全使用SDL來建立渲染窗口的話,一定要加上這個庫)

下一步,先創建一個QWidget用來做渲染

#ifndef _SDL_RENDER_WND_H__
#define _SDL_RENDER_WND_H__
/********************************************************************
文件名  :    SDLRenderWnd
作者    :    @Kaiming
創建時間:    2018/3/26 11:27
版本    :     1.0
文件描述:    SDL YUV420流輸出窗口
*********************************************************************/

#include <QWidget>
struct SDL_Renderer;
struct SDL_Texture;
struct SDL_Window;

class SDLRenderWnd : public QWidget
{
    Q_OBJECT

public:
    SDLRenderWnd(QWidget *parent = 0);
    ~SDLRenderWnd();

    void Clear();

protected:
    virtual void resizeEvent(QResizeEvent *event);

private:
    static void SDL_Related_Init();
    static void SDL_Related_Uninit();

public slots:
//根據傳入數據流顯示視頻
    void PresentFrame(const unsigned char* pBuffer, int nImageWidth, int nImageHeight);
private:
    SDL_Renderer*        m_pRender;
    SDL_Texture*         m_pTexture;
    SDL_Window*          m_pWindow;

    static int            m_nRef;        //引用計數來確定SDL全局資源的創建和回收
};

#endif // _SDL_RENDER_WND_H__

具體實現

#include "SDLRenderWnd.h"
extern "C" {
#include "sdl\SDL.h"
}
#pragma comment(lib, "SDL2.lib")

int SDLRenderWnd::m_nRef = 0;
SDLRenderWnd::SDLRenderWnd(QWidget *parent)
    : QWidget(parent),
      m_pTexture(nullptr),
      m_bEmpty(true)
{
    setUpdatesEnabled(false);
    SDL_Related_Init();
    m_pWindow = SDL_CreateWindowFrom((void*)winId());
    m_pRender = SDL_CreateRenderer(m_pWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
}

SDLRenderWnd::~SDLRenderWnd()
{
    if (m_pWindow)
        SDL_DestroyWindow(m_pWindow);
    if (m_pRender)
        SDL_DestroyRenderer(m_pRender);
    if (m_pTexture)
        SDL_DestroyTexture(m_pTexture);
    SDL_Related_Uninit();
}

void SDLRenderWnd::PresentFrame(const unsigned char* pBuffer, int nImageWidth, int nImageHeight)
{
    if (!m_pRender) {
        printf("Render not Create\n");
    }
    else {
        int nTextureWidth = 0, nTextureHeight = 0;
    //首先查詢當前紋理對象的寬高,如果不符合,那么需要重建紋理對象 SDL_QueryTexture(m_pTexture, nullptr, nullptr,
&nTextureWidth, &nTextureHeight); if (nTextureWidth != nImageWidth || nTextureHeight != nImageHeight) { if (m_pTexture) SDL_DestroyTexture(m_pTexture);
      //這里指定了渲染的數據格式,訪問方式和寬高大小 m_pTexture
= SDL_CreateTexture(m_pRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, nImageWidth, nImageHeight); } } if (!m_pTexture) { printf("YUV Texture Create Failed\n"); } else {
     //用新數據刷新紋理 SDL_UpdateTexture(m_pTexture, nullptr, pBuffer, nImageWidth);
     //清除當前渲染 SDL_RenderClear(m_pRender);
//拷貝紋理對象到渲染器中 SDL_RenderCopy(m_pRender, m_pTexture, nullptr, nullptr);
     //最終渲染
     SDL_RenderPresent(m_pRender);
  } }
void SDLRenderWnd::Clear()
 
} void SDLRenderWnd::SDL_Related_Init() { if (0 == m_nRef++) { SDL_Init(SDL_INIT_VIDEO); } } void SDLRenderWnd::SDL_Related_Uninit() { if (0 == --m_nRef) { SDL_Quit(); } }

因為SDL是純C庫,注意加上extern “C";通過SDL_CreateWindowFrom((void*)winId());建立QWidget的窗口句柄和SDL的聯系,這之后,Widget本身就沒什么操作的了,剩下的工程都交給SDL來負責;然后通過創建的SDL_Window建立SDL_Render渲染器

渲染過程具體看PresentFrame里面的注釋,還有一點要特別注意的是一定要加上setUpdatesEnabled(false);這句是關閉QWidget自身的刷新動作,如果不設置,那么就會存在兩個渲染互相刷新,如果窗口不動還好,一旦resize的時候就就會有明顯的閃爍效果

還有一點就是,參看我頭文件里是把PresentFrame寫成了一個slot,所以刷新動作是放在主線程執行的,並不是說子線程執行不可以,但是從自己的測試來看,子線程執行渲染會出現很多不可預知的問題,比如窗口縮放后會導致渲染無效,崩潰等等。另外提一句老生常談的話就是調用這個slot注意保護參數,數據流是指針形式,如果異步執行要小心指針無效的情況

下一篇談在這個基礎上給視頻加圖片,文字效果的方式

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM