本節記錄下如何使用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文件已經可以順利渲染到指定的窗口上。
<完>