frameInterpolation beta ready

This commit is contained in:
True Zhuanjia
2026-08-09 20:34:39 +08:00
parent fc2b1689d0
commit 7b7bb3837c
17 changed files with 1261 additions and 162 deletions
+40
View File
@@ -411,6 +411,42 @@
<constraint firstAttribute="height" constant="56.5" id="IFC-Wo-Eu0"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-StK" userLabel="interpolationLevel Stack">
<rect key="frame" x="440" y="1350" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Interpolation Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-Lbl">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Lv-Sel" userLabel="Interpolation Level Slider">
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
</slider>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="380" id="FIp-Lv-Wid"/>
<constraint firstAttribute="height" constant="56.5" id="FIp-Lv-Hgt"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-StK" userLabel="streamDimensionScale Stack">
<rect key="frame" x="440" y="1411" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scaled Stream Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-Lbl">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIp-Sc-Sld" userLabel="Stream Dimension Scale Slider">
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
</slider>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="380" id="FIp-Sc-Wid"/>
<constraint firstAttribute="height" constant="56.5" id="FIp-Sc-Hgt"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="hcl-DT-peO" userLabel="redirectMicStack">
<rect key="frame" x="432" y="2215" width="245.5" height="31"/>
<subviews>
@@ -2302,6 +2338,10 @@
<outlet property="fpsStack" destination="TuR-H8-e2H" id="zGQ-gR-P1i"/>
<outlet property="framePacingModeSelector" destination="2Dp-Qg-chi" id="h8A-YF-SZP"/>
<outlet property="framePacingStack" destination="oZG-5Y-pw5" id="vGd-hD-zTr"/>
<outlet property="interpolationLevelSlider" destination="FIp-Lv-Sel" id="FIp-Out-Sel"/>
<outlet property="interpolationLevelStack" destination="FIp-Lv-StK" id="FIp-Out-StK"/>
<outlet property="streamDimensionScaleSlider" destination="FIp-Sc-Sld" id="FIp-Out-ScS"/>
<outlet property="streamDimensionScaleStack" destination="FIp-Sc-StK" id="FIp-Out-ScK"/>
<outlet property="frameQueueSizeLabel" destination="Ce3-FM-vIh" id="90D-Tq-OGd"/>
<outlet property="frameQueueSizeSlider" destination="oZa-8Y-icz" id="Jo1-Fx-UMw"/>
<outlet property="frameQueueSizeStack" destination="MML-c1-D5j" id="82B-r7-Zbh"/>
+40
View File
@@ -2548,6 +2548,42 @@
<constraint firstAttribute="height" constant="56.5" id="Asf-2R-Qoi"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-StK" userLabel="interpolationLevel Stack">
<rect key="frame" x="456" y="2232" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Interpolation Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-Lbl">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Lv-Sel" userLabel="Interpolation Level Slider">
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
</slider>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="380" id="FIi-Lv-Wid"/>
<constraint firstAttribute="height" constant="56.5" id="FIi-Lv-Hgt"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-StK" userLabel="streamDimensionScale Stack">
<rect key="frame" x="456" y="2293" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Scaled Stream Resolution" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-Lbl">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="FIi-Sc-Sld" userLabel="Stream Dimension Scale Slider">
<rect key="frame" x="0.0" y="25.5" width="380" height="31"/>
</slider>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="380" id="FIi-Sc-Wid"/>
<constraint firstAttribute="height" constant="56.5" id="FIi-Sc-Hgt"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="CKa-yk-GSi" userLabel="redirectMicStack">
<rect key="frame" x="5" y="2157" width="245.5" height="31"/>
<subviews>
@@ -3569,6 +3605,10 @@
<outlet property="fpsStack" destination="aMv-7Q-yYg" id="JxU-wq-2Za"/>
<outlet property="framePacingModeSelector" destination="CC6-pu-Uoh" id="HTB-6I-QgB"/>
<outlet property="framePacingStack" destination="U0H-kh-QLS" id="M8w-wy-HI8"/>
<outlet property="interpolationLevelSlider" destination="FIi-Lv-Sel" id="FIi-Out-Sel"/>
<outlet property="interpolationLevelStack" destination="FIi-Lv-StK" id="FIi-Out-StK"/>
<outlet property="streamDimensionScaleSlider" destination="FIi-Sc-Sld" id="FIi-Out-ScS"/>
<outlet property="streamDimensionScaleStack" destination="FIi-Sc-StK" id="FIi-Out-ScK"/>
<outlet property="frameQueueSizeLabel" destination="r8X-jt-JfH" id="Tr9-eb-bIx"/>
<outlet property="frameQueueSizeSlider" destination="thv-JE-onX" id="gKD-6c-5tw"/>
<outlet property="frameQueueSizeStack" destination="IHx-FO-ctr" id="gob-Ea-Avh"/>
+3
View File
@@ -130,6 +130,9 @@ typedef NS_ENUM(NSInteger, PencilTickMode) {
graphOpacity:(NSInteger)graphOpacity
renderingBackend:(NSInteger)renderingBackend
framePacingMode:(NSInteger)framePacingMode
interpolationMaximumDimension:(NSInteger)interpolationMaximumDimension
interpolationMaximumPixelCount:(NSInteger)interpolationMaximumPixelCount
streamDimensionScale:(CGFloat)streamDimensionScale
sendDummyEvent:(BOOL)sendDummyEvent
rememberFoldState:(BOOL)rememberFoldState
singleTapSensitivy:(CGFloat)singleTapSensitivy
+6 -1
View File
@@ -106,6 +106,9 @@
graphOpacity:(NSInteger)graphOpacity
renderingBackend:(NSInteger)renderingBackend
framePacingMode:(NSInteger)framePacingMode
interpolationMaximumDimension:(NSInteger)interpolationMaximumDimension
interpolationMaximumPixelCount:(NSInteger)interpolationMaximumPixelCount
streamDimensionScale:(CGFloat)streamDimensionScale
sendDummyEvent:(BOOL)sendDummyEvent
rememberFoldState:(BOOL)rememberFoldState
singleTapSensitivy:(CGFloat)singleTapSensitivy
@@ -193,6 +196,9 @@
settingsToSave.graphOpacity = [NSNumber numberWithInteger:graphOpacity];
settingsToSave.renderingBackend = [NSNumber numberWithInteger:renderingBackend];
settingsToSave.framePacingMode = [NSNumber numberWithInteger:framePacingMode];
settingsToSave.interpolationMaximumDimension = [NSNumber numberWithInteger:interpolationMaximumDimension];
settingsToSave.interpolationMaximumPixelCount = [NSNumber numberWithInteger:interpolationMaximumPixelCount];
settingsToSave.streamDimensionScale = [NSNumber numberWithDouble:streamDimensionScale];
settingsToSave.sendDummyEvent = sendDummyEvent;
settingsToSave.singleTapSensitivity = [NSNumber numberWithDouble:singleTapSensitivy];
settingsToSave.hapticEngine = [NSNumber numberWithInteger:hapticEngine];
@@ -370,4 +376,3 @@
}
@end
+3
View File
@@ -99,6 +99,9 @@
@property (nonatomic) BOOL ctrlDownForPinch;
@property (nonatomic) BOOL passthroughGestures;
@property (nonatomic, retain) NSNumber * framePacingMode;
@property (nonatomic, retain) NSNumber * interpolationMaximumDimension;
@property (nonatomic, retain) NSNumber * interpolationMaximumPixelCount;
@property (nonatomic, retain) NSNumber * streamDimensionScale;
@property (nonatomic, retain) NSNumber * scrollSensitivity;
@property (nonatomic, retain) NSNumber * pinchSensitivity;
@property (nonatomic, retain) NSNumber * relativeTouchSlideThreshold;
+6
View File
@@ -55,6 +55,9 @@
self.graphOpacity = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"graphOpacity"]];
self.renderingBackend = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"renderingBackend"]];
self.framePacingMode = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"framePacingMode"]];
self.interpolationMaximumDimension = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"interpolationMaximumDimension"]];
self.interpolationMaximumPixelCount = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"interpolationMaximumPixelCount"]];
self.streamDimensionScale = [NSNumber numberWithDouble:[[NSUserDefaults standardUserDefaults] doubleForKey:@"streamDimensionScale"]];
NSInteger _screenSize = [[NSUserDefaults standardUserDefaults] integerForKey:@"streamResolution"];
switch (_screenSize) {
@@ -135,6 +138,9 @@
self.graphOpacity = settings.graphOpacity;
self.renderingBackend = settings.renderingBackend;
self.framePacingMode = settings.framePacingMode;
self.interpolationMaximumDimension = settings.interpolationMaximumDimension ?: @(0);
self.interpolationMaximumPixelCount = settings.interpolationMaximumPixelCount ?: @(0);
self.streamDimensionScale = settings.streamDimensionScale ?: @(1);
self.sendDummyEvent = settings.sendDummyEvent;
self.rememberFoldState = settings.rememberFoldState;
self.gyroBiasX = settings.gyroBiasX;
@@ -74,6 +74,8 @@
<attribute name="gyroSensitivity" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="hapticEngine" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="interpolationMaximumDimension" optional="YES" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
<attribute name="interpolationMaximumPixelCount" optional="YES" attributeType="Integer 32" defaultValueString="921600" usesScalarValueType="NO" syncable="YES"/>
<attribute name="keyboardToggleFingers" optional="YES" attributeType="Integer 16" defaultValueString="99" usesScalarValueType="NO" syncable="YES"/>
<attribute name="leftClickDelayMs" optional="YES" attributeType="Float" defaultValueString="15" usesScalarValueType="NO" syncable="YES"/>
<attribute name="liftStreamViewForKeyboard" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
@@ -115,6 +117,7 @@
<attribute name="softKeyboardHeight" attributeType="Float" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="statsOverlayEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="statsOverlayLevel" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="streamDimensionScale" optional="YES" attributeType="Double" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="streamingRadialMenuButton" attributeType="Integer 32" defaultValueString="16" usesScalarValueType="NO" syncable="YES"/>
<attribute name="streamingRadialMenuDelay" optional="YES" attributeType="Float" defaultValueString="0.5" usesScalarValueType="NO" syncable="YES"/>
<attribute name="swapABXYButtons" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
@@ -9304,6 +9304,46 @@
}
}
},
"Interpolation Resolution" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "插帧分辨率"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "影格插補解析度"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "影格插補解析度"
}
}
}
},
"Most devices currently support frame interpolation only for 8-bit YUV 4:2:0 SDR video. In this case, if HDR or YUV 4:4:4 is enabled, intermediate frames will still be generated in 8-bit YUV 4:2:0 SDR." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "目前多数设备仅支持 8-bit YUV420 SDR 视频插帧。这种情况下,如果您打开了 HDR 或 YUV444 选项,中间帧将依然按 8-bit YUV420 SDR 格式生成。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "目前多數裝置僅支援 8-bit YUV420 SDR 影片影格插補。在這種情況下,如果您開啟 HDR 或 YUV444 選項,中間影格仍會以 8-bit YUV420 SDR 格式產生。"
}
}
}
},
"Interpolation failed for an unknown reason." : {
"extractionState" : "manual",
"localizations" : {
@@ -17209,6 +17249,29 @@
}
}
},
"Scaled Stream Resolution" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "串流分辨率缩放"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "串流解析度縮放"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "串流解析度縮放"
}
}
}
},
"Slow connection to PC, Reduce your bitrate" : {
"extractionState" : "manual",
"localizations" : {
File diff suppressed because it is too large Load Diff
+2
View File
@@ -49,5 +49,7 @@
- (void)invalidateDecompressionSession;
+ (void)setFrameInterpolationEnabled:(bool)enabled;
+ (void)startOrRestartFrameInterpolation;
+ (void)stopFrameInterpolation;
@end
+180 -80
View File
@@ -36,11 +36,19 @@
//#define DISPLAYLINK_VERBOSE
static BOOL kEnableFrameInterpolation = false;
static __weak VideoDecoderRenderer *sActiveRenderer = nil;
// Private libavformat API for writing the AV1 Codec Configuration Box
extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
int write_seq_header);
@interface VideoDecoderRenderer ()
- (void)startOrRestartFrameInterpolation;
- (void)stopFrameInterpolation;
- (NSInteger)displayLinkFrameRateForInterpolationEnabled:(BOOL)enabled;
- (void)restartDisplayLinkForInterpolationEnabled:(BOOL)enabled;
@end
@implementation VideoDecoderRenderer {
dispatch_queue_t _sq, _vtq;
StreamView* _view;
@@ -60,6 +68,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
CMVideoFormatDescriptionRef _formatDescImageBuffer;
VTDecompressionSessionRef _decompressionSession;
FrameInterpolator *_frameInterpolator;
BOOL _frameInterpolationPaused;
CADisplayLink *_displayLink;
NSInteger _maxRefreshRate;
@@ -77,6 +86,26 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
kEnableFrameInterpolation = enabled;
}
+ (void)startOrRestartFrameInterpolation {
kEnableFrameInterpolation = true;
VideoDecoderRenderer *renderer;
@synchronized(self) {
renderer = sActiveRenderer;
}
[renderer startOrRestartFrameInterpolation];
}
+ (void)stopFrameInterpolation {
kEnableFrameInterpolation = false;
VideoDecoderRenderer *renderer;
@synchronized(self) {
renderer = sActiveRenderer;
}
[renderer stopFrameInterpolation];
}
- (void)reinitializeDisplayLayer
{
if (_displayLayer == nil) {
@@ -174,7 +203,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
_asyncFrameDequeue = tempSettings.asyncFrameDequeue;
NSLog(@"_asyncFrameDequeue %d", _asyncFrameDequeue);
_enableTimebase = false;
_queueSize = kEnableFrameInterpolation ? 4 : tempSettings.frameQueueSize.intValue;
_queueSize = kEnableFrameInterpolation ? 8 : tempSettings.frameQueueSize.intValue;
_needRequeuing = _queueSize>0;
_frameQueue = [FrameQueue sharedInstance];
@@ -189,9 +218,115 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
name:@"ScreenChanged"
object:nil];
@synchronized([VideoDecoderRenderer class]) {
sActiveRenderer = self;
}
return self;
}
- (NSInteger)displayLinkFrameRateForInterpolationEnabled:(BOOL)enabled {
NSInteger multiplier = enabled ? 2 : 1;
return MIN(self->_frameRate * multiplier, self->_maxRefreshRate);
}
- (void)restartDisplayLinkForInterpolationEnabled:(BOOL)enabled {
dispatch_block_t restartBlock = ^{
if (self->_renderingBackend != RENDER_AVSB || self->_displayLink == nil) {
return;
}
NSInteger targetFrameRate = [self displayLinkFrameRateForInterpolationEnabled:enabled];
BOOL shouldRemainPaused = appDidEnterBackgroundWithoutPip;
self->_displayLink.paused = YES;
if (@available(iOS 15.0, tvOS 15.0, *)) {
self->_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(targetFrameRate, targetFrameRate, targetFrameRate);
}
else {
self->_displayLink.preferredFramesPerSecond = targetFrameRate;
}
self->_displayLink.paused = shouldRemainPaused;
Log(LOG_I, @"Display link restarted at %ld FPS (interpolation %@)",
(long)targetFrameRate, enabled ? @"enabled" : @"disabled");
};
if ([NSThread isMainThread]) {
restartBlock();
}
else {
dispatch_async(dispatch_get_main_queue(), restartBlock);
}
}
- (void)invalidateDecompressionSession {
@synchronized(self) {
if (self->_decompressionSession != NULL) {
VTDecompressionSessionInvalidate(self->_decompressionSession);
CFRelease(self->_decompressionSession);
self->_decompressionSession = nil;
}
}
}
- (void)startOrRestartFrameInterpolation {
if (self->_displayLink == nil || self->_renderingBackend != RENDER_AVSB) {
return;
}
DataManager *dataMan = [[DataManager alloc] init];
TemporarySettings *settings = [dataMan getSettings];
NSInteger maximumDimension = settings.interpolationMaximumDimension.integerValue;
NSInteger maximumPixelCount = settings.interpolationMaximumPixelCount.integerValue;
dispatch_async(self->_vtq, ^{
FrameInterpolator *oldInterpolator = self->_frameInterpolator;
self->_frameInterpolator = [[FrameInterpolator alloc]
initWithMaximumDimension:maximumDimension
maximumPixelCount:maximumPixelCount];
self->_frameInterpolator.isEnabled = YES;
self->_frameInterpolationPaused = NO;
[oldInterpolator reset];
self->_queueSize = 8;
self->_needRequeuing = YES;
[self->_frameQueue setHighWaterMark:self->_queueSize];
[self->_frameQueue clear];
[self invalidateDecompressionSession];
[self restartDisplayLinkForInterpolationEnabled:YES];
LiRequestIdrFrame();
Log(LOG_I, @"Frame interpolation started or restarted with limits %ld / %ld pixels",
(long)maximumDimension, (long)maximumPixelCount);
});
}
- (void)stopFrameInterpolation {
if (self->_displayLink == nil || self->_renderingBackend != RENDER_AVSB) {
return;
}
DataManager *dataMan = [[DataManager alloc] init];
TemporarySettings *settings = [dataMan getSettings];
NSInteger normalQueueSize = settings.frameQueueSize.integerValue;
dispatch_async(self->_vtq, ^{
FrameInterpolator *oldInterpolator = self->_frameInterpolator;
self->_frameInterpolator = nil;
self->_frameInterpolationPaused = NO;
[oldInterpolator reset];
self->_queueSize = (int32_t)normalQueueSize;
self->_needRequeuing = self->_queueSize > 0;
[self->_frameQueue setHighWaterMark:MAX(1, self->_queueSize)];
[self->_frameQueue clear];
[self invalidateDecompressionSession];
[self restartDisplayLinkForInterpolationEnabled:NO];
LiRequestIdrFrame();
Log(LOG_I, @"Frame interpolation stopped");
});
}
# pragma mark DisplayLink vsync callback
- (void)setupWithVideoFormat:(int)videoFormat width:(int)videoWidth height:(int)videoHeight frameRate:(int)frameRate fullRange:(BOOL)fullRange request10BitCodec:(BOOL)request10BitCodec
@@ -210,7 +345,9 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
_renderingBackend = RENDER_AVSB;
if (kEnableFrameInterpolation) {
_frameInterpolator = [[FrameInterpolator alloc] init];
_frameInterpolator = [[FrameInterpolator alloc]
initWithMaximumDimension:settings.interpolationMaximumDimension.integerValue
maximumPixelCount:settings.interpolationMaximumPixelCount.integerValue];
_frameInterpolator.isEnabled = YES;
}
@@ -229,10 +366,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:displayLinkSelector];
NSInteger targetFrameRate = self->_frameRate;
if (self->_frameInterpolator != nil) {
targetFrameRate = MIN(self->_frameRate * 2, self->_maxRefreshRate);
}
NSInteger targetFrameRate = [self displayLinkFrameRateForInterpolationEnabled:self->_frameInterpolator != nil];
if (@available(iOS 15.0, tvOS 15.0, *)) {
_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(targetFrameRate, targetFrameRate, targetFrameRate);
@@ -289,61 +423,26 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
nativePixelFormat = self->_fullRange ? frFormat : vrFormat;
}
BOOL interpolationRequested = self->_frameInterpolator != nil;
NSNumber *pixelFormat = interpolationRequested ? @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) : nativePixelFormat;
OSType interpolationPixelFormat = 0;
if (interpolationRequested) {
LogOnce(LOG_I, @"Frame interpolation enabled; requesting 420v output from VTDecompressionSession");
interpolationPixelFormat = [FrameInterpolator supportedPixelFormatClosestTo:nativePixelFormat.unsignedIntValue];
if (interpolationPixelFormat == 0) {
LogOnce(LOG_W, @"Frame interpolation disabled because the device reports no supported pixel format");
[self->_frameInterpolator reset];
self->_frameInterpolator = nil;
interpolationRequested = NO;
[self restartDisplayLinkForInterpolationEnabled:NO];
}
}
NSNumber *pixelFormat = interpolationRequested ? @(interpolationPixelFormat) : nativePixelFormat;
if (interpolationRequested) {
LogOnce(LOG_I, @"Frame interpolation enabled; requesting pixel format %@ from VTDecompressionSession", pixelFormat);
}
NSMutableDictionary *destinationPixelBufferAttributes = [@{
(id)kCVPixelBufferPixelFormatTypeKey : pixelFormat
} mutableCopy];
#endif
#if !TARGET_OS_SIMULATOR
if (interpolationRequested) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(_formatDesc);
NSDictionary *interpolationSourceAttributes =
[FrameInterpolator sourcePixelBufferAttributesForDimensions:dimensions];
CFDictionaryRef resolvedAttributes = NULL;
CVReturn resolveStatus = kCVReturnInvalidArgument;
if (interpolationSourceAttributes != nil) {
NSArray *attributeRequirements = @[
destinationPixelBufferAttributes,
interpolationSourceAttributes
];
resolveStatus = CVPixelBufferCreateResolvedAttributesDictionary(
kCFAllocatorDefault,
(__bridge CFArrayRef)attributeRequirements,
&resolvedAttributes);
}
if (resolveStatus == kCVReturnSuccess && resolvedAttributes != NULL) {
destinationPixelBufferAttributes =
[(__bridge NSDictionary *)resolvedAttributes mutableCopy];
CFRelease(resolvedAttributes);
} else {
if (resolvedAttributes != NULL) {
CFRelease(resolvedAttributes);
}
Log(LOG_W,
@"Unable to resolve frame-interpolation source attributes for %dx%d (status %d); using native decode output",
dimensions.width,
dimensions.height,
resolveStatus);
[self->_frameInterpolator reset];
self->_frameInterpolator = nil;
interpolationRequested = NO;
destinationPixelBufferAttributes = [@{
(id)kCVPixelBufferPixelFormatTypeKey : nativePixelFormat
} mutableCopy];
NSInteger targetFrameRate = MIN(self->_frameRate, self->_maxRefreshRate);
if (@available(iOS 15.0, tvOS 15.0, *)) {
self->_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(targetFrameRate, targetFrameRate, targetFrameRate);
} else {
self->_displayLink.preferredFramesPerSecond = targetFrameRate;
}
}
destinationPixelBufferAttributes[(id)kCVPixelBufferIOSurfacePropertiesKey] = @{};
}
#endif
@@ -360,7 +459,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
OSStatus status = [self setupDecompressionSessionWithAttributes:destinationPixelBufferAttributes];
if (status != noErr) {
Log(LOG_W, @"Falling back from 420v frame-interpolation decode output to native pixel format %@", nativePixelFormat);
Log(LOG_W, @"Falling back from frame-interpolation decode output %@ to native pixel format %@", pixelFormat, nativePixelFormat);
[self->_frameInterpolator reset];
self->_frameInterpolator = nil;
destinationPixelBufferAttributes = [@{
@@ -372,12 +471,7 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
}
[self setupDecompressionSessionWithAttributes:destinationPixelBufferAttributes];
NSInteger targetFrameRate = MIN(self->_frameRate, self->_maxRefreshRate);
if (@available(iOS 15.0, tvOS 15.0, *)) {
self->_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(targetFrameRate, targetFrameRate, targetFrameRate);
} else {
self->_displayLink.preferredFramesPerSecond = targetFrameRate;
}
[self restartDisplayLinkForInterpolationEnabled:NO];
}
#else
[self setupDecompressionSessionWithAttributes:destinationPixelBufferAttributes];
@@ -574,6 +668,12 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
}
- (void)cleanup{
@synchronized([VideoDecoderRenderer class]) {
if (sActiveRenderer == self) {
sActiveRenderer = nil;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[self->_frameQueue stop];
@@ -1187,9 +1287,24 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
[frame setFormatDesc:self->_formatDesc];
}
if (self->_frameInterpolator != nil) {
[self->_frameInterpolator processFrame:frame completion:^(NSArray *frames) {
FrameInterpolator *interpolator = self->_frameInterpolator;
if (interpolator != nil && appDidEnterBackgroundWithoutPip) {
if (!self->_frameInterpolationPaused) {
self->_frameInterpolationPaused = YES;
[interpolator setPaused:YES];
}
}
else if (interpolator != nil) {
if (self->_frameInterpolationPaused) {
self->_frameInterpolationPaused = NO;
[interpolator setPaused:NO];
}
[interpolator processFrame:frame completion:^(NSArray *frames) {
dispatch_async(self->_vtq, ^{
if (self->_frameInterpolator != interpolator) {
return;
}
int framesDropped = 0;
for (Frame *outputFrame in (NSArray<Frame *> *)frames) {
framesDropped += [self->_frameQueue enqueue:outputFrame withSlackSize:3];
@@ -1403,22 +1518,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
- (void)resetFramePacing {
// Ensure this only runs for the AVSampleBuffer rendering backend and that the display link exists.
if (_renderingBackend == RENDER_AVSB && _displayLink) {
Log(LOG_I, @"Frame pacing is being reset to %d FPS...", self->_frameRate);
// Toggling the paused state can help re-engage the display link with the
// run loop correctly after the app resumes from a background state like PiP.
_displayLink.paused = YES;
// Re-apply the desired frame rate range. This is the critical hint for ProMotion
// that may have been lost or ignored during the PiP transition.
if (@available(iOS 15.0, *)) {
_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(self->_frameRate, self->_frameRate, self->_frameRate);
} else {
_displayLink.preferredFramesPerSecond = self->_frameRate;
}
// Resume the display link immediately.
_displayLink.paused = NO;
[self restartDisplayLinkForInterpolationEnabled:self->_frameInterpolator.isEnabled];
} else if (_renderingBackend == RENDER_METAL) {
@synchronized(self) {
if (_decompressionSession != nil) {
+17
View File
@@ -55,6 +55,23 @@ import UIKit
completion: {})
}
}
@objc public static func handleFrameInterpolationPixelFormatTip(in vc: UIViewController?) {
let key = "hasShownFrameInterpolationPixelFormatTip"
guard !UserDefaults.standard.bool(forKey: key) else {
return
}
UserDefaults.standard.set(true, forKey: key)
AlertControllerUtil.showAlert(
in: vc,
title: LocalizationHelper.localizedString(forKey: "Tips"),
message: "\n\(LocalizationHelper.localizedString(forKey: "Most devices currently support frame interpolation only for 8-bit YUV 4:2:0 SDR video. In this case, if HDR or YUV 4:4:4 is enabled, intermediate frames will still be generated in 8-bit YUV 4:2:0 SDR."))",
withCancel: false,
buttonTitle: LocalizationHelper.localizedString(forKey: "Got it!"),
countdown: 5
)
}
@objc public static func needUpdateDefaultSettings() -> Bool {
// let key = "needUpdateDefaultSettings20260226-1"
@@ -852,8 +852,8 @@ static NSMutableSet* hostList;
}
}
_streamConfig.height = [streamSettings.height intValue];
_streamConfig.width = [streamSettings.width intValue];
_streamConfig.height = streamSettings.height.intValue;
_streamConfig.width = streamSettings.width.intValue;
NSLog(@"saveSettings.width %d, %d", _streamConfig.width, _streamConfig.height);
#if TARGET_OS_TV
@@ -1399,11 +1399,16 @@ static NSMutableSet* hostList;
[self.settingsViewController setHidden:_settingsExpandedInStreamView forStack:self.settingsViewController.appThemeStack];
[self.settingsViewController.renderingBackendSelector setEnabled:!_settingsExpandedInStreamView];
// Enable frame pacing mode selector only if not in stream view AND not in performance mode
BOOL shouldEnableFramePacingSelector = !_settingsExpandedInStreamView && (self.settingsViewController.renderingBackendSelector.selectedSegmentIndex != RENDER_METAL);
[self.settingsViewController.framePacingModeSelector setEnabled:shouldEnableFramePacingSelector];
BOOL shouldEnableFrameQueueSettings = !_settingsExpandedInStreamView && (self.settingsViewController.renderingBackendSelector.selectedSegmentIndex != RENDER_METAL);
// [self.settingsViewController.framePacingModeSelector setEnabled:shouldEnableFrameQueueSettings];
[self.settingsViewController.framePacingModeSelector setEnabled:!_settingsExpandedInStreamView forSegmentAtIndex:0];
[self.settingsViewController.framePacingModeSelector setEnabled:!_settingsExpandedInStreamView forSegmentAtIndex:1];
// [self.settingsViewController.frameTimebaseSwitch setEnabled:shouldEnableFramePacing];
[self.settingsViewController.asyncFrameDequeueSwitch setEnabled:shouldEnableFramePacingSelector];
[self.settingsViewController setHidden:_settingsExpandedInStreamView || !(shouldEnableFramePacingSelector && self.settingsViewController.framePacingModeSelector.selectedSegmentIndex == FramePacingModeQueue) forStack:self.settingsViewController.frameQueueSizeStack];
[self.settingsViewController.asyncFrameDequeueSwitch setEnabled:shouldEnableFrameQueueSettings];
[self.settingsViewController setHidden:_settingsExpandedInStreamView || !(shouldEnableFrameQueueSettings && self.settingsViewController.framePacingModeSelector.selectedSegmentIndex == FramePacingModeQueue) forStack:self.settingsViewController.frameQueueSizeStack];
[self.settingsViewController setHidden:!(self.settingsViewController.framePacingModeSelector.selectedSegmentIndex == FramePacingModeInterpolation) forStack:self.settingsViewController.interpolationLevelStack];
[self.settingsViewController setHidden:_settingsExpandedInStreamView || !(self.settingsViewController.framePacingModeSelector.selectedSegmentIndex == FramePacingModeInterpolation) forStack:self.settingsViewController.streamDimensionScaleStack];
// Disable mic switch if sunshine does not support mic redirection
[self.settingsViewController.redirectMicSwitch setEnabled:!_settingsExpandedInStreamView||streamFrameViewController.micStreamInitialized];
@@ -2145,24 +2150,19 @@ static NSMutableSet* hostList;
.height = (int32_t)currentSettings.height.intValue,
};
[self fillResolutionTable:tempResolutionTable externalDisplayMode:currentSettings.externalDisplayMode.intValue];
return;
/*
int selectedIndex = currentSettings.resolutionSelected.intValue;
if (selectedIndex >= 0 && selectedIndex < 6) {
CMVideoDimensions originalDimensions = tempResolutionTable[selectedIndex];
CMVideoDimensions targetDimensions;
if(currentSettings.framePacingMode.intValue == FramePacingModeInterpolation) {
targetDimensions = [FrameInterpolator interpolatableDimensionsBy:originalDimensions];
if(!(targetDimensions.width>0 && targetDimensions.height>0)) targetDimensions = [FrameInterpolator interpolatableDimensionsBy720p:originalDimensions];
}
else targetDimensions = originalDimensions;
CMVideoDimensions targetDimensions = originalDimensions;
currentSettings.width = @(targetDimensions.width);
currentSettings.height = @(targetDimensions.height);
NSLog(@"Updated resolution to: %@ x %@", currentSettings.width, currentSettings.height);
}
[dataMan saveData];
*/
}
#if TARGET_OS_TV
@@ -128,6 +128,10 @@
@property (strong, nonatomic) IBOutlet UIStackView *graphOpacityStack;
@property (strong, nonatomic) IBOutlet UISegmentedControl *renderingBackendSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *framePacingModeSelector;
@property (strong, nonatomic) IBOutlet UIStackView *interpolationLevelStack;
@property (strong, nonatomic) IBOutlet UISlider *interpolationLevelSlider;
@property (strong, nonatomic) IBOutlet UIStackView *streamDimensionScaleStack;
@property (strong, nonatomic) IBOutlet UISlider *streamDimensionScaleSlider;
@property (strong, nonatomic) IBOutlet UIStackView *backgroundSessionTimerStack;
@property (strong, nonatomic) IBOutlet UISlider *backgroundSessionTimerSlider;
+113 -11
View File
@@ -633,6 +633,12 @@ BOOL isCustomResolution(int resolutionSelected) {
if(!settingsViewAlreadyAppeared) {
if(![self contentOffsetRestored]) _scrollView.contentOffset = CGPointMake(_scrollView.contentOffset.x, tempSettings.settingsMenuOffset.floatValue);
dispatch_async(dispatch_get_main_queue(), ^{
[self.view layoutIfNeeded];
[self updateResolutionTable];
[self updateInterpolationLevelSliderWithMaximumDimension:self->tempSettings.interpolationMaximumDimension.integerValue];
[self.streamDimensionScaleSlider sendActionsForControlEvents:UIControlEventValueChanged];
});
}
if(@available(iOS 13.0, *)) if(ControllerUtil.primaryGCController) [self restoreControllerNavigationHighlight];
@@ -656,6 +662,13 @@ BOOL isCustomResolution(int resolutionSelected) {
bool unlockDisplayOrientationFlipped = tempSettings.unlockDisplayOrientation != (_unlockDisplayOrientationSelector.selectedSegmentIndex == 1);
if(unlockDisplayOrientationFlipped) [_mainFrameViewController setNeedsUpdateAllowedOrientation]; // handle allow portratit on & off
if (@available(iOS 13.0, *)) if(ControllerNavigator.radialMenuView.superview) [ControllerNavigator updateRadialMenu];
if(self.framePacingModeSelector.selectedSegmentIndex == FramePacingModeInterpolation) {
[VideoDecoderRenderer startOrRestartFrameInterpolation];
}
else {
[VideoDecoderRenderer stopFrameInterpolation];
}
}
@@ -884,6 +897,12 @@ BOOL isCustomResolution(int resolutionSelected) {
[self addSetting:self.framePacingStack ofId:@"framePacingStack" to:videoSection];
if(!FrameInterpolator.deviceSupportsInterpolation) [self.framerateSelector setEnabled:false forSegmentAtIndex:FramePacingModeInterpolation];
self.interpolationLevelStack.hasDynamicLabel = YES;
[self addSetting:self.interpolationLevelStack ofId:@"interpolationLevelStack" to:videoSection];
self.streamDimensionScaleStack.hasDynamicLabel = YES;
[self addSetting:self.streamDimensionScaleStack ofId:@"streamDimensionScaleStack" to:videoSection];
self.frameQueueSizeStack.hasDynamicLabel = YES;
[self addSetting:self.frameQueueSizeStack ofId:@"frameQueueSizeStack" to:videoSection];
@@ -2235,6 +2254,12 @@ BOOL isCustomResolution(int resolutionSelected) {
NSInteger framePacingMode = [self->tempSettings.framePacingMode integerValue];
self.framePacingModeSelector.selectedSegmentIndex = framePacingMode;
((UILabel *)self.interpolationLevelStack.arrangedSubviews.firstObject).text =
[LocalizationHelper localizedStringForKey:@"Interpolation Resolution"];
[self.interpolationLevelSlider addTarget:self action:@selector(interpolationLevelSliderMoved:) forControlEvents:UIControlEventValueChanged];
((UILabel *)self.streamDimensionScaleStack.arrangedSubviews.firstObject).text =
[LocalizationHelper localizedStringForKey:@"Scaled Stream Resolution"];
[self.streamDimensionScaleSlider addTarget:self action:@selector(streamDimensionScaleSliderMoved:) forControlEvents:UIControlEventValueChanged];
[self.framePacingModeSelector addTarget:self action:@selector(framePacingModeChanged:) forControlEvents:UIControlEventValueChanged];
[self.framePacingModeSelector sendActionsForControlEvents:UIControlEventValueChanged];
@@ -2311,6 +2336,11 @@ BOOL isCustomResolution(int resolutionSelected) {
self->_lastSelectedResolutionIndex = resolution;
self.resolutionSelector.selectedSegmentIndex = resolution;
[self updateInterpolationLevelSliderWithMaximumDimension:self->tempSettings.interpolationMaximumDimension.integerValue];
self.streamDimensionScaleSlider.minimumValue = 0;
self.streamDimensionScaleSlider.maximumValue = 1;
self.streamDimensionScaleSlider.value = MIN(1, MAX(0, self->tempSettings.streamDimensionScale.floatValue));
[self.streamDimensionScaleSlider sendActionsForControlEvents:UIControlEventValueChanged];
[self.resolutionSelector addTarget:self action:@selector(newResolutionChosen) forControlEvents:UIControlEventValueChanged];
self.framerateSelector.selectedSegmentIndex = framerate;
@@ -2690,10 +2720,11 @@ BOOL isCustomResolution(int resolutionSelected) {
- (void)framePacingModeChanged:(UISegmentedControl *)sender {
// Hide frame queue size for Off and Legacy modes
[GenericUtils handleLegacyFramePacingTipIn:self with:sender passAlert:settingsViewJustExpanded uiAction:^{
[self setHidden:(sender.selectedSegmentIndex == FramePacingModeOff || sender.selectedSegmentIndex == FramePacingModeLegacy) forStack:self.frameQueueSizeStack];
// [self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue) forStack:self.frameTimebaseStack];
[self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue
|| sender.selectedSegmentIndex != FramePacingModeInterpolation
|| self->_mainFrameViewController.isStreaming) forStack:self.frameQueueSizeStack];
// [self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue) forStack:self.frameTimebaseStack];
[self setHidden:((sender.selectedSegmentIndex != FramePacingModeQueue
&& sender.selectedSegmentIndex != FramePacingModeInterpolation)
|| self.renderingBackendSelector.selectedSegmentIndex != 0) forStack:self.asyncFrameDequeueStack];
if(sender.selectedSegmentIndex == FramePacingModeOff || sender.selectedSegmentIndex == FramePacingModeLegacy){
@@ -2705,6 +2736,8 @@ BOOL isCustomResolution(int resolutionSelected) {
[self setHidden:(sender.selectedSegmentIndex == FramePacingModeOff || sender.selectedSegmentIndex == FramePacingModeLegacy) forStack:self.performanceGraphStack];
bool enableInterpolation = sender.selectedSegmentIndex == FramePacingModeInterpolation;
[self setHidden:!enableInterpolation forStack:self.interpolationLevelStack];
[self setHidden:!enableInterpolation || self->_mainFrameViewController.isStreaming forStack:self.streamDimensionScaleStack];
[self widget:self.frameQueueSizeSlider setEnabled:!enableInterpolation];
[self updateResolutionDisplayLabel];
if(enableInterpolation){
@@ -2712,6 +2745,9 @@ BOOL isCustomResolution(int resolutionSelected) {
if(!FrameInterpolator.dimensionLimitsAreKnown){
NSLog(@"FrameInterpolator dimensionLimitsUnknown");
}
if(!self->settingsViewJustExpanded){
[GenericUtils handleFrameInterpolationPixelFormatTipIn:self];
}
}
}];
@@ -3663,7 +3699,7 @@ BOOL isCustomResolution(int resolutionSelected) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
if(self.framerateSelector.selectedSegmentIndex == 2
if(self.framerateSelector.selectedSegmentIndex == self.framerateSelector.numberOfSegments - 1
&& self.framePacingModeSelector.selectedSegmentIndex == FramePacingModeInterpolation) self.framerateSelector.selectedSegmentIndex = self.framerateSelector.previousSelectedSegmentIndex;
// NSInteger fps = [self getChosenFrameRate];
@@ -3735,10 +3771,13 @@ BOOL isCustomResolution(int resolutionSelected) {
}
- (void) newResolutionChosen {
[self updateBitrate];
[self updateResolutionDisplayLabel];
_lastSelectedResolutionIndex = self.resolutionSelector.selectedSegmentIndex;
[self updateResolutionTable];
if (self.mainFrameViewController.settingsExpandedInStreamView) {
[self updateResolutionDisplayLabel];
} else {
[self updateResolutionTable];
}
[self updateBitrate];
}
- (void)customResolutionSwitched:(UISwitch* )sender{
@@ -3836,11 +3875,44 @@ BOOL isCustomResolution(int resolutionSelected) {
}
- (void) updateResolutionDisplayLabel {
CMVideoDimensions dimensions = [self getChosenStreamDimensions];
CMVideoDimensions dimensions = [self getChosenPresetStreamDimensions];
NSInteger width = dimensions.width;
NSInteger height = dimensions.height;
[self findDynamicLabelFromStack:_resolutionSelectorStack].text = [NSString stringWithFormat:@"%ld × %ld", (long)width, (long)height];
if (self.interpolationLevelSlider != nil) {
[self.interpolationLevelSlider sendActionsForControlEvents:UIControlEventValueChanged];
}
}
- (void)updateInterpolationLevelSliderWithMaximumDimension:(NSInteger)savedMaximumDimension {
self.interpolationLevelSlider.minimumValue = 0;
self.interpolationLevelSlider.maximumValue = 1;
self.interpolationLevelSlider.value = [FrameInterpolator
interpolationLevelForDimensions:[self getChosenPresetStreamDimensions]
savedMaximumDimension:savedMaximumDimension];
[self.interpolationLevelSlider sendActionsForControlEvents:UIControlEventValueChanged];
}
- (void)interpolationLevelSliderMoved:(UISlider *)sender {
InterpolationResolutionConfiguration *configuration = [self getCurrentInterpolationResolutionConfiguration];
[self findDynamicLabelFromStack:self.interpolationLevelStack].text =
[NSString stringWithFormat:@" %d × %d ", configuration.dimensions.width, configuration.dimensions.height];
if (self.streamDimensionScaleSlider != nil) {
[self.streamDimensionScaleSlider sendActionsForControlEvents:UIControlEventValueChanged];
}
}
- (InterpolationResolutionConfiguration *)getCurrentInterpolationResolutionConfiguration {
return [FrameInterpolator
resolutionConfigurationForDimensions:[self getChosenPresetStreamDimensions]
level:self.interpolationLevelSlider.value];
}
- (void)streamDimensionScaleSliderMoved:(UISlider *)sender {
CMVideoDimensions dimensions = [self getChosenStreamDimensions];
[self findDynamicLabelFromStack:self.streamDimensionScaleStack].text =
[NSString stringWithFormat:@" %d × %d ", dimensions.width, dimensions.height];
}
- (void) touchMoveEventIntervalSliderMoved:(UISlider* )sender{
@@ -3911,7 +3983,7 @@ BOOL isCustomResolution(int resolutionSelected) {
}
}
- (CMVideoDimensions)getChosenStreamDimensions {
- (CMVideoDimensions)getChosenPresetStreamDimensions {
// because the 4k resolution can be removed
CMVideoDimensions originalDimensions;
if (self.customResolutionSwitch.isOn) {
@@ -3920,6 +3992,12 @@ BOOL isCustomResolution(int resolutionSelected) {
originalDimensions = resolutionTable[self.resolutionSelector.selectedSegmentIndex];
}
if(originalDimensions.width == 0 || originalDimensions.height == 0) {
originalDimensions.width = tempSettings.width.intValue;
originalDimensions.height = tempSettings.height.intValue;
}
/*
CMVideoDimensions targetDimensions = self.framePacingModeSelector.selectedSegmentIndex == FramePacingModeInterpolation
? [FrameInterpolator interpolatableDimensionsBy:originalDimensions]
: originalDimensions;
@@ -3929,8 +4007,25 @@ BOOL isCustomResolution(int resolutionSelected) {
}
NSLog(@"dimensions: %d, %d", targetDimensions.width, targetDimensions.height);
*/
return originalDimensions;
}
return targetDimensions;
- (CMVideoDimensions)getChosenStreamDimensions {
CMVideoDimensions presetDimensions = [self getChosenPresetStreamDimensions];
if (self.streamDimensionScaleSlider == nil) {
return [FrameInterpolator
scaledStreamDimensionsWithPresetDimensions:presetDimensions
interpolationDimensions:presetDimensions
scale:1];
}
InterpolationResolutionConfiguration *configuration = [self getCurrentInterpolationResolutionConfiguration];
return [FrameInterpolator
scaledStreamDimensionsWithPresetDimensions:presetDimensions
interpolationDimensions:configuration.dimensions
scale:self.streamDimensionScaleSlider.value];
}
- (UIStackView *)findFlatStackViewFrom:(UIView *)view {
@@ -4299,6 +4394,11 @@ BOOL isCustomResolution(int resolutionSelected) {
NSInteger renderingBackend = self.renderingBackendSelector.selectedSegmentIndex;
NSInteger framePacingMode = self.framePacingModeSelector.selectedSegmentIndex;
InterpolationResolutionConfiguration *interpolationConfiguration =
[self getCurrentInterpolationResolutionConfiguration];
NSInteger interpolationMaximumDimension = interpolationConfiguration.maximumDimension;
NSInteger interpolationMaximumPixelCount = interpolationConfiguration.maximumPixelCount;
CGFloat streamDimensionScale = self.streamDimensionScaleSlider.value;
NSInteger onscreenControls = self.onScreenWidgetSelector.selectedSegmentIndex;
NSInteger keyboardToggleFingers = self.softKeyboardGestureSelector.selectedSegmentIndex == 3 ? 20 : self.softKeyboardGestureSelector.selectedSegmentIndex+3;
NSInteger oscLayoutToolFingers = (uint16_t)self->oswLayoutFingers;
@@ -4428,6 +4528,9 @@ BOOL isCustomResolution(int resolutionSelected) {
graphOpacity:graphOpacity
renderingBackend:renderingBackend
framePacingMode:framePacingMode
interpolationMaximumDimension:interpolationMaximumDimension
interpolationMaximumPixelCount:interpolationMaximumPixelCount
streamDimensionScale:streamDimensionScale
sendDummyEvent:sendDummyEvent
rememberFoldState:rememberFoldState
singleTapSensitivy:singleTapSensitivity
@@ -4466,7 +4569,6 @@ BOOL isCustomResolution(int resolutionSelected) {
}];
});
}
}
- (void)didReceiveMemoryWarning {
+24
View File
@@ -3056,6 +3056,30 @@
}
}
},
"FIp-Lv-Lbl.text" : {
"comment" : "Class = \"UILabel\"; text = \"Interpolation Resolution\"; ObjectID = \"FIp-Lv-Lbl\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Interpolation Resolution"
}
}
}
},
"FIp-Sc-Lbl.text" : {
"comment" : "Class = \"UILabel\"; text = \"Scaled Stream Resolution\"; ObjectID = \"FIp-Sc-Lbl\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Scaled Stream Resolution"
}
}
}
},
"FJv-HJ-UYJ.text" : {
"comment" : "Class = \"UILabel\"; text = \"Touch Move Event Interval\"; ObjectID = \"FJv-HJ-UYJ\";",
"extractionState" : "extracted_with_value",
+24
View File
@@ -2557,6 +2557,30 @@
}
}
},
"FIi-Lv-Lbl.text" : {
"comment" : "Class = \"UILabel\"; text = \"Interpolation Resolution\"; ObjectID = \"FIi-Lv-Lbl\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Interpolation Resolution"
}
}
}
},
"FIi-Sc-Lbl.text" : {
"comment" : "Class = \"UILabel\"; text = \"Scaled Stream Resolution\"; ObjectID = \"FIi-Sc-Lbl\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Scaled Stream Resolution"
}
}
}
},
"fRl-Ra-Lbl.text" : {
"comment" : "Class = \"UILabel\"; text = \"Full Color Range\"; ObjectID = \"fRl-Ra-Lbl\";",
"extractionState" : "extracted_with_value",