Przeglądaj źródła

feat(iOS): screensharing extension swift implementation

j8
Alex Bumbu 4 lat temu
rodzic
commit
9bffe149d3

+ 73
- 65
ios/app/app.xcodeproj/project.pbxproj Wyświetl plik

@@ -23,12 +23,13 @@
23 23
 		13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
24 24
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
25 25
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
26
-		4E51B75E25E4115F0038575A /* DarwinNotificationCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B75D25E4115F0038575A /* DarwinNotificationCenter.m */; };
27
-		4EC49BB725BEDAC100E76218 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC49B8625BED71300E76218 /* ReplayKit.framework */; };
28
-		4EC49BBB25BEDAC100E76218 /* SampleHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC49BBA25BEDAC100E76218 /* SampleHandler.m */; };
29
-		4EC49BBF25BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
30
-		4EC49BCB25BEDB6400E76218 /* SocketConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC49BCA25BEDB6400E76218 /* SocketConnection.m */; };
31
-		4EC49BD125BF19CF00E76218 /* SampleUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EC49BD025BF19CF00E76218 /* SampleUploader.m */; };
26
+		4E90F9402632D1AB001102D4 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E90F93F2632D1AB001102D4 /* Atomic.swift */; };
27
+		4EB06024260E026600F524C5 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC49B8625BED71300E76218 /* ReplayKit.framework */; };
28
+		4EB06027260E026600F524C5 /* SampleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06026260E026600F524C5 /* SampleHandler.swift */; };
29
+		4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
30
+		4EB0603C260E09D000F524C5 /* SocketConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB06039260E09D000F524C5 /* SocketConnection.swift */; };
31
+		4EB0603D260E09D000F524C5 /* DarwinNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */; };
32
+		4EB0603E260E09D000F524C5 /* SampleUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EB0603B260E09D000F524C5 /* SampleUploader.swift */; };
32 33
 		55BEDABDA92D47D399A70A5E /* libPods-JitsiMeet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D878B07B3FBD6E305EAA6B27 /* libPods-JitsiMeet.a */; };
33 34
 		DE050389256E904600DEE3A5 /* WebRTC.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DE050388256E904600DEE3A5 /* WebRTC.xcframework */; };
34 35
 		DE05038A256E904600DEE3A5 /* WebRTC.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DE050388256E904600DEE3A5 /* WebRTC.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -54,11 +55,11 @@
54 55
 			remoteGlobalIDString = 0BEA5C241F7B8F73000D0AB4;
55 56
 			remoteInfo = JitsiMeetCompanion;
56 57
 		};
57
-		4EC49BBD25BEDAC100E76218 /* PBXContainerItemProxy */ = {
58
+		4EB06029260E026600F524C5 /* PBXContainerItemProxy */ = {
58 59
 			isa = PBXContainerItemProxy;
59 60
 			containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
60 61
 			proxyType = 1;
61
-			remoteGlobalIDString = 4EC49BB525BEDAC100E76218;
62
+			remoteGlobalIDString = 4EB06022260E026600F524C5;
62 63
 			remoteInfo = "JitsiMeetBroadcast Extension";
63 64
 		};
64 65
 /* End PBXContainerItemProxy section */
@@ -104,7 +105,7 @@
104 105
 			dstPath = "";
105 106
 			dstSubfolderSpec = 13;
106 107
 			files = (
107
-				4EC49BBF25BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex in Embed App Extensions */,
108
+				4EB0602B260E026600F524C5 /* JitsiMeetBroadcastExtension.appex in Embed App Extensions */,
108 109
 			);
109 110
 			name = "Embed App Extensions";
110 111
 			runOnlyForDeploymentPostprocessing = 0;
@@ -139,19 +140,18 @@
139 140
 		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
140 141
 		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
141 142
 		4670A512A688E2DC34528282 /* Pods-jitsi-meet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-jitsi-meet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-jitsi-meet/Pods-jitsi-meet.debug.xcconfig"; sourceTree = "<group>"; };
142
-		4E51B75C25E4115F0038575A /* DarwinNotificationCenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DarwinNotificationCenter.h; sourceTree = "<group>"; };
143
-		4E51B75D25E4115F0038575A /* DarwinNotificationCenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DarwinNotificationCenter.m; sourceTree = "<group>"; };
143
+		4E90F93F2632D1AB001102D4 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
144
+		4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = JitsiMeetBroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
145
+		4EB06026260E026600F524C5 /* SampleHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleHandler.swift; sourceTree = "<group>"; };
146
+		4EB06028260E026600F524C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
147
+		4EB06032260E08CC00F524C5 /* extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = extension.entitlements; sourceTree = "<group>"; };
148
+		4EB06039260E09D000F524C5 /* SocketConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketConnection.swift; sourceTree = "<group>"; };
149
+		4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarwinNotificationCenter.swift; sourceTree = "<group>"; };
150
+		4EB0603B260E09D000F524C5 /* SampleUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleUploader.swift; sourceTree = "<group>"; };
144 151
 		4EC49B8625BED71300E76218 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
145
-		4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "JitsiMeetBroadcast Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
146
-		4EC49BB925BEDAC100E76218 /* SampleHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleHandler.h; sourceTree = "<group>"; };
147
-		4EC49BBA25BEDAC100E76218 /* SampleHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleHandler.m; sourceTree = "<group>"; };
148
-		4EC49BBC25BEDAC100E76218 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
149
-		4EC49BC925BEDB6400E76218 /* SocketConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SocketConnection.h; sourceTree = "<group>"; };
150
-		4EC49BCA25BEDB6400E76218 /* SocketConnection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SocketConnection.m; sourceTree = "<group>"; };
151
-		4EC49BCF25BF19CF00E76218 /* SampleUploader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleUploader.h; sourceTree = "<group>"; };
152
-		4EC49BD025BF19CF00E76218 /* SampleUploader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleUploader.m; sourceTree = "<group>"; };
153
-		4EC49BDB25BF280A00E76218 /* extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = extension.entitlements; sourceTree = "<group>"; };
152
+		5FEF9D87A4D2A38AD7193308 /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet-JitsiMeetBroadcastExtension/Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig"; sourceTree = "<group>"; };
154 153
 		609CB2080B75F75A89923F3D /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
154
+		A7B2827E068A0E05260054AC /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet-JitsiMeetBroadcastExtension/Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig"; sourceTree = "<group>"; };
155 155
 		B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
156 156
 		D878B07B3FBD6E305EAA6B27 /* libPods-JitsiMeet.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JitsiMeet.a"; sourceTree = BUILT_PRODUCTS_DIR; };
157 157
 		DE050388256E904600DEE3A5 /* WebRTC.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WebRTC.xcframework; path = "../../node_modules/react-native-webrtc/apple/WebRTC.xcframework"; sourceTree = "<group>"; };
@@ -189,11 +189,11 @@
189 189
 			);
190 190
 			runOnlyForDeploymentPostprocessing = 0;
191 191
 		};
192
-		4EC49BB325BEDAC100E76218 /* Frameworks */ = {
192
+		4EB06020260E026600F524C5 /* Frameworks */ = {
193 193
 			isa = PBXFrameworksBuildPhase;
194 194
 			buildActionMask = 2147483647;
195 195
 			files = (
196
-				4EC49BB725BEDAC100E76218 /* ReplayKit.framework in Frameworks */,
196
+				4EB06024260E026600F524C5 /* ReplayKit.framework in Frameworks */,
197 197
 			);
198 198
 			runOnlyForDeploymentPostprocessing = 0;
199 199
 		};
@@ -261,23 +261,22 @@
261 261
 			path = src;
262 262
 			sourceTree = "<group>";
263 263
 		};
264
-		4EC49BB825BEDAC100E76218 /* JitsiMeetBroadcast Extension */ = {
264
+		4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */ = {
265 265
 			isa = PBXGroup;
266 266
 			children = (
267
-				4EC49BDB25BF280A00E76218 /* extension.entitlements */,
268
-				4EC49BB925BEDAC100E76218 /* SampleHandler.h */,
269
-				4EC49BBA25BEDAC100E76218 /* SampleHandler.m */,
270
-				4EC49BC925BEDB6400E76218 /* SocketConnection.h */,
271
-				4EC49BCA25BEDB6400E76218 /* SocketConnection.m */,
272
-				4EC49BCF25BF19CF00E76218 /* SampleUploader.h */,
273
-				4EC49BD025BF19CF00E76218 /* SampleUploader.m */,
274
-				4EC49BBC25BEDAC100E76218 /* Info.plist */,
275
-				4E51B75C25E4115F0038575A /* DarwinNotificationCenter.h */,
276
-				4E51B75D25E4115F0038575A /* DarwinNotificationCenter.m */,
277
-			);
267
+				4EB06032260E08CC00F524C5 /* extension.entitlements */,
268
+				4EB06026260E026600F524C5 /* SampleHandler.swift */,
269
+				4EB0603B260E09D000F524C5 /* SampleUploader.swift */,
270
+				4EB06039260E09D000F524C5 /* SocketConnection.swift */,
271
+				4EB0603A260E09D000F524C5 /* DarwinNotificationCenter.swift */,
272
+				4E90F93F2632D1AB001102D4 /* Atomic.swift */,
273
+				4EB06028260E026600F524C5 /* Info.plist */,
274
+			);
275
+			indentWidth = 4;
278 276
 			name = "JitsiMeetBroadcast Extension";
279 277
 			path = "broadcast-extension";
280 278
 			sourceTree = "<group>";
279
+			tabWidth = 4;
281 280
 		};
282 281
 		5E96ADD5E49F3B3822EF9A52 /* Pods */ = {
283 282
 			isa = PBXGroup;
@@ -286,6 +285,8 @@
286 285
 				09AA3B93E4CC62D84B424690 /* Pods-jitsi-meet.release.xcconfig */,
287 286
 				609CB2080B75F75A89923F3D /* Pods-JitsiMeet.debug.xcconfig */,
288 287
 				FC040BBED70876444D89E91C /* Pods-JitsiMeet.release.xcconfig */,
288
+				A7B2827E068A0E05260054AC /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.debug.xcconfig */,
289
+				5FEF9D87A4D2A38AD7193308 /* Pods-JitsiMeet-JitsiMeetBroadcastExtension.release.xcconfig */,
289 290
 			);
290 291
 			name = Pods;
291 292
 			sourceTree = "<group>";
@@ -299,8 +300,8 @@
299 300
 				13B07FAE1A68108700A75B9A /* src */,
300 301
 				5E96ADD5E49F3B3822EF9A52 /* Pods */,
301 302
 				0BEA5C261F7B8F73000D0AB4 /* Watch app */,
302
-				4EC49BB825BEDAC100E76218 /* JitsiMeetBroadcast Extension */,
303 303
 				0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
304
+				4EB06025260E026600F524C5 /* JitsiMeetBroadcast Extension */,
304 305
 			);
305 306
 			indentWidth = 2;
306 307
 			sourceTree = "<group>";
@@ -312,7 +313,7 @@
312 313
 				13B07F961A680F5B00A75B9A /* jitsi-meet.app */,
313 314
 				0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
314 315
 				0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
315
-				4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */,
316
+				4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */,
316 317
 			);
317 318
 			name = Products;
318 319
 			sourceTree = "<group>";
@@ -376,28 +377,28 @@
376 377
 			);
377 378
 			dependencies = (
378 379
 				0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
379
-				4EC49BBE25BEDAC100E76218 /* PBXTargetDependency */,
380
+				4EB0602A260E026600F524C5 /* PBXTargetDependency */,
380 381
 			);
381 382
 			name = JitsiMeet;
382 383
 			productName = "Jitsi Meet";
383 384
 			productReference = 13B07F961A680F5B00A75B9A /* jitsi-meet.app */;
384 385
 			productType = "com.apple.product-type.application";
385 386
 		};
386
-		4EC49BB525BEDAC100E76218 /* JitsiMeetBroadcast Extension */ = {
387
+		4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */ = {
387 388
 			isa = PBXNativeTarget;
388
-			buildConfigurationList = 4EC49BC025BEDAC100E76218 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcast Extension" */;
389
+			buildConfigurationList = 4EB0602C260E026700F524C5 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcastExtension" */;
389 390
 			buildPhases = (
390
-				4EC49BB225BEDAC100E76218 /* Sources */,
391
-				4EC49BB325BEDAC100E76218 /* Frameworks */,
392
-				4EC49BB425BEDAC100E76218 /* Resources */,
391
+				4EB0601F260E026600F524C5 /* Sources */,
392
+				4EB06020260E026600F524C5 /* Frameworks */,
393
+				4EB06021260E026600F524C5 /* Resources */,
393 394
 			);
394 395
 			buildRules = (
395 396
 			);
396 397
 			dependencies = (
397 398
 			);
398
-			name = "JitsiMeetBroadcast Extension";
399
+			name = JitsiMeetBroadcastExtension;
399 400
 			productName = "JitsiMeetBroadcast Extension";
400
-			productReference = 4EC49BB625BEDAC100E76218 /* JitsiMeetBroadcast Extension.appex */;
401
+			productReference = 4EB06023260E026600F524C5 /* JitsiMeetBroadcastExtension.appex */;
401 402
 			productType = "com.apple.product-type.app-extension";
402 403
 		};
403 404
 /* End PBXNativeTarget section */
@@ -406,6 +407,7 @@
406 407
 		83CBB9F71A601CBA00E9B192 /* Project object */ = {
407 408
 			isa = PBXProject;
408 409
 			attributes = {
410
+				LastSwiftUpdateCheck = 1240;
409 411
 				LastUpgradeCheck = 1020;
410 412
 				ORGANIZATIONNAME = Facebook;
411 413
 				TargetAttributes = {
@@ -429,8 +431,8 @@
429 431
 							};
430 432
 						};
431 433
 					};
432
-					4EC49BB525BEDAC100E76218 = {
433
-						CreatedOnToolsVersion = 12.2;
434
+					4EB06022260E026600F524C5 = {
435
+						CreatedOnToolsVersion = 12.4;
434 436
 					};
435 437
 				};
436 438
 			};
@@ -450,7 +452,7 @@
450 452
 				13B07F861A680F5B00A75B9A /* JitsiMeet */,
451 453
 				0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
452 454
 				0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
453
-				4EC49BB525BEDAC100E76218 /* JitsiMeetBroadcast Extension */,
455
+				4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */,
454 456
 			);
455 457
 		};
456 458
 /* End PBXProject section */
@@ -483,7 +485,7 @@
483 485
 			);
484 486
 			runOnlyForDeploymentPostprocessing = 0;
485 487
 		};
486
-		4EC49BB425BEDAC100E76218 /* Resources */ = {
488
+		4EB06021260E026600F524C5 /* Resources */ = {
487 489
 			isa = PBXResourcesBuildPhase;
488 490
 			buildActionMask = 2147483647;
489 491
 			files = (
@@ -625,14 +627,15 @@
625 627
 			);
626 628
 			runOnlyForDeploymentPostprocessing = 0;
627 629
 		};
628
-		4EC49BB225BEDAC100E76218 /* Sources */ = {
630
+		4EB0601F260E026600F524C5 /* Sources */ = {
629 631
 			isa = PBXSourcesBuildPhase;
630 632
 			buildActionMask = 2147483647;
631 633
 			files = (
632
-				4EC49BCB25BEDB6400E76218 /* SocketConnection.m in Sources */,
633
-				4EC49BBB25BEDAC100E76218 /* SampleHandler.m in Sources */,
634
-				4E51B75E25E4115F0038575A /* DarwinNotificationCenter.m in Sources */,
635
-				4EC49BD125BF19CF00E76218 /* SampleUploader.m in Sources */,
634
+				4EB0603C260E09D000F524C5 /* SocketConnection.swift in Sources */,
635
+				4EB0603E260E09D000F524C5 /* SampleUploader.swift in Sources */,
636
+				4EB0603D260E09D000F524C5 /* DarwinNotificationCenter.swift in Sources */,
637
+				4EB06027260E026600F524C5 /* SampleHandler.swift in Sources */,
638
+				4E90F9402632D1AB001102D4 /* Atomic.swift in Sources */,
636 639
 			);
637 640
 			runOnlyForDeploymentPostprocessing = 0;
638 641
 		};
@@ -649,10 +652,10 @@
649 652
 			target = 0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */;
650 653
 			targetProxy = 0BEA5C3F1F7B8F73000D0AB4 /* PBXContainerItemProxy */;
651 654
 		};
652
-		4EC49BBE25BEDAC100E76218 /* PBXTargetDependency */ = {
655
+		4EB0602A260E026600F524C5 /* PBXTargetDependency */ = {
653 656
 			isa = PBXTargetDependency;
654
-			target = 4EC49BB525BEDAC100E76218 /* JitsiMeetBroadcast Extension */;
655
-			targetProxy = 4EC49BBD25BEDAC100E76218 /* PBXContainerItemProxy */;
657
+			target = 4EB06022260E026600F524C5 /* JitsiMeetBroadcastExtension */;
658
+			targetProxy = 4EB06029260E026600F524C5 /* PBXContainerItemProxy */;
656 659
 		};
657 660
 /* End PBXTargetDependency section */
658 661
 
@@ -879,7 +882,7 @@
879 882
 			};
880 883
 			name = Release;
881 884
 		};
882
-		4EC49BC125BEDAC100E76218 /* Debug */ = {
885
+		4EB0602D260E026700F524C5 /* Debug */ = {
883 886
 			isa = XCBuildConfiguration;
884 887
 			buildSettings = {
885 888
 				CLANG_ANALYZER_NONNULL = YES;
@@ -890,13 +893,13 @@
890 893
 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
891 894
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
892 895
 				CODE_SIGN_ENTITLEMENTS = "broadcast-extension/extension.entitlements";
893
-				CODE_SIGN_IDENTITY = "iPhone Developer";
896
+				CODE_SIGN_IDENTITY = "Apple Development";
894 897
 				CODE_SIGN_STYLE = Automatic;
895 898
 				DEBUG_INFORMATION_FORMAT = dwarf;
896 899
 				DEVELOPMENT_TEAM = FC967L3QRG;
897 900
 				GCC_C_LANGUAGE_STANDARD = gnu11;
898 901
 				INFOPLIST_FILE = "broadcast-extension/Info.plist";
899
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
902
+				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
900 903
 				LD_RUNPATH_SEARCH_PATHS = (
901 904
 					"$(inherited)",
902 905
 					"@executable_path/Frameworks",
@@ -907,11 +910,14 @@
907 910
 				PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.broadcast.extension;
908 911
 				PRODUCT_NAME = "$(TARGET_NAME)";
909 912
 				SKIP_INSTALL = YES;
913
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
914
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
915
+				SWIFT_VERSION = 5.0;
910 916
 				TARGETED_DEVICE_FAMILY = "1,2";
911 917
 			};
912 918
 			name = Debug;
913 919
 		};
914
-		4EC49BC225BEDAC100E76218 /* Release */ = {
920
+		4EB0602E260E026700F524C5 /* Release */ = {
915 921
 			isa = XCBuildConfiguration;
916 922
 			buildSettings = {
917 923
 				CLANG_ANALYZER_NONNULL = YES;
@@ -922,14 +928,14 @@
922 928
 				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
923 929
 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
924 930
 				CODE_SIGN_ENTITLEMENTS = "broadcast-extension/extension.entitlements";
925
-				CODE_SIGN_IDENTITY = "iPhone Developer";
931
+				CODE_SIGN_IDENTITY = "Apple Development";
926 932
 				CODE_SIGN_STYLE = Automatic;
927 933
 				COPY_PHASE_STRIP = NO;
928 934
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
929 935
 				DEVELOPMENT_TEAM = FC967L3QRG;
930 936
 				GCC_C_LANGUAGE_STANDARD = gnu11;
931 937
 				INFOPLIST_FILE = "broadcast-extension/Info.plist";
932
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
938
+				IPHONEOS_DEPLOYMENT_TARGET = 14.4;
933 939
 				LD_RUNPATH_SEARCH_PATHS = (
934 940
 					"$(inherited)",
935 941
 					"@executable_path/Frameworks",
@@ -939,6 +945,8 @@
939 945
 				PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.broadcast.extension;
940 946
 				PRODUCT_NAME = "$(TARGET_NAME)";
941 947
 				SKIP_INSTALL = YES;
948
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
949
+				SWIFT_VERSION = 5.0;
942 950
 				TARGETED_DEVICE_FAMILY = "1,2";
943 951
 			};
944 952
 			name = Release;
@@ -1087,11 +1095,11 @@
1087 1095
 			defaultConfigurationIsVisible = 0;
1088 1096
 			defaultConfigurationName = Release;
1089 1097
 		};
1090
-		4EC49BC025BEDAC100E76218 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcast Extension" */ = {
1098
+		4EB0602C260E026700F524C5 /* Build configuration list for PBXNativeTarget "JitsiMeetBroadcastExtension" */ = {
1091 1099
 			isa = XCConfigurationList;
1092 1100
 			buildConfigurations = (
1093
-				4EC49BC125BEDAC100E76218 /* Debug */,
1094
-				4EC49BC225BEDAC100E76218 /* Release */,
1101
+				4EB0602D260E026700F524C5 /* Debug */,
1102
+				4EB0602E260E026700F524C5 /* Release */,
1095 1103
 			);
1096 1104
 			defaultConfigurationIsVisible = 0;
1097 1105
 			defaultConfigurationName = Release;

+ 30
- 0
ios/app/broadcast-extension/Atomic.swift Wyświetl plik

@@ -0,0 +1,30 @@
1
+
2
+import Foundation
3
+
4
+@propertyWrapper
5
+struct Atomic<Value> {
6
+
7
+    private var value: Value
8
+    private let lock = NSLock()
9
+
10
+    init(wrappedValue value: Value) {
11
+        self.value = value
12
+    }
13
+
14
+    var wrappedValue: Value {
15
+      get { return load() }
16
+      set { store(newValue: newValue) }
17
+    }
18
+
19
+    func load() -> Value {
20
+        lock.lock()
21
+        defer { lock.unlock() }
22
+        return value
23
+    }
24
+
25
+    mutating func store(newValue: Value) {
26
+        lock.lock()
27
+        defer { lock.unlock() }
28
+        value = newValue
29
+    }
30
+}

+ 0
- 31
ios/app/broadcast-extension/DarwinNotificationCenter.h Wyświetl plik

@@ -1,31 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#import <Foundation/Foundation.h>
18
-
19
-NS_ASSUME_NONNULL_BEGIN
20
-
21
-extern NSNotificationName const kBroadcastStartedNotification;
22
-extern NSNotificationName const kBroadcastStoppedNotification;
23
-
24
-@interface DarwinNotificationCenter: NSObject
25
-
26
-+ (instancetype)sharedInstance;
27
-- (void)postNotificationWithName:(NSNotificationName)name;
28
-
29
-@end
30
-
31
-NS_ASSUME_NONNULL_END

+ 0
- 50
ios/app/broadcast-extension/DarwinNotificationCenter.m Wyświetl plik

@@ -1,50 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#import "DarwinNotificationCenter.h"
18
-
19
-NSNotificationName const kBroadcastStartedNotification = @"iOS_BroadcastStarted";
20
-NSNotificationName const kBroadcastStoppedNotification = @"iOS_BroadcastStopped";
21
-
22
-@implementation DarwinNotificationCenter {
23
-  CFNotificationCenterRef _notificationCenter;
24
-}
25
-
26
-+ (instancetype)sharedInstance {
27
-  static DarwinNotificationCenter *sharedInstance = nil;
28
-  static dispatch_once_t onceToken;
29
-  dispatch_once(&onceToken, ^{
30
-      sharedInstance = [[self alloc] init];
31
-  });
32
-  
33
-  return sharedInstance;
34
-}
35
-
36
-- (instancetype)init {
37
-  self = [super init];
38
-  if (self) {
39
-    _notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
40
-  }
41
-  
42
-  return self;
43
-}
44
-
45
-- (void)postNotificationWithName:(NSString*)name {
46
-  CFNotificationCenterPostNotification(_notificationCenter, (__bridge CFStringRef)name, NULL, NULL, true);
47
-}
48
-
49
-@end
50
-

+ 37
- 0
ios/app/broadcast-extension/DarwinNotificationCenter.swift Wyświetl plik

@@ -0,0 +1,37 @@
1
+/*
2
+ * Copyright @ 2021-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+import Foundation
18
+
19
+enum DarwinNotification: String {
20
+    case broadcastStarted = "iOS_BroadcastStarted"
21
+    case broadcastStopped = "iOS_BroadcastStopped"
22
+}
23
+
24
+class DarwinNotificationCenter {
25
+    
26
+    static var shared = DarwinNotificationCenter()
27
+    
28
+    private var notificationCenter: CFNotificationCenter
29
+    
30
+    init() {
31
+        notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
32
+    }
33
+    
34
+    func postNotification(_ name: DarwinNotification) {
35
+        CFNotificationCenterPostNotification(notificationCenter, CFNotificationName(rawValue: name.rawValue as CFString), nil, nil, true)
36
+    }
37
+}

+ 2
- 2
ios/app/broadcast-extension/Info.plist Wyświetl plik

@@ -5,7 +5,7 @@
5 5
 	<key>CFBundleDevelopmentRegion</key>
6 6
 	<string>$(DEVELOPMENT_LANGUAGE)</string>
7 7
 	<key>CFBundleDisplayName</key>
8
-	<string>JitsiMeet Broadcast Extension</string>
8
+	<string>Jitsi Meet Broadcast Extension</string>
9 9
 	<key>CFBundleExecutable</key>
10 10
 	<string>$(EXECUTABLE_NAME)</string>
11 11
 	<key>CFBundleIdentifier</key>
@@ -25,7 +25,7 @@
25 25
 		<key>NSExtensionPointIdentifier</key>
26 26
 		<string>com.apple.broadcast-services-upload</string>
27 27
 		<key>NSExtensionPrincipalClass</key>
28
-		<string>SampleHandler</string>
28
+		<string>$(PRODUCT_MODULE_NAME).SampleHandler</string>
29 29
 		<key>RPBroadcastProcessMode</key>
30 30
 		<string>RPBroadcastProcessModeSampleBuffer</string>
31 31
 	</dict>

+ 0
- 21
ios/app/broadcast-extension/SampleHandler.h Wyświetl plik

@@ -1,21 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#import <ReplayKit/ReplayKit.h>
18
-
19
-@interface SampleHandler : RPBroadcastSampleHandler
20
-
21
-@end

+ 0
- 123
ios/app/broadcast-extension/SampleHandler.m Wyświetl plik

@@ -1,123 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#import "SampleHandler.h"
18
-#import "SocketConnection.h"
19
-#import "SampleUploader.h"
20
-#import "DarwinNotificationCenter.h"
21
-
22
-@interface SampleHandler ()
23
-
24
-@property (nonatomic, retain) SocketConnection *clientConnection;
25
-@property (nonatomic, retain) SampleUploader *uploader;
26
-
27
-@end
28
-
29
-@implementation SampleHandler
30
-
31
-- (instancetype)init {
32
-  self = [super init];
33
-  if (self) {
34
-    self.clientConnection = [[SocketConnection alloc] initWithFilePath:self.socketFilePath];
35
-    [self setupConnection];
36
-    
37
-    self.uploader = [[SampleUploader alloc] initWithConnection:self.clientConnection];    
38
-  }
39
-  
40
-  return self;
41
-}
42
-
43
-- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
44
-  // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
45
-  NSLog(@"broadcast started");
46
-  
47
-  [[DarwinNotificationCenter sharedInstance] postNotificationWithName:kBroadcastStartedNotification];
48
-  [self openConnection];
49
-}
50
-
51
-- (void)broadcastPaused {
52
-    // User has requested to pause the broadcast. Samples will stop being delivered.
53
-}
54
-
55
-- (void)broadcastResumed {
56
-    // User has requested to resume the broadcast. Samples delivery will resume.
57
-}
58
-
59
-- (void)broadcastFinished {
60
-    // User has requested to finish the broadcast.
61
-  [[DarwinNotificationCenter sharedInstance] postNotificationWithName:kBroadcastStoppedNotification];
62
-  [self.clientConnection close];
63
-}
64
-
65
-- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
66
-  static NSUInteger frameCount = 0;
67
-  switch (sampleBufferType) {
68
-    case RPSampleBufferTypeVideo:
69
-      // adjust frame rate by using every third frame
70
-      if (++frameCount%3 == 0 && self.uploader.isReady) {
71
-        [self.uploader sendSample:sampleBuffer];
72
-      }
73
-      break;
74
-
75
-    default:
76
-      break;
77
-  }
78
-}
79
-
80
-// MARK: Private Methods
81
-
82
-- (NSString *)socketFilePath {
83
-    // the appGroupIdentifier must match the value provided in the app's info.plist for the RTCAppGroupIdentifier key
84
-    NSString *appGroupIdentifier = @"group.org.jitsi.meet.appgroup";
85
-    NSURL *sharedContainer = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupIdentifier];
86
-    NSString *socketFilePath = [[sharedContainer URLByAppendingPathComponent:@"rtc_SSFD"] path];
87
-      
88
-    return socketFilePath;
89
-}
90
-
91
-- (void)setupConnection {
92
-  __weak __typeof(self) weakSelf = self;
93
-  self.clientConnection.didClose = ^(NSError *error) {
94
-    NSLog(@"client connection did close: %@", error);
95
-    if (error) {
96
-      [weakSelf finishBroadcastWithError:error];
97
-    }
98
-    else {
99
-      NSInteger JMScreenSharingStopped = 10001;
100
-      NSError *customError = [NSError errorWithDomain:RPRecordingErrorDomain
101
-                                                 code:JMScreenSharingStopped
102
-                                             userInfo:@{NSLocalizedDescriptionKey: @"Screen sharing stopped"}];
103
-      [weakSelf finishBroadcastWithError:customError];
104
-    }
105
-  };
106
-}
107
-
108
-- (void)openConnection {
109
-  dispatch_queue_t queue = dispatch_queue_create("org.jitsi.meet.broadcast.connectTimer", 0);
110
-  dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
111
-  dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 0.1 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
112
-  
113
-  dispatch_source_set_event_handler(timer, ^{
114
-    BOOL success = [self.clientConnection open];
115
-    if (success) {
116
-      dispatch_source_cancel(timer);
117
-    }
118
-  });
119
-
120
-  dispatch_resume(timer);
121
-}
122
-
123
-@end

+ 117
- 0
ios/app/broadcast-extension/SampleHandler.swift Wyświetl plik

@@ -0,0 +1,117 @@
1
+/*
2
+ * Copyright @ 2021-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+import ReplayKit
18
+import JitsiMeetSDK
19
+
20
+private enum Constants {
21
+    // the App Group ID value that the app and the broadcast extension targets are setup with. It differs for each app.
22
+    static let appGroupIdentifier = "group.org.jitsi.meet.appgroup"
23
+}
24
+
25
+class SampleHandler: RPBroadcastSampleHandler {
26
+    
27
+    private var clientConnection: SocketConnection?
28
+    private var uploader: SampleUploader?
29
+    
30
+    private var frameCount: Int = 0
31
+    
32
+    var socketFilePath: String {
33
+      let sharedContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupIdentifier)
34
+        
35
+        return sharedContainer?.appendingPathComponent("rtc_SSFD").path ?? ""
36
+    }
37
+    
38
+    override init() {
39
+      super.init()
40
+        if let connection = SocketConnection(filePath: socketFilePath) {
41
+          clientConnection = connection
42
+          setupConnection()
43
+          
44
+          uploader = SampleUploader(connection: connection)
45
+        }
46
+    }
47
+
48
+    override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) {
49
+        // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.
50
+        print("broadcast started")
51
+        
52
+        frameCount = 0
53
+        
54
+        DarwinNotificationCenter.shared.postNotification(.broadcastStarted)
55
+        openConnection()
56
+    }
57
+    
58
+    override func broadcastPaused() {
59
+        // User has requested to pause the broadcast. Samples will stop being delivered.
60
+    }
61
+    
62
+    override func broadcastResumed() {
63
+        // User has requested to resume the broadcast. Samples delivery will resume.
64
+    }
65
+    
66
+    override func broadcastFinished() {
67
+        // User has requested to finish the broadcast.
68
+        DarwinNotificationCenter.shared.postNotification(.broadcastStopped)
69
+        clientConnection?.close()
70
+    }
71
+    
72
+    override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
73
+        switch sampleBufferType {
74
+        case RPSampleBufferType.video:
75
+            // very simple mechanism for adjusting frame rate by using every third frame
76
+            frameCount += 1
77
+            if frameCount % 3 == 0 {
78
+                uploader?.send(sample: sampleBuffer)
79
+            }
80
+        default:
81
+            break
82
+        }
83
+    }
84
+}
85
+
86
+private extension SampleHandler {
87
+  
88
+    func setupConnection() {
89
+        clientConnection?.didClose = { [weak self] error in
90
+            print("client connection did close \(String(describing: error))")
91
+          
92
+            if let error = error {
93
+                self?.finishBroadcastWithError(error)
94
+            } else {
95
+                // the displayed failure message is more user friendly when using NSError instead of Error
96
+                let JMScreenSharingStopped = 10001
97
+                let customError = NSError(domain: RPRecordingErrorDomain, code: JMScreenSharingStopped, userInfo: [NSLocalizedDescriptionKey: "Screen sharing stopped"])
98
+                self?.finishBroadcastWithError(customError)
99
+            }
100
+        }
101
+    }
102
+    
103
+    func openConnection() {
104
+        let queue = DispatchQueue(label: "broadcast.connectTimer")
105
+        let timer = DispatchSource.makeTimerSource(queue: queue)
106
+        timer.schedule(deadline: .now(), repeating: .milliseconds(100), leeway: .milliseconds(500))
107
+        timer.setEventHandler { [weak self] in
108
+            guard self?.clientConnection?.open() == true else {
109
+                return
110
+            }
111
+            
112
+            timer.cancel()
113
+        }
114
+        
115
+        timer.resume()
116
+    }
117
+}

+ 0
- 33
ios/app/broadcast-extension/SampleUploader.h Wyświetl plik

@@ -1,33 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#import <Foundation/Foundation.h>
18
-#import <ReplayKit/ReplayKit.h>
19
-
20
-NS_ASSUME_NONNULL_BEGIN
21
-
22
-@class SocketConnection;
23
-
24
-@interface SampleUploader : NSObject
25
-
26
-@property (nonatomic, assign, readonly) BOOL isReady;
27
-
28
-- (instancetype)initWithConnection:(SocketConnection *)connection;
29
-- (void)sendSample:(CMSampleBufferRef)sampleBuffer;
30
-
31
-@end
32
-
33
-NS_ASSUME_NONNULL_END

+ 0
- 157
ios/app/broadcast-extension/SampleUploader.m Wyświetl plik

@@ -1,157 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#import <MessageUI/MessageUI.h>
18
-#import <ReplayKit/ReplayKit.h>
19
-
20
-#import "SampleUploader.h"
21
-#import "SocketConnection.h"
22
-
23
-static const NSInteger kBufferMaxLenght = 10 * 1024;
24
-
25
-@interface SampleUploader ()
26
-
27
-@property (nonatomic, assign) BOOL isReady;
28
-
29
-@property (nonatomic, strong) dispatch_queue_t serialQueue;
30
-@property (nonatomic, strong) SocketConnection *connection;
31
-@property (nonatomic, strong) CIContext *imageContext;
32
-
33
-@property (nonatomic, strong) NSData *dataToSend;
34
-@property (nonatomic, assign) NSUInteger byteIndex;
35
-
36
-@end
37
-
38
-@implementation SampleUploader
39
-
40
-- (instancetype)initWithConnection:(SocketConnection *)connection {
41
-  self = [super init];
42
-  if (self) {
43
-    self.serialQueue = dispatch_queue_create("org.jitsi.meet.broadcast.sampleUploader", DISPATCH_QUEUE_SERIAL);
44
-    
45
-    self.connection = connection;
46
-    [self setupConnection];
47
-    
48
-    self.imageContext = [[CIContext alloc] initWithOptions:nil];
49
-    self.isReady = false;
50
-  }
51
-  
52
-  return self;
53
-}
54
-
55
-- (void)sendSample:(CMSampleBufferRef)sampleBuffer {
56
-  self.isReady = false;
57
-  
58
-  self.dataToSend = [self prepareSample:sampleBuffer];
59
-  self.byteIndex = 0;
60
-  
61
-  dispatch_async(self.serialQueue, ^{
62
-    [self sendData];
63
-  });
64
-}
65
-
66
-// MARK: Private Methods
67
-
68
-- (void)setupConnection {
69
-  __weak __typeof(self) weakSelf = self;
70
-  self.connection.didOpen = ^{
71
-    weakSelf.isReady = true;
72
-  };
73
-  self.connection.streamHasSpaceAvailable = ^{
74
-    dispatch_async(weakSelf.serialQueue, ^{
75
-      weakSelf.isReady = ![weakSelf sendData];
76
-    });
77
-  };
78
-}
79
-
80
-/**
81
- This function downscales and converts to jpeg the provided sample buffer, then wraps the resulted image data into a CFHTTPMessageRef. Returns the serialized CFHTTPMessageRef.
82
- */
83
-- (NSData *)prepareSample:(CMSampleBufferRef)sampleBuffer {
84
-  CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
85
-    
86
-  CVPixelBufferLockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
87
-  
88
-  CGFloat scaleFactor = 2;
89
-  size_t width = CVPixelBufferGetWidth(imageBuffer)/scaleFactor;
90
-  size_t height = CVPixelBufferGetHeight(imageBuffer)/scaleFactor;
91
-  CGImagePropertyOrientation orientation = ((__bridge NSNumber*)CMGetAttachment(sampleBuffer, (__bridge CFStringRef)RPVideoSampleOrientationKey , NULL)).unsignedIntValue;
92
-    
93
-  CGAffineTransform scaleTransform = CGAffineTransformMakeScale(1/scaleFactor, 1/scaleFactor);
94
-  NSData *bufferData = [self jpegDataFromPixelBuffer:imageBuffer withScaling:scaleTransform];
95
-  
96
-  CVPixelBufferUnlockBaseAddress(imageBuffer, kCVPixelBufferLock_ReadOnly);
97
-  
98
-  if (bufferData) {
99
-    CFHTTPMessageRef httpResponse = CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1);
100
-    CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Content-Length", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", bufferData.length]);
101
-    CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Width", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", width]);
102
-    CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Height", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", height]);
103
-    CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Orientation", (__bridge CFStringRef)[NSString stringWithFormat:@"%u", orientation]);
104
-
105
-    CFHTTPMessageSetBody(httpResponse, (__bridge CFDataRef)bufferData);
106
-
107
-    CFDataRef serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse);
108
-    CFRelease(httpResponse);
109
-    
110
-    return CFBridgingRelease(serializedMessage);
111
-  }
112
-  
113
-  return nil;
114
-}
115
-
116
-- (BOOL)sendData {
117
-  if (!self.dataToSend) {
118
-    NSLog(@"no data to send");
119
-    return false;
120
-  }
121
-
122
-  NSUInteger bytesLeft = self.dataToSend.length - self.byteIndex;
123
-  
124
-  NSInteger length = bytesLeft > kBufferMaxLenght ? kBufferMaxLenght : bytesLeft;
125
-  uint8_t buffer[length];
126
-  [self.dataToSend getBytes:&buffer range:NSMakeRange(self.byteIndex, length)];
127
-
128
-  length = [self.connection writeBufferToStream:buffer maxLength:length];
129
-  if (length > 0) {
130
-    self.byteIndex += length;
131
-    bytesLeft -= length;
132
-
133
-    if (bytesLeft == 0) {
134
-      NSLog(@"video sample processed successfully");
135
-      self.dataToSend = nil;
136
-      self.byteIndex = 0;
137
-    }
138
-  }
139
-  else {
140
-    NSLog(@"writeBufferToStream failure");
141
-  }
142
-  
143
-  return true;
144
-}
145
-
146
-- (NSData *)jpegDataFromPixelBuffer:(CVPixelBufferRef)pixelBuffer withScaling:(CGAffineTransform)scaleTransform {
147
-  CIImage *image = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer];
148
-  image = [image imageByApplyingTransform:scaleTransform];
149
-  
150
-  NSDictionary *options = @{(NSString *)kCGImageDestinationLossyCompressionQuality: [NSNumber numberWithFloat:1.0]};
151
-  NSData *imageData = [self.imageContext JPEGRepresentationOfImage:image
152
-                                                        colorSpace:image.colorSpace
153
-                                                           options:options];
154
-  return imageData;
155
-}
156
-
157
-@end

+ 154
- 0
ios/app/broadcast-extension/SampleUploader.swift Wyświetl plik

@@ -0,0 +1,154 @@
1
+/*
2
+ * Copyright @ 2021-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+import Foundation
18
+import ReplayKit
19
+
20
+private enum Constants {
21
+    static let bufferMaxLength = 10240
22
+}
23
+
24
+class SampleUploader {
25
+    
26
+    private static var imageContext = CIContext(options: nil)
27
+    
28
+    @Atomic private var isReady: Bool = false
29
+    private var connection: SocketConnection
30
+  
31
+    private var dataToSend: Data?
32
+    private var byteIndex = 0
33
+  
34
+    private let serialQueue: DispatchQueue
35
+    
36
+    init(connection: SocketConnection) {
37
+        self.connection = connection
38
+        self.serialQueue = DispatchQueue(label: "org.jitsi.meet.broadcast.sampleUploader")
39
+      
40
+        setupConnection()
41
+    }
42
+  
43
+    @discardableResult func send(sample buffer: CMSampleBuffer) -> Bool {
44
+        guard isReady == true else {
45
+            return false
46
+        }
47
+        
48
+        isReady = false
49
+
50
+        dataToSend = prepare(sample: buffer)
51
+        byteIndex = 0
52
+
53
+        serialQueue.async { [weak self] in
54
+            self?.sendDataChunk()
55
+        }
56
+        
57
+        return true
58
+    }
59
+}
60
+
61
+private extension SampleUploader {
62
+    
63
+    func setupConnection() {
64
+        connection.didOpen = { [weak self] in
65
+            self?.isReady = true
66
+        }
67
+        connection.streamHasSpaceAvailable = { [weak self] in
68
+            self?.serialQueue.async {
69
+                self?.isReady = !(self?.sendDataChunk() ?? true)
70
+            }
71
+        }
72
+    }
73
+    
74
+    @discardableResult func sendDataChunk() -> Bool {
75
+        guard let dataToSend = dataToSend else {
76
+            return false
77
+        }
78
+      
79
+        var bytesLeft = dataToSend.count - byteIndex
80
+        var length = bytesLeft > Constants.bufferMaxLength ? Constants.bufferMaxLength : bytesLeft
81
+
82
+        length = dataToSend[byteIndex..<(byteIndex + length)].withUnsafeBytes {
83
+            guard let ptr = $0.bindMemory(to: UInt8.self).baseAddress else {
84
+                return 0
85
+            }
86
+          
87
+            return connection.writeToStream(buffer: ptr, maxLength: length)
88
+        }
89
+
90
+        if length > 0 {
91
+            byteIndex += length
92
+            bytesLeft -= length
93
+
94
+            if bytesLeft == 0 {
95
+                self.dataToSend = nil
96
+                byteIndex = 0
97
+            }
98
+        } else {
99
+            print("writeBufferToStream failure")
100
+        }
101
+      
102
+        return true
103
+    }
104
+    
105
+    func prepare(sample buffer: CMSampleBuffer) -> Data? {
106
+        guard let imageBuffer = CMSampleBufferGetImageBuffer(buffer) else {
107
+            print("image buffer not available")
108
+            return nil
109
+        }
110
+        
111
+        CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
112
+        
113
+        let scaleFactor = 2.0
114
+        let width = CVPixelBufferGetWidth(imageBuffer)/Int(scaleFactor)
115
+        let height = CVPixelBufferGetHeight(imageBuffer)/Int(scaleFactor)
116
+        let orientation = CMGetAttachment(buffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uintValue ?? 0
117
+                                    
118
+        let scaleTransform = CGAffineTransform(scaleX: CGFloat(1.0/scaleFactor), y: CGFloat(1.0/scaleFactor))
119
+        let bufferData = self.jpegData(from: imageBuffer, scale: scaleTransform)
120
+        
121
+        CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
122
+        
123
+        guard let messageData = bufferData else {
124
+            print("corrupted image buffer")
125
+            return nil
126
+        }
127
+              
128
+        let httpResponse = CFHTTPMessageCreateResponse(nil, 200, nil, kCFHTTPVersion1_1).takeRetainedValue()
129
+        CFHTTPMessageSetHeaderFieldValue(httpResponse, "Content-Length" as CFString, String(messageData.count) as CFString)
130
+        CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Width" as CFString, String(width) as CFString)
131
+        CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Height" as CFString, String(height) as CFString)
132
+        CFHTTPMessageSetHeaderFieldValue(httpResponse, "Buffer-Orientation" as CFString, String(orientation) as CFString)
133
+        
134
+        CFHTTPMessageSetBody(httpResponse, messageData as CFData)
135
+        
136
+        let serializedMessage = CFHTTPMessageCopySerializedMessage(httpResponse)?.takeRetainedValue() as Data?
137
+      
138
+        return serializedMessage
139
+    }
140
+    
141
+    func jpegData(from buffer: CVPixelBuffer, scale scaleTransform: CGAffineTransform) -> Data? {
142
+        var image = CIImage(cvPixelBuffer: buffer)
143
+        image = image.transformed(by: scaleTransform)
144
+        
145
+        guard let colorSpace = image.colorSpace else {
146
+            return nil
147
+        }
148
+      
149
+        let options: [CIImageRepresentationOption: Float] = [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption: 1.0]
150
+        let imageData = SampleUploader.imageContext.jpegRepresentation(of: image, colorSpace: colorSpace, options: options)
151
+      
152
+        return imageData
153
+    }
154
+}

+ 0
- 34
ios/app/broadcast-extension/SocketConnection.h Wyświetl plik

@@ -1,34 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#import <Foundation/Foundation.h>
18
-
19
-NS_ASSUME_NONNULL_BEGIN
20
-
21
-@interface SocketConnection : NSObject
22
-
23
-@property (nonatomic, copy, nullable) void (^didOpen)(void);
24
-@property (nonatomic, copy, nullable) void (^didClose)(NSError*);
25
-@property (nonatomic, copy, nullable) void (^streamHasSpaceAvailable)(void);
26
-
27
-- (instancetype)initWithFilePath:(nonnull NSString *)filePath;
28
-- (BOOL)open;
29
-- (void)close;
30
-- (NSInteger)writeBufferToStream:(const uint8_t*)buffer maxLength:(NSInteger)length;
31
-
32
-@end
33
-
34
-NS_ASSUME_NONNULL_END

+ 0
- 189
ios/app/broadcast-extension/SocketConnection.m Wyświetl plik

@@ -1,189 +0,0 @@
1
-/*
2
- * Copyright @ 2021-present 8x8, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- *     http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
-#include <sys/socket.h>
18
-#include <sys/un.h>
19
-
20
-#import "SocketConnection.h"
21
-
22
-@interface SocketConnection () <NSStreamDelegate>
23
-
24
-@property (nonatomic, copy) NSString *filePath;
25
-
26
-@property (nonatomic, strong) NSInputStream *inputStream;
27
-@property (nonatomic, strong) NSOutputStream *outputStream;
28
-
29
-@property (nonatomic, strong) NSThread *networkThread;
30
-
31
-@end
32
-
33
-@implementation SocketConnection {
34
-  int _socket;
35
-  struct sockaddr_un _socketAddr;
36
-}
37
-
38
-- (instancetype)initWithFilePath:(NSString *)path {
39
-  self = [super init];
40
-  if (self) {
41
-    self.filePath = path;
42
-    
43
-    [self setupSocketWithFilePath:path];
44
-    [self setupNetworkThread];
45
-  }
46
-  
47
-  return self;
48
-}
49
-
50
-- (BOOL)open {
51
-  NSLog(@"Open socket connection");
52
-  
53
-  if (![[NSFileManager defaultManager] fileExistsAtPath:self.filePath]) {
54
-      NSLog(@"failure: socket file missing");
55
-      return false;
56
-  }
57
-
58
-  int status = connect(_socket, (struct sockaddr *)&_socketAddr, sizeof(_socketAddr));
59
-  if (status < 0) {
60
-      NSLog(@"failure: socket connect (%d)", status);
61
-      return false;
62
-  }
63
-  
64
-  [self.networkThread start];
65
-
66
-  CFReadStreamRef readStream;
67
-  CFWriteStreamRef writeStream;
68
-
69
-  CFStreamCreatePairWithSocket(kCFAllocatorDefault, _socket, &readStream, &writeStream);
70
-
71
-  self.inputStream = (__bridge_transfer NSInputStream *)readStream;
72
-  self.inputStream.delegate = self;
73
-  [self.inputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
74
-
75
-  self.outputStream = (__bridge_transfer NSOutputStream *)writeStream;
76
-  self.outputStream.delegate = self;
77
-  [self.outputStream setProperty:@"kCFBooleanTrue" forKey:@"kCFStreamPropertyShouldCloseNativeSocket"];
78
-  
79
-  [self performSelector:@selector(scheduleStreams) onThread:self.networkThread withObject:nil waitUntilDone:true];
80
-
81
-  [self.inputStream open];
82
-  [self.outputStream open];
83
-
84
-  NSLog(@"read stream status: %ld", CFReadStreamGetStatus(readStream));
85
-  NSLog(@"write stream status: %ld", CFWriteStreamGetStatus(writeStream));
86
-  
87
-  return true;
88
-}
89
-
90
-- (void)close {
91
-  [self performSelector:@selector(unscheduleStreams) onThread:self.networkThread withObject:nil waitUntilDone:true];
92
-
93
-  self.inputStream.delegate = nil;
94
-  self.outputStream.delegate = nil;
95
-  
96
-  [self.inputStream close];
97
-  [self.outputStream close];
98
-
99
-  [self.networkThread cancel];
100
-}
101
-
102
-- (NSInteger)writeBufferToStream:(const uint8_t*)buffer maxLength:(NSInteger)length {
103
-  return [self.outputStream write:buffer maxLength:length];
104
-}
105
-
106
-// MARK: Private Methods
107
-
108
-- (BOOL)isOpen {
109
-  return self.inputStream.streamStatus == NSStreamStatusOpen && self.outputStream.streamStatus == NSStreamStatusOpen;
110
-}
111
-
112
-- (void)setupSocketWithFilePath:(NSString*)path {
113
-  _socket = socket(AF_UNIX, SOCK_STREAM, 0);
114
-  
115
-  memset(&_socketAddr, 0, sizeof(_socketAddr));
116
-  _socketAddr.sun_family = AF_UNIX;
117
-  strncpy(_socketAddr.sun_path, path.UTF8String, sizeof(_socketAddr.sun_path) - 1);
118
-}
119
-
120
-- (void)setupNetworkThread {
121
-  self.networkThread = [[NSThread alloc] initWithBlock:^{
122
-    do {
123
-      @autoreleasepool {
124
-        [[NSRunLoop currentRunLoop] run];
125
-      }
126
-    } while (![NSThread currentThread].isCancelled);
127
-  }];
128
-  self.networkThread.qualityOfService = NSQualityOfServiceUserInitiated;
129
-}
130
-
131
-- (void)scheduleStreams {
132
-  [self.inputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
133
-  [self.outputStream scheduleInRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
134
-}
135
-
136
-- (void)unscheduleStreams {
137
-  [self.inputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
138
-  [self.outputStream removeFromRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
139
-}
140
-
141
-- (void)notifyDidClose:(NSError *)error {
142
-  if (self.didClose) {
143
-    self.didClose(error);
144
-  }
145
-}
146
-
147
-@end
148
-
149
-#pragma mark - NSStreamDelegate
150
-
151
-@implementation SocketConnection (NSStreamDelegate)
152
-
153
-- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
154
-  switch (eventCode) {
155
-    case NSStreamEventOpenCompleted:
156
-      NSLog(@"client stream open completed");
157
-      if (aStream == self.outputStream && self.didOpen) {
158
-        self.didOpen();
159
-      }
160
-      break;
161
-    case NSStreamEventHasBytesAvailable:
162
-      if (aStream == self.inputStream) {
163
-        uint8_t buffer;
164
-        NSInteger numberOfBytesRead = [(NSInputStream *)aStream read:&buffer maxLength:sizeof(buffer)];
165
-        if (!numberOfBytesRead && aStream.streamStatus == NSStreamStatusAtEnd) {
166
-          NSLog(@"server socket closed");
167
-          [self close];
168
-          [self notifyDidClose:nil];
169
-        }
170
-      }
171
-      break;
172
-    case NSStreamEventHasSpaceAvailable:
173
-      if (aStream == self.outputStream && self.streamHasSpaceAvailable) {
174
-        NSLog(@"client stream has space available");
175
-        self.streamHasSpaceAvailable();
176
-      }
177
-      break;
178
-    case NSStreamEventErrorOccurred:
179
-      NSLog(@"client stream error occurred: %@", aStream.streamError);
180
-      [self close];
181
-      [self notifyDidClose:aStream.streamError];
182
-      break;
183
-      
184
-    default:
185
-      break;
186
-  }
187
-}
188
-
189
-@end

+ 205
- 0
ios/app/broadcast-extension/SocketConnection.swift Wyświetl plik

@@ -0,0 +1,205 @@
1
+/*
2
+ * Copyright @ 2021-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+import Foundation
18
+
19
+class SocketConnection: NSObject {
20
+    var didOpen: (() -> Void)?
21
+    var didClose: ((Error?) -> Void)?
22
+    var streamHasSpaceAvailable: (() -> Void)?
23
+
24
+    private let filePath: String
25
+    private var socketHandle: Int32 = -1
26
+    private var address: sockaddr_un?
27
+
28
+    private var inputStream: InputStream?
29
+    private var outputStream: OutputStream?
30
+    
31
+    private var networkQueue: DispatchQueue?
32
+    private var shouldKeepRunning = false
33
+
34
+    init?(filePath path: String) {
35
+        filePath = path
36
+        socketHandle = Darwin.socket(AF_UNIX, SOCK_STREAM, 0)
37
+
38
+        guard socketHandle != -1 else {
39
+            print("failure: create socket")
40
+            return nil
41
+        }
42
+    }
43
+
44
+    func open() -> Bool {
45
+        print("open socket connection")
46
+
47
+        guard FileManager.default.fileExists(atPath: filePath) else {
48
+            print("failure: socket file missing")
49
+            return false
50
+        }
51
+      
52
+        guard setupAddress() == true else {
53
+            return false
54
+        }
55
+        
56
+        guard connectSocket() == true else {
57
+            return false
58
+        }
59
+
60
+        setupStreams()
61
+        
62
+        inputStream?.open()
63
+        outputStream?.open()
64
+
65
+        return true
66
+    }
67
+
68
+    func close() {
69
+        unscheduleStreams()
70
+
71
+        inputStream?.delegate = nil
72
+        outputStream?.delegate = nil
73
+
74
+        inputStream?.close()
75
+        outputStream?.close()
76
+        
77
+        inputStream = nil
78
+        outputStream = nil
79
+    }
80
+
81
+    func writeToStream(buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int {
82
+        return outputStream?.write(buffer, maxLength: length) ?? 0
83
+    }
84
+}
85
+
86
+extension SocketConnection: StreamDelegate {
87
+
88
+    func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
89
+        switch eventCode {
90
+        case .openCompleted:
91
+            print("client stream open completed")
92
+            if aStream == outputStream {
93
+                didOpen?()
94
+            }
95
+        case .hasBytesAvailable:
96
+            if aStream == inputStream {
97
+                var buffer: UInt8 = 0
98
+                let numberOfBytesRead = inputStream?.read(&buffer, maxLength: 1)
99
+                if numberOfBytesRead == 0 && aStream.streamStatus == .atEnd {
100
+                    print("server socket closed")
101
+                    close()
102
+                    notifyDidClose(error: nil)
103
+                }
104
+            }
105
+        case .hasSpaceAvailable:
106
+            if aStream == outputStream {
107
+                streamHasSpaceAvailable?()
108
+            }
109
+        case .errorOccurred:
110
+            print("client stream error occured: \(String(describing: aStream.streamError))")
111
+            close()
112
+            notifyDidClose(error: aStream.streamError)
113
+
114
+        default:
115
+            break
116
+        }
117
+    }
118
+}
119
+
120
+private extension SocketConnection {
121
+  
122
+    func setupAddress() -> Bool {
123
+        var addr = sockaddr_un()
124
+        guard filePath.count < MemoryLayout.size(ofValue: addr.sun_path) else {
125
+            print("failure: fd path is too long")
126
+            return false
127
+        }
128
+
129
+        _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in
130
+            filePath.withCString {
131
+                strncpy(ptr, $0, filePath.count)
132
+            }
133
+        }
134
+        
135
+        address = addr
136
+        return true
137
+    }
138
+
139
+    func connectSocket() -> Bool {
140
+        guard var addr = address else {
141
+            return false
142
+        }
143
+        
144
+        let status = withUnsafePointer(to: &addr) { ptr in
145
+            ptr.withMemoryRebound(to: sockaddr.self, capacity: 1) {
146
+                Darwin.connect(socketHandle, $0, socklen_t(MemoryLayout<sockaddr_un>.size))
147
+            }
148
+        }
149
+
150
+        guard status == noErr else {
151
+            print("failure: \(status)")
152
+            return false
153
+        }
154
+        
155
+        return true
156
+    }
157
+
158
+    func setupStreams() {
159
+        var readStream: Unmanaged<CFReadStream>?
160
+        var writeStream: Unmanaged<CFWriteStream>?
161
+
162
+        CFStreamCreatePairWithSocket(kCFAllocatorDefault, socketHandle, &readStream, &writeStream)
163
+
164
+        inputStream = readStream?.takeRetainedValue()
165
+        inputStream?.delegate = self
166
+        inputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
167
+
168
+        outputStream = writeStream?.takeRetainedValue()
169
+        outputStream?.delegate = self
170
+        outputStream?.setProperty(kCFBooleanTrue, forKey: Stream.PropertyKey(kCFStreamPropertyShouldCloseNativeSocket as String))
171
+
172
+        scheduleStreams()
173
+    }
174
+  
175
+    func scheduleStreams() {
176
+        shouldKeepRunning = true
177
+        
178
+        networkQueue = DispatchQueue.global(qos: .userInitiated)
179
+        networkQueue?.async { [weak self] in
180
+            self?.inputStream?.schedule(in: .current, forMode: .common)
181
+            self?.outputStream?.schedule(in: .current, forMode: .common)
182
+            
183
+            var isRunning = false
184
+                        
185
+            repeat {
186
+                isRunning = self?.shouldKeepRunning ?? false && RunLoop.current.run(mode: .default, before: .distantFuture)
187
+            } while (isRunning)
188
+        }
189
+    }
190
+    
191
+    func unscheduleStreams() {
192
+        networkQueue?.sync { [weak self] in
193
+            self?.inputStream?.remove(from: .current, forMode: .common)
194
+            self?.outputStream?.remove(from: .current, forMode: .common)
195
+        }
196
+        
197
+        shouldKeepRunning = false
198
+    }
199
+    
200
+    func notifyDidClose(error: Error?) {
201
+        if didClose != nil {
202
+            didClose?(error)
203
+        }
204
+    }
205
+}

Ładowanie…
Anuluj
Zapisz