最终解码效果:

1.UI设计
2.在控件属性窗口中输入默认值

3.复制已编译FFmpeg库到工程同级目录下
4.在工程引用FFmpeg库及头文件

5.链接指定FFmpeg库
6.使用FFmpeg库
引用头文件
- extern "C"
- {
- #include "libswscale/swscale.h"
- #include "libavdevice/avdevice.h"
- #include "libavcodec/avcodec.h"
- #include "libavcodec/bsf.h"
- #include "libavformat/avformat.h"
- #include "libavutil/avutil.h"
- #include "libavutil/imgutils.h"
- #include "libavutil/log.h"
- #include "libavutil/imgutils.h"
- #include "libavutil/time.h"
- #include
-
- }
创建视频编解码管理类

实现视频编解码管理类
- #include "ffmpegmananger.h"
- #include
- ffmpegMananger::ffmpegMananger(QObject *parent ):
- QObject(parent)
- {
- m_pInFmtCtx = nullptr;
- m_pTsFmtCtx = nullptr;
- m_qstrRtspURL = "";
- m_qstrOutPutFile = "";
- }
- ffmpegMananger::~ffmpegMananger()
- {
- avformat_free_context(m_pInFmtCtx);
- avformat_free_context(m_pTsFmtCtx);
- }
-
- void ffmpegMananger::getRtspURL(QString strRtspURL)
- {
- this->m_qstrRtspURL = strRtspURL;
- }
- void ffmpegMananger::getOutURL(QString strRute)
- {
- this->m_qstrOutPutFile = strRute;
- printf("===========%s\n",m_qstrOutPutFile.toStdString().c_str());
- }
- void ffmpegMananger::setOutputCtx(AVCodecContext *encCtx, AVFormatContext **pTsFmtCtx,int &nVideoIdx_out)
- {
- avformat_alloc_output_context2(pTsFmtCtx , nullptr, nullptr, m_qstrOutPutFile.toStdString().c_str());
- if (!pTsFmtCtx ) {
- printf("Could not create output context\n");
- return;
- }
- if (avio_open(&((*pTsFmtCtx)->pb), m_qstrOutPutFile.toStdString().c_str(), AVIO_FLAG_READ_WRITE) < 0)
- {
- avformat_free_context(*pTsFmtCtx);
- printf("avio_open fail.");
- return;
- }
- AVStream *out_stream = avformat_new_stream(*pTsFmtCtx, encCtx->codec);
- nVideoIdx_out = out_stream->index;
- //nVideoIdx_out = out_stream->index;
- avcodec_parameters_from_context(out_stream->codecpar, encCtx);
- printf("==========Output Information==========\n");
- av_dump_format(*pTsFmtCtx, 0, m_qstrOutPutFile.toStdString().c_str(), 1);
- printf("======================================\n");
- }
- int ffmpegMananger::ffmepgInput()
- {
- int nRet = 0;
- AVCodecContext *encCtx = nullptr;//编码器
- //const char *pUrl = "D:/videos/264.dat";
- std::string temp = m_qstrRtspURL.toStdString();
- const char *pUrl = temp.c_str();
- printf("===========%s\n",pUrl);
- AVDictionary *options = nullptr;
- av_dict_set(&options,"rtsp_transport", "tcp", 0);
- av_dict_set(&options,"stimeout","10000000",0);
- // 设置“buffer_size”缓存容量
- av_dict_set(&options, "buffer_size", "1024000", 0);
- nRet = avformat_open_input(&m_pInFmtCtx,pUrl,nullptr,&options);
- if( nRet < 0)
- {
- printf("Could not open input file,===========keep trying \n");
- return nRet;
- }
- avformat_find_stream_info(m_pInFmtCtx, nullptr);
- printf("===========Input Information==========\n");
- av_dump_format(m_pInFmtCtx, 0, pUrl, 0);
- printf("======================================\n");
- //1.获取视频流编号
- int nVideo_indx = av_find_best_stream(m_pInFmtCtx,AVMEDIA_TYPE_VIDEO,-1,-1,nullptr,0);
- if(nVideo_indx < 0)
- {
- avformat_free_context(m_pInFmtCtx);
- printf("查找解码器失败\n");
- return -1;
- }
- //2.查找解码器
- AVCodec *pInCodec = avcodec_find_decoder(m_pInFmtCtx->streams[nVideo_indx]->codecpar->codec_id);
- if(nullptr == pInCodec)
- {
- printf("avcodec_find_decoder fail.");
- return -1;
- }
- //获取解码器上下文
- AVCodecContext* pInCodecCtx = avcodec_alloc_context3(pInCodec);
- //复制解码器参数
- nRet = avcodec_parameters_to_context(pInCodecCtx, m_pInFmtCtx->streams[nVideo_indx]->codecpar);
- if(nRet < 0)
- {
-
- avcodec_free_context(&pInCodecCtx);
- printf("avcodec_parameters_to_context fail.");
- return -1;
- }
- //打开解码器
- if(avcodec_open2(pInCodecCtx, pInCodec, nullptr) < 0)
- {
- avcodec_free_context(&pInCodecCtx);
- printf("Error: Can't open codec!\n");
- return -1;
- }
- printf("width = %d\n", pInCodecCtx->width);
- printf("height = %d\n", pInCodecCtx->height);
- int frame_index = 0;
- int got_picture = 0;
- AVStream *in_stream =nullptr;
- AVStream *out_stream =nullptr;
- AVFrame *pFrame= av_frame_alloc();
- AVPacket *newpkt = av_packet_alloc();
- AVPacket *packet = av_packet_alloc();
- av_init_packet(newpkt);
- av_init_packet(packet);
- // alloc AVFrame
- AVFrame*pFrameRGB = av_frame_alloc();
- // 图像色彩空间转换、分辨率缩放、前后图像滤波处理
- SwsContext *m_SwsContext = sws_getContext(pInCodecCtx->width, pInCodecCtx->height,
- pInCodecCtx->pix_fmt, pInCodecCtx->width, pInCodecCtx->height,
- AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);
-
- int bytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, pInCodecCtx->width, pInCodecCtx->height,4);
- uint8_t *m_OutBuffer = (uint8_t *)av_malloc(bytes * sizeof(uint8_t));
-
- // 将分配的内存空间给pFrameRGB使用
- avpicture_fill((AVPicture *)pFrameRGB, m_OutBuffer, AV_PIX_FMT_RGB32, pInCodecCtx->width, pInCodecCtx->height);
- if(encCtx == nullptr)
- {
- //打开编码器
- openEncoder(pInCodecCtx->width, pInCodecCtx->height,&encCtx);
- }
- int videoindex_out = 0;
- //设置输出文件上下文
- setOutputCtx(encCtx,&m_pTsFmtCtx,videoindex_out);
- //Write file header
- if (avformat_write_header(m_pTsFmtCtx, nullptr) < 0)
- {
- avformat_free_context(m_pTsFmtCtx);
- printf("Error occurred when opening output file\n");
- return -1;
- }
- printf("==============writer trail===================.\n");
- int count = 0;
- nRet = 0;
- while(av_read_frame(m_pInFmtCtx, packet) >= 0)//从pInFmtCtx读H264数据到packet;
- {
- if(packet->stream_index != nVideo_indx)//仅保留图像
- {
- continue;
- }
- if(avcodec_send_packet(pInCodecCtx, packet)<0)//送packet中H264数据给解码器码器进行解码,解码好的YUV数据放在pInCodecCtx,
- {
- break;
- }
- av_packet_unref(packet);
- got_picture = avcodec_receive_frame(pInCodecCtx, pFrame);//把解码好的YUV数据放到pFrame中
- if(0 == got_picture)//解码好一帧数据
- {
- //发送显示图像的信号
- // 对解码视频帧进行缩放、格式转换等操作
- sws_scale(m_SwsContext, (uint8_t const * const *)pFrame->data,
- pFrame->linesize, 0, pInCodecCtx->height,
- pFrameRGB->data, pFrameRGB->linesize);
-
- // 转换到QImage
- QImage tmmImage((uchar *)m_OutBuffer, pInCodecCtx->width, pInCodecCtx->height, QImage::Format_RGB32);
- QImage image = tmmImage.copy();
-
- // 发送QImage
- emit Sig_GetOneFrame(image);
-
- setDecoderPts(newpkt->stream_index,count, pFrame);
- count++;
- //送原始数据给编码器进行编码
- nRet = avcodec_send_frame(encCtx,pFrame);
- if(nRet < 0)
- {
- continue;
- }
- //从编码器获取编号的数据
- while(nRet >= 0)
- {
- nRet = avcodec_receive_packet(encCtx,newpkt);
- if(nRet < 0)
- {
- break;
- }
- setEncoderPts(nVideo_indx,frame_index,videoindex_out,newpkt);
- int _count = 1;
- printf("Write %d Packet. size:%5d\tpts:%lld\n", _count,newpkt->size, newpkt->pts);
-
- if (av_interleaved_write_frame(m_pTsFmtCtx, newpkt) < 0)
- {
- printf("Error muxing packet\n");
- goto end;
- }
- _count++;
- av_packet_unref(newpkt);
- }
- }
- }
- while(1)//从pInFmtCtx读H264数据到packet;
- {
- if(packet->stream_index != nVideo_indx)//仅保留图像
- {
- continue;
- }
- if(avcodec_send_packet(pInCodecCtx, packet)<0)//送packet中H264数据给解码器码器进行解码,解码好的YUV数据放在pInCodecCtx,
- {
- continue;
- }
- av_packet_unref(packet);
- got_picture = avcodec_receive_frame(pInCodecCtx, pFrame);//把解码好的YUV数据放到pFrame中
- if(!got_picture)//解码好一帧数据
- {
- AVRational in_time_base1 = in_stream->time_base;
- in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
-
- //Duration between 2 frames (us)
- int64_t in_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
- pFrame->pts = (double)(count*in_duration) / (double)(av_q2d(in_time_base1)*AV_TIME_BASE);
- count++;
- //送原始数据给编码器进行编码
- nRet = avcodec_send_frame(encCtx,pFrame);
- if(nRet < 0)
- {
- break;
- }
- //从编码器获取编号的数据
- while(nRet >= 0)
- {
- nRet = avcodec_receive_packet(encCtx,newpkt);
- if(nRet < 0)
- {
- continue;
- }
- in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
- out_stream = m_pTsFmtCtx->streams[videoindex_out];
- if (newpkt->stream_index == nVideo_indx)
- {
- //FIX:No PTS (Example: Raw H.264)
- //Simple Write PTS
- if (newpkt->pts == AV_NOPTS_VALUE)
- {
- //Write PTS
- AVRational time_base1 = in_stream->time_base;
- //Duration between 2 frames (us)
- int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
- //Parameters
- newpkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
- newpkt->dts = newpkt->pts;
- newpkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
- frame_index++;
- }
- }
- //Convert PTS/DTS
- newpkt->pts = av_rescale_q_rnd(newpkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
- newpkt->dts = av_rescale_q_rnd(newpkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
- newpkt->duration = av_rescale_q(newpkt->duration, in_stream->time_base, out_stream->time_base);
- newpkt->pos = -1;
- newpkt->stream_index = videoindex_out;
- int count = 1;
- printf("Write %d Packet. size:%5d\tpts:%lld\n", count,newpkt->size, newpkt->pts);
-
- if (av_interleaved_write_frame(m_pTsFmtCtx, newpkt) < 0)
- {
- printf("Error muxing packet\n");
- goto end;
- }
- count++;
- av_packet_unref(newpkt);
- }
- }
- }
- //Write file trailer
- av_write_trailer(m_pTsFmtCtx);
- end:
- av_frame_free(&pFrame);
- av_frame_free(&pFrameRGB);
- av_packet_unref(newpkt);
- av_packet_unref(packet);
- std::cout<<"rtsp's h264 to ts end";
- return 0;
- }
- void ffmpegMananger::setDecoderPts(int idx,int count,AVFrame *pFrame)
- {
- AVStream* in_stream = m_pInFmtCtx->streams[idx];
- AVRational in_time_base1 = in_stream->time_base;
- //Duration between 2 frames (us)
- int64_t in_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
- pFrame->pts = (double)(count*in_duration) / (double)(av_q2d(in_time_base1)*AV_TIME_BASE);
- }
- void ffmpegMananger::setEncoderPts(int nVideo_indx,int frame_index,int videoindex_out,AVPacket *newpkt)
- {
- AVStream*in_stream = m_pInFmtCtx->streams[newpkt->stream_index];
- AVStream*out_stream = m_pTsFmtCtx->streams[videoindex_out];
- if (newpkt->stream_index == nVideo_indx)
- {
- //FIX:No PTS (Example: Raw H.264)
- //Simple Write PTS
- if (newpkt->pts == AV_NOPTS_VALUE)
- {
- //Write PTS
- AVRational time_base1 = in_stream->time_base;
- //Duration between 2 frames (us)
- int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
- //Parameters
- newpkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
- newpkt->dts = newpkt->pts;
- newpkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
- frame_index++;
- }
- }
- //Convert PTS/DTS
- newpkt->pts = av_rescale_q_rnd(newpkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
- newpkt->dts = av_rescale_q_rnd(newpkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
- newpkt->duration = av_rescale_q(newpkt->duration, in_stream->time_base, out_stream->time_base);
- newpkt->pos = -1;
- newpkt->stream_index = videoindex_out;
- }
- void ffmpegMananger::writeTail()
- {
- //Write file trailer
- av_write_trailer(m_pTsFmtCtx);
- }
- void ffmpegMananger::openEncoder(int width, int height, AVCodecContext** enc_ctx)
- {
- //使用libx264编码器
- AVCodec * pCodec = avcodec_find_encoder_by_name("libx264");
- if(nullptr == pCodec)
- {
- printf("avcodec_find_encoder_by_name fail.\n");
- return;
- }
- //获取编码器上下文
- *enc_ctx = avcodec_alloc_context3(pCodec);
- if(nullptr == enc_ctx)
- {
- printf("avcodec_alloc_context3(pCodec) fail.\n");
- return;
- }
- //sps/pps
- (*enc_ctx)->profile = FF_PROFILE_H264_MAIN;
- (*enc_ctx)->level = 30;//表示level是5.0
- //分辨率
- (*enc_ctx)->width = width;
- (*enc_ctx)->height = height;
- //gop
- (*enc_ctx)->gop_size = 25;//i帧间隔
- (*enc_ctx)->keyint_min = 20;//设置最小自动插入i帧的间隔.OPTION
- //B帧
- (*enc_ctx)->max_b_frames = 0;//不要B帧
- (*enc_ctx)->has_b_frames = 0;//
- //参考帧
- (*enc_ctx)->refs = 3;//OPTION
- //设置输入的yuv格式
- (*enc_ctx)->pix_fmt = AV_PIX_FMT_YUV420P;
- //设置码率
- (*enc_ctx)->bit_rate = 3000000;
- //设置帧率
- //(*enc_ctx)->time_base = (AVRational){1,25};//帧与帧之间的间隔
- (*enc_ctx)->time_base.num = 1;
- (*enc_ctx)->time_base.den = 25;
- //(*enc_ctx)->framerate = (AVRational){25,1};//帧率 25帧每秒
- (*enc_ctx)->framerate.num = 25;
- (*enc_ctx)->framerate.den = 1;
- if(avcodec_open2((*enc_ctx),pCodec,nullptr) < 0)
- {
- printf("avcodec_open2 fail.\n");
- }
- return;
- }
-