为啥要封装和解封装呢?
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;///hunandedecout << "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;//记住音频和视频的 avcodecIdif (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是否为nunllptrif (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) {//说明是视频的avpacketbool xdecodeSendAVPacketSuccess = xdecode.DecoderSendAVPacket(avpacket);if (!xdecodeSendAVPacketSuccess) {cout << "xdecode.DecoderSendAVPacket = " << xdecodeSendAVPacketSuccess << endl;//如果没有成功则continue,让继续从avformatContext 读取数据continue;}else {//发送成功了,这时候就可以从xdecode中获得avframe了,注意的是:发送一次avpacket,可能有多个avframe被弄出来while (xdecode.DecoderRecvAVFrame(avframe)) {//成功的从 xdecode 中获得了 avframethis_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;
}