diff --git a/player/SDLAudioDevice.cpp b/player/SDLAudioDevice.cpp index 709c95e4..19490be8 100644 --- a/player/SDLAudioDevice.cpp +++ b/player/SDLAudioDevice.cpp @@ -18,47 +18,66 @@ using namespace toolkit; INSTANCE_IMP(SDLAudioDevice); SDLAudioDevice::~SDLAudioDevice() { - if (_device) { - SDL_CloseAudioDevice(_device); + if (_init_thread.joinable()) { + _init_thread.join(); + } + auto dev = _device.load(); + if (dev) { + SDL_CloseAudioDevice(dev); _device = 0; } } SDLAudioDevice::SDLAudioDevice() { - 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); - }; + // 将 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); + }; - _device = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &_audio_config, 0); - if (_device <= 0) - _device = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &_audio_config, SDL_AUDIO_ALLOW_ANY_CHANGE); - if (_device <= 0) { - throw std::runtime_error("SDL_OpenAudioDevice failed"); - } + 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; + } - 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; + 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; - _play_buf.reset(new char[_audio_config.size], [](char *ptr) { - delete[] ptr; + // 先设置 _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); }); } void SDLAudioDevice::addChannel(AudioSRC *chn) { + 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)"); + } lock_guard lck(_channel_mtx); if (_channels.empty()) { - SDL_PauseAudioDevice(_device, false); + SDL_PauseAudioDevice(dev, false); } chn->setOutputAudioConfig(_audio_config); _channels.emplace(chn); @@ -67,8 +86,9 @@ void SDLAudioDevice::addChannel(AudioSRC *chn) { void SDLAudioDevice::delChannel(AudioSRC *chn) { lock_guard lck(_channel_mtx); _channels.erase(chn); - if (_channels.empty()) { - SDL_PauseAudioDevice(_device, true); + auto dev = _device.load(); + if (_channels.empty() && dev) { + SDL_PauseAudioDevice(dev, true); } } diff --git a/player/SDLAudioDevice.h b/player/SDLAudioDevice.h index 69320995..87721add 100644 --- a/player/SDLAudioDevice.h +++ b/player/SDLAudioDevice.h @@ -13,6 +13,8 @@ #include #include +#include +#include #include #include @@ -40,11 +42,12 @@ private: void onReqPCM(char *stream, int len); private: - SDL_AudioDeviceID _device; + std::atomic _device{ 0 }; std::shared_ptr _play_buf; SDL_AudioSpec _audio_config; std::recursive_mutex _channel_mtx; std::unordered_set _channels; + std::thread _init_thread; }; #endif /* SDLAUDIOMIXER_SDLAUDIODEVICE_H_ */ diff --git a/player/test_player.cpp b/player/test_player.cpp index 9904dfd5..ad48f38e 100644 --- a/player/test_player.cpp +++ b/player/test_player.cpp @@ -93,37 +93,52 @@ int main(int argc, char *argv[]) { } if (audioTrack) { - auto decoder = std::make_shared(audioTrack); - auto audio_player = std::make_shared(); - // FFmpeg解码时已经统一转换为16位整型pcm - audio_player->setup(audioTrack->getAudioSampleRate(), audioTrack->getAudioChannel(), AUDIO_S16); - FFmpegSwr::Ptr swr; + try { + auto decoder = std::make_shared(audioTrack); + auto audio_player = std::make_shared(); + // FFmpeg解码时已经统一转换为16位整型pcm + audio_player->setup(audioTrack->getAudioSampleRate(), audioTrack->getAudioChannel(), AUDIO_S16); + FFmpegSwr::Ptr swr; - decoder->setOnDecode([audio_player, swr](const FFmpegFrame::Ptr &frame) mutable { - if (!swr) { -# if LIBAVCODEC_VERSION_INT >= FF_CODEC_VER_7_1 - swr = std::make_shared(AV_SAMPLE_FMT_S16, &(frame->get()->ch_layout), frame->get()->sample_rate); + decoder->setOnDecode([audio_player, swr](const FFmpegFrame::Ptr &frame) mutable { + if (!swr) { +#if LIBAVCODEC_VERSION_INT >= FF_CODEC_VER_7_1 + swr = std::make_shared(AV_SAMPLE_FMT_S16, &(frame->get()->ch_layout), frame->get()->sample_rate); #else - swr = std::make_shared(AV_SAMPLE_FMT_S16, frame->get()->channels, frame->get()->channel_layout, frame->get()->sample_rate); + swr = std::make_shared(AV_SAMPLE_FMT_S16, frame->get()->channels, frame->get()->channel_layout, frame->get()->sample_rate); #endif - } - auto pcm = swr->inputFrame(frame); - auto len = pcm->get()->nb_samples * pcm->getChannels() * av_get_bytes_per_sample((enum AVSampleFormat)pcm->get()->format); - audio_player->playPCM((const char *)(pcm->get()->data[0]), MIN(len, frame->get()->linesize[0])); - }); - audioTrack->addDelegate([decoder](const Frame::Ptr &frame) { return decoder->inputFrame(frame, false, true); }); + } + auto pcm = swr->inputFrame(frame); + auto len = pcm->get()->nb_samples * pcm->getChannels() * av_get_bytes_per_sample((enum AVSampleFormat)pcm->get()->format); + audio_player->playPCM((const char *)(pcm->get()->data[0]), MIN(len, frame->get()->linesize[0])); + }); + audioTrack->addDelegate([decoder](const Frame::Ptr &frame) { return decoder->inputFrame(frame, false, true); }); + } catch (const std::exception &ex) { + WarnL << "audio output disabled: " << ex.what(); + } } }); player->setOnShutdown([](const SockException &ex) { WarnL << "play shutdown: " << ex.what(); }); // 不等待track ready再回调播放成功事件,这样可以加快秒开速度 (*player)[Client::kWaitTrackReady] = false; + (*player)[Client::kPlayTrack] = 0; if (argc > 2) { (*player)[Client::kRtpType] = atoi(argv[2]); } if (argc > 3) { (*player)[Client::kPlayTrack] = atoi(argv[3]); } + + // 提前触发 SDL 音频设备异步初始化(后台线程) + // 某些平台在无音频设备时 SDL_OpenAudioDevice 会长时间阻塞 + // (如 Windows WASAPI 约 16 秒),提前启动确保 play 回调时已就绪 + try { + SDLAudioDevice::Instance(); + } catch (const std::exception &ex) { + WarnL << "SDL audio device pre-init failed: " << ex.what(); + } + player->play(argv[1]); SDLDisplayerHelper::Instance().runLoop(); }