播放YUV视频的步骤
- 初始化SDL库:
- 目的:确保SDL库正确初始化,以便可以使用其窗口、渲染和事件处理功能。
- 操作:调用
SDL_Init(SDL_INIT_VIDEO)
来初始化SDL的视频子系统。
- 创建窗口用于显示YUV视频:
目的:创建一个窗口作为显示YUV视频的目标。
操作:使用SDL_CreateWindow
创建一个SDL窗口,并指定窗口的位置、大小和标志(如是否全屏等)。你可以根据需要自定义窗口的属性。 - 创建渲染器:
- 目的:创建一个渲染器,用于在窗口中绘制图像。
- 操作:使用
SDL_CreateRenderer
创建一个渲染器。可以选择硬件加速和垂直同步选项以提高性能和视觉质量。
- 创建纹理并设置颜色格式:
- 目的:创建一个纹理对象,用于在GPU中存储图像数据,并设置纹理的颜色格式(如YUV420P)。
- 操作:使用
SDL_CreateTexture
创建一个纹理,指定像素格式(例如SDL_PIXELFORMAT_YV12 或 SDL_PIXELFORMAT_IYUV
),以及访问模式(如SDL_TEXTUREACCESS_STREAMING
)。
- 循环读取YUV文件并更新纹理:
- 目的:从YUV文件中逐帧读取原始像素数据,并将每一帧的数据更新到纹理中,完成每一帧的渲染。
- 操作:在一个循环中读取YUV文件中的每一帧数据,使用
SDL_UpdateYUVTexture
将Y、U、V平面的数据分别更新到纹理中,然后调用SDL_RenderClear
、SDL_RenderCopy
和SDL_RenderPresent
来刷新屏幕以显示当前帧。确保每帧之间有适当的延时以维持正确的播放速度。
- 清理资源:
目的:释放所有分配的资源,确保没有内存泄漏。
操作:在程序结束时调用相应的销毁函数(如 SDL_DestroyTexture、SDL_DestroyRenderer 和 SDL_DestroyWindow),最后调用 SDL_Quit 退出SDL库。
示例代码如下:
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <SDL2/SDL.h>
#include <QDebug>
#include <QFile>
#define RET(judge,SDL_Init) \
if(judge) \
{\
qDebug() << #SDL_Init << "error" << SDL_GetError();\
return;\
}
#define FILENAME "D:/ffmpeg/in.yuv"
#define PIXEL_FORMAT SDL_PIXELFORMAT_IYUV
#define IMG_W 352
#define IMG_H 288
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
_widget = new QWidget(this);
//设置窗口位置和大小
_widget->setGeometry(100,0,IMG_W,IMG_H);
//初始化子系统
RET(SDL_Init(SDL_INIT_VIDEO),SDL_Init);
//创建一个窗口
//标题-X-Y-width-height
_window = SDL_CreateWindowFrom((void *)_widget->winId());
RET(!_window,SDL_CreateWindow);
//创建渲染上下文--用于渲染图形到窗口
//这SDL_RENDERER_ACCELERATED -- 个标志告诉 SDL 尝试创建一个使用硬件加速的渲染器
//SDL_RENDERER_PRESENTVSYNC -- 这个标志使渲染器的呈现操作同步到显示器的垂直同步(VSync)
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
//如果创建失败
if(!_renderer)
{
_renderer = SDL_CreateRenderer(_window,-1,0);
RET(!_renderer,SDL_CreateRenderer);
}
//创建纹理t
_texture = SDL_CreateTexture(_renderer,
PIXEL_FORMAT,
SDL_TEXTUREACCESS_STREAMING,
IMG_W,IMG_H);
RET(!_texture,SDL_CreateTextureFromSurface);
//打开YUV文件
_file.setFileName(FILENAME);
if(!_file.open(QFile::ReadOnly))
{
qDebug() << "_file open error" << FILENAME;
return;
}
}
MainWindow::~MainWindow()
{
delete ui;
_file.close();
SDL_DestroyRenderer(_renderer);
SDL_DestroyTexture(_texture);
SDL_DestroyWindow(_window);
SDL_Quit();//初始化子系统后必须做一个退出操作
}
void MainWindow::on_pushButton_clicked()
{
//开启定时器
_timerId = startTimer(60);
}
//每隔一段事件就会调用
void MainWindow::timerEvent(QTimerEvent *enent)
{
int imgSize = IMG_W * IMG_H * 1.5;
char data[imgSize];
if(_file.read(data,imgSize) > 0)
{
//将YUV的像素数据填充到texture -- nullptr空表示整个texture都是
RET(SDL_UpdateTexture(_texture,nullptr,data,IMG_W),SDL_UpdateTexture);
//设置绘制颜色(画笔颜色)
SDL_SetRenderDrawColor(_renderer,0,0,0,SDL_ALPHA_OPAQUE);
//用绘制颜色(画笔颜色)清除渲染目标---也就是覆盖
SDL_RenderClear(_renderer);
//拷贝纹理数据到渲染目标(默认时window)
RET(SDL_RenderCopy(_renderer,_texture,nullptr,nullptr),SDL_RenderCopy);
//更新所有的渲染操作到屏幕上
SDL_RenderPresent(_renderer);
}
else
{
//文件数据已经读取完毕
killTimer(_timerId);
}
}
由于是一个视频,所以只截取了一个照片