音視頻技術應用(10)- 使用SDL 直接播放和渲染YUV文件


本節記錄下如何使用SDL直接播放和渲染RGB文件

1. 首先准備好需要播放的YUV文件

這里准備了一個mp4文件,我們要利用ffmpeg將該mp4文件直接轉換成YUV文件,另外需要注意的是,由於YUV文件是未經壓縮的文件,不同於mp4, 如果轉換前的mp4文件時間很長,那么轉換后的YUV文件將會很大,所以我們這里只取一小段的mp4文件(分辨率為400*300,幀率為25fps)進行格式轉換。轉換需要用到ffmpeg.exe, 可以從這里下載,

鏈接: https://pan.baidu.com/s/1g18PLIrA-6DhYLnaSwx9Qw 提取碼: umby 

下載完畢后,將ffmpeg.exe和需要轉換的mp4文件放置到指定的目錄

然后執行:

ffmpeg -i 400_300_25.mp4 400_300_25.yuv

生成完畢后的YUV文件如下:

可以看到,生成的YUV文件還是很大的。

另外需要注意的是,我們這里並沒有給ffmpeg添加任何參數,所以ffmpeg是按照內部默認的格式進行轉換的,它轉換后的YUV的采樣格式是 yuv420p , 這點可以從剛才的轉換窗口中看出來:

2. 准備coding

修改之前編寫的qt_rgb 解決方案,修改cpp code 如下:

#include "sdlqtrgb.h"

#include <iostream>
#include <fstream>
#include <qmessagebox.h>

#include <sdl/SDL.h>

#pragma comment(lib, "SDL2.lib")

using namespace std;

static int sdl_width = 0;
static int sdl_height = 0;

static SDL_Window* sdl_window = NULL;
static SDL_Renderer* sdl_render = NULL;
static SDL_Texture* sdl_texture = NULL;

static int pixel_size = 2;                  // 在YUV420p采樣格式下,單個像素的大小是1.5個字節,這里使用2個字節,便於容納,這里的單個像素大小可以大於1.5,但是絕對不能小於1.5 
static unsigned char* yuv = NULL;

static ifstream yuv_file;

SDLQtRGB::SDLQtRGB(QWidget *parent)
    : QWidget(parent)
{


    ui.setupUi(this);

    // 打開YUV文件
    yuv_file.open("400_300_25.yuv", ios::binary);
    if (!yuv_file)
    {
        QMessageBox::information(this, "", "open yuv failed!");
        return;
    }

    // 設定窗口和label的寬高
    sdl_width = 400;
    sdl_height = 300;

    // 1. 初始化SDL
    if (SDL_Init(SDL_INIT_VIDEO))
    {
        cout << SDL_GetError() << endl;
        return;
    }

    // 2. 創建窗口, 這里取得label所對應的窗口句柄
    sdl_window = SDL_CreateWindowFrom((void*)ui.label->winId());
    if (!sdl_window)
    {
        cout << SDL_GetError() << endl;
        return;
    }

    // 3. 創建渲染器
    sdl_render = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED);
    if (!sdl_render)
    {
        cout << SDL_GetError() << endl;
        return;
    }

    // 重新設定label的大小, 因為yuv視頻文件的分辨率是400*300, 所以這里更新label的大小,以便能夠正常顯示YUV圖像
    ui.label->resize(sdl_width, sdl_height);

    // 4. 根據label控件的寬高來創建材質
    // 這里的像素格式要與YUV的圖像格式相對應,之前生成的YUV的圖像格式是YUV420p, 所以對應的材質的格式必須是 SDL_PIXELFORMAT_IYUV
    sdl_texture = SDL_CreateTexture(sdl_render,
        SDL_PIXELFORMAT_IYUV,
        SDL_TEXTUREACCESS_STREAMING, 
        sdl_width, sdl_height
    );

    if (!sdl_texture)
    {
        cout << SDL_GetError() << endl;
        return;
    }

    // 申請一塊內存空間用於存放RGB數據
    yuv = new unsigned char[sdl_width * sdl_height * pixel_size];


    // 每隔10ms調用一次timerEvent函數
    startTimer(10);
}


void SDLQtRGB::timerEvent(QTimerEvent* ev)
{
   
    // 讀取yuv數據信息(一次讀取一幀)
    // 1.5 代表單個YUV像素點的大小
    yuv_file.read((char *)yuv, sdl_width * sdl_height * 1.5);


    // 5. 動態更新材質信息
    // 這里寬度為什么可以設置成sdl_width?
    // 這是因為當前的采樣格式是YUV420p, 是以平面格式進行存儲的,也就是yyyy uu vv,
    // 所以每一行只需要傳入y的字節數即可,而yuv420p 采樣格式,y的字節大小為1,所以y的字節數為sdl_width
    if (SDL_UpdateTexture(sdl_texture, NULL, yuv, sdl_width))
    {
        cout << SDL_GetError() << endl;
        return;
    }

    // 6. 清理屏幕
    if (SDL_RenderClear(sdl_render))
    {
        cout << SDL_GetError() << endl;
        return;
    }

    // 7. 復制材質到渲染器對象
    SDL_Rect rect;
    rect.x = 0;
    rect.y = 0;
    rect.w = sdl_width;
    rect.h = sdl_height;
    if (SDL_RenderCopy(sdl_render, sdl_texture, NULL, &rect))
    {
        cout << SDL_GetError() << endl;
        return;
    }

    // 8. 執行渲染操作
    SDL_RenderPresent(sdl_render);

}

運行:

可以看到yuv文件已經可以順利渲染到指定的窗口上。

<完>


免責聲明!

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



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