diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 431bb91b..4800960c 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -454,9 +454,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, .setEnableHdr(willStreamHdr) .setAttachedGamepadMask(gamepadMask) .setClientRefreshRateX100((int)(displayRefreshRate * 100)) - .setAudioConfiguration(prefConfig.enable51Surround ? - MoonBridge.AUDIO_CONFIGURATION_51_SURROUND : - MoonBridge.AUDIO_CONFIGURATION_STEREO) + .setAudioConfiguration(prefConfig.audioConfiguration) .build(); // Initialize the connection diff --git a/app/src/main/java/com/limelight/binding/audio/AndroidAudioRenderer.java b/app/src/main/java/com/limelight/binding/audio/AndroidAudioRenderer.java index 550ad505..8a04cc88 100644 --- a/app/src/main/java/com/limelight/binding/audio/AndroidAudioRenderer.java +++ b/app/src/main/java/com/limelight/binding/audio/AndroidAudioRenderer.java @@ -64,25 +64,36 @@ public class AndroidAudioRenderer implements AudioRenderer { } @Override - public int setup(int audioConfiguration, int sampleRate, int samplesPerFrame) { + public int setup(MoonBridge.AudioConfiguration audioConfiguration, int sampleRate, int samplesPerFrame) { int channelConfig; int bytesPerFrame; - switch (audioConfiguration) + switch (audioConfiguration.channelCount) { - case MoonBridge.AUDIO_CONFIGURATION_STEREO: + case 2: channelConfig = AudioFormat.CHANNEL_OUT_STEREO; - bytesPerFrame = 2 * samplesPerFrame * 2; break; - case MoonBridge.AUDIO_CONFIGURATION_51_SURROUND: + case 4: + channelConfig = AudioFormat.CHANNEL_OUT_QUAD; + break; + case 6: channelConfig = AudioFormat.CHANNEL_OUT_5POINT1; - bytesPerFrame = 6 * samplesPerFrame * 2; + break; + case 8: + // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND isn't available until Android 6.0, + // yet the CHANNEL_OUT_SIDE_LEFT and CHANNEL_OUT_SIDE_RIGHT constants were added + // in 5.0, so just hardcode the constant so we can work on Lollipop. + channelConfig = 0x000018fc; // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND break; default: LimeLog.severe("Decoder returned unhandled channel count"); return -1; } + LimeLog.info("Audio channel config: "+String.format("0x%X", channelConfig)); + + bytesPerFrame = audioConfiguration.channelCount * samplesPerFrame * 2; + // We're not supposed to request less than the minimum // buffer size for our buffer, but it appears that we can // do this on many devices and it lowers audio latency. diff --git a/app/src/main/java/com/limelight/nvstream/NvConnection.java b/app/src/main/java/com/limelight/nvstream/NvConnection.java index b3f5b37c..95220aa1 100644 --- a/app/src/main/java/com/limelight/nvstream/NvConnection.java +++ b/app/src/main/java/com/limelight/nvstream/NvConnection.java @@ -263,7 +263,7 @@ public class NvConnection { context.negotiatedWidth, context.negotiatedHeight, context.streamConfig.getRefreshRate(), context.streamConfig.getBitrate(), context.streamConfig.getMaxPacketSize(), - context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration(), + context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration().toInt(), context.streamConfig.getHevcSupported(), context.negotiatedHdr, context.streamConfig.getHevcBitratePercentageMultiplier(), diff --git a/app/src/main/java/com/limelight/nvstream/StreamConfiguration.java b/app/src/main/java/com/limelight/nvstream/StreamConfiguration.java index 4f149b24..8e1a3a5a 100644 --- a/app/src/main/java/com/limelight/nvstream/StreamConfiguration.java +++ b/app/src/main/java/com/limelight/nvstream/StreamConfiguration.java @@ -9,12 +9,6 @@ public class StreamConfiguration { public static final int STREAM_CFG_LOCAL = 0; public static final int STREAM_CFG_REMOTE = 1; public static final int STREAM_CFG_AUTO = 2; - - private static final int CHANNEL_COUNT_STEREO = 2; - private static final int CHANNEL_COUNT_5_1 = 6; - - private static final int CHANNEL_MASK_STEREO = 0x3; - private static final int CHANNEL_MASK_5_1 = 0xFC; private NvApp app; private int width, height; @@ -27,9 +21,7 @@ public class StreamConfiguration { private boolean playLocalAudio; private int maxPacketSize; private int remote; - private int audioChannelMask; - private int audioChannelCount; - private int audioConfiguration; + private MoonBridge.AudioConfiguration audioConfiguration; private boolean supportsHevc; private int hevcBitratePercentageMultiplier; private boolean enableHdr; @@ -119,21 +111,8 @@ public class StreamConfiguration { return this; } - public StreamConfiguration.Builder setAudioConfiguration(int audioConfig) { - if (audioConfig == MoonBridge.AUDIO_CONFIGURATION_STEREO) { - config.audioChannelCount = CHANNEL_COUNT_STEREO; - config.audioChannelMask = CHANNEL_MASK_STEREO; - } - else if (audioConfig == MoonBridge.AUDIO_CONFIGURATION_51_SURROUND) { - config.audioChannelCount = CHANNEL_COUNT_5_1; - config.audioChannelMask = CHANNEL_MASK_5_1; - } - else { - throw new IllegalArgumentException("Invalid audio configuration"); - } - + public StreamConfiguration.Builder setAudioConfiguration(MoonBridge.AudioConfiguration audioConfig) { config.audioConfiguration = audioConfig; - return this; } @@ -159,8 +138,7 @@ public class StreamConfiguration { this.remote = STREAM_CFG_AUTO; this.sops = true; this.enableAdaptiveResolution = false; - this.audioChannelCount = CHANNEL_COUNT_STEREO; - this.audioChannelMask = CHANNEL_MASK_STEREO; + this.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_STEREO; this.supportsHevc = false; this.enableHdr = false; this.attachedGamepadMask = 0; @@ -209,16 +187,8 @@ public class StreamConfiguration { public int getRemote() { return remote; } - - public int getAudioChannelCount() { - return audioChannelCount; - } - - public int getAudioChannelMask() { - return audioChannelMask; - } - public int getAudioConfiguration() { + public MoonBridge.AudioConfiguration getAudioConfiguration() { return audioConfiguration; } diff --git a/app/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java b/app/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java index 46182c62..8b0053b2 100644 --- a/app/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java +++ b/app/src/main/java/com/limelight/nvstream/av/audio/AudioRenderer.java @@ -1,7 +1,9 @@ package com.limelight.nvstream.av.audio; +import com.limelight.nvstream.jni.MoonBridge; + public interface AudioRenderer { - int setup(int audioConfiguration, int sampleRate, int samplesPerFrame); + int setup(MoonBridge.AudioConfiguration audioConfiguration, int sampleRate, int samplesPerFrame); void start(); diff --git a/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java b/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java index 93b62373..a1b2a998 100644 --- a/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java +++ b/app/src/main/java/com/limelight/nvstream/http/NvHTTP.java @@ -648,7 +648,7 @@ public class NvHTTP { "&rikeyid="+context.riKeyId + (!enableHdr ? "" : "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0") + "&localAudioPlayMode=" + (context.streamConfig.getPlayLocalAudio() ? 1 : 0) + - "&surroundAudioInfo=" + ((context.streamConfig.getAudioChannelMask() << 16) + context.streamConfig.getAudioChannelCount()) + + "&surroundAudioInfo=" + context.streamConfig.getAudioConfiguration().getSurroundAudioInfo() + (context.streamConfig.getAttachedGamepadMask() != 0 ? "&remoteControllersBitmap=" + context.streamConfig.getAttachedGamepadMask() : "") + (context.streamConfig.getAttachedGamepadMask() != 0 ? "&gcmap=" + context.streamConfig.getAttachedGamepadMask() : ""), false); @@ -660,7 +660,7 @@ public class NvHTTP { String xmlStr = openHttpConnectionToString(baseUrlHttps + "/resume?" + buildUniqueIdUuidString() + "&rikey="+bytesToHex(context.riKey.getEncoded()) + "&rikeyid="+context.riKeyId + - "&surroundAudioInfo=" + ((context.streamConfig.getAudioChannelMask() << 16) + context.streamConfig.getAudioChannelCount()), + "&surroundAudioInfo=" + context.streamConfig.getAudioConfiguration().getSurroundAudioInfo(), false); String resume = getXmlString(xmlStr, "resume"); return Integer.parseInt(resume) != 0; diff --git a/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java b/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java index 68ea2ddc..847668b7 100644 --- a/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java +++ b/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java @@ -7,8 +7,9 @@ import com.limelight.nvstream.av.video.VideoDecoderRenderer; public class MoonBridge { /* See documentation in Limelight.h for information about these functions and constants */ - public static final int AUDIO_CONFIGURATION_STEREO = 0; - public static final int AUDIO_CONFIGURATION_51_SURROUND = 1; + public static final AudioConfiguration AUDIO_CONFIGURATION_STEREO = new AudioConfiguration(2, 0x3); + public static final AudioConfiguration AUDIO_CONFIGURATION_51_SURROUND = new AudioConfiguration(6, 0x3F); + public static final AudioConfiguration AUDIO_CONFIGURATION_71_SURROUND = new AudioConfiguration(8, 0x63F); public static final int VIDEO_FORMAT_H264 = 0x0001; public static final int VIDEO_FORMAT_H265 = 0x0100; @@ -45,6 +46,57 @@ public class MoonBridge { return slices << 24; } + public static class AudioConfiguration { + public final int channelCount; + public final int channelMask; + + public AudioConfiguration(int channelCount, int channelMask) { + this.channelCount = channelCount; + this.channelMask = channelMask; + } + + // Creates an AudioConfiguration from the integer value returned by moonlight-common-c + // See CHANNEL_COUNT_FROM_AUDIO_CONFIGURATION() and CHANNEL_MASK_FROM_AUDIO_CONFIGURATION() + // in Limelight.h + private AudioConfiguration(int audioConfiguration) { + // Check the magic byte before decoding to make sure we got something that's actually + // a MAKE_AUDIO_CONFIGURATION()-based value and not something else like an older version + // hardcoded AUDIO_CONFIGURATION value from an earlier version of moonlight-common-c. + if ((audioConfiguration & 0xFF) != 0xCA) { + throw new IllegalArgumentException("Audio configuration has invalid magic byte!"); + } + + this.channelCount = (audioConfiguration >> 8) & 0xFF; + this.channelMask = (audioConfiguration >> 16) & 0xFFFF; + } + + // See SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION() in Limelight.h + public int getSurroundAudioInfo() { + return channelMask << 16 | channelCount; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof AudioConfiguration) { + AudioConfiguration that = (AudioConfiguration)obj; + return this.toInt() == that.toInt(); + } + + return false; + } + + @Override + public int hashCode() { + return toInt(); + } + + // Returns the integer value expected by moonlight-common-c + // See MAKE_AUDIO_CONFIGURATION() in Limelight.h + public int toInt() { + return ((channelMask) << 16) | (channelCount << 8) | 0xCA; + } + } + public static int bridgeDrSetup(int videoFormat, int width, int height, int redrawRate) { if (videoRenderer != null) { return videoRenderer.setup(videoFormat, width, height, redrawRate); @@ -86,7 +138,7 @@ public class MoonBridge { public static int bridgeArInit(int audioConfiguration, int sampleRate, int samplesPerFrame) { if (audioRenderer != null) { - return audioRenderer.setup(audioConfiguration, sampleRate, samplesPerFrame); + return audioRenderer.setup(new AudioConfiguration(audioConfiguration), sampleRate, samplesPerFrame); } else { return -1; diff --git a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java index 87b645cd..e2dca581 100644 --- a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java +++ b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java @@ -6,6 +6,8 @@ import android.content.pm.PackageManager; import android.os.Build; import android.preference.PreferenceManager; +import com.limelight.nvstream.jni.MoonBridge; + public class PreferenceConfiguration { private static final String LEGACY_RES_FPS_PREF_STRING = "list_resolution_fps"; @@ -78,7 +80,7 @@ public class PreferenceConfiguration { public int oscOpacity; public boolean stretchVideo, enableSops, playHostAudio, disableWarnings; public String language; - public boolean listMode, smallIconMode, multiController, enable51Surround, usbDriver; + public boolean listMode, smallIconMode, multiController, usbDriver; public boolean onscreenController; public boolean onlyL3R3; public boolean disableFrameDrop; @@ -91,6 +93,7 @@ public class PreferenceConfiguration { public boolean unlockFps; public boolean vibrateOsc; public boolean vibrateFallbackToDevice; + public MoonBridge.AudioConfiguration audioConfiguration; private static int getHeightFromResolutionString(String resString) { if (resString.equalsIgnoreCase("360p")) { @@ -332,7 +335,8 @@ public class PreferenceConfiguration { config.listMode = prefs.getBoolean(LIST_MODE_PREF_STRING, DEFAULT_LIST_MODE); config.smallIconMode = prefs.getBoolean(SMALL_ICONS_PREF_STRING, getDefaultSmallMode(context)); config.multiController = prefs.getBoolean(MULTI_CONTROLLER_PREF_STRING, DEFAULT_MULTI_CONTROLLER); - config.enable51Surround = prefs.getBoolean(ENABLE_51_SURROUND_PREF_STRING, DEFAULT_ENABLE_51_SURROUND); + config.audioConfiguration = prefs.getBoolean(ENABLE_51_SURROUND_PREF_STRING, DEFAULT_ENABLE_51_SURROUND) ? + MoonBridge.AUDIO_CONFIGURATION_51_SURROUND : MoonBridge.AUDIO_CONFIGURATION_STEREO; config.usbDriver = prefs.getBoolean(USB_DRIVER_PREF_SRING, DEFAULT_USB_DRIVER); config.onscreenController = prefs.getBoolean(ONSCREEN_CONTROLLER_PREF_STRING, ONSCREEN_CONTROLLER_DEFAULT); config.onlyL3R3 = prefs.getBoolean(ONLY_L3_R3_PREF_STRING, ONLY_L3_R3_DEFAULT); diff --git a/app/src/main/jni/moonlight-core/moonlight-common-c b/app/src/main/jni/moonlight-core/moonlight-common-c index f489c9d7..85de16b4 160000 --- a/app/src/main/jni/moonlight-core/moonlight-common-c +++ b/app/src/main/jni/moonlight-core/moonlight-common-c @@ -1 +1 @@ -Subproject commit f489c9d725d22517d3b3a017f6bbb0a22ed43707 +Subproject commit 85de16b41bca7ece0a6e0c69acdb76a5da0e79e0