serve-sim-sjchmiela 0.1.55 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Package.swift +11 -24
- package/Sources/{SimStreamHelper → SimNative}/WebRTCPublisher.swift +284 -4
- package/Sources/SimNative/build.sh +18 -24
- package/bin/LiveKitWebRTC.framework/Versions/A/Headers/RTCAudioProcessingConfig.h +1 -1
- package/bin/LiveKitWebRTC.framework/Versions/A/Headers/RTCFrameCryptorKeyProvider.h +1 -1
- package/dist/middleware.js +27 -27
- package/dist/native/serve-sim-native.node +0 -0
- package/dist/serve-sim.js +57 -57
- package/package.json +3 -6
- package/src/middleware.ts +66 -26
- /package/Sources/{SimStreamHelper → SimNative}/AccessibilityBridge.swift +0 -0
- /package/Sources/{SimStreamHelper → SimNative}/FrameCapture.swift +0 -0
- /package/Sources/{SimStreamHelper → SimNative}/H264Encoder.swift +0 -0
- /package/Sources/{SimStreamHelper → SimNative}/HIDInjector.swift +0 -0
- /package/Sources/{SimStreamHelper → SimNative}/SimFrameworks.swift +0 -0
- /package/Sources/{SimStreamHelper → SimNative}/StreamFormat.swift +0 -0
- /package/Sources/{SimStreamHelper → SimNative}/VideoEncoder.swift +0 -0
- /package/Sources/{SimStreamHelper → SimNative}/Xcode.swift +0 -0
package/Package.swift
CHANGED
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
// arch only, and cross-compiling per-arch with `--triple` breaks the
|
|
15
15
|
// `#NodeModule` macro.
|
|
16
16
|
|
|
17
|
+
import Foundation
|
|
17
18
|
import PackageDescription
|
|
18
19
|
|
|
20
|
+
let packageRoot = URL(fileURLWithPath: #filePath).deletingLastPathComponent().path
|
|
21
|
+
let liveKitWebRTCFrameworkSearchPath = "\(packageRoot)/bin"
|
|
22
|
+
|
|
19
23
|
let package = Package(
|
|
20
24
|
name: "serve-sim-native",
|
|
21
25
|
platforms: [.macOS(.v14)],
|
|
@@ -23,7 +27,7 @@ let package = Package(
|
|
|
23
27
|
.library(
|
|
24
28
|
name: "serve-sim-native",
|
|
25
29
|
type: .dynamic,
|
|
26
|
-
targets: ["
|
|
30
|
+
targets: ["SimNative"]
|
|
27
31
|
),
|
|
28
32
|
],
|
|
29
33
|
dependencies: [
|
|
@@ -31,42 +35,25 @@ let package = Package(
|
|
|
31
35
|
],
|
|
32
36
|
targets: [
|
|
33
37
|
.target(
|
|
34
|
-
name: "
|
|
38
|
+
name: "SimNative",
|
|
35
39
|
dependencies: [
|
|
36
40
|
.product(name: "NodeAPI", package: "node-swift"),
|
|
37
41
|
.product(name: "NodeModuleSupport", package: "node-swift"),
|
|
38
42
|
],
|
|
39
|
-
path: "Sources",
|
|
40
|
-
// Sibling targets built by their own build.sh (Obj-C/ObjC++), not
|
|
41
|
-
// part of this SwiftPM package.
|
|
42
43
|
exclude: [
|
|
43
|
-
"
|
|
44
|
-
"SimCameraHelper",
|
|
45
|
-
"SimAXSettings",
|
|
46
|
-
"SimNative/build.sh",
|
|
47
|
-
],
|
|
48
|
-
sources: [
|
|
49
|
-
"SimNative/sim-module.swift",
|
|
50
|
-
"SimNative/sim-capture.swift",
|
|
51
|
-
"SimStreamHelper/WebRTCPublisher.swift",
|
|
52
|
-
"SimStreamHelper/HIDInjector.swift",
|
|
53
|
-
"SimStreamHelper/FrameCapture.swift",
|
|
54
|
-
"SimStreamHelper/VideoEncoder.swift",
|
|
55
|
-
"SimStreamHelper/H264Encoder.swift",
|
|
56
|
-
"SimStreamHelper/StreamFormat.swift",
|
|
57
|
-
"SimStreamHelper/AccessibilityBridge.swift",
|
|
58
|
-
"SimStreamHelper/SimFrameworks.swift",
|
|
59
|
-
"SimStreamHelper/Xcode.swift",
|
|
44
|
+
"build.sh",
|
|
60
45
|
],
|
|
61
46
|
swiftSettings: [
|
|
62
|
-
.unsafeFlags(["-F",
|
|
47
|
+
.unsafeFlags(["-F", liveKitWebRTCFrameworkSearchPath]),
|
|
63
48
|
],
|
|
64
49
|
linkerSettings: [
|
|
65
50
|
.unsafeFlags([
|
|
66
|
-
"-F",
|
|
51
|
+
"-F", liveKitWebRTCFrameworkSearchPath,
|
|
67
52
|
"-framework", "LiveKitWebRTC",
|
|
68
53
|
"-Xlinker", "-rpath",
|
|
69
54
|
"-Xlinker", "@loader_path/../bin",
|
|
55
|
+
"-Xlinker", "-undefined",
|
|
56
|
+
"-Xlinker", "dynamic_lookup",
|
|
70
57
|
]),
|
|
71
58
|
.linkedFramework("CoreVideo"),
|
|
72
59
|
.linkedFramework("CoreMedia"),
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import Darwin
|
|
2
3
|
import CoreVideo
|
|
3
4
|
import CoreMedia
|
|
4
5
|
import Accelerate
|
|
6
|
+
import VideoToolbox
|
|
5
7
|
import LiveKitWebRTC
|
|
6
8
|
|
|
7
9
|
struct WebRTCIceServerPayload: Codable {
|
|
@@ -47,8 +49,10 @@ final class WebRTCPublisher {
|
|
|
47
49
|
private var lastForwardedPixelFormat: OSType?
|
|
48
50
|
private var lastFrameMode = "none"
|
|
49
51
|
private var useNativePixelBufferFrames: Bool?
|
|
52
|
+
private var requestedCodecName = "H264"
|
|
50
53
|
private var selectedCodecName = "H264"
|
|
51
54
|
private let h264PixelBufferConverter = H264WebRTCPixelBufferConverter()
|
|
55
|
+
private let h264WebRTCSupport: WebRTCH264Support
|
|
52
56
|
private var maxFps = 30
|
|
53
57
|
private var targetBitrate = 6_000_000
|
|
54
58
|
private var convertedFrameCount: Int64 = 0
|
|
@@ -61,6 +65,7 @@ final class WebRTCPublisher {
|
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
init() {
|
|
68
|
+
h264WebRTCSupport = Self.detectH264WebRTCSupport()
|
|
64
69
|
let encoderFactory = LKRTCDefaultVideoEncoderFactory()
|
|
65
70
|
let decoderFactory = LKRTCDefaultVideoDecoderFactory()
|
|
66
71
|
factory = LKRTCPeerConnectionFactory(
|
|
@@ -71,9 +76,12 @@ final class WebRTCPublisher {
|
|
|
71
76
|
videoTrack = factory.videoTrack(with: videoSource, trackId: "simulator-video")
|
|
72
77
|
videoTrack.isEnabled = true
|
|
73
78
|
capturer = LKRTCVideoCapturer(delegate: videoSource)
|
|
79
|
+
let h264Status = h264WebRTCSupport.allowed
|
|
80
|
+
? "enabled(\(h264WebRTCSupport.probeSummary))"
|
|
81
|
+
: "disabled(\(h264WebRTCSupport.reason ?? "unsupported runtime"))"
|
|
74
82
|
print(
|
|
75
83
|
"[webrtc] Publisher ready (default codec factory + screen-cast video source) " +
|
|
76
|
-
"senderCodecs=\(senderCodecSummary())"
|
|
84
|
+
"h264=\(h264Status) senderCodecs=\(senderCodecSummary())"
|
|
77
85
|
)
|
|
78
86
|
}
|
|
79
87
|
|
|
@@ -135,7 +143,10 @@ final class WebRTCPublisher {
|
|
|
135
143
|
"active": session != nil,
|
|
136
144
|
"targetFps": maxFps,
|
|
137
145
|
"targetBitrate": targetBitrate,
|
|
146
|
+
"requestedCodec": requestedCodecName,
|
|
138
147
|
"selectedCodec": selectedCodecName,
|
|
148
|
+
"h264WebRTCEnabled": h264WebRTCSupport.allowed,
|
|
149
|
+
"h264WebRTCProbe": h264WebRTCSupport.probeSummary,
|
|
139
150
|
"frameMode": lastFrameMode,
|
|
140
151
|
"outputWidth": lastOutputWidth,
|
|
141
152
|
"outputHeight": lastOutputHeight,
|
|
@@ -159,6 +170,15 @@ final class WebRTCPublisher {
|
|
|
159
170
|
if let lastForwardedPixelFormat {
|
|
160
171
|
stats["forwardedPixelFormat"] = pixelFormatDescription(lastForwardedPixelFormat)
|
|
161
172
|
}
|
|
173
|
+
if let reason = h264WebRTCSupport.reason {
|
|
174
|
+
stats["h264WebRTCDisabledReason"] = reason
|
|
175
|
+
}
|
|
176
|
+
if let encoderID = h264WebRTCSupport.encoderID {
|
|
177
|
+
stats["h264VideoToolboxEncoderID"] = encoderID
|
|
178
|
+
}
|
|
179
|
+
if let usesHardware = h264WebRTCSupport.usesHardware {
|
|
180
|
+
stats["h264VideoToolboxUsesHardware"] = usesHardware
|
|
181
|
+
}
|
|
162
182
|
if let session {
|
|
163
183
|
stats["outboundRtp"] = session.delegate.outboundStatsSnapshot()
|
|
164
184
|
}
|
|
@@ -567,10 +587,22 @@ final class WebRTCPublisher {
|
|
|
567
587
|
}
|
|
568
588
|
|
|
569
589
|
private func applyVideoCodecPreference(_ codec: String?, to transceiver: LKRTCRtpTransceiver) {
|
|
570
|
-
let
|
|
590
|
+
let requestedName = Self.preferredVideoCodecName(codec)
|
|
591
|
+
requestedCodecName = requestedName
|
|
592
|
+
var preferredName = requestedName
|
|
593
|
+
if requestedName == "H264", !h264WebRTCSupport.allowed {
|
|
594
|
+
preferredName = "VP8"
|
|
595
|
+
print(
|
|
596
|
+
"[webrtc] H.264 requested but disabled (\(h264WebRTCSupport.reason ?? "unsupported runtime")); " +
|
|
597
|
+
"preferring VP8"
|
|
598
|
+
)
|
|
599
|
+
}
|
|
571
600
|
selectedCodecName = preferredName
|
|
572
601
|
let capabilities = factory.rtpSenderCapabilities(forKind: "video")
|
|
573
|
-
let
|
|
602
|
+
let usableCodecs = capabilities.codecs.filter { capability in
|
|
603
|
+
h264WebRTCSupport.allowed || !Self.codecCapability(capability, matches: "H264")
|
|
604
|
+
}
|
|
605
|
+
let preferredCodecs = usableCodecs.filter {
|
|
574
606
|
$0.name.caseInsensitiveCompare(preferredName) == .orderedSame ||
|
|
575
607
|
$0.mimeType.caseInsensitiveCompare("video/\(preferredName)") == .orderedSame
|
|
576
608
|
}
|
|
@@ -578,7 +610,7 @@ final class WebRTCPublisher {
|
|
|
578
610
|
print("[webrtc] No sender codec capability found for \(preferredName); using default order")
|
|
579
611
|
return
|
|
580
612
|
}
|
|
581
|
-
let remainingCodecs =
|
|
613
|
+
let remainingCodecs = usableCodecs.filter { capability in
|
|
582
614
|
!preferredCodecs.contains { $0 === capability }
|
|
583
615
|
}
|
|
584
616
|
let orderedCodecs = preferredCodecs + remainingCodecs
|
|
@@ -650,10 +682,258 @@ final class WebRTCPublisher {
|
|
|
650
682
|
}
|
|
651
683
|
}
|
|
652
684
|
|
|
685
|
+
private static func codecCapability(_ capability: LKRTCRtpCodecCapability, matches name: String) -> Bool {
|
|
686
|
+
capability.name.caseInsensitiveCompare(name) == .orderedSame ||
|
|
687
|
+
capability.mimeType.caseInsensitiveCompare("video/\(name)") == .orderedSame
|
|
688
|
+
}
|
|
689
|
+
|
|
653
690
|
private static func isBiPlanar420(_ pixelFormat: OSType) -> Bool {
|
|
654
691
|
pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ||
|
|
655
692
|
pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
|
656
693
|
}
|
|
694
|
+
|
|
695
|
+
private static func detectH264WebRTCSupport() -> WebRTCH264Support {
|
|
696
|
+
let environment = ProcessInfo.processInfo.environment
|
|
697
|
+
if envFlagEnabled(environment["SERVE_SIM_DISABLE_WEBRTC_H264"]) {
|
|
698
|
+
return WebRTCH264Support(
|
|
699
|
+
allowed: false,
|
|
700
|
+
reason: "disabled by SERVE_SIM_DISABLE_WEBRTC_H264",
|
|
701
|
+
encoderID: nil,
|
|
702
|
+
usesHardware: nil,
|
|
703
|
+
probeSummary: "disabled by environment"
|
|
704
|
+
)
|
|
705
|
+
}
|
|
706
|
+
if envFlagEnabled(environment["SERVE_SIM_ALLOW_VM_H264_WEBRTC"]) ||
|
|
707
|
+
envFlagEnabled(environment["SERVE_SIM_FORCE_WEBRTC_H264"]) {
|
|
708
|
+
return WebRTCH264Support(
|
|
709
|
+
allowed: true,
|
|
710
|
+
reason: nil,
|
|
711
|
+
encoderID: nil,
|
|
712
|
+
usesHardware: nil,
|
|
713
|
+
probeSummary: "forced by environment"
|
|
714
|
+
)
|
|
715
|
+
}
|
|
716
|
+
let probe = probeVideoToolboxH264Encoder()
|
|
717
|
+
if probe.encodedFrame {
|
|
718
|
+
return WebRTCH264Support(
|
|
719
|
+
allowed: true,
|
|
720
|
+
reason: nil,
|
|
721
|
+
encoderID: probe.encoderID,
|
|
722
|
+
usesHardware: probe.usesHardware,
|
|
723
|
+
probeSummary: probe.summary
|
|
724
|
+
)
|
|
725
|
+
}
|
|
726
|
+
let modelPrefix = sysctlString("hw.model").map { " on \($0)" } ?? ""
|
|
727
|
+
return WebRTCH264Support(
|
|
728
|
+
allowed: false,
|
|
729
|
+
reason: "VideoToolbox H.264 probe failed\(modelPrefix): \(probe.summary)",
|
|
730
|
+
encoderID: probe.encoderID,
|
|
731
|
+
usesHardware: probe.usesHardware,
|
|
732
|
+
probeSummary: probe.summary
|
|
733
|
+
)
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
private static func probeVideoToolboxH264Encoder() -> H264VideoToolboxProbe {
|
|
737
|
+
let width: Int32 = 64
|
|
738
|
+
let height: Int32 = 64
|
|
739
|
+
let attrs: [String: Any] = [
|
|
740
|
+
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
|
741
|
+
kCVPixelBufferWidthKey as String: Int(width),
|
|
742
|
+
kCVPixelBufferHeightKey as String: Int(height),
|
|
743
|
+
kCVPixelBufferIOSurfacePropertiesKey as String: [:],
|
|
744
|
+
]
|
|
745
|
+
var session: VTCompressionSession?
|
|
746
|
+
let createStatus = VTCompressionSessionCreate(
|
|
747
|
+
allocator: kCFAllocatorDefault,
|
|
748
|
+
width: width,
|
|
749
|
+
height: height,
|
|
750
|
+
codecType: kCMVideoCodecType_H264,
|
|
751
|
+
encoderSpecification: nil,
|
|
752
|
+
imageBufferAttributes: attrs as CFDictionary,
|
|
753
|
+
compressedDataAllocator: nil,
|
|
754
|
+
outputCallback: nil,
|
|
755
|
+
refcon: nil,
|
|
756
|
+
compressionSessionOut: &session
|
|
757
|
+
)
|
|
758
|
+
guard createStatus == noErr, let session else {
|
|
759
|
+
return H264VideoToolboxProbe(
|
|
760
|
+
encodedFrame: false,
|
|
761
|
+
encoderID: nil,
|
|
762
|
+
usesHardware: nil,
|
|
763
|
+
summary: "createStatus=\(createStatus)"
|
|
764
|
+
)
|
|
765
|
+
}
|
|
766
|
+
defer { VTCompressionSessionInvalidate(session) }
|
|
767
|
+
|
|
768
|
+
_ = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue)
|
|
769
|
+
_ = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_Baseline_AutoLevel)
|
|
770
|
+
_ = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AllowFrameReordering, value: kCFBooleanFalse)
|
|
771
|
+
_ = VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ExpectedFrameRate, value: NSNumber(value: 30))
|
|
772
|
+
|
|
773
|
+
let prepareStatus = VTCompressionSessionPrepareToEncodeFrames(session)
|
|
774
|
+
let encoderID = vtSessionStringProperty(session, key: kVTCompressionPropertyKey_EncoderID)
|
|
775
|
+
let usesHardware = inferredHardwareAcceleration(
|
|
776
|
+
encoderID: encoderID,
|
|
777
|
+
reported: vtSessionBoolProperty(session, key: kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder)
|
|
778
|
+
)
|
|
779
|
+
guard prepareStatus == noErr else {
|
|
780
|
+
return H264VideoToolboxProbe(
|
|
781
|
+
encodedFrame: false,
|
|
782
|
+
encoderID: encoderID,
|
|
783
|
+
usesHardware: usesHardware,
|
|
784
|
+
summary: "encoderID=\(encoderID ?? "unknown") prepareStatus=\(prepareStatus)"
|
|
785
|
+
)
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
guard let pixelBuffer = makeH264ProbePixelBuffer(width: Int(width), height: Int(height)) else {
|
|
789
|
+
return H264VideoToolboxProbe(
|
|
790
|
+
encodedFrame: false,
|
|
791
|
+
encoderID: encoderID,
|
|
792
|
+
usesHardware: usesHardware,
|
|
793
|
+
summary: "encoderID=\(encoderID ?? "unknown") pixelBufferAllocationFailed"
|
|
794
|
+
)
|
|
795
|
+
}
|
|
796
|
+
let semaphore = DispatchSemaphore(value: 0)
|
|
797
|
+
var callbackStatus: OSStatus?
|
|
798
|
+
var producedSample = false
|
|
799
|
+
let encodeStatus = VTCompressionSessionEncodeFrame(
|
|
800
|
+
session,
|
|
801
|
+
imageBuffer: pixelBuffer,
|
|
802
|
+
presentationTimeStamp: CMTime(value: 0, timescale: 30),
|
|
803
|
+
duration: CMTime(value: 1, timescale: 30),
|
|
804
|
+
frameProperties: nil,
|
|
805
|
+
infoFlagsOut: nil
|
|
806
|
+
) { status, _, sampleBuffer in
|
|
807
|
+
callbackStatus = status
|
|
808
|
+
producedSample = status == noErr && sampleBuffer.map(CMSampleBufferDataIsReady) == true
|
|
809
|
+
semaphore.signal()
|
|
810
|
+
}
|
|
811
|
+
let completeStatus = VTCompressionSessionCompleteFrames(session, untilPresentationTimeStamp: .invalid)
|
|
812
|
+
let completed = semaphore.wait(timeout: .now() + .milliseconds(750)) == .success
|
|
813
|
+
let encodedFrame = encodeStatus == noErr &&
|
|
814
|
+
completeStatus == noErr &&
|
|
815
|
+
completed &&
|
|
816
|
+
callbackStatus == noErr &&
|
|
817
|
+
producedSample
|
|
818
|
+
let hardwareSummary = usesHardware.map { "hardware=\($0)" } ?? "hardware=unknown"
|
|
819
|
+
return H264VideoToolboxProbe(
|
|
820
|
+
encodedFrame: encodedFrame,
|
|
821
|
+
encoderID: encoderID,
|
|
822
|
+
usesHardware: usesHardware,
|
|
823
|
+
summary: "encoderID=\(encoderID ?? "unknown") \(hardwareSummary) " +
|
|
824
|
+
"encodeStatus=\(encodeStatus) completeStatus=\(completeStatus) " +
|
|
825
|
+
"callbackStatus=\(callbackStatus.map(String.init) ?? "missing") " +
|
|
826
|
+
"sample=\(producedSample)"
|
|
827
|
+
)
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
private static func makeH264ProbePixelBuffer(width: Int, height: Int) -> CVPixelBuffer? {
|
|
831
|
+
let attrs: [String: Any] = [
|
|
832
|
+
kCVPixelBufferIOSurfacePropertiesKey as String: [:],
|
|
833
|
+
]
|
|
834
|
+
var pixelBuffer: CVPixelBuffer?
|
|
835
|
+
let status = CVPixelBufferCreate(
|
|
836
|
+
kCFAllocatorDefault,
|
|
837
|
+
width,
|
|
838
|
+
height,
|
|
839
|
+
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
|
|
840
|
+
attrs as CFDictionary,
|
|
841
|
+
&pixelBuffer
|
|
842
|
+
)
|
|
843
|
+
guard status == kCVReturnSuccess, let pixelBuffer else { return nil }
|
|
844
|
+
CVPixelBufferLockBaseAddress(pixelBuffer, [])
|
|
845
|
+
defer { CVPixelBufferUnlockBaseAddress(pixelBuffer, []) }
|
|
846
|
+
guard
|
|
847
|
+
let yAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0),
|
|
848
|
+
let cbCrAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)
|
|
849
|
+
else {
|
|
850
|
+
return nil
|
|
851
|
+
}
|
|
852
|
+
let yStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
|
|
853
|
+
let cbCrStride = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
|
|
854
|
+
let yPointer = yAddress.assumingMemoryBound(to: UInt8.self)
|
|
855
|
+
let cbCrPointer = cbCrAddress.assumingMemoryBound(to: UInt8.self)
|
|
856
|
+
for row in 0..<height {
|
|
857
|
+
let rowPointer = yPointer.advanced(by: row * yStride)
|
|
858
|
+
for column in 0..<width {
|
|
859
|
+
rowPointer[column] = UInt8((row + column) & 0xff)
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
for row in 0..<(height / 2) {
|
|
863
|
+
let rowPointer = cbCrPointer.advanced(by: row * cbCrStride)
|
|
864
|
+
for column in stride(from: 0, to: width, by: 2) {
|
|
865
|
+
rowPointer[column] = 128
|
|
866
|
+
rowPointer[column + 1] = 128
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return pixelBuffer
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
private static func vtSessionStringProperty(_ session: VTCompressionSession, key: CFString) -> String? {
|
|
873
|
+
var value: CFTypeRef?
|
|
874
|
+
let status = withUnsafeMutablePointer(to: &value) { pointer in
|
|
875
|
+
VTSessionCopyProperty(session, key: key, allocator: kCFAllocatorDefault, valueOut: pointer)
|
|
876
|
+
}
|
|
877
|
+
guard status == noErr, let value else { return nil }
|
|
878
|
+
return String(describing: value)
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
private static func vtSessionBoolProperty(_ session: VTCompressionSession, key: CFString) -> Bool? {
|
|
882
|
+
var value: CFTypeRef?
|
|
883
|
+
let status = withUnsafeMutablePointer(to: &value) { pointer in
|
|
884
|
+
VTSessionCopyProperty(session, key: key, allocator: kCFAllocatorDefault, valueOut: pointer)
|
|
885
|
+
}
|
|
886
|
+
guard status == noErr, let value else { return nil }
|
|
887
|
+
if CFGetTypeID(value) == CFBooleanGetTypeID() {
|
|
888
|
+
return CFBooleanGetValue((value as! CFBoolean))
|
|
889
|
+
}
|
|
890
|
+
return (value as? NSNumber)?.boolValue
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
private static func inferredHardwareAcceleration(encoderID: String?, reported: Bool?) -> Bool? {
|
|
894
|
+
if let reported { return reported }
|
|
895
|
+
guard let encoderID else { return nil }
|
|
896
|
+
let normalized = encoderID.lowercased()
|
|
897
|
+
if normalized.contains("paravirtualized") || normalized.contains(".ave.") {
|
|
898
|
+
return true
|
|
899
|
+
}
|
|
900
|
+
if normalized.contains("com.apple.videotoolbox.videoencoder.h264") {
|
|
901
|
+
return false
|
|
902
|
+
}
|
|
903
|
+
return nil
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
private static func envFlagEnabled(_ value: String?) -> Bool {
|
|
907
|
+
switch value?.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() {
|
|
908
|
+
case "1", "true", "yes", "on":
|
|
909
|
+
return true
|
|
910
|
+
default:
|
|
911
|
+
return false
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
private static func sysctlString(_ name: String) -> String? {
|
|
916
|
+
var size = 0
|
|
917
|
+
guard sysctlbyname(name, nil, &size, nil, 0) == 0, size > 1 else { return nil }
|
|
918
|
+
var buffer = [CChar](repeating: 0, count: size)
|
|
919
|
+
guard sysctlbyname(name, &buffer, &size, nil, 0) == 0 else { return nil }
|
|
920
|
+
return String(cString: buffer)
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
private struct WebRTCH264Support {
|
|
925
|
+
let allowed: Bool
|
|
926
|
+
let reason: String?
|
|
927
|
+
let encoderID: String?
|
|
928
|
+
let usesHardware: Bool?
|
|
929
|
+
let probeSummary: String
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
private struct H264VideoToolboxProbe {
|
|
933
|
+
let encodedFrame: Bool
|
|
934
|
+
let encoderID: String?
|
|
935
|
+
let usesHardware: Bool?
|
|
936
|
+
let summary: String
|
|
657
937
|
}
|
|
658
938
|
|
|
659
939
|
private final class H264WebRTCPixelBufferConverter {
|
|
@@ -3,15 +3,11 @@
|
|
|
3
3
|
# spawned serve-sim-bin helper. The JS bindings are written in Swift with
|
|
4
4
|
# node-swift (see ../../Package.swift and sim-module.swift).
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# NodeAPIMacros" / "unable to resolve module SwiftSyntax"). Cross-compiling the
|
|
12
|
-
# x86_64 slice would therefore require an x86_64-native toolchain (Rosetta),
|
|
13
|
-
# which isn't available everywhere; serve-sim targets Apple Silicon. napi_* stay
|
|
14
|
-
# undefined and resolve against the host (Node/Bun) at dlopen via
|
|
6
|
+
# We opt into the new `swiftbuild` build system, because it supports building universal
|
|
7
|
+
# binaries with macros, which neither the legacy `native` build system nor the
|
|
8
|
+
# perennially-janky legacy `xcode` build system had support for.
|
|
9
|
+
#
|
|
10
|
+
# napi_* stay undefined and resolve against the host (Node/Bun) at dlopen via
|
|
15
11
|
# `-undefined dynamic_lookup`.
|
|
16
12
|
set -euo pipefail
|
|
17
13
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
@@ -26,25 +22,23 @@ if [ ! -d "$PKG/node_modules/node-swift" ]; then
|
|
|
26
22
|
exit 1
|
|
27
23
|
fi
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
-c release
|
|
31
|
-
--product "$PRODUCT"
|
|
32
|
-
--package-path "$PKG"
|
|
33
|
-
--build-path "$BUILD_DIR"
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
DYLIB="$(find "$BUILD_DIR" -name "lib${PRODUCT}.dylib" -type f -not -path '*.dSYM*' -print -quit)"
|
|
41
|
-
if [ -z "$DYLIB" ]; then
|
|
42
|
-
echo "Build succeeded but lib${PRODUCT}.dylib was not found under $BUILD_DIR" >&2
|
|
25
|
+
build_flags=(
|
|
26
|
+
-c release
|
|
27
|
+
--product "$PRODUCT"
|
|
28
|
+
--package-path "$PKG"
|
|
29
|
+
--build-path "$BUILD_DIR"
|
|
30
|
+
--build-system swiftbuild
|
|
31
|
+
)
|
|
32
|
+
swift build "${build_flags[@]}" >&2
|
|
33
|
+
DYLIB="$(swift build --show-bin-path "${build_flags[@]}")/lib${PRODUCT}.dylib"
|
|
34
|
+
if [ ! -f "$DYLIB" ]; then
|
|
35
|
+
echo "Expected build product not found at $DYLIB" >&2
|
|
43
36
|
exit 1
|
|
44
37
|
fi
|
|
45
38
|
|
|
46
39
|
OUT="$OUT_DIR/${PRODUCT}.node"
|
|
47
|
-
cp "$DYLIB" "$OUT"
|
|
40
|
+
cp -a "$DYLIB" "$OUT"
|
|
41
|
+
strip -x "$OUT"
|
|
48
42
|
install_name_tool \
|
|
49
43
|
-change "@rpath/LiveKitWebRTC.framework/LiveKitWebRTC" \
|
|
50
44
|
"@loader_path/../../bin/LiveKitWebRTC.framework/Versions/A/LiveKitWebRTC" \
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
NS_ASSUME_NONNULL_BEGIN
|
|
21
21
|
|
|
22
22
|
RTC_OBJC_EXPORT
|
|
23
|
-
@interface RTC_OBJC_TYPE (RTCAudioProcessingConfig) : NSObject
|
|
23
|
+
@interface RTC_OBJC_TYPE (RTCAudioProcessingConfig) : NSObject
|
|
24
24
|
|
|
25
25
|
@property(nonatomic, assign) BOOL isEchoCancellationEnabled;
|
|
26
26
|
@property(nonatomic, assign) BOOL isEchoCancellationMobileMode;
|
|
@@ -60,7 +60,7 @@ RTC_OBJC_EXPORT
|
|
|
60
60
|
uncryptedMagicBytes:(nullable NSData *)uncryptedMagicBytes
|
|
61
61
|
failureTolerance:(int)failureTolerance
|
|
62
62
|
keyRingSize:(int)keyRingSize
|
|
63
|
-
discardFrameWhenCryptorNotReady:(BOOL)discardFrameWhenCryptorNotReady;
|
|
63
|
+
discardFrameWhenCryptorNotReady:(BOOL)discardFrameWhenCryptorNotReady;
|
|
64
64
|
|
|
65
65
|
- (instancetype)initWithRatchetSalt:(NSData *)salt
|
|
66
66
|
ratchetWindowSize:(int)windowSize
|