SDL播放yuv

一、下载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

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SDL2.0 支持渲染 YUV4:2:0 格式的视频,需要使用 SDL_Texture 和 SDL_RenderCopy 函数来实现。 以下是一个简单的示例代码: ```c #include <SDL2/SDL.h> #include <stdio.h> const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; int main(int argc, char* argv[]) { SDL_Window* window = NULL; SDL_Renderer* renderer = NULL; SDL_Texture* texture = NULL; if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); return 1; } window = SDL_CreateWindow("YUV4:2:0 Video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); if (window == NULL) { printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } renderer = SDL_CreateRenderer(window, -1, 0); if (renderer == NULL) { printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); if (texture == NULL) { printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } // YUV data Uint8* yPlane = NULL; Uint8* uPlane = NULL; Uint8* vPlane = NULL; int yPitch = 0; int uPitch = 0; int vPitch = 0; int w = 0; int h = 0; // read YUV file FILE* fp = fopen("video.yuv", "rb"); if (fp == NULL) { printf("Could not open YUV file!\n"); return 1; } // allocate memory for YUV data int bufferSize = SCREEN_WIDTH * SCREEN_HEIGHT * 3 / 2; Uint8* buffer = (Uint8*)malloc(bufferSize); // main loop SDL_Event event; bool quit = false; while (!quit) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = true; } } // read YUV data if (fread(buffer, 1, bufferSize, fp) != bufferSize) { // end of file rewind(fp); } // set YUV data yPlane = buffer; uPlane = yPlane + SCREEN_WIDTH * SCREEN_HEIGHT; vPlane = uPlane + SCREEN_WIDTH * SCREEN_HEIGHT / 4; yPitch = SCREEN_WIDTH; uPitch = SCREEN_WIDTH / 2; vPitch = SCREEN_WIDTH / 2; w = SCREEN_WIDTH; h = SCREEN_HEIGHT; // update texture SDL_UpdateYUVTexture(texture, NULL, yPlane, yPitch, uPlane, uPitch, vPlane, vPitch); // clear screen SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(renderer); // render texture SDL_RenderCopy(renderer, texture, NULL, NULL); // update screen SDL_RenderPresent(renderer); } // free memory free(buffer); // cleanup SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` 在代码中,我们首先创建一个 SDL 窗口和渲染器。然后,使用 SDL_CreateTexture 函数创建一个 YV12 格式的纹理。接下来,读取 YUV 文件,并将数据设置到纹理中。在主循环中,使用 SDL_RenderCopy 函数将纹理渲染到屏幕上。最后,记得在程序结束前释放所有内存和资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值