You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

InputStream.cs 3.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. namespace Discord.Audio.Streams
  6. {
  7. ///<summary> Reads the payload from an RTP frame </summary>
  8. public class InputStream : AudioInStream
  9. {
  10. private const int MaxFrames = 100; //1-2 Seconds
  11. private readonly AudioClient _client;
  12. private ConcurrentQueue<RTPFrame> _frames;
  13. private SemaphoreSlim _signal;
  14. private ushort _nextSeq;
  15. private uint _nextTimestamp;
  16. private bool _nextMissed;
  17. private bool _hasHeader;
  18. private bool _isDisposed;
  19. public override bool CanRead => !_isDisposed;
  20. public override bool CanSeek => false;
  21. public override bool CanWrite => false;
  22. public override int AvailableFrames => _signal.CurrentCount;
  23. public InputStream(IAudioClient client)
  24. {
  25. _client = (AudioClient)client;
  26. _frames = new ConcurrentQueue<RTPFrame>();
  27. _signal = new SemaphoreSlim(0, MaxFrames);
  28. }
  29. public override bool TryReadFrame(CancellationToken cancelToken, out RTPFrame frame)
  30. {
  31. cancelToken.ThrowIfCancellationRequested();
  32. if (_signal.Wait(0))
  33. {
  34. _frames.TryDequeue(out frame);
  35. return true;
  36. }
  37. frame = default(RTPFrame);
  38. return false;
  39. }
  40. public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken)
  41. {
  42. cancelToken.ThrowIfCancellationRequested();
  43. var frame = await ReadFrameAsync(cancelToken).ConfigureAwait(false);
  44. if (count < frame.Payload.Length)
  45. throw new InvalidOperationException("Buffer is too small.");
  46. Buffer.BlockCopy(frame.Payload, 0, buffer, offset, frame.Payload.Length);
  47. return frame.Payload.Length;
  48. }
  49. public override async Task<RTPFrame> ReadFrameAsync(CancellationToken cancelToken)
  50. {
  51. cancelToken.ThrowIfCancellationRequested();
  52. await _signal.WaitAsync(cancelToken).ConfigureAwait(false);
  53. _frames.TryDequeue(out RTPFrame frame);
  54. return frame;
  55. }
  56. public override void WriteHeader(ushort seq, uint timestamp, bool missed)
  57. {
  58. if (_hasHeader)
  59. throw new InvalidOperationException("Header received with no payload");
  60. _hasHeader = true;
  61. _nextSeq = seq;
  62. _nextTimestamp = timestamp;
  63. _nextMissed = missed;
  64. }
  65. public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken)
  66. => WriteAsync(buffer, offset, count, cancelToken, false);
  67. internal Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken, bool claimBuffer)
  68. {
  69. cancelToken.ThrowIfCancellationRequested();
  70. if (!_hasHeader)
  71. throw new InvalidOperationException("Received payload without an RTP header");
  72. _hasHeader = false;
  73. if (_signal.CurrentCount >= MaxFrames) //1-2 seconds
  74. return Task.Delay(0); //Buffer overloaded
  75. _frames.Enqueue(new RTPFrame(
  76. sequence: _nextSeq,
  77. timestamp: _nextTimestamp,
  78. missed: _nextMissed,
  79. payload: CopyBuffer(buffer, offset, count)
  80. ));
  81. _signal.Release();
  82. return Task.Delay(0);
  83. }
  84. protected byte[] CopyBuffer(byte[] buffer, int offset, int count)
  85. {
  86. byte[] payload = _client.GetFrameBuffer();
  87. Buffer.BlockCopy(buffer, offset, payload, 0, count);
  88. return payload;
  89. }
  90. protected override void Dispose(bool isDisposing)
  91. {
  92. _isDisposed = true;
  93. }
  94. }
  95. }