ffmpeg解码流程
一、概述
案例:编写一个ffmpeg编解码流程案例
ps:开发工具是Qt Creator 并且在Qt中做的测试
二、图
三、代码示例
#include "xplayer.h" #include "ui_xplayer.h" #include <QDebug> #include <iostream> extern "C"{ #include "libavformat/avformat.h" #include "libavutil/avutil.h" #include "libavcodec/avcodec.h" #include "libswscale/swscale.h" #include "libswresample/swresample.h" } static double r2d(AVRational r){ qDebug()<<"百分数:"<<r.num<<"/"<<r.den; return r.den == 0?0:(double)r.num/(double)r.den; } XPlayer::XPlayer(QWidget *parent) : QWidget(parent) , ui(new Ui::XPlayer) { ui->setupUi(this); //初始化封装库 av_register_all(); //初始化网络库(可以打开rtsp、rtmp、http协议的流媒体视频) avformat_network_init(); //注册编解码器 avcodec_register_all(); //打开文件 //参数设置 AVDictionary *opts = NULL; //设置rtsp流以tcp方式打开 av_dict_set(&opts,"rtsp_transport","tcp",0); //设置网络延迟时间未500毫秒 av_dict_set(&opts,"max_delay","500",0); //解封装上下文 AVFormatContext *ic = NULL; const char * path = "C:/Users/wei.yang/Downloads/test_circle_video.mp4"; int ret = avformat_open_input(&ic, path, 0//表示自动选择解封装器 ,&opts//参数设置 ); if(ret!=0){ //如果出错了就打印错误信息 char buf[1024] = {0}; av_strerror(ret,buf,sizeof(buf)-1); qDebug()<<"open"<<path<< "failed!!"<<buf; return; } qDebug()<<"打开视频文件成功"; qDebug()<<"流个数:"<<ic->nb_streams; //获取流信息 ret = avformat_find_stream_info(ic,NULL); //计算视频总时长 int totalMs = ic->duration/(AV_TIME_BASE/1000); qDebug()<<"总时长(毫秒:)"<<totalMs; //打印视频流详细信息 av_dump_format(ic,0,path,0); //音视频索引,读取时区分音视频 int videoStreamIndex = 0; int audioStreamIndex = 1; //获取音视频流信息 for(int i=0;i<ic->nb_streams;i++){ AVStream *as = ic->streams[i]; qDebug()<<"公共信息:"; qDebug()<<"codec_id:"<<as->codecpar->codec_id; qDebug()<<"format:"<<as->codecpar->format; //音频AVMEDIA_TYPE_AUDIO if(as->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){ audioStreamIndex = i; qDebug()<<"音频信息:"; qDebug()<<"sample_rate:"<<as->codecpar->sample_rate; qDebug()<<"channels:"<<as->codecpar->channels; //一帧数据,单通道样本数 qDebug()<<"frame_size:"<<as->codecpar->frame_size; // } //视频AVMEDIA_TYPE_VIDEO else if(as->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){ videoStreamIndex = i; qDebug()<<"视频信息:"; //宽高、fps qDebug()<<"width:"<<as->codecpar->width; qDebug()<<"height:"<<as->codecpar->height; //帧率fps qDebug()<<"video fps:"<<r2d(as->avg_frame_rate); } } //////////////////////////////////// ///视频解码器打开 ///找到视频解码器 AVCodec *vcodec = avcodec_find_decoder(ic->streams[videoStreamIndex]->codecpar->codec_id); if(!vcodec){ qDebug()<<"can't find codec id:"<<ic->streams[videoStreamIndex]->codecpar->codec_id; return; } qDebug()<<"find the AVCodec "<<ic->streams[videoStreamIndex]->codecpar->codec_id; //创建视频解码器上下文 AVCodecContext *vc = avcodec_alloc_context3(vcodec); //配置解码器上下文参数 avcodec_parameters_to_context(vc,ic->streams[videoStreamIndex]->codecpar); //设置需要多少个线程解码 vc->thread_count = 8; //打开解码器上下文 ret = avcodec_open2(vc,0,0); if(ret!=0){ char buf[1024] = {0}; av_strerror(ret,buf,sizeof(buf)-1); qDebug()<<"video avcodec_open2 failed!:"<<buf; return; } qDebug()<<"video avcodec_open2 success!"; //////////////////////////////////// ///音频解码器打开 ///找音频解码器 AVCodec *acodec = avcodec_find_decoder(ic->streams[audioStreamIndex]->codecpar->codec_id); if(!acodec){ qDebug()<<"can't find codec id:"<<ic->streams[audioStreamIndex]->codecpar->codec_id; return; } qDebug()<<"find the AVCodec "<<ic->streams[audioStreamIndex]->codecpar->codec_id; //创建音频解码器上下文 AVCodecContext *ac = avcodec_alloc_context3(acodec); //配置解码器上下文参数 avcodec_parameters_to_context(ac,ic->streams[audioStreamIndex]->codecpar); //设置需要多少个线程解码 ac->thread_count = 8; //打开解码器上下文 ret = avcodec_open2(ac,0,0); if(ret!=0){ char buf[1024] = {0}; av_strerror(ret,buf,sizeof(buf)-1); qDebug()<<"audio avcodec_open2 failed!:"<<buf; return; } qDebug()<<"audio avcodec_open2 success!"; //获取视频流 ic->streams[videoStreamIndex] av_find_best_stream(ic,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0); //malloc AVPacket并初始化 AVPacket *pkt = av_packet_alloc(); //初始化AVFrame AVFrame *frame = av_frame_alloc(); //像素格式和尺寸转换上下文 SwsContext *vctx = NULL; unsigned char *rgb = NULL; //音频重采样上下文初始化 SwrContext *actx = swr_alloc(); actx = swr_alloc_set_opts(actx, av_get_default_channel_layout(2),//输出 AV_SAMPLE_FMT_S16,//输出 ac->sample_rate,//输出 av_get_default_channel_layout(ac->channels),//输入 ac->sample_fmt,//输入 ac->sample_rate,//输入 0,0); ret = swr_init(actx); if(ret!=0){ char buf[1024] = {0}; av_strerror(ret,buf,sizeof(buf)-1); qDebug()<<"swr_init error:"<<buf; return; } unsigned char *pcm = NULL;//音频原始数据 for(;;){ //读取一帧数据 int ret = av_read_frame(ic,pkt); if(ret!=0){//代表这一帧数据读取失败,那就继续读取 int ms = 3000; long long pos = (double)ms/(double)1000*r2d(ic->streams[pkt->stream_index]->time_base); av_seek_frame(ic,videoStreamIndex, pos,//要seek的具体位置 AVSEEK_FLAG_BACKWARD//向后找 | AVSEEK_FLAG_FRAME//并且需要定位到关键帧位置 ); continue;//继续 } qDebug()<<"pkt->size:"<<pkt->size; //显示时间 qDebug()<<"pkt->pts:"<<pkt->pts; //转换为毫秒,方便做同步 qDebug()<<"pkt->pts ms:"<<pkt->pts*(r2d(ic->streams[pkt->stream_index]->time_base)*1000); //解码时间 qDebug()<<"pkt->dts:"<<pkt->dts; AVCodecContext *cc= NULL;//解码器上下文 if(pkt->stream_index==videoStreamIndex){ qDebug()<<"视频流"; cc = vc; } if(pkt->stream_index==audioStreamIndex){ qDebug()<<"音频流"; cc = ac; } //解码视频 //发送packet到解码线程,send传NULL后调用多次receive取出所有缓冲帧数据 ret = avcodec_send_packet(cc,pkt); //释放,引用计数-1,为0释放空间 av_packet_unref(pkt); if(ret!=0){//说明发送数据失败 char buf[1024] = { 0 }; av_strerror(ret, buf, sizeof(buf) - 1); qDebug() << "avcodec_send_packet failed! :" << buf ; continue; } //循环从线程中取出数据 for(;;){ //从线程中获取解码接口,一次send可能对应多次receive ret = avcodec_receive_frame(cc,frame); if(ret!=0){ char buf[1024] = { 0 }; av_strerror(ret, buf, sizeof(buf) - 1); qDebug() << "avcodec_receive_frame failed! :" << buf ; break; } qDebug()<<"receive frame:"<<frame->format<<" "<<frame->linesize[0]; //视频 if(cc==vc){ qDebug()<<"视频宽高:width:"<<frame->width<<" height:"<<frame->height; vctx = sws_getCachedContext( vctx,//传NULL会重新创建 frame->width,//输入宽 frame->height,//输入搞 (AVPixelFormat)frame->format,//输入格式YUV420P frame->width,//输出宽 frame->height,//输出高 AV_PIX_FMT_RGBA,//输出格式RGBA SWS_BILINEAR,//双线性插值 0,0,0 ); if(vctx){ if(!rgb)rgb = new unsigned char[frame->width*frame->height*4]; uint8_t *data[2] = {0}; data[0] = rgb; int lines[2] ={0}; lines[0] = frame->width*4; ret = sws_scale(vctx, frame->data,//输入数据 frame->linesize,//输入行大小 0, frame->height,//输入高度 data,//输出数据大小 lines// ); } qDebug()<<"sws_scale="<<ret; }else{//音频 uint8_t *data[2] = {0}; if(!pcm)pcm = new uint8_t[frame->nb_samples*2*2]; data[0] = pcm; // int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, // const uint8_t **in , int in_count); ret = swr_convert(actx, data, frame->nb_samples, //输出 (const uint8_t**)frame->data,frame->nb_samples //输入 ); qDebug()<<"swr_convert="<<ret; } } //释放引用计数 av_packet_unref(pkt); } av_frame_free(&frame); //释放AVPacket空间 av_packet_free(&pkt); //关闭打开的文件资源 if(ic){ avformat_close_input(&ic); } } XPlayer::~XPlayer() { delete ui; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探