1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
using Concentus;
using Concentus.Enums;
using Concentus.Structs;
using Ryujinx.HLE.HOS.Services.Audio.Types;
using System;
using System.Runtime.CompilerServices;
namespace Ryujinx.HLE.HOS.Services.Audio.HardwareOpusDecoderManager
{
static class DecoderCommon
{
private static ResultCode GetPacketNumSamples(this IDecoder decoder, out int numSamples, byte[] packet)
{
int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate);
numSamples = result;
if (result == OpusError.OPUS_INVALID_PACKET)
{
return ResultCode.OpusInvalidInput;
}
else if (result == OpusError.OPUS_BAD_ARG)
{
return ResultCode.OpusInvalidInput;
}
return ResultCode.Success;
}
public static ResultCode DecodeInterleaved(
this IDecoder decoder,
bool reset,
ReadOnlySpan<byte> input,
out short[] outPcmData,
ulong outputSize,
out uint outConsumed,
out int outSamples)
{
outPcmData = null;
outConsumed = 0;
outSamples = 0;
int streamSize = input.Length;
if (streamSize < Unsafe.SizeOf<OpusPacketHeader>())
{
return ResultCode.OpusInvalidInput;
}
OpusPacketHeader header = OpusPacketHeader.FromSpan(input);
int headerSize = Unsafe.SizeOf<OpusPacketHeader>();
uint totalSize = header.length + (uint)headerSize;
if (totalSize > streamSize)
{
return ResultCode.OpusInvalidInput;
}
byte[] opusData = input.Slice(headerSize, (int)header.length).ToArray();
ResultCode result = decoder.GetPacketNumSamples(out int numSamples, opusData);
if (result == ResultCode.Success)
{
if ((uint)numSamples * (uint)decoder.ChannelsCount * sizeof(short) > outputSize)
{
return ResultCode.OpusInvalidInput;
}
outPcmData = new short[numSamples * decoder.ChannelsCount];
if (reset)
{
decoder.ResetState();
}
try
{
outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount);
outConsumed = totalSize;
}
catch (OpusException)
{
// TODO: as OpusException doesn't provide us the exact error code, this is kind of inaccurate in some cases...
return ResultCode.OpusInvalidInput;
}
}
return ResultCode.Success;
}
}
}
|