1 具体功能
源码github
如图所示 把一个pcm 文件和一个 yuv 文件合成一个MP4 文件
2 具体需求
1 Muxer
2 AudioEncoder
3 VideoEncoder
4 设置统一的time_base 保证同步问题
具体步骤 1 Muxer 类的封装(每个主要代码附近都有注释)
Muxer 的需求
1 创建流
2 写入流
3 写入Mp4文件
Muxer类的定义
#include "iostream"
extern "C"{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
};
class Muxer
{
public:
Muxer();
~Muxer();
// 输出文件 返回<0值异常
// 初始化
int Init(const char *url);
// 资源释放
void DeInit();
// 创建流
int AddStream(AVCodecContext *codec_ctx);
// 写流
int SendHeader();
int SendPacket(AVPacket *packet);
int SendTrailer();
int Open(); // avio open
int GetAudioStreamIndex();
int GetVideoStreamIndex();
private:
AVFormatContext *fmt_ctx_ = NULL;
std::string url_ = "";
// 编码器上下文
AVCodecContext *aud_codec_ctx_= NULL;
AVStream *aud_stream_ = NULL;
AVCodecContext *vid_codec_ctx_= NULL;
AVStream *vid_stream_ = NULL;
int audio_index_ = -1;
int video_index_ = -1;
};
Muxer类是实现
#include "Muxer.h"
Muxer::Muxer()
{
}
Muxer::~Muxer()
{
}
int Muxer::Init(const char *url)
{
int ret = avformat_alloc_output_context2(&fmt_ctx_, NULL, NULL,url);
if(ret < 0) {
char errbuf[1024] = {
0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avformat_alloc_output_context2 failed:%s\n", errbuf);
return -1;
}
url_ = url;
return 0;
}
void Muxer::DeInit()
{
if(fmt_ctx_) {
avformat_close_input(&fmt_ctx_);
}
url_ = "";
aud_codec_ctx_ = NULL;
aud_stream_ = NULL;
audio_index_ = -1;
vid_codec_ctx_ = NULL;
vid_stream_ = NULL;
video_index_ = -1;
}
int Muxer::AddStream(AVCodecContext *codec_ctx)
{
if(!fmt_ctx_) {
printf("fmt ctx is NULL\n");
return -1;
}
if(!codec_ctx) {
printf("codec ctx is NULL\n");
return -1;
}
// 创建一个新流 从编码器上下文中拷贝相关参数 到st->codecpar
AVStream *st = avformat_new_stream(fmt_ctx_, NULL);
if(!st) {
printf("avformat_new_stream failed\n");
return -1;
}
// st->codecpar->codec_tag = 0;
// 从编码器上下文复制
avcodec_parameters_from_context(st->codecpar, codec_ctx);
av_dump_format(fmt_ctx_, 0, url_.c_str(), 1);
// 判断当前的是视频流还是音频流
if(codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
aud_codec_ctx_ = codec_ctx;
aud_stream_ = st;
audio_index_ = st->index;
} else if(codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
vid_codec_ctx_ = codec_ctx;
vid_stream_ = st;
video_index_ = st->index;
}
return 0;
}
// 写入文件头
int Muxer::SendHeader()
{
if(!fmt_ctx_) {
printf("fmt ctx is NULL\n");
return -1;
}
int ret = avformat_write_header(fmt_ctx_, NULL);
if(ret < 0) {
char errbuf[1024] = {
0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avformat_write_header failed:%s\n", errbuf);
return -1;
}
return 0;
}
int Muxer::SendPacket(AVPacket *packet)
{
// 看看是什么流
int stream_index = packet->stream_index;
printf("index:%d, pts:%lld\n", stream_index, packet->pts);
//文件有问题 free packet
if(!packet || packet->size <= 0 || !packet->data) {
printf("packet is null\n");
if(packet)
av_packet_free(&packet);
return -1;
}
//获取两个time_base
AVRational src_time_base; // 源time_base
AVRational dst_time_base; // mp4输出文件对应流的time_base
if(vid_stream_ && vid_codec_ctx_ && stream_index == video_index_) {
src_time_base = vid_codec_ctx_->time_base;
dst_time_base = vid_stream_->time_base;
} else if(aud_stream_ && aud_codec_ctx_ && stream_index == audio_index_) {
src_time_base = aud_codec_ctx_->time_base;
dst_time_base = aud_stream_->time_base;
}
// 通过不同的 time_base 时间基转换
packet->pts = av_rescale_q(packet->pts, src_time_base, dst_time_base);
packet->dts = av_rescale_q(packet->dts, src_time_base, dst_time_base);
packet->duration = av_rescale_q(packet->duration, src_time_base, dst_time_base);
int ret = 0;
ret = av_interleaved_write_frame(fmt_ctx_, packet); // 不是立即写入文件,内部缓存,主要是对pts进行排序 然后写入
// ret = av_write_frame(fmt_ctx_, packet);
av_packet_free(&packet);
if(ret == 0) {
return 0;
} else {
char errbuf[1024] = {
0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("av_write_frame failed:%s\n", errbuf);
return -1;
}
}
// 写入文件尾
int Muxer::SendTrailer()
{
if(!fmt_ctx_) {
printf("fmt ctx is NULL\n");
return -1;
}
int ret = av_write_trailer(fmt_ctx_);
if(ret != 0) {
char errbuf[1024] = {
0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("av_write_trailer failed:%s\n", errbuf);
return -1;
}
return 0;
}
// 通过打开输入文件 设置 AVIO_FLAG_WRITE 设置为写入模式
int Muxer::Open()
{
int ret = avio_open(&fmt_ctx_->pb, url_.c_str(), AVIO_FLAG_WRITE);
if(ret < 0) {
char errbuf[1024] = {
0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avio_open %s failed:%s\n",url_.c_str(), errbuf);
return -1;
}
return 0;
}
int Muxer::GetAudioStreamIndex()
{
return audio_index_;
}
int Muxer::GetVideoStreamIndex()
{
return video_index_;
}