使用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_IYUVint 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 eventSDL_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对应一个uuint32_t v_frame_len = video_width * video_height / 4; // 420p是4个y对应一个vuint32_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;
}