在 【FFmpeg】SDL 音视频开发 ① ( SDL 窗口绘制 | SDL 视频显示函数 | SDL_Window 窗口 | SDL_Renderer 渲染器 | SDL_Texture 纹理 ) 博客中 , 介绍了
的用法 , 这些函数中 , 前四个函数是显示 视频画面 前的准备工作 , 最后一个 SDL_Quit 函数 是最后退出渲染时 , 释放 SDL 框架占用的各种资源 ;
在 【FFmpeg】SDL 音视频开发 ② ( SDL 视频显示函数 | 设置渲染器目标纹理 | 设置渲染器颜色 | 清除渲染器 | 渲染器绘制矩形 | 纹理拷贝 | 窗口中显示渲染纹理 ) 博客中 , 介绍了
几个函数 , 这些都是渲染视频画面的重要函数 ;
在本篇博客中 , 将会使用到上面的部分函数 ;
SDL 播放 YUV 画面流程 :
代码编译后 , SDL_Demo 工程会在本地生成 编译 后的可执行文件目录 build-SDL_Demo-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug ,
将要播放的 YUV 420P 格式的 视频文件 , 拷贝到这个 build-SDL_Demo-Desktop_Qt_5_14_2_MSVC2015_32bit-Debug 目录中 ,
将 视频文件 拷贝到根目录即可 ;
使用 SDL 播放 YUV 视频时 , 视频画面刷新是在 主线程 中执行的 ;
此处专门开启了一个子线程 , 用于控制 YUV 画面的刷新 ;
在下面的代码中 , 开启了子线程 , 子线程中执行 refresh_video_timer 函数 , 然后再启动主线程 , 主线程直接无限循环执行 , 每次执行时 都要接收 子线程 中传递的事件 , 受子线程控制 ;
// 创建 YUV 画面 刷新线程 , 该线程与主线程 并行执行
timer_thread = SDL_CreateThread(refresh_video_timer, NULL, NULL); // 创建刷新线程
// 在下面 主循环 中 , 不断刷新 YUV 画面数据
while (1) // 主循环
{
子线程 执行的 refresh_video_timer 函数内容如下 , 该函数用于 在子线程 中 控制画面的刷新速度 , 子线程 中 向主线程发送 刷新事件 , 主线程收到 REFRESH_EVENT 事件 , 就会刷新界面 ;
YUV 视频 播放完毕后 子线程会向主线程发送 QUIT_EVENT 事件 , 主线程 收到 QUIT_EVENT 事件 , 就会停止播放 ;
// 该函数用于 在子线程 中 控制画面的刷新速度
// 子线程 中 向主线程发送 刷新事件 , 主线程收到 REFRESH_EVENT 事件 , 就会刷新界面
// 播放完毕后 主线程 收到 QUIT_EVENT 事件 , 就会停止播放
// 本函数中设置 每 40ms 刷新一次 , 一秒刷新 25 帧 , 25 FPS
int refresh_video_timer(void *data)
{
while (!s_thread_exit) // 当未请求退出时
{
SDL_Event event; // 创建事件
event.type = REFRESH_EVENT; // 设置事件类型为画面刷新
// 将自定义的 画面刷新事件 推送事件到事件队列
SDL_PushEvent(&event);
SDL_Delay(40); // 延时40毫秒
}
s_thread_exit = 0; // 退出标志重置为0
// 推送退出事件
SDL_Event event;
event.type = QUIT_EVENT; // 设置事件类型为退出
SDL_PushEvent(&event); // 推送事件到事件队列
return 0;
}
在上述 refresh_video_timer 函数中 , 设置 每 40ms 刷新一次 , 一秒刷新 25 帧 , 也就是 帧率为 25 FPS ;
在主线程中的 while (1) 主循环中 , 执行本程序的核心操作 ;
主循环部分代码示例 :
// 在下面 主循环 中 , 不断刷新 YUV 画面数据
while (1) // 主循环
{
SDL_WaitEvent(&event); // 等待事件发生
if(event.type == REFRESH_EVENT) // 如果是画面刷新事件
{// 省略部分代码
// ...
// 更新纹理数据
SDL_UpdateTexture(texture, NULL, video_buf, video_width);
// ...
// 清除当前显示
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) // 如果是退出事件 , SDL_QUIT 是标准退出事件
{
s_thread_exit = 1; // 设置退出标志
}
else if(event.type == QUIT_EVENT) // 自定义退出事件
{
break; // 退出主循环
}