diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1f02c12 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.16) + +project(sdl-yuv LANGUAGES C) +find_package(SDL2 REQUIRED) + +add_executable(sdl-yuv main.c) + +include(GNUInstallDirs) +install(TARGETS sdl-yuv + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +target_link_libraries(${PROJECT_NAME} PRIVATE SDL2::SDL2) \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..01bf721 --- /dev/null +++ b/main.c @@ -0,0 +1,141 @@ +#include +#include + +#define REFRESH_EVENT (SDL_USEREVENT + 1) +#define QUIT_EVENT (SDL_USEREVENT + 1) + +#define YUV_WIDTH 320 +#define YUV_HEIGHT 240 + +// SDL_PIXELFORMAT_IYUV = /**< Planar mode: Y + U + V (3 planes) */ +#define YUV_FORMAT SDL_PIXELFORMAT_IYUV + +int nThreadExitFlag = 0; + +int refreshVideoThread(void *data) { + while (!nThreadExitFlag) + { + SDL_Event event; + event.type = REFRESH_EVENT; + SDL_PushEvent(&event); + SDL_Delay(40); + } + nThreadExitFlag = 0; + SDL_Event eq; + eq.type = QUIT_EVENT; + SDL_PushEvent(&eq); + + return 0; +} + +int main() +{ + if (SDL_Init(SDL_INIT_VIDEO)) { + fprintf(stderr, "SDL_Init(SDL_INIT_VIDEO) failed: %s\n", SDL_GetError()); + return -1; + } + + SDL_Event event; + SDL_Rect rect; + SDL_Window *window; + SDL_Renderer *renderer = NULL; + SDL_Texture *texture = NULL; + SDL_Thread *t = 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_size = y_frame_len + u_frame_len + v_frame_len; + + window = SDL_CreateWindow("YUV", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + win_width, win_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + + if (!window) { + printf("SDL_CreateWindow() failed: %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_size); + if (!video_buf) { + fprintf(stderr, "malloc video_buf failed\n"); + } + + video_fd = fopen(yuv_path, "rb"); + if (!video_fd) { + fprintf(stderr, "fopen failed\n"); + } + + t = SDL_CreateThread(refreshVideoThread, "refresh_video_thread", NULL); + + while (1) { + SDL_WaitEvent(&event); + + if (event.type == REFRESH_EVENT) { + video_buff_len = fread(video_buf, 1, yuv_frame_size, video_fd); + if (video_buff_len <= 0) { + fprintf(stderr, "fread from yuv file failed\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); + } + else if (event.type == SDL_QUIT) { + nThreadExitFlag = 1; + } + else if (event.type == QUIT_EVENT) { + break; + } + } +fail: + nThreadExitFlag = 1; + if (t) { + SDL_WaitThread(t, 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; +}