TestStreamSource.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Windows.Media;
  10. using MatrixIO.IO.MpegTs;
  11. using MatrixIO.IO.MpegTs.Streams;
  12. namespace SLTest
  13. {
  14. public enum NalUnitTypeCode
  15. {
  16. Unspecified = 0,
  17. SliceLayerWithoutPartitioningNonIDR = 1,
  18. SliceDataPartitionLayerA = 2,
  19. SliceDataPartitionLayerB = 3,
  20. SliceDataPartitionLayerC = 4,
  21. SliceLayerWithoutPartitioningIDR = 5,
  22. SupplementalEnahancementInformation = 6,
  23. SequanceParameterSet = 7,
  24. PictureParameterSet = 8,
  25. AccessUnitDelimiter = 9,
  26. EndOfSequence = 10,
  27. EndOfStream = 11,
  28. FillerData = 12,
  29. // 13..23 Reserved
  30. // 24..31 Unspecified
  31. }
  32. public class TestStreamSource : MediaStreamSource
  33. {
  34. private readonly object _syncObject = new object();
  35. private readonly TsStream _stream;
  36. private readonly Queue<TsStreamEventArgs> _sampleQueue = new Queue<TsStreamEventArgs>();
  37. private readonly AutoResetEvent _sampleArrivedEvent = new AutoResetEvent(false);
  38. private readonly MediaStreamDescription _streamDescription;
  39. private Dictionary<MediaSourceAttributesKeys, string> _mediaSourceAttributes;
  40. private readonly Dictionary<MediaStreamAttributeKeys, string> _mediaStreamAttributes;
  41. private Dictionary<MediaSampleAttributeKeys, string> _mediaSampleAttributes;
  42. public TestStreamSource(TsStream stream)
  43. {
  44. _stream = stream;
  45. _mediaSourceAttributes = new Dictionary<MediaSourceAttributesKeys, string>();
  46. _mediaStreamAttributes = new Dictionary<MediaStreamAttributeKeys, string>();
  47. _mediaSampleAttributes = new Dictionary<MediaSampleAttributeKeys, string>();
  48. MediaStreamType type;
  49. switch(_stream.Type)
  50. {
  51. case StreamType.MPEG4_AVC:
  52. type = MediaStreamType.Video;
  53. _mediaStreamAttributes.Add(MediaStreamAttributeKeys.VideoFourCC, "H264");
  54. _mediaStreamAttributes.Add(MediaStreamAttributeKeys.Width, "1280");
  55. _mediaStreamAttributes.Add(MediaStreamAttributeKeys.Height, "720");
  56. break;
  57. case StreamType.MPEG4_AAC:
  58. type = MediaStreamType.Audio;
  59. break;
  60. default:
  61. return;
  62. }
  63. _streamDescription = new MediaStreamDescription(type, _mediaStreamAttributes);
  64. }
  65. private byte[] _sps;
  66. private byte[] _pps;
  67. void UnitReceived(object sender, TsStreamEventArgs e)
  68. {
  69. _sampleQueue.Enqueue(e);
  70. if (!(e is TsStreamEventArgs<PesPacket>) || (_sps != null && _pps != null)) return;
  71. var pesPacket = ((TsStreamEventArgs<PesPacket>) e).DecodedUnit;
  72. if (pesPacket.Data == null) return;
  73. var position = 0;
  74. int nalUnitStart = 0;
  75. var nalUnitType = NalUnitTypeCode.Unspecified;
  76. var foundNalUnit = false;
  77. while (position + 3 < pesPacket.Data.Length && (_sps == null || _pps == null))
  78. {
  79. if (pesPacket.Data[position] == 0x00 && pesPacket.Data[position + 1] == 0x00 &&
  80. pesPacket.Data[position + 2] == 0x00 && pesPacket.Data[position + 3] == 0x01)
  81. {
  82. var type = (NalUnitTypeCode)(pesPacket.Data[position + 4] & 0x1F);
  83. Debug.WriteLine("Found " + type + " NAL Unit");
  84. if (foundNalUnit)
  85. {
  86. var nalUnit = new byte[position - (nalUnitStart + 4)];
  87. Buffer.BlockCopy(pesPacket.Data, nalUnitStart + 4, nalUnit, 0, nalUnit.Length);
  88. switch(nalUnitType)
  89. {
  90. case NalUnitTypeCode.PictureParameterSet:
  91. _pps = nalUnit;
  92. break;
  93. case NalUnitTypeCode.SequanceParameterSet:
  94. _sps = nalUnit;
  95. break;
  96. }
  97. }
  98. if (type == NalUnitTypeCode.SequanceParameterSet || type == NalUnitTypeCode.PictureParameterSet)
  99. {
  100. foundNalUnit = true;
  101. nalUnitStart = position;
  102. nalUnitType = type;
  103. }
  104. position += 4;
  105. }
  106. else position++;
  107. }
  108. if (_sps != null && _pps != null)
  109. {
  110. StringBuilder sb = new StringBuilder("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ");
  111. foreach (var b in _sps) sb.Append(b.ToString("X2"));
  112. foreach (var b in _pps) sb.Append(b.ToString("X2"));
  113. Debug.WriteLine(sb.ToString());
  114. var codecPrivateData = Convert.ToBase64String(_sps) + "," + Convert.ToBase64String(_pps);
  115. _streamDescription.MediaAttributes.Add(MediaStreamAttributeKeys.CodecPrivateData, codecPrivateData);
  116. ReportOpenMediaCompleted(_mediaSourceAttributes, new[] {_streamDescription});
  117. }
  118. }
  119. protected override void CloseMedia()
  120. {
  121. _stream.UnitReceived -= UnitReceived;
  122. lock (_syncObject) _sampleQueue.Clear();
  123. _sampleArrivedEvent.Set();
  124. }
  125. protected override void GetDiagnosticAsync(MediaStreamSourceDiagnosticKind diagnosticKind)
  126. {
  127. throw new NotImplementedException();
  128. }
  129. private TsStreamEventArgs _sample;
  130. private List<ArraySegment<byte>> _sampleUnits;
  131. private int _currentSampleUnit;
  132. private MemoryStream _currentSampleStream;
  133. private List<ArraySegment<byte>> IndexNetworkAbstractionLayerUnits(TsStreamEventArgs<PesPacket> pesPacketArgs)
  134. {
  135. var nalUnits = new List<ArraySegment<byte>>();
  136. var pesPacket = pesPacketArgs.DecodedUnit;
  137. var position = 0;
  138. var nalUnitStart = 0;
  139. var havePreviousNalUnit = false;
  140. Debug.WriteLine("Parsing NAL Units:");
  141. while (position + 3 < pesPacket.Data.Length)
  142. {
  143. if (pesPacket.Data[position] == 0x00 && pesPacket.Data[position + 1] == 0x00 &&
  144. pesPacket.Data[position + 2] == 0x00 && pesPacket.Data[position + 3] == 0x01)
  145. {
  146. if (havePreviousNalUnit)
  147. {
  148. nalUnits.Add(new ArraySegment<byte>(pesPacket.Data, nalUnitStart, position - nalUnitStart));
  149. Debug.WriteLine("\tStart: " + nalUnitStart + " Length: " + (position - nalUnitStart));
  150. }
  151. else havePreviousNalUnit = true;
  152. // NOTE: Not sure if this should be before or after. Neither works at present. Assuming after for now because the extradata has the start codes stripped out.
  153. nalUnitStart = position + 1;
  154. position += 4;
  155. }
  156. else position++;
  157. }
  158. if (havePreviousNalUnit)
  159. {
  160. nalUnits.Add(new ArraySegment<byte>(pesPacket.Data, nalUnitStart, position - nalUnitStart));
  161. Debug.WriteLine("\tStart: " + nalUnitStart + " Length: " + (position - nalUnitStart));
  162. }
  163. return nalUnits;
  164. }
  165. protected override void GetSampleAsync(MediaStreamType mediaStreamType)
  166. {
  167. switch(mediaStreamType)
  168. {
  169. case MediaStreamType.Audio:
  170. break;
  171. case MediaStreamType.Video:
  172. GetVideoSampleAsync();
  173. break;
  174. case MediaStreamType.Script:
  175. break;
  176. }
  177. }
  178. private bool _parametersSent;
  179. private void GetVideoSampleAsync()
  180. {
  181. MemoryStream stream;
  182. int streamOffset;
  183. int streamLength;
  184. bool isKeyFrame;
  185. long pts;
  186. if (!_parametersSent)
  187. {
  188. stream = new MemoryStream();
  189. stream.Write(new byte[] { 0x00, 0x00, 0x01 }, 0, 3);
  190. stream.Write(_sps, 0, _sps.Length);
  191. stream.Write(new byte[] { 0x00, 0x00, 0x01 }, 0, 3);
  192. stream.Write(_pps, 0, _pps.Length);
  193. stream.Seek(0, SeekOrigin.Begin);
  194. streamOffset = 0;
  195. streamLength = stream.Length;
  196. _parametersSent = true;
  197. }
  198. else
  199. {
  200. while (_sample == null)
  201. {
  202. lock (_syncObject)
  203. {
  204. if (_sampleQueue.Count > 0)
  205. {
  206. _sample = _sampleQueue.Dequeue();
  207. var tsStreamEventArgs = _sample as TsStreamEventArgs<PesPacket>;
  208. if (tsStreamEventArgs != null)
  209. {
  210. _sampleUnits = IndexNetworkAbstractionLayerUnits(tsStreamEventArgs);
  211. _currentSampleUnit = 0;
  212. }
  213. }
  214. }
  215. if (_sample == null) _sampleArrivedEvent.WaitOne();
  216. }
  217. if (_sample is TsStreamEventArgs<byte[]>)
  218. {
  219. stream = new MemoryStream(((TsStreamEventArgs<byte[]>) _sample).DecodedUnit);
  220. streamOffset = 0;
  221. streamLength = (int) stream.Length;
  222. isKeyFrame = true;
  223. pts = 0;
  224. }
  225. else if (_sample is TsStreamEventArgs<PesPacket>)
  226. {
  227. var pesPacket = ((TsStreamEventArgs<PesPacket>) _sample).DecodedUnit;
  228. if (_currentSampleUnit == 0)
  229. _currentSampleStream = new MemoryStream(pesPacket.Data);
  230. stream = _currentSampleStream;
  231. var sampleUnit = _sampleUnits[_currentSampleUnit];
  232. streamOffset = sampleUnit.Offset;
  233. streamLength = sampleUnit.Count;
  234. pts = pesPacket.Header.PTS.HasValue ? pesPacket.Header.PTS.Value.Ticks : 0;
  235. var type = (NalUnitTypeCode) (sampleUnit.Array[sampleUnit.Offset + 4] & 0x1F);
  236. isKeyFrame = type == NalUnitTypeCode.SliceLayerWithoutPartitioningIDR;
  237. _currentSampleUnit++;
  238. if (_currentSampleUnit >= _sampleUnits.Count)
  239. {
  240. _sample = null;
  241. _currentSampleUnit = 0;
  242. }
  243. }
  244. else return;
  245. }
  246. /*
  247. var mediaSampleAttributes = new Dictionary<MediaSampleAttributeKeys, string>()
  248. {
  249. {MediaSampleAttributeKeys.KeyFrameFlag, isKeyFrame ? "1" : "0"},
  250. {MediaSampleAttributeKeys.FrameWidth, "1280"},
  251. {MediaSampleAttributeKeys.FrameHeight, "720"},
  252. };
  253. */
  254. #if DEBUG2
  255. StringBuilder sb = new StringBuilder("@@@ ");
  256. stream.Seek(streamOffset, SeekOrigin.Begin);
  257. for (int i = 0; i < streamLength; i++)
  258. {
  259. byte b = (byte)stream.ReadByte();
  260. sb.Append(b.ToString("X2"));
  261. }
  262. Debug.WriteLine(sb.ToString());
  263. stream.Seek(streamOffset, SeekOrigin.Begin);
  264. #endif
  265. ReportGetSampleCompleted(new MediaStreamSample(_streamDescription, stream, streamOffset,
  266. streamLength, pts, _mediaSampleAttributes));
  267. }
  268. protected override void OpenMediaAsync()
  269. {
  270. _stream.UnitReceived += UnitReceived;
  271. _mediaSourceAttributes.Clear();
  272. _mediaSourceAttributes.Add(MediaSourceAttributesKeys.CanSeek, "False");
  273. _mediaSourceAttributes.Add(MediaSourceAttributesKeys.Duration, "0");
  274. }
  275. protected override void SeekAsync(long seekToTime)
  276. {
  277. ReportSeekCompleted(seekToTime);
  278. }
  279. protected override void SwitchMediaStreamAsync(MediaStreamDescription mediaStreamDescription)
  280. {
  281. throw new NotImplementedException();
  282. }
  283. }
  284. }