diff --git a/Limelight/Network/HttpManager.m b/Limelight/Network/HttpManager.m index 6782603..779640f 100644 --- a/Limelight/Network/HttpManager.m +++ b/Limelight/Network/HttpManager.m @@ -14,6 +14,8 @@ #include #include +#include + #define SHORT_TIMEOUT_SEC 2 #define NORMAL_TIMEOUT_SEC 5 #define LONG_TIMEOUT_SEC 60 @@ -214,7 +216,7 @@ static const NSString* HTTPS_PORT = @"47984"; [Utils bytesToHex:config.riKey], config.riKeyId, config.enableHdr ? @"&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0": @"", config.playAudioOnPC ? 1 : 0, - (config.audioChannelMask << 16) | config.audioChannelCount, + SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config.audioConfiguration), config.gamepadMask, config.gamepadMask]; Log(LOG_I, @"Requesting: %@", urlString); // 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", _baseHTTPSURL, _uniqueId, [Utils bytesToHex:config.riKey], config.riKeyId, - (config.audioChannelMask << 16) | config.audioChannelCount]; + SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config.audioConfiguration)]; Log(LOG_I, @"Requesting: %@", urlString); // This blocks while the app is resuming return [self createRequestFromString:urlString timeout:LONG_TIMEOUT_SEC]; diff --git a/Limelight/Stream/Connection.m b/Limelight/Stream/Connection.m index 44350f3..90238b8 100644 --- a/Limelight/Stream/Connection.m +++ b/Limelight/Stream/Connection.m @@ -100,37 +100,64 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) 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; + AudioChannelLayout channelLayout = {}; + OPUS_MULTISTREAM_CONFIGURATION opusConfig = *originalOpusConfig; // Initialize the circular buffer audioBufferWriteIndex = audioBufferReadIndex = 0; - audioSamplesPerFrame = opusConfig->samplesPerFrame; - audioBufferStride = opusConfig->channelCount * opusConfig->samplesPerFrame; - audioBufferEntries = CIRCULAR_BUFFER_DURATION / (opusConfig->samplesPerFrame / (opusConfig->sampleRate / 1000)); + audioSamplesPerFrame = opusConfig.samplesPerFrame; + audioBufferStride = opusConfig.channelCount * opusConfig.samplesPerFrame; + audioBufferEntries = CIRCULAR_BUFFER_DURATION / (opusConfig.samplesPerFrame / (opusConfig.sampleRate / 1000)); audioCircularBuffer = malloc(audioBufferEntries * audioBufferStride * sizeof(short)); if (audioCircularBuffer == NULL) { Log(LOG_E, @"Error allocating output queue\n"); return -1; } - - opusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate, - opusConfig->channelCount, - opusConfig->streams, - opusConfig->coupledStreams, - opusConfig->mapping, + + switch (opusConfig.channelCount) { + case 2: + channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; + break; + 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); // Configure the audio session for our app NSError *audioSessionError = nil; AVAudioSession* audioSession = [AVAudioSession sharedInstance]; - [audioSession setPreferredSampleRate:opusConfig->sampleRate error:&audioSessionError]; + [audioSession setPreferredSampleRate:opusConfig.sampleRate error:&audioSessionError]; [audioSession setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&audioSessionError]; - [audioSession setPreferredIOBufferDuration:(opusConfig->samplesPerFrame / (opusConfig->sampleRate / 1000)) / 1000.0 + [audioSession setPreferredIOBufferDuration:(opusConfig.samplesPerFrame / (opusConfig.sampleRate / 1000)) / 1000.0 error:&audioSessionError]; [audioSession setActive: YES error: &audioSessionError]; @@ -140,11 +167,11 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v OSStatus status; AudioStreamBasicDescription audioFormat = {0}; - audioFormat.mSampleRate = opusConfig->sampleRate; + audioFormat.mSampleRate = opusConfig.sampleRate; audioFormat.mBitsPerChannel = 16; audioFormat.mFormatID = kAudioFormatLinearPCM; audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; - audioFormat.mChannelsPerFrame = opusConfig->channelCount; + audioFormat.mChannelsPerFrame = opusConfig.channelCount; audioFormat.mBytesPerFrame = audioFormat.mChannelsPerFrame * (audioFormat.mBitsPerChannel / 8); 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 - if (opusConfig->channelCount > 2) { - AudioChannelLayout channelLayout = {}; - - switch (opusConfig->channelCount) { - 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; - } + 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++) { - status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * opusConfig->samplesPerFrame, &audioBuffers[i]); + status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * opusConfig.samplesPerFrame, &audioBuffers[i]); if (status != noErr) { Log(LOG_E, @"Error allocating output buffer: %d\n", status); return status; @@ -342,6 +355,7 @@ void ClConnectionStatusUpdate(int status) _streamConfig.fps = config.frameRate; _streamConfig.bitrate = config.bitRate; _streamConfig.enableHdr = config.enableHdr; + _streamConfig.audioConfiguration = config.audioConfiguration; // Use some of the HEVC encoding efficiency improvements to // reduce bandwidth usage while still gaining some image @@ -359,18 +373,6 @@ void ClConnectionStatusUpdate(int status) _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 if (config.enableHdr) { config.allowHevc = YES; diff --git a/Limelight/Stream/StreamConfiguration.h b/Limelight/Stream/StreamConfiguration.h index 7c8ec33..145db82 100644 --- a/Limelight/Stream/StreamConfiguration.h +++ b/Limelight/Stream/StreamConfiguration.h @@ -23,8 +23,7 @@ @property int gamepadMask; @property BOOL optimizeGameSettings; @property BOOL playAudioOnPC; -@property int audioChannelCount; -@property int audioChannelMask; +@property int audioConfiguration; @property BOOL enableHdr; @property BOOL multiController; @property BOOL allowHevc; diff --git a/Limelight/Stream/StreamConfiguration.m b/Limelight/Stream/StreamConfiguration.m index 45ab688..371eafb 100644 --- a/Limelight/Stream/StreamConfiguration.m +++ b/Limelight/Stream/StreamConfiguration.m @@ -9,5 +9,5 @@ #import "StreamConfiguration.h" @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 diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 754d6ec..dce1b01 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -35,6 +35,8 @@ #import +#include + @implementation MainFrameViewController { NSOperationQueue* _opQueue; TemporaryHost* _selectedHost; @@ -548,14 +550,16 @@ static NSMutableSet* hostList; // Probe for supported channel configurations - Log(LOG_I, @"Audio device supports %d channels", [AVAudioSession sharedInstance].maximumOutputNumberOfChannels); - if ([AVAudioSession sharedInstance].maximumOutputNumberOfChannels >= 6) { - _streamConfig.audioChannelCount = 6; - _streamConfig.audioChannelMask = 0xFC; + long outputChannels = [AVAudioSession sharedInstance].maximumOutputNumberOfChannels; + Log(LOG_I, @"Audio device supports %d channels", outputChannels); + if (outputChannels >= 8) { + _streamConfig.audioConfiguration = AUDIO_CONFIGURATION_71_SURROUND; + } + else if (outputChannels >= 6) { + _streamConfig.audioConfiguration = AUDIO_CONFIGURATION_51_SURROUND; } else { - _streamConfig.audioChannelCount = 2; - _streamConfig.audioChannelMask = 0x3; + _streamConfig.audioConfiguration = AUDIO_CONFIGURATION_STEREO; } // HDR requires HDR10 game, HDR10 display, and HEVC Main10 decoder on the client. diff --git a/moonlight-common/moonlight-common-c b/moonlight-common/moonlight-common-c index f489c9d..c247115 160000 --- a/moonlight-common/moonlight-common-c +++ b/moonlight-common/moonlight-common-c @@ -1 +1 @@ -Subproject commit f489c9d725d22517d3b3a017f6bbb0a22ed43707 +Subproject commit c2471157c0e1ba841f8419e7009c8835fb2e6d45