文章写得不多、尽量只写干货
简要
CFNotificationCenterGetDarwinNotifyCenter,是CoreFundation中的一个类,可以实现进程间的通知,将通知从扩展App发送到主App中。
而之前文章说的屏幕共享,使用了Broadcast Upload Extension,而这就是扩展App,其负责采集和传输数据,本章将讲述数据传输的简单实现。
SampleHandler
SampleHandler中有诸多方法都是获取各种信息的、我们需要把信息传给主App:
- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
[self sendNotificationWithIdentifier:@"broadcastStartedWithSetupInfo" userInfo:setupInfo];
}
- (void)broadcastPaused {
// User has requested to pause the broadcast. Samples will stop being delivered.
[self sendNotificationWithIdentifier:@"broadcastPaused" userInfo:nil];
}
- (void)broadcastResumed {
// User has requested to resume the broadcast. Samples delivery will resume.
[self sendNotificationWithIdentifier:@"broadcastResumed" userInfo:nil];
}
- (void)broadcastFinished {
// User has requested to finish the broadcast.
[self sendNotificationWithIdentifier:@"broadcastFinished" userInfo:nil];
}
- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
switch (sampleBufferType) {
case RPSampleBufferTypeVideo:{
// Handle video sample buffer
//如果有网速不好的情况、请酌情丢弃数据、不要阻塞线程导致程序崩溃
NSDictionary * info = [[NSDictionary alloc] initWithObjectsAndKeys:[self dataWithSampBuffer:sampleBuffer],@"buffer", nil];
[self sendNotificationWithIdentifier:@"processSampleBuffer" userInfo:info];
}
break;
case RPSampleBufferTypeAudioApp:
// Handle audio sample buffer for app audio
break;
case RPSampleBufferTypeAudioMic:
// Handle audio sample buffer for mic audio
break;
default:
break;
}
}
identifier为通知标识,为区分哪个方法发出的通知。
传输数据流
CMSampleBufferRef不能直接传输,那么我们把它转换成NSData:
- (NSData *)dataWithSampBuffer:(CMSampleBufferRef)sampBuffer { CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampBuffer);
CVPixelBufferLockBaseAddress(imageBuffer,0);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
void *src_buff = CVPixelBufferGetBaseAddress(imageBuffer);
NSData *data = [NSData dataWithBytes:src_buff length:bytesPerRow * height];
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
return data;
}
发送通知
- (void)sendNotificationWithIdentifier:(nullable NSString *)identifier userInfo:(NSDictionary *)info {
CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();
CFDictionaryRef userInfo = (__bridge CFDictionaryRef)info;
BOOL const deliverImmediately = YES;
CFStringRef identifierRef = (__bridge CFStringRef)identifier;
CFNotificationCenterPostNotification(center, identifierRef, NULL, userInfo, deliverImmediately);
}
主App
接收通知~首先需要:
注册通知
identifier需要与发送端保持一致哟~~
- (void)addNotifications {
[self registerNotificationsWithIdentifier:@"broadcastStartedWithSetupInfo"];
[self registerNotificationsWithIdentifier:@"broadcastPaused"];
[self registerNotificationsWithIdentifier:@"broadcastResumed"];
[self registerNotificationsWithIdentifier:@"broadcastFinished"];
[self registerNotificationsWithIdentifier:@"processSampleBuffer"];
//这里同时注册了分发消息的通知,在宿主App中使用
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(NotificationAction:) name:NotificationName object:nil];
}
- (void)registerNotificationsWithIdentifier:(nullable NSString *)identifier{
CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();
CFStringRef str = (__bridge CFStringRef)identifier;
CFNotificationCenterAddObserver(center,
(__bridge const void *)(self),
NotificationCallback,
str,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);
}
其中NotificationCallback为接到通知后使用此方法取值。
void NotificationCallback(CFNotificationCenterRef center,
void * observer,
CFStringRef name,
void const * object,
CFDictionaryRef userInfo) {
NSString *identifier = (__bridge NSString *)name;
NSObject *sender = (__bridge NSObject *)observer;
//NSDictionary *info = (__bridge NSDictionary *)userInfo;
// NSDictionary *info = CFBridgingRelease(userInfo);
NSDictionary *notiUserInfo = @{@"identifier":identifier};
[[NSNotificationCenter defaultCenter] postNotificationName:NotificationName
object:sender
userInfo:notiUserInfo];
}
结果输出
- (void)NotificationAction:(NSNotification *)noti {
NSDictionary *userInfo = noti.userInfo;
NSString *identifier = userInfo[@"identifier"];
if ([identifier isEqualToString:@"broadcastStartedWithSetupInfo"]) {
NSLog(@"broadcastStartedWithSetupInfo");
}
if ([identifier isEqualToString:@"broadcastPaused"]) {
NSLog(@"broadcastPaused");
}
if ([identifier isEqualToString:@"broadcastResumed"]) {
NSLog(@"broadcastResumed");
}
if ([identifier isEqualToString:@"broadcastFinished"]) {
NSLog(@"broadcastFinished");
}
if ([identifier isEqualToString:@"processSampleBuffer"]) {
NSLog(@"processSampleBuffer");
}
}
测试
这时你会发现你接到了扩展中发出的通知,通过输出测试突然发现:传的数据流哪去了?为什么没有输出?好奇怪,是不是扩展没有传过来?
调试
运行扩展进行断点输出:
断点输出
呃、事实证明我们确实传输了数据、那么数据究竟哪去了?
调研
怀着官网肯定不会骗我的心态开始了官网的"扫描"
CFNotificationCenterPostNotification:
CFNotificationCenterAddObserver:
并没有userInfo的接收参数
所以:进程级通知不允许传输数据?!!!(咱也不确定、只能在此请求大佬给予指点🙏)
App Groups:数据共享
App Groups详情
此处直接使用代码完成了,详情请看上行链接
SampleHandler.m
- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
switch (sampleBufferType) {
case RPSampleBufferTypeVideo:{
// Handle video sample buffer
//如果有网速不好的情况、请酌情丢弃数据、不要阻塞线程导致程序崩溃
[self saveNSDataWithFileManager:[self dataWithSampBuffer:sampleBuffer]];
}
break;
case RPSampleBufferTypeAudioApp:
// Handle audio sample buffer for app audio
break;
case RPSampleBufferTypeAudioMic:
// Handle audio sample buffer for mic audio
break;
default:
break;
}
}
- (void)saveNSDataWithFileManager:(NSData*)data{
NSError *err = nil;
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.tongxun_xiaolang"];
containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/data"];
NSString *dataString = [[NSString alloc] initWithData:data encoding:kCFStringEncodingUTF8];
BOOL result = [dataString writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&err];
if (result) {
//已经修改完sendNotificationWithIdentifier方法(不传userInfo)
[self sendNotificationWithIdentifier:@"processSampleBuffer"];
}
}
主App
-(void)getDataWithFileManager{
NSError *err = nil;
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.tongxun_xiaolang"];
containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/data"];
NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:&err];
NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
if (err != nil) {
NSLog(@"xxxx %@",err);
return;
}
NSLog(@"----%@",data);
}
且NotificationAction方法中:
if ([identifier isEqualToString:@"processSampleBuffer"]) {
[self getDataWithFileManager];
}
测试
主App已经能拿到NSData(非SampleBuffer)明天添加转化
但运行一下之后扩展突然崩溃了、原因:内存不足、扩展中不能大于50M数据、需要进行编解码处理~ 明天继续
ps:没那么简单、、、在多种操作合并时卡住了😢、修复中、、、
网友评论