过滤器链接流程
+--------+
auto_aresample_0:default--[48000Hz flt:stereo]--input0| amix |default--[48000Hz flt:stereo]--auto_aresample_2:default
auto_aresample_1:default--[48000Hz flt:stereo]--input1| (amix) |
+--------+
+---------------+
aformat:default--[48000Hz s16:stereo]--default| sink |
| (abuffersink) |
+---------------+
+-----------+
| output |default--[48000Hz s16:stereo]--auto_aresample_0:default
| (abuffer) |
+-----------+
+-----------+
| output |default--[48000Hz s32:stereo]--auto_aresample_1:default
| (abuffer) |
+-----------+
+-----------+
auto_aresample_2:default--[48000Hz s16:stereo]--default| aformat |default--[48000Hz s16:stereo]--sink:default
| (aformat) |
+-----------+
+------------------+
output:default--[48000Hz s16:stereo]--default| auto_aresample_0 |default--[48000Hz flt:stereo]--amix:input0
| (aresample) |
+------------------+
+------------------+
output:default--[48000Hz s32:stereo]--default| auto_aresample_1 |default--[48000Hz flt:stereo]--amix:input1
| (aresample) |
+------------------+
+------------------+
amix:default--[48000Hz flt:stereo]--default| auto_aresample_2 |default--[48000Hz s16:stereo]--aformat:default
| (aresample) |
+------------------+
测试密令
ffmpeg -i FirstTest.mp3 -i SecondTest.mp3 -filter_complex amix=inputs=2:duration=longest:dropout_transition=3 out.mp3 -y
/**
* ffmpeg -i FirstTest.mp3 -i SecondTest.mp3 -filter_complex amix=inputs=2:duration=longest:dropout_transition=3 out.mp3 -y
* -filter_complex:这个参数表示使用一个复杂的过滤器。
* amix 是一个用于音频混合的过滤器。
* complex 表示我们将要应用一个比简单过滤器更复杂的操作
* inputs=2 指明我们有两个输入音频流需要进行混合。
* duration=longest:指定混合的时长策略
* longest 表示混合的持续时间将取两个音频文件中较长的那个。
* dropout_transition=3:这是一个过渡参数
* dropout_transition=3 则是指定在音频丢失时的过渡时间(单位为秒)。这里设置为3秒,表示在一个音频渐渐退场时,会持续这样的过渡时间。
* -y:这个参数表示自动覆盖输出文件而不提示。如果 out.mp3 已存在,FFmpeg 将直接用新的文件替换它。
*/
全局配置
extern "C"
{
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>#include "libavformat/avformat.h"#include "libavutil/time.h"
#include "libavutil/log.h"
#include "libavutil/avutil.h"
#include "libavutil/mem.h"
#include "libavutil/parseutils.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavutil/channel_layout.h"
#include "libavutil/samplefmt.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/mathematics.h"#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/avfilter.h"#include "libavcodec/avcodec.h"
#include "libavcodec/bsf.h"#include "libswscale/swscale.h"
#include "libswresample/swresample.h"#include <math.h>
}#include <iostream>
#include <string>
#include <memory>
#include <map>
#include <mutex> #define OUT_BUFFER_SIZE 2 * 2 * 1024// 跟格式匹配
// 一个 32 - sample - 4个字节 * 双通道 * 1024个采样点
#define FIRST_FRAME_SIZE 2 * 2 * 1024
#define SECOND_FRAME_SIZE 4 * 2 * 1024// ffplay -ar 48000 -ac 2 -f s16le output.pcm
#define FIRST_FILE "/home/king/ffmpeg_src/lessonCode/ZeroVoiceEducation/05_lesson/resource/48KHZ_S16_2CH.pcm"
/*** ffmpeg -i input.mp4 -vn -ar 48000 -ac 2 -f s32le 48KHZ_S32_2CH_PCM.pcm* ffplay -ar 48000 -ac 2 -f s32le 48KHZ_S32_2CH.pcm* */
#define SECOND_FILE "/home/king/ffmpeg_src/lessonCode/ZeroVoiceEducation/05_lesson/resource/48KHZ_S32_2CH.pcm"// ffplay -ar 48000 -ac 2 -f s16le output.pcm
#define OUT_FILE "output.pcm"
封装一个AudioMIxer
#include "Global.h"
using namespace std;struct AudioInfo
{uint32_t SampleRate; // 采样率uint32_t channels; // 声道数uint32_t PreSampleBitSize; // 单样本比特数AVSampleFormat format;string name;AVFilterContext *FilterCtx;
};class AudioMixer
{
public:AudioMixer();~AudioMixer();bool AddAudioInput(uint32_t index/*通道号*/, uint32_t SampleRate, uint32_t channels, uint32_t PreSampleBitSize, AVSampleFormat format);bool AddAudioOutput(uint32_t SampleRate, uint32_t channels, uint32_t PreSampleBitSize, AVSampleFormat format);bool Initialize(const char *duration = "longest");bool AddFrame(uint32_t index, uint8_t *InBuf, uint32_t size);int GetFrame(uint8_t *OutBuf, uint32_t MaxOutBufSize);void SetErrorString(int ret);private:string ErrorString_;bool IsInitialize_;mutex MutexLock_;map<uint32_t, AudioInfo> InputAudioInfo_;shared_ptr<AudioInfo> OutputAudioInfo_;shared_ptr<AudioInfo> MixAudioInfo_;shared_ptr<AudioInfo> SinkAudioInfo_;AVFilterGraph *FilterGraph_;
};
#include "AudioMixer.h"AudioMixer::AudioMixer()
{IsInitialize_ = false;OutputAudioInfo_ = nullptr;FilterGraph_ = nullptr;/*** 这一行将 MixAudioInfo_ 指向一个新的 AudioInfo 对象。* new AudioInfo 动态分配了一个 AudioInfo 类型的对象,并返回其指针。* reset 方法会将 MixAudioInfo_ 的现有指针(如果有的话)释放掉,确保没有内存泄漏,* 然后将其指向新的 AudioInfo 对象。**/MixAudioInfo_.reset(new AudioInfo);MixAudioInfo_->name = "amix"; // 混音使用SinkAudioInfo_.reset(new AudioInfo);SinkAudioInfo_->name = "sink"; // 输出
}AudioMixer::~AudioMixer()
{// 在构造时自动获取锁,在析构时自动释放锁。适用于函数或代码块内部,确保锁在作用域结束时正确释放。lock_guard<mutex> locker(MutexLock_);if (IsInitialize_ == true){for (auto iter : InputAudioInfo_){if (iter.second.FilterCtx != nullptr){avfilter_free(iter.second.FilterCtx);}}InputAudioInfo_.clear();if (OutputAudioInfo_ != nullptr && OutputAudioInfo_->FilterCtx != nullptr){avfilter_free(OutputAudioInfo_->FilterCtx);OutputAudioInfo_->FilterCtx = nullptr;}if (MixAudioInfo_->FilterCtx != nullptr){avfilter_free(MixAudioInfo_->FilterCtx);MixAudioInfo_->FilterCtx = nullptr;}if (SinkAudioInfo_->FilterCtx != nullptr){avfilter_free(SinkAudioInfo_->FilterCtx);SinkAudioInfo_->FilterCtx = nullptr;}avfilter_graph_free(&FilterGraph_);FilterGraph_ = nullptr;IsInitialize_ = false;av_log(NULL, AV_LOG_WARNING, "[~AudioMixer] end -- line:%d\n", __LINE__);}
}/** 单样本* S16 -- 16 bits* S32 -- 32 bits*/
bool AudioMixer::AddAudioInput(uint32_t index, uint32_t SampleRate, uint32_t channels, uint32_t PreSampleBitSize /*单样本*/, AVSampleFormat format)
{lock_guard<mutex> locker(MutexLock_);if (IsInitialize_ == true) // 初始化之前添加流{av_log(NULL, AV_LOG_ERROR, "[AudioMixer::AddAudioInput] IsInitialize_ == true | 初始化之前添加流 -- line:%d\n", __LINE__);return false;}// find 返回迭代器指向当前查找元素的位置否则返回map::end()位置if (InputAudioInfo_.find(index) != InputAudioInfo_.end()){ // 已经存在返回av_log(NULL, AV_LOG_ERROR, "[AudioMixer::AddAudioInput] This audio is already in the InputAudioInfo_ -- line:%d\n", __LINE__);return false;}// 初始化一个 input// FilterInfo 是 InputAudioInfo_[index] 的别名auto &FilterInfo = InputAudioInfo_[index];// 初始化音频相关的参数FilterInfo.SampleRate = SampleRate;FilterInfo.channels = channels;FilterInfo.PreSampleBitSize = PreSampleBitSize;FilterInfo.format = format;FilterInfo.name = string("input") + to_string(index);av_log(NULL, AV_LOG_INFO, "[AudioMixer::AddAudioInput] InputAudioInfo_[%d], SampleRate:%d, channels:%d, PreSampleBitSize:%d, format:%d, name:%s -- line:%d\n",index, FilterInfo.SampleRate, FilterInfo.channels, FilterInfo.PreSampleBitSize, FilterInfo.format, FilterInfo.name.c_str(), __LINE__);return true;
}bool AudioMixer::AddAudioOutput(uint32_t SampleRate, uint32_t channels, uint32_t PreSampleBitSize, AVSampleFormat format)
{lock_guard<mutex> locker(MutexLock_);if (IsInitialize_ == true) // 初始化之前添加流{av_log(NULL, AV_LOG_ERROR, "[AudioMixer::AddAudioOutput] IsInitialize_ == true | 初始化之前先添加输出流 -- line:%d\n", __LINE__);return false;}// 初始化输出相关的参数 只有一个输出OutputAudioInfo_.reset(new AudioInfo);OutputAudioInfo_->SampleRate = SampleRate;OutputAudioInfo_->channels = channels;OutputAudioInfo_->PreSampleBitSize = PreSampleBitSize;OutputAudioInfo_->format = format;OutputAudioInfo_->name = "output";av_log(NULL, AV_LOG_INFO, "[AudioMixer::AddAudioOutput] SampleRate:%d, channels:%d, PreSampleBitSize:%d, format:%d, name:%s -- line:%d\n",OutputAudioInfo_->SampleRate, OutputAudioInfo_->channels, OutputAudioInfo_->PreSampleBitSize, OutputAudioInfo_->format, OutputAudioInfo_->name.c_str(), __LINE__);return true;
}bool AudioMixer::Initialize(const char *duration)
{lock_guard<mutex> locker(MutexLock_);if (IsInitialize_ == true){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] IsInitialize_ == true -- line:%d\n", __LINE__);return false;}if (InputAudioInfo_.size() == 0){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] No audio input! -- line:%d\n", __LINE__);return false;}FilterGraph_ = avfilter_graph_alloc();if (FilterGraph_ == nullptr){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] FilterGraph_ == nullptr -- line:%d\n", __LINE__);return false;}// 初始化一个音频混合过滤器char args[512] = {0};// 在过滤器图中创建一个新的过滤器实例const AVFilter *MixAudioFilter = avfilter_get_by_name("amix"); // 混音MixAudioInfo_->FilterCtx = avfilter_graph_alloc_filter(FilterGraph_, MixAudioFilter, "amix");/*** inputs=输入流数量,duration= 决定流的结束* dropout_transition= 输入流结束时,容量重整时间 |(longest最长输出时间,shortest最短,first第一个输入持续的时间)**/snprintf(args, sizeof(args), "inputs=%ld:duration=%s:dropout_transition=0", InputAudioInfo_.size(), duration);int ret = avfilter_init_str(MixAudioInfo_->FilterCtx, args); // 用提供的参数初始化一个过滤器。if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] av filter init str MixAudioInfo_->FilterCtx error:%s! -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}// 初始化一个输出过滤器const AVFilter *AudioBufferSinkFilter = avfilter_get_by_name("abuffersink");SinkAudioInfo_->FilterCtx = avfilter_graph_alloc_filter(FilterGraph_, AudioBufferSinkFilter, "sink");ret = avfilter_init_str(SinkAudioInfo_->FilterCtx, nullptr);if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] av filter init str SinkAudioInfo_->FilterCtx error:%s! -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}// 初始化一个输入过滤器for (auto &iter : InputAudioInfo_){const AVFilter *AudioBufferFilter = avfilter_get_by_name("abuffer");snprintf(args, sizeof(args), "sample_rate=%d:sample_fmt=%s:channel_layout=0x%x",iter.second.SampleRate,av_get_sample_fmt_name(iter.second.format),av_get_default_channel_layout(iter.second.channels));av_log(NULL, AV_LOG_INFO, "[AudioMixer::Initialize] input:%d args:%s! -- line:%d\n", iter.first, args, __LINE__);if (FilterGraph_ == nullptr || AudioBufferFilter == nullptr || OutputAudioInfo_->name.c_str() == nullptr){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] av filter init str SinkAudioInfo_->FilterCtx error:%s! -- line:%d\n", ErrorString_.c_str(), __LINE__);}iter.second.FilterCtx = avfilter_graph_alloc_filter(FilterGraph_, AudioBufferFilter, OutputAudioInfo_->name.c_str());ret = avfilter_init_str(iter.second.FilterCtx, args);if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] av filter init str InputAudioInfo_[%d].FilterCtx error:%s! -- line:%d\n", iter.first, ErrorString_.c_str(), __LINE__);return false;}ret = avfilter_link(iter.second.FilterCtx, 0, MixAudioInfo_->FilterCtx, iter.first);if (ret != 0){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] InputAudioInfo_[%d].FilterCtx ---> MixAudioInfo_->FilterCtx error:%s -- line:%d\n", iter.first, ErrorString_.c_str(), __LINE__);return false;}}if (OutputAudioInfo_ != nullptr){const AVFilter *OutputAudioFormatFilter = avfilter_get_by_name("aformat");snprintf(args, sizeof(args), "sample_fmts=%s:sample_rates=%d:channel_layouts=0x%lx",av_get_sample_fmt_name(OutputAudioInfo_->format),OutputAudioInfo_->SampleRate,av_get_default_channel_layout(OutputAudioInfo_->channels));OutputAudioInfo_->FilterCtx = avfilter_graph_alloc_filter(FilterGraph_, OutputAudioFormatFilter, "aformat");ret = avfilter_init_str(OutputAudioInfo_->FilterCtx, args);if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] av filter init str OutputAudioInfo_->FilterCtx error:%s! -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}ret = avfilter_link(MixAudioInfo_->FilterCtx, 0, OutputAudioInfo_->FilterCtx, 0);if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] avfilter link MixAudioInfo_->FilterCtx, OutputAudioInfo_->FilterCtx error:%s! -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}ret = avfilter_link(OutputAudioInfo_->FilterCtx, 0, SinkAudioInfo_->FilterCtx, 0);if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] avfilter link OutputAudioInfo_->FilterCtx, SinkAudioInfo_->FilterCtx error:%s! -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}}ret = avfilter_graph_config(FilterGraph_, NULL);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::Initialize] avfilter graph config error:%s! -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}IsInitialize_ = true;string GraphString = avfilter_graph_dump(FilterGraph_, NULL);if (GraphString.c_str() != nullptr){FILE *GraphFile = fopen("GraphFile.txt", "w"); // 打印 filterfraph 的 具体情况if (GraphFile != nullptr){fwrite(GraphString.c_str(), 1, GraphString.size(), GraphFile);fclose(GraphFile);}}return true;
}bool AudioMixer::AddFrame(uint32_t index, uint8_t *InBuf, uint32_t size)
{lock_guard<mutex> locker(MutexLock_);if (IsInitialize_ == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::AddFrame] IsInitialize_ == false -- line:%d\n", __LINE__);return false;}auto iter = InputAudioInfo_.find(index);if (iter == InputAudioInfo_.end()){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::AddFrame] iter == InputAudioInfo_.end() -- line:%d\n", __LINE__);return false;}// AVFrame *frame = av_frame_alloc();if (InBuf != nullptr && size > 0){/*** 调用 av_frame_alloc() 分配一个 AVFrame 结构的内存,并返回指向该结构的指针。* 使用这个指针创建一个 shared_ptr,这样在 shared_ptr 对象生命周期结束时,会调用指定的 lambda 表达式作为删除器。* 删除器使用 av_frame_free() 释放 AVFrame 结构的内存,确保资源得到正确管理。**/shared_ptr<AVFrame> frame(av_frame_alloc(), [](AVFrame *ptr){ av_frame_free(&ptr); });frame->sample_rate = iter->second.SampleRate;frame->format = iter->second.format;frame->channel_layout = av_get_default_channel_layout(iter->second.channels);frame->nb_samples = size * 8 / iter->second.PreSampleBitSize / iter->second.channels;av_frame_get_buffer(frame.get(), 1);memcpy(frame->extended_data[0], InBuf, size);int ret = av_buffersrc_add_frame(iter->second.FilterCtx, frame.get());if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::AddFrame] av buffersrc add frame error:%s -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}}else{int ret = av_buffersrc_add_frame(iter->second.FilterCtx, nullptr);if (ret != 0){SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::AddFrame] av buffersrc add frame error:%s -- line:%d\n", ErrorString_.c_str(), __LINE__);return false;}}return true; // 所有操作成功
}int AudioMixer::GetFrame(uint8_t *OutBuf, uint32_t MaxOutBufSize)
{lock_guard<mutex> locker(MutexLock_);if (IsInitialize_ == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::GetFrame] IsInitialize_ == false -- line:%d\n", __LINE__);return -1;}shared_ptr<AVFrame> frame(av_frame_alloc(), [](AVFrame *ptr){ av_frame_free(&ptr); });int ret = av_buffersink_get_frame(SinkAudioInfo_->FilterCtx, frame.get());if (ret < 0){if (ret == -541478725 || ret == -11){SetErrorString(ret);av_log(NULL, AV_LOG_DEBUG, "[AudioMixer::GetFrame] buffersink get frame:%s -- line:%d\n", ErrorString_.c_str(), __LINE__);return -1;}SetErrorString(ret);av_log(NULL, AV_LOG_ERROR, "[AudioMixer::GetFrame] buffersink get frame error:%s -- line:%d\n", ErrorString_.c_str(), __LINE__);return -1;}int size = av_samples_get_buffer_size(NULL, frame->channels, frame->nb_samples, (AVSampleFormat)frame->format, 0);if (size > MaxOutBufSize){av_log(NULL, AV_LOG_ERROR, "[AudioMixer::GetFrame] get size > MaxOutBufSize -- line:%d\n", __LINE__);return -1;}memcpy(OutBuf, frame->extended_data[0], size);return size;
}void AudioMixer::SetErrorString(int ret)
{ErrorString_.clear();char errbuf[1024] = {0};av_strerror(ret, errbuf, sizeof(errbuf) - 1);ErrorString_ = errbuf;
}
封装manage
#include "Global.h"
#include "AudioMixer.h"using namespace std;class AudioMixManage
{
public:AudioMixManage();~AudioMixManage();// 添加输入输出源bool AddSources();// 初始化bool ManageInitialize();// 写文件bool WriteMixDocument();private:AudioMixer mixer_;FILE *FIRST_IN_AUDIO_;FILE *SECOND_IN_AUDIO_;FILE *OUT_AUDIO_;const char *FirstInFileName_;const char *SecondInFileName_;const char *OutFileName_;uint8_t OutBuffer_[OUT_BUFFER_SIZE]; // 读取帧size_t OutSize_;uint8_t FirstBuffer_[FIRST_FRAME_SIZE]; // 临时读写数据size_t FirstSize_;uint8_t SecondBuffer_[SECOND_FRAME_SIZE];// 临时读写数据size_t SecondSize_;bool FirstFinish_;bool SecondFinish_;
};
#include "MixManage.h"AudioMixManage::AudioMixManage()
{FIRST_IN_AUDIO_ = nullptr;SECOND_IN_AUDIO_ = nullptr;OUT_AUDIO_ = nullptr;FirstInFileName_ = FIRST_FILE;SecondInFileName_ = SECOND_FILE;OutFileName_ = OUT_FILE;OutSize_ = 0;FirstSize_ = -1;SecondSize_ = -1;memset(OutBuffer_, 0, OUT_BUFFER_SIZE);memset(FirstBuffer_, 0, FIRST_FRAME_SIZE);memset(SecondBuffer_, 0, SECOND_FRAME_SIZE);FirstFinish_ = false;SecondFinish_ = false;
}AudioMixManage::~AudioMixManage()
{if (OUT_AUDIO_ != nullptr){fclose(OUT_AUDIO_);}if (FIRST_IN_AUDIO_ != nullptr){fclose(FIRST_IN_AUDIO_);}if (SECOND_IN_AUDIO_ != nullptr){fclose(SECOND_IN_AUDIO_);}av_log(NULL, AV_LOG_WARNING, "[%s] end! -- line:%d\n", __FUNCTION__,__LINE__);
}bool AudioMixManage::AddSources()
{// 输入流bool ret = mixer_.AddAudioInput(0, 48000, 2, 16, AV_SAMPLE_FMT_S16);if (ret == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::AddSources] mixer_.AddAudioInput error! -- line:%d\n", __LINE__);return false;}ret = mixer_.AddAudioInput(1, 48000, 2, 32, AV_SAMPLE_FMT_S32);if (ret == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::AddSources] mixer_.AddAudioInput error! -- line:%d\n", __LINE__);return false;}// 输出流ret = mixer_.AddAudioOutput(48000, 2, 16, AV_SAMPLE_FMT_S16);if (ret == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::AddSources] mixer_.AddAudioOutput error! -- line:%d\n", __LINE__);return false;}return true;
}bool AudioMixManage::ManageInitialize()
{// 打开输入/输出文件FIRST_IN_AUDIO_ = fopen(FirstInFileName_, "rb");SECOND_IN_AUDIO_ = fopen(SecondInFileName_, "rb");OUT_AUDIO_ = fopen(OutFileName_, "wb");if (FIRST_IN_AUDIO_ == nullptr || SECOND_IN_AUDIO_ == nullptr || OUT_AUDIO_ == nullptr){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::ManageInitialize] open file error! -- line:%d\n", __LINE__);return false;}if (mixer_.Initialize("longest") < 0){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::ManageInitialize] open fil e error! -- line:%d\n", __LINE__);return false;}return true;
}bool AudioMixManage::WriteMixDocument()
{uint16_t ReadCount = 0;while ((FirstFinish_ == false || SecondFinish_ == false)){SecondSize_ = fread(SecondBuffer_, 1, SECOND_FRAME_SIZE, SECOND_IN_AUDIO_);FirstSize_ = fread(FirstBuffer_, 1, FIRST_FRAME_SIZE, FIRST_IN_AUDIO_);if (++ReadCount % 50 == 0){av_log(NULL, AV_LOG_INFO, "[AudioMixManage::WriteMixDocument] FirstSize:%ld, SecondSize:%ld ReadCount:%d -- line:%d\n", FirstSize_, SecondSize_, ReadCount, __LINE__);}if (FirstSize_ > 0){if (mixer_.AddFrame(0, FirstBuffer_, FirstSize_) == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::WriteMixDocument] mixer_.AddFrame error! -- line:%d\n", __LINE__);break;}}else{if (FirstFinish_ == false){FirstFinish_ = true;if (mixer_.AddFrame(0, nullptr, 0) == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::WriteMixDocument] mixer_.AddFrame error! -- line:%d\n", __LINE__);}}}if (SecondSize_ > 0){if (mixer_.AddFrame(1, SecondBuffer_, SecondSize_) == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::WriteMixDocument] mixer_.AddFrame error! -- line:%d\n", __LINE__);break;}}else{if (SecondFinish_ == false){SecondFinish_ = true;if (mixer_.AddFrame(1, nullptr, 0) == false){av_log(NULL, AV_LOG_ERROR, "[AudioMixManage::WriteMixDocument] mixer_.AddFrame error! -- line:%d\n", __LINE__);}}}int ret = 0;while ((ret = mixer_.GetFrame(OutBuffer_, OUT_BUFFER_SIZE)) >= 0){OutSize_ += ret;if ((OutSize_ % OUT_BUFFER_SIZE == 0) && (ReadCount % 25 == 0)){av_log(NULL, AV_LOG_INFO, "[AudioMixManage::WriteMixDocument] mix audio:%d, write sum to size_:%ld -- line:%d\n", ret, OutSize_, __LINE__);}fwrite(OutBuffer_, 1, ret, OUT_AUDIO_);}}return true;
}