diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index df3a2f35..4aeb53ba 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -23,7 +23,7 @@ - + diff --git a/VoidLink/Stream/StreamManager.h b/VoidLink/Stream/StreamManager.h index 50345496..63782a64 100644 --- a/VoidLink/Stream/StreamManager.h +++ b/VoidLink/Stream/StreamManager.h @@ -19,6 +19,7 @@ - (id) initWithConfig:(StreamConfiguration*)config renderView:(UIView*)view connectionCallbacks:(id)callback; - (void) stopStream; +- (void) setNeedRequeuing:(bool)needRequeuing; - (NSString*) getStatsOverlayText: (uint16_t) overlayLevel; diff --git a/VoidLink/Stream/StreamManager.m b/VoidLink/Stream/StreamManager.m index d64ea23a..5e42dc16 100644 --- a/VoidLink/Stream/StreamManager.m +++ b/VoidLink/Stream/StreamManager.m @@ -114,6 +114,11 @@ }); } +- (void) setNeedRequeuing:(bool)needRequeuing{ + [_videoRenderer.frameQueue clear]; + _videoRenderer.needRequeuing = needRequeuing; +} + - (void) stopStream { [_connection terminate]; diff --git a/VoidLink/Stream/VideoDecoderRenderer.h b/VoidLink/Stream/VideoDecoderRenderer.h index 93b1ec41..fcc10e7c 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.h +++ b/VoidLink/Stream/VideoDecoderRenderer.h @@ -18,6 +18,8 @@ @property (atomic, readonly) PlotMetrics decodeMetrics; @property (atomic, readonly) PlotMetrics frameQueueMetrics; +@property (atomic, assign) bool needRequeuing; +@property (nonatomic, strong) FrameQueue* frameQueue; @property (nonatomic, strong, readonly) AVSampleBufferDisplayLayer *displayLayer; diff --git a/VoidLink/Stream/VideoDecoderRenderer.m b/VoidLink/Stream/VideoDecoderRenderer.m index 48573d21..b7d6595d 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.m +++ b/VoidLink/Stream/VideoDecoderRenderer.m @@ -52,9 +52,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, VTDecompressionSessionRef _decompressionSession; CADisplayLink *_displayLink; - FrameQueue *_frameQueue; uint8_t queueSize; - bool isFirstFrame; NSInteger _maxRefreshRate; RenderingBackend _renderingBackend; @@ -145,9 +143,10 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, _framePacingMode = tempSettings.framePacingMode.integerValue; _asyncFrameDequeue = tempSettings.asyncFrameDequeue; + NSLog(@"_asyncFrameDequeue %d", _asyncFrameDequeue); _enableTimebase = false; queueSize = tempSettings.frameQueueSize.intValue; - isFirstFrame = true; + _needRequeuing = true; isIPhone = [Utils isIPhone]; _frameQueue = [FrameQueue sharedInstance]; @@ -155,7 +154,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, [_frameQueue setHighWaterMark:(int)[tempSettings.frameQueueSize integerValue]]; [self reinitializeDisplayLayer]; - NSTimeInterval interval = 1.0/tempSettings.framerate.intValue; + // NSTimeInterval interval = 1.0/tempSettings.framerate.intValue; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reinitializeDisplayLayer) @@ -261,7 +260,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, // Check for issues with the SampleBuffer, this should be much less likely since // AVSB is not actually decoding the frames anymore if (self->_displayLayer.status == AVQueuedSampleBufferRenderingStatusFailed) { - Log(LOG_E, @"Display layer rendering failed: %@", _displayLayer.error); + // Log(LOG_E, @"Display layer rendering failed: %@", _displayLayer.error); // Recreate the display layer. We are already on the main thread, // so this is safe to do right here. @@ -277,10 +276,11 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); #pragma mark DisplayLink - Frame Pacing - Vsync with FrameQueue +/* // This frame pacing method was inspired by the behavior of moonlight-qt's Pacer class, although it has evolved // a few additional features. Incoming frames from Sunshine are asynchronously processed into a queue by the VideoRecv thread. // DisplayLink calls us every vsync we we try to present the most recent frame. We try to maintain a user-configurable buffer -// of 1-5 frames. If the buffer is full, every other frame is dropped which just appears to the user as a lower framerate stream. +// of 1-5 frames. If the buffer is full, every other frame is dropped which just appears to the user as a lower framerate stream. */ - (void)renderModeAVSB:(CADisplayLink *)link { // NSTimeInterval current = link.targetTimestamp; // NSLog(@"link %f", link.duration); @@ -303,13 +303,17 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); CFTimeInterval waitFor = targetTime - dl0; + /* if (waitFor < 0.001f) { - waitFor = isIPhone ? waitFor : 0.0f; + NSLog(@"waitFor %f", waitFor); + // waitFor = isIPhone ? waitFor : 0.0f; + waitFor = 0.0f; } + */ - if(isFirstFrame ? _frameQueue.count>0 : true){ + if(_needRequeuing ? _frameQueue.count>queueSize-1 : true){ // if(true){ - if(isFirstFrame) isFirstFrame = false; + _needRequeuing = false; if(_asyncFrameDequeue){ [_frameQueue dequeueWithTimeout:waitFor completion:^(Frame *frame) { if (frame) { diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 485a68ae..d1622d6b 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -1168,6 +1168,15 @@ static NSMutableSet* hostList; _settingsViewExpanded = position != FrontViewPositionLeft; if (position == FrontViewPositionLeft) { self.navigationItem.leftBarButtonItems = @[_settingsButton]; + + if(streamFrameViewController.streamMan){ + NSLog(@"setNeedRequeuing %f", CACurrentMediaTime()); + double delayInSeconds = 0.1; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); + dispatch_after(popTime, dispatch_get_main_queue(), ^{ + [self->streamFrameViewController.streamMan setNeedRequeuing:true]; + }); + } } else { self.navigationItem.leftBarButtonItems = @[]; @@ -1227,10 +1236,10 @@ static NSMutableSet* hostList; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.appThemeStack]; [settingsViewController.renderingBackendSelector setEnabled:!_settingsExpandedInStreamView]; // Enable frame pacing mode selector only if not in stream view AND not in performance mode - BOOL shouldEnableFramePacing = !_settingsExpandedInStreamView && (settingsViewController.renderingBackendSelector.selectedSegmentIndex != RENDER_METAL); - [settingsViewController.framePacingModeSelector setEnabled:shouldEnableFramePacing]; + BOOL shouldEnableFramePacingSelector = !_settingsExpandedInStreamView && (settingsViewController.renderingBackendSelector.selectedSegmentIndex != RENDER_METAL); + [settingsViewController.framePacingModeSelector setEnabled:shouldEnableFramePacingSelector]; // [settingsViewController.frameTimebaseSwitch setEnabled:shouldEnableFramePacing]; - // [settingsViewController.asyncFrameDequeueSwitch setEnabled:shouldEnableFramePacing]; + [settingsViewController.asyncFrameDequeueSwitch setEnabled:shouldEnableFramePacingSelector && settingsViewController.framePacingModeSelector.selectedSegmentIndex == FramePacingModeQueue]; [settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.frameQueueSizeStack]; // Disable mic switch if sunshine does not support mic redirection diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 6ded761a..51173ccc 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -982,7 +982,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self addSetting:self.leftClickDelayStack ofId:@"leftClickDelayStack" withInfoTag:NO withDynamicLabel:YES to:experimentalSection]; [self addSetting:self.renderingBackendStack ofId:@"renderingBackendStack" withInfoTag:YES withDynamicLabel:NO to:experimentalSection]; // [self addSetting:self.frameTimebaseStack ofId:@"frameTimebaseStack" withInfoTag:NO withDynamicLabel:NO to:videoSection]; - // [self addSetting:self.asyncFrameDequeueStack ofId:@"asyncFrameDequeueStack" withInfoTag:NO withDynamicLabel:NO to:experimentalSection]; + [self addSetting:self.asyncFrameDequeueStack ofId:@"asyncFrameDequeueStack" withInfoTag:NO withDynamicLabel:NO to:experimentalSection]; [self addSetting:self.performanceGraphStack ofId:@"performanceGraphStack" withInfoTag:YES withDynamicLabel:NO to:experimentalSection]; [self addDynamicLabelForStack:self.graphOpacityStack]; @@ -2292,6 +2292,7 @@ BOOL isCustomResolution(int resolutionSelected) { // Set pacing method to Queue and disable selector [self.framePacingModeSelector setSelectedSegmentIndex:FramePacingModeQueue]; [self.framePacingModeSelector setEnabled:NO]; + [self setHidden:true forStack:self.asyncFrameDequeueStack]; } else { // Balanced mode (AVSB renderer) - enable PiP toggle if iOS 15+ if (@available(iOS 15.0, *)) { @@ -2301,6 +2302,7 @@ BOOL isCustomResolution(int resolutionSelected) { [self.pipSwitch setEnabled:NO]; } [self.framePacingModeSelector setEnabled:YES]; + [self setHidden:false forStack:self.asyncFrameDequeueStack]; } // Get the current settings to compare with the new selection @@ -2331,6 +2333,7 @@ BOOL isCustomResolution(int resolutionSelected) { style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { self.renderingBackendSelector.selectedSegmentIndex = 0; + [self renderingBackendChanged:self.renderingBackendSelector]; NSURL *url = [NSURL URLWithString:[LocalizationHelper localizedStringForKey:@"betterPerformanceLink"]]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; @@ -2347,7 +2350,8 @@ BOOL isCustomResolution(int resolutionSelected) { // Hide frame queue size for Off and Legacy modes [self setHidden:(sender.selectedSegmentIndex == FramePacingModeOff || sender.selectedSegmentIndex == FramePacingModeLegacy) forStack:self.frameQueueSizeStack]; // [self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue) forStack:self.frameTimebaseStack]; - // [self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue) forStack:self.asyncFrameDequeueStack]; + [self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue + || self.renderingBackendSelector.selectedSegmentIndex != 0) forStack:self.asyncFrameDequeueStack]; if(sender.selectedSegmentIndex == FramePacingModeOff || sender.selectedSegmentIndex == FramePacingModeLegacy){ [self.enableGraphsSwitch setOn:NO]; diff --git a/VoidLink/ViewControllers/StreamFrameViewController.h b/VoidLink/ViewControllers/StreamFrameViewController.h index 354033af..974664f4 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.h +++ b/VoidLink/ViewControllers/StreamFrameViewController.h @@ -30,7 +30,7 @@ @interface StreamFrameViewController : UIViewController #endif -@property (nonatomic) StreamManager* streamMan; +@property (nonatomic, strong) StreamManager* streamMan; @property (nonatomic) StreamConfiguration* streamConfig; @property (nonatomic, strong) AVPictureInPictureController *pipController API_AVAILABLE(ios(9.0)); @property (nonatomic, strong) AVPictureInPictureControllerContentSource *pipContentSource API_AVAILABLE(ios(15.0)); // Needed for iOS 15+ layer-based PiP diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 3c6ad968..5f9160d5 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -47,7 +47,6 @@ @implementation StreamFrameViewController { ControllerSupport *_controllerSupport; - StreamManager *_streamMan; TemporarySettings *_settings; OSCProfile* _oscProfile; NSTimer *_inactivityTimer; @@ -1092,6 +1091,7 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification { appDidEnterBackgroundWithoutPip = false; + [_streamMan setNeedRequeuing:true]; // Stop the background timer, since we're foregrounded again if (_inactivityTimer != nil) { Log(LOG_I, @"Stopping inactivity timer after becoming active again"); @@ -1338,11 +1338,15 @@ && _settings.sdrPerformanceWorkaround && [Utils hdrSupported] ){ - dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)); + dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)); dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if(LiGetCurrentHostDisplayHdrMode()){ NSArray* hdrCommand = [CommandManager.shared extractAutoReleaseButtonStringsFrom:@"WIN+ALT+B"]; [CommandManager.shared sendAutoReleaseComboCommandWithCmdStrings:hdrCommand delay:0.15 index:0 pressOnly:false releaseOnly:false]; + dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)); + dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self->_streamMan setNeedRequeuing:true]; + }); } }); } @@ -1711,6 +1715,11 @@ Log(LOG_I, @"View size changed, terminating stream"); + dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)); + dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [self->_streamMan setNeedRequeuing:true]; + }); + double delayInSeconds = 0.2; if (_delayedRemoveExtScreen) { dispatch_block_cancel(_delayedRemoveExtScreen);