05-SDL YUV显示:SDL视频显示的流程

SDL_Init
SDL_CreateWindow
SDL_Window
SDL_CreateRenderer
SDL_Renderer
SDL_CreateTexture 
SDL_Texture     <--  读取本地YUV文件   
SDL_UpdateTexture <-- YUV data <-- 本地文件 
SDL_RenderClear 
SDL_RenderCopy
SDL_RenderPresent <-- 显示

用ffmpeg提取测试文件

ffmpeg -i test.mp4 -vf format=yuv420p output.yuv

全局

#define TRUE 1
#define FALSE 0

#define REFRESH_EVENT (SDL_USEREVENT + 1) // 请求画面刷新时间
#define FF_QUIT_EVENT (SDL_USEREVENT + 2) // 用户自定义事件
#define QUIT_EVENT (SDL_USEREVENT + 3)    // 退出事件

// 定义分辨率 YUV 像素分辨率
#define YUV_WIDTH 1920
#define YUV_HEIGHT 1080
// 定义 YUV 格式
#define YUV_FORMAT SDL_PIXELFORMAT_IYUV

int s_thread_exit = FALSE; // 退出标志 = 1 则退出

线程回调函数



int refresh_video_timer(void *data)
{
    while (!s_thread_exit)
    {
        SDL_Event event;
        event.type = REFRESH_EVENT;
        SDL_PushEvent(&event);
        SDL_Delay(40); // 40毫秒发一次刷新事件
    }
    s_thread_exit = FALSE;

    // 触发退出事件
    SDL_Event event;
    event.type = QUIT_EVENT;
    SDL_PushEvent(&event);

    return 0;
}

播放 

int YUV_video_display()
{
    SDL_Init(SDL_INIT_VIDEO);
    // SDL
    SDL_Event event;                 // 事件
    SDL_Rect rect;                   // 矩形
    SDL_Window *window = NULL;       // 窗口
    SDL_Renderer *renderer = NULL;   // 渲染
    SDL_Texture *texture = NULL;     // 纹理
    SDL_Thread *timer_thread = NULL; // 请求刷新线程
    uint32_t pixformat = YUV_FORMAT; // YUV420P --> SDL_PIXELFORMAT_IYUV

    // 分辨率
    // 1.YUV 的分辨率
    int video_width = YUV_WIDTH;
    int video_height = YUV_HEIGHT; 
    // 2.显示窗口的分辨率
    int win_width = YUV_WIDTH;
    int win_height = YUV_HEIGHT;

    // YUV 文件句柄
    FILE *video_fd = NULL;
    const char *yuv_path = "/home/king/ffmpeg_src/lessonCode/ZeroVoiceEducation/01_lesson/yuv420pTest.yuv";

    size_t video_buff_len = 0;
    uint8_t *video_buf = NULL; // 读取数据后先把放到buffer里面

    // 测试文件YUV420P格式
    uint32_t y_frame_len = video_width * video_height;
    uint32_t u_frame_len = video_width * video_height / 4;
    uint32_t v_frame_len = video_width * video_height / 4;
    uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;

    // 创建窗口
    window = SDL_CreateWindow("Simplest YUV Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, video_width, video_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
    if (!window)
    {
        fprintf(stderr, "SDL: could not create window,err:%s\n", SDL_GetError());
        goto _FAIL;
    }
    // 基于窗口创建渲染器
    renderer = SDL_CreateRenderer(window, -1, 0);
    // 基于渲染器创建纹理 | SDL_TEXTUREACCESS_STREAMING - 动态显示流的方式
    texture = SDL_CreateTexture(renderer, pixformat, SDL_TEXTUREACCESS_STREAMING, video_width, video_height);
    // 分配空间
    video_buf = (uint8_t *)malloc(yuv_frame_len);
    if (!video_buf)
    {
        fprintf(stderr, "Failed to alloc yuv frame space!\n");
        goto _FAIL;
    }

    // 打开YUV文件
    video_fd = fopen(yuv_path, "rb");
    if (!video_fd)
    {
        fprintf(stderr, "Failed to open yuv file\n");
        goto _FAIL;
    }
    // 创建请求刷新线程
    timer_thread = SDL_CreateThread(refresh_video_timer, NULL, NULL);
    while (TRUE)
    {
        // 收取SDL系统里面的事件
        SDL_WaitEvent(&event);

        if (event.type == REFRESH_EVENT) // 画面刷新事件
        {
            video_buff_len = fread(video_buf, 1, yuv_frame_len, video_fd);
            if (video_buff_len <= 0)
            {
                fprintf(stderr, "Failed to read data from yuv file!\n");
                goto _FAIL;
            }
            // 设置纹理的数据  video_width  = 1080 - plane
            SDL_UpdateTexture(texture, NULL, video_buf, video_width);
            // 显示区域,可以修改w和h进行缩放
            rect.x = 0;
            rect.y = 0;
            float w_ratio = win_width * 1.0 / video_width;
            float h_ratio = win_height * 1.0 / video_height;
            rect.w = video_width * w_ratio;
            rect.h = video_height * h_ratio;
            // 清除当前显示
            SDL_RenderClear(renderer);
            // 将纹理的数据拷贝给渲染器
            SDL_RenderCopy(renderer, texture, NULL, &rect);
            // 显示
            SDL_RenderPresent(renderer);
        }
        else if (event.type == SDL_WINDOWEVENT)
        {
            // if resize
            SDL_GetWindowSize(window, &win_width, &win_height);
            printf("SDL_WINDOWEVENT --窗口宽度:%d--, --窗口高度:%d--\n", win_width, win_height);
        }
        else if (event.type == SDL_QUIT) // SDL 标准退出事件
        {
            s_thread_exit = TRUE;
        }
        else if (event.type == QUIT_EVENT) // 自定义退出事件
        {
            break;
        }
    }
_FAIL:
    s_thread_exit = TRUE; // 保证线程能够退出
    // 释放资源
    if (timer_thread)
        SDL_WaitThread(timer_thread, NULL); // 等待线程退出
    if (video_buf)
        free(video_buf);
    if (video_fd)
        fclose(video_fd);
    if (texture)
        SDL_DestroyTexture(texture);
    if (renderer)
        SDL_DestroyRenderer(renderer);
    if (window)
        SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

运行结果:

  • 10
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值