简介

使用SDL实现简单的YUV播放器。

这里还需要使用到像素格式和计算图片大小,这两个我们直接使用ffmpeg来实现,因此需要使用ffmpeg的libavutil/avutil.hlibavutil/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视频的更多相关文章

  1. 使用DirectDraw直接显示YUV视频数据

    最近在编写一个进行视频播放的ActiveX控件,工作已经接近尾声,现将其中显示YUV数据的使用DirectDraw的一些经验总结如下:(解码部分不是我编写的,我负责从网络接收数据,将数据传给解码器,并 ...

  2. directdraw显示yuv视频,出现屏保时,yuv显示不出来,表面丢失

    原因是: DDrawSurface 丢失, DDraw表面在很多情况下都会丢失(如:启动其他全屏独占程序,屏保,或锁屏时), 表面丢失其实就是表面所使用的内存或显存被DirectDraw系统释放, 分 ...

  3. SDL 开发实战(六): 使用 SDL 实现 YUV 播放器

    前面铺垫了这么多,现在终于进入核心的主题了,那就是使用SDL播放视频,本节我们将使用SDL播放YUV视频,也就是做一个YUV播放器. 下面说明一下使用SDL播放YUV视频的基本流程,主要分为两大部分: ...

  4. avi视频格式转yuv格式与播放yuv视频

    因为要用到yuv格式视频.而眼下仅仅有avi格式的视频,所以须要转换,而且opencv不支持yuv编码的视频播放.所以须要转换为rgb编码.而后播放.写了两个程序.以供參考: 1,avi格式视频转yu ...

  5. FFmpeg YUV视频序列编码为视频

    上一篇已经写了如何配置好开发环境,这次就先小试牛刀,来个视频的编码.搞视频处理的朋友肯定比较熟悉YUV视频序列,很多测试库提供的视频数据都是YUV视频序列,我们这里就用用YUV视频序列来做视频.关于Y ...

  6. 使用D3D渲染YUV视频数据

    源代码下载 在PC机上,对于YUV格式的视频如YV12,YUY2等的显示方法,一般是采用DIRECTDRAW,使用显卡的OVERLAY表面显示.OVERLAY技术主要是为了解决在PC上播放VCD而在显 ...

  7. Android上使用OpenGLES2.0显示YUV数据

    在Android上用OpenGLES来显示YUV图像,之所以这样做,是因为: 1.Android本身也不能直接显示YUV图像,YUV转成RGB还是必要的: 2.YUV手动转RGB会占用大量的CPU资源 ...

  8. 使用D3D渲染YUV视频数据<转>

    源代码下载 转载地址:http://blog.csdn.net/dengzikun/article/details/5824874 源码地址:http://download.csdn.net/down ...

  9. Android用surface直接显示yuv数据(三)

    本文用Java创建UI并联合JNI层操作surface来直接显示yuv数据(yv12),开发环境为Android 4.4,全志A23平台. package com.example.myyuvviewe ...

  10. Video标签播放视频?谷歌浏览器?safari?? 谷歌浏览器播放不了mp4格式的视频的原因

    webm格式和mp4格式,判断了浏览器能否支持的视频类型后,给了一个if判断,如果是支持mp4格式,就返回视频后缀mp4,如果是webm,就返回后缀webm.结果,在谷歌浏览器中播放不了,为什么我指定 ...

随机推荐

  1. 简单总结Tomcat/SpringMVC/Spring/SpringBoot的关系

    Tomcat是一个Web应用服务器,可以作为Servlet容器.它的作用是,解析客户端client发起的request,并组装出HttpRequest.创建HttpResponse,将二者交于内部的H ...

  2. PyTorch下,使用list放置模块,导致计算设备不一的报错

    报错 在复现 Transformer 代码的训练阶段时,发生报错: RuntimeError: Expected all tensors to be on the same device, but f ...

  3. Java并发(六)----线程start、run、state方法

    1.start 与 run 调用 run public static void main(String[] args) {    Thread t1 = new Thread("t1&quo ...

  4. electron 安装不同的版本的方法

    1.官网:http://www.electronjs.org/ 2.思考,既然是npm 安装,那么肯定也在 npm中央仓库有,那么去中央仓库看下: npm i -D electron@11.0.4

  5. DBGRIDEH 排序 我自己大总结【含DBX,Dac控件】

    1.三个属性让DBGridEH可以点击表头自动排序 只要设置下面三个属性: ColumDefValues->Title->TitleButton设为TRUE  sortlocal   设为 ...

  6. .NET Core开发实战(第15课:选项框架:服务组件集成配置的最佳实践)--学习笔记

    15 | 选项框架:服务组件集成配置的最佳实践 这一节讲解如何使用选项框架来处理服务和配置的关系 选项框架的特性: 1.支持单例模式读取配置 2.支持快照 3.支持配置变更通知 4.支持运行时动态修改 ...

  7. [刺客伍六七&黑客] 魔刀千刃evilblade的使用手册与开源

    0x00 前言 2023.8.15 夜里 非常欢迎使用我的魔刀千刃,并且欢迎各位师傅对我的开源代码进行指导! -–Offense without defense, unparalleled in th ...

  8. JS 记一次工作中,由深度优先到广度优先的算法优化

    壹 ❀ 引 坦白的说,本人的算法简直一塌糊涂,虽然有刷过一段时间的算法题,但依然只能解决不算复杂的问题,稍微麻烦的问题都只是站在能不能解决问题的角度,至于性能优化,算法方法的选择并没有过于深刻的理解. ...

  9. zookeeper源码(08)请求处理及数据读写流程

    ServerCnxnFactory 用于接收客户端连接.管理客户端session.处理客户端请求. ServerCnxn抽象类 代表一个客户端连接对象: 从网络读写数据 数据编解码 将请求转发给上层组 ...

  10. TS内置类型与拓展

    TS内置类型与拓展 TypeScript具有类型系统,且是JavaScript的超集,其可以编译成普通的JavaScript代码,也就是说,其是带有类型检查的JavaScript. 内置类型 Type ...