SDL2播放视频(RGB/YUV)

原文: https://blog.csdn.net/leixiaohua1020/article/details/40525591

SDL播放视频的流程

1. 初始化

 1) 初始化SDL
 2) 创建窗口(Window)
 3) 基于窗口创建渲染器(Render)
 4) 创建纹理(Texture)

2. 循环显示画面

 1) 设置纹理的数据
 2) 纹理复制给渲染目标
 3) 显示

下面详细分析一下上文的流程。

1. 初始化
1) 初始化SDL

使用SDL_Init()初始化SDL。该函数可以确定希望激活的子系统。SDL_Init()函数原型如下:

int SDLCALL 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_Init()有一点需要注意:初始化的时候尽量做到“够用就好”,而不要用SDL_INIT_EVERYTHING。因为有些情况下使用SDL_INIT_EVERYTHING会出现一些不可预知的问题。例如,在MFC应用程序中播放纯音频,如果初始化SDL的时候使用SDL_INIT_EVERYTHING,那么就会出现听不到声音的情况。后来发现,去掉了SDL_INIT_VIDEO之后,问题才得以解决。

2) 创建窗口(Window)

使用SDL_CreateWindow()创建一个用于视频播放的窗口。SDL_CreateWindow()的原型如下。

SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
                                      int x, int y, int w,
                                      int h, Uint32 flags);

参数含义如下。

title   :窗口标题
x       :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTEREDSDL_WINDOWPOS_UNDEFINEDy       :窗口位置y坐标。同上。
w       :窗口的宽
h       :窗口的高
flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。
       ::SDL_WINDOW_FULLSCREEN,    ::SDL_WINDOW_OPENGL,
       ::SDL_WINDOW_HIDDEN,        ::SDL_WINDOW_BORDERLESS,
       ::SDL_WINDOW_RESIZABLE,     ::SDL_WINDOW_MAXIMIZED,
       ::SDL_WINDOW_MINIMIZED,     ::SDL_WINDOW_INPUT_GRABBED,
       ::SDL_WINDOW_ALLOW_HIGHDPI.

返回创建完成的窗口的ID。如果创建失败则返回0。

3) 基于窗口创建渲染器(Render)

使用SDL_CreateRenderer()基于窗口创建渲染器。SDL_CreateRenderer()原型如下。

SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,
                                          int index, Uint32 flags);

参数含义如下。
window : 渲染的目标窗口。
index :打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备。
flags :支持以下值(位于SDL_RendererFlags定义中)

SDL_RENDERER_SOFTWARE :使用软件渲染
SDL_RENDERER_ACCELERATED :使用硬件加速
SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步
SDL_RENDERER_TARGETTEXTURE :不太懂

返回创建完成的渲染器的ID。如果创建失败则返回NULL。

4) 创建纹理(Texture)

使用SDL_CreateTexture()基于渲染器创建一个纹理。SDL_CreateTexture()的原型如下。

SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
                                        Uint32 format,
                                        int access, int w,
                                        int h);

参数的含义如下。
renderer:目标渲染器。
format :纹理的格式。后面会详述。
access :可以取以下值(定义位于SDL_TextureAccess中)

    SDL_TEXTUREACCESS_STATIC         :变化极少
    SDL_TEXTUREACCESS_STREAMING      :变化频繁
    SDL_TEXTUREACCESS_TARGET       :暂时没有理解

w :纹理的宽
h :纹理的高
创建成功则返回纹理的ID,失败返回0。

在纹理的创建过程中,需要指定纹理的格式(即第二个参数)。SDL的中的格式很多,如下所列。

    SDL_PIXELFORMAT_UNKNOWN,
    SDL_PIXELFORMAT_INDEX1LSB =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_4321, 0,
                               1, 0),
    SDL_PIXELFORMAT_INDEX1MSB =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_1234, 0,
                               1, 0),
    SDL_PIXELFORMAT_INDEX4LSB =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_4321, 0,
                               4, 0),
    SDL_PIXELFORMAT_INDEX4MSB =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_1234, 0,
                               4, 0),
    SDL_PIXELFORMAT_INDEX8 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1),
    SDL_PIXELFORMAT_RGB332 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED8, SDL_PACKEDORDER_XRGB,
                               SDL_PACKEDLAYOUT_332, 8, 1),
    SDL_PIXELFORMAT_RGB444 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
                               SDL_PACKEDLAYOUT_4444, 12, 2),
    SDL_PIXELFORMAT_RGB555 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
                               SDL_PACKEDLAYOUT_1555, 15, 2),
    SDL_PIXELFORMAT_BGR555 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,
                               SDL_PACKEDLAYOUT_1555, 15, 2),
    SDL_PIXELFORMAT_ARGB4444 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,
                               SDL_PACKEDLAYOUT_4444, 16, 2),
    SDL_PIXELFORMAT_RGBA4444 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,
                               SDL_PACKEDLAYOUT_4444, 16, 2),
    SDL_PIXELFORMAT_ABGR4444 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,
                               SDL_PACKEDLAYOUT_4444, 16, 2),
    SDL_PIXELFORMAT_BGRA4444 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,
                               SDL_PACKEDLAYOUT_4444, 16, 2),
    SDL_PIXELFORMAT_ARGB1555 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,
                               SDL_PACKEDLAYOUT_1555, 16, 2),
    SDL_PIXELFORMAT_RGBA5551 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,
                               SDL_PACKEDLAYOUT_5551, 16, 2),
    SDL_PIXELFORMAT_ABGR1555 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,
                               SDL_PACKEDLAYOUT_1555, 16, 2),
    SDL_PIXELFORMAT_BGRA5551 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,
                               SDL_PACKEDLAYOUT_5551, 16, 2),
    SDL_PIXELFORMAT_RGB565 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
                               SDL_PACKEDLAYOUT_565, 16, 2),
    SDL_PIXELFORMAT_BGR565 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,
                               SDL_PACKEDLAYOUT_565, 16, 2),
    SDL_PIXELFORMAT_RGB24 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_RGB, 0,
                               24, 3),
    SDL_PIXELFORMAT_BGR24 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_BGR, 0,
                               24, 3),
    SDL_PIXELFORMAT_RGB888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB,
                               SDL_PACKEDLAYOUT_8888, 24, 4),
    SDL_PIXELFORMAT_RGBX8888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBX,
                               SDL_PACKEDLAYOUT_8888, 24, 4),
    SDL_PIXELFORMAT_BGR888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XBGR,
                               SDL_PACKEDLAYOUT_8888, 24, 4),
    SDL_PIXELFORMAT_BGRX8888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRX,
                               SDL_PACKEDLAYOUT_8888, 24, 4),
    SDL_PIXELFORMAT_ARGB8888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
                               SDL_PACKEDLAYOUT_8888, 32, 4),
    SDL_PIXELFORMAT_RGBA8888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA,
                               SDL_PACKEDLAYOUT_8888, 32, 4),
    SDL_PIXELFORMAT_ABGR8888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR,
                               SDL_PACKEDLAYOUT_8888, 32, 4),
    SDL_PIXELFORMAT_BGRA8888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRA,
                               SDL_PACKEDLAYOUT_8888, 32, 4),
    SDL_PIXELFORMAT_ARGB2101010 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
                               SDL_PACKEDLAYOUT_2101010, 32, 4),

    SDL_PIXELFORMAT_YV12 =      /**< Planar mode: Y + V + U  (3 planes) */
        SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'),
    SDL_PIXELFORMAT_IYUV =      /**< Planar mode: Y + U + V  (3 planes) */
        SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'),
    SDL_PIXELFORMAT_YUY2 =      /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
        SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'),
    SDL_PIXELFORMAT_UYVY =      /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
        SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'),
    SDL_PIXELFORMAT_YVYU =      /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */
        SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U')

这一看确实给人一种“眼花缭乱”的感觉。简单分析一下其中的定义吧。例如ARGB8888的定义如下。

SDL_PIXELFORMAT_ARGB8888 =
        SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
                               SDL_PACKEDLAYOUT_8888, 32, 4),

其中用了一个宏SDL_DEFINE_PIXELFORMAT用于将几种属性合并到一个格式中。下面我们看看一个格式都包含哪些属性:
SDL_PIXELTYPE_PACKED32:代表了像素分量的存储方式。PACKED代表了像素的几个分量是一起存储的,内存中存储方式如下:R1|G1|B1,R2|G2|B2…;ARRAY则代表了像素的几个分量是分开存储的,内存中存储方式如下:R1|R2|R3…,G1|G2|G3…,B1|B2|B3…
SDL_PACKEDORDER_ARGB:代表了PACKED存储方式下像素分量的顺序。注意,这里所说的顺序涉及到了一个“大端”和“小端”的问题。这个问题在《最简单的视音频播放示例2:GDI播放YUV, RGB》中已经叙述,不再重复记录。对于Windows这样的“小端”系统,“ARGB”格式在内存中的存储顺序是B|G|R|A。
SDL_PACKEDLAYOUT_8888:说明了每个分量占据的比特数。例如ARGB格式每个分量分别占据了8bit。
32:每个像素占用的比特数。例如ARGB格式占用了32bit(每个分量占据8bit)。
4:每个像素占用的字节数。例如ARGB格式占用了4Byte(每个分量占据1Byte)。

2. 循环显示画面
1) 设置纹理的数据

使用SDL_UpdateTexture()设置纹理的像素数据。SDL_UpdateTexture()的原型如下。

int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,
                              const SDL_Rect * rect,
                              const void *pixels, int pitch);

参数的含义如下。
texture:目标纹理。
rect:更新像素的矩形区域。设置为NULL的时候更新整个区域。
pixels:像素数据。
pitch:一行像素数据的字节数。
成功的话返回0,失败的话返回-1。

2) 纹理复制给渲染目标

使用SDL_RenderCopy()将纹理数据复制给渲染目标。在使用SDL_RenderCopy()之前,可以使用SDL_RenderClear()先使用清空渲染目标。实际上视频播放的时候不使用SDL_RenderClear()也是可以的,因为视频的后一帧会完全覆盖前一帧。
SDL_RenderClear()原型如下。

int SDLCALL SDL_RenderClear(SDL_Renderer * renderer);

参数renderer用于指定渲染目标。
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。

3) 显示

使用SDL_RenderPresent()显示画面。SDL_RenderPresent()原型如下。

void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);

参数renderer用于指定渲染目标。

流程总结

使用SDL播放视频的流程可以概括为下图。
这里写图片描述
SDL中几个关键的结构体之间的关系可以用下图概述。
这里写图片描述
简单解释一下各变量的作用:
SDL_Window就是使用SDL的时候弹出的那个窗口。在SDL1.x版本中,只可以创建一个一个窗口。在SDL2.0版本中,可以创建多个窗口。
SDL_Texture用于显示YUV数据。一个SDL_Texture对应一帧YUV数据。
SDL_Renderer用于渲染SDL_Texture至SDL_Window。
SDL_Rect用于确定SDL_Texture显示的位置。

代码

贴出源代码。

/**
 * 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
 * Simplest Video Play SDL2 (SDL2 play RGB/YUV) 
 *
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图
 * API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层
 * API。
 *
 * 函数调用步骤如下: 
 *
 * [初始化]
 * SDL_Init(): 初始化SDL。
 * SDL_CreateWindow(): 创建窗口(Window)。
 * SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。
 * SDL_CreateTexture(): 创建纹理(Texture)。
 *
 * [循环渲染数据]
 * SDL_UpdateTexture(): 设置纹理的数据。
 * SDL_RenderCopy(): 纹理复制给渲染器。
 * SDL_RenderPresent(): 显示。
 *
 * This software plays RGB/YUV raw video data using SDL2.
 * SDL is a wrapper of low-level API (Direct3D, OpenGL).
 * Use SDL is much easier than directly call these low-level API.  
 *
 * The process is shown as follows:
 *
 * [Init]
 * SDL_Init(): Init SDL.
 * SDL_CreateWindow(): Create a Window.
 * SDL_CreateRenderer(): Create a Render.
 * SDL_CreateTexture(): Create a Texture.
 *
 * [Loop to Render data]
 * SDL_UpdateTexture(): Set Texture's data.
 * SDL_RenderCopy(): Copy Texture to Render.
 * SDL_RenderPresent(): Show.
 */

#include <stdio.h>

extern "C"
{
#include "sdl/SDL.h"
};

//set '1' to choose a type of file to play
#define LOAD_BGRA    1
#define LOAD_RGB24   0
#define LOAD_BGR24   0
#define LOAD_YUV420P 0

//Bit per Pixel
#if LOAD_BGRA
const int bpp=32;
#elif LOAD_RGB24|LOAD_BGR24
const int bpp=24;
#elif LOAD_YUV420P
const int bpp=12;
#endif

int screen_w=500,screen_h=500;
const int pixel_w=320,pixel_h=180;

unsigned char buffer[pixel_w*pixel_h*bpp/8];
//BPP=32
unsigned char buffer_convert[pixel_w*pixel_h*4];

//Convert RGB24/BGR24 to RGB32/BGR32
//And change Endian if needed
void CONVERT_24to32(unsigned char *image_in,unsigned char *image_out,int w,int h){
    for(int i =0;i<h;i++)
        for(int j=0;j<w;j++){
            //Big Endian or Small Endian?
            //"ARGB" order:high bit -> low bit.
            //ARGB Format Big Endian (low address save high MSB, here is A) in memory : A|R|G|B
            //ARGB Format Little Endian (low address save low MSB, here is B) in memory : B|G|R|A
            if(SDL_BYTEORDER==SDL_LIL_ENDIAN){
                //Little Endian (x86): R|G|B --> B|G|R|A
                image_out[(i*w+j)*4+0]=image_in[(i*w+j)*3+2];
                image_out[(i*w+j)*4+1]=image_in[(i*w+j)*3+1];
                image_out[(i*w+j)*4+2]=image_in[(i*w+j)*3];
                image_out[(i*w+j)*4+3]='0';
            }else{
                //Big Endian: R|G|B --> A|R|G|B
                image_out[(i*w+j)*4]='0';
                memcpy(image_out+(i*w+j)*4+1,image_in+(i*w+j)*3,3);
            }
        }
}


//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)

int thread_exit=0;

int refresh_video(void *opaque){
    while (thread_exit==0) {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40);
    }
    return 0;
}

int main(int argc, char* argv[])
{
    if(SDL_Init(SDL_INIT_VIDEO)) {  
        printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
        return -1;
    } 

    SDL_Window *screen; 
    //SDL 2.0 Support for multiple windows
    screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
    if(!screen) {  
        printf("SDL: could not create window - exiting:%s\n",SDL_GetError());  
        return -1;
    }
    SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);  

    Uint32 pixformat=0;
#if LOAD_BGRA
    //Note: ARGB8888 in "Little Endian" system stores as B|G|R|A
    pixformat= SDL_PIXELFORMAT_ARGB8888;  
#elif LOAD_RGB24
    pixformat= SDL_PIXELFORMAT_RGB888;  
#elif LOAD_BGR24
    pixformat= SDL_PIXELFORMAT_BGR888;  
#elif LOAD_YUV420P
    //IYUV: Y + U + V  (3 planes)
    //YV12: Y + V + U  (3 planes)
    pixformat= SDL_PIXELFORMAT_IYUV;  
#endif

    SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);



    FILE *fp=NULL;
#if LOAD_BGRA
    fp=fopen("../test_bgra_320x180.rgb","rb+");
#elif LOAD_RGB24
    fp=fopen("../test_rgb24_320x180.rgb","rb+");
#elif LOAD_BGR24
    fp=fopen("../test_bgr24_320x180.rgb","rb+");
#elif LOAD_YUV420P
    fp=fopen("../test_yuv420p_320x180.yuv","rb+");
#endif
    if(fp==NULL){
        printf("cannot open this file\n");
        return -1;
    }

    SDL_Rect sdlRect;  

    SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);
    SDL_Event event;
    while(1){
        //Wait
        SDL_WaitEvent(&event);
        if(event.type==REFRESH_EVENT){
            if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){
                // Loop
                fseek(fp, 0, SEEK_SET);
                fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);
            }

#if LOAD_BGRA
            //We don't need to change Endian
            //Because input BGRA pixel data(B|G|R|A) is same as ARGB8888 in Little Endian (B|G|R|A)
            SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w*4);  
#elif LOAD_RGB24|LOAD_BGR24
            //change 24bit to 32 bit
            //and in Windows we need to change Endian
            CONVERT_24to32(buffer,buffer_convert,pixel_w,pixel_h);
            SDL_UpdateTexture( sdlTexture, NULL, buffer_convert, pixel_w*4);  
#elif LOAD_YUV420P
            SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);  
#endif
            //FIX: If window is resize
            sdlRect.x = 0;  
            sdlRect.y = 0;  
            sdlRect.w = screen_w;  
            sdlRect.h = screen_h;  

            SDL_RenderClear( sdlRenderer );   
            SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);  
            SDL_RenderPresent( sdlRenderer );  
            //Delay 40ms
            SDL_Delay(40);

        }else if(event.type==SDL_WINDOWEVENT){
            //If Resize
            SDL_GetWindowSize(screen,&screen_w,&screen_h);
        }else if(event.type==SDL_QUIT){
            break;
        }
    }

    return 0;
}
运行结果

程序的运行结果如下图所示。
这里写图片描述

下载

代码位于“Simplest Media Play”中

SourceForge项目地址:https://sourceforge.net/projects/simplestmediaplay/

CSDN下载地址:http://download.csdn.net/detail/leixiaohua1020/8054395

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
由于yuvrgb格式之间存在转换关系,因此要实现yuv格式视频rgb格式视频的转换,我们需要进行以下步骤: 1. 打开yuv格式视频文件,读取每一帧视频yuv数据。 2. 将yuv数据转换为rgb数据,这是一个复杂的计算过程,需要根据视频宽度、高度、颜色采样等信息计算。可以使用FFmpeg等开源库进行转换。 3. 将rgb数据写入新的rgb格式视频文件。 4. 播放rgb格式视频文件,可以使用SDL、Qt、OpenCV等库进行播放。 下面是一份用C语言实现的yuv视频rgb视频的示例代码: ``` #include <stdio.h> #include <stdlib.h> #include <stdint.h> #define WIDTH 640 #define HEIGHT 480 #define YUV_FILE "test.yuv" #define RGB_FILE "test.rgb" void yuv2rgb(uint8_t *yuv, uint8_t *rgb) { int y = yuv[0]; int u = yuv[1] - 128; int v = yuv[2] - 128; int r = (298 * y + 409 * v + 128) >> 8; int g = (298 * y - 100 * u - 208 * v + 128) >> 8; int b = (298 * y + 516 * u + 128) >> 8; rgb[0] = (uint8_t)r; rgb[1] = (uint8_t)g; rgb[2] = (uint8_t)b; } int main() { FILE *yuv_file, *rgb_file; uint8_t yuv_buffer[WIDTH * HEIGHT * 3 / 2]; uint8_t rgb_buffer[WIDTH * HEIGHT * 3]; uint8_t *yuv_ptr, *rgb_ptr; int row, col; yuv_file = fopen(YUV_FILE, "rb"); rgb_file = fopen(RGB_FILE, "wb"); while (fread(yuv_buffer, 1, WIDTH * HEIGHT * 3 / 2, yuv_file) == WIDTH * HEIGHT * 3 / 2) { yuv_ptr = yuv_buffer; rgb_ptr = rgb_buffer; for (row = 0; row < HEIGHT; row++) { for (col = 0; col < WIDTH; col++) { yuv2rgb(yuv_ptr, rgb_ptr); yuv_ptr += 3; rgb_ptr += 3; } } fwrite(rgb_buffer, 1, WIDTH * HEIGHT * 3, rgb_file); } fclose(yuv_file); fclose(rgb_file); return 0; } ``` 这段代码实现了从yuv格式读取视频数据,转换为rgb格式并写入新的文件中。注意,这里的yuv格式指的是NV12格式,在这种格式下,一个像素点对应一个y值,但对应一个uv值对。在yuv2rgb函数中,我们使用了ITU-R BT.601标准将yuv值转换为rgb值,最终得到一个3字节的rgb数据。在主函数中,我们批量读取yuv数据,然后将每个像素点的yuv值转换为rgb值,并将所有的rgb值写入rgb文件中。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值