using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Concentus.Structs;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Logan Stromberg")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Logan Stromberg, Andrew Ward")]
[assembly: AssemblyDescription("This package implements file streams which can be used to extract or encode Opus packets in an Ogg-formatted audio file (usually .opus), giving developers a very simple API to perform the task of reading or writing audio files that can be played universally. The Concentus library is used to encode/decode the opus packets automatically. The codec can optionally be accelerated by also referencing the Concentus.Native package.")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6.0+dee65d9de0ef009658c2d60be6fd9b29d362a482")]
[assembly: AssemblyProduct("Concentus.Oggfile")]
[assembly: AssemblyTitle("Concentus.Oggfile")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus.oggfile")]
[assembly: AssemblyVersion("1.0.6.0")]
namespace Concentus.Oggfile;
internal static class BinaryHelpers
{
internal static void Int16ToByteArrayLittleEndian(short val, byte[] target, int targetOffset)
{
UInt16ToByteArrayLittleEndian((ushort)val, target, targetOffset);
}
internal static void UInt16ToByteArrayLittleEndian(ushort val, byte[] target, int targetOffset)
{
target[targetOffset + 1] = (byte)((uint)(val >> 8) & 0xFFu);
target[targetOffset] = (byte)(val & 0xFFu);
}
internal static void Int16ToByteSpanLittleEndian(short val, ref Span<byte> target)
{
UInt16ToByteArraySpanEndian((ushort)val, ref target);
}
internal static void UInt16ToByteArraySpanEndian(ushort val, ref Span<byte> target)
{
target[1] = (byte)((uint)(val >> 8) & 0xFFu);
target[0] = (byte)(val & 0xFFu);
}
internal static void Int16ToByteArrayBigEndian(short val, byte[] target, int targetOffset)
{
UInt16ToByteArrayBigEndian((ushort)val, target, targetOffset);
}
internal static void UInt16ToByteArrayBigEndian(ushort val, byte[] target, int targetOffset)
{
target[targetOffset] = (byte)((uint)(val >> 8) & 0xFFu);
target[targetOffset + 1] = (byte)(val & 0xFFu);
}
internal static void Int16ToByteSpanBigEndian(short val, ref Span<byte> target)
{
UInt16ToByteSpanBigEndian((ushort)val, ref target);
}
internal static void UInt16ToByteSpanBigEndian(ushort val, ref Span<byte> target)
{
target[0] = (byte)((uint)(val >> 8) & 0xFFu);
target[1] = (byte)(val & 0xFFu);
}
internal static void Int32ToByteArrayLittleEndian(int val, byte[] target, int targetOffset)
{
UInt32ToByteArrayLittleEndian((uint)val, target, targetOffset);
}
internal static void UInt32ToByteArrayLittleEndian(uint val, byte[] target, int targetOffset)
{
target[targetOffset + 3] = (byte)((val >> 24) & 0xFFu);
target[targetOffset + 2] = (byte)((val >> 16) & 0xFFu);
target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu);
target[targetOffset] = (byte)(val & 0xFFu);
}
internal static void Int32ToByteSpanLittleEndian(int val, ref Span<byte> target)
{
UInt32ToByteSpanLittleEndian((uint)val, ref target);
}
internal static void UInt32ToByteSpanLittleEndian(uint val, ref Span<byte> target)
{
target[3] = (byte)((val >> 24) & 0xFFu);
target[2] = (byte)((val >> 16) & 0xFFu);
target[1] = (byte)((val >> 8) & 0xFFu);
target[0] = (byte)(val & 0xFFu);
}
internal static void UInt24ToByteArrayBigEndian(uint val, byte[] target, int targetOffset)
{
target[targetOffset] = (byte)((val >> 16) & 0xFFu);
target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu);
target[targetOffset + 2] = (byte)(val & 0xFFu);
}
internal static void UInt24ToByteSpanBigEndian(uint val, ref Span<byte> target)
{
target[0] = (byte)((val >> 16) & 0xFFu);
target[1] = (byte)((val >> 8) & 0xFFu);
target[2] = (byte)(val & 0xFFu);
}
internal static void Int32ToByteArrayBigEndian(int val, byte[] target, int targetOffset)
{
UInt32ToByteArrayBigEndian((uint)val, target, targetOffset);
}
internal static void UInt32ToByteArrayBigEndian(uint val, byte[] target, int targetOffset)
{
target[targetOffset] = (byte)((val >> 24) & 0xFFu);
target[targetOffset + 1] = (byte)((val >> 16) & 0xFFu);
target[targetOffset + 2] = (byte)((val >> 8) & 0xFFu);
target[targetOffset + 3] = (byte)(val & 0xFFu);
}
internal static void Int32ToByteSpanBigEndian(int val, ref Span<byte> target)
{
UInt32ToByteSpanBigEndian((uint)val, ref target);
}
internal static void UInt32ToByteSpanBigEndian(uint val, ref Span<byte> target)
{
target[0] = (byte)((val >> 24) & 0xFFu);
target[1] = (byte)((val >> 16) & 0xFFu);
target[2] = (byte)((val >> 8) & 0xFFu);
target[3] = (byte)(val & 0xFFu);
}
internal static void Int64ToByteArrayLittleEndian(long val, byte[] target, int targetOffset)
{
UInt64ToByteArrayLittleEndian((ulong)val, target, targetOffset);
}
internal static void UInt64ToByteArrayLittleEndian(ulong val, byte[] target, int targetOffset)
{
target[targetOffset + 7] = (byte)((val >> 56) & 0xFF);
target[targetOffset + 6] = (byte)((val >> 48) & 0xFF);
target[targetOffset + 5] = (byte)((val >> 40) & 0xFF);
target[targetOffset + 4] = (byte)((val >> 32) & 0xFF);
target[targetOffset + 3] = (byte)((val >> 24) & 0xFF);
target[targetOffset + 2] = (byte)((val >> 16) & 0xFF);
target[targetOffset + 1] = (byte)((val >> 8) & 0xFF);
target[targetOffset] = (byte)(val & 0xFF);
}
internal static void Int64ToByteSpanLittleEndian(long val, ref Span<byte> target)
{
UInt64ToByteSpanLittleEndian((ulong)val, ref target);
}
internal static void UInt64ToByteSpanLittleEndian(ulong val, ref Span<byte> target)
{
target[7] = (byte)((val >> 56) & 0xFF);
target[6] = (byte)((val >> 48) & 0xFF);
target[5] = (byte)((val >> 40) & 0xFF);
target[4] = (byte)((val >> 32) & 0xFF);
target[3] = (byte)((val >> 24) & 0xFF);
target[2] = (byte)((val >> 16) & 0xFF);
target[1] = (byte)((val >> 8) & 0xFF);
target[0] = (byte)(val & 0xFF);
}
internal static void Int64ToByteArrayBigEndian(long val, byte[] target, int targetOffset)
{
UInt64ToByteArrayBigEndian((ulong)val, target, targetOffset);
}
internal static void UInt64ToByteArrayBigEndian(ulong val, byte[] target, int targetOffset)
{
target[targetOffset] = (byte)((val >> 56) & 0xFF);
target[targetOffset + 1] = (byte)((val >> 48) & 0xFF);
target[targetOffset + 2] = (byte)((val >> 40) & 0xFF);
target[targetOffset + 3] = (byte)((val >> 32) & 0xFF);
target[targetOffset + 4] = (byte)((val >> 24) & 0xFF);
target[targetOffset + 5] = (byte)((val >> 16) & 0xFF);
target[targetOffset + 6] = (byte)((val >> 8) & 0xFF);
target[targetOffset + 7] = (byte)(val & 0xFF);
}
internal static void Int64ToByteSpanBigEndian(long val, ref Span<byte> target)
{
UInt64ToByteSpanBigEndian((ulong)val, ref target);
}
internal static void UInt64ToByteSpanBigEndian(ulong val, ref Span<byte> target)
{
target[0] = (byte)((val >> 56) & 0xFF);
target[1] = (byte)((val >> 48) & 0xFF);
target[2] = (byte)((val >> 40) & 0xFF);
target[3] = (byte)((val >> 32) & 0xFF);
target[4] = (byte)((val >> 24) & 0xFF);
target[5] = (byte)((val >> 16) & 0xFF);
target[6] = (byte)((val >> 8) & 0xFF);
target[7] = (byte)(val & 0xFF);
}
internal static short ByteArrayToInt16LittleEndian(byte[] source, int offset)
{
return (short)((short)(0 | (short)(source[offset + 1] << 8)) | (short)source[offset]);
}
internal static short ByteSpanToInt16LittleEndian(ref Span<byte> source)
{
return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]);
}
internal static short ByteSpanToInt16LittleEndian(ref ReadOnlySpan<byte> source)
{
return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]);
}
internal static ushort ByteArrayToUInt16LittleEndian(byte[] source, int offset)
{
return (ushort)((ushort)(0u | (ushort)(source[offset + 1] << 8)) | source[offset]);
}
internal static ushort ByteSpanToUInt16LittleEndian(ref Span<byte> source)
{
return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]);
}
internal static ushort ByteSpanToUInt16LittleEndian(ref ReadOnlySpan<byte> source)
{
return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]);
}
internal static short ByteArrayToInt16BigEndian(byte[] source, int offset)
{
return (short)((short)(0 | (short)(source[offset] << 8)) | (short)source[offset + 1]);
}
internal static short ByteSpanToInt16BigEndian(ref Span<byte> source)
{
return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]);
}
internal static short ByteSpanToInt16BigEndian(ref ReadOnlySpan<byte> source)
{
return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]);
}
internal static ushort ByteArrayToUInt16BigEndian(byte[] source, int offset)
{
return (ushort)((ushort)(0u | (ushort)(source[offset] << 8)) | source[offset + 1]);
}
internal static ushort ByteSpanToUInt16BigEndian(ref Span<byte> source)
{
return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]);
}
internal static ushort ByteSpanToUInt16BigEndian(ref ReadOnlySpan<byte> source)
{
return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]);
}
internal static int ByteArrayToInt32LittleEndian(byte[] source, int offset)
{
return 0 | (source[offset + 3] << 24) | (source[offset + 2] << 16) | (source[offset + 1] << 8) | source[offset];
}
internal static int ByteSpanToInt32LittleEndian(ref Span<byte> source)
{
return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0];
}
internal static int ByteSpanToInt32LittleEndian(ref ReadOnlySpan<byte> source)
{
return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0];
}
internal static uint ByteArrayToUInt32LittleEndian(byte[] source, int offset)
{
return 0u | (uint)(source[offset + 3] << 24) | (uint)(source[offset + 2] << 16) | (uint)(source[offset + 1] << 8) | source[offset];
}
internal static uint ByteSpanToUInt32LittleEndian(ref Span<byte> source)
{
return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0];
}
internal static uint ByteSpanToUInt32LittleEndian(ref ReadOnlySpan<byte> source)
{
return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0];
}
internal static uint ByteArrayToUInt24BigEndian(byte[] source, int offset)
{
return 0u | (uint)(source[offset] << 16) | (uint)(source[offset + 1] << 8) | source[offset + 2];
}
internal static uint ByteSpanToUInt24BigEndian(ref Span<byte> source)
{
return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2];
}
internal static uint ByteSpanToUInt24BigEndian(ref ReadOnlySpan<byte> source)
{
return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2];
}
internal static int ByteArrayToInt32BigEndian(byte[] source, int offset)
{
return 0 | (source[offset] << 24) | (source[offset + 1] << 16) | (source[offset + 2] << 8) | source[offset + 3];
}
internal static int ByteSpanToInt32BigEndian(ref Span<byte> source)
{
return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3];
}
internal static int ByteSpanToInt32BigEndian(ref ReadOnlySpan<byte> source)
{
return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3];
}
internal static uint ByteArrayToUInt32BigEndian(byte[] source, int offset)
{
return 0u | (uint)(source[offset] << 24) | (uint)(source[offset + 1] << 16) | (uint)(source[offset + 2] << 8) | source[offset + 3];
}
internal static uint ByteSpanToUInt32BigEndian(ref Span<byte> source)
{
return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3];
}
internal static uint ByteSpanToUInt32BigEndian(ref ReadOnlySpan<byte> source)
{
return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3];
}
internal static long ByteArrayToInt64LittleEndian(byte[] source, int offset)
{
return (long)(0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset]);
}
internal static long ByteSpanToInt64LittleEndian(ref Span<byte> source)
{
return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]);
}
internal static long ByteSpanToInt64LittleEndian(ref ReadOnlySpan<byte> source)
{
return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]);
}
internal static ulong ByteArrayToUInt64LittleEndian(byte[] source, int offset)
{
return 0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset];
}
internal static ulong ByteSpanToUInt64LittleEndian(ref Span<byte> source)
{
return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0];
}
internal static ulong ByteSpanToUInt64LittleEndian(ref ReadOnlySpan<byte> source)
{
return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0];
}
internal static long ByteArrayToInt64BigEndian(byte[] source, int offset)
{
return (long)(0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7]);
}
internal static long ByteSpanToInt64BigEndian(ref Span<byte> source)
{
return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]);
}
internal static long ByteSpanToInt64BigEndian(ref ReadOnlySpan<byte> source)
{
return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]);
}
internal static ulong ByteArrayToUInt64BigEndian(byte[] source, int offset)
{
return 0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7];
}
internal static ulong ByteSpanToUInt64BigEndian(ref Span<byte> source)
{
return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7];
}
internal static ulong ByteSpanToUInt64BigEndian(ref ReadOnlySpan<byte> source)
{
return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7];
}
}
internal class BufferedReadStream : Stream
{
private const int DEFAULT_INITIAL_SIZE = 32768;
private const int DEFAULT_MAX_SIZE = 262144;
private Stream _baseStream;
private StreamReadBuffer _buffer;
private long _readPosition;
public bool CloseBaseStream { get; set; }
public bool MinimalRead
{
get
{
return _buffer.MinimalRead;
}
set
{
_buffer.MinimalRead = value;
}
}
public int MaxBufferSize
{
get
{
return _buffer.MaxSize;
}
set
{
CheckLock();
_buffer.MaxSize = value;
}
}
public long BufferBaseOffset => _buffer.BaseOffset;
public int BufferBytesFilled => _buffer.BytesFilled;
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override long Length => _baseStream.Length;
public override long Position
{
get
{
return _readPosition;
}
set
{
Seek(value, SeekOrigin.Begin);
}
}
public BufferedReadStream(Stream baseStream)
: this(baseStream, 32768, 262144, minimalRead: false)
{
}
public BufferedReadStream(Stream baseStream, bool minimalRead)
: this(baseStream, 32768, 262144, minimalRead)
{
}
public BufferedReadStream(Stream baseStream, int initialSize, int maxSize)
: this(baseStream, initialSize, maxSize, minimalRead: false)
{
}
public BufferedReadStream(Stream baseStream, int initialSize, int maxBufferSize, bool minimalRead)
{
if (baseStream == null)
{
throw new ArgumentNullException("baseStream");
}
if (!baseStream.CanRead)
{
throw new ArgumentException("baseStream");
}
if (maxBufferSize < 1)
{
maxBufferSize = 1;
}
if (initialSize < 1)
{
initialSize = 1;
}
if (initialSize > maxBufferSize)
{
initialSize = maxBufferSize;
}
_baseStream = baseStream;
_buffer = new StreamReadBuffer(baseStream, initialSize, maxBufferSize, minimalRead);
_buffer.MaxSize = maxBufferSize;
_buffer.MinimalRead = minimalRead;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
if (_buffer != null)
{
_buffer.Dispose();
_buffer = null;
}
_ = CloseBaseStream;
}
}
public void TakeLock()
{
}
private void CheckLock()
{
}
public void ReleaseLock()
{
}
public void Discard(int bytes)
{
CheckLock();
_buffer.DiscardThrough(_buffer.BaseOffset + bytes);
}
public void DiscardThrough(long offset)
{
CheckLock();
_buffer.DiscardThrough(offset);
}
public override void Flush()
{
}
public override int ReadByte()
{
CheckLock();
int num = _buffer.ReadByte(Position);
if (num > -1)
{
Seek(1L, SeekOrigin.Current);
}
return num;
}
public override int Read(byte[] buffer, int offset, int count)
{
CheckLock();
int num = _buffer.Read(Position, buffer, offset, count);
Seek(num, SeekOrigin.Current);
return num;
}
public override long Seek(long offset, SeekOrigin origin)
{
CheckLock();
switch (origin)
{
case SeekOrigin.Current:
offset += Position;
break;
case SeekOrigin.End:
offset += _baseStream.Length;
break;
}
if (!_baseStream.CanSeek)
{
if (offset < _buffer.BaseOffset)
{
throw new InvalidOperationException("Cannot seek to before the start of the buffer!");
}
if (offset > _buffer.BufferEndOffset)
{
throw new InvalidOperationException("Cannot seek to beyond the end of the buffer! Discard some bytes.");
}
}
return _readPosition = offset;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
internal abstract class DataPacket
{
[Flags]
protected enum PacketFlags : byte
{
IsResync = 1,
IsEndOfStream = 2,
IsShort = 4,
HasGranuleCount = 8,
User1 = 0x10,
User2 = 0x20,
User3 = 0x40,
User4 = 0x80
}
private ulong _bitBucket;
private int _bitCount;
private int _readBits;
private byte _overflowBits;
private PacketFlags _packetFlags;
private long _granulePosition;
private long _pageGranulePosition;
private int _length;
private int _granuleCount;
private int _pageSequenceNumber;
public bool IsResync
{
get
{
return GetFlag(PacketFlags.IsResync);
}
internal set
{
SetFlag(PacketFlags.IsResync, value);
}
}
public long GranulePosition
{
get
{
return _granulePosition;
}
set
{
_granulePosition = value;
}
}
public long PageGranulePosition
{
get
{
return _pageGranulePosition;
}
internal set
{
_pageGranulePosition = value;
}
}
public int Length
{
get
{
return _length;
}
protected set
{
_length = value;
}
}
public bool IsEndOfStream
{
get
{
return GetFlag(PacketFlags.IsEndOfStream);
}
internal set
{
SetFlag(PacketFlags.IsEndOfStream, value);
}
}
public long BitsRead => _readBits;
public int? GranuleCount
{
get
{
if (GetFlag(PacketFlags.HasGranuleCount))
{
return _granuleCount;
}
return null;
}
set
{
if (value.HasValue)
{
_granuleCount = value.Value;
SetFlag(PacketFlags.HasGranuleCount, value: true);
}
else
{
SetFlag(PacketFlags.HasGranuleCount, value: false);
}
}
}
internal int PageSequenceNumber
{
get
{
return _pageSequenceNumber;
}
set
{
_pageSequenceNumber = value;
}
}
internal bool IsShort
{
get
{
return GetFlag(PacketFlags.IsShort);
}
private set
{
SetFlag(PacketFlags.IsShort, value);
}
}
protected bool GetFlag(PacketFlags flag)
{
return (_packetFlags & flag) == flag;
}
protected void SetFlag(PacketFlags flag, bool value)
{
if (value)
{
_packetFlags |= flag;
}
else
{
_packetFlags &= (PacketFlags)(byte)(~(int)flag);
}
}
protected DataPacket(int length)
{
Length = length;
}
protected abstract int ReadNextByte();
public virtual void Done()
{
}
public ulong TryPeekBits(int count, out int bitsRead)
{
ulong num = 0uL;
switch (count)
{
default:
throw new ArgumentOutOfRangeException("count");
case 0:
bitsRead = 0;
return 0uL;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
case 58:
case 59:
case 60:
case 61:
case 62:
case 63:
case 64:
break;
}
while (_bitCount < count)
{
int num2 = ReadNextByte();
if (num2 == -1)
{
bitsRead = _bitCount;
num = _bitBucket;
_bitBucket = 0uL;
_bitCount = 0;
IsShort = true;
return num;
}
_bitBucket = (ulong)((long)(num2 & 0xFF) << _bitCount) | _bitBucket;
_bitCount += 8;
if (_bitCount > 64)
{
_overflowBits = (byte)(num2 >> 72 - _bitCount);
}
}
num = _bitBucket;
if (count < 64)
{
num &= (ulong)((1L << count) - 1);
}
bitsRead = count;
return num;
}
public void SkipBits(int count)
{
if (count == 0)
{
return;
}
if (_bitCount > count)
{
if (count > 63)
{
_bitBucket = 0uL;
}
else
{
_bitBucket >>= count;
}
if (_bitCount > 64)
{
int num = _bitCount - 64;
_bitBucket |= (ulong)_overflowBits << _bitCount - count - num;
if (num > count)
{
_overflowBits = (byte)(_overflowBits >> count);
}
}
_bitCount -= count;
_readBits += count;
return;
}
if (_bitCount == count)
{
_bitBucket = 0uL;
_bitCount = 0;
_readBits += count;
return;
}
count -= _bitCount;
_readBits += _bitCount;
_bitCount = 0;
_bitBucket = 0uL;
while (count > 8)
{
if (ReadNextByte() == -1)
{
count = 0;
IsShort = true;
break;
}
count -= 8;
_readBits += 8;
}
if (count > 0)
{
int num2 = ReadNextByte();
if (num2 == -1)
{
IsShort = true;
return;
}
_bitBucket = (ulong)(num2 >> count);
_bitCount = 8 - count;
_readBits += count;
}
}
protected void ResetBitReader()
{
_bitBucket = 0uL;
_bitCount = 0;
_readBits = 0;
IsShort = false;
}
public ulong ReadBits(int count)
{
if (count == 0)
{
return 0uL;
}
int bitsRead;
ulong result = TryPeekBits(count, out bitsRead);
SkipBits(count);
return result;
}
public byte PeekByte()
{
int bitsRead;
return (byte)TryPeekBits(8, out bitsRead);
}
public byte ReadByte()
{
return (byte)ReadBits(8);
}
public byte[] ReadBytes(int count)
{
List<byte> list = new List<byte>(count);
while (list.Count < count)
{
list.Add(ReadByte());
}
return list.ToArray();
}
public int Read(byte[] buffer, int index, int count)
{
if (index < 0 || index + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("index");
}
for (int i = 0; i < count; i++)
{
int bitsRead;
byte b = (byte)TryPeekBits(8, out bitsRead);
if (bitsRead == 0)
{
return i;
}
buffer[index++] = b;
SkipBits(8);
}
return count;
}
public bool ReadBit()
{
return ReadBits(1) == 1;
}
public short ReadInt16()
{
return (short)ReadBits(16);
}
public int ReadInt32()
{
return (int)ReadBits(32);
}
public long ReadInt64()
{
return (long)ReadBits(64);
}
public ushort ReadUInt16()
{
return (ushort)ReadBits(16);
}
public uint ReadUInt32()
{
return (uint)ReadBits(32);
}
public ulong ReadUInt64()
{
return ReadBits(64);
}
public void SkipBytes(int count)
{
SkipBits(count * 8);
}
}
internal interface IContainerReader : IDisposable
{
int[] StreamSerials { get; }
bool CanSeek { get; }
long WasteBits { get; }
int PagesRead { get; }
event EventHandler<NewStreamEventArgs> NewStream;
bool Init();
bool FindNextStream();
int GetTotalPageCount();
}
internal interface IPacketProvider : IDisposable
{
int StreamSerial { get; }
bool CanSeek { get; }
long ContainerBits { get; }
event EventHandler<ParameterChangeEventArgs> ParameterChange;
int GetTotalPageCount();
DataPacket GetNextPacket();
DataPacket PeekNextPacket();
DataPacket GetPacket(int packetIndex);
long GetGranuleCount();
DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback);
void SeekToPacket(DataPacket packet, int preRoll);
}
internal class NewStreamEventArgs : EventArgs
{
public IPacketProvider PacketProvider { get; private set; }
public bool IgnoreStream { get; set; }
public NewStreamEventArgs(IPacketProvider packetProvider)
{
if (packetProvider == null)
{
throw new ArgumentNullException("packetProvider");
}
PacketProvider = packetProvider;
}
}
internal class OggContainerReader : IContainerReader, IDisposable
{
private class PageHeader
{
public int StreamSerial { get; set; }
public PageFlags Flags { get; set; }
public long GranulePosition { get; set; }
public int SequenceNumber { get; set; }
public long DataOffset { get; set; }
public int[] PacketSizes { get; set; }
public bool LastPacketContinues { get; set; }
public bool IsResync { get; set; }
}
private Crc _crc = new Crc();
private BufferedReadStream _stream;
private Dictionary<int, PacketReader> _packetReaders;
private List<int> _disposedStreamSerials;
private long _nextPageOffset;
private int _pageCount;
private byte[] _readBuffer = new byte[65025];
private long _containerBits;
private long _wasteBits;
public int[] StreamSerials => _packetReaders.Keys.ToArray();
public int PagesRead => _pageCount;
public bool CanSeek => _stream.CanSeek;
public long WasteBits => _wasteBits;
public event EventHandler<NewStreamEventArgs> NewStream;
public OggContainerReader(Stream stream, bool closeOnDispose)
{
_packetReaders = new Dictionary<int, PacketReader>();
_disposedStreamSerials = new List<int>();
_stream = (stream as BufferedReadStream) ?? new BufferedReadStream(stream)
{
CloseBaseStream = closeOnDispose
};
}
public bool Init()
{
_stream.TakeLock();
try
{
return GatherNextPage() != -1;
}
finally
{
_stream.ReleaseLock();
}
}
public void Dispose()
{
int[] streamSerials = StreamSerials;
foreach (int key in streamSerials)
{
_packetReaders[key].Dispose();
}
_nextPageOffset = 0L;
_containerBits = 0L;
_wasteBits = 0L;
_stream.Dispose();
}
public IPacketProvider GetStream(int streamSerial)
{
if (!_packetReaders.TryGetValue(streamSerial, out var value))
{
throw new ArgumentOutOfRangeException("streamSerial");
}
return value;
}
public bool FindNextStream()
{
if (!CanSeek)
{
throw new InvalidOperationException();
}
int count = _packetReaders.Count;
while (_packetReaders.Count == count)
{
_stream.TakeLock();
try
{
if (GatherNextPage() == -1)
{
break;
}
}
finally
{
_stream.ReleaseLock();
}
}
return count > _packetReaders.Count;
}
public int GetTotalPageCount()
{
if (!CanSeek)
{
throw new InvalidOperationException();
}
while (true)
{
_stream.TakeLock();
try
{
if (GatherNextPage() == -1)
{
break;
}
}
finally
{
_stream.ReleaseLock();
}
}
return _pageCount;
}
private PageHeader ReadPageHeader(long position)
{
_stream.Seek(position, SeekOrigin.Begin);
if (_stream.Read(_readBuffer, 0, 27) != 27)
{
return null;
}
if (_readBuffer[0] != 79 || _readBuffer[1] != 103 || _readBuffer[2] != 103 || _readBuffer[3] != 83)
{
return null;
}
if (_readBuffer[4] != 0)
{
return null;
}
PageHeader pageHeader = new PageHeader();
pageHeader.Flags = (PageFlags)_readBuffer[5];
pageHeader.GranulePosition = BitConverter.ToInt64(_readBuffer, 6);
pageHeader.StreamSerial = BitConverter.ToInt32(_readBuffer, 14);
pageHeader.SequenceNumber = BitConverter.ToInt32(_readBuffer, 18);
uint checkCrc = BitConverter.ToUInt32(_readBuffer, 22);
_crc.Reset();
for (int i = 0; i < 22; i++)
{
_crc.Update(_readBuffer[i]);
}
_crc.Update(0);
_crc.Update(0);
_crc.Update(0);
_crc.Update(0);
_crc.Update(_readBuffer[26]);
int num = _readBuffer[26];
if (_stream.Read(_readBuffer, 0, num) != num)
{
return null;
}
List<int> list = new List<int>(num);
int num2 = 0;
int num3 = 0;
for (int j = 0; j < num; j++)
{
byte b = _readBuffer[j];
_crc.Update(b);
if (num3 == list.Count)
{
list.Add(0);
}
list[num3] += b;
if (b < byte.MaxValue)
{
num3++;
pageHeader.LastPacketContinues = false;
}
else
{
pageHeader.LastPacketContinues = true;
}
num2 += b;
}
pageHeader.PacketSizes = list.ToArray();
pageHeader.DataOffset = position + 27 + num;
if (_stream.Read(_readBuffer, 0, num2) != num2)
{
return null;
}
for (int k = 0; k < num2; k++)
{
_crc.Update(_readBuffer[k]);
}
if (_crc.Test(checkCrc))
{
_containerBits += 8 * (27 + num);
_pageCount++;
return pageHeader;
}
return null;
}
private PageHeader FindNextPageHeader()
{
long num = _nextPageOffset;
bool isResync = false;
PageHeader pageHeader;
while ((pageHeader = ReadPageHeader(num)) == null)
{
isResync = true;
_wasteBits += 8L;
num = (_stream.Position = num + 1);
int num3 = 0;
do
{
switch (_stream.ReadByte())
{
case 79:
if (_stream.ReadByte() == 103 && _stream.ReadByte() == 103 && _stream.ReadByte() == 83)
{
num += num3;
goto end_IL_0032;
}
_stream.Seek(-3L, SeekOrigin.Current);
break;
case -1:
return null;
}
_wasteBits += 8L;
continue;
end_IL_0032:
break;
}
while (++num3 < 65536);
if (num3 == 65536)
{
return null;
}
}
pageHeader.IsResync = isResync;
_nextPageOffset = pageHeader.DataOffset;
for (int i = 0; i < pageHeader.PacketSizes.Length; i++)
{
_nextPageOffset += pageHeader.PacketSizes[i];
}
return pageHeader;
}
private bool AddPage(PageHeader hdr)
{
if (!_packetReaders.TryGetValue(hdr.StreamSerial, out var value))
{
value = new PacketReader(this, hdr.StreamSerial);
}
value.ContainerBits += _containerBits;
_containerBits = 0L;
bool isContinued = hdr.PacketSizes.Length == 1 && hdr.LastPacketContinues;
bool isContinuation = (hdr.Flags & PageFlags.ContinuesPacket) == PageFlags.ContinuesPacket;
bool isEndOfStream = false;
bool isResync = hdr.IsResync;
long num = hdr.DataOffset;
int num2 = hdr.PacketSizes.Length;
int[] packetSizes = hdr.PacketSizes;
foreach (int num3 in packetSizes)
{
Packet packet = new Packet(this, num, num3)
{
PageGranulePosition = hdr.GranulePosition,
IsEndOfStream = isEndOfStream,
PageSequenceNumber = hdr.SequenceNumber,
IsContinued = isContinued,
IsContinuation = isContinuation,
IsResync = isResync
};
value.AddPacket(packet);
num += num3;
isContinuation = false;
isResync = false;
if (--num2 == 1)
{
isContinued = hdr.LastPacketContinues;
isEndOfStream = (hdr.Flags & PageFlags.EndOfStream) == PageFlags.EndOfStream;
}
}
if (!_packetReaders.ContainsKey(hdr.StreamSerial))
{
_packetReaders.Add(hdr.StreamSerial, value);
return true;
}
return false;
}
private int GatherNextPage()
{
PageHeader pageHeader;
while (true)
{
pageHeader = FindNextPageHeader();
if (pageHeader == null)
{
return -1;
}
if (!_disposedStreamSerials.Contains(pageHeader.StreamSerial))
{
if (!AddPage(pageHeader))
{
break;
}
EventHandler<NewStreamEventArgs> newStream = this.NewStream;
if (newStream == null)
{
break;
}
NewStreamEventArgs newStreamEventArgs = new NewStreamEventArgs(_packetReaders[pageHeader.StreamSerial]);
newStream(this, newStreamEventArgs);
if (!newStreamEventArgs.IgnoreStream)
{
break;
}
_packetReaders[pageHeader.StreamSerial].Dispose();
}
}
return pageHeader.StreamSerial;
}
internal void DisposePacketReader(PacketReader packetReader)
{
_disposedStreamSerials.Add(packetReader.StreamSerial);
_packetReaders.Remove(packetReader.StreamSerial);
}
internal int PacketReadByte(long offset)
{
_stream.TakeLock();
try
{
_stream.Position = offset;
return _stream.ReadByte();
}
finally
{
_stream.ReleaseLock();
}
}
internal void PacketDiscardThrough(long offset)
{
_stream.TakeLock();
try
{
_stream.DiscardThrough(offset);
}
finally
{
_stream.ReleaseLock();
}
}
internal void GatherNextPage(int streamSerial)
{
if (!_packetReaders.ContainsKey(streamSerial))
{
throw new ArgumentOutOfRangeException("streamSerial");
}
int num;
do
{
_stream.TakeLock();
try
{
if (_packetReaders[streamSerial].HasEndOfStream)
{
break;
}
num = GatherNextPage();
if (num != -1)
{
continue;
}
foreach (KeyValuePair<int, PacketReader> packetReader in _packetReaders)
{
if (!packetReader.Value.HasEndOfStream)
{
packetReader.Value.SetEndOfStream();
}
}
break;
}
finally
{
_stream.ReleaseLock();
}
}
while (num != streamSerial);
}
}
internal class Crc
{
private const uint CRC32_POLY = 79764919u;
private static uint[] crcTable;
private uint _crc;
public uint Value => _crc;
static Crc()
{
crcTable = new uint[256];
for (uint num = 0u; num < 256; num++)
{
uint num2 = num << 24;
for (int i = 0; i < 8; i++)
{
num2 = (num2 << 1) ^ ((num2 >= 2147483648u) ? 79764919u : 0u);
}
crcTable[num] = num2;
}
}
public Crc()
{
Reset();
}
public void Reset()
{
_crc = 0u;
}
public void Update(int nextVal)
{
_crc = (_crc << 8) ^ crcTable[nextVal ^ (_crc >> 24)];
}
public bool Test(uint checkCrc)
{
return _crc == checkCrc;
}
}
internal class Packet : DataPacket
{
private long _offset;
private int _length;
private int _curOfs;
private Packet _mergedPacket;
private Packet _next;
private Packet _prev;
private OggContainerReader _containerReader;
internal Packet Next
{
get
{
return _next;
}
set
{
_next = value;
}
}
internal Packet Prev
{
get
{
return _prev;
}
set
{
_prev = value;
}
}
internal bool IsContinued
{
get
{
return GetFlag(PacketFlags.User1);
}
set
{
SetFlag(PacketFlags.User1, value);
}
}
internal bool IsContinuation
{
get
{
return GetFlag(PacketFlags.User2);
}
set
{
SetFlag(PacketFlags.User2, value);
}
}
internal Packet(OggContainerReader containerReader, long streamOffset, int length)
: base(length)
{
_containerReader = containerReader;
_offset = streamOffset;
_length = length;
_curOfs = 0;
}
internal void MergeWith(DataPacket continuation)
{
if (!(continuation is Packet mergedPacket))
{
throw new ArgumentException("Incorrect packet type!");
}
base.Length += continuation.Length;
if (_mergedPacket == null)
{
_mergedPacket = mergedPacket;
}
else
{
_mergedPacket.MergeWith(continuation);
}
base.PageGranulePosition = continuation.PageGranulePosition;
base.PageSequenceNumber = continuation.PageSequenceNumber;
}
internal void Reset()
{
_curOfs = 0;
ResetBitReader();
if (_mergedPacket != null)
{
_mergedPacket.Reset();
}
}
protected override int ReadNextByte()
{
if (_curOfs == _length)
{
if (_mergedPacket == null)
{
return -1;
}
return _mergedPacket.ReadNextByte();
}
int num = _containerReader.PacketReadByte(_offset + _curOfs);
if (num != -1)
{
_curOfs++;
}
return num;
}
public override void Done()
{
if (_mergedPacket != null)
{
_mergedPacket.Done();
}
else
{
_containerReader.PacketDiscardThrough(_offset + _length);
}
}
}
[DebuggerTypeProxy(typeof(DebugView))]
internal class PacketReader : IPacketProvider, IDisposable
{
internal class DebugView
{
private PacketReader _reader;
private Packet _last;
private Packet _first;
private Packet[] _packetList = new Packet[0];
public OggContainerReader Container => _reader._container;
public int StreamSerial => _reader._streamSerial;
public bool EndOfStreamFound => _reader._eosFound;
public int CurrentPacketIndex
{
get
{
if (_reader._current == null)
{
return -1;
}
return Array.IndexOf(Packets, _reader._current);
}
}
public Packet[] Packets
{
get
{
if (_reader._last == _last && _reader._first == _first)
{
return _packetList;
}
_last = _reader._last;
_first = _reader._first;
List<Packet> list = new List<Packet>();
for (Packet packet = _first; packet != null; packet = packet.Next)
{
list.Add(packet);
}
_packetList = list.ToArray();
return _packetList;
}
}
public DebugView(PacketReader reader)
{
if (reader == null)
{
throw new ArgumentNullException("reader");
}
_reader = reader;
}
}
private OggContainerReader _container;
private int _streamSerial;
private bool _eosFound;
private Packet _first;
private Packet _current;
private Packet _last;
private object _packetLock = new object();
internal bool HasEndOfStream => _eosFound;
public int StreamSerial => _streamSerial;
public long ContainerBits { get; set; }
public bool CanSeek => _container.CanSeek;
public event EventHandler<ParameterChangeEventArgs> ParameterChange;
internal PacketReader(OggContainerReader container, int streamSerial)
{
_container = container;
_streamSerial = streamSerial;
}
public void Dispose()
{
_eosFound = true;
_container.DisposePacketReader(this);
_container = null;
_current = null;
if (_first != null)
{
Packet packet = _first;
_first = null;
while (packet.Next != null)
{
Packet next = packet.Next;
packet.Next = null;
packet = next;
packet.Prev = null;
}
packet = null;
}
_last = null;
}
internal void AddPacket(Packet packet)
{
lock (_packetLock)
{
if (_eosFound)
{
return;
}
if (packet.IsResync)
{
packet.IsContinuation = false;
if (_last != null)
{
_last.IsContinued = false;
}
}
if (packet.IsContinuation)
{
if (_last == null)
{
throw new InvalidDataException();
}
if (!_last.IsContinued)
{
throw new InvalidDataException();
}
_last.MergeWith(packet);
_last.IsContinued = packet.IsContinued;
}
else
{
if (packet == null)
{
throw new ArgumentException("Wrong packet datatype", "packet");
}
if (_first == null)
{
_first = packet;
_last = packet;
}
else
{
Packet packet2 = (packet.Prev = _last);
Packet last2 = (packet2.Next = packet);
_last = last2;
}
}
if (packet.IsEndOfStream)
{
SetEndOfStream();
}
}
}
internal void SetEndOfStream()
{
lock (_packetLock)
{
_eosFound = true;
if (_last.IsContinued)
{
_last = _last.Prev;
_last.Next.Prev = null;
_last.Next = null;
}
}
}
public DataPacket GetNextPacket()
{
return _current = PeekNextPacketInternal();
}
public DataPacket PeekNextPacket()
{
return PeekNextPacketInternal();
}
private Packet PeekNextPacketInternal()
{
Packet packet;
if (_current == null)
{
packet = _first;
}
else
{
while (true)
{
lock (_packetLock)
{
packet = _current.Next;
if ((packet != null && !packet.IsContinued) || _eosFound)
{
break;
}
goto IL_004f;
}
IL_004f:
_container.GatherNextPage(_streamSerial);
}
}
if (packet != null)
{
if (packet.IsContinued)
{
throw new InvalidDataException("Packet is incomplete!");
}
packet.Reset();
}
return packet;
}
internal void ReadAllPages()
{
if (!CanSeek)
{
throw new InvalidOperationException();
}
while (!_eosFound)
{
_container.GatherNextPage(_streamSerial);
}
}
internal DataPacket GetLastPacket()
{
ReadAllPages();
return _last;
}
public int GetTotalPageCount()
{
ReadAllPages();
int num = 0;
int num2 = 0;
for (Packet packet = _first; packet != null; packet = packet.Next)
{
if (packet.PageSequenceNumber != num2)
{
num++;
num2 = packet.PageSequenceNumber;
}
}
return num;
}
public DataPacket GetPacket(int packetIndex)
{
if (!CanSeek)
{
throw new InvalidOperationException();
}
if (packetIndex < 0)
{
throw new ArgumentOutOfRangeException("index");
}
if (_first == null)
{
throw new InvalidOperationException("Packet reader has no packets!");
}
Packet packet = _first;
while (--packetIndex >= 0)
{
while (packet.Next == null)
{
if (_eosFound)
{
throw new ArgumentOutOfRangeException("index");
}
_container.GatherNextPage(_streamSerial);
}
packet = packet.Next;
}
packet.Reset();
return packet;
}
private Packet GetLastPacketInPage(Packet packet)
{
if (packet != null)
{
int pageSequenceNumber = packet.PageSequenceNumber;
while (packet.Next != null && packet.Next.PageSequenceNumber == pageSequenceNumber)
{
packet = packet.Next;
}
if (packet != null && packet.IsContinued)
{
packet = packet.Prev;
}
}
return packet;
}
private Packet FindPacketInPage(Packet pagePacket, long targetGranulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback)
{
Packet lastPacketInPage = GetLastPacketInPage(pagePacket);
if (lastPacketInPage == null)
{
return null;
}
Packet packet = lastPacketInPage;
do
{
if (!packet.GranuleCount.HasValue)
{
if (packet == lastPacketInPage)
{
packet.GranulePosition = packet.PageGranulePosition;
}
else
{
packet.GranulePosition = packet.Next.GranulePosition - packet.Next.GranuleCount.Value;
}
if (packet == _last && _eosFound && packet.Prev.PageSequenceNumber < packet.PageSequenceNumber)
{
packet.GranuleCount = (int)(packet.GranulePosition - packet.Prev.PageGranulePosition);
}
else if (packet.Prev != null)
{
packet.Prev.Reset();
packet.Reset();
packet.GranuleCount = packetGranuleCountCallback(packet, packet.Prev);
}
else
{
if (packet.GranulePosition > packet.Next.GranulePosition - packet.Next.GranuleCount)
{
throw new InvalidOperationException("First data packet size mismatch");
}
packet.GranuleCount = (int)packet.GranulePosition;
}
}
if (targetGranulePos <= packet.GranulePosition && targetGranulePos > packet.GranulePosition - packet.GranuleCount)
{
if (packet.Prev != null && !packet.Prev.GranuleCount.HasValue)
{
packet.Prev.GranulePosition = packet.GranulePosition - packet.GranuleCount.Value;
}
return packet;
}
packet = packet.Prev;
}
while (packet != null && packet.PageSequenceNumber == lastPacketInPage.PageSequenceNumber);
if (packet != null && packet.PageGranulePosition < targetGranulePos)
{
packet.GranulePosition = packet.PageGranulePosition;
return packet.Next;
}
return null;
}
public DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback)
{
if (granulePos < 0)
{
throw new ArgumentOutOfRangeException("granulePos");
}
Packet packet = null;
Packet packet2 = _current ?? _first;
if (granulePos > packet2.PageGranulePosition)
{
while (granulePos > packet2.PageGranulePosition)
{
if ((packet2.Next == null || packet2.IsContinued) && !_eosFound)
{
_container.GatherNextPage(_streamSerial);
if (_eosFound)
{
packet2 = null;
break;
}
}
packet2 = packet2.Next;
}
return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback);
}
while (packet2.Prev != null && (granulePos <= packet2.Prev.PageGranulePosition || packet2.Prev.PageGranulePosition == -1))
{
packet2 = packet2.Prev;
}
return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback);
}
public void SeekToPacket(DataPacket packet, int preRoll)
{
if (preRoll < 0)
{
throw new ArgumentOutOfRangeException("preRoll");
}
if (packet == null)
{
throw new ArgumentNullException("granulePos");
}
Packet packet2 = packet as Packet;
if (packet2 == null)
{
throw new ArgumentException("Incorrect packet type!", "packet");
}
while (--preRoll >= 0)
{
packet2 = packet2.Prev;
if (packet2 == null)
{
throw new ArgumentOutOfRangeException("preRoll");
}
}
_current = packet2.Prev;
}
public long GetGranuleCount()
{
return GetLastPacket().PageGranulePosition;
}
}
[Flags]
internal enum PageFlags
{
None = 0,
ContinuesPacket = 1,
BeginningOfStream = 2,
EndOfStream = 4
}
internal class OpusHeader
{
private byte version;
private byte channel_count;
private ushort pre_skip;
private uint input_sample_rate;
private short output_gain;
private byte mapping_family;
private byte stream_count;
private byte coupled_count;
}
public class OpusOggReadStream
{
private const double GranuleSampleRate = 48000.0;
private readonly Stream _stream;
private readonly IOpusDecoder _decoder;
private byte[] _nextDataPacket;
private IPacketProvider _packetProvider;
private bool _endOfStream;
public bool CanSeek => _stream.CanSeek;
public OpusTags Tags { get; private set; }
public bool HasNextPacket => !_endOfStream;
public string LastError { get; private set; }
public long PageGranulePosition { get; private set; }
public TimeSpan CurrentTime => TimeSpan.FromSeconds((double)PageGranulePosition / 48000.0);
public long GranuleCount { get; private set; }
public TimeSpan TotalTime => TimeSpan.FromSeconds((double)GranuleCount / 48000.0);
public long PagePosition { get; private set; }
public long PageCount { get; private set; }
public OpusOggReadStream(IOpusDecoder decoder, Stream stream)
{
if (stream == null)
{
throw new ArgumentNullException("stream");
}
_stream = stream;
_decoder = decoder;
_endOfStream = !Initialize();
}
public short[] DecodeNextPacket()
{
//IL_0091: Expected O, but got Unknown
if (_decoder == null)
{
throw new InvalidOperationException("Cannot decode opus packets as a decoder was never provided");
}
if (_nextDataPacket == null || _nextDataPacket.Length == 0)
{
_endOfStream = true;
return null;
}
try
{
int numSamples = OpusPacketInfo.GetNumSamples((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), _decoder.SampleRate);
short[] array = new short[numSamples * _decoder.NumChannels];
_decoder.Decode((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), array.AsSpan(), numSamples, false);
QueueNextPacket();
return array;
}
catch (OpusException val)
{
OpusException val2 = val;
LastError = "Opus decoder threw exception: " + ((Exception)(object)val2).Message;
return null;
}
}
public byte[] ReadNextRawPacket()
{
if (_nextDataPacket == null || _nextDataPacket.Length == 0)
{
_endOfStream = true;
return null;
}
byte[] nextDataPacket = _nextDataPacket;
QueueNextPacket();
return nextDataPacket;
}
private bool Initialize()
{
try
{
OggContainerReader oggContainerReader = new OggContainerReader(_stream, closeOnDispose: true);
if (!oggContainerReader.Init())
{
LastError = "Could not initialize stream";
return false;
}
if (oggContainerReader.StreamSerials.Length == 0)
{
LastError = "Initialization failed: No elementary streams found in input file";
return false;
}
int streamSerial = oggContainerReader.StreamSerials[0];
_packetProvider = oggContainerReader.GetStream(streamSerial);
if (CanSeek)
{
GranuleCount = _packetProvider.GetGranuleCount();
PageCount = _packetProvider.GetTotalPageCount();
}
QueueNextPacket();
return true;
}
catch (Exception ex)
{
LastError = "Unknown initialization error: " + ex.Message;
return false;
}
}
public void SeekTo(TimeSpan playbackTime)
{
if (!CanSeek)
{
throw new InvalidOperationException("Stream is not seekable.");
}
if (playbackTime < TimeSpan.Zero || playbackTime > TotalTime)
{
throw new ArgumentOutOfRangeException("playbackTime");
}
long granulePosition = Convert.ToInt64(playbackTime.TotalSeconds * 48000.0);
SeekToGranulePosition(granulePosition);
}
private void SeekToGranulePosition(long granulePosition)
{
if (!CanSeek)
{
throw new InvalidOperationException("Stream is not seekable.");
}
if (granulePosition < 0 || granulePosition > GranuleCount)
{
throw new ArgumentOutOfRangeException("granulePosition");
}
DataPacket dataPacket = _packetProvider.FindPacket(granulePosition, GetPacketLength);
if (dataPacket == null || dataPacket.IsEndOfStream)
{
_endOfStream = true;
_nextDataPacket = null;
return;
}
_packetProvider.SeekToPacket(dataPacket, 1);
PageGranulePosition = _packetProvider.PeekNextPacket().PageGranulePosition;
if (_decoder != null)
{
_decoder.ResetState();
}
}
private int GetPacketLength(DataPacket curPacket, DataPacket lastPacket)
{
if (lastPacket == null || curPacket.IsResync)
{
return 0;
}
if (curPacket.ReadBit())
{
return 0;
}
if (lastPacket.ReadBit())
{
return 0;
}
return 1;
}
private void QueueNextPacket()
{
if (_endOfStream)
{
return;
}
DataPacket nextPacket = _packetProvider.GetNextPacket();
if (nextPacket == null)
{
_endOfStream = true;
_nextDataPacket = null;
return;
}
PageGranulePosition = nextPacket.PageGranulePosition;
PagePosition = nextPacket.PageSequenceNumber;
byte[] array = new byte[nextPacket.Length];
nextPacket.Read(array, 0, nextPacket.Length);
nextPacket.Done();
if (array.Length > 8 && "OpusHead".Equals(Encoding.UTF8.GetString(array, 0, 8)))
{
QueueNextPacket();
}
else if (array.Length > 8 && "OpusTags".Equals(Encoding.UTF8.GetString(array, 0, 8)))
{
Tags = OpusTags.ParsePacket(array, array.Length);
QueueNextPacket();
}
else
{
_nextDataPacket = array;
}
}
}
public class OpusOggWriteStream
{
private const int FRAME_SIZE_MS = 20;
private IOpusEncoder _encoder;
private Stream _outputStream;
private Crc _crc;
private int _inputChannels;
private IResampler _resampler;
private int _inputSampleRate;
private readonly bool _leaveOpen;
private int _encoderSampleRate;
private short[] _opusFrame;
private int _opusFrameSamples;
private int _opusFrameIndex;
private byte[] _currentHeader = new byte[400];
private byte[] _currentPayload = new byte[65536];
private int _headerIndex;
private int _payloadIndex;
private int _pageCounter;
private int _logicalStreamId;
private long _granulePosition;
private byte _lacingTableCount;
private const int PAGE_FLAGS_POS = 5;
private const int GRANULE_COUNT_POS = 6;
private const int CHECKSUM_HEADER_POS = 22;
private const int SEGMENT_COUNT_POS = 26;
private bool _finalized;
public OpusOggWriteStream(IOpusEncoder encoder, Stream outputStream, OpusTags fileTags = null, int inputSampleRate = 0, int resamplerQuality = 5, bool leaveOpen = false)
{
_encoder = encoder;
if (_encoder.UseDTX)
{
throw new ArgumentException("DTX is not currently supported in Ogg streams");
}
_inputSampleRate = inputSampleRate;
_leaveOpen = leaveOpen;
if (_inputSampleRate == 0)
{
_inputSampleRate = _encoder.SampleRate;
}
_logicalStreamId = new Random().Next();
_encoderSampleRate = encoder.SampleRate;
_inputChannels = encoder.NumChannels;
_outputStream = outputStream;
_opusFrameIndex = 0;
_granulePosition = 0L;
_opusFrameSamples = (int)((long)_encoderSampleRate * 20L / 1000);
_opusFrame = new short[_opusFrameSamples * _inputChannels];
_crc = new Crc();
_resampler = ResamplerFactory.CreateResampler(_inputChannels, _inputSampleRate, _encoderSampleRate, resamplerQuality, (TextWriter)null);
BeginNewPage();
WriteOpusHeadPage();
WriteOpusTagsPage(fileTags);
}
public void WriteSamples(short[] data, int offset, int count)
{
if (_finalized)
{
throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
}
if (data.Length - offset < count)
{
throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + count + " samples, but it actually only has " + (data.Length - offset));
}
int num = 0;
for (int num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num); num2 > 0; num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num))
{
if (_inputSampleRate != _encoderSampleRate)
{
int num3 = (count - num) / _inputChannels;
int num4 = num2 / _inputChannels;
_resampler.ProcessInterleaved(data.AsSpan(offset + num), ref num3, _opusFrame.AsSpan(_opusFrameIndex), ref num4);
num += num3 * _inputChannels;
_opusFrameIndex += num4 * _inputChannels;
}
else
{
data.AsSpan(offset + num, num2).CopyTo(_opusFrame.AsSpan(_opusFrameIndex, num2));
_opusFrameIndex += num2;
num += num2;
}
if (_opusFrameIndex == _opusFrame.Length)
{
int num5 = _encoder.Encode((ReadOnlySpan<short>)_opusFrame.AsSpan(), _opusFrameSamples, _currentPayload.AsSpan(_payloadIndex), _currentPayload.Length - _payloadIndex);
_payloadIndex += num5;
_granulePosition += 960L;
int num6 = num5;
while (num6 >= 255)
{
num6 -= 255;
_currentHeader[_headerIndex++] = byte.MaxValue;
_lacingTableCount++;
}
_currentHeader[_headerIndex++] = (byte)num6;
_lacingTableCount++;
if (_lacingTableCount > 248)
{
FinalizePage();
}
_opusFrameIndex = 0;
}
}
}
public void WriteSamples(float[] data, int offset, int count)
{
if (_finalized)
{
throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
}
short[] array = new short[count];
for (int i = 0; i < count; i++)
{
array[i] = (short)(data[i + offset] * 32767f);
}
WriteSamples(array, 0, count);
}
public void Finish()
{
int num = _opusFrame.Length - _opusFrameIndex;
short[] data = new short[num];
WriteSamples(data, 0, num);
FinalizePage();
WriteStreamFinishedPage();
_outputStream.Flush();
if (!_leaveOpen)
{
_outputStream.Dispose();
}
_finalized = true;
}
private void WriteStreamFinishedPage()
{
_currentHeader[_headerIndex++] = 0;
_lacingTableCount++;
_currentHeader[5] = 4;
FinalizePage();
}
private void WriteOpusHeadPage()
{
if (_payloadIndex != 0)
{
throw new InvalidOperationException("Must begin writing OpusHead on a new page!");
}
_payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex);
_currentPayload[_payloadIndex++] = 1;
_currentPayload[_payloadIndex++] = (byte)_inputChannels;
short val = 0;
_payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex);
_payloadIndex += WriteValueToByteBuffer(_encoderSampleRate, _currentPayload, _payloadIndex);
short val2 = 0;
_payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex);
_currentPayload[_payloadIndex++] = 0;
_currentHeader[_headerIndex++] = (byte)_payloadIndex;
_lacingTableCount++;
_currentHeader[5] = 2;
FinalizePage();
}
private void WriteOpusTagsPage(OpusTags tags = null)
{
if (tags == null)
{
tags = new OpusTags();
}
if (string.IsNullOrEmpty(tags.Comment))
{
tags.Comment = _encoder.GetVersionString();
}
if (_payloadIndex != 0)
{
throw new InvalidOperationException("Must begin writing OpusTags on a new page!");
}
_payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex);
int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4);
_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
_payloadIndex += num;
int payloadIndex = _payloadIndex;
_payloadIndex += 4;
int num2 = 0;
foreach (KeyValuePair<string, string> field in tags.Fields)
{
if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value))
{
num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4);
_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
_payloadIndex += num;
num2++;
}
}
WriteValueToByteBuffer(num2, _currentPayload, payloadIndex);
int num3;
for (num3 = _payloadIndex; num3 >= 255; num3 -= 255)
{
_currentHeader[_headerIndex++] = byte.MaxValue;
_lacingTableCount++;
}
_currentHeader[_headerIndex++] = (byte)num3;
_lacingTableCount++;
FinalizePage();
}
private void BeginNewPage()
{
_headerIndex = 0;
_payloadIndex = 0;
_lacingTableCount = 0;
_headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex);
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex);
_headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex);
_headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex);
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = _lacingTableCount;
_pageCounter++;
}
private void FinalizePage()
{
if (_finalized)
{
throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!");
}
if (_lacingTableCount != 0)
{
_currentHeader[26] = _lacingTableCount;
WriteValueToByteBuffer(_granulePosition, _currentHeader, 6);
_crc.Reset();
for (int i = 0; i < _headerIndex; i++)
{
_crc.Update(_currentHeader[i]);
}
for (int j = 0; j < _payloadIndex; j++)
{
_crc.Update(_currentPayload[j]);
}
WriteValueToByteBuffer(_crc.Value, _currentHeader, 22);
_outputStream.Write(_currentHeader, 0, _headerIndex);
_outputStream.Write(_currentPayload, 0, _payloadIndex);
BeginNewPage();
}
}
private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset)
{
BinaryHelpers.Int32ToByteArrayLittleEndian(val, target, targetOffset);
return 4;
}
private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset)
{
BinaryHelpers.Int64ToByteArrayLittleEndian(val, target, targetOffset);
return 8;
}
private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset)
{
BinaryHelpers.UInt32ToByteArrayLittleEndian(val, target, targetOffset);
return 4;
}
private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset)
{
BinaryHelpers.Int16ToByteArrayLittleEndian(val, target, targetOffset);
return 2;
}
private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset)
{
if (string.IsNullOrEmpty(val))
{
return 0;
}
return Encoding.UTF8.GetBytes(val, 0, val.Length, target, targetOffset);
}
}
public class OpusRawWriteStream
{
private Stream _outputStream;
private Crc _crc;
private int _inputChannels;
private readonly bool _leaveOpen;
private int _inputSampleRate;
private byte[] _currentHeader = new byte[400];
private byte[] _currentPayload = new byte[65536];
private int _headerIndex;
private int _payloadIndex;
private int _pageCounter;
private int _logicalStreamId;
private long _granulePosition;
private byte _lacingTableCount;
private const int PAGE_FLAGS_POS = 5;
private const int GRANULE_COUNT_POS = 6;
private const int CHECKSUM_HEADER_POS = 22;
private const int SEGMENT_COUNT_POS = 26;
private bool _finalized;
public OpusRawWriteStream(Stream outputStream, OpusTags fileTags, int inputSampleRate, int inputNumChannels, bool leaveOpen = false)
{
_inputSampleRate = inputSampleRate;
_logicalStreamId = new Random().Next();
_inputChannels = inputNumChannels;
_leaveOpen = leaveOpen;
_outputStream = outputStream;
_granulePosition = 0L;
_crc = new Crc();
BeginNewPage();
WriteOpusHeadPage();
WriteOpusTagsPage(fileTags);
}
public void WriteSinglePacket(byte[] opusPacket, int offset, int packetSize)
{
if (_finalized)
{
throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
}
if (opusPacket.Length - offset < packetSize)
{
throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + packetSize + " samples, but it actually only has " + (opusPacket.Length - offset));
}
Array.Copy(opusPacket, offset, _currentPayload, _payloadIndex, packetSize);
_payloadIndex += packetSize;
int numSamplesPerFrame = OpusPacketInfo.GetNumSamplesPerFrame((ReadOnlySpan<byte>)opusPacket.AsSpan(offset), 48000);
_granulePosition += numSamplesPerFrame;
int num = packetSize;
while (num >= 255)
{
num -= 255;
_currentHeader[_headerIndex++] = byte.MaxValue;
_lacingTableCount++;
}
_currentHeader[_headerIndex++] = (byte)num;
_lacingTableCount++;
if (_lacingTableCount > 248)
{
FinalizePage();
}
}
public void Finish()
{
FinalizePage();
WriteStreamFinishedPage();
_outputStream.Flush();
if (!_leaveOpen)
{
_outputStream.Dispose();
}
_finalized = true;
}
private void WriteStreamFinishedPage()
{
_currentHeader[_headerIndex++] = 0;
_lacingTableCount++;
_currentHeader[5] = 4;
FinalizePage();
}
private void WriteOpusHeadPage()
{
if (_payloadIndex != 0)
{
throw new InvalidOperationException("Must begin writing OpusHead on a new page!");
}
_payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex);
_currentPayload[_payloadIndex++] = 1;
_currentPayload[_payloadIndex++] = (byte)_inputChannels;
short val = 0;
_payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex);
_payloadIndex += WriteValueToByteBuffer(_inputSampleRate, _currentPayload, _payloadIndex);
short val2 = 0;
_payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex);
_currentPayload[_payloadIndex++] = 0;
_currentHeader[_headerIndex++] = (byte)_payloadIndex;
_lacingTableCount++;
_currentHeader[5] = 2;
FinalizePage();
}
private void WriteOpusTagsPage(OpusTags tags = null)
{
if (tags == null)
{
tags = new OpusTags();
}
if (string.IsNullOrEmpty(tags.Comment))
{
tags.Comment = "Concentus.OggFile";
}
if (_payloadIndex != 0)
{
throw new InvalidOperationException("Must begin writing OpusTags on a new page!");
}
_payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex);
int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4);
_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
_payloadIndex += num;
int payloadIndex = _payloadIndex;
_payloadIndex += 4;
int num2 = 0;
foreach (KeyValuePair<string, string> field in tags.Fields)
{
if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value))
{
num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4);
_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
_payloadIndex += num;
num2++;
}
}
WriteValueToByteBuffer(num2, _currentPayload, payloadIndex);
int num3;
for (num3 = _payloadIndex; num3 >= 255; num3 -= 255)
{
_currentHeader[_headerIndex++] = byte.MaxValue;
_lacingTableCount++;
}
_currentHeader[_headerIndex++] = (byte)num3;
_lacingTableCount++;
FinalizePage();
}
private void BeginNewPage()
{
_headerIndex = 0;
_payloadIndex = 0;
_lacingTableCount = 0;
_headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex);
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex);
_headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex);
_headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex);
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = 0;
_currentHeader[_headerIndex++] = _lacingTableCount;
_pageCounter++;
}
private void FinalizePage()
{
if (_finalized)
{
throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!");
}
if (_lacingTableCount != 0)
{
_currentHeader[26] = _lacingTableCount;
WriteValueToByteBuffer(_granulePosition, _currentHeader, 6);
_crc.Reset();
for (int i = 0; i < _headerIndex; i++)
{
_crc.Update(_currentHeader[i]);
}
for (int j = 0; j < _payloadIndex; j++)
{
_crc.Update(_currentPayload[j]);
}
WriteValueToByteBuffer(_crc.Value, _currentHeader, 22);
_outputStream.Write(_currentHeader, 0, _headerIndex);
_outputStream.Write(_currentPayload, 0, _payloadIndex);
BeginNewPage();
}
}
private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset)
{
Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4);
return 4;
}
private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset)
{
Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 8);
return 8;
}
private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset)
{
Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4);
return 4;
}
private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset)
{
Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 2);
return 2;
}
private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset)
{
if (string.IsNullOrEmpty(val))
{
return 0;
}
byte[] bytes = Encoding.UTF8.GetBytes(val);
Array.Copy(bytes, 0, target, targetOffset, bytes.Length);
return bytes.Length;
}
}
public class OpusTagName
{
public const string Title = "title";
public const string Artist = "artist";
public const string Album = "album";
}
public class OpusTags
{
private string _comment = string.Empty;
private IDictionary<string, string> _fields = new Dictionary<string, string>();
public string Comment
{
get
{
return _comment;
}
set
{
_comment = value;
}
}
public IDictionary<string, string> Fields => _fields;
internal static OpusTags ParsePacket(byte[] packet, int packetLength)
{
if (packetLength < 8)
{
return null;
}
if (!"OpusTags".Equals(Encoding.UTF8.GetString(packet, 0, 8)))
{
return null;
}
OpusTags opusTags = new OpusTags();
int num = 8;
int num2 = BitConverter.ToInt32(packet, num);
num += 4;
if (num2 > 0)
{
opusTags._comment = Encoding.UTF8.GetString(packet, num, num2);
num += num2;
}
int num3 = BitConverter.ToInt32(packet, num);
num += 4;
for (int i = 0; i < num3; i++)
{
num2 = BitConverter.ToInt32(packet, num);
num += 4;
if (num2 > 0)
{
string @string = Encoding.UTF8.GetString(packet, num, num2);
num += num2;
int num4 = @string.IndexOf('=');
if (num4 > 0)
{
string key = @string.Substring(0, num4);
string value = @string.Substring(num4 + 1);
opusTags._fields[key] = value;
}
}
}
return opusTags;
}
}
internal class ParameterChangeEventArgs : EventArgs
{
public DataPacket FirstPacket { get; private set; }
public ParameterChangeEventArgs(DataPacket firstPacket)
{
FirstPacket = firstPacket;
}
}
internal class StreamReadBuffer : IDisposable
{
internal class StreamWrapper
{
internal Stream Source;
internal object LockObject = new object();
internal long EofOffset = long.MaxValue;
internal int RefCount = 1;
}
private class SavedBuffer
{
public byte[] Buffer;
public long BaseOffset;
public int End;
public int DiscardCount;
public long VersionSaved;
}
private static Dictionary<Stream, StreamWrapper> _lockObjects = new Dictionary<Stream, StreamWrapper>();
private StreamWrapper _wrapper;
private int _maxSize;
private byte[] _data;
private long _baseOffset;
private int _end;
private int _discardCount;
private bool _minimalRead;
private long _versionCounter;
private List<SavedBuffer> _savedBuffers;
public bool MinimalRead
{
get
{
return _minimalRead;
}
set
{
_minimalRead = value;
}
}
public int MaxSize
{
get
{
return _maxSize;
}
set
{
if (value < 1)
{
throw new ArgumentOutOfRangeException("Must be greater than zero.");
}
int num = 1 << (int)Math.Ceiling(Math.Log(value, 2.0));
if (num < _end)
{
if (num < _end - _discardCount)
{
throw new ArgumentOutOfRangeException("Must be greater than or equal to the number of bytes currently buffered.");
}
CommitDiscard();
byte[] array = new byte[num];
Buffer.BlockCopy(_data, 0, array, 0, _end);
_data = array;
}
_maxSize = num;
}
}
public long BaseOffset => _baseOffset + _discardCount;
public int BytesFilled => _end - _discardCount;
public int Length => _data.Length;
internal long BufferEndOffset
{
get
{
if (_end - _discardCount > 0)
{
return _baseOffset + _discardCount + _maxSize;
}
if (_wrapper.Source.CanSeek)
{
return _wrapper.Source.Length;
}
return _baseOffset + Length;
}
}
internal StreamReadBuffer(Stream source, int initialSize, int maxSize, bool minimalRead)
{
StreamWrapper value;
lock (_lockObjects)
{
if (!_lockObjects.TryGetValue(source, out value))
{
_lockObjects.Add(source, new StreamWrapper
{
Source = source
});
value = _lockObjects[source];
if (source.CanSeek)
{
value.EofOffset = source.Length;
}
}
else
{
value.RefCount++;
}
}
initialSize = 2 << (int)Math.Log(initialSize - 1, 2.0);
maxSize = 1 << (int)Math.Log(maxSize, 2.0);
_wrapper = value;
_data = new byte[initialSize];
_maxSize = maxSize;
_minimalRead = minimalRead;
_savedBuffers = new List<SavedBuffer>();
}
public void Dispose()
{
lock (_lockObjects)
{
if (--_wrapper.RefCount == 0)
{
_lockObjects.Remove(_wrapper.Source);
}
}
}
public int Read(long offset, byte[] buffer, int index, int count)
{
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset");
}
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
if (index < 0 || index + count > buffer.Length)
{
throw new ArgumentOutOfRangeException("index");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count");
}
if (offset >= _wrapper.EofOffset)
{
return 0;
}
int srcOffset = EnsureAvailable(offset, ref count, isRecursion: false);
Buffer.BlockCopy(_data, srcOffset, buffer, index, count);
return count;
}
internal int ReadByte(long offset)
{
if (offset < 0)
{
throw new ArgumentOutOfRangeException("offset");
}
if (offset >= _wrapper.EofOffset)
{
return -1;
}
int count = 1;
int num = EnsureAvailable(offset, ref count, isRecursion: false);
if (count == 1)
{
return _data[num];
}
return -1;
}
private int EnsureAvailable(long offset, ref int count, bool isRecursion)
{
if (offset >= _baseOffset && offset + count < _baseOffset + _end)
{
return (int)(offset - _baseOffset);
}
if (count > _maxSize)
{
throw new InvalidOperationException("Not enough room in the buffer! Increase the maximum size and try again.");
}
_versionCounter++;
if (!isRecursion)
{
for (int i = 0; i < _savedBuffers.Count; i++)
{
long num = _savedBuffers[i].BaseOffset - offset;
if ((num < 0 && _savedBuffers[i].End + num > 0) || (num > 0 && count - num > 0))
{
SwapBuffers(_savedBuffers[i]);
return EnsureAvailable(offset, ref count, isRecursion: true);
}
}
}
while (_savedBuffers.Count > 0 && _savedBuffers[0].VersionSaved + 25 < _versionCounter)
{
_savedBuffers[0].Buffer = null;
_savedBuffers.RemoveAt(0);
}
if (offset < _baseOffset && !_wrapper.Source.CanSeek)
{
throw new InvalidOperationException("Cannot seek before buffer on forward-only streams!");
}
CalcBuffer(offset, count, out var readStart, out var readEnd);
count = FillBuffer(offset, count, readStart, readEnd);
return (int)(offset - _baseOffset);
}
private void SaveBuffer()
{
_savedBuffers.Add(new SavedBuffer
{
Buffer = _data,
BaseOffset = _baseOffset,
End = _end,
DiscardCount = _discardCount,
VersionSaved = _versionCounter
});
_data = null;
_end = 0;
_discardCount = 0;
}
private void CreateNewBuffer(long offset, int count)
{
SaveBuffer();
_data = new byte[Math.Min(2 << (int)Math.Log(count - 1, 2.0), _maxSize)];
_baseOffset = offset;
}
private void SwapBuffers(SavedBuffer savedBuffer)
{
_savedBuffers.Remove(savedBuffer);
SaveBuffer();
_data = savedBuffer.Buffer;
_baseOffset = savedBuffer.BaseOffset;
_end = savedBuffer.End;
_discardCount = savedBuffer.DiscardCount;
}
private void CalcBuffer(long offset, int count, out int readStart, out int readEnd)
{
readStart = 0;
readEnd = 0;
if (offset < _baseOffset)
{
if (offset + _maxSize <= _baseOffset)
{
if (_baseOffset - (offset + _maxSize) > _maxSize)
{
CreateNewBuffer(offset, count);
}
else
{
EnsureBufferSize(count, copyContents: false, 0);
}
_baseOffset = offset;
readEnd = count;
}
else
{
readEnd = (int)(offset - _baseOffset);
EnsureBufferSize(Math.Min((int)(offset + _maxSize - _baseOffset), _end) - readEnd, copyContents: true, readEnd);
readEnd = (int)(offset - _baseOffset) - readEnd;
}
}
else if (offset >= _baseOffset + _maxSize)
{
if (offset - (_baseOffset + _maxSize) > _maxSize)
{
CreateNewBuffer(offset, count);
}
else
{
EnsureBufferSize(count, copyContents: false, 0);
}
_baseOffset = offset;
readEnd = count;
}
else
{
readEnd = (int)(offset + count - _baseOffset);
int num = Math.Max(readEnd - _maxSize, 0);
EnsureBufferSize(readEnd - num, copyContents: true, num);
readStart = _end;
readEnd = (int)(offset + count - _baseOffset);
}
}
private void EnsureBufferSize(int reqSize, bool copyContents, int copyOffset)
{
byte[] array = _data;
if (reqSize > _data.Length)
{
if (reqSize > _maxSize)
{
if (!_wrapper.Source.CanSeek && reqSize - _discardCount > _maxSize)
{
throw new InvalidOperationException("Not enough room in the buffer! Increase the maximum size and try again.");
}
int num = reqSize - _maxSize;
copyOffset += num;
reqSize = _maxSize;
}
else
{
int num2;
for (num2 = _data.Length; num2 < reqSize; num2 *= 2)
{
}
reqSize = num2;
}
if (reqSize > _data.Length)
{
array = new byte[reqSize];
}
}
if (copyContents)
{
if ((copyOffset > 0 && copyOffset < _end) || (copyOffset == 0 && array != _data))
{
Buffer.BlockCopy(_data, copyOffset, array, 0, _end - copyOffset);
if ((_discardCount -= copyOffset) < 0)
{
_discardCount = 0;
}
}
else if (copyOffset < 0 && -copyOffset < _end)
{
if (array != _data || _end <= -copyOffset)
{
Buffer.BlockCopy(_data, 0, array, -copyOffset, Math.Max(_end, Math.Min(_end, _data.Length + copyOffset)));
}
else
{
_end = copyOffset;
}
_discardCount = 0;
}
else
{
_end = copyOffset;
_discardCount = 0;
}
_baseOffset += copyOffset;
_end -= copyOffset;
if (_end > array.Length)
{
_end = array.Length;
}
}
else
{
_discardCount = 0;
_end = 0;
}
_data = array;
}
private int FillBuffer(long offset, int count, int readStart, int readEnd)
{
long readOffset = _baseOffset + readStart;
int readCount = readEnd - readStart;
lock (_wrapper.LockObject)
{
readCount = PrepareStreamForRead(readCount, readOffset);
ReadStream(readStart, readCount, readOffset);
if (_end < readStart + readCount)
{
count = Math.Max(0, (int)(_baseOffset + _end - offset));
}
else if (!_minimalRead && _end < _data.Length)
{
readCount = _data.Length - _end;
readCount = PrepareStreamForRead(readCount, _baseOffset + _end);
_end += _wrapper.Source.Read(_data, _end, readCount);
}
}
return count;
}
private int PrepareStreamForRead(int readCount, long readOffset)
{
if (readCount > 0 && _wrapper.Source.Position != readOffset)
{
if (readOffset < _wrapper.EofOffset)
{
if (_wrapper.Source.CanSeek)
{
_wrapper.Source.Position = readOffset;
}
else
{
long num = readOffset - _wrapper.Source.Position;
if (num < 0)
{
readCount = 0;
}
else
{
while (--num >= 0)
{
if (_wrapper.Source.ReadByte() == -1)
{
_wrapper.EofOffset = _wrapper.Source.Position;
readCount = 0;
break;
}
}
}
}
}
else
{
readCount = 0;
}
}
return readCount;
}
private void ReadStream(int readStart, int readCount, long readOffset)
{
while (readCount > 0 && readOffset < _wrapper.EofOffset)
{
int num = _wrapper.Source.Read(_data, readStart, readCount);
if (num == 0)
{
break;
}
readStart += num;
readOffset += num;
readCount -= num;
}
if (readStart > _end)
{
_end = readStart;
}
}
public void DiscardThrough(long offset)
{
int val = (int)(offset - _baseOffset);
_discardCount = Math.Max(val, _discardCount);
if (_discardCount >= _data.Length)
{
CommitDiscard();
}
}
private void CommitDiscard()
{
if (_discardCount >= _data.Length || _discardCount >= _end)
{
_baseOffset += _discardCount;
_end = 0;
}
else
{
Buffer.BlockCopy(_data, _discardCount, _data, 0, _end - _discardCount);
_baseOffset += _discardCount;
_end -= _discardCount;
}
_discardCount = 0;
}
}