使用SDL播放RGB24文件

一、概述

  在音视频处理领域,SDL(Simple DirectMedia Layer)是一个功能强大且跨平台的多媒体开发库,能轻松实现视频播放、渲染等功能。本文聚焦于使用 SDL 播放 RGB24 文件,详细阐述从文件准备、SDL 初始化、窗口与渲染器创建,到 RGB24 数据读取、纹理生成与渲染的完整流程。并给出在实际测试中遇到的问题与解决方案。

  注意事项:

    在用SDL做渲染的时候像素格式一定要对的上。例如:你的文件xxx.rgb是rgb24格式的,那么SDL在创建材质的时候也必须指定SDL_PIXELFORMAT_RGB24。如果这两者不对应则会出现视频画面无法显示或者黑白图的问题。

二、代码实例

  1.使用ffmpeg工具生成rgb数据,命令如下:

ffmpeg -i original1080.mp4 -s 400x300 -pix_fmt rgb24 output400_300.rgb

  使用ffmpeg测试生成的数据是否正常,命令如下:

ffplay -f rawvideo -video_size 400x300 -pixel_format rgb24 -framerate 30 output400_300.rgb

  

  2.封装文件读取类BinaryFileReader.h

class BinaryFileReader {

public:
    //视频的宽高
    int width = 400;
    int height = 300;
    //文件名
    string filename;
    //
    ifstream in;
public:
    bool Init()
    {
        Close();
        in.open(filename, std::ios::binary);
        if (!in.is_open())
        {
            qDebug() << "无法打开文件";
            return false;
        }
        return true;
    }
    bool Close()
    {
        if (in) {
            in.close();
        }
        return true;
    }
    //空间需要外部申请
    bool ReadRGB24(char* data)
    {
        int dataSize = width * height * 3;
        in.read(data, dataSize);
        if (in.gcount() == 0) {
            // 未读取任何字节:可能已到文件结尾或出现错误
            if (in.eof()) {
                qDebug() << "已到达文件结尾";
                return false;
            }
            else if (in.fail()) {
                qDebug() << "读取失败(非EOF原因)";
                return false;
            }
        }
        return true;
    }
    static BinaryFileReader* Get()
    {
        static BinaryFileReader reader;
        return &reader;
    }
protected:
    BinaryFileReader() {};
    ~BinaryFileReader() {}
};

  

  3.封装SDL工具类

class SDLRenderUtil {
private:
    SDL_Window* sdlWindow;
    SDL_Renderer* sdlRender;
    SDL_Texture* sdlTexture;
public:
    int width = 400;
    int height = 300;
public:
    void Init() {
        Close();
        //初始化SDL库
        if (SDL_Init(SDL_INIT_VIDEO)) {
            qDebug() << "SDL初始化失败->" << SDL_GetError();
            return;
        }
        //2 生成SDL 窗口
        sdlWindow = SDL_CreateWindow("SDL窗口",
            SDL_WINDOWPOS_CENTERED,//窗口位置
            SDL_WINDOWPOS_CENTERED,
            width, height,
            SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
        );
        if (!sdlWindow)
        {
            qDebug() << "SDL窗口创建失败->" << SDL_GetError();
            return;
        }

        //3 生成渲染器
        sdlRender = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED);
        if (!sdlRender)
        {
            qDebug() << "SDL渲染器创建失败->" << SDL_GetError();
            return;
        }
        //4 生成材质
        //SDL_PIXELFORMAT_ARGB8888
        sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_RGB24,
            SDL_TEXTUREACCESS_STREAMING,// 可加锁
            width, height
        );
        if (!sdlTexture)
        {
            qDebug() << "SDL材质创建失败->" << SDL_GetError() << endl;
            return;
        }

    }

    void Render(char* data)
    {
        //5 内存数据写入材质
        SDL_UpdateTexture(sdlTexture, NULL, data, width * 3);
        //6 清理屏幕
        SDL_RenderClear(sdlRender);
        SDL_Rect sdl_rect;
        sdl_rect.x = 0;
        sdl_rect.y = 0;
        sdl_rect.w = width;
        sdl_rect.h = height;

        //7 复制材质到渲染器
        SDL_RenderCopy(sdlRender, sdlTexture,
            NULL,//原图位置和尺寸
            &sdl_rect//目标位置和尺寸
        );
        //8 渲染
        SDL_RenderPresent(sdlRender);
    }

    //初始化SDL事件
    bool isEventQuit()
    {
        //判断退出
        SDL_Event ev;
        SDL_WaitEventTimeout(&ev, 10);
        if (ev.type == SDL_QUIT)
        {
            Close();
            return true;
        }
        return false;
    }

    void Close()
    {
        if (sdlTexture) {
            SDL_DestroyTexture(sdlTexture);
            sdlTexture = NULL;
        }
        if (sdlRender)
        {
            SDL_DestroyRenderer(sdlRender);
            sdlRender = NULL;
        }
        if (sdlWindow) {
            SDL_DestroyWindow(sdlWindow);
            sdlWindow = NULL;
        }
    }
    static SDLRenderUtil* Get()
    {
        static SDLRenderUtil sdlRenderUtil;
        return &sdlRenderUtil;
    }
};

  4.使用工具类读取文件并渲染窗口

BinaryFileReader::Get()->filename = filePath.toStdString();
BinaryFileReader::Get()->width = mWidth;
BinaryFileReader::Get()->height = mHeight;
BinaryFileReader::Get()->Init();

SDLRenderUtil::Get()->width = mWidth;
SDLRenderUtil::Get()->height = mHeight;
SDLRenderUtil::Get()->Init();

std::thread renderThread([=] {
    char* data = new  char[mWidth * mHeight * 3];
    for (;;) {
        SDLRenderUtil::Get()->isEventQuit();
        bool ret = BinaryFileReader::Get()->ReadRGB24(data);
        if (!ret) {
            break;
        }
        SDLRenderUtil::Get()->Render(data);
        std::this_thread::sleep_for(33ms);
    }
    delete data;
    });
renderThread.detach();//线程分离

  5.测试效果

 

posted on 2025-05-12 17:51  飘杨......  阅读(59)  评论(0)    收藏  举报