使用SDL_QT直接播放渲染YUV格式文件

0.前要

  •   下载一个文件,名字为 400_300_25.mp4,我们用ffmplay.exe将其转化为yuv文件,具体操作如下:
  •   进入cmd控制台,进入ffmplay.exe文件的目录下,输入ffmpeg -i 文件名.mp4 文件名.yuv 回车,会生成一个yuv文件
  • IYUV 是 YUV420 格式的一种变体,其中 Y 分量在内存中是连续存储的,而 U 和 V 分量是交错存储的。因此,一个 IYUV 像素点的大小与 YUV420 相同。

    在 IYUV 中,一个像素的大小通常为:

Y 分量:8 位(1 字节)

U 分量:8 位/4 = 2 位(0.25 字节)

V 分量:8 位/4 = 2 位(0.25 字节)

  • 400*300的图像,指的是 400列 300行,每行400个像素点

1.代码

#include "sdlqtrgb.h"
#include <sdl/SDL.h>
#include <fstream>
#include <QMessageBox>
using namespace std;
#pragma comment(lib,"SDL2.lib")
static SDL_Window* sdl_win = NULL;
static SDL_Renderer* sdl_render = NULL;
static SDL_Texture* sdl_texture = NULL;
static int sdl_width = 0;
static int sdl_height = 0;
static unsigned  char* yuv = NULL;
static int pix_size = 2;
static ifstream yuv_file;
void SdlQtRGB::timerEvent(QTimerEvent* ev)
{
    yuv_file.read((char*)yuv, sdl_width * sdl_height * 1.5);
    //yuv 平面存储存储
    // yyyyyyyy uu vv
    SDL_UpdateTexture(sdl_texture, NULL, yuv,
        sdl_width  //一行 y的字节数
    );
    SDL_RenderClear(sdl_render);
    SDL_Rect rect;
    rect.x = 0;
    rect.y = 0;
    rect.w = sdl_width;
    rect.h = sdl_height;
    SDL_RenderCopy(sdl_render, sdl_texture, NULL, &rect);
    SDL_RenderPresent(sdl_render);
}

SdlQtRGB::SdlQtRGB(QWidget* parent)
    : QWidget(parent)
{
    //打开yuv文件
    yuv_file.open("D:\\lesson\\code\\bin\\x86\\400_300_25.yuv", ios::binary);
    if (!yuv_file.is_open())
    {
        QMessageBox::information(this, "", "open yuv failed!");
        return;
    }
    ui.setupUi(this);
    sdl_width = 400;
    sdl_height = 300;
    ui.label->resize(sdl_width, sdl_height);
    //初始化SDL
    SDL_Init(SDL_INIT_VIDEO);
    //创建窗口
    sdl_win = SDL_CreateWindowFrom((void*)ui.label->winId());
    //创建渲染器
    sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_ACCELERATED);
    //创建材质 支持YUV
    sdl_texture = SDL_CreateTexture(sdl_render,
        SDL_PIXELFORMAT_IYUV,
        SDL_TEXTUREACCESS_STREAMING,
        sdl_width,
        sdl_height
    );
    yuv = new unsigned char[sdl_width * sdl_height * pix_size];
    startTimer(10);
}

2.代码解析

1.构造函数解析

  • 打开了一个YUV文件,文件路径是指定的路径。ios::binary 表示以二进制模式打开文件,注意yuv文件名用绝对路径,且用双斜杠,不然报错!!我因为这个试过多种排错,比如打印当前工作目录,没问题,换了一个yuv文件怀疑我的yuv文件损坏了,也是一样的问题,最后加了双斜杠才能打开这个文件,再windows下可能\后跟一个字母会被误认为一个操作,比如\n是换行,那如果一个文件名为nnn.yuv呢 就会出现歧义,反正我的环境要打双斜杠
  • 设置了窗口的宽度和高度,并将界面上的 label 控件的大小设置为这些值。这个控件将用于显示YUV视频帧,其实可以直接再QT的UI可视化界面上进行操作,直接拖动一个label到wiget上设置为400*300的大小即可,用代码有点烦喏
  • 调用 SDL_Init 函数来初始化SDL库的视频子系统。这是必要的,因为我们将使用SDL来处理图形。
  • 调用 SDL_CreateWindowFrom 函数来创建一个SDL窗口,并将其与界面上的 label 控件关联起来。
  • 调用 SDL_CreateRenderer 函数来创建一个SDL渲染器,用于在窗口上进行绘制。
  • 调用 SDL_CreateTexture 函数来创建一个SDL纹理。参数包括渲染器、纹理格式(SDL_PIXELFORMAT_IYUV 表示YUV格式)、纹理访问方式(SDL_TEXTUREACCESS_STREAMING 表示数据将被频繁更新)、宽度和高度。
  • 动态分配了一个大小为 sdl_width * sdl_height * pix_size 字节的内存块,并将指针存储在 yuv 变量中。这个内存块用于存储YUV视频帧的数据。
  • 调用 startTimer 函数,启动一个定时器,每隔10毫秒触发一次定时器事件。定时器事件在之前的代码中已经被重载,用于读取YUV数据、更新纹理和刷新屏幕显示。

这样,构造函数完成了打开YUV文件、设置窗口大小、初始化SDL、创建窗口、渲染器和纹理,以及分配内存并启动定时器的一系列操作。整个流程为在SDL窗口中显示YUV视频提供了基础。

2.定时器函数解析

  • 使用 read 函数从 yuv_file 文件流中读取数据,并将其存储到 yuv 数组中。这个数组通常用于存储YUV格式的视频帧数据。数据的大小为 sdl_width * sdl_height * 1.5 字节,这是因为YUV格式中,每个像素有一个Y分量和两个色度分量(U和V),所以总共需要 sdl_width * sdl_height * 1.5 字节的空间来存储整个图像的数据。
  • yuv 数据的存储方式。在这个示例中,Y分量依次存储在数组的前面,接着是U分量,最后是V分量。yyyyyyyy uu vv yyyyyyyy uu vv.......(IYUV像素点存储格式)一直循环 对比 BGRABGRABGRA........(ARGB像素点的存储格式)
yyyyyyyy
uu
vv
yyyyyyyy
uu
vv
yyyyyyyy
uu
vv
...
...
...
  • 调用 SDL_UpdateTexture 函数来更新SDL纹理中的数据。参数包括要更新的纹理、要更新的矩形区域(在这里是整个纹理)、指向新数据的指针(yuv 数组),以及一行Y分量的字节数。由于YUV数据是按行存储的,所以传递一行Y分量的字节数可以帮助SDL正确地解析数据。SDL_UpdateTexture 函数中,第四个参数表示每一行的像素大小,为什么本代码传的是sdl_width而对ARGB格式处理的时候传的是sdl_width*像素大小呢?因为YUV420P中每个Y分量大小为1B,对YUV处理的时候默认传一行Y所占字节数也就是sdl_width*1B=sdl_width。
  • 调用 SDL_RenderClear 函数来清空渲染器的内容,准备绘制新的帧。
  • 定义了一个 SDL_Rect 结构体变量 rect,用于表示要绘制的矩形区域的位置和大小。在这个例子中,矩形的位置是窗口的左上角,大小是整个窗口的大小。
  • 调用 SDL_RenderCopy 函数来将纹理复制到渲染器中。参数包括渲染器、要复制的纹理、源矩形(在这里是整个纹理,即 NULL),以及目标矩形(在这里是整个窗口的矩形区域)。
  • 调用 SDL_RenderPresent 函数来更新屏幕显示,将渲染器中的内容呈现到屏幕上。

这样,timerEvent 函数完成了读取YUV数据、更新纹理、清空渲染器、复制纹理到渲染器中以及更新屏幕显示的一系列操作。这些操作使得YUV视频帧得以在SDL窗口中实时显示。

3.运行结果展示

giao giao giao

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值