@livedesk/client 0.1.93 → 0.1.95
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.
|
@@ -17,7 +17,9 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
17
17
|
private let writeLock = NSLock()
|
|
18
18
|
private var stream: SCStream?
|
|
19
19
|
private var compressionSession: VTCompressionSession?
|
|
20
|
+
private var sampleCount: Int = 0
|
|
20
21
|
private var frameCount: Int = 0
|
|
22
|
+
private var skippedCount: Int = 0
|
|
21
23
|
private var encodedCount: Int = 0
|
|
22
24
|
|
|
23
25
|
init(displayIndex: Int, width: Int, height: Int, fps: Int, bitrateKbps: Int) {
|
|
@@ -52,7 +54,7 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
52
54
|
configuration.width = width
|
|
53
55
|
configuration.height = height
|
|
54
56
|
configuration.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(fps))
|
|
55
|
-
configuration.queueDepth =
|
|
57
|
+
configuration.queueDepth = 3
|
|
56
58
|
configuration.showsCursor = false
|
|
57
59
|
configuration.pixelFormat = kCVPixelFormatType_32BGRA
|
|
58
60
|
|
|
@@ -69,12 +71,15 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
69
71
|
|
|
70
72
|
private func createCompressionSession() throws {
|
|
71
73
|
var session: VTCompressionSession?
|
|
74
|
+
let encoderSpecification: [CFString: Any] = [
|
|
75
|
+
kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder: true
|
|
76
|
+
]
|
|
72
77
|
let status = VTCompressionSessionCreate(
|
|
73
78
|
allocator: kCFAllocatorDefault,
|
|
74
79
|
width: Int32(width),
|
|
75
80
|
height: Int32(height),
|
|
76
81
|
codecType: kCMVideoCodecType_H264,
|
|
77
|
-
encoderSpecification:
|
|
82
|
+
encoderSpecification: encoderSpecification as CFDictionary,
|
|
78
83
|
imageBufferAttributes: nil,
|
|
79
84
|
compressedDataAllocator: nil,
|
|
80
85
|
outputCallback: Self.compressionCallback,
|
|
@@ -87,6 +92,7 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
87
92
|
|
|
88
93
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue)
|
|
89
94
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AllowFrameReordering, value: kCFBooleanFalse)
|
|
95
|
+
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxFrameDelayCount, value: NSNumber(value: 1))
|
|
90
96
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_Baseline_AutoLevel)
|
|
91
97
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameInterval, value: NSNumber(value: fps))
|
|
92
98
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, value: NSNumber(value: 1.0))
|
|
@@ -111,12 +117,18 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
111
117
|
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
|
|
112
118
|
guard type == .screen,
|
|
113
119
|
CMSampleBufferDataIsReady(sampleBuffer),
|
|
114
|
-
isCompleteFrame(sampleBuffer),
|
|
115
120
|
let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
|
|
116
121
|
let session = compressionSession else {
|
|
117
122
|
return
|
|
118
123
|
}
|
|
119
124
|
|
|
125
|
+
sampleCount += 1
|
|
126
|
+
guard isCompleteFrame(sampleBuffer) else {
|
|
127
|
+
skippedCount += 1
|
|
128
|
+
logCaptureProgressIfNeeded()
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
frameCount += 1
|
|
121
133
|
let pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
|
|
122
134
|
let duration = CMTime(value: 1, timescale: CMTimeScale(fps))
|
|
@@ -139,6 +151,7 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
139
151
|
if status != noErr && (frameCount == 1 || frameCount % 120 == 0) {
|
|
140
152
|
fputs("LiveDeskSCK encode status=\(status) frames=\(frameCount)\n", stderr)
|
|
141
153
|
}
|
|
154
|
+
logCaptureProgressIfNeeded()
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
func stream(_ stream: SCStream, didStopWithError error: Error) {
|
|
@@ -155,12 +168,20 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
155
168
|
return rawStatus == SCFrameStatus.complete.rawValue
|
|
156
169
|
}
|
|
157
170
|
|
|
171
|
+
private func logCaptureProgressIfNeeded() {
|
|
172
|
+
let interval = max(1, fps * 5)
|
|
173
|
+
if sampleCount == 1 || sampleCount % interval == 0 {
|
|
174
|
+
fputs("LiveDeskSCK capture samples=\(sampleCount) complete=\(frameCount) skipped=\(skippedCount)\n", stderr)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
158
178
|
private func writeEncodedSampleBuffer(_ sampleBuffer: CMSampleBuffer) {
|
|
159
179
|
guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else {
|
|
160
180
|
return
|
|
161
181
|
}
|
|
162
182
|
|
|
163
183
|
var output = Data()
|
|
184
|
+
appendAccessUnitDelimiter(to: &output)
|
|
164
185
|
if isKeyFrame(sampleBuffer), let formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer) {
|
|
165
186
|
appendParameterSet(formatDescription, index: 0, to: &output)
|
|
166
187
|
appendParameterSet(formatDescription, index: 1, to: &output)
|
|
@@ -241,6 +262,11 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
241
262
|
private func appendStartCode(to output: inout Data) {
|
|
242
263
|
output.append(contentsOf: [0, 0, 0, 1])
|
|
243
264
|
}
|
|
265
|
+
|
|
266
|
+
private func appendAccessUnitDelimiter(to output: inout Data) {
|
|
267
|
+
appendStartCode(to: &output)
|
|
268
|
+
output.append(contentsOf: [0x09, 0xF0])
|
|
269
|
+
}
|
|
244
270
|
}
|
|
245
271
|
|
|
246
272
|
enum LiveDeskSCKError: Error {
|
|
@@ -17,7 +17,9 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
17
17
|
private let writeLock = NSLock()
|
|
18
18
|
private var stream: SCStream?
|
|
19
19
|
private var compressionSession: VTCompressionSession?
|
|
20
|
+
private var sampleCount: Int = 0
|
|
20
21
|
private var frameCount: Int = 0
|
|
22
|
+
private var skippedCount: Int = 0
|
|
21
23
|
private var encodedCount: Int = 0
|
|
22
24
|
|
|
23
25
|
init(displayIndex: Int, width: Int, height: Int, fps: Int, bitrateKbps: Int) {
|
|
@@ -52,7 +54,7 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
52
54
|
configuration.width = width
|
|
53
55
|
configuration.height = height
|
|
54
56
|
configuration.minimumFrameInterval = CMTime(value: 1, timescale: CMTimeScale(fps))
|
|
55
|
-
configuration.queueDepth =
|
|
57
|
+
configuration.queueDepth = 3
|
|
56
58
|
configuration.showsCursor = false
|
|
57
59
|
configuration.pixelFormat = kCVPixelFormatType_32BGRA
|
|
58
60
|
|
|
@@ -69,12 +71,15 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
69
71
|
|
|
70
72
|
private func createCompressionSession() throws {
|
|
71
73
|
var session: VTCompressionSession?
|
|
74
|
+
let encoderSpecification: [CFString: Any] = [
|
|
75
|
+
kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder: true
|
|
76
|
+
]
|
|
72
77
|
let status = VTCompressionSessionCreate(
|
|
73
78
|
allocator: kCFAllocatorDefault,
|
|
74
79
|
width: Int32(width),
|
|
75
80
|
height: Int32(height),
|
|
76
81
|
codecType: kCMVideoCodecType_H264,
|
|
77
|
-
encoderSpecification:
|
|
82
|
+
encoderSpecification: encoderSpecification as CFDictionary,
|
|
78
83
|
imageBufferAttributes: nil,
|
|
79
84
|
compressedDataAllocator: nil,
|
|
80
85
|
outputCallback: Self.compressionCallback,
|
|
@@ -87,6 +92,7 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
87
92
|
|
|
88
93
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue)
|
|
89
94
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AllowFrameReordering, value: kCFBooleanFalse)
|
|
95
|
+
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxFrameDelayCount, value: NSNumber(value: 1))
|
|
90
96
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_Baseline_AutoLevel)
|
|
91
97
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameInterval, value: NSNumber(value: fps))
|
|
92
98
|
VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, value: NSNumber(value: 1.0))
|
|
@@ -111,12 +117,18 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
111
117
|
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
|
|
112
118
|
guard type == .screen,
|
|
113
119
|
CMSampleBufferDataIsReady(sampleBuffer),
|
|
114
|
-
isCompleteFrame(sampleBuffer),
|
|
115
120
|
let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer),
|
|
116
121
|
let session = compressionSession else {
|
|
117
122
|
return
|
|
118
123
|
}
|
|
119
124
|
|
|
125
|
+
sampleCount += 1
|
|
126
|
+
guard isCompleteFrame(sampleBuffer) else {
|
|
127
|
+
skippedCount += 1
|
|
128
|
+
logCaptureProgressIfNeeded()
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
frameCount += 1
|
|
121
133
|
let pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
|
|
122
134
|
let duration = CMTime(value: 1, timescale: CMTimeScale(fps))
|
|
@@ -139,6 +151,7 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
139
151
|
if status != noErr && (frameCount == 1 || frameCount % 120 == 0) {
|
|
140
152
|
fputs("LiveDeskSCK encode status=\(status) frames=\(frameCount)\n", stderr)
|
|
141
153
|
}
|
|
154
|
+
logCaptureProgressIfNeeded()
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
func stream(_ stream: SCStream, didStopWithError error: Error) {
|
|
@@ -155,12 +168,20 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
155
168
|
return rawStatus == SCFrameStatus.complete.rawValue
|
|
156
169
|
}
|
|
157
170
|
|
|
171
|
+
private func logCaptureProgressIfNeeded() {
|
|
172
|
+
let interval = max(1, fps * 5)
|
|
173
|
+
if sampleCount == 1 || sampleCount % interval == 0 {
|
|
174
|
+
fputs("LiveDeskSCK capture samples=\(sampleCount) complete=\(frameCount) skipped=\(skippedCount)\n", stderr)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
158
178
|
private func writeEncodedSampleBuffer(_ sampleBuffer: CMSampleBuffer) {
|
|
159
179
|
guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else {
|
|
160
180
|
return
|
|
161
181
|
}
|
|
162
182
|
|
|
163
183
|
var output = Data()
|
|
184
|
+
appendAccessUnitDelimiter(to: &output)
|
|
164
185
|
if isKeyFrame(sampleBuffer), let formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer) {
|
|
165
186
|
appendParameterSet(formatDescription, index: 0, to: &output)
|
|
166
187
|
appendParameterSet(formatDescription, index: 1, to: &output)
|
|
@@ -241,6 +262,11 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
|
|
|
241
262
|
private func appendStartCode(to output: inout Data) {
|
|
242
263
|
output.append(contentsOf: [0, 0, 0, 1])
|
|
243
264
|
}
|
|
265
|
+
|
|
266
|
+
private func appendAccessUnitDelimiter(to output: inout Data) {
|
|
267
|
+
appendStartCode(to: &output)
|
|
268
|
+
output.append(contentsOf: [0x09, 0xF0])
|
|
269
|
+
}
|
|
244
270
|
}
|
|
245
271
|
|
|
246
272
|
enum LiveDeskSCKError: Error {
|