SDL2.0-播放YUV文件

本文详细介绍了使用SDL库进行音视频渲染的基本流程,包括初始化、创建窗口、渲染器及纹理,以及如何设置纹理数据并将其渲染到窗口。同时,文章提供了代码示例,展示了如何从YUV文件中读取数据并实时渲染。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 基本流程

  1. 初始化
    • 初始化SDL
    • 创建窗口
    • 创建渲染器
    • 创建纹理
  2. 渲染
    • 设置纹理的数据
    • 绘制要显示的内容
    • 将缓冲区的内容渲染到窗口,显示
  3. 清理
    • 销毁渲染器
    • 销毁窗口
    • 退出SDL

2. API介绍

  • SDL_Init 初始化

    int 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_NOPARACHUTE:    不捕获关键信号(这个不理解)
    SDL_INIT_EVERYTHING:    包含上述所有选项
    
  • SDL_CreateWindow 创建视频播放的窗口

    SDL_Window* SDL_CreateWindow(const char *title,
                                 int x, int y, int w,
                                 int h, Uint32 flags);
    
    title:窗口标题
    x,y:窗口x,y坐标,可以设置为SDL_WINDOWPOS_UNDEFINED
    w,h:窗口宽高
    flags:
    SDL_WINDOW_FULLSCREEN,        //全屏        
    SDL_WINDOW_OPENGL,            //使用OpenGL上下文
    SDL_WINDOW_HIDDEN,             //窗口不可见
    SDL_WINDOW_BORDERLESS,         //无边框
    SDL_WINDOW_RESIZABLE,        //窗口大小可变
    SDL_WINDOW_MAXIMIZED,         //窗口最大化
    SDL_WINDOW_MINIMIZED,        //窗口最小化    
    SDL_WINDOW_INPUT_GRABBED,    //输入捕获
    
  • SDL_CreateRenderer 基于窗口创建渲染器

    SDL_Renderer* SDL_CreateRenderer(SDL_Window* window,
                                     int index,
                                     Uint32 flags)
    
    window: 指明在哪个窗口里进行渲染
    index: 指定渲染驱动的索引号。-1:初始化默认的渲染设备
    flags:
    SDL_RENDERER_SOFTWARE         //使用软件渲染
    SDL_RENDERER_ACCELERATED     //硬件加速
    SDL_RENDERER_PRESENTVSYNC     //和显示器的刷新率同步
    SDL_RENDERER_TARGETTEXTURE     //支持渲染纹理
    
    返回值:返回创建完成的渲染器的ID。如果创建失败则返回NULL。
    
  • SDL_CreateTexture 基于渲染器创建一个纹理

    SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
                                            Uint32 format,
                                            int access,
                                            int w,
                                            int h);
    renderer:目标渲染器
    format:纹理的格式
    access:
    SDL_TEXTUREACCESS_STATIC :        变化极少
    SDL_TEXTUREACCESS_STREAMING :    变化频繁
    SDL_TEXTUREACCESS_TARGET :        ???
    
  • SDL_UpdateTexture 设置纹理数据

    int SDLCALL SDL_UpdateTexture(    SDL_Texture * texture,
                                    const SDL_Rect * rect,
                                    const void *pixels, int pitch);
    
    texture     目标纹理
    rec            更新像素的矩阵区域,NULL:更新整个区域
    pixels        像素数据
    返回值:成功返回0,失败返回-1
    
  • SDL_RenderCopy将纹理数据复制给渲染目标

    int SDLCALL SDL_RenderCopy(
                            SDL_Renderer * renderer,
                            SDL_Texture * texture,
                            const SDL_Rect * srcrect,
                            const SDL_Rect * dstrect);
    
    renderer    渲染目标
    texture        输入纹理
    srcrect        选择输入纹理的块矩形区域作为输入,NULL:整个纹理作为输入
    dstrect        选择渲染目标的块矩形区域作为输出,NULL:整个渲染目标作为输出
    返回值:成功返回0,失败返回-1
    
  • SDL_RenderPresent 显示纹理到窗口

    void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);
    
    renderer    指定渲染器
    

3. Demo

#include "SDLHeader.h"
#include <string>
#include <fstream>
#include <iostream>
#include <memory>
#include <iostream>
 
//生成yuv数据
//ffmpeg.exe -i zgr.flv  -f rawvideo -video_size 852x480 -pixel_format yuv420p -y zgr_852x480_yuv420p.yuv
//ffplay播放
//ffplay.exe -i zgr_852x480_yuv420p.yuv -video_size 852x480 -pixel_format yuv420p
const static std::string yuv_path = "D:\\vod\\testvideo\\zgr_852x480_yuv420p.yuv";
const static int width = 852;
const static int height = 480;
const static Uint32 pix_fmt = SDL_PIXELFORMAT_IYUV;     //YUV420
const static uint32_t frame_size = width * height * 3 / 2;
 
int ReadYUV(std::fstream& yuv);
void RenderYUV(SDL_Texture* texture, SDL_Renderer* renderer);
void TextureYUV(SDL_Texture* texture, std::shared_ptr<char> yuv_data_sp);
 
int main(int argc, char** argv)
{
    bool quit = false;
    SDL_Window* window = nullptr;
    SDL_Renderer* renderer = nullptr;
    SDL_Texture* texture = nullptr;
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) //init
        return -1;
    window = SDL_CreateWindow(
        "Show YUV",
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        width,
        height,
        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
    );
    if (!window)
        return -1;
 
    renderer = SDL_CreateRenderer(window, -1, 0);
    if (!renderer)
        return -1;
    texture = SDL_CreateTexture(
        renderer,
        pix_fmt,
        SDL_TEXTUREACCESS_STREAMING,
        width,
        height
    );
    if (!texture)
        return -1;
 
    RenderYUV(texture, renderer);
 
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 
int ReadYUV(std::fstream& yuv)
{
    if (yuv.is_open())
        yuv.close();
    yuv.open(yuv_path.c_str(), std::ios::in | std::ios::binary);
    if (!yuv.is_open())
    {
        throw std::logic_error("Open file error " + yuv_path);
        return -1;
    }
 
    return 0;
}
 
void RenderYUV(SDL_Texture* texture, SDL_Renderer* renderer)
{
    std::fstream yuv;
    if (ReadYUV(yuv) != 0)
        return;
    //用于存放YUV数据
    std::shared_ptr<char> yuv_data_sp(new char[frame_size],
        [](char* p)
        {
            std::cout << "Delete frame data" << std::endl;
            delete[] p;
        }
    );
    bool quit = false;
    SDL_Event event;
    SDL_Rect rect;
    while (!quit && !yuv.eof())
    {
        SDL_PollEvent(&event);
        switch (event.type)
        {
        case SDL_QUIT:
            SDL_Log("quit");
            quit = true;
            break;      
        }
        //读取一帧图像
        yuv.read(yuv_data_sp.get(), frame_size);
        if (yuv.bad())
        {
            throw std::runtime_error("Read yuv data");
            break;
        }
 
        //要绘制的区域
        rect.x = 0;
        rect.y = 0;
        rect.w = width;
        rect.h = height;
 
        //直接渲染数据
        SDL_UpdateTexture(texture, NULL, yuv_data_sp.get(), width);
 
        //自己填充texture
        //TextureYUV(texture, yuv_data_sp);
 
        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, nullptr, &rect);
        SDL_RenderPresent(renderer);                    //输出到目标窗口
        SDL_Delay(40);
    }
}
 
void TextureYUV(SDL_Texture* texture, std::shared_ptr<char> yuv_data_sp)
{
    void* pixel = nullptr;
    int pitch = 0;
    if (SDL_LockTexture(texture, nullptr, &pixel, &pitch) == 0)
    {
        if (pitch == width)
            memcpy(pixel, yuv_data_sp.get(), frame_size);
        else
        {
            int h = height;
            int w = width;
            //Copy all data to dst
            uint8_t* dst = reinterpret_cast<uint8_t*>(pixel);
            uint8_t* src = reinterpret_cast<uint8_t*>(yuv_data_sp.get());
            //Copy Y
            for (int i = 0; i < h; i++)
            {
                memcpy(dst, src, w);
                dst += pitch;
                src += w;
            }
 
            h >>= 1;
            w >>= 1;
            pitch >>= 1;
 
            //Copy U
            for (int i = 0; i < h; i++)
            {
                memcpy(dst, src, w);
                dst += pitch;
                src += w;
            }
 
            //Copy V
            for (int i = 0; i < h; i++)
            {
                memcpy(dst, src, w);
                dst += pitch;
                src += w;
            }
        }
    }
 
    SDL_UnlockTexture(texture);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值