用ffmpeg+qt做点有趣的事(4)----- 简易视频播放器
来源:互联网 发布:咸鱼如何申请淘宝介入 编辑:程序博客网 时间:2024/06/05 17:06
前面讲解了如何用FFMPEG解码视频。
现在,我们就着手用FFMPEG+Qt写一个视频播放器吧:
由于现在我们需要显示图像了,因此现在开始需要使用Qt GUI工程了。
创建工程的时候记得选择Qt GUI应用。
引用FFMPEG请参考前面的文章,这里不再介绍。
做过图像界面开发的都知道,任何耗时的操作都不能放在主线程进行,一旦主线程阻塞了,那么体现出来的就是界面卡了。 而我们读取视频和解码视频是一个非常耗时的操作,因此需要另外开辟一个线程来专门做这件事。
Qt里面线程的用法 则是写一个类继承QThread, 然后重载其run函数,把耗时的操作全部放入run函数。
class
VideoPlayer :
public
QThread
{
Q_OBJECT
public
:
explicit
VideoPlayer();
~VideoPlayer();
protected
:
void
run();
};
这里run函数里面就是写我们读取视频和解码视频的代码了;
读取和解码还是和前面说的一样的方法:
改动的一点是:
由于我们需要用Qt的控件来显示,因此是把解码之后的YUV数据转换成了RGB32格式的数据:
///这里我们改成了 将解码后的YUV数据转换成RGB32
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes *
sizeof
(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB32,
pCodecCtx->width, pCodecCtx->height);
....
...
..
sws_scale(img_convert_ctx,
(uint8_t
const
*
const
*) pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);
同时将转换后的RGB32数据存入QImage对象:
//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
由于我们不能够在子线程中操作界面,(操作界面只能在主线程中进行,几乎所有的图形界面开发都是这样设定),因此我们只能给主线程发送信号,信号带上这个QIMage,让主线程帮忙把这个图像显示出来。
声明信号:
signals:
void
sig_GetOneFrame(QImage);
//没获取到一帧图像 就发送此信号
发送信号:
//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
QImage image = tmpImg.copy();
//把图像复制一份 传递给界面显示
emit sig_GetOneFrame(image);
//发送信号
主线程绑定并接收信号:
mPlayer =
new
VideoPlayer;
connect(mPlayer,SIGNAL(sig_GetOneFrame(QImage)),
this
,SLOT(slotGetOneFrame(QImage)));
信号处理函数如下:
void
MainWindow::slotGetOneFrame(QImage img){
mImage = img;
update();
//调用update将执行 paintEvent函数
}
主线程显示图像 则是通过QPainter直接绘制:
void
MainWindow::paintEvent(QPaintEvent *event){
QPainter painter(
this
);
painter.setBrush(Qt::black);
painter.drawRect(0, 0,
this
->width(),
this
->height());
//先画成黑色
if
(mImage.size().width() <= 0)
return
;
///将图像按比例缩放成和窗口一样大小
QImage img = mImage.scaled(
this
->size(),Qt::KeepAspectRatio);
int
x =
this
->width() - img.width();
int
y =
this
->height() - img.height();
x /= 2;
y /= 2;
painter.drawImage(QPoint(x,y),img);
//画出图像
}
运行之后的程序如下:
当然这个离播放器还有十万八千里的步伐,后面我们将会一步一步完善它。
古语有云:不积跬步,无以至千里;不积小流,无以成江海。
写代码也一样,得慢慢来,一步一步往目标走,总能成功。
主程序:
#include <QApplication>
#include <QTextCodec>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTextCodec *codec = QTextCodec::codecForName("UTF-8"); //设置编码格式为UTF-8
QTextCodec::setCodecForLocale(codec);
//QTextCodec::setCodecForCString(codec);
//QTextCodec::setCodecForTr(codec);
MainWindow w;
w.show();
return a.exec();
}
MainWindow类的cpp文件:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPainter>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mPlayer = new VideoPlayer;
connect(mPlayer,SIGNAL(sig_GetOneFrame(QImage)),this,SLOT(slotGetOneFrame(QImage)));
mPlayer->setFileName("./1111.mp4");
mPlayer->startPlay();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setBrush(Qt::black);//设置画刷颜色
painter.drawRect(0, 0, this->width(), this->height()); //先画成黑色
if (mImage.size().width() <= 0)
return;
///将图像按比例缩放成和窗口一样大小
QImage img = mImage.scaled(this->size(),Qt::KeepAspectRatio);
int x = this->width() - img.width();
int y = this->height() - img.height();
x /= 2;
y /= 2;
painter.drawImage(QPoint(x,y),img); //画出图像
}
void MainWindow::slotGetOneFrame(QImage img)
{
mImage = img;
update(); //调用update将执行 paintEvent函数
}
MainWindow类的h文件:#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QImage>
#include <QPaintEvent>
#include "videoplayer/videoplayer.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
protected:
void paintEvent(QPaintEvent *event);
private:
Ui::MainWindow *ui;
VideoPlayer *mPlayer; //播放线程
QImage mImage; //记录当前的图像
private slots:
void slotGetOneFrame(QImage img);
};
#endif // MAINWINDOW_H
videoplayer类:
#ifndef VIDEOPLAYER_H
#define VIDEOPLAYER_H
#include <QThread>
#include <QImage>
class VideoPlayer : public QThread
{
Q_OBJECT
public:
explicit VideoPlayer();
~VideoPlayer();
void setFileName(char* path)
{
int len = strlen(path);
int i;
for(i=0;i<len;i++)
mFileName[i] = path[i];
mFileName[i] = '\0';
}
void startPlay();
signals:
void sig_GetOneFrame(QImage); //没获取到一帧图像 就发送此信号
protected:
void run();
private:
char mFileName[25];
};
#endif // VIDEOPLAYER_H
#include "videoplayer.h"
#include <QDebug>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"
}
#include <stdio.h>
VideoPlayer::VideoPlayer()
{
}
VideoPlayer::~VideoPlayer()
{
}
void VideoPlayer::startPlay()
{
///调用 QThread 的start函数 将会自动执行下面的run函数 run函数是一个新的线程
this->start();
}
void VideoPlayer::run()
{
// char *file_path = mFileName.toUtf8().data();
//qDebug()<<*file_path;
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame, *pFrameRGB;
AVPacket *packet;
uint8_t *out_buffer;
static struct SwsContext *img_convert_ctx;
int videoStream, i, numBytes;
int ret, got_picture;
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器
//Allocate an AVFormatContext.
pFormatCtx = avformat_alloc_context();
if (avformat_open_input(&pFormatCtx, mFileName, NULL, NULL) != 0) {
printf("can't open the file11. \n");
return;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("Could't find stream infomation.\n");
return;
}
videoStream = -1;
///循环查找视频中包含的流信息,直到找到视频类型的流
///便将其记录下来 保存到videoStream变量中
///这里我们现在只处理视频流 音频流先不管他
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
}
}
///如果videoStream为-1 说明没有找到视频流
if (videoStream == -1) {
printf("Didn't find a video stream.\n");
return;
}
///查找解码器
pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL) {
printf("Codec not found.\n");
return;
}
///打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("Could not open codec.\n");
return;
}
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
///这里我们改成了 将解码后的YUV数据转换成RGB32
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
numBytes = avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB32,
pCodecCtx->width, pCodecCtx->height);
int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据
av_dump_format(pFormatCtx, 0, mFileName, 0); //输出视频信息
while (1)
{
if (av_read_frame(pFormatCtx, packet) < 0)
{
break; //这里认为视频读取完了
}
if (packet->stream_index == videoStream) {
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);
if (ret < 0) {
printf("decode error.\n");
return;
}
if (got_picture) {
sws_scale(img_convert_ctx,
(uint8_t const * const *) pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);
//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)out_buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
emit sig_GetOneFrame(image); //发送信号
}
}
av_free_packet(packet);
msleep(5); //停一停 不然放的太快了
}
av_free(out_buffer);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
}
- 用ffmpeg+qt做点有趣的事(4)----- 简易视频播放器
- 用Qt+ffmpeg做点有趣的事(5)--- FFMPEG Qt视频播放器之SDL的使用
- 用ffmpeg+Qt做点有趣的事(1)
- 用ffmpeg+qt做点有趣的事(2)
- 用ffmpeg+qt做点有趣的事(3)----- ffmpeg解码流程
- 用Qt和ffmpeg做点有趣的事(10)录频软件开发0.1
- mfc对话框做一个简易的ffmpeg视频播放器
- 用ffmpeg做的视频播放demo
- 一个基于ffmpeg的简易视频播放器
- QT5.7+opencv3.0做的简易视频播放器
- 用live555+ffmpeg做iOS平台的实时视频播放
- 一个基于ffmpeg+QT的视频播放器
- FFMPEG Qt视频播放器之SDL的使用
- QT+ffmpeg视频播放器学习知识点
- 从零开始学习音视频编程技术(十) FFMPEG Qt视频播放器之播放控制
- 从零开始学习音视频编程技术(十) FFMPEG Qt视频播放器之播放控制
- Qt+FFmpeg的视频播放器--(先占位,后面续)
- Qt调用mplayer做一个小小的视频播放器
- 2017第八节蓝桥杯决赛_36进制
- mybatis配置文件
- 学习总结一(数据库表关系)
- TP5完成第三方登入
- 并查集_无线网络
- 用ffmpeg+qt做点有趣的事(4)----- 简易视频播放器
- C语言——回调函数
- 树莓派安装ntru-crypto-master的时候:cannot open shared object file: No such file or directory
- 规则区域填充
- 2017第八届安桥杯决赛_磁砖样式
- VC2010下编译OpenCV3.2问题记录
- 草根程序员转型做项目管理走过的点点滴滴之_编程的热爱与坚持
- IMWeb提升营Day4 | 训练题24:二叉树中和为某一值的路径
- 天天都在用的 SDWebImage, 你了解它的缓存策略吗?