视频解码
流程图
步骤
加入需要的头文件
1,注册所有组件 av_register_all()
2,打开视频文件 avformat_open_input(),(判断是否打开成功)
3,取视频相关信息:视频码流,音频码流,文字码流
4,查找流信息: avformat_find_stream_infp()
5,从查找到的流信息中找到视频码流信息
6,找到解码器 avcodec_find_decoder()(判断是否找到)
7,打开解码器 avcodec_open2()(判断是否打开成功)
8,读取码流中的一帧码流数据 av_read_frame()
9,解码读到的这一帧码流数据,得到一帧的像素数据,YUV,RGB 进行保存
Avcodec_decode_video2()
10,重复 8,9,动作,直到视频的所有帧都处理完
11,关闭解码器 avcode_close()
12,关闭视频文件 avcode_clode_input()
解码类package_decoder代码
package_decoder.h
#ifndef PACKAGE_DECODER_H
#define PACKAGE_DECODER_H
#include
#include
#include
//#include "package_ecoder.h"
extern "C"
{
#include
#include
#include
#include
#include
#include
#include
}
class package_decoder:public QThread
{
Q_OBJECT
public:
package_decoder();
int opendecoderbycideo(const char *filename);
void videodecode();
void videodecodergb();
void setstate(int state);
void setspeed(int speed);
void run();
private:
AVFormatContext *forcontent;
const char *filename;
int videoType;
AVCodecContext *codec;
AVCodec *decoder;
AVPacket *pkt;
FILE *fp;
FILE *fpyuv;
int size;
AVFrame *pictureyuv;
AVFrame *picturergb;
AVFrame *picture;
AVInputFormat *fmt;
SwsContext * rgbsws;
SwsContext * sws;
//package_ecoder *ecoder;
int state;
int speed;
signals:
void sendimage(QImage img);
void sendyuv(AVFrame * yuv);
void sendend();
};
#endif // PACKAGE_DECODER_H
package_decoder.cpp
#include "package_decoder.h"
package_decoder::package_decoder()
{
av_register_all();
avdevice_register_all();//注册摄像头
forcontent =avformat_alloc_context();//分配内存空间
pkt = (AVPacket *)malloc(sizeof (AVPacket));//分配内存空间
pictureyuv=nullptr;
picturergb=nullptr;//保存rgb像素文件
picture=nullptr;//保存包含损坏数据的像素文件
pictureyuv =av_frame_alloc();
picture =av_frame_alloc();//开空间
picturergb =av_frame_alloc();
rgbsws =nullptr;
sws=nullptr;
//ecoder = new package_ecoder;
state=1;
speed=1;
}
/*
函数名:opendecoderbycideo
返回值: 如果返回值为1,执行成功;返回-1,执行失败
*/
int package_decoder::opendecoderbycideo(const char *filename)
{
int sign=-1;
fmt= av_find_input_format("dshow");//推流(windows系统)
//打开视频文件
int res=avformat_open_input(&forcontent,filename,fmt,nullptr);
//int res=avformat_open_input(&forcontent,filename,nullptr,nullptr);
if(res!=0)
{
return -1;
}
//查找流数据
res=avformat_find_stream_info(forcontent,nullptr);
if(res<0)
{
return -1;
}
//从查找到的流信息中找到视频码流信息
videoType=-1;//标记视频流索引
for(int i=0;inb_streams;i++)
{
if(forcontent->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)//视频流
{
videoType=i;
break;
}
}
if(videoType==-1)
{
return -1;
}
//根据视频流信息中的编解码器id去找合适的解码器
codec=forcontent->streams[videoType]->codec;
decoder = avcodec_find_decoder(codec->codec_id);
//判断是否找到对应的解码器
if(decoder==nullptr)//没找到解码器
{
return -1;
}
//找到解码器后,打开解码器
res=avcodec_open2(codec,decoder,nullptr);
if(res==0)
{
sign=1;
}
else
{
return -1;
}
return sign;
}
//
/*
解码
*/
void package_decoder::videodecode()
{
size =codec->width*codec->height;//计算一帧码流数据的大小
av_new_packet(pkt,size);//开空间,用于存储一帧码流数据
//像素数据
pictureyuv->width=codec->width;
pictureyuv->height=codec->height;
pictureyuv->format=codec->pix_fmt;//格式设置
//获取到一帧图像大小(yuv)
int numByte=avpicture_get_size(AV_PIX_FMT_YUV420P,codec->width,codec->height);
uint8_t * buffer=(uint8_t *)av_malloc(numByte *sizeof (uint8_t));//分配内存空间存像素数据
//像素数据填充到AVFvame
avpicture_fill((AVPicture *)pictureyuv,buffer,AV_PIX_FMT_YUV420P,codec->width,codec->height);
//转换的规则设置(剔除压缩后的坏数据)
sws=sws_getContext(codec->width,codec->height,codec->pix_fmt,codec->width,codec->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,nullptr,nullptr,nullptr);
//编码前准备
//ecoder->condeInit(codec->width,codec->height);
int num=0;
while(av_read_frame(forcontent,pkt)==0)//读到了一帧
{
if(pkt->stream_index==videoType)//视频流
{
fwrite(pkt->data,1,pkt->size,fp);//保存码流数据
int got_picture=-1;
avcodec_decode_video2(codec,picture,&got_picture,pkt);
if(got_picture!=0)//解码得到了数据
{
num++;
if(num==100)
{
while(1)
{
}
}
//进行损坏数据的删除
sws_scale(sws,picture->data,picture->linesize,0,picture->height,pictureyuv->data,pictureyuv->linesize);
fwrite(pictureyuv->data[0],1,size,fpyuv);
fwrite(pictureyuv->data[1],1,size/4,fpyuv);
fwrite(pictureyuv->data[2],1,size/4,fpyuv);
//ecoder->codecFrame(pictureyuv);
}
}
av_packet_unref(pkt);//清空
}
//写入尾巴帧
// ecoder->writeEnd();
qDebug()<<"写入成功";
fclose(fp);
fclose(fpyuv);
}
void package_decoder::videodecodergb()
{
size =codec->width*codec->height;//计算一帧码流数据的大小
av_new_packet(pkt,size);//开空间,用于存储一帧码流数据
//像素数据
picturergb->width=codec->width;
picturergb->height=codec->height;
picturergb->format=codec->pix_fmt;//格式设置
pictureyuv->width=codec->width;
pictureyuv->height=codec->height;
pictureyuv->format=codec->pix_fmt;//格式设置
int numrgb=avpicture_get_size(AV_PIX_FMT_RGB32,codec->width,codec->height);
uint8_t * rgbbuffer=(uint8_t *)av_malloc(numrgb *sizeof (uint8_t));//分配内存空间存像素数据
//像素数据填充到AVFvame
avpicture_fill((AVPicture *)picturergb,rgbbuffer,AV_PIX_FMT_RGB32,codec->width,codec->height);
//转换的规则设置(剔除压缩后的坏数据)
rgbsws=sws_getContext(codec->width,codec->height,codec->pix_fmt,codec->width,codec->height,AV_PIX_FMT_RGB32,SWS_BICUBIC,nullptr,nullptr,nullptr);
//yuv//
//像素数据
//获取到一帧图像大小(yuv)
int numByte=avpicture_get_size(AV_PIX_FMT_YUV420P,codec->width,codec->height);
uint8_t * buffer=(uint8_t *)av_malloc(numByte *sizeof (uint8_t));//分配内存空间存像素数据
//像素数据填充到AVFvame
avpicture_fill((AVPicture *)pictureyuv,buffer,AV_PIX_FMT_YUV420P,codec->width,codec->height);
//转换的规则设置(剔除压缩后的坏数据)
sws=sws_getContext(codec->width,codec->height,codec->pix_fmt,codec->width,codec->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,nullptr,nullptr,nullptr);
//编码前准备
//ecoder->condeInit(codec->width,codec->height);
int num=0;
QString path;
while(av_read_frame(forcontent,pkt)>=0)//读到了一帧
{
if(num==50)
{
emit sendend();
//break;
}
//暂停播放
while(this->state%2==0)
{
}
if(pkt->stream_index==videoType)//视频流
{
int got_picture=-1;
avcodec_decode_video2(codec,picture,&got_picture,pkt);
if(got_picture!=0)//解码得到了数据
{
//进行损坏数据的删除
sws_scale(sws,picture->data,picture->linesize,0,picture->height,pictureyuv->data,pictureyuv->linesize);
//编码
// ecoder->codecFrame(pictureyuv);
sws_scale(rgbsws,picture->data,picture->linesize,0,picture->height,picturergb->data,picturergb->linesize);
//每25帧保存一次图片
QImage img = QImage((uchar *)rgbbuffer,codec->width,codec->height,QImage::Format_RGB32);
//path = QString("RGB32/image%1.png").arg(num);
//img.save(path);
emit sendimage(img);
emit sendyuv(pictureyuv);
num++;
if(speed%3==1)//正常倍数
{
msleep(50);
}
else if(speed%3==2)//1.5倍数
{
msleep(50/1.5);
}
else if(speed%3==0)//2倍数
{
msleep(50/2);
}
}
}
av_packet_unref(pkt);//清空
av_frame_unref(picture);
}
//写入尾巴帧
//ecoder->writeEnd();
qDebug()<<"写入成功";
}
void package_decoder::setstate(int state)
{
this->state=state;
}
void package_decoder::setspeed(int speed)
{
this->speed=speed;
}
void package_decoder::run()
{
const char *filename="video=USB2.0 HD UVC WebCam";//视频文件路径
//const char *filename="Warcraft3_End.avi";
//根据打开一个视频文件打开一个解码器
this->opendecoderbycideo(filename);
//解码
this->videodecodergb();
}