mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-10-12 03:24:19 +00:00
Add 7.1 surround sound support
This commit is contained in:
@@ -14,6 +14,8 @@
|
|||||||
#include <libxml2/libxml/xmlreader.h>
|
#include <libxml2/libxml/xmlreader.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Limelight.h>
|
||||||
|
|
||||||
#define SHORT_TIMEOUT_SEC 2
|
#define SHORT_TIMEOUT_SEC 2
|
||||||
#define NORMAL_TIMEOUT_SEC 5
|
#define NORMAL_TIMEOUT_SEC 5
|
||||||
#define LONG_TIMEOUT_SEC 60
|
#define LONG_TIMEOUT_SEC 60
|
||||||
@@ -214,7 +216,7 @@ static const NSString* HTTPS_PORT = @"47984";
|
|||||||
[Utils bytesToHex:config.riKey], config.riKeyId,
|
[Utils bytesToHex:config.riKey], config.riKeyId,
|
||||||
config.enableHdr ? @"&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0": @"",
|
config.enableHdr ? @"&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0": @"",
|
||||||
config.playAudioOnPC ? 1 : 0,
|
config.playAudioOnPC ? 1 : 0,
|
||||||
(config.audioChannelMask << 16) | config.audioChannelCount,
|
SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config.audioConfiguration),
|
||||||
config.gamepadMask, config.gamepadMask];
|
config.gamepadMask, config.gamepadMask];
|
||||||
Log(LOG_I, @"Requesting: %@", urlString);
|
Log(LOG_I, @"Requesting: %@", urlString);
|
||||||
// This blocks while the app is launching
|
// This blocks while the app is launching
|
||||||
@@ -225,7 +227,7 @@ static const NSString* HTTPS_PORT = @"47984";
|
|||||||
NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d&surroundAudioInfo=%d",
|
NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d&surroundAudioInfo=%d",
|
||||||
_baseHTTPSURL, _uniqueId,
|
_baseHTTPSURL, _uniqueId,
|
||||||
[Utils bytesToHex:config.riKey], config.riKeyId,
|
[Utils bytesToHex:config.riKey], config.riKeyId,
|
||||||
(config.audioChannelMask << 16) | config.audioChannelCount];
|
SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config.audioConfiguration)];
|
||||||
Log(LOG_I, @"Requesting: %@", urlString);
|
Log(LOG_I, @"Requesting: %@", urlString);
|
||||||
// This blocks while the app is resuming
|
// This blocks while the app is resuming
|
||||||
return [self createRequestFromString:urlString timeout:LONG_TIMEOUT_SEC];
|
return [self createRequestFromString:urlString timeout:LONG_TIMEOUT_SEC];
|
||||||
|
|||||||
@@ -100,37 +100,64 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit)
|
|||||||
pts:decodeUnit->presentationTimeMs];
|
pts:decodeUnit->presentationTimeMs];
|
||||||
}
|
}
|
||||||
|
|
||||||
int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int flags)
|
int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION originalOpusConfig, void* context, int flags)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
AudioChannelLayout channelLayout = {};
|
||||||
|
OPUS_MULTISTREAM_CONFIGURATION opusConfig = *originalOpusConfig;
|
||||||
|
|
||||||
// Initialize the circular buffer
|
// Initialize the circular buffer
|
||||||
audioBufferWriteIndex = audioBufferReadIndex = 0;
|
audioBufferWriteIndex = audioBufferReadIndex = 0;
|
||||||
audioSamplesPerFrame = opusConfig->samplesPerFrame;
|
audioSamplesPerFrame = opusConfig.samplesPerFrame;
|
||||||
audioBufferStride = opusConfig->channelCount * opusConfig->samplesPerFrame;
|
audioBufferStride = opusConfig.channelCount * opusConfig.samplesPerFrame;
|
||||||
audioBufferEntries = CIRCULAR_BUFFER_DURATION / (opusConfig->samplesPerFrame / (opusConfig->sampleRate / 1000));
|
audioBufferEntries = CIRCULAR_BUFFER_DURATION / (opusConfig.samplesPerFrame / (opusConfig.sampleRate / 1000));
|
||||||
audioCircularBuffer = malloc(audioBufferEntries * audioBufferStride * sizeof(short));
|
audioCircularBuffer = malloc(audioBufferEntries * audioBufferStride * sizeof(short));
|
||||||
if (audioCircularBuffer == NULL) {
|
if (audioCircularBuffer == NULL) {
|
||||||
Log(LOG_E, @"Error allocating output queue\n");
|
Log(LOG_E, @"Error allocating output queue\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
opusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
switch (opusConfig.channelCount) {
|
||||||
opusConfig->channelCount,
|
case 2:
|
||||||
opusConfig->streams,
|
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
|
||||||
opusConfig->coupledStreams,
|
break;
|
||||||
opusConfig->mapping,
|
case 4:
|
||||||
|
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Quadraphonic;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_7_1;
|
||||||
|
|
||||||
|
// Swap SL/SR and RL/RR to match the selected channel layout
|
||||||
|
opusConfig.mapping[4] = originalOpusConfig->mapping[6];
|
||||||
|
opusConfig.mapping[5] = originalOpusConfig->mapping[7];
|
||||||
|
opusConfig.mapping[6] = originalOpusConfig->mapping[4];
|
||||||
|
opusConfig.mapping[7] = originalOpusConfig->mapping[5];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unsupported channel layout
|
||||||
|
Log(LOG_E, @"Unsupported channel layout: %d\n", opusConfig.channelCount);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
opusDecoder = opus_multistream_decoder_create(opusConfig.sampleRate,
|
||||||
|
opusConfig.channelCount,
|
||||||
|
opusConfig.streams,
|
||||||
|
opusConfig.coupledStreams,
|
||||||
|
opusConfig.mapping,
|
||||||
&err);
|
&err);
|
||||||
|
|
||||||
// Configure the audio session for our app
|
// Configure the audio session for our app
|
||||||
NSError *audioSessionError = nil;
|
NSError *audioSessionError = nil;
|
||||||
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
|
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
|
||||||
|
|
||||||
[audioSession setPreferredSampleRate:opusConfig->sampleRate error:&audioSessionError];
|
[audioSession setPreferredSampleRate:opusConfig.sampleRate error:&audioSessionError];
|
||||||
[audioSession setCategory:AVAudioSessionCategoryPlayback
|
[audioSession setCategory:AVAudioSessionCategoryPlayback
|
||||||
withOptions:AVAudioSessionCategoryOptionMixWithOthers
|
withOptions:AVAudioSessionCategoryOptionMixWithOthers
|
||||||
error:&audioSessionError];
|
error:&audioSessionError];
|
||||||
[audioSession setPreferredIOBufferDuration:(opusConfig->samplesPerFrame / (opusConfig->sampleRate / 1000)) / 1000.0
|
[audioSession setPreferredIOBufferDuration:(opusConfig.samplesPerFrame / (opusConfig.sampleRate / 1000)) / 1000.0
|
||||||
error:&audioSessionError];
|
error:&audioSessionError];
|
||||||
[audioSession setActive: YES error: &audioSessionError];
|
[audioSession setActive: YES error: &audioSessionError];
|
||||||
|
|
||||||
@@ -140,11 +167,11 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v
|
|||||||
OSStatus status;
|
OSStatus status;
|
||||||
|
|
||||||
AudioStreamBasicDescription audioFormat = {0};
|
AudioStreamBasicDescription audioFormat = {0};
|
||||||
audioFormat.mSampleRate = opusConfig->sampleRate;
|
audioFormat.mSampleRate = opusConfig.sampleRate;
|
||||||
audioFormat.mBitsPerChannel = 16;
|
audioFormat.mBitsPerChannel = 16;
|
||||||
audioFormat.mFormatID = kAudioFormatLinearPCM;
|
audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||||
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||||
audioFormat.mChannelsPerFrame = opusConfig->channelCount;
|
audioFormat.mChannelsPerFrame = opusConfig.channelCount;
|
||||||
audioFormat.mBytesPerFrame = audioFormat.mChannelsPerFrame * (audioFormat.mBitsPerChannel / 8);
|
audioFormat.mBytesPerFrame = audioFormat.mChannelsPerFrame * (audioFormat.mBitsPerChannel / 8);
|
||||||
audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame;
|
audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame;
|
||||||
audioFormat.mFramesPerPacket = audioFormat.mBytesPerPacket / audioFormat.mBytesPerFrame;
|
audioFormat.mFramesPerPacket = audioFormat.mBytesPerPacket / audioFormat.mBytesPerFrame;
|
||||||
@@ -157,28 +184,14 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need to specify a channel layout for surround sound configurations
|
// We need to specify a channel layout for surround sound configurations
|
||||||
if (opusConfig->channelCount > 2) {
|
status = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_ChannelLayout, &channelLayout, sizeof(channelLayout));
|
||||||
AudioChannelLayout channelLayout = {};
|
if (status != noErr) {
|
||||||
|
Log(LOG_E, @"Error configuring surround channel layout: %d\n", status);
|
||||||
switch (opusConfig->channelCount) {
|
return status;
|
||||||
case 6:
|
|
||||||
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Unsupported channel layout
|
|
||||||
Log(LOG_E, @"Unsupported channel layout: %d\n", opusConfig->channelCount);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
status = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_ChannelLayout, &channelLayout, sizeof(channelLayout));
|
|
||||||
if (status != noErr) {
|
|
||||||
Log(LOG_E, @"Error configuring surround channel layout: %d\n", status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < AUDIO_QUEUE_BUFFERS; i++) {
|
for (int i = 0; i < AUDIO_QUEUE_BUFFERS; i++) {
|
||||||
status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * opusConfig->samplesPerFrame, &audioBuffers[i]);
|
status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * opusConfig.samplesPerFrame, &audioBuffers[i]);
|
||||||
if (status != noErr) {
|
if (status != noErr) {
|
||||||
Log(LOG_E, @"Error allocating output buffer: %d\n", status);
|
Log(LOG_E, @"Error allocating output buffer: %d\n", status);
|
||||||
return status;
|
return status;
|
||||||
@@ -342,6 +355,7 @@ void ClConnectionStatusUpdate(int status)
|
|||||||
_streamConfig.fps = config.frameRate;
|
_streamConfig.fps = config.frameRate;
|
||||||
_streamConfig.bitrate = config.bitRate;
|
_streamConfig.bitrate = config.bitRate;
|
||||||
_streamConfig.enableHdr = config.enableHdr;
|
_streamConfig.enableHdr = config.enableHdr;
|
||||||
|
_streamConfig.audioConfiguration = config.audioConfiguration;
|
||||||
|
|
||||||
// Use some of the HEVC encoding efficiency improvements to
|
// Use some of the HEVC encoding efficiency improvements to
|
||||||
// reduce bandwidth usage while still gaining some image
|
// reduce bandwidth usage while still gaining some image
|
||||||
@@ -359,18 +373,6 @@ void ClConnectionStatusUpdate(int status)
|
|||||||
_streamConfig.packetSize = 1392;
|
_streamConfig.packetSize = 1392;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (config.audioChannelCount) {
|
|
||||||
case 2:
|
|
||||||
_streamConfig.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
_streamConfig.audioConfiguration = AUDIO_CONFIGURATION_51_SURROUND;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log(LOG_E, @"Unknown audio channel count: %d", config.audioChannelCount);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
// HDR implies HEVC allowed
|
// HDR implies HEVC allowed
|
||||||
if (config.enableHdr) {
|
if (config.enableHdr) {
|
||||||
config.allowHevc = YES;
|
config.allowHevc = YES;
|
||||||
|
|||||||
@@ -23,8 +23,7 @@
|
|||||||
@property int gamepadMask;
|
@property int gamepadMask;
|
||||||
@property BOOL optimizeGameSettings;
|
@property BOOL optimizeGameSettings;
|
||||||
@property BOOL playAudioOnPC;
|
@property BOOL playAudioOnPC;
|
||||||
@property int audioChannelCount;
|
@property int audioConfiguration;
|
||||||
@property int audioChannelMask;
|
|
||||||
@property BOOL enableHdr;
|
@property BOOL enableHdr;
|
||||||
@property BOOL multiController;
|
@property BOOL multiController;
|
||||||
@property BOOL allowHevc;
|
@property BOOL allowHevc;
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
#import "StreamConfiguration.h"
|
#import "StreamConfiguration.h"
|
||||||
|
|
||||||
@implementation StreamConfiguration
|
@implementation StreamConfiguration
|
||||||
@synthesize host, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, streamingRemotely, appName, optimizeGameSettings, playAudioOnPC, audioChannelMask, audioChannelCount, enableHdr, multiController, allowHevc, serverCert;
|
@synthesize host, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, streamingRemotely, appName, optimizeGameSettings, playAudioOnPC, audioConfiguration, enableHdr, multiController, allowHevc, serverCert;
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -35,6 +35,8 @@
|
|||||||
|
|
||||||
#import <VideoToolbox/VideoToolbox.h>
|
#import <VideoToolbox/VideoToolbox.h>
|
||||||
|
|
||||||
|
#include <Limelight.h>
|
||||||
|
|
||||||
@implementation MainFrameViewController {
|
@implementation MainFrameViewController {
|
||||||
NSOperationQueue* _opQueue;
|
NSOperationQueue* _opQueue;
|
||||||
TemporaryHost* _selectedHost;
|
TemporaryHost* _selectedHost;
|
||||||
@@ -548,14 +550,16 @@ static NSMutableSet* hostList;
|
|||||||
|
|
||||||
|
|
||||||
// Probe for supported channel configurations
|
// Probe for supported channel configurations
|
||||||
Log(LOG_I, @"Audio device supports %d channels", [AVAudioSession sharedInstance].maximumOutputNumberOfChannels);
|
long outputChannels = [AVAudioSession sharedInstance].maximumOutputNumberOfChannels;
|
||||||
if ([AVAudioSession sharedInstance].maximumOutputNumberOfChannels >= 6) {
|
Log(LOG_I, @"Audio device supports %d channels", outputChannels);
|
||||||
_streamConfig.audioChannelCount = 6;
|
if (outputChannels >= 8) {
|
||||||
_streamConfig.audioChannelMask = 0xFC;
|
_streamConfig.audioConfiguration = AUDIO_CONFIGURATION_71_SURROUND;
|
||||||
|
}
|
||||||
|
else if (outputChannels >= 6) {
|
||||||
|
_streamConfig.audioConfiguration = AUDIO_CONFIGURATION_51_SURROUND;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_streamConfig.audioChannelCount = 2;
|
_streamConfig.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
|
||||||
_streamConfig.audioChannelMask = 0x3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HDR requires HDR10 game, HDR10 display, and HEVC Main10 decoder on the client.
|
// HDR requires HDR10 game, HDR10 display, and HEVC Main10 decoder on the client.
|
||||||
|
|||||||
Submodule moonlight-common/moonlight-common-c updated: f489c9d725...c2471157c0
Reference in New Issue
Block a user