[2] SDL的基础知识以及利用SDL播放视频

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


日期:2016.10.1
作者:isshe
github:github.com/isshe
邮箱:i.sshe@outlook.com


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\n", 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\n",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\n");
        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

结果截图:
这里写图片描述
* 注意,这个程序如果是在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\n");
         return 0;
    }
    else if (argc == 2)
    {
         memcpy(filename, argv[1], strlen(argv[1]) + 1);
//         filename[strlen(argv[1])] = '\0';
    }
    printf("video file name: %s\n", filename);

    if (SDL_Init(SDL_INIT_VIDEO))
     {
         printf("Couldn't initialize SDL - %s\n", 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\n", 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\n", 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

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用SDL2在Linux上播放视频,需要先安装SDL2库和FFmpeg库。接下来,需要用FFmpeg将视频解码成yuv像素数据,再使用SDL2将像素数据渲染到屏幕上。 以下是基本的步骤: 1. 引入SDL2和FFmpeg库,定义相关变量和函数。 ```c #include <SDL2/SDL.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> AVCodecContext *codec_ctx; AVFormatContext *format_ctx; AVCodec *codec; SDL_Renderer* renderer; SDL_Texture* texture; SDL_Window *window; SDL_Rect rect; unsigned char *output_buffer; int pitch; struct SwsContext *sws_ctx; ``` 2. 初始化SDL2和FFmpeg库。 ```c if(SDL_Init(SDL_INIT_VIDEO) != 0) { printf("SDL_Init Error: %s", SDL_GetError()); return -1; } av_register_all(); avformat_network_init(); format_ctx = avformat_alloc_context(); if(avformat_open_input(&format_ctx, file_path, NULL, NULL) != 0) { return -1; } if(avformat_find_stream_info(format_ctx, NULL) < 0) { return -1; } int video_stream_index = -1; for(int i=0; i < format_ctx->nb_streams; i++) { if(format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; break; } } if(video_stream_index == -1) { return -1; } codec_ctx = format_ctx->streams[video_stream_index]->codec; codec = avcodec_find_decoder(codec_ctx->codec_id); if(codec == NULL) { return -1; } if(avcodec_open2(codec_ctx, codec, NULL) < 0) { return -1; } ``` 3. 配置SDL2窗口、渲染器和纹理。 ```c window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, codec_ctx->width, codec_ctx->height, SDL_WINDOW_OPENGL); renderer = SDL_CreateRenderer(window, -1, 0); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, codec_ctx->width, codec_ctx->height); rect.w = codec_ctx->width; rect.h = codec_ctx->height; ``` 4. 读取视频流、解码和渲染。 ```c AVPacket packet; int frame_finished; AVFrame* frame = av_frame_alloc(); while(av_read_frame(format_ctx, &packet) >= 0) { if(packet.stream_index == video_stream_index) { if(avcodec_send_packet(codec_ctx, &packet) == 0) { while(avcodec_receive_frame(codec_ctx, frame) == 0) { sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); sws_scale(sws_ctx, (const uint8_t * const*)frame->data, frame->linesize, 0, codec_ctx->height, (uint8_t * const *)output_buffer, &pitch); SDL_UpdateYUVTexture(texture, NULL, output_buffer, codec_ctx->width, output_buffer+codec_ctx->width*codec_ctx->height, codec_ctx->width/2, output_buffer+codec_ctx->width*codec_ctx->height*5/4, codec_ctx->width/2); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); sws_freeContext(sws_ctx); } } } av_packet_unref(&packet); } ``` 5. 释放资源。 ```c avformat_close_input(&format_ctx); avcodec_free_context(&codec_ctx); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); ``` 总体上来说,使用SDL2播放视频需要对FFmpeg库有一定的了解,掌握基本的视频解码、像素转换和纹理渲染等技巧。对于初学者来说,需要仔细研究相关文档和示例程序,并多做实践。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值