22_播放器之使用SDL显示YUV视频
简介
使用SDL实现简单的YUV播放器。
这里还需要使用到像素格式和计算图片大小,这两个我们直接使用ffmpeg来实现,因此需要使用ffmpeg的libavutil/avutil.h和libavutil/imgutils.h
初始化Video子系统
main.cpp
这里我们把SDL的初始化和退出都写在main函数里。
int main(int argc, char *argv[]){
// 初始化Video子系统
if (SDL_Init(SDL_INIT_VIDEO)) {
qDebug() << "SDL_Init error" << SDL_GetError();
return 0;
}
QApplication a(argc, argv);
MainWindow w;
w.show();
int ret = a.exec();
SDL_Quit();
return ret;
}
创建播放器YuvPlayer类
这里我们先创建YuvPlayer类,然后向往提供一些方法。
yuvplayer.h
#ifndef YUVPLAYER_H
#define YUVPLAYER_H
#include <QWidget>
#include <SDL2/SDL.h>
#include <QFile>
extern "C"{
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
}
typedef struct{
const char *filename;
int width;
int height;
AVPixelFormat pixelFormat;
int fps;
}Yuv;
class YuvPlayer : public QWidget{
Q_OBJECT
public:
// 状态
typedef enum{
Stopped = 0,
Playing,
Paused,
Finished
} State;
explicit YuvPlayer(QWidget *parent = nullptr);
~YuvPlayer();
void play();
void pause();
void stop();
bool isPlaying();
void setYuv(Yuv &yuv);
State getState();
signals:
private:
// 窗口
SDL_Window *_window = nullptr;
// 渲染上下文
SDL_Renderer *_renderer = nullptr;
// 纹理(直接跟特定驱动程序相关的像素数据)
SDL_Texture *_texture = nullptr;
QFile _file;
int _timerId = 0;
State _state = Stopped;
Yuv _yuv;
bool _playing;
void timerEvent(QTimerEvent *event);
};
#endif // YUVPLAYER_H
mainwindow.cpp
在MainWindow类中调用播放器YuvPlayer的一些方法
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#ifdef Q_OS_WIN
#define FILENAME "../test/out.yuv"
#else
#define FILENAME "/Users/zuojie/QtProjects/audio-video-dev/test/out.yuv"
#endif
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow){
ui->setupUi(this);
// 创建播放器
_player = new YuvPlayer(this);
// 设置播放器的位置和尺寸
int w = 640;
int h = 480;
int x = (width() - w) >> 1;
int y = (height() - h) >> 1;
_player->setGeometry(x, y, w, h);
// 设置需要播放的文件
Yuv yuv = {
FILENAME,
848,480,
AV_PIX_FMT_YUV420P,
30
};
_player->setYuv(yuv);
}
MainWindow::~MainWindow(){
delete ui;
}
void MainWindow::on_playButton_clicked(){
if(_player->isPlaying()){// 正在播放
_player->pause();
ui->playButton->setText("播放");
}else{// 没有正在播放
_player->play();
ui->playButton->setText("暂停");
}
}
void MainWindow::on_stopButton_clicked(){
_player->stop();
}
实现播放器YuvPlayer的方法
yuvplayer.cpp
#include "yuvplayer.h"
#include <QDebug>
#define RET(judge, func) \
if (judge) { \
qDebug() << #func << "error" << SDL_GetError(); \
return; \
}
static const std::map<AVPixelFormat, SDL_PixelFormatEnum>
PIXEL_FORMAT_MAP = {
{AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV},
{AV_PIX_FMT_YUYV422, SDL_PIXELFORMAT_YUY2},
{AV_PIX_FMT_NONE, SDL_PIXELFORMAT_UNKNOWN}
};
YuvPlayer::YuvPlayer(QWidget *parent) : QWidget(parent){
// 创建窗口
_window = SDL_CreateWindowFrom((void *)winId());
RET(!_window, SDL_CreateWindow);
// 创建渲染上下文(默认的渲染目标是window)
_renderer = SDL_CreateRenderer(_window,
// 要初始化的渲染设备的索引,设置 -1 则初始化第一个支持 flags 的设备
-1,
SDL_RENDERER_ACCELERATED |
SDL_RENDERER_PRESENTVSYNC);
if (!_renderer) { // 说明开启硬件加速失败
_renderer = SDL_CreateRenderer(_window, -1, 0);
}
RET(!_renderer, SDL_CreateRenderer);
}
YuvPlayer::~YuvPlayer(){
_file.close();
SDL_DestroyTexture(_texture);
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
}
void YuvPlayer::play(){
// 开启定时器
_timerId = startTimer(1000 / _yuv.fps);
_state = YuvPlayer::Playing;
}
void YuvPlayer::pause(){
if(_timerId){
killTimer(_timerId);
}
_state = YuvPlayer::Paused;
}
void YuvPlayer::stop(){
if(_timerId){
killTimer(_timerId);
}
_state = YuvPlayer::Stopped;
}
bool YuvPlayer::isPlaying(){
return _state == YuvPlayer::Playing;
}
YuvPlayer::State YuvPlayer::getState(){
return _state;
}
void YuvPlayer::setYuv(Yuv &yuv){
_yuv = yuv;
// 创建纹理
_texture = SDL_CreateTexture(_renderer,
//显示的像素数据格式,我们显示的YUV图片像素格式是yuv420p,
//其实SDL_PIXELFORMAT_IYUV就是yuv420p像素格式
PIXEL_FORMAT_MAP.find(yuv.pixelFormat)->second,
//之前我们把同一个texture在窗口绘制多次时,我们设置的是SDL_TEXTUREACCESS_TARGET,
//这里我们设置SDL_TEXTUREACCESS_STATIC,当然设置成SDL_TEXTUREACCESS_STREAMING也可以
SDL_TEXTUREACCESS_STREAMING,
yuv.width,yuv.height);
RET(!_texture, SDL_CreateTexture);
// 打开文件
_file.setFileName(yuv.filename);
if(!_file.open(QFile::ReadOnly)){
qDebug() << "file open error" << yuv.filename;
}
}
void YuvPlayer::timerEvent(QTimerEvent *event){
// 图片大小
int imgSize = av_image_get_buffer_size(_yuv.pixelFormat,
_yuv.width,_yuv.height,
1);
char data[imgSize];
// 每次读取一帧图像
if(_file.read(data,imgSize) > 0){
// 将YUV的像素数据填充到texture
RET(SDL_UpdateTexture(_texture,
nullptr,// SDL_Rect:更新像素的矩形区域,传nullptr表示更新整个纹理区域
data,// 原始像素数据
_yuv.width),// 一行像素数据的字节数,这里传图片宽度即可
SDL_UpdateTexture);
// 渲染
// 设置绘制颜色(这里随便设置了一个颜色:黑色)
RET(SDL_SetRenderDrawColor(_renderer,0,0,0,SDL_ALPHA_OPAQUE),SDL_SetRenderDrawColor);
// 用DrawColor清除渲染目标
RET(SDL_RenderClear(_renderer),SDL_RenderClear);
// 复制纹理到渲染目标上(默认是window)可以使用SDL_SetRenderTarget()修改渲染目标
// srcrect源矩形框,dstrect目标矩形框,两者都传nullptr表示整个纹理渲染到整个目标上去
RET(SDL_RenderCopy(_renderer,_texture,nullptr,nullptr),SDL_RenderCopy);
// 将此前的所有需要渲染的内容更新到屏幕上
SDL_RenderPresent(_renderer);
}else{
// 文件数据已经读取完毕
killTimer(_timerId);
}
}
22_播放器之使用SDL显示YUV视频的更多相关文章
- 使用DirectDraw直接显示YUV视频数据
最近在编写一个进行视频播放的ActiveX控件,工作已经接近尾声,现将其中显示YUV数据的使用DirectDraw的一些经验总结如下:(解码部分不是我编写的,我负责从网络接收数据,将数据传给解码器,并 ...
- directdraw显示yuv视频,出现屏保时,yuv显示不出来,表面丢失
原因是: DDrawSurface 丢失, DDraw表面在很多情况下都会丢失(如:启动其他全屏独占程序,屏保,或锁屏时), 表面丢失其实就是表面所使用的内存或显存被DirectDraw系统释放, 分 ...
- SDL 开发实战(六): 使用 SDL 实现 YUV 播放器
前面铺垫了这么多,现在终于进入核心的主题了,那就是使用SDL播放视频,本节我们将使用SDL播放YUV视频,也就是做一个YUV播放器. 下面说明一下使用SDL播放YUV视频的基本流程,主要分为两大部分: ...
- avi视频格式转yuv格式与播放yuv视频
因为要用到yuv格式视频.而眼下仅仅有avi格式的视频,所以须要转换,而且opencv不支持yuv编码的视频播放.所以须要转换为rgb编码.而后播放.写了两个程序.以供參考: 1,avi格式视频转yu ...
- FFmpeg YUV视频序列编码为视频
上一篇已经写了如何配置好开发环境,这次就先小试牛刀,来个视频的编码.搞视频处理的朋友肯定比较熟悉YUV视频序列,很多测试库提供的视频数据都是YUV视频序列,我们这里就用用YUV视频序列来做视频.关于Y ...
- 使用D3D渲染YUV视频数据
源代码下载 在PC机上,对于YUV格式的视频如YV12,YUY2等的显示方法,一般是采用DIRECTDRAW,使用显卡的OVERLAY表面显示.OVERLAY技术主要是为了解决在PC上播放VCD而在显 ...
- Android上使用OpenGLES2.0显示YUV数据
在Android上用OpenGLES来显示YUV图像,之所以这样做,是因为: 1.Android本身也不能直接显示YUV图像,YUV转成RGB还是必要的: 2.YUV手动转RGB会占用大量的CPU资源 ...
- 使用D3D渲染YUV视频数据<转>
源代码下载 转载地址:http://blog.csdn.net/dengzikun/article/details/5824874 源码地址:http://download.csdn.net/down ...
- Android用surface直接显示yuv数据(三)
本文用Java创建UI并联合JNI层操作surface来直接显示yuv数据(yv12),开发环境为Android 4.4,全志A23平台. package com.example.myyuvviewe ...
- Video标签播放视频?谷歌浏览器?safari?? 谷歌浏览器播放不了mp4格式的视频的原因
webm格式和mp4格式,判断了浏览器能否支持的视频类型后,给了一个if判断,如果是支持mp4格式,就返回视频后缀mp4,如果是webm,就返回后缀webm.结果,在谷歌浏览器中播放不了,为什么我指定 ...
随机推荐
- spring-cloud 配置管理
作用: 实现配置热更新 实现网关配置热部署 配置模板 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc ...
- 洛谷P1249最大乘积,数论找规律
这道题是数论加高精度,高精度倒好说,就是高精度乘法实现,模拟列竖式乘法,但是找出要乘的这几个数实属不容易,没学过数论,只能从题解中学怎么找规律这里引用一下洛谷题解区赞数最高的题解 不知道大家看其他的题 ...
- 案例:使用sqlplus登录报ORA-12547错误
现象:Exadata刷机之后grid/oracle用户的环境变量是没有设置的,需要手工进行设置,设置完成后发现grid用户执行报错ORA-12547: [grid@dbm0dbadm01 ~]$ sq ...
- .NET 云原生架构师训练营(模块二 基础巩固 配置)--学习笔记
2.2.3 核心模块--配置 IConfiguration Options ASP.NET Core 中的配置:https://docs.microsoft.com/zh-cn/aspnet/core ...
- 【Unity3D】花瓣特效
1 花瓣绘制原理 如下图是实现的花瓣特效效果,为方便描述,我们将每个红色的扁状长条称为花瓣,每个花瓣中心的绿点称为花蕊,花朵的正中心称为花心. 我们在 xOz 平面上绘制花朵,假设花心为 O ...
- 【Lua】ToLua逻辑热更新
1 前言 Lua基础语法 中系统介绍了 Lua 的语法体系,xLua逻辑热更新 中介绍了 xLua 的应用,本文将进一步介绍 Unity3D 中基于 ToLua 实现逻辑热更新. 逻辑热更新是 ...
- Java设计模式-装饰者模式Decorator
介绍 装饰者模式的核心思想是通过创建一个装饰对象(即装饰者),动态扩展目标对象的功能,并且不会改变目标对象的结构,提供了一种比继承更灵活的替代方案.需要注意的是,装饰对象要与目标对象实现相同的接口,或 ...
- 配置nginx反向代理
最近在做一个前后分离的项目,前端用Vue,后台spring boot,使用nginx做反向代理.下面说一下如何配置: 启动spring boot项目,端口8110 启动nginx 修改nginx.co ...
- 解密C语言选择结构:掌握条件语句与分支逻辑的利器
欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言学习 贝蒂的主页:Betty's blog 引言 C语⾔是结构化的程序设计语⾔,这⾥的结构指的是顺序结构.选择结构.循环结构.为什么有 ...
- error接口
// 实际上它是interface类型,这个类型有一个返回错误信息的单一方法: type error interface{ Error() string } 创建一个error最简单的方法就是调用er ...