目录
一、解码线程的设计
1.解码初始化
(1)注册所有组件
av_register_all();//第一步注册所有组件
(2)打开本地片源
(3) 查找视频流
(4)查找对应的解码器
(5)打开解码器![](https://img-blog.csdnimg.cn/278809e96d9a4af8a623a7b3db3b010e.png)
以上就是解码的初始化工作,附上完整源码
#include "video.h"
video::video(QString filename)
{
this->m_stop=false;
this->current_frame_index = 0;
openVideoFile(filename);
}
void video::openVideoFile(QString filename)
{
qDebug()<<"1.注册所有组件";
av_register_all();//第一步注册所有组件
qDebug()<<"2.打开视频输入文件";
//给avformat_context开空间
avformat_context = avformat_alloc_context();
//avformat_open_input参数一:视频文件格式上下文->AVFormatContext->包含了视频信息(视频名、时长、大小等)
//参数二:文件路径
qDebug()<<"打开"<<filename<<"视频文件进行播放";
int avformat_open_result = avformat_open_input(&avformat_context,filename.toStdString().c_str(),NULL,NULL);
if (avformat_open_result != 0)
{
//获取异常信息
char* error_info = new char[32];
av_strerror(avformat_open_result, error_info, 100);
qDebug()<<QString("异常信息 %1").arg(error_info);
};
qDebug()<<"3.查找视频流信息";
//参数一:视频封装格式上下文->AVFormatContext; 参数二:配置,可以不设置
//返回值:>=0找到,否则失败
int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context,NULL);
if (avformat_find_stream_info_result < 0)
{
//获取失败
char* error_info = new char[32];
av_strerror(avformat_find_stream_info_result, error_info, 100);
qDebug()<<QString("异常信息 %1").arg(error_info);//打印异常信息
}
av_stream_index = -1;
for (int i = 0; i < avformat_context->nb_streams; i++)
{
//循环遍历每一流找到视频流
if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
//找到了
av_stream_index = i;
break;
}
}
if (av_stream_index == -1)
{
qDebug()<<QString("没有找到视频流");
}
qDebug()<<"4.查找解码器";
//第一点:获取当前解码器是属于什么类型解码器->找到了视频流
//第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
avcodec_context = avformat_context->streams[av_stream_index]->codec;//编解码器上下文
//第三点:根据解码器上下文->获取解码器ID
avcodec = avcodec_find_decoder(avcodec_context->codec_id);
if (avcodec == NULL)
{
qDebug()<<QString("没有找到视频解码器");
}
qDebug()<<"5.打开解码器";
int avcodec_open2_result = avcodec_open2(avcodec_context,avcodec,NULL);
if (avcodec_open2_result != 0)
{
char* error_info = new char[32];
av_strerror(avformat_find_stream_info_result, error_info, 100);
qDebug()<<QString("异常信息 %1").arg(error_info);
}
qDebug()<<"视频详细信息输出";
qDebug()<<"视频名称"<<avformat_context->filename;
qDebug()<<"视频时长/s"<<avformat_context->duration/1000000;
qDebug()<<QString("视频分辨率%1x%2").arg(avcodec_context->width).arg(avcodec_context->height);
//自动打印输入或输出的详细信息
av_dump_format(avformat_context,0, filename.toStdString().c_str(),0);
}
2.run函数循环解码
首先要做的是初始化包数据、输入输出帧数据、缓冲区、转换图片格式参数准备等
然后循环读取视频帧,进行循环解码
(1)图片转成RGB格式
(2)发送图片信号
(3)发送一张图片计数+1
(4)延时操作——1s显示25帧图片
void video::run()
{
qDebug()<<"6.循环解码";
//packet开空间
av_packet = (AVPacket*)av_malloc(sizeof(AVPacket));
//输入->环境一帧数据->缓冲区->类似于一张图
pFramein = av_frame_alloc();
//输出->帧数据->数据格式->RGB
pFrameRGB = av_frame_alloc();
//只有指定了AVFrame的像素格式、画面大小才能真正分配内存
//缓冲区分配内存
pOutbuffer = (uint8_t *)av_malloc(avpicture_get_size(
AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height));
//初始化缓冲区 类似于memset
avpicture_fill((AVPicture *)pFrameRGB, pOutbuffer,
AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height);
//解码的状态类型(0:表示解码完毕,非0:表示正在解码)
//int current_frame_index = 0;
//用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
//准备一个视频像素数据格式上下文
//参数一:输入帧数据宽
//参数二:输入帧数据高
//参数三:输入帧数据格式
//参数四:输出帧数据宽
//参数五:输出帧数据高
//参数六:输出帧数据格式->AV_PIX_FMT_RGB32
//参数七:视频像素数据格式转换算法类型
//参数八:字节对齐类型(C/C++里面)->提高读取效率
SwsContext* pSwsContext = sws_getContext(avcodec_context->width,
avcodec_context->height,
avcodec_context->pix_fmt,
avcodec_context->width,
avcodec_context->height,
AV_PIX_FMT_RGB32,
SWS_BICUBIC,NULL,NULL,NULL);
int ret=0;
while (m_stop == false)
{
//>=0:说明有数据,继续读取 <0:说明读取完毕,结束
//从视频文件上下文中读取包--- 有数据就一直读取
if (av_read_frame(avformat_context,av_packet) >= 0)
{
//解码什么类型流(视频流、音频流、字幕流等等...)
if (av_packet->stream_index == av_stream_index)
{
//发送一个包数据进行解码
avcodec_send_packet(avcodec_context, av_packet);
//接收一个包数据,解压成一帧
ret = avcodec_receive_frame(avcodec_context,pFramein);
if (ret == 0)
{
//图片格式的转换 输入 输出
sws_scale(pSwsContext, (const unsigned char* const*)pFramein->data, pFramein->linesize, 0, avcodec_context->height,
pFrameRGB->data, pFrameRGB->linesize);
QImage *tmpImg = new QImage((uchar *)pOutbuffer, avcodec_context->width,
avcodec_context->height,QImage::Format_RGB32);
QImage image=tmpImg->copy();
qDebug()<<"接收图片信号"<<image;
//计数第几张图片
current_frame_index++;
//发送图片信号
emit sigGetOneFrame(image);
//emit SendOneData(current_frame_index);
//延时操作 1秒显示25帧--1000/25=40
QThread::msleep(timeSpeed);
//获取的视频信息
qDebug()<<QString("当前遍历第 %1 帧").arg(current_frame_index);
}
}
}
else
{
qDebug()<<"播放完毕";
this->current_frame_index=0;//清空计数
this->stop();
}
av_free_packet(av_packet);
}
}
video::~video()
{
qDebug()<<"7.关闭所有解码组件";
av_packet_free(&av_packet);
//关闭流
avcodec_close(avcodec_context);
avformat_free_context(avformat_context);
}
void video::stop()
{
m_stop = true;
}
头文件
#ifndef VIDEO_H
#define VIDEO_H
#include <QString>
#include <QDebug>
#include "mythread.h"
#include <QImage>
//当前C++兼容C语言
extern "C"
{
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
//avformat:封装格式处理
#include <libavformat/avformat.h>
//swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
//avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
//avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
#include "libswresample/swresample.h"
}
class video : public myThread//自定义线程类,可以直接继承QThread
{
Q_OBJECT
public:
video(QString filename);
~video();
//打开视频文件
void openVideoFile(QString filename);
void run();//重写run函数,线程执行任务
void stop();
//定义停止的标志位
bool m_stop;
private:
//视频文件上下文格式
AVFormatContext* avformat_context;
//编解码器上下文格式
AVCodecContext* avcodec_context;
//解码器上下文格式
AVCodec* avcodec;
//数据包
AVPacket* av_packet;
//保存视频流的索引
int av_stream_index;
//输入和输出的帧数据
AVFrame *pFramein;
AVFrame *pFrameRGB;
uint8_t * pOutbuffer;
int ret;
QImage m_image;
//计算第几帧图片
int current_frame_index;
signals:
//发送解码得到的一帧帧像素数据--保存的图片信息
void sigGetOneFrame(QImage image);
};
#endif // VIDEO_H
二、播放窗口类的设计
构造函数中
1.创建窗口
2.创建解码线程
3.开启解码线程
4.绑定信号槽接收图片信号
5.界面刷新显示
绘图事件
playVideoWidget::playVideoWidget(QString filename)
{
this->resize(700,468);
this->setWindowTitle("播放器");
this->filename=filename;
//创建解码线程
toH264=new video(filename);
connect(toH264,SIGNAL(sigGetOneFrame(QImage)),this,SLOT(recviceSlots(QImage)));//接收图片信号槽
//启动线程
toH264->start();
}
void playVideoWidget::recviceSlots(QImage imge)
{
//接收解码线程发送的一帧图片数据
qDebug()<<"recive imge";
this->image=imge;
//更新界面
this->update();
}
void playVideoWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
if(!this->image.isNull())//如果图片不为空
{
painter.drawImage(QRect(0,0,this->width(),this->height()),this->image);
}
}
效果
这样就可以播放本地视频了
感谢观看!!!!
以上就是全部内容,如果对您有帮助,欢迎点赞评论,或者发现有哪里写错的,欢迎指正!