使用C++ 封装一个FFmpeg通用性Muxer和Encoder组件-1主要功能和Muxer

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

猜你喜欢

转载自blog.csdn.net/qq_33329316/article/details/124158457
今日推荐