diff --git a/src/speaker_capturer/macosx/speaker_capturer_macosx.mm b/src/speaker_capturer/macosx/speaker_capturer_macosx.mm index 1d91065..dda4730 100644 --- a/src/speaker_capturer/macosx/speaker_capturer_macosx.mm +++ b/src/speaker_capturer/macosx/speaker_capturer_macosx.mm @@ -2,11 +2,11 @@ #import #import +#include "rd_log.h" #include "speaker_capturer_macosx.h" -// 这个delegate用来接收SCStream回调 @interface SpeakerCaptureDelegate : NSObject -@property(nonatomic, assign) SpeakerCapturerMacosx* owner; // assign用于C++指针,不用weak +@property(nonatomic, assign) SpeakerCapturerMacosx* owner; - (instancetype)initWithOwner:(SpeakerCapturerMacosx*)owner; @end @@ -27,8 +27,6 @@ size_t length = CMBlockBufferGetDataLength(blockBuffer); char* dataPtr = NULL; CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, NULL, &dataPtr); - - // 获取输入格式 CMAudioFormatDescriptionRef formatDesc = CMSampleBufferGetFormatDescription(sampleBuffer); const AudioStreamBasicDescription* asbd = CMAudioFormatDescriptionGetStreamBasicDescription(formatDesc); @@ -46,7 +44,7 @@ if (v < -1.0f) v = -1.0f; pcm16[i] = (short)(v * 32767.0f); } - // 混合为单声道 + if (channels > 1) { int mono_samples = samples / channels; out_pcm16.resize(mono_samples); @@ -79,28 +77,47 @@ } } - // 分包,每960字节送一次cb_(即480采样点) size_t frame_bytes = 960; // 480 * 2 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"); } - // 如有剩余,可缓存到下次补齐 } } } @end -// C++类实现细节,放这里,隐藏在.mm中 class SpeakerCapturerMacosx::Impl { public: SCStreamConfiguration* config = nil; SCStream* stream = nil; SpeakerCaptureDelegate* delegate = nil; + dispatch_queue_t queue = nil; + SCShareableContent* content = nil; + SCDisplay* mainDisplay = nil; + + ~Impl() { + if (stream) { + [stream stopCaptureWithCompletionHandler:^(NSError* _Nullable error){ + }]; + stream = nil; + } + delegate = nil; + if (queue) { + queue = nil; + } + content = nil; + mainDisplay = nil; + config = nil; + } }; -SpeakerCapturerMacosx::SpeakerCapturerMacosx() { impl_ = new Impl(); } +SpeakerCapturerMacosx::SpeakerCapturerMacosx() { + impl_ = new Impl(); + inited_ = false; + cb_ = nullptr; +} SpeakerCapturerMacosx::~SpeakerCapturerMacosx() { Destroy(); @@ -109,7 +126,9 @@ SpeakerCapturerMacosx::~SpeakerCapturerMacosx() { } int SpeakerCapturerMacosx::Init(speaker_data_cb cb) { - if (inited_) return 0; + if (inited_) { + return 0; + } cb_ = cb; impl_->config = [[SCStreamConfiguration alloc] init]; @@ -117,94 +136,129 @@ int SpeakerCapturerMacosx::Init(speaker_data_cb cb) { impl_->config.sampleRate = 48000; impl_->config.channelCount = 1; - impl_->delegate = [[SpeakerCaptureDelegate alloc] initWithOwner:this]; - - // 异步获取可共享内容,改为同步等待 dispatch_semaphore_t sema = dispatch_semaphore_create(0); - __block SCShareableContent* content = nil; __block NSError* error = nil; [SCShareableContent getShareableContentWithCompletionHandler:^(SCShareableContent* c, NSError* e) { - content = c; + impl_->content = c; error = e; dispatch_semaphore_signal(sema); }]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); - if (error || !content) return -1; + if (error || !impl_->content) { + LOG_ERROR("Failed to get shareable content: {}", + std::string([error.localizedDescription UTF8String])); + return -1; + } - // 选择主显示屏 CGDirectDisplayID mainDisplayId = CGMainDisplayID(); - SCDisplay* mainDisplay = nil; - for (SCDisplay* d in content.displays) { + impl_->mainDisplay = nil; + for (SCDisplay* d in impl_->content.displays) { if (d.displayID == mainDisplayId) { - mainDisplay = d; + impl_->mainDisplay = d; break; } } - if (!mainDisplay) return -1; - - // 用SCContentFilter包装 - SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:mainDisplay - excludingWindows:@[]]; - - impl_->stream = [[SCStream alloc] initWithFilter:filter - configuration:impl_->config - delegate:impl_->delegate]; - - NSError* addOutputError = nil; - dispatch_queue_t queue = dispatch_queue_create("SpeakerAudio.Queue", DISPATCH_QUEUE_SERIAL); - BOOL ok = [impl_->stream addStreamOutput:impl_->delegate - type:SCStreamOutputTypeAudio - sampleHandlerQueue:queue - error:&addOutputError]; - if (!ok || addOutputError) { - NSLog(@"addStreamOutput error: %@", addOutputError); + if (!impl_->mainDisplay) { + LOG_ERROR("Main display not found"); return -1; } + if (!impl_->queue) { + impl_->queue = dispatch_queue_create("SpeakerAudio.Queue", DISPATCH_QUEUE_SERIAL); + } + inited_ = true; return 0; } int SpeakerCapturerMacosx::Start() { - if (!inited_) return -1; - dispatch_semaphore_t sema = dispatch_semaphore_create(0); + if (!inited_) { + return -1; + } + + if (impl_->stream) { + dispatch_semaphore_t semaStop = dispatch_semaphore_create(0); + [impl_->stream stopCaptureWithCompletionHandler:^(NSError* error) { + dispatch_semaphore_signal(semaStop); + }]; + dispatch_semaphore_wait(semaStop, DISPATCH_TIME_FOREVER); + impl_->stream = nil; + impl_->delegate = nil; + } + + impl_->delegate = [[SpeakerCaptureDelegate alloc] initWithOwner:this]; + SCContentFilter* filter = [[SCContentFilter alloc] initWithDisplay:impl_->mainDisplay + excludingWindows:@[]]; + impl_->stream = [[SCStream alloc] initWithFilter:filter + configuration:impl_->config + delegate:impl_->delegate]; + + NSError* addOutputError = nil; + BOOL ok = [impl_->stream addStreamOutput:impl_->delegate + type:SCStreamOutputTypeAudio + sampleHandlerQueue:impl_->queue + error:&addOutputError]; + if (!ok || addOutputError) { + LOG_ERROR("addStreamOutput error: {}", + std::string([addOutputError.localizedDescription UTF8String])); + impl_->stream = nil; + impl_->delegate = nil; + return -1; + } + + dispatch_semaphore_t semaStart = dispatch_semaphore_create(0); __block int ret = 0; [impl_->stream startCaptureWithCompletionHandler:^(NSError* _Nullable error) { - if (error) ret = -1; - dispatch_semaphore_signal(sema); + if (error) { + LOG_ERROR("startCaptureWithCompletionHandler error: {}", + std::string([error.localizedDescription UTF8String])); + ret = -1; + } + dispatch_semaphore_signal(semaStart); }]; - dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(semaStart, DISPATCH_TIME_FOREVER); + return ret; } int SpeakerCapturerMacosx::Stop() { if (!inited_) return -1; + if (!impl_->stream) return -1; + dispatch_semaphore_t sema = dispatch_semaphore_create(0); [impl_->stream stopCaptureWithCompletionHandler:^(NSError* error) { + if (error) { + LOG_ERROR("stopCaptureWithCompletionHandler error: {}", + std::string([error.localizedDescription UTF8String])); + } dispatch_semaphore_signal(sema); }]; dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); - inited_ = false; + + impl_->stream = nil; + impl_->delegate = nil; + return 0; } int SpeakerCapturerMacosx::Destroy() { Stop(); cb_ = nullptr; + if (impl_) { - impl_->delegate = nil; - impl_->stream = nil; impl_->config = nil; + impl_->content = nil; + impl_->mainDisplay = nil; + if (impl_->queue) { + impl_->queue = nil; + } } inited_ = false; return 0; } -int SpeakerCapturerMacosx::Pause() { - // ScreenCaptureKit无暂停接口,暂时无实现 - return 0; -} +int SpeakerCapturerMacosx::Pause() { return 0; } int SpeakerCapturerMacosx::Resume() { return Start(); }