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

发布于:2024-12-07 ⋅ 阅读:(110) ⋅ 点赞:(0)

为啥要封装和解封装呢?

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;
}


网站公告

今日签到

点亮在社区的每一天
去签到