记录 SDL硬件渲染(AVFrame)

说明

记录一下SDL方式来渲染图像的操作。这里封装成了类,导入SDL库后,复制代码实例化对象直接可用
SDL是跨平台的多媒体开发库,封装了不同系统中复杂的底层渲染细节,提供了统一的接口,极大提升了开发效率。音视频开发肯定离不开FFmpeg,而FFmpeg解码后的图像数据一般都是包含AVFrame中(主要为yuv420p格式)。所以对于解码后的AVFrame数据,直接调用SDL相关接口即可实现图像显示,简单便捷。另外还有很重要的一点是,SDL可选择硬件渲染,对于多路视频将极大减少CPU的占用

代码

已经封装成了类,具体逻辑不复杂,直接看代码即可。需要注意以下两点:

  1. 使用SDK库需要开始运行时先初始化库,程序运行结束后释放库。SDL分为多个子系统,如音频、视频、事件、定时器等,这里是视频即先 SDL_Init(SDL_INIT_VIDEO); 结束后 SDL_Quit();
  2. Qt 环境开发,显示的窗口句柄通过winId()方法获得,格式为WId。其它环境只要得到正确的窗口句柄是一样的,SDL实际需要的是(void *)的一个句柄,自行修改转换即可。

头文件

//SDL渲染类
class DisplaySDL
{
public:
    DisplaySDL();
    ~DisplaySDL();

    //只显示AVFrame 不负责释放
    void display(AVFrame *videoFrame);

    //初始化参数 窗口句柄、视频长、宽
    bool initParm(WId showId, int width, int height);
    //释放
    void release();

public:
	//用到的相关变量
    SDL_Window *sdlWindow;
    SDL_Renderer *sdlRender;
    SDL_Texture *sdlTexture;
    
    //显示的句柄
    WId showId;
};

实现文件

DisplaySDL::DisplaySDL()
{
    sdlWindow = NULL;
    sdlRender = NULL;
    sdlTexture = NULL;
    showId = NULL;
}

DisplaySDL::~DisplaySDL()
{
    release();
}

void DisplaySDL::display(AVFrame *videoFrame)
{
    // QWidget尺寸变化,那么SDL渲染的窗口也会跟着变化。
    SDL_Surface *sdlSurface = SDL_GetWindowSurface(sdlWindow);
    if (!sdlSurface)
    {
        qDebug() << "SDL_GetWindowSurface fail";
        return;
    }

    SDL_Rect rect;
    rect.x = 0;
    rect.y = 0;
    rect.w = sdlSurface->w;
    rect.h = sdlSurface->h;

    int ret = SDL_UpdateYUVTexture(sdlTexture, NULL, videoFrame->data[0], videoFrame->linesize[0], videoFrame->data[1], videoFrame->linesize[1], videoFrame->data[2], videoFrame->linesize[2]);
    if (ret != 0)
        qDebug() << "SDL_UpdateYUVTexture fail";

    // 渲染
    SDL_RenderClear(sdlRender);
    SDL_RenderCopy(sdlRender, sdlTexture, NULL, &rect);
    SDL_RenderPresent(sdlRender);
}

bool DisplaySDL::initParm(WId showId, int width, int height)
{
    if(this->showId == showId)
        return true;
    else
        release();

    this->showId = showId;
    sdlWindow = SDL_CreateWindowFrom((void *)showId);
    if(!sdlWindow)
    {
        qDebug() << "SDL_CreateWindowFrom error";
        return false;
    }

    sdlRender = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED); //SDL_RENDERER_SOFTWARE
    if (!sdlRender)
    {
        qDebug() << "SDL_CreateRenderer error";
        return false;
    }

    sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
    if (!sdlTexture)
    {
        qDebug() << "SDL_CreateRenderer error";
        return false;
    }

    return true;
}

void DisplaySDL::release()
{
    if(sdlTexture)
    {
        SDL_DestroyTexture(sdlTexture);
        sdlTexture = NULL;
    }

    if(sdlRender)
    {
        SDL_DestroyRenderer(sdlRender);
        sdlRender = NULL;
    }

    if(sdlWindow)
    {
        SDL_DestroyWindow(sdlWindow);
        sdlWindow = NULL;
    }
    
    showId = NULL;
}

你可能感兴趣的:(音视频流媒体,ffmpeg,实时音视频)