From a13e68b0421814cb9acf93d4d4466e14700e4e39 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 25 Oct 2015 13:08:20 -0700 Subject: [PATCH] Add support for 5.1 surround sound with GFE 2.7 --- limelight-common/AudioStream.c | 30 +++++++++++++++++++++++++++--- limelight-common/FakeCallbacks.c | 2 +- limelight-common/Limelight.h | 27 +++++++++++++++++++++++++-- limelight-common/SdpGenerator.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/limelight-common/AudioStream.c b/limelight-common/AudioStream.c index 7785cb3..6e94272 100644 --- a/limelight-common/AudioStream.c +++ b/limelight-common/AudioStream.c @@ -17,13 +17,36 @@ static unsigned short lastSeq; #define RTP_PORT 48000 -#define MAX_PACKET_SIZE 100 +#define MAX_PACKET_SIZE 250 // This is much larger than we should typically have buffered, but // it needs to be. We need a cushion in case our thread gets blocked // for longer than normal. #define RTP_RECV_BUFFER (64 * MAX_PACKET_SIZE) +#define SAMPLE_RATE 48000 + +static OPUS_MULTISTREAM_CONFIGURATION opusStereoConfig = { + .sampleRate = SAMPLE_RATE, + .channelCount = 2, + .streams = 1, + .coupledStreams = 1, + .mapping = {0, 1} +}; + +static OPUS_MULTISTREAM_CONFIGURATION opus51SurroundConfig = { + .sampleRate = SAMPLE_RATE, + .channelCount = 6, + .streams = 4, + .coupledStreams = 2, + .mapping = {0, 4, 1, 5, 2, 3} +}; + +static POPUS_MULTISTREAM_CONFIGURATION opusConfigArray[] = { + &opusStereoConfig, + &opus51SurroundConfig, +}; + typedef struct _QUEUED_AUDIO_PACKET { // data must remain at the front char data[MAX_PACKET_SIZE]; @@ -241,8 +264,9 @@ void stopAudioStream(void) { int startAudioStream(void) { int err; - - AudioCallbacks.init(); + + AudioCallbacks.init(StreamConfig.audioConfiguration, + opusConfigArray[StreamConfig.audioConfiguration]); rtpSocket = bindUdpSocket(RemoteAddr.ss_family, RTP_RECV_BUFFER); if (rtpSocket == INVALID_SOCKET) { diff --git a/limelight-common/FakeCallbacks.c b/limelight-common/FakeCallbacks.c index fdd04d7..0859cb4 100644 --- a/limelight-common/FakeCallbacks.c +++ b/limelight-common/FakeCallbacks.c @@ -10,7 +10,7 @@ static DECODER_RENDERER_CALLBACKS fakeDrCallbacks = { .submitDecodeUnit = fakeDrSubmitDecodeUnit, }; -static void fakeArInit(void) {} +static void fakeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {} static void fakeArCleanup(void) {} static void fakeArDecodeAndPlaySample(char* sampleData, int sampleLength) {} diff --git a/limelight-common/Limelight.h b/limelight-common/Limelight.h index ac6774b..f740270 100644 --- a/limelight-common/Limelight.h +++ b/limelight-common/Limelight.h @@ -29,6 +29,10 @@ typedef struct _STREAM_CONFIGURATION { // streaming optimizations. If unsure, set to 0. int streamingRemotely; + // Specifies the channel configuration of the audio stream. + // See AUDIO_CONFIGURATION_XXX constants below. + int audioConfiguration; + // AES encryption data for the remote input stream. This must be // the same as what was passed as rikey and rikeyid // in /launch and /resume requests. @@ -59,6 +63,12 @@ typedef struct _DECODE_UNIT { PLENTRY bufferList; } DECODE_UNIT, *PDECODE_UNIT; +// Specifies that the audio stream should be encoded in stereo (default) +#define AUDIO_CONFIGURATION_STEREO 0 + +// Specifies that the audio stream should be in 5.1 surround sound if the PC is able +#define AUDIO_CONFIGURATION_51_SURROUND 1 + // If set in the renderer capabilities field, this flag will cause audio/video data to // be submitted directly from the receive thread. This should only be specified if the // renderer is non-blocking. This flag is valid on both audio and video renderers. @@ -97,8 +107,21 @@ typedef struct _DECODER_RENDERER_CALLBACKS { // Use this function to zero the video callbacks when allocated on the stack or heap void LiInitializeVideoCallbacks(PDECODER_RENDERER_CALLBACKS drCallbacks); -// This callback initializes the audio renderer -typedef void(*AudioRendererInit)(void); +// This structure provides the Opus multistream decoder parameters required to successfully +// decode the audio stream being sent from the computer. See opus_multistream_decoder_init docs +// for details about these fields. +typedef struct _OPUS_MULTISTREAM_CONFIGURATION { + int sampleRate; + int channelCount; + int streams; + int coupledStreams; + const unsigned char mapping[6]; +} OPUS_MULTISTREAM_CONFIGURATION, *POPUS_MULTISTREAM_CONFIGURATION; + +// This callback initializes the audio renderer. The audio configuration parameter +// provides the negotiated audio configuration. This may differ from the one +// specified in the stream configuration. +typedef void(*AudioRendererInit)(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig); // This callback performs the final teardown of the audio decoder typedef void(*AudioRendererCleanup)(void); diff --git a/limelight-common/SdpGenerator.c b/limelight-common/SdpGenerator.c index 7d8c688..1f34432 100644 --- a/limelight-common/SdpGenerator.c +++ b/limelight-common/SdpGenerator.c @@ -5,6 +5,12 @@ #define MAX_SDP_HEADER_LEN 128 #define MAX_SDP_TAIL_LEN 128 +#define CHANNEL_COUNT_STEREO 2 +#define CHANNEL_COUNT_51_SURROUND 6 + +#define CHANNEL_MASK_STEREO 0x3 +#define CHANNEL_MASK_51_SURROUND 0xFC + typedef struct _SDP_OPTION { char name[MAX_OPTION_NAME_LEN+1]; void* payload; @@ -134,6 +140,8 @@ static int addGen4Options(PSDP_OPTION *head, char* addrStr) { char payloadStr[92]; int err = 0; unsigned char slicesPerFrame; + int audioChannelCount; + int audioChannelMask; sprintf(payloadStr, "rtsp://%s:48010", addrStr); err |= addAttributeString(head, "x-nv-general.serverAddress", payloadStr); @@ -148,6 +156,26 @@ static int addGen4Options(PSDP_OPTION *head, char* addrStr) { } sprintf(payloadStr, "%d", slicesPerFrame); err |= addAttributeString(head, "x-nv-video[0].videoEncoderSlicesPerFrame", payloadStr); + + if (StreamConfig.audioConfiguration == AUDIO_CONFIGURATION_51_SURROUND) { + audioChannelCount = CHANNEL_COUNT_51_SURROUND; + audioChannelMask = CHANNEL_MASK_51_SURROUND; + } + else { + audioChannelCount = CHANNEL_COUNT_STEREO; + audioChannelMask = CHANNEL_MASK_STEREO; + } + + sprintf(payloadStr, "%d", audioChannelCount); + err |= addAttributeString(head, "x-nv-audio.surround.numChannels", payloadStr); + sprintf(payloadStr, "%d", audioChannelMask); + err |= addAttributeString(head, "x-nv-audio.surround.channelMask", payloadStr); + if (audioChannelCount > 2) { + err |= addAttributeString(head, "x-nv-audio.surround.enable", "1"); + } + else { + err |= addAttributeString(head, "x-nv-audio.surround.enable", "0"); + } return err; }