音视频开发35 FFmpeg 编码- 将YUV 和 pcm合成一个mp4文件

一 程序的目的

/***
 *该程序的目的是:
 * 将 一个pcm文件 和 一个 yuv文件,合成为一个 0804_out.mp4文件
 * pcm文件和yuv文件是从哪里来的呢?是从 sound_in_sync_test.mp4 文件中,使用ffmpeg命令 抽取出来的。
 * 这样做的目的是为了对比前后两个mp4(sound_in_sync_test.mp4  和 0804_out.mp4 ) 文件。
 *
 * 1. 从sound_in_sync_test.mp4 文件 中抽取 pcm命令如下:
 * ffmpeg -i sound_in_sync_test.mp4 -vn -ar 44100 -ac 2 -f s16le 44100_2_s16le.pcm
 * -vn 表示不处理视频
 *
 * 2. 从sound_in_sync_test.mp4 文件 中抽取 yuv命令如下:
 *
 * ffmpeg -i sound_in_sync_test.mp4 -pix_fmt yuv420p 720x576_yuv420p.yuv
 *
 * 3.播放测试
 *  对于 pcm 数据
 ffplay -ac 2 -ar 44100 -f s16le 44100_2_s16le.pcm
 *  对于 YUV 数据
 ffplay -pixel_format yuv420p -video_size 720x576 -framerate 25  720x576_yuv420p.yuv
 ***/

原始 sound_in_sync_test.mp4 的信息

二 流程 以及 思路

整体思路是这样的:

1. 将video 的yuv数据,变成avframe,然后变成avpacket,

2. 然后需要将avpacket 通过  av_write_frame(this->_avformatContext,avpacket); 发送出去,那么这就需要有 avformatContext。顺便要有avstream,

3. 对于video 来pcm数据来说,应该还需要 audioresample

设计思路

1. 视频编码相关类 videoencoder.h

///设计思路:该函数的作用是,将yuv_data数据存储到 avframe中,然后将avframe中的数据发送到 编码器上下文,经过编码后变成 avpacket。

///核心函数1:初始化h264,要初始化h264,核心是找到编码器;初始化编码器上下文;设置编码器上下文参数;而设置编码器参数,如下的5个参数都需要
/// yuv视频的宽 yuvwidth,yuv视频的高 yuvheight, yuv视频格式 AVPixelFormat yuvpix_fmt, yuv视频fps int yuvfps,视频 比特率 video_bit_rate
///     int InitH264(int yuvwidth, int yuvheight, AVPixelFormat yuvpix_fmt, int yuvfps, int video_bit_rate);
/// 在初始化中,我们还可以定义一个类变量avframe,顺便设定avframe的参数,为了后续方便使用 avframe 做准备。
/// 此方法中和 audio 不同的一点是,会设置 avcodecContext的timebase为1000000,这是因为

///核心函数2:将yuv数据变成 avpacket
///第一个参数为 yuv_data,是一张yuv数据的指针,
/// 第二个参数为 第一个参数yuv_data的大小
/// 第三个参数为 avstream中 video_index,这个参数从 avstream中获得,通过muxer内部变量传递过来,作用是 给avpacket设置 video_index。
/// 第四个参数为 yuv数据一帧图片需要花费的时间,当yuv_fps为25时候,那么一帧数据显示的时刻就是 1/25秒,也就是1000000 * (1/25) 微秒,注意时间。
/// 那么第一张yuv图片 pts 就是 40 000  微秒,
///    第二章yuv图片 pts 就是 80 000  微秒,
/// 第四个参数 pts 是yuv数据的 读取到这时候的时间,参考调用时候 double video_frame_duration = 1.0/yuv_fps * video_time_base;
/// 第五个参数为 yuv数据的时间基,我们设定为 1000000
/// 第四个参数和第五个参数的目的是:使用 yuv的pts  和 yuv 的时间基 转化成 avframe的 pts,这里需要使用到 avcodecContex的timebase,这个avcodecContext 的timebase 是在前面 InitH264 函数中设定的。
/// 注意,video的avcodecContext的timebase 和 audio 的avcodecContext 的timebase 获得方式是不同的,
/// audio 的avcodecContext 的timebase 是 ffmpeg 中在avcodec_open2方法中设定的,video的avcodecContext 的timebase 是程序员自己设定的
/// 然后存储于到 最后一个参数 vector<AVPacket *> 中
/// return 小于0没有packet
/// int videoencoder::Encode(uint8_t *yuv_data, int yuv_size,int stream_index, int64_t pts, int64_t time_base,std::vector<AVPacket *> &packets)


 

videoencoder.h 代码

#ifndef VIDEOENCODER_H
#define VIDEOENCODER_H


///设计思路:该函数的作用是,将yuv_data数据存储到 avframe中,然后将avframe中的数据发送到 编码器上下文,经过编码后变成 avpacket。

///核心函数1:初始化h264,要初始化h264,核心是找到编码器;初始化编码器上下文;设置编码器上下文参数;而设置编码器参数,如下的5个参数都需要
/// yuv视频的宽 yuvwidth,yuv视频的高 yuvheight, yuv视频格式 AVPixelFormat yuvpix_fmt, yuv视频fps int yuvfps,视频 比特率 video_bit_rate
///     int InitH264(int yuvwidth, int yuvheight, AVPixelFormat yuvpix_fmt, int yuvfps, int video_bit_rate);
/// 在初始化中,我们还可以定义一个类变量avframe,顺便设定avframe的参数,为了后续方便使用 avframe 做准备。
/// 此方法中和 audio 不同的一点是,会设置 avcodecContext的timebase为1000000,这是因为

///核心函数2:将yuv数据变成 avpacket
///第一个参数为 yuv_data,是一张yuv数据的指针,
/// 第二个参数为 第一个参数yuv_data的大小
/// 第三个参数为 avstream中 video_index,这个参数从 avstream中获得,通过muxer内部变量传递过来,作用是 给avpacket设置 video_index。
/// 第四个参数为 yuv数据一帧图片需要花费的时间,当yuv_fps为25时候,那么一帧数据显示的时刻就是 1/25秒,也就是1000000 * (1/25) 微秒,注意时间。
/// 那么第一张yuv图片 pts 就是 40 000  微秒,
///    第二章yuv图片 pts 就是 80 000  微秒,
/// 第四个参数 pts 是yuv数据的 读取到这时候的时间,参考调用时候 double video_frame_duration = 1.0/yuv_fps * video_time_base;
/// 第五个参数为 yuv数据的时间基,我们设定为 1000000
/// 第四个参数和第五个参数的目的是:使用 yuv的pts  和 yuv 的时间基 转化成 avframe的 pts,这里需要使用到 avcodecContex的timebase,这个avcodecContext 的timebase 是在前面 InitH264 函数中设定的。
/// 注意,video的avcodecContext的timebase 和 audio 的avcodecContext 的timebase 获得方式是不同的,
/// audio 的avcodecContext 的timebase 是 ffmpeg 中在avcodec_open2方法中设定的,video的avcodecContext 的timebase 是程序员自己设定的
/// 然后存储于到 最后一个参数 vector<AVPacket *> 中
/// return 小于0没有packet
/// int videoencoder::Encode(uint8_t *yuv_data, int yuv_size,int stream_index, int64_t pts, int64_t time_base,std::vector<AVPacket *> &packets)








#include "iostream"
using namespace std;
extern "C" {
    #include "libavutil/avassert.h" // include 后面<> 表示会从标准库路径中查找指定的文件,""表示从当前当前目录(即包含 #include 指令的文件所在的目录)中查找指定的文件
    #include "libavutil/channel_layout.h"
    #include "libavutil/opt.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/timestamp.h"
    #include "libswscale/swscale.h"
    #include "libswresample/swresample.h"
    #include "libavutil/error.h"
    #include "libavutil/common.h"
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/imgutils.h"
}
#include <vector>

class videoencoder
{
public:
    videoencoder();
    ~videoencoder();
    //该函数的作用是 找到h264编码器,通过h264编码器上下文,打开h264编码器上下文,设置h264编码器上下文参数,分配avframe,以及设置avframe参数
    int InitH264(int yuvwidth, int yuvheight, AVPixelFormat yuvpix_fmt, int yuvfps, int video_bit_rate);
    void DeInit();

    ///第一个参数为 yuv_data,是yuv数据的指针,
    /// 第二个参数为 第一个参数yuv_data的大小
    /// 第三个参数为 avstream中 video_index
    /// 第四个参数为 yuv数据一帧图片需要花费的时间,当yuv_fps为25时候,那么一帧数据显示的时刻就是 1/25秒,也就是1000000 * (1/25) 微秒,注意时间。
    /// 那么第一张yuv图片 pts 就是 40 000  微秒,
    ///    第二章yuv图片 pts 就是 80 000  微秒,
    /// 第四个参数 参考调用时候 double video_frame_duration = 1.0/yuv_fps * video_time_base;
    /// 第五个参数为 时间基 1000000
    /// 该函数的作用是,将yuv_data数据存储到 avframe中,然后将avframe中的数据发送到 编码器上下文,经过编码后变成 avpacket。返回该avpacket
    AVPacket *Encode(uint8_t *yuv_data, int yuv_size,
                     int stream_index, int64_t pts, int64_t time_base);

    ///第一个参数为 yuv_data,是yuv数据的指针,
    /// 第二个参数为 第一个参数yuv_data的大小
    /// 第三个参数为 avstream中 video_index
    /// 第四个参数为 yuv数据一帧图片需要花费的时间,当yuv_fps为25时候,那么一帧数据显示的时刻就是 1/25秒,也就是1000000 * (1/25) 微秒,注意时间。
    /// 那么第一张yuv图片 pts 就是 40 000  微秒,
    ///    第二章yuv图片 pts 就是 80 000  微秒,
    /// 第四个参数 参考调用时候 double video_frame_duration = 1.0/yuv_fps * video_time_base;
    /// 第五个参数为 时间基 1000000
    /// 该函数的作用是,将yuv_data数据存储到 avframe中,然后将avframe中的数据发送到 编码器上下文,经过编码后变成 avpacket。
    /// 然后存储于到 最后一个参数 vector<AVPacket *> 中
    /// 小于0没有packet
    int Encode(uint8_t *yuv_data, int yuv_size, int stream_index, int64_t pts, int64_t time_base,
               std::vector<AVPacket *> &packets);
    AVCodecContext *GetCodecContext();

private:
    int _width = 0;
    int _height = 0;
    AVPixelFormat _pix_fmt = AV_PIX_FMT_NONE;
    int _fps = 25;  // 这个默认值,也不会用到,所有的参数都会在构造方法中传递真正的参数
    int _bit_rate = 500*1024; //  这个默认值,也不会用到,所有的参数都会在构造方法中传递真正的参数
    int64_t _pts = 0;
    AVCodecContext * _avcodecContext = NULL;
    AVFrame *_avframe = NULL;
    AVDictionary *_avdictionary = NULL;

};

#endif // VIDEOENCODER_H

videoencoder.cpp

#include "videoencoder.h"

videoencoder::videoencoder()
{

}

videoencoder::~videoencoder()
{
    if(this->_avcodecContext) {
        DeInit();
    }
}


int videoencoder::InitH264(int yuvwidth, int yuvheight, AVPixelFormat yuvpix_fmt, int yuvfps, int video_bit_rate)
{
    int ret =0;
    cout <<" videoencoder.InitH264 call yuvwidth = " << yuvwidth
         <<" yuvheight = "<< yuvheight
         <<" yuvpix_fmt = " << yuvpix_fmt
         <<" yuvfps = " <<yuvfps
         <<" video_bit_rate = " << video_bit_rate
         << endl;

    //1.使用VideoEncoder内部的变量记住 传递进来的值

    this->_width = yuvwidth;
    this->_height = yuvheight;
    this->_pix_fmt = yuvpix_fmt;
    this->_fps = yuvfps;
    this->_bit_rate = video_bit_rate;

    //2.找到视频编码器
    const AVCodec * avcodec =  avcodec_find_encoder(AV_CODEC_ID_H264);
    if(avcodec == nullptr){
        ret = -1;
        cout<<" func InitAAC error because avcodec_find_encoder(AV_CODEC_ID_H264) error "<<endl;
        return ret;
    }

    //3.通过视频编码器找到视频编码器上下文
    this->_avcodecContext = avcodec_alloc_context3(avcodec);
    if(_avcodecContext == nullptr){
        ret = -1;
        cout<<" func InitAAC error because avcodec_alloc_context3(avcodec) error "<<endl;
        return ret;
    }


    //3.1 设定 视频编码器上下文 的参数,这里除了要设定 三要素之外,需要设置 flag 的值为 AV_CODEC_FLAG_GLOBAL_HEADER

    _avcodecContext->width = yuvwidth;
    _avcodecContext->height = yuvheight;
    _avcodecContext->pix_fmt = yuvpix_fmt;
    _avcodecContext->framerate = {yuvfps, 1};


    ///AV_CODEC_FLAG_GLOBAL_HEADER参数相关--中文翻译:将全局标头放置在extradata中,而不是每个关键帧中。
    ///这里要明白为什么加这个参数,需要知道如下的两个知识点:
    /// 1. mp4文件中的 aac 是不带 adst header的,因此我们在将 aac 合成为mp4的时候,不能给每个aac帧的前面加 adst header
    /// 2. AV_CODEC_FLAG_GLOBAL_HEADER 参数的含义就是:将全局标头放置在extradata中,而不是每个关键帧中。,对于aac来说,在每一帧的前面不加 adst header
    /// 这里扩展一下h264,对于h264,有Annexb 和 AVCC 两种存储模式,MP4中的存储的是h264是AVCC格式的,AVCC格式是只有一个头文件在最前面,后面的都是h264纯数据,因此,h264编码成mp4的时候,应该也需要添加 AV_CODEC_FLAG_GLOBAL_HEADER这个标志flag
    _avcodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    // 视频的 如果不主动设置:The encoder timebase is not set
    _avcodecContext->time_base = {1, 1000000};   // 单位为微妙


    ///设置比特率
    _avcodecContext->bit_rate = video_bit_rate;

    ///设置 gop 的大小 和 fps一致。gop size 可以是fps的整数倍
    _avcodecContext->gop_size = yuvfps;

    //设置所有的 图像没有b帧
    _avcodecContext->max_b_frames = 0;

    //设置 aac 额外的参数,通过 _avdictionary设置
    //    av_dict_set(&_avdictionary, "tune", "zerolatency", 0);


    //4.打开编码器上下文
    ret = avcodec_open2(_avcodecContext, NULL, &_avdictionary);
    if(ret != 0) {
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avcodec_open2 failed:%s\n", errbuf);
        return -1;
    }


    //5.分配 avframe内存,为什么要在这里分配呢?

    this->_avframe = av_frame_alloc();
    if(!_avframe) {
        printf("av_frame_alloc failed\n");
        return -1;
    }
    _avframe->width = _width;
    _avframe->height = _height;
    _avframe->format = _avcodecContext->pix_fmt;
    printf("Inith264 success\n");

    return ret;


}

void videoencoder::DeInit()
{
    if(this->_avcodecContext) {
        avcodec_free_context(&_avcodecContext);
    }
    if(this->_avframe) {
        av_frame_free(&_avframe);
    }
    if(this->_avdictionary) {
        av_dict_free(&_avdictionary);
    }
}



///第一个参数为 yuv_data,是yuv数据的指针,
/// 第二个参数为 第一个参数yuv_data的大小
/// 第三个参数为 avstream中 video_index
/// 第四个参数为 yuv数据一帧图片需要花费的时间,当yuv_fps为25时候,那么一帧数据显示的时刻就是 1/25秒,也就是1000000 * (1/25) 微秒,注意时间。
/// 那么第一张yuv图片 pts 就是 40 000  微秒,
///    第二章yuv图片 pts 就是 80 000  微秒,
/// 第四个参数 参考调用时候 double video_frame_duration = 1.0/yuv_fps * video_time_base;
/// 第五个参数为 时间基 1000000
/// 该函数的作用是,将yuv_data数据存储到 avframe中,然后将avframe中的数据发送到 编码器上下文,经过编码后变成 avpacket。
AVPacket *videoencoder::Encode(uint8_t *yuv_data,
                               int yuv_size,
                               int stream_index,
                               int64_t pts,
                               int64_t time_base)
{
    if(!this->_avcodecContext) {
        printf("codec_ctx_ null\n");
        return NULL;
    }
    int ret = 0;
    /// 时间基 转换, 当第一帧的时候: pts = 40000;
    /// 视频编码器上下文的 time_base是user自己设定的,音频编码器上下文中的timebase是 ffmpeg在 avcodec_open2 方法中实现的。
    /// 但是视频编码器上下文的time_base并没有在 ffmpeg源码中实现,需要user自己设置,我们在前面的代码中已经设置了
    /// 前面设置的timebase为1,1000000   :    _avcodecContext->time_base = {1, 1000000}
    /// 计算值:第一帧时候重新计算的pts的值为:  (40000 *(1/1000000)) / (1/1000000) = 40000
    /// 那为什么还要计算呢?
    pts = av_rescale_q(pts, AVRational{1, (int)time_base}, _avcodecContext->time_base);
    this->_avframe->pts = pts;


    //将yuv_data数据填充到avframe中,调用的时候有两种方式,一种是yuv_data真的有数据,一种是yuv_data的值为nullptr,目的是刷新编码器上下文

    if(yuv_data){
        int ret_size = av_image_fill_arrays(this->_avframe->data, this->_avframe->linesize,
                                            yuv_data, (AVPixelFormat)this->_avframe->format,
                                            this->_avframe->width, this->_avframe->height, 1);
        if(ret_size != yuv_size) {
            printf("ret_size:%d != yuv_size:%d -> failed\n", ret_size, yuv_size);
            return NULL;
        }
        ret = avcodec_send_frame(this->_avcodecContext, this->_avframe);

    } else {
        ret = avcodec_send_frame(this->_avcodecContext, NULL);
    }


    if(ret != 0) {
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avcodec_send_frame failed:%s\n", errbuf);
        return NULL;
    }
    AVPacket *packet = av_packet_alloc();
    ret = avcodec_receive_packet(this->_avcodecContext, packet);
    if(ret != 0) {
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("h264 avcodec_receive_packet failed:%s\n", errbuf);
        av_packet_free(&packet);
        return NULL;
    }
    packet->stream_index = stream_index;
    return packet;
}


int videoencoder::Encode(uint8_t *yuv_data, int yuv_size,
                         int stream_index, int64_t pts, int64_t time_base,
                         std::vector<AVPacket *> &packets)
{
    if(!this->_avcodecContext) {
        printf("codec_ctx_ null\n");
        return -1;
    }
    int ret = 0;

    pts = av_rescale_q(pts, AVRational{1, (int)time_base}, this->_avcodecContext->time_base);
    this->_avframe->pts = pts;
    if(yuv_data) {
        int ret_size = av_image_fill_arrays(_avframe->data, _avframe->linesize,
                                            yuv_data, (AVPixelFormat)_avframe->format,
                                            _avframe->width, _avframe->height, 1);
        if(ret_size != yuv_size) {
            printf("ret_size:%d != yuv_size:%d -> failed\n", ret_size, yuv_size);
            return -1;
        }
        ret = avcodec_send_frame(this->_avcodecContext, this->_avframe);
    } else {
        ret = avcodec_send_frame(this->_avcodecContext, NULL);
    }

    if(ret != 0) {
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avcodec_send_frame failed:%s\n", errbuf);
        return -1;
    }
    while(1)
    {
        AVPacket *packet = av_packet_alloc();
        ret = avcodec_receive_packet(this->_avcodecContext, packet);
        packet->stream_index = stream_index;
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            ret = 0;
            av_packet_free(&packet);
            break;
        } else if (ret < 0) {
            char errbuf[1024] = {0};
            av_strerror(ret, errbuf, sizeof(errbuf) - 1);
            printf("h264 avcodec_receive_packet failed:%s\n", errbuf);
            av_packet_free(&packet);
            ret = -1;
        }
        printf("h264 pts:%lld\n", packet->pts);
        packets.push_back(packet);
    }
    return ret;
}

AVCodecContext *videoencoder::GetCodecContext()
{
    if(this->_avcodecContext){
        return this->_avcodecContext;
    }
    return nullptr;
}

2.重采样编码相关类 audioresample.h

audioresample.h

#ifndef AUDIORESAMPLER_H
#define AUDIORESAMPLER_H
extern "C" {
#include "libavutil/channel_layout.h"
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"
}
#include "iostream"
using namespace std;

///该类的作用是将 原始的 pcm数据转化成 FFMPEG 自带的 AAC编码器 可以处理的 pcm格式
///
///核心函数一,初始化swrcontext,实际上就是将 swr_alloc_set_opts2 函数 和 swr_init 函数整合在一起
///
/// 因此需要的参数为 const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
/// const AVChannelLayout *in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
/// 因此我们的 initResampler 函数,应该至少有如上的6个参数,还应该有 SwrContext 的实例,实际上我们在这里处理的时候使用swrcontext参数的时候,使用的就是 类内部变量
///     int InitResampler(const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
///                       const AVChannelLayout *in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate);

/// /**
///* Allocate SwrContext if needed and set/reset common parameters.
///*
///* This function does not require *ps to be allocated with swr_alloc(). On the
///* other hand, swr_alloc() can use swr_alloc_set_opts2() to set the parameters
///* on the allocated context.
///*
///* @param ps              Pointer to an existing Swr context if available, or to NULL if not.
///*                        On success, *ps will be set to the allocated context.
///* @param out_ch_layout   output channel layout (e.g. AV_CHANNEL_LAYOUT_*)
///* @param out_sample_fmt  output sample format (AV_SAMPLE_FMT_*).
///* @param out_sample_rate output sample rate (frequency in Hz)
///* @param in_ch_layout    input channel layout (e.g. AV_CHANNEL_LAYOUT_*)
///* @param in_sample_fmt   input sample format (AV_SAMPLE_FMT_*).
///* @param in_sample_rate  input sample rate (frequency in Hz)
///* @param log_offset      logging level offset
///* @param log_ctx         parent logging context, can be NULL
///*
///* @see swr_init(), swr_free()
///* @return 0 on success, a negative AVERROR code on error.
///*         On error, the Swr context is freed and *ps set to NULL.
///*/
///int swr_alloc_set_opts2(struct SwrContext **ps,
///                       const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
///                       const AVChannelLayout *in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
///                       int log_offset, void *log_ctx);


///核心函数二 创建输入缓冲区,也就是原始的pcm数据。创建输出缓冲区,也就是我们audioencoder支持的pcm数据。
///如何创建这个输入缓冲区呢?又根据哪些参数创建这个输入缓冲区呢?
///很显然,输入缓冲区是要根据 输入的音频的三要素 来创建的。但是没有用到的的是采样率,类似 44100这个。创建出来的缓冲区放在哪里呢?

/// 核心作用一
///int av_samples_alloc_array_and_samples(uint8_t ***audio_data,
/// int *linesize,
/// int nb_channels, //采样 声道数量 2
/// int nb_samples, //一帧有多少个采样点 1152
/// enum AVSampleFormat sample_fmt, //采样格式 s16le,
/// int align);
///
/// 第一个参数audio_data为:输入缓冲区的首地址,是个三级指针,本质上是对于 一个二级指针的 取地址,out参数
/// 这里要说明一下为什么 audio_data 是个三级指针,首先是一个输出参数,那么意味着,我们传递进来的要改动的就是二级指针,
/// 这个二级指针可以想象成是一个 uint8_t * audiodata[8], 每一个audiodata[i] 都是指向的 每个planar的具体数据。
/// 实际上这里就是为了兼容planar才弄了个三级指针。如果不考虑planar 的,二级指针就够了。
///
/// 第二个参数linesize为:输入缓冲区对齐的音频缓冲区大小,可能为 NULL,out参数
/// 这个linesize 是每个 audio_data[x]的大小,并不是输入缓冲区整体的大小。
/// 这个文档中并没有明确的说明,但是debug的时候可以发现。
/// 我们以 2声道,1024个样本数, 每个样本都是s16le(2个字节),交错模式计算:
/// 那么linesize 的 大小为:2*1024*2 = 4096字节
/// 我们以 2声道,1024个样本数, 每个样本都是s16le(2个字节),planar模式计算:
/// 那么linesize 的 大小为:2*1024 = 2048字节  (这里不用乘2声道)因为在planar模式下,audio_data[0] 中存储的是LLLLLLLLL,audio_data[1] 中存储的是RRRRRRRR
/// 当我们在交错模式的时候,所有的pcm的数据都是存储在 audio_data[0]中的,因此linesize就表示的全部的大小
/// 第三个参数nb_channels为:输入源的 声道数
/// 第四个参数nb_samples为:输入源每个声道的样本数,aac 为1024。也就是说,aac每一帧有1024个样本帧,还记得采样率吗?采样率是44100的话,就说明1秒钟采集44100个样本帧。这里不要搞混淆了。
/// 第五个参数sample_fmt为:输入源的AVSampleFormat -- 类似AV_SAMPLE_FMT_DBL
/// 第六个参数align为:是否要字节对齐,0为对齐,1为不对齐,一般都要对齐
/// 为什么通过3,4,5参数,就能计算出来 输入缓存大小呢?
/// 还记得这个吗? 每一帧的大小 = 声道 * 每个声道的样本数量 * 每个样本的大小
/// 这就对应着,参数3,4,5呀。通过5可以得到每个样本的大小。因此这么设计的内部实现,估计也就是这几个参数相乘得到的。再加上是否需要字节对齐。

///核心作用二,创建输出缓冲区
/// 前面我们在计算输入缓冲区大小的时候,用到了 三个要素,如下
/// 第三个参数nb_channels为:输入源的 声道数
/// 第四个参数nb_samples为:输入源每个声道的样本数,aac 一帧为1024个样本。
/// 第五个参数sample_fmt为:输入源的AVSampleFormat -- 类似AV_SAMPLE_FMT_DBL
/// 为什么需要这三个参数也讲清楚了。那么问题来了,在 创建输出缓冲区 的时候这三个参数应该是多少呢?
/// 第三个参数nb_channels为:输出源的 声道数,这个是我们写代码前就规定的,
/// 比如说我们的目的就是将一个  -ar 44100 -ac 2 -f f32le 变成  -ar 48000 -ac 1 -f s16le 的.
/// 那么这个  输出源的nb_channels 就是1,输出源的 sample_fmt就是  AV_SAMPLE_FMT_S16。
/// 第四个参数 nb_samples 为:输出源每个声道的样本数,这个值是需要变化的,因为sample_fmt变化了
/// 不管怎么变化,你将一首2分钟的歌曲,转化后应该还是2分钟的歌曲,时间是不能变化的。
/// 有了这个认识,我们再来看,下面就比较好理解了。
/// 我们要从 44100 ---- 变成 48000,也就是说,之前1秒钟,采集的样本数量是44100个,我们一帧是1024个样本,花费的时间是 1024/44100
/// 我们变成48000后,花费的时间应该是不变的,那么有变化的只能是 采样的样本数量
/// 因此可以推断出 第四个参数为: 1024/44100 = nb_samples/48000
/// nb_samples = (1024/44100) * 48000 = 1024 * 48000 / 44100 = 输出源每个声道的样本个数 = 输出源采样率 * 输入源每个声道的样本数 / 输入源的采样率
/// 使用 ffmpeg 的api  av_rescale_rnd 完成,第三个参数 填写AV_ROUND_UP
/// int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const;

///基于此,我们设计如下的api,要用到的 输入pcm的三要素,和输出pcm的三要素 在初始化的时候会保存到 内部变量,因此没有必要再api中,加入这些参数。
///int resamples_alloc_inbuffer_and_outbuffer(uint8_t ***in_audio_data,
///                                           int *in_linesize,
///                                           uint8_t ***out_audio_data,
///                                           int *out_linesize);

/// 到这里,我们再来回顾一下:我们的目的是 转化成 aac encoder 可以处理的 pcm ,进而转换成可以处理的avframe
/// 也就是我们 知道 重采样 输出 nb_number 为1024,那么 输入的 nb_number 就应该是计算出来的 (此处只是自己的猜想,并没有十足的把我,待测试,)
/// 输入源每个声道的样本个数 = 输入源采样率 * 输出源每个声道的样本数 / 输出源的采样率
///  我们这里输入源采样率 是 44100,输出源每个声道的样本数是1024,输出源的采样率是我们可以设置的,我们这里为了方便也设置为 44100(当然可以是48000,或者什么的,主要符合ffmpeg aac支持的采样率都是可以的)


///核心函数三
///
/// 这时候已经有了 输入缓冲区 和输出缓冲区了。当我们从 原始的文件中读取数据后,会放在 输入缓冲区中,然后通过 swr_convert 方法去做转换
/// int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in , int in_count);
/// 假设我们都转换成功了,那么数据就会在 uint8_t **out, int out_count中,那么我们怎么将 uint8_t **out, int out_count变成avframe呢?
/// 可以调用 AVFrame *av_frame_alloc(void);  或者 int av_frame_get_buffer(AVFrame *frame, int align);
/// 设置该avframe 的参数:三要素 需要和 我们的audioencoder一样才行,
/// 然后将 avframe 的参数    uint8_t *data[AV_NUM_DATA_POINTERS] = out
/// 将 avframe 的参数     int linesize[AV_NUM_DATA_POINTERS] = out_count;
/// 为了方便期间,我们可以 给 swr_convert 方法实际调用的 传递参数为
/// int swr_convert(struct SwrContext *s, avframe.data[x], avframe.cout_count[x], const uint8_t **in , int in_count);
/// 也就是说,我们在转换数据之前,最好先弄一个 avframe 准备存储转换好的数据
/// 因此最好先弄一个avframe出来,显然的是,我们的三要素是要和audioencoder的参数一样的, AVSampleFormat必须是AV_SAMPLE_FMT_FLTP,int out_nb_samples 必须是1024
///
/// AVFrame *AllocFltpPcmFrame(AVChannelLayout *out_ch_layout, AVSampleFormat out_sample_fmt, int out_nb_samples);


///核心函数四,真正的 swr数据
/// 我们现在已经init swr,然后有了输入输出缓冲区,还 AllocFltpPcmFrame 出来了一个 avframe,那么下来应该真正的转化数据了,并将数据放置到 avframe中了
///  /// int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in , int in_count);

///int swr_convert_and_fill_avframe(struct SwrContext *s, uint8_t **out, int out_count, const uint8_t **in , int in_count, AVFrame * outavframe);



class AudioResampler
{
public:
    AudioResampler();
    ~AudioResampler();

//AVFrame
    int InitResampler(const AVChannelLayout *out_ch_layout,
                      enum AVSampleFormat out_sample_fmt,
                      int out_sample_rate,
                      const AVChannelLayout *in_ch_layout,
                      enum AVSampleFormat  in_sample_fmt,
                      int  in_sample_rate);
    void DeInitResampler();

    int resamples_alloc_inbuffer_and_outbuffer(uint8_t ***in_audio_data,
                                               int *in_linesize,
                                               uint8_t ***out_audio_data,
                                               int *out_linesize);
    AVFrame *AllocFltpPcmFrame(AVChannelLayout out_ch_layout, AVSampleFormat out_sample_fmt, int out_sample_rate,int out_nb_samples);


    int swr_convert_and_fill_avframe(uint8_t **out, int out_count, const uint8_t **in , int in_count, AVFrame * outavframe);

    int swr_convert_and_fill_avframe_changed( AVFrame * avframe, const uint8_t **in , int in_count);

    void FreeFltpavframe(AVFrame * avframe);
public:
    SwrContext* _swrContext = nullptr;

    const AVChannelLayout *_out_ch_layout;
    AVSampleFormat _out_sample_fmt;
    int _out_sample_rate;

    const AVChannelLayout *_in_ch_layout;
    enum AVSampleFormat  _in_sample_fmt;
    int  _in_sample_rate;

    //重采样输入缓冲区 和输出缓冲区相关
    uint8_t **in_audio_data_buf;
    int in_linesize_buf;
    uint8_t **out_audio_data_buf;
    int out_linesize_buf;

};

#endif // AUDIORESAMPLER_H

audioresample.cpp

#include "audioresampler.h"

//这个类是用来 音频重采样相关
AudioResampler::AudioResampler()
{

}

AudioResampler::~AudioResampler()
{
    DeInitResampler();
}

#define ERROR_BUF \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof (errbuf));

#define CODE(func, code) \
    if (ret < 0) { \
    ERROR_BUF; \
    qDebug() << #func << "error" << errbuf; \
    code; \
    }

#define END(func) CODE(func, fataError(); return;)
#define RET(func) CODE(func, return ret;)
#define CONTINUE(func) CODE(func, continue;)
#define BREAK(func) CODE(func, break;)

int AudioResampler::InitResampler(const AVChannelLayout *out_ch_layout,
                                  enum AVSampleFormat out_sample_fmt,
                                  int out_sample_rate,
                                  const AVChannelLayout *in_ch_layout,
                                  enum AVSampleFormat  in_sample_fmt,
                                  int  in_sample_rate){
    cout << "InitResampler func "
         << " out_ch_layout = " << out_ch_layout
         << " out_sample_fmt = "<< out_sample_fmt
         << " out_sample_rate = " << out_sample_rate
         <<" in_ch_layout = " << in_ch_layout
        <<" in_sample_fmt = " << in_sample_fmt
       <<" in_sample_rate = " << in_sample_rate
      << endl;

    int ret = 0;
    this->_out_ch_layout = out_ch_layout;
    this->_out_sample_fmt = out_sample_fmt;
    this->_out_sample_rate = out_sample_rate;

    this->_in_ch_layout = in_ch_layout;
    this->_in_sample_fmt = in_sample_fmt;
    this->_in_sample_rate = in_sample_rate;
    ret = swr_alloc_set_opts2(&this->_swrContext,
                              out_ch_layout,
                              out_sample_fmt,
                              out_sample_rate,
                              in_ch_layout,
                              in_sample_fmt,
                              in_sample_rate,
                              0,nullptr);
    if(ret < 0){
        cout << "InitResampler func error because swr_alloc_set_opts2 error "
             << " out_ch_layout = " << out_ch_layout
             << " out_sample_fmt = "<< out_sample_fmt
             << " out_sample_rate = " << out_sample_rate
             <<" in_ch_layout = " << in_ch_layout
            <<" in_sample_fmt = " << in_sample_fmt
           <<" in_sample_rate = " << in_sample_rate
          << endl;
        ERROR_BUF;
        return ret;
    }

    ret = swr_init(this->_swrContext);
    if(ret < 0){
        cout<<"swr_init error "<<endl;
        ERROR_BUF;
        return ret;
    }
    return ret ;
}

void AudioResampler::DeInitResampler()
{
    if(this->_swrContext){
        swr_free(&this->_swrContext);
        this->_swrContext = nullptr;
    }
}


int AudioResampler::resamples_alloc_inbuffer_and_outbuffer(uint8_t ***in_audio_data,
                                           int *in_linesize,
                                           uint8_t ***out_audio_data,
                                           int *out_linesize){
    int ret = 0;
    int out_nb_number = 1024; //我们输出的每次要是1024个样本帧,这是 ffmpeg aac决定的。
    /// 输入源每个声道的样本个数 = 输入源采样率 * 输出源每个声道的样本数 / 输出源的采样率

    int in_nb_samplerate = av_rescale_rnd(this->_in_sample_rate,out_nb_number,this->_in_sample_rate,AV_ROUND_UP);
//    int av_samples_alloc_array_and_samples(uint8_t ***audio_data, int *linesize, int nb_channels,
//                                           int nb_samples, enum AVSampleFormat sample_fmt, int align);
     ret = av_samples_alloc_array_and_samples(in_audio_data,
                                              in_linesize,
                                              this->_in_ch_layout->nb_channels,
                                              in_nb_samplerate,
                                              this->_in_sample_fmt,
                                              0);
     if(ret <0 ){
         cout<<"av_samples_alloc_array_and_samples inbuffer error"<<endl;
         ERROR_BUF;
         return ret;
     }

     ret = av_samples_alloc_array_and_samples(out_audio_data,out_linesize,
                                        this->_out_ch_layout->nb_channels,
                                        out_nb_number,
                                        this->_out_sample_fmt,
                                        0);
     if(ret <0 ){
         cout<<"av_samples_alloc_array_and_samples outbuffer error"<<endl;
         ERROR_BUF;
         return ret;
     }

    return ret;
}

AVFrame * AudioResampler::AllocFltpPcmFrame(AVChannelLayout out_ch_layout, AVSampleFormat out_sample_fmt, int out_sample_rate,int out_nb_samples){

    AVFrame * fltppcmavframe = nullptr ;
    fltppcmavframe = av_frame_alloc();
    if(fltppcmavframe == nullptr){
        cout<<"AllocFltpPcmFrame func error bacause av_frame_alloc erorr"<<endl;
        return nullptr;
    }
    fltppcmavframe->ch_layout = (const AVChannelLayout )out_ch_layout;
    fltppcmavframe->format = out_sample_fmt;
    fltppcmavframe->sample_rate = out_sample_rate;
    fltppcmavframe->nb_samples = out_nb_samples;
    int ret  = av_frame_get_buffer(fltppcmavframe, 0);
    if(ret < 0 ){
        ERROR_BUF;
        cout<<"AllocFltpPcmFrame func error bacause av_frame_get_buffer erorr"<<endl;
        return nullptr;
    }

    return fltppcmavframe;
}

int AudioResampler::swr_convert_and_fill_avframe_changed( AVFrame * avframe, const uint8_t **in , int in_count){
    int ret =0;
    int realconvert =  swr_convert(this->_swrContext,avframe->data,avframe->nb_samples,in,in_count);
    if(realconvert < 0 ){
        ERROR_BUF;
        cout<<"swr_convert_and_fill_avframe func error because swr_convert error"<<endl;
    }
    return ret;
}
int AudioResampler::swr_convert_and_fill_avframe(
                                                 uint8_t **out, int out_count,
                                                 const uint8_t **in , int in_count,
                                                 AVFrame * outavframe){
   int ret =0;
   int realconvert =  swr_convert(this->_swrContext,out,out_count,in,in_count);
   if(realconvert < 0 ){
       ERROR_BUF;
       cout<<"swr_convert_and_fill_avframe func error because swr_convert error"<<endl;
   }

   // 从 in 开始的地址,转化最多 in_count个字节,到 out,最多 out_count个字节
   int isplanner = av_sample_fmt_is_planar((AVSampleFormat)outavframe->format);
   if(isplanner == 1){
       //我们使用的ffmpeg 的aac ,是planner 模式
       for(int i=0; i< outavframe->ch_layout.nb_channels; ++i){
           outavframe->data[i] = out[i];
           //realconvert为真正读取到的数据的长度,在planner模式下,每个planner的数据都是一样的。
           outavframe->linesize[i] = realconvert;
       }
   }else {
       //交错模式,就只有 out[0]有数据
       outavframe->data[0] = out[0];
       outavframe->linesize[0] = realconvert;
   }

   return ret;

}

void AudioResampler::FreeFltpavframe(AVFrame * avframe){
    if(avframe)
       av_frame_free(&avframe);
}


3. 音频编码相关类 audioencoder.h

audioencoder.h

#ifndef AUDIOENCODER_H
#define AUDIOENCODER_H

/// 设计思路,该类主要的目的是将 avframe数据变成 aac 的avpacket。
///
/// 核心函数1,编码函数 encode:
/// 那么该用于编码的函数,假设我们叫做Encode函数,第一个参数就应该是 avframe,返回值理论上可以是avpacket,或者参数中有一个 AVPacket *,

/// 1.1我们知道一个avframe在 send到编码器上下文的时候,调用函数为    ret = avcodec_send_frame(this->_avcodecContext,frame);
/// 然后我们再调用        ret = avcodec_receive_packet(this->_avcodecContext, avpacket); 这时候 aac 编码的数据就到了 avpacket中了
/// 我们还知道,一个avframe 在send到编码器后,可能 avcodec_receive_packet 要多次才能接受完成。
/// 因此前面Encode函数中的参数要改动一下,不能是返回一个avpacket,应该是返回 一堆 avpacket,我们这里使用vector<AVPacket *>就可以,或者说参数中不能只有一个 AVPacket *,而应该是有 vector<AVPacket *> &packets

/// 1.2那么返回的这个的vector<AVPacket *> 中的AVPacket需不需要什么参数呢?我们可以想象一下,我们这里的aac会变成一堆avpacket,那么h264也会变成一堆avpacket,当返回这些AVPacket 都是会给 AVStream ,那么AVStream一定需要区分哪些是aac的avpacket,哪些是h264的avpacket
/// 因此这里avpacket是需要 一个 stream_index,表明自己是 aac的 stream_index,还是 h264的stream_index,那么这个参数怎么传递的呢?因此encode方法应该还有一个参数就做 stream_index
/// 而且最后需要执行     packet->stream_index = stream_index;

/// 1.3时间相关问题,
/// pcm阶段的计算:
/// 我们在还是pcm数据的时候,会计算出来1024个样本帧的花费的时间,具体的计算公式为 1024/44100 * 1000,000。这里1000000 是转化为微秒的时间基
/// 也就是在pcm的时候,计算出来的1024个样本花费的时间,那么每从pcm文件中读取1024个数据,就会给 pts +=1024/44100 * 1000,000
/// 总结在pcm阶段,我们从pcm文件中读取1024个样本,就需要给pts+1024/44100 * 1000,000, pcm 时间基是1000,000

/// avframe阶段的计算
/// 当我们将读取的数据要存储到avframe中的时候,就需要重新计算avframe的pts,使用avframe对应的时间基
/// 首先要明确的是的 对于avframe 对应的时间基用的是avcodec中的timebase,那么avcodec中的timebase是怎么知道的呢?观察源码会发现,音频编码器上下文的timebase 是在 avcodec_open2方法中 设置的,且设置的值的为 avcodecContext中的 sample_rate 的倒数,也就是1/44100
/// 注意,avcodecContext中的 sample_rate实际上是需要user 手动的设定的,因此你就明白了为什么在 编码的时候一定要设置 编码器上下文的 sample_rate了
/// 那么现在我们有了三个值了,一个是 pcm的pts,一个是pcm的时间基,一个是avframe的时间基,求avframe的pts?
///  pcm的pts * pcm 的时间基 = avframe 的pts * avframe的时间基(也就是avcodecContext的timebase)
/// 结论为 :avframe 的pts  = pcm的pts * pcm 的时间基 / avframe的时间基(avcodecContext的timebase)
/// 为了防止内存溢出等问题,使用ffmpeg给我们提供的方法 :av_rescale_q(pts, AVRational{1, (int)time_base}, this->_avcodecContext->time_base)
/// 可以观察 :av_rescale_q方法的本质,就是第一个参数乘以第二个参数,最后除以第三个参数

/// avpacket阶段的计算
/// 到这里,我们最终生成的avpacket的 pts,dts,duration的值又是多少呢?
/// 实际上,在我们这个阶段,avpacket的 pts,dts,duration的值会直接 copy avframe中的pts,dts,duration(都是通过avcodec_receive_packet(this->_avcodecContext, avpacket) 方法传递的)
/// 这明显是不合理的。我们可以回顾一下 avframe 和avpacket 的本质,avframe装的是未压缩的pcm数据,avpacket装的是压缩过的pcm数据(也就是类似aac这样的数据)
/// 那么依然存在着  avframe的pts(当前已经存储在avpacket的pts) 转化成 真正的avpacket的pts的问题,那么avframe的时间基我们是知道的--是avcodec中的timebase,那么 这个真正的avpacket的时间基是什么呢?是avstream中的timebase
/// 那么avstream中的timebase怎么知道呢?实际上不同的编码器,它的avstream中的timebase是不同的,ffmpeg在你调用 avformat_write_header(fmt_ctx_, NULL);函数的时候就确定了您的timebase 是多少了,这也很容易想到,对于不同的编码器,它的头部信息一定就含有了 timebase这样重要的信息
/// avframe 的pts(当前旧的avpacket的pts) * avframe的时间基(avcodec的时间基) = 真正的avpacket的pts * 真正的avpacket的时间基(也就是avstream的时间基)
/// 结论为 :真正的avpacket的pts  = avframe 的pts(当前旧的avpacket的pts) * avframe的时间基(avcodec的时间基) / 真正的avpacket的时间基(也就是avstream的时间基)
/// 对应代码在 muxer.cpp的sendPacket中。
///
/// 关于时间问题,看了网上的资料,大致结论如下,
/// 不同结构体的 time_base
///1、AVStream的time_base的单位是秒。每种格式的time_base的值不一样,根据采样来计算,比如mpeg的pts、dts都是以90kHz来采样的,所以采样间隔就是1/900000秒。
///2、AVCodecContext的time_base单位同样为秒,不过精度没有AVStream->time_base高,大小为1/framerate。
///3、AVPacket下的pts和dts以AVStream->time_base为单位(数值比较大),时间间隔就是AVStream->time_base。
///4、AVFrame里面的pkt_pts和pkt_dts是拷贝自AVPacket,同样以AVStream->time_base为单位;而pts是为输出(显示)准备的,以AVCodecContex->time_base为单位。
///5、输入流InputStream下的pts和dts以AV_TIME_BASE为单位(微秒),至于为什么要转化为微秒,可能是为了避免使用浮点数。
///6、输出流OutputStream涉及音视频同步,结构和InputStream不同,暂时只作记录,不分析

///1.4 好,我们回到正题,还是看encode的参数,应该还有哪些?如果上述时间问题,明白了6,7成,那么就应该能感觉到,我们encode方法应该还有两个参数,一个是pcm的pts,一个是pcm的timebase

///1.5 该方法设计原型为如下
///int Encode(AVFrame *frame, int stream_index, int64_t pcmpts, int64_t pcmtime_base, vector<AVPacket *> &packets);
///到这里的时候,有必要回顾一下 initaac方法的各个参数,就能理解即使 重采样后应该也没有问题,即使audioresample后 sampleformat 和samplerate发生变化,由于我们在initaac的参数是能处理的编码器上下文参数, 传递进来的avframe需要和 编码器上下文 需要的参数一致



///核心函数2 初始化函数initAAC 初始化编码器和编码器上下文,传递的参数该编码器最终要处理的pcm的格式,声道数,采样率。最后一个参数是 比特率。
/// 首先,我们就需要一个编码器,以及一个编码器上下文,而且因为我们使用的是ffmpeg自带的aac编码器,支持的 采样格式只能是 AV_SAMPLE_FMT_FLTP 的,
/// 而且 支持的采样率 和声道格式 也是有要求的。
/// 因此我们的initaac函数应该是 参数的,需要的有四个 : AVChannelLayout, AVSampleFormat, int samplerate, int bit_rate

///2.1 实际上 AVSampleFormat 参数是不需要的,因为初始化的编码器只能是ffmpeg的aac,而ffmepg自带的aac编码器只支持一种格式--AV_SAMPLE_FMT_FLTP
/// 那么你传递一个 AV_SAMPLE_FMT_S16 进来去 初始化这个编码器,一定会失败。
/// 那么 AVChannelLayout,int samplerate, int bit_rate 这三个传递就来是干啥的呢?
///     const AVCodec * avcodec =  avcodec_find_encoder(AV_CODEC_ID_AAC);
///     this->_avcodecContext  = avcodec_alloc_context3(avcodec);
/// 主要是给avcodecContext中的关键参数赋值的。
/// _avcodecContext->bit_rate = bit_rate;
/// _avcodecContext->sample_fmt = avsampleformat;
/// _avcodecContext->sample_rate = samplerate;
/// _avcodecContext->ch_layout = avchannel;
/// 而且合理的写法是,我们应该在 avcodec_find_encoder(AV_CODEC_ID_AAC);后,根据 得到的 const AVCodec * avcodec 中的 如下三个参数判断传递进来的参数是否合理,如果不合理,应该提示user或者退出程序
/// const AVRational *supported_framerates; ///< array of supported framerates, or NULL if any, array is terminated by {0,0}
/// const int *supported_samplerates;       ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0
/// const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1
/// 当前程序应该是直接忽略检查 传递进来的参数 的写法。

/// 2.2 为了方便期间,我们可以在audioencoder类中,定义一些变量,让这些变量记住传递进来的这些参数,
/// 还应该用变量记住当前的 avcodecContext,因为avcodecContext 非常重要。


/// 3 核心函数 获取一帧数据在单个通道有多少个采样点 函数原型设计为:int AudioEncoder::GetFrameSize();本质上是获得 avctx->frame_size

///该函数用来获取 一帧数据在单个通道有多少个采样点,我们前面并没有给avcodecContex设置 framesize,为什么在这里framesize就有值了呢?
///这是因为 在InitAAC 的第三步 打开编码器上下文(也就是 avcodec_open2 方法内部实现中),会设置frame_size.
///那么这里有一个问题 avcodec_open2 方法是 ffmepg内部实现的,frame_size是 aac这个具体的编码器的值(比如aac是1024,mp3是1152),那么这个逻辑是怎么串起来的呢?
/// 3.1从最开始的avcodec_find_encoder 或者 avcodec_find_decoder开始看起。
/// 方法在 D:\Ctool\yinshipin\ffmpeg-6.0source\libavcodec\allcodecs.c 文件中。
///
/// const AVCodec *avcodec_find_encoder(enum AVCodecID id)
/// {
///     return find_codec(id, av_codec_is_encoder);
/// }
///
/// 3.2 find_codec的核心是av_codec_iterate方法
///
/// 3.3 av_codec_iterate 方法的本质是从codec_list中查找     const FFCodec *c = codec_list[i];
///
/// 3.4 在 codec_list 对应的aac有多个,其中ffmpeg自带的aac就是 ff_aac_encoder
///
/// 3.5 我们在源码中查找 ff_aac_encoder关键字,可以看到在 D:\Ctool\yinshipin\ffmpeg-6.0source\libavcodec\aacenc.c
/// const FFCodec ff_aac_encoder = {
/// .p.name         = "aac",
/// CODEC_LONG_NAME("AAC (Advanced Audio Coding)"),
/// .p.type         = AVMEDIA_TYPE_AUDIO,
/// .p.id           = AV_CODEC_ID_AAC,
/// .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |AV_CODEC_CAP_SMALL_LAST_FRAME,
/// .priv_data_size = sizeof(AACEncContext),
/// .init           = aac_encode_init,
/// FF_CODEC_ENCODE_CB(aac_encode_frame),
/// .close          = aac_encode_end,
/// .defaults       = aac_encode_defaults,
/// .p.supported_samplerates = ff_mpeg4audio_sample_rates,
/// .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
/// .p.sample_fmts  = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE },
/// .p.priv_class   = &aacenc_class,
/// };
///
/// 3.6 如何启动的呢?
/// ret = avcodec_open2(avcodecContext,nullptr,nullptr); 内部是做了这个事情的。
/// if (codec2->init) {
/// 实际上就会调用到:
/// static av_cold int aac_encode_init(AVCodecContext *avctx)
/// aac_encode_init 函数中会设置     avctx->frame_size = 1024;




#include "iostream"
using namespace std;
extern "C" {
    #include "libavutil/avassert.h" // include 后面<> 表示会从标准库路径中查找指定的文件,""表示从当前当前目录(即包含 #include 指令的文件所在的目录)中查找指定的文件
    #include "libavutil/channel_layout.h"
    #include "libavutil/opt.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/timestamp.h"
    #include "libswscale/swscale.h"
    #include "libswresample/swresample.h"
    #include "libavutil/error.h"
    #include "libavutil/common.h"
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
}
#include <vector>

#define ERROR_BUF \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof (errbuf));

#define CODE(func, code) \
    if (ret < 0) { \
        ERROR_BUF; \
        cout << #func << "error" << errbuf; \
        code; \
    }

class AudioEncoder
{
public:
    AudioEncoder();
    ~AudioEncoder();

    ///
    int InitAAC(AVChannelLayout avchannel, AVSampleFormat aacavsampleformat,int aacsamplerate, int aacbit_rate);
//    int InitAAC(AVChannelLayout avchannel, int samplerate,int bit_rate); //要AVSampleFormat avsampleformat参数没有啥用

    //为mp3编码预留接口
    int InitMP3(AVChannelLayout avchannel, AVSampleFormat avsampleformat,int samplerate);

    //释放资源
    int Deinit();


    //该函数用来获取 一帧数据在单个通道有多少个采样点,
    int GetFrameSize();

    //该函数用来获取 pcm格式,
    int GetSampleFormat();

///  将传进来的avframe转编码成AVPacket,第一个参数是avframe,实际上是一帧数据。
//   对于pts而言,pts的增长如下 double audio_frame_duration = 1.0 * audio_encoder.GetFrameSize()/pcm_sample_rate*audio_time_base;
//   1024 / 44100 * 1000000 = 23219.95464852608 微秒 第一帧数据在pcm阶段 显示的时间为23219


    /**
     * @brief Encode 该函数的作用是将avframe 转换成 avpacket
     * @param frame  要转换的avframe,注意的是这个avframe是音频重采样后的avframe,
     * @param stream_index  是video index 还是 audio index
     * @param pts          pcm 花费的时间
     * @param time_base    pcm 的时间基,
     * pts 和 time_base的作用是 :加上avframe的timebase(avframe的timebase 就是 avcodecContext的timebase)  最终计算出 avframe的pts。
     * @return nullptr 表示失败,成功则返回AVPacket,注意返回的AVPacket中的 pts 是从 avframe拷贝过去的,因此 这时候avpacket中的pts是有问题的,不是真正的pts
     * 这里要注意的是,pts是原pcm的pts,第一次参数frame是重采样后的frame,
     * 这里有个问题:如果原始的pcm是 48000 , pts是一次读取1024个样本帧的时间,
     * 而音频重采样后的pcm是 44100 ,那么这个pts参数还是利用 1024/48000 * 1000000吗?
     * 答案是:也是一样的,虽然重采样 从48000变成44100,avcodec中的timebase 是传递的该编码器处理的采样率,应该传递的也是44100
     *
     */
    AVPacket *Encode(AVFrame *frame , int stream_index, int64_t pts, int64_t time_base);
    int Encode(AVFrame *frame, int stream_index, int64_t pts, int64_t time_base,
               vector<AVPacket *> &packets);


    AVCodecContext *GetCodecContext();
    AVChannelLayout GetChannelLayout();
    int GetSampleRate();

private:
    //pcm声道格式 -ac 2, 这里为什么要给一个初始值为AV_CHANNEL_LAYOUT_STEREO?猜测为AVChannelLayout没有none的值,因此随便给了一个值,这个默认值不会用到,所有的参数都需要在构造方法中传递真正的值
    AVChannelLayout _avchannel = AV_CHANNEL_LAYOUT_STEREO;

    //pcm格式 -f s16le
    AVSampleFormat _avsampleFormat = AV_SAMPLE_FMT_NONE;

    //pcm采样率 -ar 44100
    int _samplerates = 0;

    //aac encoder 的比特率。
    int _bitrate = 0; // aac的比特率,对于不同格式的aac是有比特率的,这里这个默认值应该也不会用到,所有的参数都需要在构造方法中传递真正的值


    //编码后的 AVCodecContext
    AVCodecContext * _avcodecContext = nullptr;

};

#endif // AUDIOENCODER_H

audioencoder.cpp

#include "audioencoder.h"

AudioEncoder::AudioEncoder()
{

}

AudioEncoder::~AudioEncoder()
{
    if(this->_avcodecContext){
        Deinit();
    }

}

//    int InitAAC(AVChannelLayout avchannel, int samplerate,int bit_rate); teacher代码

//传递进来的 avsampleformat 必须是 AV_SAMPLE_FMT_FLTP,因为我们用的是 ffmpeg 自带的encoder aac
//这里的参数是传递进来的,而不是在AudioEncoder中参数默认的值
int AudioEncoder::InitAAC(AVChannelLayout avchannel,
                          AVSampleFormat avsampleformat,
                          int samplerate,
                          int bit_rate)
{

    cout<<"audioencoder.InitH264 avchannel.nb_channels = " << avchannel.nb_channels
       <<" avsampleformat = "<< avsampleformat
       <<" samplerate = " << samplerate
       <<" bit_rate = " << bit_rate
       <<endl;

    int ret = 0;
    //1.使用AudioEncoder内部的变量记住 传递进来的值
    this->_avchannel = avchannel;
    this->_avsampleFormat = avsampleformat;
    this->_samplerates = samplerate;
    this->_bitrate = bit_rate;

    //2.找到编码器
    const AVCodec * avcodec =  avcodec_find_encoder(AV_CODEC_ID_AAC);
    if(avcodec == nullptr){
        ret = -1;
        cout<<" func InitAAC error because avcodec_find_encoder(AV_CODEC_ID_AAC) error "<<endl;
        return ret;
    }

    //3.通过编码器找到编码器上下文
    this->_avcodecContext  = avcodec_alloc_context3(avcodec);
    if(_avcodecContext == nullptr){
        ret = -1;
        cout<<" func InitAAC error because avcodec_alloc_context3(avcodec) error "<<endl;
        return ret;
    }


    //3.1 设置 音频编码器上下文的参数,很显然,除了 音频三要素 要设定外,我们还要设定的是 bit_rate,以及 flags(用途是保证aac文件不带adst header),还要设定 bit_rate

    ///AV_CODEC_FLAG_GLOBAL_HEADER参数相关--中文翻译:将全局标头放置在extradata中,而不是每个关键帧中。
    ///这里要明白为什么加这个参数,需要知道如下的两个知识点:
    /// 1. mp4文件中的 aac 是不带 adst header的,因此我们在将 aac 合成为mp4的时候,不能给每个aac帧的前面加 adst header
    /// 2. AV_CODEC_FLAG_GLOBAL_HEADER参数的含义就是:将全局标头放置在extradata中,而不是每个关键帧中。,对于aac来说,在每一帧的前面不加 adst header
    /// 这里扩展一下h264,对于h264,有Annexb 和 AVCC 两种存储模式,MP4中的存储的是h264是AVCC格式的,AVCC格式是只有一个头文件在最前面,后面的都是h264纯数据,因此,h264编码成mp4的时候,应该也需要添加 AV_CODEC_FLAG_GLOBAL_HEADER这个标志flag
    _avcodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    //将avcodecContext中的关键参数也使用 传递进来的值赋值。
    _avcodecContext->bit_rate = bit_rate;
    _avcodecContext->sample_fmt = avsampleformat;
    _avcodecContext->sample_rate = samplerate;
    _avcodecContext->ch_layout = avchannel;

    //3.打开编码器上下文 - 使用avcodec_open2 方法,第二个参数这里传递为nullptr,是因为avcodecContext 是用
    ret = avcodec_open2(_avcodecContext,nullptr,nullptr);
    if(ret < 0 ){
        char errinfo[1024] = "";
        av_strerror(ret, errinfo,1024);
        cout << "Init_Muxer func error because avformat_alloc_output_context2 error errinfo = " << errinfo << endl;
        avcodec_free_context(&_avcodecContext);
        return ret;
    }


    cout<<"init aac success..."<<endl;

    return ret;
}

int AudioEncoder::Deinit()
{
    if(this->_avcodecContext){
        avcodec_free_context(&this->_avcodecContext);
        this->_avcodecContext = nullptr;  //这一句实际上不需要,因为avcodec_free_context中已经值置空了。
    }
}

//该函数用来获取 一帧数据在单个通道有多少个采样点,我们前面并没有给avcodecContex设置 framesize,为什么在这里framesize就有值了呢?
//这是因为 在InitAAC 的第三步 打开编码器上下文(也就是 avcodec_open2 方法内部实现中),会设置frame_size.
//那么这里有一个问题 avcodec_open2 方法是 ffmepg内部实现的,frame_size是 aac这个具体的编码器的值(比如aac是1024,mp3是1152),那么这个逻辑是怎么串起来的呢?
//
int AudioEncoder::GetFrameSize()
{
    if(this->_avcodecContext){
        return this->_avcodecContext->frame_size;
    }
    return -1;
}

//该函数用来获取 pcm格式,pcm格式是user设定的。
int AudioEncoder::GetSampleFormat()
{
    if(this->_avcodecContext){
        return this->_avcodecContext->sample_fmt;
    }
    return AV_SAMPLE_FMT_NONE; //-1
}


//将传进来的frame转编码成AVPacket,第一个参数是avframe,实际上是一帧数据。
//第二个参数是 int stream_index ,这个从调用来看,应该是 avstream 中对应的 audio_index,还是 video_index.
//第三个参数是 int64_t pts,这个是用来干啥的呢?从使用来看,每次处理一个avframe,这个pts是 一帧audio frame 中所有pcm数据 (对于aac来说,就是1024个采样点)所占用的时间。当处理一帧 avframe,这个值就会 + 一帧avframe所需要的时间
//第四个参数是 int64_t time_base,这个是用来干啥的呢?从使用来看,传递的是1000000,是微秒 单位。
//也就是说:第三个参数和第四个参数的意义为:pcm数据这一帧显示的时间,用的时间基是啥

//   对于pts而言,pts的增长如下 double audio_frame_duration = 1.0 * audio_encoder.GetFrameSize()/pcm_sample_rate*audio_time_base;
//    1024 / 44100 * 1000000 = 23219.95464852608 微秒 第一帧数据在pcm阶段 显示的时间为23219.95464 ,第二帧数据 pts 显示 46439.90929705215
// time_base 为1000000.

AVPacket *AudioEncoder::Encode(AVFrame *frame,
                               int stream_index,
                               int64_t pts,
                               int64_t time_base)
{

    int ret =0;
    if(this->_avcodecContext == nullptr){
        cout<<"class AudioEncoder func Encode error because this->_avcodecContext == nullptr return nullptr"<<endl;
        return nullptr;
    }

    //this->_avcodecContext->time_base 的值是 1/44100
    ///从  avcodec_open2 源码可以看到,如果是 avcodecContext 的 type 是 AUDIO,avcodecContex的timebase就是  1/sample_rate
    ///     if (avctx->codec_type == AVMEDIA_TYPE_AUDIO &&
    ///         (!avctx->time_base.num || !avctx->time_base.den)) {
    ///             avctx->time_base.num = 1;
    ///             avctx->time_base.den = avctx->sample_rate;
    ///     }

    //转化时间,传递进来的(pts * 1/1000000)/ (1/44100)
    //转化后发现,第一个帧的时候,pts是1024,第二帧的时候,pts是2048
    pts = av_rescale_q(pts, AVRational{1, (int)time_base}, this->_avcodecContext->time_base);
    if(frame) {
        frame->pts = pts;
    }
    //这个传递进来的 frame是 已经打包好的frame,因此直接发送到 编码器就好了
    ret = avcodec_send_frame(this->_avcodecContext,frame);
    if(ret < 0){
        //@retval 0                 success
        //<0 说明
        cout<<"AudioEncoder::Encode avcodec_send_frame error ret = " << ret  << endl;
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avcodec_send_frame failed:%s\n", errbuf);
        return nullptr;

    }
    // 编码和解码都是一样的,都是send 1次,然后receive多次, 直到AVERROR(EAGAIN)或者AVERROR_EOF
    AVPacket *packet = av_packet_alloc();
    ret = avcodec_receive_packet(this->_avcodecContext, packet);在avcodec_receive_packet 完成后,avpacket的pts,avpacket的dts,avpacket的duration都有确定的值了,这时候avpacket中的pts,dts,duration都是从 avframe中拷贝过去的(这个从源码中看好像是这样,但是不是很确定)
    if(ret != 0) {
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("aac avcodec_receive_packet failed:%s\n", errbuf);
        av_packet_free(&packet);
        return NULL;
    }
    packet->stream_index = stream_index;
    return packet;



}


//这种写法,会将 avframe 中的数据打包成 avpacket,然后存储在avpacket 的vector中,由于是在
int AudioEncoder::Encode(AVFrame *frame, int stream_index, int64_t pts, int64_t time_base,
                         vector<AVPacket *> &packets)
{
    if(!this->_avcodecContext) {
        printf("codec_ctx_ null\n");
        return NULL;
    }
    pts = av_rescale_q(pts, AVRational{1, (int)time_base}, this->_avcodecContext->time_base);
    if(frame) {
        frame->pts = pts;
    }
    int ret = avcodec_send_frame(this->_avcodecContext, frame);
    if(ret != 0) {
        char errbuf[1024] = {0};
        av_strerror(ret, errbuf, sizeof(errbuf) - 1);
        printf("avcodec_send_frame failed:%s\n", errbuf);
        return NULL;
    }
    while(1)
    {
        //这个写法有没有问题呢?假设从frame中读取的数据要通过  9 次  avcodec_revceive 完成,也就是说,我们每次在内存应该有9个 avpacket,等待释放。
        AVPacket *packet = av_packet_alloc();
        ret = avcodec_receive_packet(this->_avcodecContext, packet);
        packet->stream_index = stream_index;
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            ret = 0;
            av_packet_free(&packet);
            break;
        } else if (ret < 0) {
            char errbuf[1024] = {0};
            av_strerror(ret, errbuf, sizeof(errbuf) - 1);
            printf("aac avcodec_receive_packet failed:%s\n", errbuf);
            av_packet_free(&packet);
            ret = -1;
        }
        packets.push_back(packet);
    }
    return ret;
}


AVCodecContext* AudioEncoder::GetCodecContext(){
    return this->_avcodecContext;
}


AVChannelLayout AudioEncoder::GetChannelLayout(){
    return this->_avchannel;
}
int AudioEncoder::GetSampleRate(){
    return this->_samplerates;
}









4. 视频和音频 结合的相关类 muxer.h

muxer.h

#ifndef MUXER_H
#define MUXER_H

//用于 将 aac 和 h264 组合在一起的类
#include "iostream"
using namespace std;
extern "C" {
    #include "libavutil/avassert.h" // include 后面<> 表示会从标准库路径中查找指定的文件,""表示从当前当前目录(即包含 #include 指令的文件所在的目录)中查找指定的文件
    #include "libavutil/channel_layout.h"
    #include "libavutil/opt.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/timestamp.h"
    #include "libswscale/swscale.h"
    #include "libswresample/swresample.h"
    #include "libavutil/error.h"
    #include "libavutil/common.h"
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
}

#define ERROR_BUF \
    char errbuf[1024]; \
    av_strerror(ret, errbuf, sizeof (errbuf));

#define CODE(func, code) \
    if (ret < 0) { \
        ERROR_BUF; \
        cout << #func << "error" << errbuf; \
        code; \
    }

class Muxer
{
public:
    Muxer();
    ~Muxer();

    //1. 初始化,其主要作用是调用 int avformat_alloc_output_context2(AVFormatContext **ctx, const AVOutputFormat *oformat, const char *format_name, const char *filename);
    int Init_Muxer(const char *url);


    //资源释放
    int DeInit_Muxer();

    //创建流,创建出来的avstream 会保存在成员变量 _audio_avstream 或者 _video_avstream 中
    int  AddStream(AVCodecContext *avcodecContext);

    //给音视频文件里面写入内容的三大步
    int sendHeader();// 写mp4文件的头部
    int sendPacket(AVPacket *avpacket);//写mp4文件的身体
    int sendTrailer(); //写mp4文件的尾部

    //打开 AVIO
    int open();

    int getAudioIndex();

    int getVideoIndex();

    AVCodecContext * getAudioAVCondecContext();

    AVCodecContext * getVideoAVCondecContext();


private:
    // 1.初始化 时候用到的参数,可以直接为nullptr,为了 avformat_alloc_output_context2 方法准备的,做为avformat_alloc_output_context2方法参数,传递时候时用 &_avformatContext 传递,也就是二级指针,在avformat_alloc_output_context2方法内部,会给_avformatContext 重新赋值,因此方法执行完毕后,_avformatContext不再为nullptr
    AVFormatContext * _avformatContext = nullptr;

    // 1. 初始化 时候用到,会将user传递的url保存 到 _url中,相当于将 要生成的 out.mp4 文件名字保存在这个里面
    string _url = "";

    //音频编码器上下文
    AVCodecContext *_audio_avcodecContext = nullptr;

    //视频编码器上下文
    AVCodecContext *_video_avcodecContext = nullptr;

    //音频stream
    AVStream * _audio_avstream = nullptr;

    //视频stream
    AVStream * _video_avstream = nullptr;

    //音频stream 的index
    int _audio_index = -1;

    //视频stream 的index
    int _video_index = -1;
};



#endif // MUXER_H

muxer.cpp

#include "muxer.h"


Muxer::Muxer()
{

}

Muxer::~Muxer()
{
    DeInit_Muxer();
}

//初始化
int Muxer::Init_Muxer(const char *url)
{
    int ret = 0;
    ///函数介绍:为output fromat创建一个 avformatContext。Allocate an AVFormatContext for an output format.
    ///分配一个 avformatContext for an output format,需要通过 avformat_free_context()方法释放,我们会在 释放资源的函数中调用avformat_free_context(),释放资源的函数为 int DeInit_Muxer();
    ///    int avformat_alloc_output_context2(AVFormatContext **ctx, const AVOutputFormat *oformat, const char *format_name, const char *filename);
    ///此函数第二个参数和第三个参数,在读取本地文件的时候,可以为nullptr,意思是根据第四个参数自行推断,但是在 网络推拉流的时候,有特殊的设置,不能为nullptr
    ///参数说明:
    ///    ctx:需要创建的context,返回NULL表示失败。
    ///    oformat:指定对应的AVOutputFormat,如果不指定,可以通过后面format_name、filename两个参数进行指定,让ffmpeg自己推断。
    ///    format_name: 指定音视频的格式,比如“flv”,“mpeg”等,如果设置为NULL,则由filename进行指定,让ffmpeg自己推断。
    ///    filename: 指定视频文件的路径,如果oformat、format_name为NULL,则ffmpeg内部根据filename后缀名选择合适的复用器,⽐如xxx.flv则使用flv复用器。
    ///    @return  >= 0 in case of success, a negative AVERROR code in case of failure
    ///    也就是说,此函数有两个判断值,可以判断失败,一个就是函数返回值,一个是第一个参数返回NULL
    ret = avformat_alloc_output_context2(&_avformatContext,
                                         nullptr,
                                         nullptr,
                                         url);
    if(ret < 0){
        char errinfo[1024] = "";
        av_strerror(ret, errinfo,1024);
        cout << "Init_Muxer func error because avformat_alloc_output_context2 error errinfo = " << errinfo << endl;
        return ret;
    }
    //    ERROR_BUF;这一句和上面一句的功能一样


    //保存 传递过来的url 到 _url
    this->_url = url;

    return ret;
}


//释放资源相关,
int Muxer::DeInit_Muxer()
{
    if(this->_avformatContext){
        avformat_free_context(this->_avformatContext);
        //        avformat_close_input(&(this->_avformatContext));// 示例代码中用的 avformat_close_input 方法 , avformat_close_input 方法和 avformat_free_context 方法应该两个都行,看源码 avformat_close_input方法内部会调用avformat_free_context方法
    }
    this->_url = "";
    this->_avformatContext = nullptr;
    this->_audio_avstream = nullptr;
    this->_video_avstream = nullptr;
    this->_audio_index = -1;
    this->_video_index = -1;
    this->_audio_avcodecContext = nullptr;
    this->_video_avcodecContext = nullptr;
    return 0;
}

int Muxer::AddStream(AVCodecContext *avcodecContext)
{
    int ret =0;
    //error 判断
    if(this->_avformatContext == nullptr){
        ret =-1;
        cout<<"func AddStream error bacause this->_avformatContext == nullptr"<<endl;
        return ret;
    }

    //error 判断
    if(avcodecContext == nullptr){
        ret =-1;
        cout<<"func AddStream error bacause avcodecContext == nullptr"<<endl;
        return ret;
    }

    //1.创建 avstream
    AVStream *avstream = avformat_new_stream(this->_avformatContext,nullptr);
    if(avstream == nullptr){

        cout<<"func AddStream error bacause avformat_new_stream func error == nullptr"<<endl;
        return ret;
    }

    //2. 将AddStream 方法参数 copy 到 AVStream 的cpdecpar中
    ret = avcodec_parameters_from_context(avstream->codecpar,avcodecContext);
    if(ret <0 ){
        ERROR_BUF;
        return ret;
    }

    //3. 打印 avfromatcontext 的信息。四个参数代表的含义如下:
    //    ic:输入/输出的AVFormatContext结构体;
    //    index:视频流/音频流/字幕流等流的索引号,可以为负数表示打印所有流的信息;
    //    url:输出的地址,可以为NULL,表示输出到标准错误流中;
    //    is_output:标识是输入还是输出,0表示输入,1表示输出。
    av_dump_format(this->_avformatContext,0,this->_url.c_str(),1);


    //4.判断当前是视频流还是 音频流, 然后 将对应的
    if(avcodecContext->codec_type == AVMEDIA_TYPE_AUDIO){
        this->_audio_avcodecContext = avcodecContext;
        this->_audio_avstream = avstream;// 说明当前new 出来的要是 audio 的 avstream。
        this->_audio_index = avstream->index;
    }

    if(avcodecContext->codec_type == AVMEDIA_TYPE_VIDEO){
        this->_video_avcodecContext = avcodecContext;
        this->_video_avstream = avstream;// 说明当前new 出来的要是 video 的 avstream。
        this->_video_index = avstream->index;
    }


    return ret ;
}


//发送头部信息,在发送头部信息的时候,会将avstream的 time_base 变成 1,90000
int Muxer::sendHeader()
{
    int ret =0;
    //error 判断
    if(this->_avformatContext == nullptr){
        ret =-1;
        cout<<"func sendHeader error bacause this->_avformatContext == nullptr"<<endl;
        return ret;
    }

    ret = avformat_write_header(this->_avformatContext,nullptr);
    cout << "avformat_write_header ret = " << ret << endl;
    if(ret < 0){
        ERROR_BUF;
        cout<<"func sendHeader error bacause avformat_write_header func error" << endl;
        return ret;
    }

    return ret;
}


//真正发送packet信息
int Muxer::sendPacket(AVPacket *avpacket)
{

    int ret =0;
    //error 判断
    if(this->_avformatContext == nullptr){
        ret =-1;
        cout<<"func sendPacket error bacause this->_avformatContext == nullptr"<<endl;
        //释放数据
        av_packet_free(&avpacket);
        return ret;
    }

    //error 判断
    if(avpacket == nullptr){
        ret =-1;
        cout<<"func sendPacket error bacause avpacket == nullptr"<<endl;
        //释放数据
        av_packet_free(&avpacket);
        return ret;
    }




    //得到avpacket 的data 首地址,avpacket->data指向保存压缩数据的指针,这就是AVPacket的实际数据。
    uint8_t * avpacket_data = avpacket->data;

    //得到avpacket 中data 的size
    int avpacket_size = avpacket->size;

    //error 判断
    if(avpacket_data == nullptr || avpacket_size <= 0){
        // 这说明没有数据,或者数据的大小为0,或者小于0,。
        ret =-1;
        cout<<"func sendPacket error bacause avpacket_data == nullptr || avpacket_size <= 0"<<endl;
        //释放数据
        av_packet_free(&avpacket);
        return ret;
    }


    //得到avpacket 的stream_index,用于判断是video_index 还是 audio_index
    int avpacket_stream_index = avpacket->stream_index;


    AVRational src_timebase; //avcodecContext 中的time_base,aacavcodecContext 是1,44100. h264的timebase是1,1000000
    AVRational dst_timebase; // 输出文件的 timebase
    if(avpacket_stream_index == this->_video_index && this->_video_avstream && this->_video_avcodecContext ){
        //如果当前传递进来的 stream_index 是 video_index,并且_video_avstream 和 _video_avcodecContext 两者都不为nullptr
        src_timebase = this->_video_avcodecContext->time_base; //(1,1000000)
        dst_timebase = this->_video_avstream->time_base;
    }else if(avpacket_stream_index == this->_audio_index && this->_audio_avstream && this->_audio_avcodecContext){
        //如果当前传递进来的 stream_index 是 audio_index,并且_audio_avstream 和 _audio_avcodecContext 两者都不为nullptr
        src_timebase = this->_audio_avcodecContext->time_base;//(1,44100)
        dst_timebase = this->_audio_avstream->time_base;
    }

    //时间基的转换 av_rescale_q 函数的 实质是 `a * bq / cq`.
//    x = a * bq / cq
//    目标是将原来的 avpacket->pts转化一下,原来的pts*src_timebase 就是要真正花费的时间。
//    例如原先的 pts 是10000,src_base是1/25,这时候 pts 对应的时间就应该是 400s,这个时间是一定的,
//    那么在avstream中的timebase如果和avpacket中不同,那么就需要重新计算 avstream中的pts,而实际上 avstream是没有 pts,avpacket 和avframe都用的 avstream中的time_base

    avpacket->pts = av_rescale_q(avpacket->pts, src_timebase,dst_timebase);
    avpacket->dts = av_rescale_q(avpacket->dts, src_timebase,dst_timebase);
    avpacket->duration = av_rescale_q(avpacket->duration, src_timebase,dst_timebase);

    //将avpacket数据写入, * Write a packet to an output media file ensuring correct interleaving.
    //av_interleaved_write_frame是有缓存的,假设我们 audio 是30,60,video是50,那么av_interleaved_write_frame内部会自动写成 audio 30,video 50,audio 60
    //由于有缓存,因此在直播的时候不建议使用这个方法
    ret = av_interleaved_write_frame(this->_avformatContext, avpacket);
    if(ret <0 ){
        ERROR_BUF;
        cout<<"func sendPacket error bacause av_interleaved_write_frame func error" << endl;
        //释放数据
        av_packet_free(&avpacket);
        return ret;
    }

    //在直播时候建议使用如下的代码
//    av_write_frame(this->_avformatContext,avpacket);


    //释放数据
    av_packet_free(&avpacket);
    return ret;


}















//发送尾部信息
int Muxer::sendTrailer()
{
    int ret =0;
    //error 判断
    if(this->_avformatContext == nullptr){
        ret =-1;
        cout<<"func sendTrailer error bacause this->_avformatContext == nullptr"<<endl;
        return ret;
    }
    ret = av_write_trailer(this->_avformatContext);
    if(ret < 0){
        ERROR_BUF;
        cout<<"func sendTrailer error bacause av_write_trailer func error" << endl;
        return ret;
    }

    return ret;
}

int Muxer::open()
{
    //创建并初始化AVIOContext以访问url指示的资源。
    int ret = avio_open(&this->_avformatContext->pb, this->_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",this->_url.c_str(), errbuf);
        return -1;
    }
    return 0;
}

int Muxer::getAudioIndex()
{
    return this->_audio_index;
}

int Muxer::getVideoIndex()
{
    return this->_video_index;
}

AVCodecContext * Muxer::getAudioAVCondecContext(){
    return this->_audio_avcodecContext;
}

AVCodecContext * Muxer::getVideoAVCondecContext(){
    return this->_video_avcodecContext;
}











5.核心调用

main.cpp

/***
 *该程序的目的是:
 * 将 一个pcm文件 和 一个 yuv文件,合成为一个 0804_out.mp4文件
 * pcm文件和yuv文件是从哪里来的呢?是从 sound_in_sync_test.mp4 文件中,使用ffmpeg命令 抽取出来的。
 * 这样做的目的是为了对比前后两个mp4(sound_in_sync_test.mp4  和 0804_out.mp4 ) 文件。
 *
 * 1. 从sound_in_sync_test.mp4 文件 中抽取 pcm命令如下:
 * ffmpeg -i sound_in_sync_test.mp4 -vn -ar 44100 -ac 2 -f s16le 44100_2_s16le.pcm
 * -vn 表示不处理视频
 *
 * 2. 从sound_in_sync_test.mp4 文件 中抽取 yuv命令如下:
 *
 * ffmpeg -i sound_in_sync_test.mp4 -pix_fmt yuv420p 720x576_yuv420p.yuv
 *
 * 3.播放测试
 *  对于 pcm 数据
 ffplay -ac 2 -ar 44100 -f s16le 44100_2_s16le.pcm
 *  对于 YUV 数据
 ffplay -pixel_format yuv420p -video_size 720x576 -framerate 25  720x576_yuv420p.yuv
 ***/

#include <iostream>
#include <string>
#include "audioencoder.h"
#include "videoencoder.h"
#include "muxer.h"
#include "audioresampler.h"

extern "C" {

#include "libavutil/avassert.h"
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/error.h>
#include <libavutil/common.h>

}
using namespace std;


#define YUV_WIDTH 720
#define YUV_HEIGHT 576
#define YUV_FPS 25
#define YUV_PIX_FMT AV_PIX_FMT_YUV420P
#define VIDEO_BIT_RATE 500 * 1024


#define PCM_SAMPLE_FORMAT AV_SAMPLE_FMT_S16
#define PCM_SAMPLE_RATE 44100
#define PCM_CHANNEL_LAYOUT AV_CHANNEL_LAYOUT_STEREO
#define AUDIO_BIT_RATE 128 * 1024


//时间基相关
#define AUDIO_TIME_BASE 1000000
#define VIDEO_TIME_BASE 1000000

int main()
{

    cout<<"aaa"<<endl;
    int ret =0;
    ///     0.先前准备。使用命令先弄出来pcm 为文件 和 yuv 文件
    ///     ffmpeg -i sound_in_sync_test.mp4 -pix_fmt yuv420p 720x576_yuv420p.yuv
    ///     ffmpeg -i sound_in_sync_test.mp4 -vn -ar 44100 -ac 2 -f s16le 44100_2_s16le.pcm

    ///第一步:打开yuv pcm文件
    string in_yuv_name = "D:/AllInformation/qtworkspacenew/08muxing_04_mp4/720x576_yuv420p.yuv";
    const char * in_pcm_name = "D:/AllInformation/qtworkspacenew/08muxing_04_mp4/44100_2_s16le.pcm";
    const char * out_mp4_name = "D:/AllInformation/qtworkspacenew/08muxing_04_mp4/out.mp4";

    FILE * in_yuv_fd = nullptr;
    FILE * in_pcm_fd = nullptr;

    ///第二步:初始化编码器,包括视频,音频编码器,分配yuv,pcm的帧buffer
    ///2.1 初始化视频编码器,这里为了后续的扩展和调试,我们使用变量,而不是宏定义完成
    int yuvwidth = YUV_WIDTH;
    int yuvheight = YUV_HEIGHT;
    int yuvfps = YUV_FPS;
    AVPixelFormat yuvpixfmt = YUV_PIX_FMT;
    int video_bit_rate = VIDEO_BIT_RATE;//注意,这个是video 的比特率,不是yuv
    videoencoder videoencoder;
    /// 2.1.1分配yuv buf
    int y_frame_size = 0;
    int u_frame_size = 0;
    int v_frame_size = 0;
    int yuv_frame_size = 0;
    uint8_t *yuv_frame_buf = nullptr;


    ///2.2 初始化音频编码器,这里为了后续的扩展和调试,我们使用变量,而不是宏定义完成
    int pcm_samplerate = PCM_SAMPLE_RATE;
    AVSampleFormat pcm_sampleformat = PCM_SAMPLE_FORMAT;
    AVChannelLayout pcm_avchannel_layout = PCM_CHANNEL_LAYOUT;
    int audio_bit_rate = AUDIO_BIT_RATE;
    AudioEncoder audioencoder;

    /// 2.2.1 分配pcm buf pcm_frame_size  = 单个采样点占用的字节 * 通道数量 * 每个通道有多少给采用点
    int pcm_frame_size = 0;
    uint8_t *pcm_frame_buf = nullptr;

    ///第三步:mp4初始化,包括新建流,open io,send header
    Muxer mp4_muxer;

    /// 4.1  时间戳相关 audio_pts 和 video_pts 刚开始都是0,audio_frame_duration是一帧aac数据
    double audio_pts = 0;
    double video_pts = 0;
    double audio_frame_duration = 0;
    double video_frame_duration = 0;
    int64_t audiotimebase = AUDIO_TIME_BASE;
    int64_t videotimebase = VIDEO_TIME_BASE;


    /// 4.2 video读完 或者 audio 读完 标志
    int audio_finish = 0;
    int video_finish = 0;
    int read_size = 0;  //用于记录每次读取的数据大小

    ///4.4 将 pcm变成 avframe,然后变成avpacket时,用于存储avpacket
    AVPacket *avpacket = nullptr;

    ///4.3  audio resample 相关
    AVChannelLayout out_resample_ch_layout;
    AVSampleFormat out_resample_sample_fmt;
    int out_resample_sample_rate;

    AVChannelLayout in_resample_ch_layout;
    AVSampleFormat in_resample_sample_fmt;
    int in_resample_sample_rate;

    AudioResampler audioresample;
    vector<AVPacket *> packets;

    ///第一步:打开yuv pcm文件
    in_yuv_fd = fopen(in_yuv_name.c_str(),"rb");
    if(!in_yuv_fd){
        ret = -1;
        cout<<"main.cpp fopen(in_yuv_name,'rb') func error return "<<endl;
        goto mainend;
    }
    in_pcm_fd = fopen(in_pcm_name, "rb");
    if(!in_pcm_fd){
        ret = -1;
        cout<<"main.cpp fopen(in_pcm_fd,'rb') func error return "<<endl;
        goto mainend;
    }




    ///第二步:初始化编码器,包括视频,音频编码器,分配yuv,pcm的帧buffer
    ///2.1 初始化视频编码器,这里为了后续的扩展和调试,我们使用变量,而不是宏定义完成
    ret = videoencoder.InitH264(yuvwidth,yuvheight,yuvpixfmt,yuvfps,video_bit_rate);
    if(ret < 0 ){
        cout<<"videoencoder.InitH264 error yuvwidth = " << yuvwidth
           <<" yuvheight = "<< yuvheight
          <<" yuvpixfmt = " << yuvpixfmt
         <<" yuvfps = " <<yuvfps
        <<" video_bit_rate = " << video_bit_rate
        <<endl;
        goto mainend;
    }
    /// 2.1.1分配yuv buf
    y_frame_size = yuvwidth * yuvheight;
    u_frame_size = yuvwidth * yuvheight /4;
    v_frame_size = yuvwidth * yuvheight /4;
    yuv_frame_size = y_frame_size + u_frame_size + v_frame_size;
    yuv_frame_buf = (uint8_t *)malloc(yuv_frame_size);
    if(!yuv_frame_buf)
    {
        ret =-1;
        printf("malloc(yuv_frame_size)\n");
        goto mainend;
    }


    ///2.2 初始化 音频编码器,这里为了后续的扩展,依然使用变量,而不是宏定义完成
    ret = audioencoder.InitAAC(pcm_avchannel_layout,AV_SAMPLE_FMT_FLTP,pcm_samplerate,audio_bit_rate);
    if(ret < 0 ){
        cout<<"audioencoder.InitH264 error pcm_avchannel_layout.nb_channels = " << pcm_avchannel_layout.nb_channels
           <<" pcm_sampleformat = "<< pcm_sampleformat
          <<" pcm_samplerate = " << pcm_samplerate
         <<" audio_bit_rate = " << audio_bit_rate
        <<endl;
        ret = -1;
        goto mainend;
    }

    ///2.2.1 分配pcm buf pcm_frame_size  = 单个采样点占用的字节 * 通道数量 * 每个通道有多少给采用点
    pcm_frame_size = av_get_bytes_per_sample((AVSampleFormat)pcm_sampleformat)
            *pcm_avchannel_layout.nb_channels * audioencoder.GetFrameSize();
    if(pcm_frame_size <= 0) {
        printf("pcm_frame_size <= 0\n");
        return -1;
    }
    pcm_frame_buf = (uint8_t *)malloc(pcm_frame_size);
    if(!pcm_frame_buf)
    {
        printf("malloc(pcm_frame_size)\n");
        ret = -1;
        goto mainend;
    }




    ///第三步:mp4初始化,包括新建流,open io,send header

    ///3.1 Init_Muxer方法 的核心是 avformat_alloc_output_context2方法,
    ret = mp4_muxer.Init_Muxer(out_mp4_name);
    if(ret <0){
        cout<<"main func error muxer.Init_Muxer(out_mp4_name) error out_mp4_name = "<< out_mp4_name << endl;
        goto mainend;
    }

    ///3.2 创建 video 的avstream,创建的video 的 avstream 会存储在 mp4_muxer 中的变量中
    ret = mp4_muxer.AddStream(videoencoder.GetCodecContext());
    if(ret < 0)
    {
        printf("mp4_muxer.AddStream video failed\n");
        goto mainend;
    }

    ///3.3 创建 audio 的avstream,创建的audio 的avstream 会存储在 mp4_muxer 中的变量中
    ret = mp4_muxer.AddStream(audioencoder.GetCodecContext());
    if(ret < 0)
    {
        printf("mp4_muxer.AddStream audio failed\n");
        return -1;
    }

    /// 3.4 核心方法是avio_open
    ret = mp4_muxer.open();
    if(ret < 0)
    {
        printf("mp4_muxer.Open failed\n");
        return -1;
    }
    /// 3.5 发送头部信息
    ret = mp4_muxer.sendHeader();

    if(ret < 0)
    {
        printf("mp4_muxer.SendHeader failed\n");
        return -1;
    }

    ///第四步中间,audio resample
    /// ///     int InitResampler(const AVChannelLayout *out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
    ///                       const AVChannelLayout *in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate);
    out_resample_ch_layout = mp4_muxer.getAudioAVCondecContext()->ch_layout;
    out_resample_sample_fmt = mp4_muxer.getAudioAVCondecContext()->sample_fmt;
    out_resample_sample_rate =  mp4_muxer.getAudioAVCondecContext()->sample_rate;

    in_resample_ch_layout = PCM_CHANNEL_LAYOUT;
    in_resample_sample_fmt = PCM_SAMPLE_FORMAT;
    in_resample_sample_rate = PCM_SAMPLE_RATE;
    ret =  audioresample.InitResampler(&out_resample_ch_layout,
                                       out_resample_sample_fmt,
                                       out_resample_sample_rate,
                                       &in_resample_ch_layout,
                                       in_resample_sample_fmt,
                                       in_resample_sample_rate);
    if(ret <0){
        cout<<"main func  audioresample.InitResampler error "<<endl;
    }


    ///第四步:在while循环中读取yuv,pcm进行编码然后发送给 mp4 muxer
    /// 4.1  时间戳相关
    ///  处理一帧aac avframe所花费的时间,一帧aac 的avframe中有1024个样本帧
    /// 也就是说,一次处理1024个样本帧,占用的时间,最后面乘以1000000,是将单位 秒 转化成 微秒
    audio_frame_duration = 1.0 * audioencoder.GetFrameSize() / pcm_samplerate * audiotimebase;

    /// 处理一帧h264 avframe所花费的时间为:一帧h264只有一张图片  假设1秒有25张图片,那么所占用的时间如下,最后面乘以1000000,是将单位 秒 转化成 微秒
    video_frame_duration = 1.0 / yuvfps * videotimebase;

    /// 4.2 video读完 或者 audio 读完 标志, 两者都读取完成的时候才真正的完成
    //    audio_finish = 1;
    //    int video_finish = 0;
    //    int read_size = 0;  //用于记录每次读取的数据大小


    while(1){
        if(audio_finish && video_finish){
            break;
        }

        //读取video,从 in_yuv_fd 文件中读取数据,读取的内容存储到 yuv_frame_buf 中,每次读取1个字节,每次读取的大小为 yuv_frame_size
        if(video_finish != 1){
            read_size = fread(yuv_frame_buf, 1, yuv_frame_size, in_yuv_fd);
            ///对于视频来说,每次读取的大小都是 一张图片的大小,返回值也应该是一张图片的大小,因此如果读取到的返回值不是一张图片的大小,则有问题,需要在这里判断
            ///那么会是什么问题呢?一种情况就是 read_size的大小可能是0,读取完成了,就是0了。那么有没有可能是半张图片呢?理论上没有的,
            ///我们这里简单的认为:当读取到值是 < yuv_frame_size的时候,就认为读取完毕了,
            if(read_size < yuv_frame_size){
                video_finish = 1;
            }


            if(video_finish != 1){
                ///这里 在encode方法中,发送一次frame可能会生成多个packet,因此要使用vector<AVPakcet *> &pakcets 记录所有的 avpakcet
//                avpacket =  videoencoder.Encode(yuv_frame_buf,read_size,mp4_muxer.getVideoIndex(),video_pts,videotimebase);

                ret =  videoencoder.Encode(yuv_frame_buf,read_size,mp4_muxer.getVideoIndex(),video_pts,videotimebase,packets);
            }else{
                //当最后一次的时候,video_finish 值会变成1,因此要刷新
//                avpacket = videoencoder.Encode(nullptr,0,mp4_muxer.getVideoIndex(),video_pts,videotimebase);

                cout<<"flush start"<<endl;
                ret = videoencoder.Encode(nullptr,0,mp4_muxer.getVideoIndex(),video_pts,videotimebase,packets);
                cout<<"flush end"<<endl;


            }
            if(ret < 0){
                cout<<"videoencoder encode func error"<<endl;
                return ret;
            }

            //每读取一次,都需要给video pts + 上一次读取图片的时间
            video_pts += video_frame_duration;


//            if(avpacket){
//                ret = mp4_muxer.sendPacket(avpacket);
//            }
            //这时候就拿到了packets了,然后将该数据发送出去
            for(int i =0;i<packets.size();++i){
                ret = mp4_muxer.sendPacket(packets[i]);
                if(ret < 0){
                    cout<<"mp4_muxer send packet error packets[i]"<< packets[i] << endl;
                    continue;//这里发送一次有error,直接return吗?还是cotinue比较好
                }
            }
//            avpacket = nullptr;
            packets.clear();
        }

        // 读取audio,
        if(audio_finish != 1){
            //            AVFrame * AudioResampler::AllocFltpPcmFrame(AVChannelLayout out_ch_layout, AVSampleFormat out_sample_fmt, int out_sample_rate,int out_nb_samples){
            //            out_resample_ch_layout = mp4_muxer.getAudioAVCondecContext()->ch_layout;
            //            out_resample_sample_fmt = mp4_muxer.getAudioAVCondecContext()->sample_fmt;
            //            out_resample_sample_rate =  mp4_muxer.getAudioAVCondecContext()->sample_rate;
            AVFrame * fltpavframe = audioresample.AllocFltpPcmFrame(out_resample_ch_layout,
                                                                    out_resample_sample_fmt,
                                                                    out_resample_sample_rate,
                                                                    audioencoder.GetFrameSize());
            //            int AudioResampler::resamples_alloc_inbuffer_and_outbuffer(uint8_t ***in_audio_data,
            //                                                       int *in_linesize,
            //                                                       uint8_t ***out_audio_data,
            //                                                       int *out_linesize);
            uint8_t **in_audio_data = nullptr;
            int in_linesize = 0;
            uint8_t **out_audio_data = nullptr;
            int out_linesize = 0;
            //            cout<<"before  resamples_alloc_inbuffer_and_outbuffer method aa  = " << in_audio_data[0]<<endl;
            //            cout<<"before  resamples_alloc_inbuffer_and_outbuffer method bb = " << out_audio_data[1]<<endl;

            ret = audioresample.resamples_alloc_inbuffer_and_outbuffer(&in_audio_data,
                                                                       &in_linesize,
                                                                       &out_audio_data,
                                                                       &out_linesize);

            if(ret <= 0 ){
                cout<<"main func  resamples_alloc_inbuffer_and_outbuffer error "<<endl;
            }

            int realnumber = fread(in_audio_data[0], 1, in_linesize, in_pcm_fd);
            cout<<"in_linesize = "<< in_linesize << " out_linesize = " << realnumber << "realnumber = " << realnumber << endl;
            cout<<"in_audio_data = "<< in_audio_data << endl;
            cout<<"out_audio_data = "<< out_audio_data << endl;

            if(realnumber <in_linesize){
                audio_finish = 1;
            }

            if(audio_finish != 1){
                int out_nb_number = 1024; //我们输出的每次要是1024个样本帧,这是 ffmpeg aac决定的。
                /// 输入源每个声道的样本个数 = 输入源采样率 * 输出源每个声道的样本数 / 输出源的采样率

                int in_nb_samplerate = av_rescale_rnd(audioresample._in_sample_rate,
                                                      out_nb_number,
                                                      audioresample._in_sample_rate,
                                                      AV_ROUND_UP);
                audioresample.swr_convert_and_fill_avframe(
                                        out_audio_data,
                                        audioencoder.GetFrameSize(),
                                        (const uint8_t **)in_audio_data,
                                        in_nb_samplerate,
                                        fltpavframe);
    //            audioresample.swr_convert_and_fill_avframe_changed(fltpavframe,
    //                                                               (const uint8_t **)in_audio_data,
    //                                                               in_nb_samplerate);//这个1024应该是计算出来的 indata中的nb_number

                //优化,使用vecotr<AVPacket *> & packets 记录
    //            avpacket =  audioencoder.Encode(fltpavframe,mp4_muxer.getAudioIndex(),audio_pts,audiotimebase);
                ret =  audioencoder.Encode(fltpavframe,mp4_muxer.getAudioIndex(),audio_pts,audiotimebase,packets);

            } else {
                ret =  audioencoder.Encode(nullptr,mp4_muxer.getAudioIndex(),audio_pts,audiotimebase,packets);
            }

            if(ret < 0 ){
                cout<<"audioencoder encode func error"<<endl;
                continue;
            }


            //每读取一次,都需要给video pts + 上一次读取图片的时间
            audio_pts += audio_frame_duration;

            //这时候就拿到了packet了,然后将该数据发送出去

//            if(avpacket){
//                ret = mp4_muxer.sendPacket(avpacket);
//            }

            for(int i =0;i<packets.size();++i){
                ret = mp4_muxer.sendPacket(packets[i]);

            }


            packets.clear();
            audioresample.FreeFltpavframe(fltpavframe);

        }
    }

    ret = mp4_muxer.sendTrailer();

mainend:
    cout<<"bbb"<<endl;
    //    if(avpacket){//这里不需要释放avpacket,因为在 前面的 mp4_muxer.sendPacket(avpacket); 方法中,已经释放了,但是并没有将 avpacket=nullptr,因此这里avpacket不为nullptr,但是内部已经没有东西了,重复释放会有问题
    //        av_packet_free(&avpacket);
    //    }
    if(yuv_frame_buf){
        free(yuv_frame_buf);
        yuv_frame_buf = nullptr;
    }
    if(pcm_frame_buf){
        free(pcm_frame_buf);
        pcm_frame_buf = nullptr;
    }

    if(in_yuv_fd){
        fclose(in_yuv_fd);
        in_yuv_fd = nullptr;
    }
    if(in_pcm_fd){
        fclose(in_pcm_fd);
        in_pcm_fd = nullptr;
    }

    return ret;



    //    cout<<"ccc"<<endl;//return之后的代码,是走不到这块的

}

4. 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YUVPCM数据编码成视频和音频文件需要使用FFmpeg库,具体的实现步骤如下: 1. 初始化FFmpeg库 在使用FFmpeg库之前,需要先进行初始化。使用av_register_all函数可以注册FFmpeg库中的所有编解码器、格式器和协议等。 ``` av_register_all(); ``` 2. 打开输出文件 使用avformat_alloc_output_context2和avio_open2函数打开输出文件,创建AVFormatContext结构体并分配内存,将输出文件与该结构体关联。 ``` AVFormatContext *out_ctx = NULL; int ret = avformat_alloc_output_context2(&out_ctx, NULL, NULL, output_file); if (ret < 0) { // 创建AVFormatContext失败 return; } if (!(out_ctx->oformat->flags & AVFMT_NOFILE)) { ret = avio_open2(&out_ctx->pb, output_file, AVIO_FLAG_WRITE, NULL, NULL); if (ret < 0) { // 打开输出文件失败 return; } } ``` 3. 创建音视频流 使用avformat_new_stream函数创建音视频流,并设置音视频流的相关参数,如编码器、帧率、码率、采样率等。 ``` AVStream *video_stream = avformat_new_stream(out_ctx, NULL); if (video_stream == NULL) { // 创建视频流失败 return; } AVCodecParameters *codecpar = video_stream->codecpar; codecpar->codec_type = AVMEDIA_TYPE_VIDEO; codecpar->width = width; codecpar->height = height; codecpar->format = AV_PIX_FMT_YUV420P; codecpar->codec_id = AV_CODEC_ID_H264; codecpar->bit_rate = bit_rate; codecpar->framerate = {fps, 1}; AVStream *audio_stream = avformat_new_stream(out_ctx, NULL); if (audio_stream == NULL) { // 创建音频流失败 return; } codecpar = audio_stream->codecpar; codecpar->codec_type = AVMEDIA_TYPE_AUDIO; codecpar->sample_rate = sample_rate; codecpar->format = AV_SAMPLE_FMT_S16; codecpar->channels = channels; codecpar->channel_layout = av_get_default_channel_layout(channels); codecpar->codec_id = AV_CODEC_ID_AAC; codecpar->bit_rate = bit_rate; ``` 4. 打开视频和音频编码器 使用avcodec_find_encoder函数查找视频和音频编码器,并使用avcodec_open2打开编码器。 ``` AVCodec *video_codec = avcodec_find_encoder(video_stream->codecpar->codec_id); if (video_codec == NULL) { // 查找视频编码器失败 return; } AVCodecContext *video_cctx = avcodec_alloc_context3(video_codec); if (video_cctx == NULL) { // 创建视频编码器上下文失败 return; } ret = avcodec_open2(video_cctx, video_codec, NULL); if (ret < 0) { // 打开视频编码器失败 return; } AVCodec *audio_codec = avcodec_find_encoder(audio_stream->codecpar->codec_id); if (audio_codec == NULL) { // 查找音频编码器失败 return; } AVCodecContext *audio_cctx = avcodec_alloc_context3(audio_codec); if (audio_cctx == NULL) { // 创建音频编码器上下文失败 return; } ret = avcodec_open2(audio_cctx, audio_codec, NULL); if (ret < 0) { // 打开音频编码器失败 return; } ``` 5. 写入视频和音频数据 使用av_frame_alloc函数创建AVFrame结构体,填充YUVPCM数据,并使用avcodec_send_frame和avcodec_receive_packet函数将数据编码成视频和音频包,最后使用av_write_frame函数将包写入输出文件。 ``` AVFrame *video_frame = av_frame_alloc(); // 填充YUV数据到video_frame中 AVPacket *video_packet = av_packet_alloc(); ret = avcodec_send_frame(video_cctx, video_frame); if (ret < 0) { // 向视频编码器发送数据失败 return; } while (ret >= 0) { ret = avcodec_receive_packet(video_cctx, video_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } if (ret < 0) { // 从视频编码器接收数据失败 return; } av_packet_rescale_ts(video_packet, video_cctx->time_base, video_stream->time_base); video_packet->stream_index = video_stream->index; ret = av_write_frame(out_ctx, video_packet); if (ret < 0) { // 写入视频数据失败 return; } } AVFrame *audio_frame = av_frame_alloc(); // 填充PCM数据到audio_frame中 AVPacket *audio_packet = av_packet_alloc(); ret = avcodec_send_frame(audio_cctx, audio_frame); if (ret < 0) { // 向音频编码器发送数据失败 return; } while (ret >= 0) { ret = avcodec_receive_packet(audio_cctx, audio_packet); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } if (ret < 0) { // 从音频编码器接收数据失败 return; } av_packet_rescale_ts(audio_packet, audio_cctx->time_base, audio_stream->time_base); audio_packet->stream_index = audio_stream->index; ret = av_write_frame(out_ctx, audio_packet); if (ret < 0) { // 写入音频数据失败 return; } } ``` 6. 关闭编码器和输出文件 使用av_write_trailer、avcodec_free_context和avformat_free_context函数释放资源并关闭编码器和输出文件。 ``` av_write_trailer(out_ctx); avcodec_free_context(&video_cctx); avcodec_free_context(&audio_cctx); avformat_free_context(out_ctx); ``` 以上是将YUVPCM数据编码成视频和音频文件的基本流程,需要注意的是各项参数的设置和数据的填充。如果需要进行更详细的配置和处理,可以参考FFmpeg库的官方文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值