最简单的音视频解封装器,基于FFmpeg4.1实现
最近在学习FFmpeg,打算照着FFmpeg官方文档和官方的examples实现一些程序,加深理解,首先先实现一个解封装的程序。代码运行效果如下图:
代码的注释写的很详细,环境是在vs2019中运行的,下面直接上代码:
#include<iostream>
#include<string.h>
extern "C"
{
#include<libavformat/avformat.h>
}
int main(int argc,char *argv[])
{
const char* videoname = NULL;
/*if (argc!=1) {
std::cout << "when you run ,the video name should be entered!" << std::endl;
return -1;
}
videoname = argv[0];*/
videoname = "test1.mp4";
/* 通过avformat_open_input()打开文件并且为acformatcontext分配空间*/
AVFormatContext* fmt_ctx = NULL;
/**
第三个参数,是媒体格式,置空则会自动探测。
第四个参数, it is not possible to set demuxer private options on a preallocated
context. Instead, the options should be passed to avformat_open_input()
wrapped in an AVDictionary:
*/
if (avformat_open_input(&fmt_ctx, videoname, NULL, NULL) < 0) {
std::cout << "could not open soure file :" << videoname << std::endl;
return -1;
};
/**
Read packets of a media file to get stream information.
This is useful for file formats with no headers such as MPEG.
This function also computes the real framerate in case of MPEG-2 repeat frame mode.
The logical file position is not changed by this function;
examined packets may be buffered for later processing.
*/
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
std::cout << "could not find stream information."<< std::endl;
return -1;
}
//查找码流
int videostream = -1;
int audiostream = -1;
int subtitlestream = -1;
videostream=av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (videostream<0) {
std::cout << "could not find video stream information." << std::endl;
}
audiostream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (audiostream<0) {
std::cout << "could not find audio stream information." << std::endl;
}
//字幕流
subtitlestream = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0);
if (subtitlestream<0) {
std::cout << "could not find subtitle stream information." << std::endl;
}
//打印信息
std::cout << "--------------------------视频流-------------------------" << std::endl;
std::cout << "索引号:" << videostream << " " << " 解码器ID:" << fmt_ctx->streams[videostream]->codecpar->codec_id
<< "视频宽*高" << fmt_ctx->streams[videostream]->codecpar->width << "*" << fmt_ctx->streams[videostream]->codecpar->height << std::endl;
std::cout << "--------------------------音频流-------------------------" << std::endl;
std::cout << "索引号:" << audiostream << " " << " 解码器ID:" << fmt_ctx->streams[audiostream]->codecpar->codec_id
<< "音频声道数:" << fmt_ctx->streams[audiostream]->codecpar->channels << std::endl;
std::cout << "--------------------------字幕流-------------------------" << std::endl;
AVPacket *pkt=NULL;
//av_init_packet(pkt);
pkt = av_packet_alloc();
for (;;)
{
if (av_read_frame(fmt_ctx, pkt) < 0)
{
std::cout << "读到文件结尾了!" << std::endl;
break;
};
if (pkt->stream_index == videostream)
{
//打印信息
std::cout << "--------------------------视频流-------------------------" << std::endl;
std::cout <<
"duration: " << pkt->duration * (double)((double)fmt_ctx->streams[1]->time_base.num / (double)fmt_ctx->streams[1]->time_base.den)*1000 <<"ms"<< std::endl;;
std::cout << "size: " << pkt->size << std::endl;
std::cout << "pts : " << pkt->pts * (double)((double)fmt_ctx->streams[1]->time_base.num / (double)fmt_ctx->streams[1]->time_base.den) * 1000 << "ms"
<<" dts :"<< pkt->dts * (double)((double)fmt_ctx->streams[1]->time_base.num / (double)fmt_ctx->streams[1]->time_base.den) * 1000 << "ms" <<std::endl;
}
if (pkt->stream_index == audiostream)
{
//打印信息
std::cout << "--------------------------音频流-------------------------" << std::endl;
std::cout <<
"duration: " << pkt->duration * (double)((double)fmt_ctx->streams[1]->time_base.num / (double)fmt_ctx->streams[1]->time_base.den) * 1000 << "ms" << std::endl;;
std::cout << "size: " << pkt->size << std::endl;
std::cout << "pts : " << pkt->pts * (double)((double)fmt_ctx->streams[1]->time_base.num / (double)fmt_ctx->streams[1]->time_base.den) * 1000 << "ms"
<< " dts :" << pkt->dts * (double)((double)fmt_ctx->streams[1]->time_base.num / (double)fmt_ctx->streams[1]->time_base.den) * 1000 << "ms" << std::endl;
}
//每次调用av_read_frame会导致pkt的引用计数加1,要即时清理。
//否则会导致内存泄漏
av_packet_unref(pkt);
}
av_packet_free(&pkt);
avformat_close_input(&fmt_ctx);
return 0;
}
这部分程序可能存在内存泄漏的地方只有一个,就是AVPacket对象在申请完内存后,记得在av_read_frame()循环中对引用技术减1,即av_packet_unref(pkt);