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 4.0 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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. _client.RecycleFrame(frame);
  48. return frame.Payload.Length;
  49. }
  50. public override async Task<RTPFrame> ReadFrameAsync(CancellationToken cancelToken)
  51. {
  52. cancelToken.ThrowIfCancellationRequested();
  53. await _signal.WaitAsync(cancelToken).ConfigureAwait(false);
  54. _frames.TryDequeue(out RTPFrame frame);
  55. return frame;
  56. }
  57. public override void WriteHeader(ushort seq, uint timestamp, bool missed)
  58. {
  59. if (_hasHeader)
  60. throw new InvalidOperationException("Header received with no payload");
  61. _hasHeader = true;
  62. _nextSeq = seq;
  63. _nextTimestamp = timestamp;
  64. _nextMissed = missed;
  65. }
  66. public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken)
  67. => WriteAsync(buffer, offset, count, cancelToken, false);
  68. internal Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken, bool claimBuffer)
  69. {
  70. cancelToken.ThrowIfCancellationRequested();
  71. if (!_hasHeader)
  72. throw new InvalidOperationException("Received payload without an RTP header");
  73. _hasHeader = false;
  74. if (_signal.CurrentCount >= MaxFrames) //1-2 seconds
  75. return Task.Delay(0); //Buffer overloaded
  76. _frames.Enqueue(new RTPFrame(
  77. sequence: _nextSeq,
  78. timestamp: _nextTimestamp,
  79. missed: _nextMissed,
  80. payload: CopyBuffer(buffer, offset, count)
  81. ));
  82. _signal.Release();
  83. return Task.Delay(0);
  84. }
  85. protected byte[] CopyBuffer(byte[] buffer, int offset, int count)
  86. {
  87. byte[] payload = _client.GetFrameBuffer();
  88. Buffer.BlockCopy(buffer, offset, payload, 0, count);
  89. return payload;
  90. }
  91. protected override void Dispose(bool isDisposing)
  92. {
  93. _isDisposed = true;
  94. }
  95. }
  96. }