@livedesk/client 0.1.91 → 0.1.93

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/README.md CHANGED
@@ -39,8 +39,11 @@ the matching callback URL to Supabase Auth redirect URLs.
39
39
  The client registers the device, sends status heartbeats, can return
40
40
  Hub-requested thumbnails, can stream a focused view-only live screen, and
41
41
  can receive safe task-only instructions. The C# RemoteFast engine also supports
42
- keyboard/mouse control, file transfer, and Windows system-audio loopback
43
- playback. When file transfer is enabled by the Hub, received files are saved
42
+ keyboard/mouse control on Windows, macOS, and Linux/X11, file transfer, and
43
+ Windows system-audio loopback playback. macOS control requires Accessibility
44
+ permission for the terminal app that started LiveDesk; screen streaming requires
45
+ Screen Recording permission. LiveDesk opens the matching System Settings page
46
+ only when that permission is first needed. When file transfer is enabled by the Hub, received files are saved
44
47
  to `Desktop/LiveDeskFiles` unless the Hub sets another destination folder.
45
48
 
46
49
  By default, the launcher uses the packaged C# RemoteFast engine when supported.
@@ -70,7 +73,7 @@ Frame pipeline roadmap:
70
73
 
71
74
  - Mode 1: `mode1-jpeg` - current test path using screen capture, resize, and JPEG binary frames.
72
75
  - Mode 2: `mode2-lz4` - Windows Fast path using raw BGRA frames compressed as an LZ4 block.
73
- - Mode 3: `mode3-h264-hw` - OS-specific ffmpeg hardware H.264 path. Windows tries Media Foundation/NVENC/QSV/AMF, macOS tries VideoToolbox, and Linux tries NVENC/VAAPI/QSV. The launcher uses the bundled ffmpeg first, unless `LIVEDESK_FFMPEG` points to a custom binary. On macOS, set `LIVEDESK_FFMPEG_AVFOUNDATION_INPUT` if your screen input is not `1:none`.
76
+ - Mode 3: `mode3-h264-hw` - OS-specific hardware H.264 path. Windows tries Media Foundation/NVENC/QSV/AMF, macOS prefers ScreenCaptureKit plus VideoToolbox, and Linux tries NVENC/VAAPI/QSV. macOS emits one startup key frame and then an approximately one-second GOP. The launcher uses bundled ffmpeg for fallback paths unless `LIVEDESK_FFMPEG` points to a custom binary. On macOS, set `LIVEDESK_FFMPEG_AVFOUNDATION_INPUT` only when the ScreenCaptureKit helper is unavailable and the AVFoundation fallback input is not `1:none`.
74
77
 
75
78
  Legacy mode names such as `remote-fast` and `remote-quality` are treated as
76
79
  Mode 1 aliases.
@@ -37,7 +37,16 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
37
37
  throw LiveDeskSCKError.noDisplays
38
38
  }
39
39
 
40
- let display = shareableContent.displays[min(displayIndex, shareableContent.displays.count - 1)]
40
+ let mainDisplayID = CGMainDisplayID()
41
+ let orderedDisplays = shareableContent.displays.sorted { left, right in
42
+ let leftIsMain = left.displayID == mainDisplayID
43
+ let rightIsMain = right.displayID == mainDisplayID
44
+ if leftIsMain != rightIsMain {
45
+ return leftIsMain
46
+ }
47
+ return left.displayID < right.displayID
48
+ }
49
+ let display = orderedDisplays[min(displayIndex, orderedDisplays.count - 1)]
41
50
  let filter = SCContentFilter(display: display, excludingWindows: [])
42
51
  let configuration = SCStreamConfiguration()
43
52
  configuration.width = width
@@ -79,8 +88,8 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
79
88
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue)
80
89
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AllowFrameReordering, value: kCFBooleanFalse)
81
90
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_Baseline_AutoLevel)
82
- VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameInterval, value: NSNumber(value: 1))
83
- VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, value: NSNumber(value: max(0.001, 1.0 / Double(fps))))
91
+ VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameInterval, value: NSNumber(value: fps))
92
+ VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, value: NSNumber(value: 1.0))
84
93
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ExpectedFrameRate, value: NSNumber(value: fps))
85
94
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AverageBitRate, value: NSNumber(value: bitrateKbps * 1000))
86
95
  VTCompressionSessionPrepareToEncodeFrames(session)
@@ -111,8 +120,12 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
111
120
  frameCount += 1
112
121
  let pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
113
122
  let duration = CMTime(value: 1, timescale: CMTimeScale(fps))
114
- let frameProperties = NSMutableDictionary()
115
- frameProperties[kVTEncodeFrameOptionKey_ForceKeyFrame] = kCFBooleanTrue
123
+ var frameProperties: CFDictionary?
124
+ if frameCount == 1 {
125
+ let properties = NSMutableDictionary()
126
+ properties[kVTEncodeFrameOptionKey_ForceKeyFrame] = kCFBooleanTrue
127
+ frameProperties = properties
128
+ }
116
129
 
117
130
  let status = VTCompressionSessionEncodeFrame(
118
131
  session,
@@ -37,7 +37,16 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
37
37
  throw LiveDeskSCKError.noDisplays
38
38
  }
39
39
 
40
- let display = shareableContent.displays[min(displayIndex, shareableContent.displays.count - 1)]
40
+ let mainDisplayID = CGMainDisplayID()
41
+ let orderedDisplays = shareableContent.displays.sorted { left, right in
42
+ let leftIsMain = left.displayID == mainDisplayID
43
+ let rightIsMain = right.displayID == mainDisplayID
44
+ if leftIsMain != rightIsMain {
45
+ return leftIsMain
46
+ }
47
+ return left.displayID < right.displayID
48
+ }
49
+ let display = orderedDisplays[min(displayIndex, orderedDisplays.count - 1)]
41
50
  let filter = SCContentFilter(display: display, excludingWindows: [])
42
51
  let configuration = SCStreamConfiguration()
43
52
  configuration.width = width
@@ -79,8 +88,8 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
79
88
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_RealTime, value: kCFBooleanTrue)
80
89
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AllowFrameReordering, value: kCFBooleanFalse)
81
90
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ProfileLevel, value: kVTProfileLevel_H264_Baseline_AutoLevel)
82
- VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameInterval, value: NSNumber(value: 1))
83
- VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, value: NSNumber(value: max(0.001, 1.0 / Double(fps))))
91
+ VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameInterval, value: NSNumber(value: fps))
92
+ VTSessionSetProperty(session, key: kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration, value: NSNumber(value: 1.0))
84
93
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_ExpectedFrameRate, value: NSNumber(value: fps))
85
94
  VTSessionSetProperty(session, key: kVTCompressionPropertyKey_AverageBitRate, value: NSNumber(value: bitrateKbps * 1000))
86
95
  VTCompressionSessionPrepareToEncodeFrames(session)
@@ -111,8 +120,12 @@ final class LiveDeskScreenCaptureKitEncoder: NSObject, SCStreamOutput, SCStreamD
111
120
  frameCount += 1
112
121
  let pts = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
113
122
  let duration = CMTime(value: 1, timescale: CMTimeScale(fps))
114
- let frameProperties = NSMutableDictionary()
115
- frameProperties[kVTEncodeFrameOptionKey_ForceKeyFrame] = kCFBooleanTrue
123
+ var frameProperties: CFDictionary?
124
+ if frameCount == 1 {
125
+ let properties = NSMutableDictionary()
126
+ properties[kVTEncodeFrameOptionKey_ForceKeyFrame] = kCFBooleanTrue
127
+ frameProperties = properties
128
+ }
116
129
 
117
130
  let status = VTCompressionSessionEncodeFrame(
118
131
  session,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.91",
3
+ "version": "0.1.93",
4
4
  "description": "LiveDesk local remote client",
5
5
  "type": "module",
6
6
  "bin": {