2021-10-11 17:16:26 +08:00
|
|
|
|
/*
|
2023-12-09 16:23:51 +08:00
|
|
|
|
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
|
2021-06-29 17:44:35 +08:00
|
|
|
|
*
|
|
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
|
|
|
|
|
*
|
2023-12-09 16:23:51 +08:00
|
|
|
|
* Use of this source code is governed by MIT-like license that can be found in the
|
2021-06-29 17:44:35 +08:00
|
|
|
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|
|
|
|
|
* may be found in the AUTHORS file in the root of the source tree.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "Util/logger.h"
|
|
|
|
|
|
#include "AudioSRC.h"
|
|
|
|
|
|
#include "SDLAudioDevice.h"
|
|
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
using namespace toolkit;
|
|
|
|
|
|
|
|
|
|
|
|
INSTANCE_IMP(SDLAudioDevice);
|
|
|
|
|
|
|
|
|
|
|
|
SDLAudioDevice::~SDLAudioDevice() {
|
2026-07-06 21:05:02 +08:00
|
|
|
|
if (_init_thread.joinable()) {
|
|
|
|
|
|
_init_thread.join();
|
|
|
|
|
|
}
|
|
|
|
|
|
auto dev = _device.load();
|
|
|
|
|
|
if (dev) {
|
|
|
|
|
|
SDL_CloseAudioDevice(dev);
|
2025-09-30 11:05:01 +08:00
|
|
|
|
_device = 0;
|
|
|
|
|
|
}
|
2021-06-29 17:44:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SDLAudioDevice::SDLAudioDevice() {
|
2026-07-06 21:05:02 +08:00
|
|
|
|
// 将 SDL_OpenAudioDevice 放到后台线程异步执行, 防止主线程(SDL事件循环)和 event poller 线程阻塞
|
|
|
|
|
|
// 在Windows下没声卡/设备异常时,SDL_OpenAudioDevice()会阻塞
|
|
|
|
|
|
// 在 Linux(PulseAudio/ALSA) 和 macOS(CoreAudio) 上通常秒回,几乎无开销
|
|
|
|
|
|
_init_thread = std::thread([this]() {
|
|
|
|
|
|
SDL_AudioSpec wanted_spec;
|
|
|
|
|
|
wanted_spec.freq = DEFAULT_SAMPLERATE;
|
|
|
|
|
|
wanted_spec.format = DEFAULT_FORMAT;
|
|
|
|
|
|
wanted_spec.channels = DEFAULT_CHANNEL;
|
|
|
|
|
|
wanted_spec.silence = 0;
|
|
|
|
|
|
wanted_spec.samples = DEFAULT_SAMPLES;
|
|
|
|
|
|
wanted_spec.userdata = this;
|
|
|
|
|
|
wanted_spec.callback = [](void *userdata, Uint8 *stream, int len) {
|
|
|
|
|
|
SDLAudioDevice *_this = (SDLAudioDevice *)userdata;
|
|
|
|
|
|
_this->onReqPCM((char *)stream, len);
|
|
|
|
|
|
};
|
2025-09-30 11:05:01 +08:00
|
|
|
|
|
2026-07-06 21:05:02 +08:00
|
|
|
|
SDL_AudioSpec audio_config;
|
|
|
|
|
|
auto dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_config, 0);
|
|
|
|
|
|
if (dev <= 0)
|
|
|
|
|
|
dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &audio_config, SDL_AUDIO_ALLOW_ANY_CHANGE);
|
|
|
|
|
|
if (dev <= 0) {
|
|
|
|
|
|
WarnL << "SDL_OpenAudioDevice failed, audio output disabled: " << SDL_GetError();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2021-06-29 17:44:35 +08:00
|
|
|
|
|
2026-07-06 21:05:02 +08:00
|
|
|
|
InfoL << "actual audioSpec, " << "freq:" << audio_config.freq
|
|
|
|
|
|
<< ", format:" << hex << audio_config.format << dec
|
|
|
|
|
|
<< ", channels:" << (int)audio_config.channels
|
|
|
|
|
|
<< ", samples:" << audio_config.samples
|
|
|
|
|
|
<< ", pcm size:" << audio_config.size;
|
2021-06-29 17:44:35 +08:00
|
|
|
|
|
2026-07-06 21:05:02 +08:00
|
|
|
|
// 先设置 _audio_config 和 _play_buf,再设置 _device (release)
|
|
|
|
|
|
// 这样 addChannel 读到 _device != 0 时,_audio_config 和 _play_buf 已就绪
|
|
|
|
|
|
_audio_config = audio_config;
|
|
|
|
|
|
_play_buf.reset(new char[audio_config.size], [](char *ptr) {
|
|
|
|
|
|
delete[] ptr;
|
|
|
|
|
|
});
|
|
|
|
|
|
_device.store(dev, std::memory_order_release);
|
2021-06-29 17:44:35 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SDLAudioDevice::addChannel(AudioSRC *chn) {
|
2026-07-06 21:05:02 +08:00
|
|
|
|
auto dev = _device.load(std::memory_order_acquire);
|
|
|
|
|
|
if (dev == 0) {
|
|
|
|
|
|
throw std::runtime_error("SDL audio device not available (not ready or no audio device)");
|
|
|
|
|
|
}
|
2021-06-29 17:44:35 +08:00
|
|
|
|
lock_guard<recursive_mutex> lck(_channel_mtx);
|
|
|
|
|
|
if (_channels.empty()) {
|
2026-07-06 21:05:02 +08:00
|
|
|
|
SDL_PauseAudioDevice(dev, false);
|
2021-06-29 17:44:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
chn->setOutputAudioConfig(_audio_config);
|
|
|
|
|
|
_channels.emplace(chn);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SDLAudioDevice::delChannel(AudioSRC *chn) {
|
|
|
|
|
|
lock_guard<recursive_mutex> lck(_channel_mtx);
|
|
|
|
|
|
_channels.erase(chn);
|
2026-07-06 21:05:02 +08:00
|
|
|
|
auto dev = _device.load();
|
|
|
|
|
|
if (_channels.empty() && dev) {
|
|
|
|
|
|
SDL_PauseAudioDevice(dev, true);
|
2021-06-29 17:44:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SDLAudioDevice::onReqPCM(char *stream, int len) {
|
|
|
|
|
|
lock_guard<recursive_mutex> lck(_channel_mtx);
|
|
|
|
|
|
int size;
|
|
|
|
|
|
int channel = 0;
|
|
|
|
|
|
for (auto &chn : _channels) {
|
|
|
|
|
|
if (channel == 0) {
|
|
|
|
|
|
size = chn->getPCMData(_play_buf.get(), len);
|
|
|
|
|
|
if (size) {
|
|
|
|
|
|
memcpy(stream, _play_buf.get(), size);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
size = chn->getPCMData(_play_buf.get(), len);
|
|
|
|
|
|
if (size) {
|
|
|
|
|
|
SDL_MixAudio((Uint8 *) stream, (Uint8 *) _play_buf.get(), size, SDL_MIX_MAXVOLUME);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (size) {
|
|
|
|
|
|
channel++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!channel) {
|
|
|
|
|
|
memset(stream, 0, len);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|