• SDL的基础知识以及利用SDL播放视频


    原文地址:http://blog.csdn.net/i_scream_/article/details/52714378

    此博文相关知识点从雷神的博客以及视频学习,截图也是用了他的课件,
    雷神博客地址:http://blog.csdn.net/leixiaohua1020/

    SDL基础知识

    • SDL结构图
      这里写图片描述

    • SDL函数调用的一般流程
      这里写图片描述

      • 最最主要操作的函数是SDL_texture();
      • 工作过程大致是:FFMpeg「Decode」解码一帧,交给SDL_texture(), 然后再复制给渲染器,渲染器再显示出来。以此循环。
      • 相关函数:待补充(不定期更新)
    • SDL的一些主要的数据结构
      这里写图片描述

      • SDL2支持多窗口显示,主要是依靠SDL_rect().
      • 相关数据结构:待补充(不定期更新)

    示例代码1:

    • 代码
    #include <stdio.h>
    
    #include "SDL2/SDL.h"
    
    #define SCREEN_W    640             //窗口的宽
    #define SCREEN_H    360             //窗口的高
    #define PIXEL_W     320             //视频像素的宽,要和视频文件相同才能显示正常
    #define PIXEL_H     180             //像素的高
    #define BPP         12              //像素深度:指存储每个像素所用的位数(bit)
    #define BUF_LEN     ((PIXEL_W) * (PIXEL_H) * (BPP) / 8)     //存一帧的需要空间
    
    const int bpp = BPP;
    int screen_w = SCREEN_W;
    int screen_h = SCREEN_H;
    const int pixel_w = PIXEL_W;
    const int pixel_h = PIXEL_H;
    
    unsigned char buffer[BUF_LEN+1];
    
    int main(int argc, char* argv[])
    {
        if(SDL_Init(SDL_INIT_VIDEO)) {
            printf( "Could not initialize SDL - %s
    ", SDL_GetError());
            return -1;
        }
    
        SDL_Window *screen;
        //SDL 2.0 Support for multiple windows
        //画一个窗口,大小为screen_w * screen_h
        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
    ",SDL_GetError());
            return -1;
        }
    
        //新建一个渲染器
        SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    
        Uint32 pixformat=0;
        //IYUV: Y + U + V  (3 planes)
        //YV12: Y + V + U  (3 planes)
        pixformat= SDL_PIXELFORMAT_IYUV;        //???
    
        SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
    
        FILE *fp=NULL;
        fp=fopen("test_yuv420p_320x180.yuv","rb+");
    
        if(fp==NULL){
            printf("cannot open this file
    ");
            return -1;
        }
    
        SDL_Rect sdlRect;
        int i = 5;
        while(i >= 0){
                //一次读1byte,总共读一帧
                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);
                    i--;
                    continue;
                }
    
                //更新纹理数据
                SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);
    
                //(x,y)是窗口左上边开始的点。
                //w,h是整个像素窗口宽和高(注意不是整个窗口)
                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,一般视频都是这个,25帧/s.
                SDL_Delay(40);
    
        }
        SDL_Quit();
        return 0;
    }
    
    • 编译:
    gcc1_SDL_create_window.c -o 1_SDL_create_window.out -O2 -Wall -g -lSDL2 -lSDL2main
    • 1

    结果截图:
    这里写图片描述
    * 注意,这个程序如果是在windows下面,窗口是不能移动的,鼠标放上去也是忙的状态。在下一个程序中修改程序,使它能移动,以及自动适应窗口大小。


    示例程序2:

    • 代码:
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #include <SDL2/SDL.h>
    
    #define SCREEN_W    640             //窗口的宽
    #define SCREEN_H    360             //窗口的高
    #define PIXEL_W     320             //视频像素的宽,要和视频文件相同才能显示正常
    #define PIXEL_H     180             //像素的高
    #define BPP         12              //像素深度:指存储每个像素所用的位数(bit)
    #define BUF_LEN     ((PIXEL_W) * (PIXEL_H) * (BPP) / 8)     //存一帧的需要空间
    #define FILENAME    "test_yuv420p_320x180.yuv"
    #define MY_DEFINE_REFRESH_EVENT     (SDL_USEREVENT + 1)
    #define MY_DEFINE_BREAK_EVENT       (SDL_USEREVENT + 2)
    
    int thread_exit = 0;
    static int refresh_func(void *arg)
    {
        SDL_Event   event;
        thread_exit = 0;
    
        while(0 == thread_exit)
        {
            event.type = MY_DEFINE_REFRESH_EVENT;
            SDL_PushEvent(&event);      //发送一个事件,使主线程继续运行
            SDL_Delay(40);
        }
    
        //子线程退出后发送事件给主线程,使主线程也退出
        thread_exit = 0;
        event.type = MY_DEFINE_BREAK_EVENT;
        SDL_PushEvent(&event);
        return 0;
    }
    
    const int bpp = BPP;
    
    int main(int argc, char *argv[])
    {
        int screen_w = SCREEN_W;
        int screen_h = SCREEN_H;
        const int pixel_w = PIXEL_W;
        const int pixel_h = PIXEL_H;
        unsigned char buffer[BUF_LEN + 1];      //注意类型
        char            filename[256] = FILENAME;
    
        SDL_Window      *screen = NULL;         //窗口数据结构
        SDL_Renderer    *sdlRenderer = NULL;    //渲染器数据结构
        Uint32          pixformat = 0;
        SDL_Texture     *sdlTexture = NULL;     //主要操作的
        FILE            *fp = NULL;
        SDL_Rect        sdlRect;
    //    SDL_Thread      *refresh_thread = NULL; //线程数据结构
        SDL_Event       event;                  //事件数据结构
    
        //注意:可以把文件传进来了,但是如果不使用ffmmpeg的函数还不知道怎么改像素值,以使视频正常显示!!!!!
        if (argc > 2)
        {
             printf("Usage: ./*.out videofile.yuv
    ");
             return 0;
        }
        else if (argc == 2)
        {
             memcpy(filename, argv[1], strlen(argv[1]) + 1);
    //         filename[strlen(argv[1])] = '';
        }
        printf("video file name: %s
    ", filename);
    
        if (SDL_Init(SDL_INIT_VIDEO))
         {
             printf("Couldn't initialize SDL - %s
    ", SDL_GetError());
             return (-1);
         }
    
        screen = SDL_CreateWindow("isshe Video Player SDL2",
                SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                screen_w, screen_h,
                SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
        if (!screen)
        {
             printf("SDL:Couldn't not create window error: %s
    ", SDL_GetError());
             return (-1);
        }
    
        //创建渲染器,-1,0不懂什么意思,再看这个函数的定义
        sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
    
        //在pixels.h中,大概是指定输入数据格式?不懂!
        pixformat = SDL_PIXELFORMAT_IYUV;
    
        sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
                SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
    
        //打开文件
        fp = fopen(filename, "r");
        if (NULL == fp)
        {
            printf("Open file error:%s
    ", strerror(errno));
            return (-1);
        }
    
        //新建线程
    //    refresh_thread =
        SDL_CreateThread(refresh_func, NULL, NULL);
    
        while(1)
        {
             //等待一个事件
             SDL_WaitEvent(&event);     //事件的信息存到结构中了
    
             //处理事件, 尝试使用自定义的事件
             if (event.type == MY_DEFINE_REFRESH_EVENT)
             {
                  //读一帧
                  if (fread(buffer, 1, BUF_LEN, fp) != BUF_LEN) //出错或结尾
                  {
                      //重定位会文件头部
                      fseek(fp, 0, SEEK_SET);
                      continue;         //
                  }
    
                  //更新纹理,但是不懂最后一个参数,是一次更新一行吗?
                  SDL_UpdateTexture(sdlTexture, NULL, buffer, pixel_w);
    
                  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);
             }
             else if (event.type == SDL_WINDOWEVENT)
             {
                  //获取像素窗口的大小,窗口拉伸的时候用这个则会自动调整
                  SDL_GetWindowSize(screen, &screen_w, &screen_h);
             }
             else if (event.type == SDL_QUIT)
             {
                 thread_exit = 1;
             }
             else if (event.type == MY_DEFINE_BREAK_EVENT)    //线程结束,主线程也结束
             {
                  break;
             }
        }
    
        fclose(fp);
        SDL_Quit();
        return 0;
    }
    • 程序中主线程阻塞等待事件,子线程发送事件后主线程继续运行。
    • 程序中自定义了两个事件类型,用以说明事件类型可自定义。

    • 编译:

    gcc  2_SDL_pthread_event.c -o 2_SDL_pthread_event.out -O2 -Wall -g -lSDL2 -lSDL2main

     

    • 运行结果:
      这里写图片描述
      • 可以随意拉伸窗口。
      • 可以关闭。

    参考资料:

    • 雷神的视频
    • SDL2.0 源码

    资料下载:http://download.csdn.net/detail/i_scream_/9644380

  • 相关阅读:
    基于注解的IOC配置
    字符串典型问题分析
    指针与数组
    数组的本质
    数组与指针分析
    指针的本质
    #与##操作符使用
    #pragma使用分析
    #error和#line使用分析
    条件编译使用
  • 原文地址:https://www.cnblogs.com/boonya/p/8605044.html
Copyright © 2020-2023  润新知