@livedesk/client 0.1.94 → 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,6 +168,13 @@ 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
|
|
@@ -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,6 +168,13 @@ 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
|