Add Frame Pacing feature

This commit is contained in:
Felipe Cavalcanti
2022-02-02 13:37:07 -03:00
parent bd582aa6c0
commit 7d6cb247b8
19 changed files with 141 additions and 28 deletions

View File

@@ -22,6 +22,7 @@
multiController:(BOOL)multiController
audioOnPC:(BOOL)audioOnPC
useHevc:(BOOL)useHevc
useFramePacing:(BOOL)useFramePacing
enableHdr:(BOOL)enableHdr
btMouseSupport:(BOOL)btMouseSupport
absoluteTouchMode:(BOOL)absoluteTouchMode

View File

@@ -61,6 +61,7 @@
multiController:(BOOL)multiController
audioOnPC:(BOOL)audioOnPC
useHevc:(BOOL)useHevc
useFramePacing:(BOOL)useFramePacing
enableHdr:(BOOL)enableHdr
btMouseSupport:(BOOL)btMouseSupport
absoluteTouchMode:(BOOL)absoluteTouchMode
@@ -77,6 +78,7 @@
settingsToSave.multiController = multiController;
settingsToSave.playAudioOnPC = audioOnPC;
settingsToSave.useHevc = useHevc;
settingsToSave.useFramePacing = useFramePacing;
settingsToSave.enableHdr = enableHdr;
settingsToSave.btMouseSupport = btMouseSupport;
settingsToSave.absoluteTouchMode = absoluteTouchMode;

View File

@@ -19,6 +19,7 @@
@property (nonatomic, retain) NSNumber * onscreenControls;
@property (nonatomic, retain) NSString * uniqueId;
@property (nonatomic) BOOL useHevc;
@property (nonatomic) BOOL useFramePacing;
@property (nonatomic) BOOL multiController;
@property (nonatomic) BOOL playAudioOnPC;
@property (nonatomic) BOOL optimizeGames;

View File

@@ -35,6 +35,7 @@
self.framerate = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"framerate"]];
assert([self.framerate intValue] != 0);
self.useHevc = [[NSUserDefaults standardUserDefaults] boolForKey:@"useHevc"];
self.useFramePacing = [[NSUserDefaults standardUserDefaults] boolForKey:@"useFramePacing"];
self.playAudioOnPC = [[NSUserDefaults standardUserDefaults] boolForKey:@"audioOnPC"];
self.enableHdr = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableHdr"];
self.optimizeGames = [[NSUserDefaults standardUserDefaults] boolForKey:@"optimizeGames"];
@@ -66,6 +67,7 @@
self.height = settings.height;
self.width = settings.width;
self.useHevc = settings.useHevc;
self.useFramePacing = settings.useFramePacing;
self.playAudioOnPC = settings.playAudioOnPC;
self.enableHdr = settings.enableHdr;
self.optimizeGames = settings.optimizeGames;

View File

@@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Moonlight v1.6.xcdatamodel</string>
<string>Moonlight v1.7.xcdatamodel</string>
</dict>
</plist>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="20G224" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="hidden" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
</entity>
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="ipv6Address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serverCert" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
<attribute name="absoluteTouchMode" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
<attribute name="btMouseSupport" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="statsOverlay" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
<attribute name="useFramePacing" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="App" positionX="0" positionY="54" width="128" height="118"/>
<element name="Host" positionX="0" positionY="0" width="128" height="210"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="254"/>
</elements>
</model>

View File

@@ -437,7 +437,6 @@ void ClConnectionStatusUpdate(int status)
CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC |
#endif
CAPABILITY_PULL_RENDERER;
LiInitializeAudioCallbacks(&_arCallbacks);
_arCallbacks.init = ArInit;
_arCallbacks.cleanup = ArCleanup;

View File

@@ -27,6 +27,7 @@
@property BOOL enableHdr;
@property BOOL multiController;
@property BOOL allowHevc;
@property BOOL useFramePacing;
@property NSData* serverCert;
@end

View File

@@ -100,7 +100,7 @@
// Initializing the renderer must be done on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
VideoDecoderRenderer* renderer = [[VideoDecoderRenderer alloc] initWithView:self->_renderView callbacks:self->_callbacks];
VideoDecoderRenderer* renderer = [[VideoDecoderRenderer alloc] initWithView:self->_renderView callbacks:self->_callbacks useFramePacing:self->_config.useFramePacing];
self->_connection = [[Connection alloc] initWithConfig:self->_config renderer:renderer connectionCallbacks:self->_callbacks];
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperation:self->_connection];

View File

@@ -12,7 +12,7 @@
@interface VideoDecoderRenderer : NSObject
- (id)initWithView:(UIView*)view callbacks:(id<ConnectionCallbacks>)callbacks;
- (id)initWithView:(UIView*)view callbacks:(id<ConnectionCallbacks>)callbacks useFramePacing:(BOOL)useFramePacing;
- (void)setupWithVideoFormat:(int)videoFormat frameRate:(int)frameRate;
- (void)start;

View File

@@ -24,6 +24,7 @@
CMVideoFormatDescriptionRef formatDesc;
CADisplayLink* _displayLink;
BOOL framePacing;
}
- (void)reinitializeDisplayLayer
@@ -63,12 +64,13 @@
}
}
- (id)initWithView:(StreamView*)view callbacks:(id<ConnectionCallbacks>)callbacks
- (id)initWithView:(StreamView*)view callbacks:(id<ConnectionCallbacks>)callbacks useFramePacing:(BOOL)useFramePacing
{
self = [super init];
_view = view;
_callbacks = callbacks;
framePacing = useFramePacing;
[self reinitializeDisplayLayer];
@@ -90,12 +92,6 @@
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stop
{
[_displayLink invalidate];
}
// TODO: Refactor this
int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
- (void)displayLinkCallback:(CADisplayLink *)sender
@@ -105,9 +101,22 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
while (LiPollNextVideoFrame(&handle, &du)) {
LiCompleteVideoFrame(handle, DrSubmitDecodeUnit(du));
if (framePacing){
// Always keep one pending frame smooth out gaps due to
// network jitter at the cost of 1 frame of latency
if (LiGetPendingVideoFrames() == 1) {
break;
}
}
}
}
- (void)stop
{
[_displayLink invalidate];
}
#define FRAME_START_PREFIX_SIZE 4
#define NALU_START_PREFIX_SIZE 3
#define NAL_LENGTH_PREFIX_SIZE 4
@@ -350,14 +359,14 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
CFDictionarySetValue(dict, kCMSampleAttachmentKey_NotSync, kCFBooleanFalse);
CFDictionarySetValue(dict, kCMSampleAttachmentKey_DependsOnOthers, kCFBooleanFalse);
}
// Enqueue the next frame
[self->displayLayer enqueueSampleBuffer:sampleBuffer];
if (frameType == FRAME_TYPE_IDR) {
// Ensure the layer is visible now
self->displayLayer.hidden = NO;
// Tell our parent VC to hide the progress indicator
[self->_callbacks videoContentShown];
}
@@ -365,7 +374,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
// Dereference the buffers
CFRelease(blockBuffer);
CFRelease(sampleBuffer);
return DR_OK;
}

View File

@@ -626,6 +626,7 @@ static NSMutableSet* hostList;
_streamConfig.optimizeGameSettings = streamSettings.optimizeGames;
_streamConfig.playAudioOnPC = streamSettings.playAudioOnPC;
_streamConfig.allowHevc = streamSettings.useHevc;
_streamConfig.useFramePacing = streamSettings.useFramePacing;
// multiController must be set before calling getConnectedGamepadMask
_streamConfig.multiController = streamSettings.multiController;
@@ -650,7 +651,7 @@ static NSMutableSet* hostList;
//
// It should also be a user preference, since some games may require higher peak
// brightness than the iOS device can support to look correct in HDR mode.
if (@available(iOS 11.3, *)) {
if (@available(iOS 11.3, tvOS 11.2, *)) {
_streamConfig.enableHdr =
app.hdrSupported && // App supported
(app.host.serverCodecModeSupport & 0x200) != 0 && // HEVC Main10 encoding on host PC GPU

View File

@@ -20,6 +20,7 @@
@property (strong, nonatomic) IBOutlet UISegmentedControl *multiControllerSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *audioOnPCSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *hevcSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *framePacingSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *btMouseSelector;
@property (strong, nonatomic) IBOutlet UISegmentedControl *statsOverlaySelector;
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;

View File

@@ -198,6 +198,7 @@ static const int bitrateTable[] = {
[self.statsOverlaySelector setSelectedSegmentIndex:currentSettings.statsOverlay ? 1 : 0];
[self.btMouseSelector setSelectedSegmentIndex:currentSettings.btMouseSupport ? 1 : 0];
[self.optimizeSettingsSelector setSelectedSegmentIndex:currentSettings.optimizeGames ? 1 : 0];
[self.framePacingSelector setSelectedSegmentIndex:currentSettings.useFramePacing ? 1 : 0];
[self.multiControllerSelector setSelectedSegmentIndex:currentSettings.multiController ? 1 : 0];
[self.audioOnPCSelector setSelectedSegmentIndex:currentSettings.playAudioOnPC ? 1 : 0];
NSInteger onscreenControls = [currentSettings.onscreenControls integerValue];
@@ -303,6 +304,7 @@ static const int bitrateTable[] = {
BOOL audioOnPC = [self.audioOnPCSelector selectedSegmentIndex] == 1;
BOOL useHevc = [self.hevcSelector selectedSegmentIndex] == 1;
BOOL btMouseSupport = [self.btMouseSelector selectedSegmentIndex] == 1;
BOOL useFramePacing = [self.framePacingSelector selectedSegmentIndex] == 1;
BOOL absoluteTouchMode = [self.touchModeSelector selectedSegmentIndex] == 1;
BOOL statsOverlay = [self.statsOverlaySelector selectedSegmentIndex] == 1;
[dataMan saveSettingsWithBitrate:_bitrate
@@ -314,6 +316,7 @@ static const int bitrateTable[] = {
multiController:multiController
audioOnPC:audioOnPC
useHevc:useHevc
useFramePacing:useFramePacing
enableHdr:NO
btMouseSupport:btMouseSupport
absoluteTouchMode:absoluteTouchMode