网站商城建设视频教程排名优化外包公司
目录
1、视频解码流程分析
2、FFmpeg相关解码函数
3、创建一个类专门用来视频解码
.h文件
构造函数
打开视频文件
获取视频文件流信息并查找是否有视频流
查找视频流对应的解码器
打开解码器
循环读取每一帧并生成.h264、.yuv文件
关闭解码用到的组件
主函数测试
4、测试效果
运行效果
查看h264文件
查看yuv文件
一个视频包含有音频流、视频流、字幕流等信息,下面为视频流的解码过程以及代码实现。
1、视频解码流程分析
/*解码流程分析:* 1.注册所有组件* 2.打开文件* 3.获取文件信息* 4.看看有没有相关视频信息* 5.有视频信息,找他对应的解码器* 6.打开解码器* 7.获取一帧视频流(码流)信息* 8.读取,解码的动作* 9.得到像素数据* 重复7、8、9的动作,直到没有码流数据* 10.关闭解码器* 11.关闭视频流文件** */
2、FFmpeg相关解码函数
av_register_all():注册所有组件。avformat_open_input():打开输入视频文件。avformat_find_stream_info():获取视频文件信息。avcodec_find_decoder():查找解码器。avcodec_open2():打开解码器。av_read_frame():从输入文件读取一帧压缩数据。avcodec_decode_video2():解码一帧压缩数据。avcodec_close():关闭解码器。avformat_close_input():关闭输入视频文件。
3、创建一个类专门用来视频解码
.h文件
#include <QString>extern "C"
{#include <libavcodec/avcodec.h>#include <libavdevice/avdevice.h>#include <libavformat/avformat.h>#include <libavutil/avconfig.h>#include <libswscale/swscale.h>#include <libswresample/swresample.h>
}class fDecode
{
public:fDecode();void openMvFile(QString mvPath);//打开视频文件void findVideoStream();//查找视频流void findDecoder();//查找对应的解码器void openDecoder();//打开解码器void readFrame();//读取每一帧void closeAll();//关闭用到的组件private:AVFrame *in_frame,*out_frame;//保存解码后的像素数据int avcodec_index;//视频流所在输入视频的AVStream []数组的索引AVFormatContext* avformat_context;//封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息AVCodecContext* avcodec_context;//编码器上下文结构体,保存了视频(音频)编解码相关信息AVCodec* avcodec;//每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体
};
构造函数
fDecode::fDecode()
{ //注册组件avcodec_register_all();qDebug()<<"————注册所有组件————";qDebug()<<"注册成功!";
}
打开视频文件
void fDecode::openMvFile(QString mvPath)
{qDebug()<<"————打开视频文件————";avformat_context = avformat_alloc_context();//封装格式上下文结构体开空间int res = avformat_open_input(&avformat_context,mvPath.toStdString().c_str(),NULL,NULL);//return 0 on successif (res != 0){qDebug()<<"打开失败";exit(0);}else{qDebug()<<"打开成功!";}
}
获取视频文件流信息并查找是否有视频流
void fDecode::findVideoStream()
{qDebug()<<"————获取文件信息————";this->avcodec_index = -1;int res = avformat_find_stream_info(avformat_context, NULL);//return >=0 if OKif (res < 0){qDebug()<<"获取文件信息失败";exit(0);}else{qDebug()<<"获取文件信息成功!";qDebug()<<"视频时长为:"<<avformat_context->duration/1000000.0<<"秒";qDebug()<<"视频平均混合码率为:"<<avformat_context->bit_rate/1000<<"Kbps";qDebug()<<"————获取文件视频流信息————";//nb_streams :输入视频的AVStream 个数for (int i = 0; i < avformat_context->nb_streams; i++)//遍历流{//找到视频流if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){this->avcodec_index = i;qDebug()<<"获取文件视频流信息成功!";break;}}if (-1 == this->avcodec_index){qDebug()<<"没有找到视频流";exit(0);}}return;
}
查找视频流对应的解码器
void fDecode::findDecoder()
{qDebug()<<"————查找对应的解码器————";//avcodec_index为视频流所在输入视频的AVStream []数组的索引,通过上一步查找视频流得到的//avcodec_context 编码器上下文结构体,保存了视频(音频)编解码相关信息avcodec_context = avformat_context->streams[this->avcodec_index]->codec;//根据解码器id查找解码器对应的结构体avcodec = NULL;avcodec = avcodec_find_decoder(avcodec_context->codec_id);if (NULL == avcodec){qDebug()<<"没有找到视频解码器";exit(0);}else{qDebug()<<"找到对应的解码器为:"<<avcodec->name;}
}
打开解码器
void fDecode::openDecoder()
{qDebug()<<"————打开解码器————";//avcodec_context、 avcodec 为上一步得到的int res = avcodec_open2(avcodec_context,avcodec,NULL);//zero on success, a negative value on errorif (0 != res){qDebug()<<"打开解码器失败";exit(0);}else{qDebug()<<"打开解码器成功!";}
}
循环读取每一帧并生成.h264、.yuv文件
void fDecode::readFrame()
{qDebug()<<"————读取每一帧————";AVPacket *pkt = (AVPacket *)malloc(sizeof(AVPacket));//存储一帧压缩编码数据in_frame= av_frame_alloc();//保存解码后的像素数据out_frame = av_frame_alloc();//保存解码后剔除损坏数据后的像素数据//一帧码流数据解码后得到的像素数据有多大int numByte = avpicture_get_size(AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height);//开的空间用来保存像素数据uint8_t *buf = (uint8_t *)av_malloc(numByte * sizeof(uint8_t));//像素数据的填充avpicture_fill((AVPicture *)out_frame, buf, AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height);//保存转换规则SwsContext *sws_content = nullptr;//转换规则的设置sws_content = sws_getContext(avcodec_context->width, avcodec_context->height ,avcodec_context->pix_fmt,avcodec_context->width, avcodec_context->height ,AV_PIX_FMT_YUV420P,SWS_BICUBIC,nullptr,nullptr,nullptr);FILE *fp_H264=NULL;FILE *fp_yuv=NULL;//以二进制格式创建或打开文件fp_H264=fopen("test.h264","wb+");fp_yuv=fopen("test.yuv","wb+");if(fp_H264 == NULL || fp_yuv == NULL){qDebug()<<"file open fail!";exit(0);}int size = avcodec_context->width * avcodec_context->height;av_new_packet(pkt,size);//循环读取每一帧while (av_read_frame(avformat_context,pkt) == 0) //return 0 if OK, < 0 on error or end of file{if(pkt->stream_index == this->avcodec_index) //如果这一帧是视频流{fwrite(pkt->data,pkt->size,1,fp_H264);//写入h264文件//解码——>得到yuv AVFrame//got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero.int got_picture_ptr = -1;avcodec_decode_video2(avcodec_context, in_frame, &got_picture_ptr, pkt);if(got_picture_ptr != 0)//做解码的操作{//把解码得到的损坏的数据剔除sws_scale(sws_content, in_frame->data, in_frame->linesize, 0, in_frame->height, //原数据out_frame->data, out_frame->linesize); //输出数据fwrite(out_frame->data[0], size, 1, fp_yuv);//写入y数据fwrite(out_frame->data[1], size/4, 1, fp_yuv);//写入u数据fwrite(out_frame->data[2], size/4, 1, fp_yuv);//写入v数据}}}av_packet_unref(pkt);//清空}//关闭h264、yuv文件fclose(fp_H264);fclose(fp_yuv);qDebug()<<"写入成功";
}
关闭解码用到的组件
void fDecode::closeAll()
{qDebug()<<"————关闭所有组件————";av_frame_free(&in_frame);av_frame_free(&out_frame);avcodec_close(avcodec_context);avformat_free_context(avformat_context);qDebug()<<"关闭成功";
}
主函数测试
int main(int argc, char *argv[])
{QApplication a(argc, argv);qDebug()<<"ffmpeg版本号:"<<avcodec_version();fDecode mvDecode;mvDecode.openMvFile("mv/Warcraft3_End.avi");//打开视频文件mvDecode.findVideoStream();//查找视频流mvDecode.findDecoder();//查找对应的解码器mvDecode.openDecoder();//打开解码器mvDecode.readFrame();//读取每一帧mvDecode.closeAll();//关闭用到的组件return 0;
}
4、测试效果
运行效果
当前目录下会生成test.h264文件和test.yuv文件
H.264压缩方法比较复杂。包含了帧内预测、帧间预测、熵编码、环路滤波等环节构成。可以将图像数据压缩100倍以上,所以可以发现未压缩的像素数据yuv文件比压缩后的码流数据h624文件大非常多。
查看h264文件
查看yuv文件