zoukankan      html  css  js  c++  java
  • Beginning SDL 2.0(4) YUV加载及渲染

    本文主要内容是基于的“Beginning SDL 2.0(3) SDL介绍及BMP渲染”(以下简称BS3)基础上,将BMP加载及渲染修改为YUV420或I420的原始视频格式。阅读完本部分内容相信你可以对SDL2.0中的Renderer机制(支持硬件加速)有一定了解,并可以直接加载显示YUV数据。

    SDL 2.0新的支持硬件加速的视频渲染技术——Render

    SDL 1.x的版本的视频渲染技术是基于纯软件实现的,效率相对较差,通常很难满足实时渲染的需求。SDL 2.0之后针对这个问题,提供了新的2D加速渲染方案——Renderer。并提供了基于以下类型的机制:

    • 单个像素点(point)
    • 直线(line)
    • 填充矩形(rectangle)
    • 纹理图像(texture image)

    为了应用新的渲染技术,我们需要修改下BS3中提供的SDLVideoRender基类,主要是添加SDL_Renderer成员函数:

    SDL_Renderer * m_sdl_renderer;

    在实现文件中修改SDLVideoRender::Init函数中添加初始化代码,用于创建SDL_Renderer对象

    m_sdl_renderer = SDL_CreateRenderer(m_sdl_window, -1, SDL_RENDERER_ACCELERATED);
    if (nullptr == m_sdl_renderer)
    {
        return false;
    }

    参数具体函数可以参考SDL_CreateRenderer。(SDL官网上的函数介绍)。

    同时在void SDLVideoRender::Deinit()函数中添加反初始化代码,用于销毁SDL_Renderer对象

        if (nullptr != m_sdl_renderer)
        {
            SDL_DestroyRenderer(m_sdl_renderer);
            m_sdl_renderer = nullptr;
        }

    YUV图像渲染

     首先我们使用YuvRender类来说明,如何实现YUV图像加载及渲染的方法。其定义如下:

    #pragma once
    #include "sdlvideorender.h"
    
    class YuvRender :public SDLVideoRender
    {
    public:
        YuvRender(void);
        ~YuvRender(void);
    
        bool Init(HWND show_wnd, RECT show_rect);
        void Deinit();
    
        // width x height resolution
        // data[] for YUV, stride is linesize of each raw
        void Update(int width, int height, unsigned char *data[3], int stride[3]){}
        bool Render();
    
    private:
        void FillTexture1();
        void FillTexture2();
    
    private:
        int m_yuv_width;
        int m_yuv_height;
        int m_yuv_frame_size;
        unsigned char * m_yuv_data;
    
        SDL_Texture * m_show_texture;
    };

     这里假定要加载的YUV图像分辨率为720x576,构造函数代码如下:

    YuvRender::YuvRender(void)
        : SDLVideoRender()
        , m_yuv_width(720), m_yuv_height(576)
        , m_yuv_frame_size(0)
        , m_yuv_data(nullptr)
        , m_show_texture(nullptr)
    {}
    View Code

    Init函数的实现是,先创建SDL_Texture,之后从创建YUV缓冲,并从文件中加载图像数据。代码如下

    bool YuvRender::Init(HWND show_wnd, RECT show_rect)
    {
        if (!SDLVideoRender::Init(show_wnd, show_rect))
            return false;
    
        // create texture
        m_show_texture = SDL_CreateTexture(m_sdl_renderer, SDL_PIXELFORMAT_IYUV, 
            SDL_TEXTUREACCESS_STREAMING, m_yuv_width, m_yuv_height);
        if (nullptr == m_show_texture)
        {
            return false;
        }
    
        // alloc buffer and read yuv file
        m_yuv_frame_size = m_yuv_width * m_yuv_height * 3 >> 1;
        m_yuv_data = new unsigned char[m_yuv_frame_size];
        FILE * fin = NULL;
        if (0 != fopen_s(&fin, "test_720x576.yuv", "rb"))
        {
            return false;
        }
    
        fread(m_yuv_data, 1, m_yuv_frame_size, fin);
    
        fclose(fin);
        
        return true;
    }
    注意创建SDL_Texture时使用SDL_TEXTUREACCESS_STREAMING表示我们希望创建的Texture可以频繁的更新,数据能够放到内存和缓存都可以访问的地方。
    Deinit函数需要将Init创建的额外资源释放掉。
    void YuvRender::Deinit()
    {
        if (nullptr != m_yuv_data)
        {
            delete [] m_yuv_data;
            m_yuv_data = nullptr;
        }
    
        if (nullptr != m_show_texture)
        {
            SDL_DestroyTexture(m_show_texture);
            m_show_texture = NULL;
        }
    
        SDLVideoRender::Deinit();
    }
    View Code

    Render渲染函数实现很简单,先填充Texture,之后将Texture渲染到renderer中,就可以提交系统刷新了。代码如下:

    bool YuvRender::Render()
    {
        if (NULL != m_show_texture)
        {
            //FillTexture1();
            FillTexture2();
    
            SDL_RenderCopy(m_sdl_renderer, m_show_texture, NULL, &m_show_rect);    
            SDL_RenderPresent(m_sdl_renderer); 
        }
    
        return true;
    }

    YUV数据拷贝及SDL_Texture的更新

    上一节中没有说明FillTexture函数的实现。这个函数主要实现将内存中的YUV数据填充到SDL_Texture中。SDL_Texture中的数据存储形式是由创建时第二个参数决定的,SDL_PIXELFORMAT_IYUV(表示I420,常用的planer YUV420格式之一,Y、1/4U、1/4V)。

    先看下FillTexture1函数的实现

    void YuvRender::FillTexture1()
    {
        // This is a fairly slow function, intended for use with static textures that do not change often
        SDL_UpdateTexture(m_show_texture, NULL, m_yuv_data, m_yuv_width);
    }

    从SDL官网上可以看到SDL_UpdateTexture可以直接刷新Texture,但是效率上很难保证。

    所以FillTexture2应该是我们在常规视频渲染中使用的机制,其代码如下

    void YuvRender::FillTexture2()
    {
        void * pixel = NULL;
        int pitch = 0;
        if(0 == SDL_LockTexture(m_show_texture, NULL, &pixel, &pitch))
        {
            // 如果不考虑数据对齐,直接拷贝YUV数据是没有问题的
            if (pitch == m_yuv_width)
            {
                memcpy(pixel, m_yuv_data, m_yuv_frame_size);
            }
            else // 可能发生pitch > width的情况
            {
                // 如果有数据对齐的情况,单独拷贝每一行数据
                // for Y
                int h = m_yuv_height;
                int w = m_yuv_width;
                unsigned char * dst = reinterpret_cast<unsigned char *>(pixel);
                unsigned char * src = m_yuv_data;
                for (int i = 0; i < h; ++i)
                {
                    memcpy(dst, src, w);
                    dst += pitch;
                    src += w;
                }
    
                h >>= 1;
                w >>= 1;
                pitch >>= 1;
                // for U
                for (int i = 0; i < h; ++i)
                {
                    memcpy(dst, src, w);
                    dst += pitch;
                    src += w;
                }
    
                // for V
                for (int i = 0; i < h; ++i)
                {
                    memcpy(dst, src, w);
                    dst += pitch;
                    src += w;
                }
            }
    
            SDL_UnlockTexture(m_show_texture);
        }
    }

    这里使用了LockTexture/UnlockTexture的机制。至于SDL_texture的实际内存布局跟Direct 3D的surface类似。

    YUV图像渲染验证

    我们用vs2010创建基于win32的工程1_Win32_yuv_render,并按照BS3中的修改,将YuvRender提供的接口添加到工程中。

    YUV的原图如下:

    程序运行后的效果图如下:(注意YUV做了拉伸和显示区域缩减)

    相关代码可以从我的git下载,url如下:https://git.oschina.net/Tocy/SampleCode.git,位于TocySDL2VisualTutorial目录下。

  • 相关阅读:
    Selector + 线程池 遇到的问题
    【转】Android TabActivity无法正常bindService解决方法
    Android 中的 Service 全面总结
    【转】IT 圈里有哪些经常被读错的词?
    【转】线程的7种状态及相互转换
    【eoeandroid 特刊】第117期打包网盘下载地址
    使用 Android 自带的 proguard 混淆源码
    Google+ 连接不上的解决办法
    【转】AsyncTask的用法
    winForm简单数据绑定
  • 原文地址:https://www.cnblogs.com/tocy/p/Beginning-SDL2-4-YUV-Render.html
Copyright © 2011-2022 走看看