mirror of
				https://github.com/ryujinx-mirror/ryujinx.git
				synced 2025-11-04 08:59:04 -06:00 
			
		
		
		
	Updating Concentus dependency to speed up Opus decoding (#6757)
* Implementing new features in the latest Concentus library - span-in, span-out Opus decoding (so we don't have to make temporary buffer copies), returning a more precise error code from the decoder, and automatically linking the native opus library with P/invoke if supported on the current system * Remove stub log messages and commit package upgrade to 2.1.0 * use more correct disposal pattern * Bump to Concentus 2.1.1 * Bump to Concentus 2.1.2 * Don't bother pulling in native opus binaries from Concentus package (using ExcludeAssets). * Fix opus MS channel count. Explicitly disable native lib probe in OpusCodecFactory. * Bump to package 2.2.0 which has split out the native libs, as suggested. --------- Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>net8.0</TargetFramework>
 | 
			
		||||
@@ -16,10 +16,4 @@
 | 
			
		||||
    <PackageReference Include="Concentus" />
 | 
			
		||||
    <PackageReference Include="LibHac" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <!-- Due to Concentus. -->
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <NoWarn>NU1605</NoWarn>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,11 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
{
 | 
			
		||||
    partial class HardwareOpusDecoder : IHardwareOpusDecoder, IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        static HardwareOpusDecoder()
 | 
			
		||||
        {
 | 
			
		||||
            OpusCodecFactory.AttemptToUseNativeLibrary = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [StructLayout(LayoutKind.Sequential)]
 | 
			
		||||
        private struct OpusPacketHeader
 | 
			
		||||
        {
 | 
			
		||||
@@ -30,60 +35,87 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private interface IDecoder
 | 
			
		||||
        private interface IDecoder : IDisposable
 | 
			
		||||
        {
 | 
			
		||||
            int SampleRate { get; }
 | 
			
		||||
            int ChannelsCount { get; }
 | 
			
		||||
 | 
			
		||||
            int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize);
 | 
			
		||||
            int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize);
 | 
			
		||||
            void ResetState();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private class Decoder : IDecoder
 | 
			
		||||
        {
 | 
			
		||||
            private readonly OpusDecoder _decoder;
 | 
			
		||||
            private readonly IOpusDecoder _decoder;
 | 
			
		||||
 | 
			
		||||
            public int SampleRate => _decoder.SampleRate;
 | 
			
		||||
            public int ChannelsCount => _decoder.NumChannels;
 | 
			
		||||
 | 
			
		||||
            public Decoder(int sampleRate, int channelsCount)
 | 
			
		||||
            {
 | 
			
		||||
                _decoder = new OpusDecoder(sampleRate, channelsCount);
 | 
			
		||||
                _decoder = OpusCodecFactory.CreateDecoder(sampleRate, channelsCount);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
 | 
			
		||||
            public int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize)
 | 
			
		||||
            {
 | 
			
		||||
                return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize);
 | 
			
		||||
                return _decoder.Decode(inData, outPcm, frameSize);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void ResetState()
 | 
			
		||||
            {
 | 
			
		||||
                _decoder.ResetState();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Dispose()
 | 
			
		||||
            {
 | 
			
		||||
                Dispose(disposing: true);
 | 
			
		||||
                GC.SuppressFinalize(this);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            protected virtual void Dispose(bool disposing)
 | 
			
		||||
            {
 | 
			
		||||
                if (disposing)
 | 
			
		||||
                {
 | 
			
		||||
                    _decoder?.Dispose();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private class MultiSampleDecoder : IDecoder
 | 
			
		||||
        {
 | 
			
		||||
            private readonly OpusMSDecoder _decoder;
 | 
			
		||||
            private readonly IOpusMultiStreamDecoder _decoder;
 | 
			
		||||
 | 
			
		||||
            public int SampleRate => _decoder.SampleRate;
 | 
			
		||||
            public int ChannelsCount { get; }
 | 
			
		||||
            public int ChannelsCount => _decoder.NumChannels;
 | 
			
		||||
 | 
			
		||||
            public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping)
 | 
			
		||||
            {
 | 
			
		||||
                ChannelsCount = channelsCount;
 | 
			
		||||
                _decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping);
 | 
			
		||||
                _decoder = OpusCodecFactory.CreateMultiStreamDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize)
 | 
			
		||||
            public int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize)
 | 
			
		||||
            {
 | 
			
		||||
                return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0);
 | 
			
		||||
                return _decoder.DecodeMultistream(inData, outPcm, frameSize, false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void ResetState()
 | 
			
		||||
            {
 | 
			
		||||
                _decoder.ResetState();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public void Dispose()
 | 
			
		||||
            {
 | 
			
		||||
                Dispose(disposing: true);
 | 
			
		||||
                GC.SuppressFinalize(this);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            protected virtual void Dispose(bool disposing)
 | 
			
		||||
            {
 | 
			
		||||
                if (disposing)
 | 
			
		||||
                {
 | 
			
		||||
                    _decoder?.Dispose();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private readonly IDecoder _decoder;
 | 
			
		||||
@@ -221,7 +253,8 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
        {
 | 
			
		||||
            timeTaken = 0;
 | 
			
		||||
 | 
			
		||||
            Result result = DecodeInterleaved(_decoder, reset, input, out short[] outPcmData, output.Length, out outConsumed, out outSamples);
 | 
			
		||||
            Span<short> outPcmSpace = MemoryMarshal.Cast<byte, short>(output);
 | 
			
		||||
            Result result = DecodeInterleaved(_decoder, reset, input, outPcmSpace, output.Length, out outConsumed, out outSamples);
 | 
			
		||||
 | 
			
		||||
            if (withPerf)
 | 
			
		||||
            {
 | 
			
		||||
@@ -229,14 +262,12 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
                timeTaken = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            MemoryMarshal.Cast<short, byte>(outPcmData).CopyTo(output[..(outPcmData.Length * sizeof(short))]);
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, byte[] packet)
 | 
			
		||||
        private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, ReadOnlySpan<byte> packet)
 | 
			
		||||
        {
 | 
			
		||||
            int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate);
 | 
			
		||||
            int result = OpusPacketInfo.GetNumSamples(packet, decoder.SampleRate);
 | 
			
		||||
 | 
			
		||||
            numSamples = result;
 | 
			
		||||
 | 
			
		||||
@@ -256,12 +287,11 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
            IDecoder decoder,
 | 
			
		||||
            bool reset,
 | 
			
		||||
            ReadOnlySpan<byte> input,
 | 
			
		||||
            out short[] outPcmData,
 | 
			
		||||
            Span<short> outPcmData,
 | 
			
		||||
            int outputSize,
 | 
			
		||||
            out int outConsumed,
 | 
			
		||||
            out int outSamples)
 | 
			
		||||
        {
 | 
			
		||||
            outPcmData = null;
 | 
			
		||||
            outConsumed = 0;
 | 
			
		||||
            outSamples = 0;
 | 
			
		||||
 | 
			
		||||
@@ -281,7 +311,7 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
                return CodecResult.InvalidLength;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            byte[] opusData = input.Slice(headerSize, (int)header.Length).ToArray();
 | 
			
		||||
            ReadOnlySpan<byte> opusData = input.Slice(headerSize, (int)header.Length);
 | 
			
		||||
 | 
			
		||||
            Result result = GetPacketNumSamples(decoder, out int numSamples, opusData);
 | 
			
		||||
 | 
			
		||||
@@ -292,8 +322,6 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
                    return CodecResult.InvalidLength;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                outPcmData = new short[numSamples * decoder.ChannelsCount];
 | 
			
		||||
 | 
			
		||||
                if (reset)
 | 
			
		||||
                {
 | 
			
		||||
                    decoder.ResetState();
 | 
			
		||||
@@ -301,13 +329,22 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount);
 | 
			
		||||
                    outSamples = decoder.Decode(opusData, outPcmData, numSamples);
 | 
			
		||||
                    outConsumed = (int)totalSize;
 | 
			
		||||
                }
 | 
			
		||||
                catch (OpusException)
 | 
			
		||||
                catch (OpusException e)
 | 
			
		||||
                {
 | 
			
		||||
                    // TODO: As OpusException doesn't return the exact error code, this is inaccurate in some cases...
 | 
			
		||||
                    return CodecResult.InvalidLength;
 | 
			
		||||
                    switch (e.OpusErrorCode)
 | 
			
		||||
                    {
 | 
			
		||||
                        case OpusError.OPUS_BUFFER_TOO_SMALL:
 | 
			
		||||
                            return CodecResult.InvalidLength;
 | 
			
		||||
                        case OpusError.OPUS_BAD_ARG:
 | 
			
		||||
                            return CodecResult.OpusBadArg;
 | 
			
		||||
                        case OpusError.OPUS_INVALID_PACKET:
 | 
			
		||||
                            return CodecResult.OpusInvalidPacket;
 | 
			
		||||
                        default:
 | 
			
		||||
                            return CodecResult.InvalidLength;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -324,6 +361,8 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail
 | 
			
		||||
 | 
			
		||||
                    _workBufferHandle = 0;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _decoder?.Dispose();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user