示例使用的是Qt5.10和minGW_32位,用C語言實現。
將用於測試的YUV影片和SDL2.dll放到對應的build目錄下,將SDL庫放到專案目錄下
.pro檔案:
點選檢視程式碼
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += main.c
win32{
INCLUDEPATH += $$PWD/SDL2/include
LIBS += $$PWD/SDL2/lib/x86/SDL2.lib
}
點選檢視程式碼
#include <stdio.h>
#include <string.h>
#include <SDL.h>
#define REFRESH_EVENT (SDL_USEREVENT+1)
#define QUIT_EVENT (SDL_USEREVENT+2)
#define YUV_WIDTH 320
#define YUV_HEIGHT 240
#define YUV_FORMAT SDL_PIXELFORMAT_IYUV
int s_thread_exit = 0;
int refresh_video_timer(void *data){
while(!s_thread_exit){
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(40);
}
//執行緒不退出,就持續將事件(更新事件)傳送到事件佇列當中,每個事件對應一幀,40ms延遲能實現25fps的影片播放
s_thread_exit = 0;
SDL_Event event;
event.type = QUIT_EVENT;
SDL_PushEvent(&event);
return 0;
}
#undef main
int main(int argc,char* argv[])
{
if(SDL_Init(SDL_INIT_VIDEO)){
fprintf(stderr,"Could not initialize SDL:%s\n",SDL_GetError());
return -1;
}
SDL_Event event;
SDL_Rect rect;
SDL_Window *window=NULL;
SDL_Renderer *renderer = NULL;
SDL_Texture *texture = NULL;
SDL_Thread *timer_thread = NULL;
uint32_t pixformat = YUV_FORMAT;
int video_width = YUV_WIDTH;
int video_height = YUV_HEIGHT;
int win_width = YUV_WIDTH;
int win_height = YUV_HEIGHT;
FILE *video_fd = NULL;
const char *yuv_path = "yuv420p_320x240.yuv";
size_t video_buff_len = 0;
uint8_t *video_buf = NULL;
uint32_t y_frame_len = video_width*video_height;
uint32_t u_frame_len = video_width*video_height/4;
uint32_t v_frame_len = video_width*video_height/4;
uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;
window = SDL_CreateWindow("Simplest YUV Player",SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,video_width,video_height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if(!window){
fprintf(stderr,"SDL:could not create window,err:%s\n",SDL_GetError());
goto _FAIL;
}
renderer = SDL_CreateRenderer(window,-1,0);
texture = SDL_CreateTexture(renderer,pixformat,SDL_TEXTUREACCESS_STREAMING,
video_width,video_height);
video_buf = (uint8_t*)malloc(yuv_frame_len);
if(!video_buf){
fprintf(stderr,"Failed to alloce yuv frame space!\n");
goto _FAIL;
}
video_fd = fopen(yuv_path,"rb");
if(!video_fd){
printf(stderr,"Failed to open yuv file\n");
goto _FAIL;
}
timer_thread = SDL_CreateThread(refresh_video_timer,NULL,NULL);
while (1) {
SDL_WaitEvent(&event);
if(event.type== REFRESH_EVENT){
video_buff_len = fread(video_buf,1,yuv_frame_len,video_fd);
if(video_buff_len<=0){
fprintf(stderr,"Failed to read data from yuv file!\n");
goto _FAIL;
}
SDL_UpdateTexture(texture,NULL,video_buf,video_width);
rect.x = 0;
rect.y = 0;
float w_ratio=win_width*1.0/video_width;
float h_ratio=win_height*1.0/video_height;
rect.w=video_width*w_ratio;
rect.h=video_height*h_ratio;
// rect.w = video_width * 0.5;
// rect.h = video_height * 0.5;
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer,texture,NULL,&rect);
SDL_RenderPresent(renderer);
}
else if(event.type==SDL_WINDOWEVENT){
SDL_GetWindowSize(window,&win_width,&win_height);
printf("SDL_WINDOWEVENT win_width:%d,win_height:%d",win_width,win_height);
printf("video_width:%d,video_height:%d",video_width,video_height);
printf("rect.w:%d,rect.h:%d\n",rect.w,rect.h);
}
else if(event.type==SDL_QUIT){
s_thread_exit =1;
}
else if(event.type==QUIT_EVENT){
break;
}
}
_FAIL:
s_thread_exit =1;
if(timer_thread)
SDL_WaitThread(timer_thread,NULL);
if(video_buf)
free(video_buf);
if(video_fd)
free(video_fd);
if(texture)
SDL_DestroyTexture(texture);
if(renderer)
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}
使用qq自帶的截圖工具檢視影片解析度和視窗大小(開啟qq掛著,Alt+A),
如果大小和命令列輸出的不一致,可能是程式碼錯誤或者顯示設定不是100%。
rect.w=video_width*w_ratio;
rect.h=video_height*h_ratio;
rect的大小隨視窗window的大小改變
這張圖是
rect.w = video_width * 0.5;
rect.h = video_height * 0.5;
的執行結果。
可見影片的解析度並不隨視窗window的縮放而改變,黑色區域大小就是windows的大小,影片可視範圍就是rect的大小。