Added Opus.Net, fixed public IP method, starting to add outgoing voice support.
This commit is contained in:
@@ -141,6 +141,12 @@
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Opus.Net.Net45\Opus.Net.csproj">
|
||||
<Project>{114c8c10-7354-4ec3-819a-33e83aa57768}</Project>
|
||||
<Name>Opus.Net</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets" Condition="Exists('..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
|
||||
@@ -1320,6 +1320,13 @@ namespace Discord
|
||||
return DiscordAPI.Undeafen(serverId, userId);
|
||||
}
|
||||
|
||||
#if !DNXCORE50
|
||||
public void SendVoiceWAV(byte[] buffer, int count)
|
||||
{
|
||||
_voiceWebSocket.SendWAV(buffer, count);
|
||||
}
|
||||
#endif
|
||||
|
||||
//Profile
|
||||
/// <summary> Changes your username to newName. </summary>
|
||||
public async Task ChangeUsername(string newName, string currentEmail, string currentPassword)
|
||||
@@ -1360,7 +1367,7 @@ namespace Discord
|
||||
private string GenerateNonce()
|
||||
{
|
||||
lock (_rand)
|
||||
return _rand.Next(0, int.MaxValue).ToString();
|
||||
return _rand.Next().ToString();
|
||||
}
|
||||
|
||||
/// <summary> Blocking call that will not return until client has been stopped. This is mainly intended for use in console applications. </summary>
|
||||
|
||||
@@ -10,6 +10,10 @@ using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using WebSocketMessage = Discord.API.Models.VoiceWebSocketCommands.WebSocketMessage;
|
||||
using System.Text;
|
||||
#if !DNXCORE50
|
||||
using Opus.Net;
|
||||
#endif
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
@@ -23,13 +27,24 @@ namespace Discord
|
||||
private byte[] _secretKey;
|
||||
private string _mode;
|
||||
private bool _isFirst;
|
||||
private ushort _sequence;
|
||||
private uint _ssrc;
|
||||
private long _startTicks;
|
||||
private readonly Random _rand = new Random();
|
||||
|
||||
#if !DNXCORE50
|
||||
private OpusEncoder _encoder;
|
||||
#endif
|
||||
|
||||
public DiscordVoiceSocket(int timeout, int interval)
|
||||
: base(timeout, interval)
|
||||
{
|
||||
_connectWaitOnLogin = new ManualResetEventSlim(false);
|
||||
_sendQueue = new ConcurrentQueue<byte[]>();
|
||||
}
|
||||
#if !DNXCORE50
|
||||
_encoder = OpusEncoder.Create(24000, 1, Application.Voip);
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnConnect()
|
||||
{
|
||||
@@ -58,7 +73,7 @@ namespace Discord
|
||||
|
||||
_connectWaitOnLogin.Reset();
|
||||
|
||||
_myIp = (await Http.Get("http://ipinfo.io/ip")).Trim();
|
||||
_sequence = 0;
|
||||
|
||||
VoiceWebSocketCommands.Login msg = new VoiceWebSocketCommands.Login();
|
||||
msg.Payload.ServerId = serverId;
|
||||
@@ -138,13 +153,18 @@ namespace Discord
|
||||
//_mode = payload.Modes.LastOrDefault();
|
||||
_mode = "plain";
|
||||
_udp.Connect(_endpoint);
|
||||
var ssrc = payload.SSRC;
|
||||
lock(_rand)
|
||||
{
|
||||
_sequence = (ushort)_rand.Next(0, ushort.MaxValue);
|
||||
_startTicks = DateTime.UtcNow.Ticks - _rand.Next();
|
||||
}
|
||||
_ssrc = payload.SSRC;
|
||||
|
||||
_sendQueue.Enqueue(new byte[70] {
|
||||
(byte)((ssrc >> 24) & 0xFF),
|
||||
(byte)((ssrc >> 16) & 0xFF),
|
||||
(byte)((ssrc >> 8) & 0xFF),
|
||||
(byte)((ssrc >> 0) & 0xFF),
|
||||
(byte)((_ssrc >> 24) & 0xFF),
|
||||
(byte)((_ssrc >> 16) & 0xFF),
|
||||
(byte)((_ssrc >> 8) & 0xFF),
|
||||
(byte)((_ssrc >> 0) & 0xFF),
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
@@ -179,6 +199,8 @@ namespace Discord
|
||||
|
||||
int port = buffer[68] | buffer[69] << 8;
|
||||
|
||||
_myIp = Encoding.ASCII.GetString(buffer, 4, 70 - 6).TrimEnd('\0');
|
||||
|
||||
var login2 = new VoiceWebSocketCommands.Login2();
|
||||
login2.Payload.Protocol = "udp";
|
||||
login2.Payload.SocketData.Address = _myIp;
|
||||
@@ -189,7 +211,7 @@ namespace Discord
|
||||
else
|
||||
{
|
||||
//Parse RTP Data
|
||||
if (length < 12)
|
||||
/*if (length < 12)
|
||||
throw new Exception($"Unexpected message length. Expected >= 12, got {length}.");
|
||||
|
||||
byte flags = buffer[0];
|
||||
@@ -227,11 +249,38 @@ namespace Discord
|
||||
byte[] newBuffer = new byte[buffer.Length - 12];
|
||||
Buffer.BlockCopy(buffer, 12, newBuffer, 0, newBuffer.Length);
|
||||
buffer = newBuffer;
|
||||
}
|
||||
}*/
|
||||
|
||||
//TODO: Use Voice Data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !DNXCORE50
|
||||
public void SendWAV(byte[] buffer, int count)
|
||||
{
|
||||
int encodedLength;
|
||||
buffer = _encoder.Encode(buffer, count, out encodedLength);
|
||||
byte[] packet = new byte[12 + encodedLength];
|
||||
Buffer.BlockCopy(buffer, 0, packet, 12, encodedLength);
|
||||
|
||||
ushort sequence = _sequence++;
|
||||
long timestamp = (DateTime.UtcNow.Ticks - _startTicks) >> 2; //200ns resolution
|
||||
packet[0] = 0x80; //Flags;
|
||||
packet[1] = 0x78; //Payload Type
|
||||
packet[2] = (byte)((sequence >> 8) & 0xFF);
|
||||
packet[3] = (byte)((sequence >> 0) & 0xFF);
|
||||
packet[4] = (byte)((timestamp >> 24) & 0xFF);
|
||||
packet[5] = (byte)((timestamp >> 16) & 0xFF);
|
||||
packet[6] = (byte)((timestamp >> 8) & 0xFF);
|
||||
packet[7] = (byte)((timestamp >> 0) & 0xFF);
|
||||
packet[8] = (byte)((_ssrc >> 24) & 0xFF);
|
||||
packet[9] = (byte)((_ssrc >> 16) & 0xFF);
|
||||
packet[10] = (byte)((_ssrc >> 8) & 0xFF);
|
||||
packet[11] = (byte)((_ssrc >> 0) & 0xFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected override object GetKeepAlive()
|
||||
{
|
||||
return new VoiceWebSocketCommands.KeepAlive();
|
||||
|
||||
@@ -1,54 +1,56 @@
|
||||
{
|
||||
"version": "0.5.0-*",
|
||||
"description": "An unofficial .Net API wrapper for the Discord client.",
|
||||
"authors": [ "RogueException" ],
|
||||
"tags": [ "discord", "discordapp" ],
|
||||
"projectUrl": "https://github.com/RogueException/Discord.Net",
|
||||
"licenseUrl": "http://opensource.org/licenses/MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/RogueException/Discord.Net"
|
||||
},
|
||||
"configurations": {
|
||||
"FullDebug": {
|
||||
"compilationOptions": {
|
||||
"define": [ "DEBUG", "TRACE", "TEST_RESPONSES" ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"version": "0.5.0-*",
|
||||
"description": "An unofficial .Net API wrapper for the Discord client.",
|
||||
"authors": [ "RogueException" ],
|
||||
"tags": [ "discord", "discordapp" ],
|
||||
"projectUrl": "https://github.com/RogueException/Discord.Net",
|
||||
"licenseUrl": "http://opensource.org/licenses/MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/RogueException/Discord.Net"
|
||||
},
|
||||
"configurations": {
|
||||
"FullDebug": {
|
||||
"compilationOptions": {
|
||||
"define": [ "DEBUG", "TRACE", "TEST_RESPONSES" ]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "7.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "7.0.1"
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
"Microsoft.Net.Http": "2.2.22",
|
||||
"libsodium-net": "0.8.0",
|
||||
"Baseclass.Contrib.Nuget.Output": "2.1.0"
|
||||
}
|
||||
},
|
||||
"dnx451": {
|
||||
"dependencies": {
|
||||
"Microsoft.Net.Http": "2.2.22",
|
||||
"libsodium-net": "0.8.0",
|
||||
"Baseclass.Contrib.Nuget.Output": "2.1.0"
|
||||
}
|
||||
},
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Collections.Concurrent": "4.0.10",
|
||||
"System.Diagnostics.Debug": "4.0.10",
|
||||
"System.IO.Compression": "4.0.0",
|
||||
"System.Linq": "4.0.0",
|
||||
"System.Net.Requests": "4.0.10",
|
||||
"System.Net.Sockets": "4.0.10-beta-23019",
|
||||
"System.Net.WebSockets.Client": "4.0.0-beta-23123",
|
||||
"System.Runtime": "4.0.20",
|
||||
"System.Text.RegularExpressions": "4.0.10",
|
||||
"System.Net.NameResolution": "4.0.0-beta-23019"
|
||||
}
|
||||
}
|
||||
}
|
||||
"frameworks": {
|
||||
"net45": {
|
||||
"dependencies": {
|
||||
"Microsoft.Net.Http": "2.2.22",
|
||||
"libsodium-net": "0.8.0",
|
||||
"Baseclass.Contrib.Nuget.Output": "2.1.0",
|
||||
"Opus.Net": "0.1.0"
|
||||
}
|
||||
},
|
||||
"dnx451": {
|
||||
"dependencies": {
|
||||
"Microsoft.Net.Http": "2.2.22",
|
||||
"libsodium-net": "0.8.0",
|
||||
"Baseclass.Contrib.Nuget.Output": "2.1.0",
|
||||
"Opus.Net": "0.1.0"
|
||||
}
|
||||
},
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Collections.Concurrent": "4.0.10",
|
||||
"System.Diagnostics.Debug": "4.0.10",
|
||||
"System.IO.Compression": "4.0.0",
|
||||
"System.Linq": "4.0.0",
|
||||
"System.Net.Requests": "4.0.10",
|
||||
"System.Net.Sockets": "4.0.10-beta-23019",
|
||||
"System.Net.WebSockets.Client": "4.0.0-beta-23123",
|
||||
"System.Runtime": "4.0.20",
|
||||
"System.Text.RegularExpressions": "4.0.10",
|
||||
"System.Net.NameResolution": "4.0.0-beta-23019"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
79
src/Opus.Net.Net45/Opus.Net.csproj
Normal file
79
src/Opus.Net.Net45/Opus.Net.csproj
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{114C8C10-7354-4EC3-819A-33E83AA57768}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Discord.Net</RootNamespace>
|
||||
<AssemblyName>Discord.Net</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>2</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Sodium, Version=0.8.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\libsodium-net.0.8.0\lib\Net40\Sodium.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Opus.Net\API.cs">
|
||||
<Link>API.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Opus.Net\OpusDecoder.cs">
|
||||
<Link>OpusDecoder.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Opus.Net\OpusEncoder.cs">
|
||||
<Link>OpusEncoder.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets" Condition="Exists('..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Baseclass.Contrib.Nuget.Output.1.0.0\build\net40\Baseclass.Contrib.Nuget.Output.targets'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
17
src/Opus.Net.Net45/Properties/AssemblyInfo.cs
Normal file
17
src/Opus.Net.Net45/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("Opus.Net")]
|
||||
[assembly: AssemblyDescription("Opus .NET Wrapper")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("John Carruthers")]
|
||||
[assembly: AssemblyProduct("Opus.Net")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2013")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: Guid("76ea00e6-ea24-41e1-acb2-639c0313fa80")]
|
||||
|
||||
[assembly: AssemblyVersion("0.1.0.0")]
|
||||
[assembly: AssemblyFileVersion("0.1.0.0")]
|
||||
6
src/Opus.Net.Net45/packages.config
Normal file
6
src/Opus.Net.Net45/packages.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Baseclass.Contrib.Nuget.Output" version="1.0.0" targetFramework="net45" />
|
||||
<package id="libsodium-net" version="0.8.0" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||
</packages>
|
||||
108
src/Opus.Net/API.cs
Normal file
108
src/Opus.Net/API.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Opus.Net
|
||||
{
|
||||
internal class API
|
||||
{
|
||||
static API()
|
||||
{
|
||||
if (LoadLibrary(Environment.Is64BitProcess ? "lib/x64/opus.dll" : "lib/x86/opus.dll") == IntPtr.Zero)
|
||||
throw new FileNotFoundException("Unable to find opus.dll", "opus.dll");
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern IntPtr opus_encoder_create(int Fs, int channels, int application, out IntPtr error);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void opus_encoder_destroy(IntPtr encoder);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int opus_encode(IntPtr st, byte[] pcm, int frame_size, IntPtr data, int max_data_bytes);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern IntPtr opus_decoder_create(int Fs, int channels, out IntPtr error);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void opus_decoder_destroy(IntPtr decoder);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int opus_decode(IntPtr st, byte[] data, int len, IntPtr pcm, int frame_size, int decode_fec);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int opus_encoder_ctl(IntPtr st, Ctl request, int value);
|
||||
|
||||
[DllImport("opus.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int opus_encoder_ctl(IntPtr st, Ctl request, out int value);
|
||||
}
|
||||
|
||||
public enum Ctl : int
|
||||
{
|
||||
SetBitrateRequest = 4002,
|
||||
GetBitrateRequest = 4003,
|
||||
SetInbandFECRequest = 4012,
|
||||
GetInbandFECRequest = 4013
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Supported coding modes.
|
||||
/// </summary>
|
||||
public enum Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Best for most VoIP/videoconference applications where listening quality and intelligibility matter most.
|
||||
/// </summary>
|
||||
Voip = 2048,
|
||||
/// <summary>
|
||||
/// Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to input.
|
||||
/// </summary>
|
||||
Audio = 2049,
|
||||
/// <summary>
|
||||
/// Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used.
|
||||
/// </summary>
|
||||
Restricted_LowLatency = 2051
|
||||
}
|
||||
|
||||
public enum Errors
|
||||
{
|
||||
/// <summary>
|
||||
/// No error.
|
||||
/// </summary>
|
||||
OK = 0,
|
||||
/// <summary>
|
||||
/// One or more invalid/out of range arguments.
|
||||
/// </summary>
|
||||
BadArg = -1,
|
||||
/// <summary>
|
||||
/// The mode struct passed is invalid.
|
||||
/// </summary>
|
||||
BufferToSmall = -2,
|
||||
/// <summary>
|
||||
/// An internal error was detected.
|
||||
/// </summary>
|
||||
InternalError = -3,
|
||||
/// <summary>
|
||||
/// The compressed data passed is corrupted.
|
||||
/// </summary>
|
||||
InvalidPacket = -4,
|
||||
/// <summary>
|
||||
/// Invalid/unsupported request number.
|
||||
/// </summary>
|
||||
Unimplemented = -5,
|
||||
/// <summary>
|
||||
/// An encoder or decoder structure is invalid or already freed.
|
||||
/// </summary>
|
||||
InvalidState = -6,
|
||||
/// <summary>
|
||||
/// Memory allocation has failed.
|
||||
/// </summary>
|
||||
AllocFail = -7
|
||||
}
|
||||
}
|
||||
20
src/Opus.Net/Opus.Net.xproj
Normal file
20
src/Opus.Net/Opus.Net.xproj
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>42ab6a2d-2f2c-4003-80ef-33b5b5b0ed8e</ProjectGuid>
|
||||
<RootNamespace>Opus.Net</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
133
src/Opus.Net/OpusDecoder.cs
Normal file
133
src/Opus.Net/OpusDecoder.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
|
||||
namespace Opus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Opus audio decoder.
|
||||
/// </summary>
|
||||
public class OpusDecoder : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new Opus decoder.
|
||||
/// </summary>
|
||||
/// <param name="outputSampleRate">Sample rate to decode at (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000.</param>
|
||||
/// <param name="outputChannels">Number of channels to decode.</param>
|
||||
/// <returns>A new <c>OpusDecoder</c>.</returns>
|
||||
public static OpusDecoder Create(int outputSampleRate, int outputChannels)
|
||||
{
|
||||
if (outputSampleRate != 8000 &&
|
||||
outputSampleRate != 12000 &&
|
||||
outputSampleRate != 16000 &&
|
||||
outputSampleRate != 24000 &&
|
||||
outputSampleRate != 48000)
|
||||
throw new ArgumentOutOfRangeException("inputSamplingRate");
|
||||
if (outputChannels != 1 && outputChannels != 2)
|
||||
throw new ArgumentOutOfRangeException("inputChannels");
|
||||
|
||||
IntPtr error;
|
||||
IntPtr decoder = API.opus_decoder_create(outputSampleRate, outputChannels, out error);
|
||||
if ((Errors)error != Errors.OK)
|
||||
{
|
||||
throw new Exception("Exception occured while creating decoder");
|
||||
}
|
||||
return new OpusDecoder(decoder, outputSampleRate, outputChannels);
|
||||
}
|
||||
|
||||
private IntPtr _decoder;
|
||||
|
||||
private OpusDecoder(IntPtr decoder, int outputSamplingRate, int outputChannels)
|
||||
{
|
||||
_decoder = decoder;
|
||||
OutputSamplingRate = outputSamplingRate;
|
||||
OutputChannels = outputChannels;
|
||||
MaxDataBytes = 4000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produces PCM samples from Opus encoded data.
|
||||
/// </summary>
|
||||
/// <param name="inputOpusData">Opus encoded data to decode, null for dropped packet.</param>
|
||||
/// <param name="dataLength">Length of data to decode.</param>
|
||||
/// <param name="decodedLength">Set to the length of the decoded sample data.</param>
|
||||
/// <returns>PCM audio samples.</returns>
|
||||
public unsafe byte[] Decode(byte[] inputOpusData, int dataLength, out int decodedLength)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("OpusDecoder");
|
||||
|
||||
IntPtr decodedPtr;
|
||||
byte[] decoded = new byte[MaxDataBytes];
|
||||
int frameCount = FrameCount(MaxDataBytes);
|
||||
int length = 0;
|
||||
fixed (byte* bdec = decoded)
|
||||
{
|
||||
decodedPtr = new IntPtr((void*)bdec);
|
||||
|
||||
if (inputOpusData != null)
|
||||
length = API.opus_decode(_decoder, inputOpusData, dataLength, decodedPtr, frameCount, 0);
|
||||
else
|
||||
length = API.opus_decode(_decoder, null, 0, decodedPtr, frameCount, (ForwardErrorCorrection) ? 1 : 0);
|
||||
}
|
||||
decodedLength = length * 2;
|
||||
if (length < 0)
|
||||
throw new Exception("Decoding failed - " + ((Errors)length).ToString());
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the number of frames that can fit into a buffer of the given size.
|
||||
/// </summary>
|
||||
/// <param name="bufferSize"></param>
|
||||
/// <returns></returns>
|
||||
public int FrameCount(int bufferSize)
|
||||
{
|
||||
// seems like bitrate should be required
|
||||
int bitrate = 16;
|
||||
int bytesPerSample = (bitrate / 8) * OutputChannels;
|
||||
return bufferSize / bytesPerSample;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the output sampling rate of the decoder.
|
||||
/// </summary>
|
||||
public int OutputSamplingRate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of channels of the decoder.
|
||||
/// </summary>
|
||||
public int OutputChannels { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of memory allocated for decoding data.
|
||||
/// </summary>
|
||||
public int MaxDataBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether forward error correction is enabled or not.
|
||||
/// </summary>
|
||||
public bool ForwardErrorCorrection { get; set; }
|
||||
|
||||
~OpusDecoder()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private bool disposed;
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
if (_decoder != IntPtr.Zero)
|
||||
{
|
||||
API.opus_decoder_destroy(_decoder);
|
||||
_decoder = IntPtr.Zero;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
198
src/Opus.Net/OpusEncoder.cs
Normal file
198
src/Opus.Net/OpusEncoder.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
|
||||
namespace Opus.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Opus codec wrapper.
|
||||
/// </summary>
|
||||
public class OpusEncoder : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new Opus encoder.
|
||||
/// </summary>
|
||||
/// <param name="inputSamplingRate">Sampling rate of the input signal (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000.</param>
|
||||
/// <param name="inputChannels">Number of channels (1 or 2) in input signal.</param>
|
||||
/// <param name="application">Coding mode.</param>
|
||||
/// <returns>A new <c>OpusEncoder</c></returns>
|
||||
public static OpusEncoder Create(int inputSamplingRate, int inputChannels, Application application)
|
||||
{
|
||||
if (inputSamplingRate != 8000 &&
|
||||
inputSamplingRate != 12000 &&
|
||||
inputSamplingRate != 16000 &&
|
||||
inputSamplingRate != 24000 &&
|
||||
inputSamplingRate != 48000)
|
||||
throw new ArgumentOutOfRangeException("inputSamplingRate");
|
||||
if (inputChannels != 1 && inputChannels != 2)
|
||||
throw new ArgumentOutOfRangeException("inputChannels");
|
||||
|
||||
IntPtr error;
|
||||
IntPtr encoder = API.opus_encoder_create(inputSamplingRate, inputChannels, (int)application, out error);
|
||||
if ((Errors)error != Errors.OK)
|
||||
{
|
||||
throw new Exception("Exception occured while creating encoder");
|
||||
}
|
||||
return new OpusEncoder(encoder, inputSamplingRate, inputChannels, application);
|
||||
}
|
||||
|
||||
private IntPtr _encoder;
|
||||
|
||||
private OpusEncoder(IntPtr encoder, int inputSamplingRate, int inputChannels, Application application)
|
||||
{
|
||||
_encoder = encoder;
|
||||
InputSamplingRate = inputSamplingRate;
|
||||
InputChannels = inputChannels;
|
||||
Application = application;
|
||||
MaxDataBytes = 4000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produces Opus encoded audio from PCM samples.
|
||||
/// </summary>
|
||||
/// <param name="inputPcmSamples">PCM samples to encode.</param>
|
||||
/// <param name="sampleLength">How many bytes to encode.</param>
|
||||
/// <param name="encodedLength">Set to length of encoded audio.</param>
|
||||
/// <returns>Opus encoded audio buffer.</returns>
|
||||
public unsafe byte[] Encode(byte[] inputPcmSamples, int sampleLength, out int encodedLength)
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("OpusEncoder");
|
||||
|
||||
int frames = FrameCount(inputPcmSamples);
|
||||
IntPtr encodedPtr;
|
||||
byte[] encoded = new byte[MaxDataBytes];
|
||||
int length = 0;
|
||||
fixed (byte* benc = encoded)
|
||||
{
|
||||
encodedPtr = new IntPtr((void*)benc);
|
||||
length = API.opus_encode(_encoder, inputPcmSamples, frames, encodedPtr, sampleLength);
|
||||
}
|
||||
encodedLength = length;
|
||||
if (length < 0)
|
||||
throw new Exception("Encoding failed - " + ((Errors)length).ToString());
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the number of frames in the PCM samples.
|
||||
/// </summary>
|
||||
/// <param name="pcmSamples"></param>
|
||||
/// <returns></returns>
|
||||
public int FrameCount(byte[] pcmSamples)
|
||||
{
|
||||
// seems like bitrate should be required
|
||||
int bitrate = 16;
|
||||
int bytesPerSample = (bitrate / 8) * InputChannels;
|
||||
return pcmSamples.Length / bytesPerSample;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to determine how many bytes are required for encoding to work.
|
||||
/// </summary>
|
||||
/// <param name="frameCount">Target frame size.</param>
|
||||
/// <returns></returns>
|
||||
public int FrameByteCount(int frameCount)
|
||||
{
|
||||
int bitrate = 16;
|
||||
int bytesPerSample = (bitrate / 8) * InputChannels;
|
||||
return frameCount * bytesPerSample;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the input sampling rate of the encoder.
|
||||
/// </summary>
|
||||
public int InputSamplingRate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of channels of the encoder.
|
||||
/// </summary>
|
||||
public int InputChannels { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the coding mode of the encoder.
|
||||
/// </summary>
|
||||
public Application Application { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of memory allocated for reading encoded data.
|
||||
/// 4000 is recommended.
|
||||
/// </summary>
|
||||
public int MaxDataBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bitrate setting of the encoding.
|
||||
/// </summary>
|
||||
public int Bitrate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("OpusEncoder");
|
||||
int bitrate;
|
||||
var ret = API.opus_encoder_ctl(_encoder, Ctl.GetBitrateRequest, out bitrate);
|
||||
if (ret < 0)
|
||||
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
|
||||
return bitrate;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (disposed)
|
||||
throw new ObjectDisposedException("OpusEncoder");
|
||||
var ret = API.opus_encoder_ctl(_encoder, Ctl.SetBitrateRequest, value);
|
||||
if (ret < 0)
|
||||
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether Forward Error Correction is enabled.
|
||||
/// </summary>
|
||||
public bool ForwardErrorCorrection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_encoder == IntPtr.Zero)
|
||||
throw new ObjectDisposedException("OpusEncoder");
|
||||
|
||||
int fec;
|
||||
int ret = API.opus_encoder_ctl(_encoder, Ctl.GetInbandFECRequest, out fec);
|
||||
if (ret < 0)
|
||||
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
|
||||
|
||||
return fec > 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_encoder == IntPtr.Zero)
|
||||
throw new ObjectDisposedException("OpusEncoder");
|
||||
|
||||
var ret = API.opus_encoder_ctl(_encoder, Ctl.SetInbandFECRequest, value ? 1 : 0);
|
||||
if (ret < 0)
|
||||
throw new Exception("Encoder error - " + ((Errors)ret).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
~OpusEncoder()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private bool disposed;
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
if (_encoder != IntPtr.Zero)
|
||||
{
|
||||
API.opus_encoder_destroy(_encoder);
|
||||
_encoder = IntPtr.Zero;
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user