[fix] fix restart audio capturer error on MacOSX

This commit is contained in:
dijunkun
2025-07-15 16:05:57 +08:00
parent 78c54136e2
commit 4bb5607702

View File

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