一、下载SDL2
地址:https://www.libsdl.org/download-2.0.php
Windows下可以下载编译好的Development Libraries
将下载好的文件解压,有着include和lib两个目录就是我们需要的文件
二、SDL2播放yuv视频流程
1、初始化SDL:SDL_Init
函数原型:
DECLSPEC int SDLCALL SDL_Init(Uint32 flags);
其中flags的取值有以下几种
SDL_INIT_TIMER 定时器
SDL_INIT_AUDIO 音频
SDL_INIT_VIDEO 视频
SDL_INIT_JOYSTICK
SDL_INIT_HAPTIC
SDL_INIT_GAMECONTROLLER
SDL_INIT_EVENTS 事件
SDL_INIT_SENSOR
SDL_INIT_NOPARACHUTE
SDL_INIT_EVERYTHING 所有
一般需要用到什么就设置什么,不建议将flags设置为SDL_INIT_EVERYTHING
2、创建SDL窗口:SDL_CreateWindow由SDL创建窗口,SDL_CreateWindowFrom基于Windows窗口句柄创建一个窗口
SDL_CreateWindow函数原型:
DECLSPEC SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
int x, int y, int w,
int h, Uint32 flags);
参数介绍:
title:窗口标题
x:窗口起始x坐标
y:窗口起始y坐标
w:窗口宽
h:窗口高
flags:窗口属性,包括了窗口的是否最大化、最小化,能否调整边界等等属性
::SDL_WINDOW_FULLSCREEN, ::SDL_WINDOW_OPENGL,
::SDL_WINDOW_HIDDEN, ::SDL_WINDOW_BORDERLESS,
::SDL_WINDOW_RESIZABLE, ::SDL_WINDOW_MAXIMIZED,
::SDL_WINDOW_MINIMIZED, ::SDL_WINDOW_INPUT_GRABBED,
::SDL_WINDOW_ALLOW_HIGHDPI, ::SDL_WINDOW_VULKAN.
SDL_CreateWindowFrom函数原型:
DECLSPEC SDL_Window * SDLCALL SDL_CreateWindowFrom(const void *data);
参数介绍:
data:外部窗口句柄HWND
3、创建Render:SDL_CreateRenderer
函数原型:
DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,
int index, Uint32 flags);
参数介绍:
window:之前创建的SDL_Window
index:打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备
flags:使用哪一种方式进行渲染
SDL_RENDERER_SOFTWARE :使用软件渲染
SDL_RENDERER_ACCELERATED :使用硬件加速
SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步
SDL_RENDERER_TARGETTEXTURE
4、创建Texture:SDL_CreateTexture
函数原型:
DECLSPEC SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
Uint32 format,
int access, int w,
int h);
参数介绍
renderer:之前创建的SDL_Renderer
format:纹理的格式
SDL_PIXELFORMAT_YV12:I420
SDL_PIXELFORMAT_NV12:NV12
SDL_PIXELFORMAT_NV21:NV21
等等
access:SDL_TextureAccess
SDL_TEXTUREACCESS_STATIC :变化极少
SDL_TEXTUREACCESS_STREAMING :变化频繁
SDL_TEXTUREACCESS_TARGET
int w:纹理的高
int h:纹理的宽
一般都设置为yuv数据的宽和高
5、给纹理设置数据:SDL_UpdateTexture
函数原型:
DECLSPEC int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,
const SDL_Rect * rect,
const void *pixels, int pitch);
参数介绍:
texture:SDL_Texture
rect:更新像素的矩形区域,设置为NULL表示整个窗口,如果要保持宽高比需要手动计算渲染区域
pixels:像素数据,也就是我们传进来的yuv数据
int pitch:间距,也叫linesize或者是stride
6、清空渲染目标:SDL_RenderClear,在每次使用前需要将上一次显示的东西清除掉,也可以不使用,因为下一帧的数据会把当前帧的数据覆盖了
函数原型:
DECLSPEC int SDLCALL SDL_RenderClear(SDL_Renderer * renderer);
参数介绍:
renderer:SDL_Renderer
7、纹理复制给渲染目标:SDL_RenderCopy
函数原型:
DECLSPEC int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,
SDL_Texture * texture,
const SDL_Rect * srcrect,
const SDL_Rect * dstrect);
参数介绍:
renderer:SDL_Renderer
texture:SDL_Texture
srcrect:源渲染区域,输入
dstrect:目标渲染区域,输出
8、显示:SDL_RenderPresent
函数原型:
DECLSPEC void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);
三、具体代码
sdl_player.h
// sdl_player.h
#include "SDL.h"
enum PixFormat {
I420,
NV12,
NV21
};
class SDL2Player {
public:
SDL2Player();
~SDL2Player();
void Init(uint32_t width, uint32_t height, PixFormat pix_format);
void CreateRenderWindow();
void SetWindow(void* handle);
void Render(uint8_t* data);
private:
void Init();
void Destroy();
void InitRender();
void InitTexture();
private:
uint32_t width_{};
uint32_t height_{};
PixFormat pix_format_ = I420;
SDL_Window* window_{}; // 窗口
SDL_Renderer* renderer_{}; // 渲染器
SDL_Texture* texture_{}; // 纹理
};
sdl_player.cpp
// sdl_palyer.cpp
#include "sdl_player.h"
#include <iostream>
SDL2Player::SDL2Player() {
Init();
}
SDL2Player::~SDL2Player() {
Destroy();
}
void SDL2Player::Init() {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) == -1) {
std::cout << "SDL2 init failed" << std::endl;
return;
}
}
void SDL2Player::Destroy() {
if (window_) {
SDL_DestroyWindow(window_);
}
if (renderer_) {
SDL_DestroyRenderer(renderer_);
}
if(texture_) {
SDL_DestroyTexture(texture_);
}
SDL_Quit();
}
void SDL2Player::Init(uint32_t width, uint32_t height, PixFormat pix_format) {
width_ = width;
height_ = height;
pix_format_ = pix_format;
}
void SDL2Player::CreateRenderWindow() {
window_ = SDL_CreateWindow("YUV Player",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width_, height_,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
);
InitRender();
InitTexture();
}
void SDL2Player::SetWindow(void* handle) {
// SDL_SetHint(SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT, nullptr);
window_ = SDL_CreateWindowFrom(handle);
if (window_ == nullptr) {
std::cout << SDL_GetError() << std::endl;
std::cout << "window is nullptr" << std::endl;
}
InitRender();
InitTexture();
}
void SDL2Player::InitRender() {
renderer_ = SDL_CreateRenderer(window_, -1, SDL_RENDERER_ACCELERATED);
}
void SDL2Player::InitTexture() {
uint32_t pixformat;
if (pix_format_ == I420) {
pixformat = SDL_PIXELFORMAT_IYUV;
}
else if (pix_format_ == NV12) {
pixformat = SDL_PIXELFORMAT_NV12;
}
else if (pix_format_ == NV21) {
pixformat = SDL_PIXELFORMAT_NV21;
}
else {
std::cout << "输入的格式暂不支持" << std::endl;
}
texture_ = SDL_CreateTexture(renderer_, pixformat, SDL_TEXTUREACCESS_STREAMING, width_, height_);
}
void SDL2Player::Render(uint8_t* data) {
SDL_UpdateTexture(texture_, NULL, data, width_);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = width_;
rect.h = height_;
SDL_RenderClear(renderer_);
SDL_RenderCopy(renderer_, texture_, NULL, &rect);
SDL_RenderPresent(renderer_);
}
main.cpp
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
#include "sdl_player.h"
HWND CreateMyWindow(uint32_t width, uint32_t height) {
WNDCLASS window;
memset(&window, 0, sizeof(window));
window.lpfnWndProc = (WNDPROC)DefWindowProc;
window.hInstance = GetModuleHandle(NULL);
window.hCursor = LoadCursor(NULL, IDC_ARROW);
window.lpszClassName = "yuv_player";
if (!RegisterClass(&window)) {
return nullptr;
}
DWORD dwStyle = NULL;
HWND wnd = CreateWindowEx(NULL, window.lpszClassName, "YUV Player", dwStyle,
100, 100, width, height, nullptr, nullptr,
GetModuleHandle(NULL), nullptr);
ShowWindow(wnd, SW_SHOWDEFAULT);
UpdateWindow(wnd);
return wnd;
}
int main(int argc,char* argv[]) {
// 要将文件放在对应的目录下面
std::string filename = "480x272_yuv420p.yuv";
uint32_t width = 480;
uint32_t height = 272;
std::shared_ptr<SDL2Player> yuv_player = std::make_shared<SDL2Player>();
yuv_player->Init(width, height, I420);
// yuv_player->CreateRenderWindow();
HWND window = CreateMyWindow(width,height);
yuv_player->SetWindow(window);
uint8_t* data = new uint8_t[width * height * 3 / 2];
std::ifstream fin(filename, std::ios::in | std::ios::binary);
while (!fin.eof()) {
fin.read((char*)data, width * height * 3 / 2);
yuv_player->Render(data);
std::this_thread::sleep_for(std::chrono::milliseconds(33));
}
delete[] data;
fin.close();
getchar();
return 0;
}
四、代码下载:https://download.csdn.net/download/yp18792574062/12912916
五、参考资料
https://blog.csdn.net/leixiaohua1020/article/details/40525591