SDL2.0-播放YUV文件

文章目录

  • 1. 基本流程

  • 2. `API`介绍

  • 3. Demo

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    指定渲染器

 本文福利,免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓

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);
}

原文链接:SDL2.0-播放YUV文件 - 资料 - 我爱音视频网 - 构建全国最权威的音视频技术交流分享论坛 

本文福利,免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓ 

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/125605282