From b12cb964169f79b92bfb3dd0bb6ef72e5ca7aa4a Mon Sep 17 00:00:00 2001 From: Acaki Date: Sat, 4 Apr 2026 17:02:52 +0800 Subject: [PATCH] fix: video streatched when changing resolution in stream --- VoidLink/Stream/VideoDecoderRenderer.m | 44 +++++++++++++++++-- .../StreamFrameViewController.m | 14 ++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/VoidLink/Stream/VideoDecoderRenderer.m b/VoidLink/Stream/VideoDecoderRenderer.m index e984d57e..d108f355 100644 --- a/VoidLink/Stream/VideoDecoderRenderer.m +++ b/VoidLink/Stream/VideoDecoderRenderer.m @@ -29,6 +29,7 @@ #include #include #include +#include // Define for extra logging related to frame pacing //#define DISPLAYLINK_VERBOSE @@ -76,16 +77,34 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size, [_view.layer addSublayer:_displayLayer]; } + // Update aspect ratio from the actual stream dimensions if we have a format description + float aspectRatioToUse = _streamAspectRatio; + if (_formatDesc != NULL) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(_formatDesc); + if (dimensions.width > 0 && dimensions.height > 0) { + aspectRatioToUse = (float)dimensions.width / (float)dimensions.height; + if (fabsf(aspectRatioToUse - _streamAspectRatio) > 0.001f) { + Log(LOG_I, @"Stream resolution changed: updating aspect ratio from %.4f to %.4f (%dx%d)", + _streamAspectRatio, aspectRatioToUse, dimensions.width, dimensions.height); + _streamAspectRatio = aspectRatioToUse; + // Also update StreamView's aspect ratio for correct touch input mapping (if it's a StreamView) + if ([_view respondsToSelector:@selector(setStreamAspectRatio:)]) { + [(StreamView*)_view setStreamAspectRatio:aspectRatioToUse]; + } + } + } + } + // Ensure the AVSampleBufferDisplayLayer is sized to preserve the aspect ratio // of the video stream. We used to use AVLayerVideoGravityResizeAspect, but that // respects the PAR encoded in the SPS which causes our computed video-relative // touch location to be wrong in StreamView if the aspect ratio of the host // desktop doesn't match the aspect ratio of the stream. CGSize videoSize; - if (_view.bounds.size.width > _view.bounds.size.height * _streamAspectRatio) { - videoSize = CGSizeMake(_view.bounds.size.height * _streamAspectRatio, _view.bounds.size.height); + if (_view.bounds.size.width > _view.bounds.size.height * aspectRatioToUse) { + videoSize = CGSizeMake(_view.bounds.size.height * aspectRatioToUse, _view.bounds.size.height); } else { - videoSize = CGSizeMake(_view.bounds.size.width, _view.bounds.size.width / _streamAspectRatio); + videoSize = CGSizeMake(_view.bounds.size.width, _view.bounds.size.width / aspectRatioToUse); } [CATransaction begin]; @@ -837,6 +856,25 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit); // Unsupported codec! abort(); } + + // Check if the resolution changed and reinitialize the display layer if needed + if (_formatDesc != NULL) { + CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(_formatDesc); + float newAspectRatio = (float)dimensions.width / (float)dimensions.height; + + // If aspect ratio changed significantly, reinitialize the display layer on the main thread + if (fabsf(newAspectRatio - _streamAspectRatio) > 0.001f) { + Log(LOG_I, @"Resolution change detected in IDR frame: %dx%d (aspect ratio %.4f -> %.4f)", + dimensions.width, dimensions.height, _streamAspectRatio, newAspectRatio); + dispatch_async(dispatch_get_main_queue(), ^{ + [self reinitializeDisplayLayer]; + // Post notification so StreamFrameViewController can update the StreamView's aspect ratio + [[NSNotificationCenter defaultCenter] postNotificationName:@"StreamAspectRatioChanged" + object:self + userInfo:@{@"aspectRatio": @(newAspectRatio)}]; + }); + } + } } if (_formatDesc == NULL) { diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 804fe10b..1c46a7e4 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -710,6 +710,11 @@ name:@"OscLayoutCloseNotification" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleStreamAspectRatioChanged:) + name:@"StreamAspectRatioChanged" + object:nil]; + #if 0 // FIXME: This doesn't work reliably on iPad for some reason. Showing and hiding the keyboard // several times in a row will not correctly restore the state of the UIScrollView. @@ -812,6 +817,15 @@ [self->_streamView reloadOnScreenWidgetViews:true]; //update keyboard buttons here } +- (void)handleStreamAspectRatioChanged:(NSNotification *)notification { + NSNumber *aspectRatioNum = notification.userInfo[@"aspectRatio"]; + if (aspectRatioNum && _streamView) { + CGFloat aspectRatio = [aspectRatioNum doubleValue]; + Log(LOG_I, @"Updating StreamView aspect ratio to %.4f", aspectRatio); + _streamView.streamAspectRatio = aspectRatio; + } +} + - (void)setUserInteractionEnabledForStreamView:(bool)enabled{ _streamView.userInteractionEnabled = enabled; for(UIView* view in self.view.subviews){