一、 如何实现?
二、完整代码
三、使用示例
音频的播放通常是单例的,在进程内通常一个播放设备只能打开一次,且只支持一个写入。写播放器的时候,会遇到需要实现多开同时播放多个视频,比如视频剪辑工具的多轨道播放。这个时候就需要采用一定的方法,比如音频混合。本文将提供一种基于ffmpeg和sdl实现的音频多路混合的方法。
为实现多路共用一个播放设备,只能采用SDL的回调式打开音频设备。
回调方法
static std::map> _audioCallbacks;//记录每一路音频的回调方法
//总的回调方法
static void _audio_callback_sum(void* userdata, uint8_t* stream, int len)
{
memset(stream, 0, len);
std::unique_lock< std::mutex >lck(_mtx);
//调用每一路的回调方法
for (auto i : _audioCallbacks)
{
i.second(userdata, stream, len);
}
}
打开音频
static SDL_AudioSpec _wanted_spec, _spec;
_wanted_spec.channels = 2;
_wanted_spec.freq = 44100;
_wanted_spec.format = AUDIO_F32SYS;
_wanted_spec.silence = 0;
_wanted_spec.samples = 1024;
_wanted_spec.callback = _audio_callback_sum;
SDL_OpenAudio(&_wanted_spec, &_spec) == 0;
注册一路音频回调示例如下
_audioCallbacks[pFrame] = [&](void* userdata, Uint8* stream, int len) {
//此处实现单独一路的播放逻辑
};
每一路音频都创建一个音频队列,将解码的音频数据写入队列,下面是使用AVAudioFifo作为音频队列的例子。
if (av_audio_fifo_space(fifo) >= samples)
{
av_audio_fifo_write(fifo, (void**)&data, (int)samples);
break;
}
在音频单独的回调中即上面_audioCallbacks注册的方法中,进行数据混合,示例如下。
//读取音频队列数据
int read = av_audio_fifo_read(fifo, (void**)&data, _spec.samples);
//开始混合
SDL_MixAudioFormat(stream, data, _spec.format, newLen, SDL_MIX_MAXVOLUME);
simple_audio_play.h
#pragma once
///
/// 播放音频
/// 此方法同步阻塞,播放完成后返回
/// 支持多路播放,可放在线程中播放,线程安全。
///
/// 输入url
/// 中断标记
///
int simple_audio_play(const char* input, int* exitFlag)
int exitflag= false;
simple_audio_play("D:\\test_music.wav", &exitflag);
#include"simple_audio_play.h"
void main() {
int exitflag = false;
auto t1 = new std::thread([&]() {
while (!exitflag) {
simple_audio_play("D:\\test_music.wav", &exitflag);
}
});
auto t2 = new std::thread([&]() {
while (!exitflag) {
simple_audio_play("D:\\test.mp4", &exitflag);
}
});
system("pause");
exitflag = true;
t1->join();
t2->join();
}
本文福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs),有需要的可以进企鹅裙927239107领取哦~