15 - FFmpeg 音频混音(过滤器)

news/2024/9/18 11:53:27/ 标签: 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;
}


http://www.ppmy.cn/news/1517086.html

相关文章

Linux 数据结构 顺序表 链表

数据结构&#xff1a; 1.衡量一个程序是否优秀&#xff1a; 1.时间复杂度&#xff1a; 数据量增长与程序运行时间的比例关系以函数描述称为时间渐进复杂度函数,简称时间复杂度 O(c) > O(logn) > O(n) > O(nlogn) > O(n^2) > O(n^3) > O…

一个prolog最简单推理示例

假设现在知道一些年轻人&#xff0c;谁喜欢谁&#xff0c;定义为love(x, y)&#xff1b; 定义了一些这样的关系&#xff1b; 如果x喜欢y&#xff0c;y也喜欢x&#xff0c;则定义他们是一对情侣&#xff1b; 规则表示为&#xff1a; lovers(X,Y) :- love(X,Y), love(Y,X). 输入…

UniApp中的Flex布局技巧

随着移动互联网的迅速发展&#xff0c;越来越多的开发者开始使用跨平台技术来开发应用程序。而在跨平台开发里&#xff0c;uniapp是一种非常受欢迎的框架&#xff0c;由于使用uniapp可以快速地开发出同时支持多个平台的应用程序。在uniapp开发中&#xff0c;flex布局是一种非常…

【C++】异常 详解

目录 异常的引出与简介 异常的使用 异常逻辑图解 异常继承体系 异常的重新抛出 异常安全 异常规范 结语 异常的引出与简介 我们可以回忆一下&#xff0c;在C语言时期&#xff0c;我们返回错误的方式只有两个 一个是assert强制返回错误&#xff0c;还有一个就是返回错误…

第三十一章:docker如何部署Nexus

docker如何部署Nexus 目标 掌握 Nexus docker compose安装安装Docker和Docker Compose:确保您的系统已安装Docker和Docker Compose。如果尚未安装,可以参考Docker官方文档进行安装12。 创建Docker Compose文件:在您选择的目录下创建一个名为docker-compose.yml的新文件,并…

GraphRAG论文解读

欢迎一起讨论 论文地址综述介绍部分核心翻译翻译解释重要的信息元素和实体的关系&#xff08;包含和被包含&#xff0c;而非相等&#xff09;Graph Index&#xff08;图索引&#xff09;Community Detection&#xff08;社区检测&#xff09;Query-Focused Summarization&#…

Android studio 升级问题记录-个性化配置迁移

前言 在本次折腾Android studio更新的过程中&#xff0c;遇到了很多问题&#xff0c;包括&#xff1a; 选择直接覆盖更新的话&#xff0c;会发现样式还是老样式旧项目运行不通过&#xff0c;并且会生成一些异常的build无法再支持代码自动补全功能本地没commit的代码丢失&…

【经验】linux下cuda的更换

linux下cuda的更换 查看当前cuda和cudnn的版本 nvcc -Vcudnn版本 cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2下载对应版本的cuda 查看驱动版本535.54.03 下载对应的cuda版本 版本查看https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.htm…

pytest参数化多种用法总结

pytest.mark.parametrize 是 pytest 的一个核心功能&#xff0c;它允许你参数化测试函数&#xff0c;这样你就可以使用不同的参数运行同一个测试函数多次。以下是 pytest.mark.parametrize 的详细用法总结&#xff1a; 基本用法 parametrize 装饰器可以接受一个或多个参数名&…

驱动:mknod-misc 杂项自动

一、杂项设备驱动 #include <linux/init.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/module.h> #include <linux/device.h> #include <asm/io.h> #include <asm/string.h> #include <asm/uaccess.h>…

TinaSDKV2.0 自定义系统开发

TinaSDKV2.0 自定义系统开发 什么是自定义系统&#xff1f; TinaSDK Kconfig界面配置 Tina Linux采用 Kconfig 机制对 SDK 和内核进行配置。 Kconfig 是一种固定格式的配置文件。Linux 编译环境中的 menuconfig 程序可以识别这种格式的配置文件&#xff0c;并提取出有效信息…

电脑丢失dll文件一键修复之dll确实损坏影响电脑运行

在使用电脑过程中&#xff0c;DLL文件丢失或损坏是一个常见的问题&#xff0c;它可能导致程序无法正常运行&#xff0c;甚至影响整个系统的稳定性。本文将详细介绍如何一键修复丢失的DLL文件&#xff0c;探讨常见的DLL丢失报错原因&#xff0c;并提供详细的修复步骤和预防措施。…

【mysql】mysql之数据操作语言(insert、delete、update)

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

《机器学习》—— OpenCV 对图片的各种操作(均值、方框、高斯、中值滤波处理)

文章目录 1、对有椒盐噪声的图片进行均值、方框、高斯、中值滤波处理2、给图像边缘增加边框3、对图片进行阈值化操作 1、对有椒盐噪声的图片进行均值、方框、高斯、中值滤波处理 均值滤波 cv2.blur是 OpenCV 库中的一个函数&#xff0c;用于对图像进行均值模糊处理。这个函数通…

幂等性简介

幂等性&#xff08;Idempotence&#xff09;是计算机科学中的一个重要概念&#xff0c;特别是在分布式系统和网络服务中。幂等性操作的特点是&#xff0c;无论执行多少次&#xff0c;结果都是相同的。换句话说&#xff0c;幂等性操作在多次执行后&#xff0c;对系统的状态不会产…

pycharm中opencv-python和opencv-contrib安装

1.去到https://pypi.org/中查找opencv-python 和opencv-contrib-python 2.分别下载。 3.下载完后&#xff0c;打开pycharm&#xff0c;然后新建一个项目&#xff0c;设置项目配置环境为当前python环境&#xff0c; 4.打开pycharm提供的控制台&#xff0c;使用pip install 安装文…

Datawhale第五期夏令营-CV竞赛

CV竞赛 0.赛事报名租用4090 1.开始运行下载文件提交结果 2.内容解释赛题背景赛题目标社会价值评分规则baseline精读代码什么是YOLO 主要代码内容精读使用Ultraalytics运行代码 0.赛事报名 赛事官网:https://www.marsbigdata.com/competition/details?id3839107548872 租用40…

密码强度验证——js基础积累

//密码强度等级 getPwdLevel:function (pwd,minLength8) {var level 0;if (pwd.length < minLength) return level;if (/\d/.test(pwd)) level; //数字if (/[a-z]/.test(pwd)) level; //小写if (/[A-Z]/.test(pwd)) level; //大写if (/\W/.test(pwd)) level; //特殊字符ret…

第四章:照相机模型与增强现实

目录 1 针孔照相机模型 1.1 照相机矩阵 1.2 三维点的投影 1.3 照相机矩阵的分解 1.4 计算照相机中心 2 照相机标定 3 以平面和标记物进行姿态估计 4 增强现实 4.1 PyGame和PyOpenGL 4.2 从照相机矩阵到OpenGL格式 4.3 在图像中放置物体 1 针孔照相机模型 针孔照相机…

MYSQL集群技术

---------------第一部分---------------------- 一.mysql源码部署 环境&#xff1a;rhel7.9 1.1.下载安装包 官网&#xff1a;http://www.mysql.com 1.2.在linux下部署mysql 1.创建登录用户和数据目录&#xff0c;并给数据目录赋权&#xff0c;因为配置文件读取需要权限&…