yuv420p 详解_详解SDL常用技术实战(2)

0.引言

本篇文章主要讲解如何使用SDL接口去显示YUV数据,在阅读本文前,可以阅读前面文章,学习和了解SDL的基础知识。文章列表如下:

详解SDL常用技术实战(1)

示例代码的基本功能就是从文件中读取yuv数据,然后通过SDL的接口,去40ms刷新一帧数据。把这个刷新事件放到线程队列,由主线程去读取,然后刷新。本次程序是支持窗口的缩放。捕获窗口大小变化的事件,然后根据窗口大小的变化,按照一定的比例去缩放就可以了。至于这个缩放后显示的位置,在以后再讲。

SDL的wiki链接:

http://wiki.libsdl.org/FrontPage

界面如下:

f1d51f951d822a7bc0055cb60841c742.png
11ab6f74366e7cbb181647ba48e3be12.png

1.SDL渲染相关的数据结构

在代码定义时,定义了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

2.YUV图像显示流程

通过代码演示如何从YUV文件中读取数据,然后通过SDL的接口去显示在窗口。需要经过如下步骤:

(1)SDL_Init()

初始化SDL,表示一个视频显示窗口,示例代码如下:

SDL_Init(SDL_INIT_VIDEO)

(2)SDL_CreateWindow()

创建窗口,窗口的大小,默认使用给YUV定义的分辨率。示例代码如下:

 window = SDL_CreateWindow("Simplest YUV Player",                           SDL_WINDOWPOS_UNDEFINED,                           SDL_WINDOWPOS_UNDEFINED,                           video_width, video_height,                           SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);

(3)SDL_CreateThread()

创建请求刷新线程,实例代码如下:

 timer_thread = SDL_CreateThread(refresh_video_timer,                                    NULL,                                    NULL);

(4)SDL_CreateRender()

根据步骤2创建的窗口的基础上,创建一个渲染器,示例代码如下:

 renderer = SDL_CreateRenderer(window, -1, 0);

(5)SDL_WaitEvent()

等待事件的发生,主线程在这里主要是捕获图像刷新、窗口大小变化及界面退出事件。示例代码如下:

timer_thread = SDL_CreateThread(refresh_video_timer,                                    NULL,                                    NULL);

(6)SDL_CreateTexture()

根据步骤4创建的渲染器基础上,创建纹理(需要指定像素格式和分辨率),示例代码如下:

 texture = SDL_CreateTexture(renderer,                                pixformat,                                SDL_TEXTUREACCESS_STREAMING,                                video_width,                                video_height);

(7)event.type == REFRESH_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!");                goto _FAIL;            }            // 设置纹理的数据 video_width = 320, 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;            // 320x240 怎么保持原视频的宽高比例          //如果窗口的值变化了,这里就会更新            rect.w = video_width * w_ratio;            rect.h = video_height * h_ratio;          。。。。。。。。

(8)SDL_UpdateTexture()

把新的像素数据放进来,并更新纹理。示例代码如下:

SDL_UpdateTexture(texture, NULL, video_buf, video_width);

(9)SDL_RenderClear()

在准备刷下一帧时,需要清除当前帧。示例代码如下:

SDL_RenderClear(renderer);

(10)SDL_RenderCopy()

将纹理的数据,拷贝给渲染器,去显示。示例代码如下:

SDL_RenderCopy(renderer, texture, NULL, &rect);

(11)SDL_RenderPresent()

渲染器,更新。示例代码如下:

SDL_RenderPresent(renderer);

(12)event.type == SDL_WINDOWEVENT

窗口变化事件,实时捕获窗口的大小。示例代码如下:

 else if(event.type == SDL_WINDOWEVENT)        {            //If Resize            SDL_GetWindowSize(window, &win_width, &win_height);            printf("SDL_WINDOWEVENT win_width:%d, win_height:%d",win_width,                   win_height );        }

(13)event.type == SDL_QUIT和event.type == QUIT_EVENT

这里要捕获系统退出事件和自定义退出事件,示例代码如下:

else if(event.type == SDL_QUIT) //退出事件        {            s_thread_exit = 1;        }        else if(event.type == QUIT_EVENT)        {            break;        }

(14)等待线程退出和资源释放

退出之前一定要释放资源,等待子线程退出,示例代码如下:

 s_thread_exit = 1;      // 保证线程能够退出    // 释放资源    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();

3.图像刷新线程

本示例代码创建了一个线程,由这个线程去控制刷新的时间和退出。刷新事件控制在40ms刷新一次。在退出时,由子线程打出一个退出事件给子线程,这样主线程才能正确退出。这背后的逻辑就是UI界面的刷新一般都是主线程在操作。示例代码如下:

int refresh_video_timer(void *data){    while (!s_thread_exit)    {        SDL_Event event;      //发出刷新界面事件        event.type = REFRESH_EVENT;        SDL_PushEvent(&event);        SDL_Delay(40);    }    s_thread_exit = 0;//发出退出事件    //push quit event    SDL_Event event;    event.type = QUIT_EVENT;    SDL_PushEvent(&event);    return 0;}

4.图像显示效果

93cb749f13bce5f9ce7bd0747b701aa5.png

5.总结

本篇文章通过SDL接口演示了如何显示YUV数据。能够快速掌握SDL显示的基本流程。欢迎关注,收藏,转发,分享。

后期关于项目知识,也会更新在微信公众号“记录世界 from antonio”,欢迎关注

b3c9bcfdd5387627a124a669c44f2549.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值