自学内容网 自学内容网

FFmpeg 4.3 音视频-多路H265监控录放C++开发十八,ffmpeg解复用

为啥要封装和解封装呢?

1.封装就相当于将 h264 和aac 包裹在一起。既能播放声音,也能播放视频

2.在封装的时候没指定编码格式,帧率,时长,等参数;特别是视频,可以将视频帧索引存储,方便视频进度跳转。

解封装流程以及相关的API

解封装:avformat_open_input,avformat_find_stream_info,av_read_frame,av_dump_format,avformat_close_input-CSDN博客

解封装过程中使用到的数据结构

解封装过程中使用到的数据结构 AVFormatContext,AVStream ,AVCodecParameters 说明-CSDN博客
 

代码:

#include <iostream>
#include <thread>
using namespace std;
#define CERR(err) if(err<0){ PrintErr(err);}

extern "C" {
#include "libavformat/avformat.h"
}
#include "xdecoder.h"
#include "xsdl.h"
#include "xvideoview.h"

//在 xcodec.cpp中已经定义了 PrintErr方法,因此这里要删除了
//void PrintErr(int err)
//{
//char buf[1024] = { 0 };
//av_strerror(err, buf, sizeof(buf) - 1);
//cerr << buf << endl;
//}


int main(int argc,char *argv) {

AVPacket* avpacket = av_packet_alloc();
AVFrame* avframe = nullptr;
AVStream* audioavstream = nullptr;
AVStream* videoavstream = nullptr;
int audioavstreamid = -1;
int videoavstreamid = -1;
AVCodecParameters* audioParamter = nullptr;
AVCodecParameters* videoParamter = nullptr;
AVCodecID audioavcodecid = AV_CODEC_ID_NONE;
AVCodecID videoavcodecid = AV_CODEC_ID_NONE;
AVCodecContext* videoavcodecContext = nullptr;

XDecoder xdecode;

XVideoView* xvideoview = nullptr;

///hunandede
cout << "aaa" << endl;
int ret = 0;
//const char* url = "2_audio_track_5s.mp4";
//const char* url = "1920_1080_25.mp4";
const char* url = "400_300_25.mp4";

AVFormatContext* avformatContext = nullptr;
//avformatContext = avformat_alloc_context();
// 如果通过 avformat_alloc_context方法创建 avformatContext,则需要通过avformat_free_context释放。
//avformat_free_context(avformatContext);

ret = avformat_open_input(&avformatContext, url, nullptr, nullptr);
if (ret <0) {
cout << "func avformat_open_input error " << endl;

CERR(ret);
goto END;
}

ret = avformat_find_stream_info(avformatContext,nullptr);
if (ret < 0) {
cout << "func avformat_find_stream_info error " << endl;

CERR(ret);
goto END;
}

av_dump_format(avformatContext, -1, url, 0);

//循环找到音频和视频
for (int i = 0; i < avformatContext->nb_streams; ++i) {
if (avformatContext->streams[i]->codecpar->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
audioavstreamid = i;
audioavstream = avformatContext->streams[i];
audioParamter = avformatContext->streams[i]->codecpar;
continue;
}
if (avformatContext->streams[i]->codecpar->codec_type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
videoavstreamid = i;
videoavstream = avformatContext->streams[i];
videoParamter = avformatContext->streams[i]->codecpar;
}
}
cout << "audioavstreamid = " << audioavstreamid << " audioavstream = " << audioavstream << " audioavstream->codecpar->bit_rate = " << audioavstream->codecpar->bit_rate << endl;
cout << "videoavstreamid = " << videoavstreamid << " videoavstream = " << videoavstream << " videoavstream->codecpar->width = " << videoavstream->codecpar->width << endl;
cout << "videoavstream->id = " << videoavstream->id << endl;
cout << "videoavstream->index = " << videoavstream->index << endl;

cout << "audioavstream->id = " << audioavstream->id << endl;

cout << "audioavstream->index = " << audioavstream->index << endl;


//记住音频和视频的 avcodecId
if (audioavstream) {
audioavcodecid = audioavstream->codecpar->codec_id;
//音频的信息先放在这里, 后续实现对于 音频的avpacket的处理
}
if (videoavstream) {
videoavcodecid = videoavstream->codecpar->codec_id;
//使用视频的 avcodecid,来创建 解码器上下文,并使用avcodec_parameters_to_context将videoavcodecContext的参数 配置为 videoavstream的参数
videoavcodecContext = xdecode.Create(videoavcodecid, false);
avcodec_parameters_to_context(videoavcodecContext, videoavstream->codecpar);
xdecode.SetAVCodecContext(videoavcodecContext);
bool xdecodeopen = xdecode.Open();
if (!xdecodeopen) {
cout << "func xdecode.Open error "  << endl;
goto END;
}
avframe = xdecode.CreateFrame();
if (avframe==nullptr) {
cout << "func xdecode.CreateFrame = nullptr"  << endl;
goto END;
}

xvideoview = XVideoView::CreateVideo();
xvideoview->Init(videoParamter->width, 
videoParamter->height, 
(XVideoView::Format)(videoParamter->format), 
nullptr);

}


//从 avformatContext中读取avpacket.

while (true) {
ret = av_read_frame(avformatContext, avpacket);
if (ret <0) {
cout << "av_read_frame error " << endl;
CERR(ret);
//这里失败了,需要 av_packet_unref(avpacket)吗?查看 av_read_frame 源码应该是不用的,但是为了保险,也可以调用一下,但是要判断avpacket是否为nunllptr
if (avpacket !=nullptr) {
av_packet_unref(avpacket);
}
break;
}
else if (ret ==0) {
//cout << "avpacket->pts : " << avpacket->pts << endl;
//this_thread::sleep_for(100ms); //这里开始自己的想法是在这里打个断点,debug时,检查内存是否增长。但是有断点的情况下,明显有内存泄漏的情况下,并没有内存增长的情况,这说明通过debug的方式
cout << "avpacket->pts : " << avpacket->pts << endl;
cout << "avpacket->stream_index : " << avpacket->stream_index << endl;

//这里有可以使用avpacket了,这个avpacket有可能是音频的avpacket,也有可能是视频的avpacket,要先判断一下
if (videoavstream && avpacket->stream_index == videoavstreamid) {
//说明是视频的avpacket
bool xdecodeSendAVPacketSuccess = xdecode.DecoderSendAVPacket(avpacket);
if (!xdecodeSendAVPacketSuccess) {
cout << "xdecode.DecoderSendAVPacket = " << xdecodeSendAVPacketSuccess << endl;
//如果没有成功则continue,让继续从avformatContext 读取数据
continue;
}
else {
//发送成功了,这时候就可以从xdecode中获得avframe了,注意的是:发送一次avpacket,可能有多个avframe被弄出来
while (xdecode.DecoderRecvAVFrame(avframe)) {
//成功的从 xdecode 中获得了 avframe
this_thread::sleep_for(100ms);
xvideoview->DrawAVFrame(avframe);
}
}
}


//再使用完毕avpacket之后,记得一定要av_packet_unref,不然会有内存泄漏
av_packet_unref(avpacket);
}
}


END:
if (videoavstream) {
xdecode.SetAVCodecContext(nullptr);
}
if(xvideoview) {
xvideoview->DestoryVideoAudio();
}
av_frame_free(&avframe);
av_packet_free(&avpacket);
avformat_close_input(&avformatContext);
return ret;
}


原文地址:https://blog.csdn.net/hunandede/article/details/144199518

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!