15 - FFmpeg 音频混音(过滤器)
过滤器链接流程
+--------+
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;
}