使用SDL进行音视频的播放
SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。
SDL的下载地址: https://www.libsdl.org/
现在较为新的版本在main函数中需要添加#define SDL_MAIN_HANDLED定义, 初步代码如下:
#include <stdio.h> #include <string.h> #include <malloc.h> #define SDL_MAIN_HANDLED #include <SDL.h> //自定义消息类型 #define REFRESH_EVENT (SDL_USEREVENT + 1) // 请求画面刷新事件 #define QUIT_EVENT (SDL_USEREVENT + 2) // 退出事件 //定义分辨率 // YUV像素分辨率 #define YUV_WIDTH 320 #define YUV_HEIGHT 240 //定义YUV格式 #define YUV_FORMAT SDL_PIXELFORMAT_IYUV int s_thread_exit = 0; // 退出标志 = 1则退出 int refresh_video_timer(void *data) { while (!s_thread_exit) { SDL_Event event; event.type = REFRESH_EVENT; SDL_PushEvent(&event); SDL_Delay(40); } s_thread_exit = 0; //push quit event SDL_Event event; event.type = QUIT_EVENT; SDL_PushEvent(&event); return 0; } int main() { if (SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "Can't not initialize SDL, err: %s\n", SDL_GetError()); } 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; // YUV420P,即是SDL_PIXELFORMAT_IYUV // 分辨率 // 1. YUV的分辨率 int video_width = YUV_WIDTH; int video_height = YUV_HEIGHT; // 2.显示窗口的分辨率 int win_width = YUV_WIDTH; int win_height = YUV_WIDTH; // YUV文件句柄 FILE *video_fd = NULL; const char *yuv_path = "yuv420p_320x240.yuv"; size_t video_buff_len = 0; uint8_t *video_buf = NULL; //读取数据后先把放到buffer里面 // 我们测试的文件是YUV420P格式 uint32_t y_frame_len = video_width * video_height; uint32_t u_frame_len = video_width * video_height / 4; // 420p是4个y对应一个u uint32_t v_frame_len = video_width * video_height / 4; // 420p是4个y对应一个v 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) { printf("Can't 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; } // 打开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); // 显示区域, 可以通过修改w和h进行缩放 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; }