From 5bbdb76d21045b40e71accdd407b2d9e3766b801 Mon Sep 17 00:00:00 2001 From: Acaki Date: Mon, 1 Dec 2025 16:38:47 +0800 Subject: [PATCH 1/5] fix: workaround for Metal AV1 darker image --- VoidLink/Stream/VideoDecoderRenderer.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/VoidLink/Stream/VideoDecoderRenderer.m b/VoidLink/Stream/VideoDecoderRenderer.m index 47ea99e8..5ab11b26 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.m +++ b/VoidLink/Stream/VideoDecoderRenderer.m @@ -629,7 +629,8 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); } Log(LOG_I, @"AV1 video range: %@", seqHeader->color_config.color_range == 1 ? @"full" : @"limited"); - SET_EXTENSION(kCMFormatDescriptionExtension_FullRangeVideo, @(seqHeader->color_config.color_range == 1)); + // Force full range AV1 video for now, as sunshine incorrect reports limited range + SET_EXTENSION(kCMFormatDescriptionExtension_FullRangeVideo, @YES); // Progressive content SET_EXTENSION(kCMFormatDescriptionExtension_FieldCount, @(1)); From 0ad6b13fae13fcc41c505d86c36cf5c71df14400 Mon Sep 17 00:00:00 2001 From: Acaki Date: Mon, 1 Dec 2025 17:12:41 +0800 Subject: [PATCH 2/5] feat: add keyboard shortcut to disconnect & quit app --- VoidLink/Input/StreamView.h | 1 + VoidLink/Input/StreamView.m | 3 +++ 2 files changed, 4 insertions(+) diff --git a/VoidLink/Input/StreamView.h b/VoidLink/Input/StreamView.h index 5162d91c..8bbc1abf 100644 --- a/VoidLink/Input/StreamView.h +++ b/VoidLink/Input/StreamView.h @@ -24,6 +24,7 @@ - (void)toggleStatsOverlay; - (void)toggleMouseCapture; - (void)toggleMouseVisible; +- (void)disconnectAndQuitApp; @end diff --git a/VoidLink/Input/StreamView.m b/VoidLink/Input/StreamView.m index 67420196..636283f1 100644 --- a/VoidLink/Input/StreamView.m +++ b/VoidLink/Input/StreamView.m @@ -960,6 +960,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5; case UIKeyboardHIDUsageKeyboardC: [interactionDelegate toggleMouseVisible]; break; + case UIKeyboardHIDUsageKeyboardD: + [interactionDelegate disconnectAndQuitApp]; + break; default: break; } From 46b5d9522dcb69d00558e21a3ab396f8adad3d87 Mon Sep 17 00:00:00 2001 From: Acaki Date: Tue, 2 Dec 2025 02:40:03 +0800 Subject: [PATCH 3/5] refactor: force limited range for AV1 only --- VoidLink/Stream/VideoDecoderRenderer.m | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/VoidLink/Stream/VideoDecoderRenderer.m b/VoidLink/Stream/VideoDecoderRenderer.m index 5ab11b26..f51babac 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.m +++ b/VoidLink/Stream/VideoDecoderRenderer.m @@ -221,10 +221,18 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, } mutableCopy]; #else NSNumber *pixelFormat = nil; - if (self->_videoFormat & VIDEO_FORMAT_MASK_YUV444) { + + // Uses limited range for AV1 as full range doesn't work well on all devices + if (self->_videoFormat & VIDEO_FORMAT_MASK_AV1) { + if (self->_videoFormat & VIDEO_FORMAT_MASK_YUV444) { + pixelFormat = @(kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange); + } else { + pixelFormat = @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); + } + Log(LOG_I, @"Using VideoRange pixel format for AV1 (workaround for M3 decoder bug)"); + } else if (self->_videoFormat & VIDEO_FORMAT_MASK_YUV444) { pixelFormat = @(kCVPixelFormatType_444YpCbCr10BiPlanarFullRange); - } - else { + } else { pixelFormat = @(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange); } NSMutableDictionary *destinationPixelBufferAttributes = [@{ @@ -628,9 +636,12 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); break; } - Log(LOG_I, @"AV1 video range: %@", seqHeader->color_config.color_range == 1 ? @"full" : @"limited"); - // Force full range AV1 video for now, as sunshine incorrect reports limited range - SET_EXTENSION(kCMFormatDescriptionExtension_FullRangeVideo, @YES); + Log(LOG_I, @"AV1 video range reported by stream: %@", seqHeader->color_config.color_range == 1 ? @"full" : @"limited"); + + // Forces limited range for AV1 as full range doesn't work well on all devices + SET_EXTENSION(kCMFormatDescriptionExtension_FullRangeVideo, @NO); + + Log(LOG_I, @"AV1 using limited range end-to-end"); // Progressive content SET_EXTENSION(kCMFormatDescriptionExtension_FieldCount, @(1)); From 85611bcab5cf8615848a9c39f7db04a3ac230b13 Mon Sep 17 00:00:00 2001 From: Acaki Date: Tue, 2 Dec 2025 15:04:15 +0800 Subject: [PATCH 4/5] refactor: request limited range for connection --- VoidLink/Stream/Connection.m | 1 - VoidLink/Stream/VideoDecoderRenderer.m | 23 +++++------------------ 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/VoidLink/Stream/Connection.m b/VoidLink/Stream/Connection.m index d8fea102..f92bdccc 100644 --- a/VoidLink/Stream/Connection.m +++ b/VoidLink/Stream/Connection.m @@ -591,7 +591,6 @@ void ClSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t _callbacks = callbacks; LiInitializeStreamConfiguration(&_streamConfig); - _streamConfig.colorRange = 1; // Full range _streamConfig.width = config.width; _streamConfig.height = config.height; _streamConfig.fps = config.frameRate; diff --git a/VoidLink/Stream/VideoDecoderRenderer.m b/VoidLink/Stream/VideoDecoderRenderer.m index f51babac..1ae5e12c 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.m +++ b/VoidLink/Stream/VideoDecoderRenderer.m @@ -221,19 +221,10 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, } mutableCopy]; #else NSNumber *pixelFormat = nil; - - // Uses limited range for AV1 as full range doesn't work well on all devices - if (self->_videoFormat & VIDEO_FORMAT_MASK_AV1) { - if (self->_videoFormat & VIDEO_FORMAT_MASK_YUV444) { - pixelFormat = @(kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange); - } else { - pixelFormat = @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); - } - Log(LOG_I, @"Using VideoRange pixel format for AV1 (workaround for M3 decoder bug)"); - } else if (self->_videoFormat & VIDEO_FORMAT_MASK_YUV444) { - pixelFormat = @(kCVPixelFormatType_444YpCbCr10BiPlanarFullRange); + if (self->_videoFormat & VIDEO_FORMAT_MASK_YUV444) { + pixelFormat = @(kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange); } else { - pixelFormat = @(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange); + pixelFormat = @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); } NSMutableDictionary *destinationPixelBufferAttributes = [@{ (id)kCVPixelBufferPixelFormatTypeKey : pixelFormat @@ -636,12 +627,8 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); break; } - Log(LOG_I, @"AV1 video range reported by stream: %@", seqHeader->color_config.color_range == 1 ? @"full" : @"limited"); - - // Forces limited range for AV1 as full range doesn't work well on all devices - SET_EXTENSION(kCMFormatDescriptionExtension_FullRangeVideo, @NO); - - Log(LOG_I, @"AV1 using limited range end-to-end"); + Log(LOG_I, @"AV1 video range: %@", seqHeader->color_config.color_range == 1 ? @"full" : @"limited"); + SET_EXTENSION(kCMFormatDescriptionExtension_FullRangeVideo, @(seqHeader->color_config.color_range == 1)); // Progressive content SET_EXTENSION(kCMFormatDescriptionExtension_FieldCount, @(1)); From 3823c955828ca92712fc611f464f97abb2a41849 Mon Sep 17 00:00:00 2001 From: Acaki Date: Tue, 2 Dec 2025 21:04:07 +0800 Subject: [PATCH 5/5] feat: add toggle for full range and disable toggles for unsupported codecs --- VoidLink/Base.lproj/iPad.storyboard | 25 +++++++ VoidLink/Base.lproj/iPhone.storyboard | 25 +++++++ VoidLink/Database/DataManager.h | 1 + VoidLink/Database/DataManager.m | 2 + VoidLink/Database/TemporarySettings.h | 1 + VoidLink/Database/TemporarySettings.m | 2 + .../VoidLink v1.0.xcdatamodel/contents | 1 + VoidLink/Stream/Connection.m | 5 +- VoidLink/Stream/StreamConfiguration.h | 1 + VoidLink/Stream/StreamConfiguration.m | 2 +- VoidLink/Stream/StreamManager.m | 8 ++- VoidLink/Stream/VideoDecoderRenderer.h | 2 +- VoidLink/Stream/VideoDecoderRenderer.m | 8 ++- .../ViewControllers/MainFrameViewController.m | 4 ++ .../ViewControllers/SettingsViewController.h | 2 + .../ViewControllers/SettingsViewController.m | 69 ++++++++++++++++--- VoidLink/mul.lproj/iPad.xcstrings | 12 ++++ VoidLink/mul.lproj/iPhone.xcstrings | 12 ++++ 18 files changed, 166 insertions(+), 16 deletions(-) diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 0affe068..241fcc79 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -584,6 +584,29 @@ + + + + + + + + + + + + + + + @@ -1966,6 +1989,8 @@ + + diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard index 08219725..53328550 100644 --- a/VoidLink/Base.lproj/iPhone.storyboard +++ b/VoidLink/Base.lproj/iPhone.storyboard @@ -1739,6 +1739,29 @@ + + + + + + + + + + + + + + + @@ -2823,6 +2846,8 @@ + + diff --git a/VoidLink/Database/DataManager.h b/VoidLink/Database/DataManager.h index b7ce2a2e..29dccf6c 100644 --- a/VoidLink/Database/DataManager.h +++ b/VoidLink/Database/DataManager.h @@ -116,6 +116,7 @@ typedef NS_ENUM(NSInteger, WidgetSizeTransition) { preferredCodec:(uint32_t)preferredCodec enableYUV444:(BOOL)enableYUV444 enablePIP:(BOOL)enablePIP + fullRange:(BOOL)fullRange enableHdr:(BOOL)enableHdr btMouseSupport:(BOOL)btMouseSupport // absoluteTouchMode:(BOOL)absoluteTouchMode diff --git a/VoidLink/Database/DataManager.m b/VoidLink/Database/DataManager.m index 404f675d..8b85d32b 100644 --- a/VoidLink/Database/DataManager.m +++ b/VoidLink/Database/DataManager.m @@ -88,6 +88,7 @@ preferredCodec:(uint32_t)preferredCodec enableYUV444:(BOOL)enableYUV444 enablePIP:(BOOL)enablePIP + fullRange:(BOOL)fullRange enableHdr:(BOOL)enableHdr btMouseSupport:(BOOL)btMouseSupport // absoluteTouchMode:(BOOL)absoluteTouchMode @@ -161,6 +162,7 @@ settingsToSave.preferredCodec = preferredCodec; settingsToSave.enableYUV444 = enableYUV444; settingsToSave.enablePIP = enablePIP; + settingsToSave.fullRange = fullRange; settingsToSave.enableHdr = enableHdr; settingsToSave.btMouseSupport = btMouseSupport; // settingsToSave.absoluteTouchMode = absoluteTouchMode; diff --git a/VoidLink/Database/TemporarySettings.h b/VoidLink/Database/TemporarySettings.h index 3cb49b8e..992c1d21 100644 --- a/VoidLink/Database/TemporarySettings.h +++ b/VoidLink/Database/TemporarySettings.h @@ -54,6 +54,7 @@ } preferredCodec; @property (nonatomic) BOOL enableYUV444; @property (nonatomic) BOOL enablePIP; +@property (nonatomic) BOOL fullRange; @property (nonatomic) BOOL reverseMouseWheelDirection; @property (nonatomic, retain) NSNumber * asyncNativeTouchPriority; @property (nonatomic) BOOL multiController; diff --git a/VoidLink/Database/TemporarySettings.m b/VoidLink/Database/TemporarySettings.m index 2ba9712d..5827b165 100644 --- a/VoidLink/Database/TemporarySettings.m +++ b/VoidLink/Database/TemporarySettings.m @@ -42,6 +42,7 @@ self.preferredCodec = (typeof(self.preferredCodec))[[NSUserDefaults standardUserDefaults] integerForKey:@"preferredCodec"]; self.enableYUV444 = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableYUV444"]; self.enablePIP = [[NSUserDefaults standardUserDefaults] boolForKey:@"enablePIP"]; + self.fullRange = [[NSUserDefaults standardUserDefaults] boolForKey:@"fullRange"]; self.frameQueueSize = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"frameQueueSize"]]; self.playAudioOnPC = [[NSUserDefaults standardUserDefaults] boolForKey:@"audioOnPC"]; self.enableHdr = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableHdr"]; @@ -88,6 +89,7 @@ self.preferredCodec = settings.preferredCodec; self.enableYUV444 = settings.enableYUV444; self.enablePIP = settings.enablePIP; + self.fullRange = settings.fullRange; self.frameQueueSize = settings.frameQueueSize; self.playAudioOnPC = settings.playAudioOnPC; self.redirectMic = settings.redirectMic; diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index e71b7d9d..d4bf1f36 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -46,6 +46,7 @@ + diff --git a/VoidLink/Stream/Connection.m b/VoidLink/Stream/Connection.m index f92bdccc..d3f41f55 100644 --- a/VoidLink/Stream/Connection.m +++ b/VoidLink/Stream/Connection.m @@ -57,6 +57,7 @@ static AVAudioPCMBuffer *pcmBuffer; static AVAudioFormat *audioFormat; static bool muteInBackground; +static bool fullRange; static VideoDecoderRenderer* renderer; @@ -64,7 +65,7 @@ static BandwidthTracker *bwTracker; int DrDecoderSetup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - [renderer setupWithVideoFormat:videoFormat width:width height:height frameRate:redrawRate]; + [renderer setupWithVideoFormat:videoFormat width:width height:height frameRate:redrawRate fullRange:fullRange]; lastFrameNumber = 0; activeVideoFormat = videoFormat; Log(LOG_I, @"Active video format: 0x%x", activeVideoFormat); @@ -591,6 +592,8 @@ void ClSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t _callbacks = callbacks; LiInitializeStreamConfiguration(&_streamConfig); + _streamConfig.colorRange = config.fullRange ? 1 : 0; + fullRange = config.fullRange; _streamConfig.width = config.width; _streamConfig.height = config.height; _streamConfig.fps = config.frameRate; diff --git a/VoidLink/Stream/StreamConfiguration.h b/VoidLink/Stream/StreamConfiguration.h index f2019778..d6b36952 100644 --- a/VoidLink/Stream/StreamConfiguration.h +++ b/VoidLink/Stream/StreamConfiguration.h @@ -21,6 +21,7 @@ @property int serverCodecModeSupport; @property BOOL enableYUV444; @property BOOL enablePIP; +@property BOOL fullRange; @property int width; @property int height; @property int frameRate; diff --git a/VoidLink/Stream/StreamConfiguration.m b/VoidLink/Stream/StreamConfiguration.m index f3be6778..3b8e2d67 100644 --- a/VoidLink/Stream/StreamConfiguration.m +++ b/VoidLink/Stream/StreamConfiguration.m @@ -9,5 +9,5 @@ #import "StreamConfiguration.h" @implementation StreamConfiguration -@synthesize host, httpsPort, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, buttonVisualFeedback, gyroMode, emulatedControllerType, audioConfiguration, supportedVideoFormats, multiController, serverCert, rtspSessionUrl, serverCodecModeSupport, enableYUV444, enablePIP, localVolume; +@synthesize host, httpsPort, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, buttonVisualFeedback, gyroMode, emulatedControllerType, audioConfiguration, supportedVideoFormats, multiController, serverCert, rtspSessionUrl, serverCodecModeSupport, enableYUV444, enablePIP, fullRange, localVolume; @end diff --git a/VoidLink/Stream/StreamManager.m b/VoidLink/Stream/StreamManager.m index 1383d8f8..6cc48ded 100644 --- a/VoidLink/Stream/StreamManager.m +++ b/VoidLink/Stream/StreamManager.m @@ -205,6 +205,8 @@ double avgVideoMbps = [_connection getBwTracker].averageMbps; double peakVideoMbps = [_connection getBwTracker].peakMbps; + NSString* colorRange = _config.fullRange ? @"Full" : @"Limited"; + if(overlayLevel == 1) return [LocalizationHelper localizedStringForKey:@"simplifiedOsdText", fps, stats.networkDroppedFrames / interval, @@ -221,7 +223,7 @@ } } - return [LocalizationHelper localizedStringForKey:@"Video stream: %dx%d %.2f FPS (Codec: %@)\n" + return [LocalizationHelper localizedStringForKey:@"Video stream: %dx%d %.2f FPS (Codec: %@, %@)\n" "Bitrate: %.1f Mbps, Peak: %.1f\n" "%@" "Renderer: %@\n" @@ -231,6 +233,7 @@ _config.height, fps, [_connection getActiveCodecName], + colorRange, avgVideoMbps, peakVideoMbps, hostProcessingString, rendererWithPacing, @@ -242,7 +245,7 @@ rendererWithPacing = @"AVSampleBuffer (Queue Pacing)"; } - return [LocalizationHelper localizedStringForKey:@"Video stream: %dx%d %.2f FPS (Codec: %@)\n" + return [LocalizationHelper localizedStringForKey:@"Video stream: %dx%d %.2f FPS (Codec: %@, %@)\n" "Bitrate: %.1f Mbps, Peak: %.1f, Frames buffered: %.1f\n" "%@" "Renderer: %@\n" @@ -253,6 +256,7 @@ _config.height, fps, [_connection getActiveCodecName], + colorRange, avgVideoMbps, peakVideoMbps, stats.frameQueueMetrics.avg, hostProcessingString, rendererWithPacing, diff --git a/VoidLink/Stream/VideoDecoderRenderer.h b/VoidLink/Stream/VideoDecoderRenderer.h index 3bc61d06..93b1ec41 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.h +++ b/VoidLink/Stream/VideoDecoderRenderer.h @@ -23,7 +23,7 @@ - (id)initWithView:(UIView*)view callbacks:(id)callbacks streamAspectRatio:(float)aspectRatio; -- (void)setupWithVideoFormat:(int)videoFormat width:(int)videoWidth height:(int)videoHeight frameRate:(int)frameRate; +- (void)setupWithVideoFormat:(int)videoFormat width:(int)videoWidth height:(int)videoHeight frameRate:(int)frameRate fullRange:(BOOL)fullRange; - (void)renderFrame:(Frame *)frame atTime:(CMTime)targetTime; - (void)cleanup; - (void)setHdrMode:(BOOL)enabled; diff --git a/VoidLink/Stream/VideoDecoderRenderer.m b/VoidLink/Stream/VideoDecoderRenderer.m index 1ae5e12c..046895b1 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.m +++ b/VoidLink/Stream/VideoDecoderRenderer.m @@ -42,6 +42,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, AVSampleBufferDisplayLayer* _displayLayer; int _videoFormat; int _frameRate; + BOOL _fullRange; NSMutableArray *_parameterSetBuffers; NSData *_masteringDisplayColorVolume; @@ -150,10 +151,11 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, # pragma mark DisplayLink vsync callback -- (void)setupWithVideoFormat:(int)videoFormat width:(int)videoWidth height:(int)videoHeight frameRate:(int)frameRate +- (void)setupWithVideoFormat:(int)videoFormat width:(int)videoWidth height:(int)videoHeight frameRate:(int)frameRate fullRange:(BOOL)fullRange { self->_videoFormat = videoFormat; self->_frameRate = frameRate; + self->_fullRange = fullRange; // reset plot data in case we've already used it for a previous renderer [[ImGuiPlots sharedInstance] clearData]; @@ -222,9 +224,9 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, #else NSNumber *pixelFormat = nil; if (self->_videoFormat & VIDEO_FORMAT_MASK_YUV444) { - pixelFormat = @(kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange); + pixelFormat = self->_fullRange ? @(kCVPixelFormatType_444YpCbCr10BiPlanarFullRange) : @(kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange); } else { - pixelFormat = @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); + pixelFormat = self->_fullRange ? @(kCVPixelFormatType_420YpCbCr10BiPlanarFullRange) : @(kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange); } NSMutableDictionary *destinationPixelBufferAttributes = [@{ (id)kCVPixelBufferPixelFormatTypeKey : pixelFormat diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 16742432..c173f43f 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -828,6 +828,9 @@ static NSMutableSet* hostList; _streamConfig.localVolume = streamSettings.localVolume.floatValue; _streamConfig.swapABXYButtons = streamSettings.swapABXYButtons; _streamConfig.buttonVisualFeedback = streamSettings.buttonVisualFeedback; + _streamConfig.enableYUV444 = streamSettings.enableYUV444; + _streamConfig.enablePIP = streamSettings.enablePIP; + _streamConfig.fullRange = streamSettings.fullRange; _streamConfig.asyncNativeTouchPriority = streamSettings.asyncNativeTouchPriority; // new streamConfig segment _streamConfig.gyroMode = [streamSettings.gyroMode intValue]; _streamConfig.emulatedControllerType = streamSettings.emulatedControllerType.intValue; @@ -1177,6 +1180,7 @@ static NSMutableSet* hostList; [settingsViewController.touchModeSelector2 setEnabled:settingsViewController.touchModeSelector1.enabled]; [settingsViewController.codecSelector setEnabled:!_settingsExpandedInStreamView]; [settingsViewController.yuv444Switch setEnabled:!_settingsExpandedInStreamView]; + [settingsViewController.fullRangeSwitch setEnabled:!_settingsExpandedInStreamView]; [settingsViewController.hdrSwitch setEnabled:!_settingsExpandedInStreamView && [settingsViewController hdrSupported]]; [settingsViewController.gyroModeSelector setEnabled:!_settingsExpandedInStreamView || ![streamFrameViewController shallDisableGyroHotSwitch]]; [settingsViewController.emulatedControllerTypeSelector setEnabled:!_settingsExpandedInStreamView]; diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index 8576fce1..998ff0d4 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -49,6 +49,7 @@ @property (strong, nonatomic) IBOutlet UIStackView *codecStack; @property (strong, nonatomic) IBOutlet UIStackView *yuv444Stack; @property (strong, nonatomic) IBOutlet UIStackView *pipStack; +@property (strong, nonatomic) IBOutlet UIStackView *fullRangeStack; @property (strong, nonatomic) IBOutlet UIStackView *hdrStack; @property (strong, nonatomic) IBOutlet UIStackView *reverseMouseWheelDirectionStack; @property (strong, nonatomic) IBOutlet UIStackView *citrixX1MouseStack; @@ -78,6 +79,7 @@ @property (strong, nonatomic) IBOutlet UISwitch *hdrSwitch; @property (strong, nonatomic) IBOutlet UISwitch *yuv444Switch; @property (strong, nonatomic) IBOutlet UISwitch *pipSwitch; +@property (strong, nonatomic) IBOutlet UISwitch *fullRangeSwitch; @property (strong, nonatomic) IBOutlet UISegmentedControl *reverseMouseWheelDirectionSelector; @property (strong, nonatomic) IBOutlet UISegmentedControl *statsOverlaySelector; @property (strong, nonatomic) IBOutlet UIScrollView *scrollView; diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 139ea844..d6fd2460 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -431,15 +431,18 @@ BOOL isCustomResolution(int resolutionSelected) { - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:NO]; - + settingsViewJustExpanded = true; - + + // Ensure codec-dependent switches are in correct state when view appears + [self updateCodecDependentSwitches]; + /* [self checkAndRequestMicPermission]; self.micHandler = [MicHandler new]; [self.micHandler startTapping]; */ - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) // handle orientation change since i made portrait mode available name:UIDeviceOrientationDidChangeNotification @@ -753,6 +756,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self addSetting:self.hdrStack ofId:@"hdrStack" withInfoTag:![self hdrSupported] withDynamicLabel:NO to:videoSection]; [self addSetting:self.yuv444Stack ofId:@"yuv444Stack" withInfoTag:YES withDynamicLabel:NO to:videoSection]; [self addSetting:self.pipStack ofId:@"pipStack" withInfoTag:YES withDynamicLabel:NO to:videoSection]; + [self addSetting:self.fullRangeStack ofId:@"fullRangeStack" withInfoTag:YES withDynamicLabel:NO to:videoSection]; [self addSetting:self.framePacingStack ofId:@"framePacingStack" withInfoTag:YES withDynamicLabel:NO to:videoSection]; [self addSetting:self.frameQueueSizeStack ofId:@"frameQueueSizeStack" withInfoTag:NO withDynamicLabel:YES to:videoSection]; @@ -1539,6 +1543,7 @@ BOOL isCustomResolution(int resolutionSelected) { [_parentStack removeFromSuperview]; [self initParentStack]; [self layoutSections]; + [self updateCodecDependentSwitches]; // Ensure switches are in correct state after layout [self updateTheme]; //[self doneRemoveSettingItem]; Settings *currentSettings = [dataMan retrieveSettings]; @@ -1663,7 +1668,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self.rememberFoldStateSwitch setOn:self->tempSettings.rememberFoldState];// Load old setting MenuSectionView.overridePersistedFoldState = !self->tempSettings.rememberFoldState; [self.rememberFoldStateSwitch addTarget:self action:@selector(rememberFoldStateSwitchFlipped:) forControlEvents:UIControlEventValueChanged]; - + [self layoutSections]; @@ -1784,14 +1789,19 @@ BOOL isCustomResolution(int resolutionSelected) { [self.hdrSwitch setOn:self->tempSettings.enableHdr]; } - [self.yuv444Switch setOn:self->tempSettings.enableYUV444]; - [self.pipSwitch setOn:self->tempSettings.enablePIP]; - if(@available(iOS 15.0, *)) [self.pipSwitch setEnabled:true]; - else{ + if(@available(iOS 15.0, *)) { + [self.pipSwitch setEnabled:true]; + } else{ [self.pipSwitch setOn:false]; [self.pipSwitch setEnabled:false]; } + + // Initialize codec-dependent switches together + [self.yuv444Switch setOn:self->tempSettings.enableYUV444]; + [self.fullRangeSwitch setOn:self->tempSettings.fullRange]; + [self.codecSelector addTarget:self action:@selector(codecSelectorChanged:) forControlEvents:UIControlEventValueChanged]; + [self updateCodecDependentSwitches]; [self.statsOverlaySelector setSelectedSegmentIndex:self->tempSettings.statsOverlayLevel.intValue]; @@ -3305,6 +3315,7 @@ BOOL isCustomResolution(int resolutionSelected) { uint32_t preferredCodec = [self getChosenCodecPreference]; BOOL enableYUV444 = self.yuv444Switch.isOn; BOOL enablePIP = self.pipSwitch.isOn; + BOOL fullRange = self.fullRangeSwitch.isOn; BOOL btMouseSupport = self.citrixX1MouseSwitch.isOn; NSInteger touchMode = [self isNotNativeTouchOnly] ? self.touchModeSelector1.selectedSegmentIndex : NativeTouchOnly; NSInteger statsOverlayLevel = [self.statsOverlaySelector selectedSegmentIndex]; @@ -3376,6 +3387,7 @@ BOOL isCustomResolution(int resolutionSelected) { preferredCodec:preferredCodec enableYUV444:enableYUV444 enablePIP:enablePIP + fullRange:fullRange enableHdr:enableHdr btMouseSupport:btMouseSupport touchMode:touchMode @@ -3425,4 +3437,45 @@ BOOL isCustomResolution(int resolutionSelected) { - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { } +- (void)codecSelectorChanged:(UISegmentedControl *)sender { + [self updateCodecDependentSwitches]; +} + +- (void)updateCodecDependentSwitches { + uint32_t codec = [self getChosenCodecPreference]; + BOOL isAV1 = (codec == CODEC_PREF_AV1); + BOOL isH264 = (codec == CODEC_PREF_H264); + + if (isAV1) { + // AV1 must use limited range, so disable fullRange and turn it off + [self.fullRangeSwitch setOn:NO animated:NO]; + [self.fullRangeSwitch setEnabled:NO]; + [self.fullRangeSwitch setUserInteractionEnabled:NO]; + + // AV1 doesn't support YUV444, so disable it and turn it off + [self.yuv444Switch setOn:NO animated:NO]; + [self.yuv444Switch setEnabled:NO]; + [self.yuv444Switch setUserInteractionEnabled:NO]; + } else { + [self.fullRangeSwitch setEnabled:YES]; + [self.fullRangeSwitch setUserInteractionEnabled:YES]; + + [self.yuv444Switch setEnabled:YES]; + [self.yuv444Switch setUserInteractionEnabled:YES]; + } + + // H264 doesn't support HDR, so disable it and turn it off + if (isH264) { + [self.hdrSwitch setOn:NO animated:NO]; + [self.hdrSwitch setEnabled:NO]; + [self.hdrSwitch setUserInteractionEnabled:NO]; + } else { + // Only enable HDR if the device supports it + if ([self hdrSupported]) { + [self.hdrSwitch setEnabled:YES]; + [self.hdrSwitch setUserInteractionEnabled:YES]; + } + } +} + @end diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings index a14352a0..432633da 100644 --- a/VoidLink/mul.lproj/iPad.xcstrings +++ b/VoidLink/mul.lproj/iPad.xcstrings @@ -2953,6 +2953,18 @@ } } }, + "iPd-Ra-Lbl.text" : { + "comment" : "Class = \"UILabel\"; text = \"Full Range\"; ObjectID = \"iPd-Ra-Lbl\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Full Range" + } + } + } + }, "IvI-tw-APj.text" : { "comment" : "Class = \"UILabel\"; text = \"Open Settings in Streaming\"; ObjectID = \"IvI-tw-APj\";", "extractionState" : "extracted_with_value", diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings index fcbe651f..aa776820 100644 --- a/VoidLink/mul.lproj/iPhone.xcstrings +++ b/VoidLink/mul.lproj/iPhone.xcstrings @@ -2197,6 +2197,18 @@ } } }, + "fRl-Ra-Lbl.text" : { + "comment" : "Class = \"UILabel\"; text = \"Full Range\"; ObjectID = \"fRl-Ra-Lbl\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Full Range" + } + } + } + }, "GbY-52-itv.text" : { "comment" : "Class = \"UILabel\"; text = \"Local Volume\"; ObjectID = \"GbY-52-itv\";", "extractionState" : "extracted_with_value",