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.5 kB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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 ConcurrentQueue<RTPFrame> _frames;
  12. private readonly SemaphoreSlim _signal;
  13. private bool _hasHeader;
  14. private bool _isDisposed;
  15. private bool _nextMissed;
  16. private ushort _nextSeq;
  17. private uint _nextTimestamp;
  18. public InputStream()
  19. {
  20. _frames = new ConcurrentQueue<RTPFrame>();
  21. _signal = new SemaphoreSlim(0, MaxFrames);
  22. }
  23. public override bool CanRead => !_isDisposed;
  24. public override bool CanSeek => false;
  25. public override bool CanWrite => false;
  26. public override int AvailableFrames => _signal.CurrentCount;
  27. public override bool TryReadFrame(CancellationToken cancelToken, out RTPFrame frame)
  28. {
  29. cancelToken.ThrowIfCancellationRequested();
  30. if (_signal.Wait(0))
  31. {
  32. _frames.TryDequeue(out frame);
  33. return true;
  34. }
  35. frame = default(RTPFrame);
  36. return false;
  37. }
  38. public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken)
  39. {
  40. cancelToken.ThrowIfCancellationRequested();
  41. var frame = await ReadFrameAsync(cancelToken).ConfigureAwait(false);
  42. if (count < frame.Payload.Length)
  43. throw new InvalidOperationException("Buffer is too small.");
  44. Buffer.BlockCopy(frame.Payload, 0, buffer, offset, frame.Payload.Length);
  45. return frame.Payload.Length;
  46. }
  47. public override async Task<RTPFrame> ReadFrameAsync(CancellationToken cancelToken)
  48. {
  49. cancelToken.ThrowIfCancellationRequested();
  50. await _signal.WaitAsync(cancelToken).ConfigureAwait(false);
  51. _frames.TryDequeue(out var frame);
  52. return frame;
  53. }
  54. public override void WriteHeader(ushort seq, uint timestamp, bool missed)
  55. {
  56. if (_hasHeader)
  57. throw new InvalidOperationException("Header received with no payload");
  58. _hasHeader = true;
  59. _nextSeq = seq;
  60. _nextTimestamp = timestamp;
  61. _nextMissed = missed;
  62. }
  63. public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken)
  64. {
  65. cancelToken.ThrowIfCancellationRequested();
  66. if (_signal.CurrentCount >= MaxFrames) //1-2 seconds
  67. {
  68. _hasHeader = false;
  69. return Task.Delay(0); //Buffer overloaded
  70. }
  71. if (!_hasHeader)
  72. throw new InvalidOperationException("Received payload without an RTP header");
  73. _hasHeader = false;
  74. var payload = new byte[count];
  75. Buffer.BlockCopy(buffer, offset, payload, 0, count);
  76. _frames.Enqueue(new RTPFrame(
  77. _nextSeq,
  78. _nextTimestamp,
  79. missed: _nextMissed,
  80. payload: payload
  81. ));
  82. _signal.Release();
  83. return Task.Delay(0);
  84. }
  85. protected override void Dispose(bool isDisposing) => _isDisposed = true;
  86. }
  87. }