音视频面经_音视频知识解析:第四章 SDL视频显示
本专栏会持续更新优质内容,敬请订阅,关注更新。
专栏地址:音视频面经_音视频知识解析
------------下面正文开始------------
1 视频显示知识
• 视频显示的流程
▫ 视频显示的流程,就是将像素数据“画”在屏幕上的过程。
▫ 例如显示YUV,就是将YUV“画”在系统的窗口中。
2 SDL简介
作用
• SDL(Simple DirectMedia Layer)库设计用于处理图形、音频、输入设备等多媒体内容。它广泛应用于游戏和多媒体应用程序的开发,因为它能够简化在不同操作系统和硬件架构上创建高性能、低延迟的多媒体应用的过程。SDL使得开发者可以专注于应用程序逻辑,而不必过多关注底层硬件和操作系统的具体实现细节。SDL在游戏开发上也常用。
简单来说就是SDL封装了复杂的视音频底层交互工作,简化了视音频处理的难度。
• 下面只涉及到SDL库的一小部分——视频显示部分。
特点
• 跨平台的C语言库
• 开源
结构
• SDL结构如下所示。可以看出它实际上还是调用了DirectX等底层的API完成了和硬件的交互。
3 VC下SDL开发环境搭建
3.1 新建控制台工程
• 打开VC++
• 文件->新建->项目->控制台应用程序
3.2 拷贝SDL开发文件
• 头文件(.h)拷贝至项目文件夹的include子文件夹下
• 导入库文件(.lib)拷贝至项目文件夹的lib子文件夹下
• 动态库文件(*.dll)拷贝至项目文件夹下
3.3 配置开发文件
• 打开属性面板
解决方案资源管理器->右键单击项目->属性
• 头文件配置
配置属性->C/C+±>常规->附加包含目录,输入“include”(刚才拷贝文件的目录)
• 导入库配置
配置属性->链接器->常规->附加库目录,输入“lib” (刚才拷贝文件的目录)
配置属性->链接器->输入->附加依赖项,输入“SDL2.lib; SDL2main.lib”(导入库的文件名)
▫ 动态库不用配置
3.4 测试
• 创建源代码文件
在工程中创建一个包含main()函数的C/C++文件(如果已经有了可以跳过这一步),后续步骤在该文件中编写源代码。
• 包含头文件
如果是C语言中使用SDL,则直接使用下面代码
#include "SDL2/SDL.h"
如果是C++语言中使用SDL,则使用下面代码
extern "C"{ #include "SDL2/SDL.h" }
• main()中调用一个SDL的接口函数
例如下面代码初始化了SDL
int main(int argc, char* argv[]){ if(SDL_Init(SDL_INIT_VIDEO)) { printf( "Could not initialize SDL - %s\n", SDL_GetError()); } else{ printf("Success init SDL"); } return 0; }
如果运行无误,则代表SDL已经配置完成。
4示例程序运行
/** * 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV) * Simplest Video Play SDL2 (SDL2 play RGB/YUV) * * 本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图 * API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层 * API。 * * This software plays RGB/YUV raw video data using SDL2. * SDL is a wrapper of low-level API (Direct3D, OpenGL). * Use SDL is much easier than directly call these low-level API. */ #include <stdio.h> extern "C" { #include "SDL.h" }; const int bpp = 12; //窗口的宽高 int screen_w = 640, screen_h = 360; //像素的宽高 const int pixel_w = 640, pixel_h = 360; //保存yuv数据 unsigned char buffer[pixel_w * pixel_h * bpp / 8]; int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_VIDEO)) { printf("Could not initialize SDL - %s\n", SDL_GetError()); return -1; } SDL_Window* screen; //SDL 2.0 Support for multiple windows screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!screen) { printf("SDL: could not create window - exiting:%s\n", SDL_GetError()); return -1; } SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0); Uint32 pixformat = 0; //IYUV: Y + U + V (3 planes) //YV12: Y + V + U (3 planes) pixformat = SDL_PIXELFORMAT_IYUV; SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h); FILE* fp = NULL; fopen_s(&fp,"sintel_640_360.yuv", "rb+"); if (fp == NULL) { printf("cannot open this file\n"); return -1; } SDL_Rect sdlRect; while (1) { if (fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp) != pixel_w * pixel_h * bpp / 8) { // Loop fseek(fp, 0, SEEK_SET); fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp); } SDL_UpdateTexture(sdlTexture, NULL, buffer, pixel_w); sdlRect.x = 0; sdlRect.y = 0; sdlRect.w = screen_w; sdlRect.h = screen_h; SDL_RenderClear(sdlRenderer); SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect); SDL_RenderPresent(sdlRenderer); //Delay 40ms SDL_Delay(40); } SDL_Quit(); return 0; }
5 SDL视频显示的函数
SDL视频显示的流程图如下所示
SDL视频显示函数简介
• SDL_Init():初始化SDL系统
• SDL_CreateWindow():创建窗口SDL_Window
• SDL_CreateRenderer():创建渲染器SDL_Renderer
• SDL_CreateTexture():创建纹理SDL_Texture
• SDL_UpdateTexture():设置纹理的数据
• SDL_RenderCopy():将纹理的数据拷贝给渲染器
• SDL_RenderPresent():显示
• SDL_Delay():工具函数,用于延时。
• SDL_Quit():退出SDL系统
6 SDL视频显示的数据结构
SDL视频显示的数据结构如下所示
SDL数据结构简介
• SDL_Window
代表了一个“窗口”
• SDL_Renderer
代表了一个“渲染器”
• SDL_Texture
代表了一个“纹理”
• SDL_Rect
一个简单的矩形结构,标明Texture显示在屏幕的位置和区域大小
7 进阶-示例程序运行
// SDLVideoPlay.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" /** * 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV) * Simplest Video Play SDL2 (SDL2 play RGB/YUV) * * * 本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图 * API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层 * API。 * * This software plays RGB/YUV raw video data using SDL2. * SDL is a wrapper of low-level API (Direct3D, OpenGL). * Use SDL is much easier than directly call these low-level API. */ #include <stdio.h> extern "C" { #include "SDL2/SDL.h" }; const int bpp = 12; //窗口的宽高 int screen_w = 640, screen_h = 360; //像素的宽高 const int pixel_w = 640, pixel_h = 360; //保存yuv数据 unsigned char buffer[pixel_w * pixel_h * bpp / 8]; int main(int argc, char* argv[]) { //初始化,激活视频子系统(其中也会激活事件子系统) if (SDL_Init(SDL_INIT_VIDEO)) { printf("Could not initialize SDL - %s\n", SDL_GetError()); return -1; } SDL_Window* screen; //SDL 2.0 Support for multiple windows //中间两个参数定位窗口,如下设置就是标明不设置具体窗口位置 //最后一个参数标明窗口支持的类型 screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!screen) { printf("SDL: could not create window - exiting:%s\n", SDL_GetError()); return -1; } //创建一个渲染器,我们不直接操作它 SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0); //纹理格式 Uint32 pixformat = 0; //IYUV: Y + U + V (3 planes) //YV12: Y + V + U (3 planes) pixformat = SDL_PIXELFORMAT_IYUV; //创建纹理,第三个参数表示纹理变化频繁 SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h); FILE* fp = NULL; fopen_s(&fp,"sintel_640_360.yuv", "rb+"); if (fp == NULL) { printf("cannot open this file\n"); return -1; } SDL_Rect sdlRect; while (1) { //这里表示如果读不到足够的内容,应该是读完了文件,因此为了循环播放重新置位fp //bpp表示存一个YUV像素点所需的二进制位数 // pixel_w * pixel_h * bpp / 8:表示一张图片的所有YUV像素点占的字节数 if (fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp) != pixel_w * pixel_h * bpp / 8) { // Loop fseek(fp, 0, SEEK_SET); fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp); } //用buffer设置纹理 SDL_UpdateTexture(sdlTexture, NULL, buffer, pixel_w); //创建矩形,确定画的位置 //FIX: If window is resize //sdlRect.x表示矩形区域和sdlwindow的相对坐标位置 sdlRect.x = 0; sdlRect.y = 0; sdlRect.w = screen_w; sdlRect.h = screen_h; SDL_RenderClear(sdlRenderer);//清空渲染器 SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);//将纹理的数据拷贝给渲染器 SDL_RenderPresent(sdlRenderer);//显示 //Delay 40ms SDL_Delay(40); } SDL_Quit(); return 0; }
8 进阶-SDL中多线程和事件
SDL多线程
• 函数
SDL_CreateThread():创建一个线程
• 数据结构
SDL_Thread:线程的句柄
SDL事件
• 函数
SDL_WaitEvent()等待一个事件
SDL_PushEvent()发送一个事件
• 数据结构
SDL_Event:代表一个事件
9 练习
• 给源代码中每个SDL函数添加中文注释
• 修改源代码。对于测试文件,实现以下几种显示
▫ 基本练习
二倍速度
二倍宽高
窗口大小固定为500x500
视频周围包围10像素的“黑框”
换一段测试YUV素材进行播放
▫ 进阶练习
窗口可以移动
窗口可以调整大小
按下空格键(SPACE)后暂停,再次按下空格后继续播放
显示黑白图像
本专栏会持续更新优质内容,敬请订阅,关注更新。
专栏地址:音视频面经_音视频知识解析
点赞、订阅后,可在评论区留言领取资料和源码工程
#音视频开发#本人在CVTE从事音视频开发工作多年,推出该专栏的目的是帮助更多有意向从事音视频开发的同学了解音视频编解码等基本知识、熟悉FFmpeg、SDL、OpenGL等开源框架的使用和编程,并动手开发视频播放器。 这些都是目前大厂音视频开发工程师相关职位要求的必备技能。本专栏会持续更新优质内容,敬请订阅,关注更新。随着内容不断丰富,可能会做付费专栏。另外提供CVTE内推、职位信息、面试答疑