文章目錄
- 1. **宏定義和初始化**
- 2. **全局變量**
- 3. **`refresh_video_timer` 函數**
- 4. **`WinMain` 函數**
- 主要功能及工作流程:
- 總結:
1. 宏定義和初始化
#define REFRESH_EVENT (SDL_USEREVENT + 1) // 請求畫面刷新事件
#define QUIT_EVENT (SDL_USEREVENT + 2) // 退出事件
REFRESH_EVENT
和QUIT_EVENT
是自定義的事件類型,用來觸發畫面刷新和退出操作。
2. 全局變量
int s_thread_exit = 0; // 退出標志
s_thread_exit
標志用于控制視頻播放線程的退出。
3. refresh_video_timer
函數
int refresh_video_timer(void *data)
{while (!s_thread_exit){SDL_Event event;event.type = REFRESH_EVENT;SDL_PushEvent(&event); // 觸發刷新事件SDL_Delay(40); // 約每40ms觸發一次}s_thread_exit = 0;//push quit eventSDL_Event event;event.type = QUIT_EVENT;SDL_PushEvent(&event);return 0;
}
refresh_video_timer
函數是一個線程函數,每隔 40 毫秒觸發一次REFRESH_EVENT
,用于請求刷新畫面。線程會一直運行,直到s_thread_exit
被設置為 1。- 當播放完畢后,觸發
QUIT_EVENT
以結束程序。
4. WinMain
函數
int WinMain(int argc, char* argv[])
{// 初始化 SDLif (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; // YUV格式// 視頻文件路徑const char *yuv_path = "H:/SDL/SDL_test/yuv420p_320x240.yuv";FILE *video_fd = NULL;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);// 分配內存用于存儲 YUV 數據video_buf = (uint8_t*)malloc(yuv_frame_len);if (!video_buf){fprintf(stderr, "Failed to allocate YUV frame space!\n");goto _FAIL;}// 打開 YUV 文件video_fd = fopen(yuv_path, "rb");if (!video_fd){fprintf(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;// 清除當前渲染內容并渲染新幀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\n", win_width, win_height);}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) fclose(video_fd);if (texture) SDL_DestroyTexture(texture);if (renderer) SDL_DestroyRenderer(renderer);if (window) SDL_DestroyWindow(window);SDL_Quit();return 0;
}
主要功能及工作流程:
-
SDL 初始化:
SDL_Init(SDL_INIT_VIDEO)
初始化 SDL 的視頻模塊。- 創建一個窗口 (
SDL_CreateWindow
)、一個渲染器 (SDL_CreateRenderer
) 和一個紋理 (SDL_CreateTexture
) 來渲染 YUV 數據。
-
讀取 YUV 數據:
- 打開 YUV 文件并讀取 YUV 數據到緩沖區
video_buf
。 - YUV 數據根據視頻的分辨率和 YUV420P 格式計算出每一幀的長度。
- 打開 YUV 文件并讀取 YUV 數據到緩沖區
-
視頻幀渲染:
- 每次讀取一幀數據后,使用
SDL_UpdateTexture
將 YUV 數據更新到紋理。 - 通過渲染器
SDL_Renderer
渲染紋理到窗口。
- 每次讀取一幀數據后,使用
-
刷新和退出事件:
- 創建了一個獨立的線程定時觸發
REFRESH_EVENT
,控制視頻的刷新頻率。 - 如果讀取到文件結尾或退出事件,程序會退出。
- 創建了一個獨立的線程定時觸發
-
窗口大小調整:
- 在窗口大小變化時,程序會自動調整渲染區域的比例,保持視頻的寬高比。
-
資源釋放:
- 在退出程序時,確保釋放所有分配的資源,包括 SDL 相關的資源(窗口、渲染器、紋理)以及 YUV 數據緩沖區。
總結:
這個程序通過 SDL2 創建一個視頻播放窗口,讀取并顯示 YUV 文件的每一幀視頻數據。它通過一個單獨的線程來周期性觸發視頻幀的刷新,保持視頻播放的流暢。