From 193e4bd5bf40c86842ea26a606640a15ddf8e5dc Mon Sep 17 00:00:00 2001 From: dijunkun Date: Thu, 28 May 2026 04:22:07 +0800 Subject: [PATCH] [fix] guard macOS speaker capture callbacks against invalid buffers --- src/gui/render.cpp | 14 +++-- .../macosx/speaker_capturer_macosx.mm | 52 ++++++++++++++++--- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 01d2c95f..b7086935 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -764,11 +764,16 @@ int Render::StartSpeakerCapturer() { } if (speaker_capturer_) { - speaker_capturer_->Start(); + const int ret = speaker_capturer_->Start(); + if (ret != 0) { + LOG_ERROR("Start speaker capturer failed: {}", ret); + return ret; + } start_speaker_capturer_ = true; + return 0; } - return 0; + return -1; } int Render::StopSpeakerCapturer() { @@ -1149,8 +1154,9 @@ void Render::UpdateInteractions() { } if (start_speaker_capturer_ && !speaker_capturer_is_started_) { - StartSpeakerCapturer(); - speaker_capturer_is_started_ = true; + if (0 == StartSpeakerCapturer()) { + speaker_capturer_is_started_ = true; + } } else if (!start_speaker_capturer_ && speaker_capturer_is_started_) { StopSpeakerCapturer(); speaker_capturer_is_started_ = false; diff --git a/src/speaker_capturer/macosx/speaker_capturer_macosx.mm b/src/speaker_capturer/macosx/speaker_capturer_macosx.mm index c40db756..5cea7d85 100644 --- a/src/speaker_capturer/macosx/speaker_capturer_macosx.mm +++ b/src/speaker_capturer/macosx/speaker_capturer_macosx.mm @@ -9,6 +9,17 @@ namespace crossdesk { class SpeakerCapturerMacosx; } +namespace { +std::string NSErrorToString(NSError* error) { + if (!error) { + return ""; + } + + const char* description = [error.localizedDescription UTF8String]; + return description ? description : ""; +} +} // namespace + @interface SpeakerCaptureDelegate : NSObject @property(nonatomic, assign) crossdesk::SpeakerCapturerMacosx* owner; - (instancetype)initWithOwner:(crossdesk::SpeakerCapturerMacosx*)owner; @@ -28,15 +39,36 @@ class SpeakerCapturerMacosx; ofType:(SCStreamOutputType)type { if (type != SCStreamOutputTypeAudio) return; + crossdesk::SpeakerCapturerMacosx* owner = _owner; + if (!owner || !owner->cb_) { + return; + } + CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer); + if (!blockBuffer) { + return; + } + size_t length = CMBlockBufferGetDataLength(blockBuffer); char* dataPtr = NULL; - CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, NULL, &dataPtr); + OSStatus dataStatus = + CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, NULL, &dataPtr); + if (dataStatus != noErr || dataPtr == nullptr || length == 0) { + return; + } + CMAudioFormatDescriptionRef formatDesc = CMSampleBufferGetFormatDescription(sampleBuffer); + if (!formatDesc) { + return; + } + const AudioStreamBasicDescription* asbd = CMAudioFormatDescriptionGetStreamBasicDescription(formatDesc); + if (!asbd || asbd->mChannelsPerFrame == 0) { + return; + } - if (_owner->cb_ && dataPtr && length > 0 && asbd) { + if (owner->cb_) { std::vector out_pcm16; if (asbd->mFormatFlags & kAudioFormatFlagIsFloat) { int channels = asbd->mChannelsPerFrame; @@ -86,7 +118,10 @@ class SpeakerCapturerMacosx; size_t total_bytes = out_pcm16.size() * sizeof(short); unsigned char* p = (unsigned char*)out_pcm16.data(); for (size_t offset = 0; offset + frame_bytes <= total_bytes; offset += frame_bytes) { - _owner->cb_(p + offset, frame_bytes, "audio"); + if (!owner->cb_) { + return; + } + owner->cb_(p + offset, frame_bytes, "audio"); } } } @@ -155,7 +190,7 @@ int SpeakerCapturerMacosx::Init(speaker_data_cb cb) { if (error || !impl_->content) { LOG_ERROR("Failed to get shareable content: {}", - std::string([error.localizedDescription UTF8String])); + NSErrorToString(error)); return -1; } @@ -209,7 +244,7 @@ int SpeakerCapturerMacosx::Start() { error:&addOutputError]; if (!ok || addOutputError) { LOG_ERROR("addStreamOutput error: {}", - std::string([addOutputError.localizedDescription UTF8String])); + NSErrorToString(addOutputError)); impl_->stream = nil; impl_->delegate = nil; return -1; @@ -220,7 +255,7 @@ int SpeakerCapturerMacosx::Start() { [impl_->stream startCaptureWithCompletionHandler:^(NSError* _Nullable error) { if (error) { LOG_ERROR("startCaptureWithCompletionHandler error: {}", - std::string([error.localizedDescription UTF8String])); + NSErrorToString(error)); ret = -1; } dispatch_semaphore_signal(semaStart); @@ -238,13 +273,14 @@ int SpeakerCapturerMacosx::Stop() { [impl_->stream stopCaptureWithCompletionHandler:^(NSError* error) { if (error) { LOG_ERROR("stopCaptureWithCompletionHandler error: {}", - std::string([error.localizedDescription UTF8String])); + NSErrorToString(error)); } dispatch_semaphore_signal(sema); }]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); impl_->stream = nil; + impl_->delegate.owner = nullptr; impl_->delegate = nil; return 0; @@ -269,4 +305,4 @@ int SpeakerCapturerMacosx::Destroy() { int SpeakerCapturerMacosx::Pause() { return 0; } int SpeakerCapturerMacosx::Resume() { return Start(); } -} // namespace crossdesk \ No newline at end of file +} // namespace crossdesk