PeerStatsView.cs 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor.UIElements;
  5. using UnityEngine.UIElements;
  6. namespace Unity.WebRTC.Editor
  7. {
  8. internal class PeerStatsView
  9. {
  10. private readonly WebRTCStats m_parent;
  11. private readonly RTCPeerConnection m_peerConnection;
  12. private ICollection<string> m_lastUpdateKeys;
  13. public PeerStatsView(RTCPeerConnection peer, WebRTCStats parent)
  14. {
  15. m_peerConnection = peer;
  16. m_parent = parent;
  17. }
  18. public VisualElement Create()
  19. {
  20. var root = new ScrollView();
  21. m_parent.OnStats += (peer, report) =>
  22. {
  23. if (peer != m_peerConnection || report.Stats.Keys.Count == m_lastUpdateKeys?.Count)
  24. {
  25. return;
  26. }
  27. m_lastUpdateKeys = report.Stats.Keys;
  28. root.Clear();
  29. var container = new VisualElement();
  30. var popup = new PopupField<string>(m_lastUpdateKeys.ToList(), 0, id => $"{id}", id => $"{id}");
  31. root.Add(popup);
  32. root.Add(container);
  33. popup.RegisterValueChangedCallback(e =>
  34. {
  35. container.Clear();
  36. var id = e.newValue;
  37. var type = report.Get(id).Type;
  38. switch (type)
  39. {
  40. case RTCStatsType.Codec:
  41. container.Add(CreateCodecView(id));
  42. break;
  43. case RTCStatsType.InboundRtp:
  44. container.Add(CreateInboundRtpView(id));
  45. break;
  46. case RTCStatsType.OutboundRtp:
  47. container.Add(CreateOutboundRtpView(id));
  48. break;
  49. case RTCStatsType.RemoteInboundRtp:
  50. container.Add(CreateRemoteInboundRtpView(id));
  51. break;
  52. case RTCStatsType.RemoteOutboundRtp:
  53. container.Add(CreateRemoteOutboundRtpView(id));
  54. break;
  55. case RTCStatsType.MediaSource:
  56. container.Add(CreateMediaSourceView(id));
  57. break;
  58. case RTCStatsType.Csrc:
  59. container.Add(CreateCsrcView(id));
  60. break;
  61. case RTCStatsType.PeerConnection:
  62. container.Add(CreatePeerConnectionView(id));
  63. break;
  64. case RTCStatsType.DataChannel:
  65. container.Add(CreateDataChannelView(id));
  66. break;
  67. case RTCStatsType.Stream:
  68. container.Add(CreateStreamView(id));
  69. break;
  70. case RTCStatsType.Track:
  71. container.Add(CreateTrackView(id));
  72. break;
  73. case RTCStatsType.Transceiver:
  74. container.Add(CreateTransceiverView(id));
  75. break;
  76. case RTCStatsType.Sender:
  77. container.Add(CreateSenderView(id));
  78. break;
  79. case RTCStatsType.Receiver:
  80. container.Add(CreateReceiverView(id));
  81. break;
  82. case RTCStatsType.Transport:
  83. container.Add(CreateTransportView(id));
  84. break;
  85. case RTCStatsType.SctpTransport:
  86. container.Add(CreateSctpTransportView(id));
  87. break;
  88. case RTCStatsType.CandidatePair:
  89. container.Add(CreateCandidatePairView(id));
  90. break;
  91. case RTCStatsType.LocalCandidate:
  92. container.Add(CreateLocalCandidateView(id));
  93. break;
  94. case RTCStatsType.RemoteCandidate:
  95. container.Add(CreateRemoteCandidateView(id));
  96. break;
  97. case RTCStatsType.Certificate:
  98. container.Add(CreateCertificateView(id));
  99. break;
  100. case RTCStatsType.IceServer:
  101. container.Add(CreateIceServerView(id));
  102. break;
  103. default:
  104. throw new ArgumentOutOfRangeException($"this type is not supported : {type}");
  105. }
  106. });
  107. };
  108. return root;
  109. }
  110. private VisualElement CreateCodecView(string id)
  111. {
  112. var root = new VisualElement();
  113. var container = new VisualElement();
  114. root.Add(container);
  115. m_parent.OnStats += (peer, report) =>
  116. {
  117. if (peer != m_peerConnection)
  118. {
  119. return;
  120. }
  121. container.Clear();
  122. if (!report.TryGetValue(id, out var stats) ||
  123. !(stats is RTCCodecStats codecStats))
  124. {
  125. container.Add(new Label($"no stats report about {RTCStatsType.Codec}"));
  126. return;
  127. }
  128. container.Add(new Label($"{nameof(codecStats.Id)}: {codecStats.Id}"));
  129. container.Add(new Label($"{nameof(codecStats.Timestamp)}: {codecStats.Timestamp}"));
  130. container.Add(new Label($"{nameof(codecStats.payloadType)}: {codecStats.payloadType}"));
  131. container.Add(new Label($"{nameof(codecStats.mimeType)}: {codecStats.mimeType}"));
  132. container.Add(new Label($"{nameof(codecStats.clockRate)}: {codecStats.clockRate}"));
  133. container.Add(new Label($"{nameof(codecStats.channels)}: {codecStats.channels}"));
  134. container.Add(new Label($"{nameof(codecStats.sdpFmtpLine)}: {codecStats.sdpFmtpLine}"));
  135. };
  136. return root;
  137. }
  138. private VisualElement CreateInboundRtpView(string id)
  139. {
  140. var root = new VisualElement();
  141. var container = new VisualElement();
  142. root.Add(container);
  143. var inboundGraph = new InboundRTPStreamGraphView();
  144. m_parent.OnStats += (peer, report) =>
  145. {
  146. if (peer != m_peerConnection)
  147. {
  148. return;
  149. }
  150. container.Clear();
  151. if (!report.TryGetValue(id, out var stats) ||
  152. !(stats is RTCInboundRTPStreamStats inboundStats))
  153. {
  154. container.Add(new Label($"no stats report about {RTCStatsType.InboundRtp}"));
  155. return;
  156. }
  157. container.Add(new Label($"{nameof(inboundStats.Id)}: {inboundStats.Id}"));
  158. container.Add(new Label($"{nameof(inboundStats.Timestamp)}: {inboundStats.Timestamp}"));
  159. container.Add(new Label($"{nameof(inboundStats.ssrc)}: {inboundStats.ssrc}"));
  160. container.Add(new Label($"{nameof(inboundStats.estimatedPlayoutTimestamp)}: {inboundStats.estimatedPlayoutTimestamp}"));
  161. container.Add(new Label($"{nameof(inboundStats.kind)}: {inboundStats.kind}"));
  162. container.Add(new Label($"{nameof(inboundStats.transportId)}: {inboundStats.transportId}"));
  163. container.Add(new Label($"{nameof(inboundStats.codecId)}: {inboundStats.codecId}"));
  164. container.Add(new Label($"{nameof(inboundStats.firCount)}: {inboundStats.firCount}"));
  165. container.Add(new Label($"{nameof(inboundStats.pliCount)}: {inboundStats.pliCount}"));
  166. container.Add(new Label($"{nameof(inboundStats.nackCount)}: {inboundStats.nackCount}"));
  167. container.Add(new Label($"{nameof(inboundStats.qpSum)}: {inboundStats.qpSum}"));
  168. container.Add(new Label($"{nameof(inboundStats.packetsReceived)}: {inboundStats.packetsReceived}"));
  169. container.Add(new Label($"{nameof(inboundStats.bytesReceived)}: {inboundStats.bytesReceived}"));
  170. container.Add(
  171. new Label($"{nameof(inboundStats.headerBytesReceived)}: {inboundStats.headerBytesReceived}"));
  172. container.Add(new Label($"{nameof(inboundStats.packetsLost)}: {inboundStats.packetsLost}"));
  173. container.Add(new Label(
  174. $"{nameof(inboundStats.lastPacketReceivedTimestamp)}: {inboundStats.lastPacketReceivedTimestamp}"));
  175. container.Add(new Label($"{nameof(inboundStats.jitter)}: {inboundStats.jitter}"));
  176. container.Add(new Label($"{nameof(inboundStats.roundTripTime)}: {inboundStats.roundTripTime}"));
  177. container.Add(new Label($"{nameof(inboundStats.packetsDiscarded)}: {inboundStats.packetsDiscarded}"));
  178. container.Add(new Label($"{nameof(inboundStats.packetsRepaired)}: {inboundStats.packetsRepaired}"));
  179. container.Add(new Label($"{nameof(inboundStats.burstPacketsLost)}: {inboundStats.burstPacketsLost}"));
  180. container.Add(
  181. new Label($"{nameof(inboundStats.burstPacketsDiscarded)}: {inboundStats.burstPacketsDiscarded}"));
  182. container.Add(new Label($"{nameof(inboundStats.burstLossCount)}: {inboundStats.burstLossCount}"));
  183. container.Add(new Label($"{nameof(inboundStats.burstDiscardCount)}: {inboundStats.burstDiscardCount}"));
  184. container.Add(new Label($"{nameof(inboundStats.burstLossRate)}: {inboundStats.burstLossRate}"));
  185. container.Add(new Label($"{nameof(inboundStats.burstDiscardRate)}: {inboundStats.burstDiscardRate}"));
  186. container.Add(new Label($"{nameof(inboundStats.gapLossRate)}: {inboundStats.gapLossRate}"));
  187. container.Add(new Label($"{nameof(inboundStats.gapDiscardRate)}: {inboundStats.gapDiscardRate}"));
  188. container.Add(new Label($"{nameof(inboundStats.framesDecoded)}: {inboundStats.framesDecoded}"));
  189. container.Add(new Label($"{nameof(inboundStats.keyFramesDecoded)}: {inboundStats.keyFramesDecoded}"));
  190. container.Add(new Label($"{nameof(inboundStats.totalDecodeTime)}: {inboundStats.totalDecodeTime}"));
  191. container.Add(new Label($"{nameof(inboundStats.contentType)}: {inboundStats.contentType}"));
  192. container.Add(
  193. new Label($"{nameof(inboundStats.decoderImplementation)}: {inboundStats.decoderImplementation}"));
  194. inboundGraph.AddInput(inboundStats);
  195. };
  196. root.Add(inboundGraph.Create());
  197. return root;
  198. }
  199. private VisualElement CreateOutboundRtpView(string id)
  200. {
  201. var root = new VisualElement();
  202. var container = new VisualElement();
  203. root.Add(container);
  204. var outboundGraph = new OutboundRTPStreamGraphView();
  205. m_parent.OnStats += (peer, report) =>
  206. {
  207. if (peer != m_peerConnection)
  208. {
  209. return;
  210. }
  211. container.Clear();
  212. if (!report.TryGetValue(id, out var stats) ||
  213. !(stats is RTCOutboundRTPStreamStats outboundStats))
  214. {
  215. container.Add(new Label($"no stats report about {RTCStatsType.OutboundRtp}"));
  216. return;
  217. }
  218. container.Add(new Label($"{nameof(outboundStats.Id)}: {outboundStats.Id}"));
  219. container.Add(new Label($"{nameof(outboundStats.Timestamp)}: {outboundStats.Timestamp}"));
  220. container.Add(new Label($"{nameof(outboundStats.ssrc)}: {outboundStats.ssrc}"));
  221. container.Add(new Label($"{nameof(outboundStats.kind)}: {outboundStats.kind}"));
  222. container.Add(new Label($"{nameof(outboundStats.transportId)}: {outboundStats.transportId}"));
  223. container.Add(new Label($"{nameof(outboundStats.codecId)}: {outboundStats.codecId}"));
  224. container.Add(new Label($"{nameof(outboundStats.firCount)}: {outboundStats.firCount}"));
  225. container.Add(new Label($"{nameof(outboundStats.pliCount)}: {outboundStats.pliCount}"));
  226. container.Add(new Label($"{nameof(outboundStats.nackCount)}: {outboundStats.nackCount}"));
  227. container.Add(new Label($"{nameof(outboundStats.qpSum)}: {outboundStats.qpSum}"));
  228. container.Add(new Label($"{nameof(outboundStats.mediaSourceId)}: {outboundStats.mediaSourceId}"));
  229. container.Add(new Label($"{nameof(outboundStats.packetsSent)}: {outboundStats.packetsSent}"));
  230. container.Add(new Label(
  231. $"{nameof(outboundStats.retransmittedPacketsSent)}: {outboundStats.retransmittedPacketsSent}"));
  232. container.Add(new Label($"{nameof(outboundStats.bytesSent)}: {outboundStats.bytesSent}"));
  233. container.Add(new Label($"{nameof(outboundStats.headerBytesSent)}: {outboundStats.headerBytesSent}"));
  234. container.Add(new Label(
  235. $"{nameof(outboundStats.retransmittedBytesSent)}: {outboundStats.retransmittedBytesSent}"));
  236. container.Add(new Label($"{nameof(outboundStats.targetBitrate)}: {outboundStats.targetBitrate}"));
  237. container.Add(new Label($"{nameof(outboundStats.framesEncoded)}: {outboundStats.framesEncoded}"));
  238. container.Add(new Label($"{nameof(outboundStats.keyFramesEncoded)}: {outboundStats.keyFramesEncoded}"));
  239. container.Add(new Label($"{nameof(outboundStats.totalEncodeTime)}: {outboundStats.totalEncodeTime}"));
  240. container.Add(new Label(
  241. $"{nameof(outboundStats.totalEncodedBytesTarget)}: {outboundStats.totalEncodedBytesTarget}"));
  242. container.Add(
  243. new Label($"{nameof(outboundStats.totalPacketSendDelay)}: {outboundStats.totalPacketSendDelay}"));
  244. container.Add(new Label(
  245. $"{nameof(outboundStats.qualityLimitationReason)}: {outboundStats.qualityLimitationReason}"));
  246. container.Add(new Label(
  247. $"{nameof(outboundStats.qualityLimitationResolutionChanges)}: {outboundStats.qualityLimitationResolutionChanges}"));
  248. container.Add(new Label($"{nameof(outboundStats.contentType)}: {outboundStats.contentType}"));
  249. container.Add(new Label(
  250. $"{nameof(outboundStats.encoderImplementation)}: {outboundStats.encoderImplementation}"));
  251. outboundGraph.AddInput(outboundStats);
  252. };
  253. root.Add(outboundGraph.Create());
  254. return root;
  255. }
  256. private VisualElement CreateRemoteInboundRtpView(string id)
  257. {
  258. var root = new VisualElement();
  259. var container = new VisualElement();
  260. root.Add(container);
  261. m_parent.OnStats += (peer, report) =>
  262. {
  263. if (peer != m_peerConnection)
  264. {
  265. return;
  266. }
  267. container.Clear();
  268. if (!report.TryGetValue(id, out var stats) ||
  269. !(stats is RTCRemoteInboundRtpStreamStats remoteInboundStats))
  270. {
  271. container.Add(new Label($"no stats report about {RTCStatsType.RemoteInboundRtp}"));
  272. return;
  273. }
  274. container.Add(new Label($"{nameof(remoteInboundStats.Id)}: {remoteInboundStats.Id}"));
  275. container.Add(new Label($"{nameof(remoteInboundStats.Timestamp)}: {remoteInboundStats.Timestamp}"));
  276. };
  277. return root;
  278. }
  279. private VisualElement CreateRemoteOutboundRtpView(string id)
  280. {
  281. var root = new VisualElement();
  282. var container = new VisualElement();
  283. root.Add(container);
  284. m_parent.OnStats += (peer, report) =>
  285. {
  286. if (peer != m_peerConnection)
  287. {
  288. return;
  289. }
  290. container.Clear();
  291. if (!report.TryGetValue(id, out var stats) ||
  292. !(stats is RTCRemoteOutboundRtpStreamStats remoteOutboundStats))
  293. {
  294. container.Add(new Label($"no stats report about {RTCStatsType.RemoteOutboundRtp}"));
  295. return;
  296. }
  297. container.Add(new Label($"{nameof(remoteOutboundStats.Id)}: {remoteOutboundStats.Id}"));
  298. container.Add(new Label($"{nameof(remoteOutboundStats.Timestamp)}: {remoteOutboundStats.Timestamp}"));
  299. };
  300. return root;
  301. }
  302. private VisualElement CreateMediaSourceView(string id)
  303. {
  304. var root = new VisualElement();
  305. var container = new VisualElement();
  306. root.Add(container);
  307. var sourceGraph = new MediaSourceGraphView();
  308. var graphView = sourceGraph.Create();
  309. m_parent.OnStats += (peer, report) =>
  310. {
  311. if (peer != m_peerConnection)
  312. {
  313. return;
  314. }
  315. container.Clear();
  316. if (!report.TryGetValue(id, out var stats) ||
  317. !(stats is RTCMediaSourceStats mediaSourceStats))
  318. {
  319. container.Add(new Label($"no stats report about {RTCStatsType.MediaSource}"));
  320. return;
  321. }
  322. container.Add(new Label($"{nameof(mediaSourceStats.Id)}: {mediaSourceStats.Id}"));
  323. container.Add(new Label($"{nameof(mediaSourceStats.Timestamp)}: {mediaSourceStats.Timestamp}"));
  324. container.Add(
  325. new Label($"{nameof(mediaSourceStats.trackIdentifier)}: {mediaSourceStats.trackIdentifier}"));
  326. container.Add(new Label($"{nameof(mediaSourceStats.kind)}: {mediaSourceStats.kind}"));
  327. if (mediaSourceStats is RTCVideoSourceStats videoSourceStats)
  328. {
  329. container.Add(new Label($"{nameof(videoSourceStats.width)}: {videoSourceStats.width}"));
  330. container.Add(new Label($"{nameof(videoSourceStats.height)}: {videoSourceStats.height}"));
  331. container.Add(new Label($"{nameof(videoSourceStats.frames)}: {videoSourceStats.frames}"));
  332. container.Add(
  333. new Label($"{nameof(videoSourceStats.framesPerSecond)}: {videoSourceStats.framesPerSecond}"));
  334. sourceGraph.AddInput(videoSourceStats);
  335. graphView.visible = true;
  336. }
  337. if (mediaSourceStats is RTCAudioSourceStats audioSourceStats)
  338. {
  339. container.Add(new Label($"{nameof(audioSourceStats.audioLevel)}: {audioSourceStats.audioLevel}"));
  340. container.Add(new Label($"{nameof(audioSourceStats.totalAudioEnergy)}: {audioSourceStats.totalAudioEnergy}"));
  341. container.Add(new Label($"{nameof(audioSourceStats.totalSamplesDuration)}: {audioSourceStats.totalSamplesDuration}"));
  342. sourceGraph.AddInput(audioSourceStats);
  343. graphView.visible = true;
  344. }
  345. };
  346. root.Add(graphView);
  347. return root;
  348. }
  349. private VisualElement CreateCsrcView(string id)
  350. {
  351. var root = new VisualElement();
  352. var container = new VisualElement();
  353. root.Add(container);
  354. m_parent.OnStats += (peer, report) =>
  355. {
  356. if (peer != m_peerConnection)
  357. {
  358. return;
  359. }
  360. container.Clear();
  361. if (!report.TryGetValue(id, out var stats) ||
  362. !(stats is RTCCodecStats csrcStats))
  363. {
  364. container.Add(new Label($"no stats report about {RTCStatsType.Csrc}"));
  365. return;
  366. }
  367. container.Add(new Label($"{nameof(csrcStats.Id)}: {csrcStats.Id}"));
  368. container.Add(new Label($"{nameof(csrcStats.Timestamp)}: {csrcStats.Timestamp}"));
  369. };
  370. return root;
  371. }
  372. private VisualElement CreatePeerConnectionView(string id)
  373. {
  374. var root = new VisualElement();
  375. var container = new VisualElement();
  376. root.Add(container);
  377. m_parent.OnStats += (peer, report) =>
  378. {
  379. if (peer != m_peerConnection)
  380. {
  381. return;
  382. }
  383. container.Clear();
  384. if (!report.TryGetValue(id, out var stats) ||
  385. !(stats is RTCPeerConnectionStats peerStats))
  386. {
  387. container.Add(new Label($"no stats report about {RTCStatsType.Codec}"));
  388. return;
  389. }
  390. container.Add(new Label($"{nameof(peerStats.Id)}: {peerStats.Id}"));
  391. container.Add(new Label($"{nameof(peerStats.Timestamp)}: {peerStats.Timestamp}"));
  392. container.Add(new Label($"{nameof(peerStats.dataChannelsOpened)}: {peerStats.dataChannelsOpened}"));
  393. container.Add(new Label($"{nameof(peerStats.dataChannelsClosed)}: {peerStats.dataChannelsClosed}"));
  394. };
  395. return root;
  396. }
  397. private VisualElement CreateDataChannelView(string id)
  398. {
  399. var root = new VisualElement();
  400. var container = new VisualElement();
  401. root.Add(container);
  402. var dataChannelGraph = new DataChannelGraphView();
  403. m_parent.OnStats += (peer, report) =>
  404. {
  405. if (peer != m_peerConnection)
  406. {
  407. return;
  408. }
  409. container.Clear();
  410. if (!report.TryGetValue(id, out var stats) ||
  411. !(stats is RTCDataChannelStats dataChannelStats))
  412. {
  413. container.Add(new Label($"no stats report about {RTCStatsType.DataChannel}"));
  414. return;
  415. }
  416. container.Add(new Label($"{nameof(dataChannelStats.Id)}: {dataChannelStats.Id}"));
  417. container.Add(new Label($"{nameof(dataChannelStats.Timestamp)}: {dataChannelStats.Timestamp}"));
  418. container.Add(new Label($"{nameof(dataChannelStats.label)}: {dataChannelStats.label}"));
  419. container.Add(new Label($"{nameof(dataChannelStats.protocol)}: {dataChannelStats.protocol}"));
  420. container.Add(new Label($"{nameof(dataChannelStats.dataChannelIdentifier)}: {dataChannelStats.dataChannelIdentifier}"));
  421. container.Add(new Label($"{nameof(dataChannelStats.state)}: {dataChannelStats.state}"));
  422. container.Add(new Label($"{nameof(dataChannelStats.messagesSent)}: {dataChannelStats.messagesSent}"));
  423. container.Add(new Label($"{nameof(dataChannelStats.bytesSent)}: {dataChannelStats.bytesSent}"));
  424. container.Add(
  425. new Label($"{nameof(dataChannelStats.messagesReceived)}: {dataChannelStats.messagesReceived}"));
  426. container.Add(new Label($"{nameof(dataChannelStats.bytesReceived)}: {dataChannelStats.bytesReceived}"));
  427. dataChannelGraph.AddInput(dataChannelStats);
  428. };
  429. root.Add(dataChannelGraph.Create());
  430. return root;
  431. }
  432. private VisualElement CreateStreamView(string id)
  433. {
  434. var root = new VisualElement();
  435. var container = new VisualElement();
  436. root.Add(container);
  437. m_parent.OnStats += (peer, report) =>
  438. {
  439. if (peer != m_peerConnection)
  440. {
  441. return;
  442. }
  443. container.Clear();
  444. if (!report.TryGetValue(id, out var stats) ||
  445. !(stats is RTCMediaStreamStats streamStats))
  446. {
  447. container.Add(new Label($"no stats report about {RTCStatsType.Stream}"));
  448. return;
  449. }
  450. container.Add(new Label($"{nameof(streamStats.Id)}: {streamStats.Id}"));
  451. container.Add(new Label($"{nameof(streamStats.Timestamp)}: {streamStats.Timestamp}"));
  452. container.Add(new Label($"{nameof(streamStats.streamIdentifier)}: {streamStats.streamIdentifier}"));
  453. container.Add(
  454. new Label($"{nameof(streamStats.trackIds)}: {string.Join(",", streamStats.trackIds)}"));
  455. };
  456. return root;
  457. }
  458. private VisualElement CreateTrackView(string id)
  459. {
  460. var root = new VisualElement();
  461. var container = new VisualElement();
  462. root.Add(container);
  463. var trackGraph = new MediaStreamTrackGraphView();
  464. m_parent.OnStats += (peer, report) =>
  465. {
  466. if (peer != m_peerConnection)
  467. {
  468. return;
  469. }
  470. container.Clear();
  471. if (!report.TryGetValue(id, out var stats) ||
  472. !(stats is RTCMediaStreamTrackStats trackStats))
  473. {
  474. container.Add(new Label($"no stats report about {RTCStatsType.Track}"));
  475. return;
  476. }
  477. container.Add(new Label($"{nameof(trackStats.Id)}: {trackStats.Id}"));
  478. container.Add(new Label($"{nameof(trackStats.Timestamp)}: {trackStats.Timestamp}"));
  479. container.Add(new Label($"{nameof(trackStats.trackIdentifier)}: {trackStats.trackIdentifier}"));
  480. container.Add(new Label($"{nameof(trackStats.mediaSourceId)}: {trackStats.mediaSourceId}"));
  481. container.Add(new Label($"{nameof(trackStats.remoteSource)}: {trackStats.remoteSource}"));
  482. container.Add(new Label($"{nameof(trackStats.ended)}: {trackStats.ended}"));
  483. container.Add(new Label($"{nameof(trackStats.detached)}: {trackStats.detached}"));
  484. container.Add(new Label($"{nameof(trackStats.kind)}: {trackStats.kind}"));
  485. container.Add(new Label($"{nameof(trackStats.jitterBufferDelay)}: {trackStats.jitterBufferDelay}"));
  486. container.Add(new Label(
  487. $"{nameof(trackStats.jitterBufferEmittedCount)}: {trackStats.jitterBufferEmittedCount}"));
  488. container.Add(new Label($"{nameof(trackStats.frameWidth)}: {trackStats.frameWidth}"));
  489. container.Add(new Label($"{nameof(trackStats.frameHeight)}: {trackStats.frameHeight}"));
  490. container.Add(new Label($"{nameof(trackStats.framesPerSecond)}: {trackStats.framesPerSecond}"));
  491. container.Add(new Label($"{nameof(trackStats.framesSent)}: {trackStats.framesSent}"));
  492. container.Add(new Label($"{nameof(trackStats.hugeFramesSent)}: {trackStats.hugeFramesSent}"));
  493. container.Add(new Label($"{nameof(trackStats.framesReceived)}: {trackStats.framesReceived}"));
  494. container.Add(new Label($"{nameof(trackStats.framesDecoded)}: {trackStats.framesDecoded}"));
  495. container.Add(new Label($"{nameof(trackStats.framesDropped)}: {trackStats.framesDropped}"));
  496. container.Add(new Label($"{nameof(trackStats.framesCorrupted)}: {trackStats.framesCorrupted}"));
  497. container.Add(new Label($"{nameof(trackStats.partialFramesLost)}: {trackStats.partialFramesLost}"));
  498. container.Add(new Label($"{nameof(trackStats.fullFramesLost)}: {trackStats.fullFramesLost}"));
  499. container.Add(new Label($"{nameof(trackStats.audioLevel)}: {trackStats.audioLevel}"));
  500. container.Add(new Label($"{nameof(trackStats.totalAudioEnergy)}: {trackStats.totalAudioEnergy}"));
  501. container.Add(new Label($"{nameof(trackStats.echoReturnLoss)}: {trackStats.echoReturnLoss}"));
  502. container.Add(new Label(
  503. $"{nameof(trackStats.echoReturnLossEnhancement)}: {trackStats.echoReturnLossEnhancement}"));
  504. container.Add(
  505. new Label($"{nameof(trackStats.totalSamplesReceived)}: {trackStats.totalSamplesReceived}"));
  506. container.Add(
  507. new Label($"{nameof(trackStats.totalSamplesDuration)}: {trackStats.totalSamplesDuration}"));
  508. container.Add(new Label($"{nameof(trackStats.concealedSamples)}: {trackStats.concealedSamples}"));
  509. container.Add(
  510. new Label($"{nameof(trackStats.silentConcealedSamples)}: {trackStats.silentConcealedSamples}"));
  511. container.Add(new Label($"{nameof(trackStats.concealmentEvents)}: {trackStats.concealmentEvents}"));
  512. container.Add(new Label(
  513. $"{nameof(trackStats.insertedSamplesForDeceleration)}: {trackStats.insertedSamplesForDeceleration}"));
  514. container.Add(new Label(
  515. $"{nameof(trackStats.removedSamplesForAcceleration)}: {trackStats.removedSamplesForAcceleration}"));
  516. container.Add(new Label($"{nameof(trackStats.jitterBufferFlushes)}: {trackStats.jitterBufferFlushes}"));
  517. container.Add(new Label(
  518. $"{nameof(trackStats.delayedPacketOutageSamples)}: {trackStats.delayedPacketOutageSamples}"));
  519. container.Add(new Label(
  520. $"{nameof(trackStats.relativePacketArrivalDelay)}: {trackStats.relativePacketArrivalDelay}"));
  521. container.Add(new Label($"{nameof(trackStats.interruptionCount)}: {trackStats.interruptionCount}"));
  522. container.Add(new Label(
  523. $"{nameof(trackStats.totalInterruptionDuration)}: {trackStats.totalInterruptionDuration}"));
  524. container.Add(new Label($"{nameof(trackStats.freezeCount)}: {trackStats.freezeCount}"));
  525. container.Add(new Label($"{nameof(trackStats.pauseCount)}: {trackStats.pauseCount}"));
  526. container.Add(
  527. new Label($"{nameof(trackStats.totalFreezesDuration)}: {trackStats.totalFreezesDuration}"));
  528. container.Add(new Label($"{nameof(trackStats.totalPausesDuration)}: {trackStats.totalPausesDuration}"));
  529. container.Add(new Label($"{nameof(trackStats.totalFramesDuration)}: {trackStats.totalFramesDuration}"));
  530. container.Add(new Label(
  531. $"{nameof(trackStats.sumOfSquaredFramesDuration)}: {trackStats.sumOfSquaredFramesDuration}"));
  532. trackGraph.AddInput(trackStats);
  533. };
  534. root.Add(trackGraph.Create());
  535. return root;
  536. }
  537. private VisualElement CreateTransceiverView(string id)
  538. {
  539. var root = new VisualElement();
  540. var container = new VisualElement();
  541. root.Add(container);
  542. m_parent.OnStats += (peer, report) =>
  543. {
  544. if (peer != m_peerConnection)
  545. {
  546. return;
  547. }
  548. container.Clear();
  549. if (!report.TryGetValue(id, out var stats) ||
  550. !(stats is RTCTransceiverStats transceiverStats))
  551. {
  552. container.Add(new Label($"no stats report about {RTCStatsType.Transceiver}"));
  553. return;
  554. }
  555. container.Add(new Label($"{nameof(transceiverStats.Id)}: {transceiverStats.Id}"));
  556. container.Add(new Label($"{nameof(transceiverStats.Timestamp)}: {transceiverStats.Timestamp}"));
  557. };
  558. return root;
  559. }
  560. private VisualElement CreateSenderView(string id)
  561. {
  562. var root = new VisualElement();
  563. var container = new VisualElement();
  564. root.Add(container);
  565. m_parent.OnStats += (peer, report) =>
  566. {
  567. if (peer != m_peerConnection)
  568. {
  569. return;
  570. }
  571. container.Clear();
  572. if (!report.TryGetValue(id, out var stats) ||
  573. !(stats is RTCSenderStats senderStats))
  574. {
  575. container.Add(new Label($"no stats report about {RTCStatsType.Sender}"));
  576. return;
  577. }
  578. container.Add(new Label($"{nameof(senderStats.Id)}: {senderStats.Id}"));
  579. container.Add(new Label($"{nameof(senderStats.Timestamp)}: {senderStats.Timestamp}"));
  580. };
  581. return root;
  582. }
  583. private VisualElement CreateReceiverView(string id)
  584. {
  585. var root = new VisualElement();
  586. var container = new VisualElement();
  587. root.Add(container);
  588. m_parent.OnStats += (peer, report) =>
  589. {
  590. if (peer != m_peerConnection)
  591. {
  592. return;
  593. }
  594. container.Clear();
  595. if (!report.TryGetValue(id, out var stats) ||
  596. !(stats is RTCReceiverStats receiverStats))
  597. {
  598. container.Add(new Label($"no stats report about {RTCStatsType.Receiver}"));
  599. return;
  600. }
  601. container.Add(new Label($"{nameof(receiverStats.Id)}: {receiverStats.Id}"));
  602. container.Add(new Label($"{nameof(receiverStats.Timestamp)}: {receiverStats.Timestamp}"));
  603. };
  604. return root;
  605. }
  606. private VisualElement CreateTransportView(string id)
  607. {
  608. var root = new VisualElement();
  609. var container = new VisualElement();
  610. root.Add(container);
  611. var transportGraph = new TransportGraphView();
  612. m_parent.OnStats += (peer, report) =>
  613. {
  614. if (peer != m_peerConnection)
  615. {
  616. return;
  617. }
  618. container.Clear();
  619. if (!report.TryGetValue(id, out var stats) ||
  620. !(stats is RTCTransportStats transportStats))
  621. {
  622. container.Add(new Label($"no stats report about {RTCStatsType.Transport}"));
  623. return;
  624. }
  625. container.Add(new Label($"{nameof(transportStats.Id)}: {transportStats.Id}"));
  626. container.Add(new Label($"{nameof(transportStats.Timestamp)}: {transportStats.Timestamp}"));
  627. container.Add(new Label($"{nameof(transportStats.bytesSent)}: {transportStats.bytesSent}"));
  628. container.Add(new Label($"{nameof(transportStats.bytesReceived)}: {transportStats.bytesReceived}"));
  629. container.Add(new Label(
  630. $"{nameof(transportStats.rtcpTransportStatsId)}: {transportStats.rtcpTransportStatsId}"));
  631. container.Add(new Label($"{nameof(transportStats.dtlsState)}: {transportStats.dtlsState}"));
  632. container.Add(new Label(
  633. $"{nameof(transportStats.selectedCandidatePairId)}: {transportStats.selectedCandidatePairId}"));
  634. container.Add(
  635. new Label($"{nameof(transportStats.localCertificateId)}: {transportStats.localCertificateId}"));
  636. container.Add(
  637. new Label($"{nameof(transportStats.remoteCertificateId)}: {transportStats.remoteCertificateId}"));
  638. container.Add(new Label(
  639. $"{nameof(transportStats.selectedCandidatePairChanges)}: {transportStats.selectedCandidatePairChanges}"));
  640. transportGraph.AddInput(transportStats);
  641. };
  642. root.Add(transportGraph.Create());
  643. return root;
  644. }
  645. private VisualElement CreateSctpTransportView(string id)
  646. {
  647. var root = new VisualElement();
  648. var container = new VisualElement();
  649. root.Add(container);
  650. var transportGraph = new TransportGraphView();
  651. m_parent.OnStats += (peer, report) =>
  652. {
  653. if (peer != m_peerConnection)
  654. {
  655. return;
  656. }
  657. container.Clear();
  658. if (!report.TryGetValue(id, out var stats) ||
  659. !(stats is RTCTransportStats transportStats))
  660. {
  661. container.Add(new Label($"no stats report about {RTCStatsType.Codec}"));
  662. return;
  663. }
  664. container.Add(new Label($"{nameof(transportStats.Id)}: {transportStats.Id}"));
  665. container.Add(new Label($"{nameof(transportStats.Timestamp)}: {transportStats.Timestamp}"));
  666. container.Add(new Label($"{nameof(transportStats.bytesSent)}: {transportStats.bytesSent}"));
  667. container.Add(new Label($"{nameof(transportStats.bytesReceived)}: {transportStats.bytesReceived}"));
  668. container.Add(new Label(
  669. $"{nameof(transportStats.rtcpTransportStatsId)}: {transportStats.rtcpTransportStatsId}"));
  670. container.Add(new Label($"{nameof(transportStats.dtlsState)}: {transportStats.dtlsState}"));
  671. container.Add(new Label(
  672. $"{nameof(transportStats.selectedCandidatePairId)}: {transportStats.selectedCandidatePairId}"));
  673. container.Add(
  674. new Label($"{nameof(transportStats.localCertificateId)}: {transportStats.localCertificateId}"));
  675. container.Add(
  676. new Label($"{nameof(transportStats.remoteCertificateId)}: {transportStats.remoteCertificateId}"));
  677. container.Add(new Label(
  678. $"{nameof(transportStats.selectedCandidatePairChanges)}: {transportStats.selectedCandidatePairChanges}"));
  679. transportGraph.AddInput(transportStats);
  680. };
  681. root.Add(transportGraph.Create());
  682. return root;
  683. }
  684. private VisualElement CreateCandidatePairView(string id)
  685. {
  686. var root = new VisualElement();
  687. var container = new VisualElement();
  688. root.Add(container);
  689. var graphView = new CandidatePairGraphView();
  690. m_parent.OnStats += (peer, report) =>
  691. {
  692. if (peer != m_peerConnection)
  693. {
  694. return;
  695. }
  696. container.Clear();
  697. if (!report.TryGetValue(id, out var stats) ||
  698. !(stats is RTCIceCandidatePairStats candidatePairStats))
  699. {
  700. container.Add(new Label($"no stats report about {RTCStatsType.CandidatePair}"));
  701. return;
  702. }
  703. container.Add(new Label($"{nameof(candidatePairStats.Id)}: {candidatePairStats.Id}"));
  704. container.Add(new Label($"{nameof(candidatePairStats.Timestamp)}: {candidatePairStats.Timestamp}"));
  705. container.Add(new Label($"{nameof(candidatePairStats.transportId)}: {candidatePairStats.transportId}"));
  706. container.Add(new Label(
  707. $"{nameof(candidatePairStats.localCandidateId)}: {candidatePairStats.localCandidateId}"));
  708. container.Add(new Label(
  709. $"{nameof(candidatePairStats.remoteCandidateId)}: {candidatePairStats.remoteCandidateId}"));
  710. container.Add(new Label($"{nameof(candidatePairStats.state)}: {candidatePairStats.state}"));
  711. container.Add(new Label($"{nameof(candidatePairStats.priority)}: {candidatePairStats.priority}"));
  712. container.Add(new Label($"{nameof(candidatePairStats.nominated)}: {candidatePairStats.nominated}"));
  713. container.Add(new Label($"{nameof(candidatePairStats.writable)}: {candidatePairStats.writable}"));
  714. container.Add(new Label($"{nameof(candidatePairStats.readable)}: {candidatePairStats.readable}"));
  715. container.Add(new Label($"{nameof(candidatePairStats.bytesSent)}: {candidatePairStats.bytesSent}"));
  716. container.Add(
  717. new Label($"{nameof(candidatePairStats.bytesReceived)}: {candidatePairStats.bytesReceived}"));
  718. container.Add(new Label(
  719. $"{nameof(candidatePairStats.totalRoundTripTime)}: {candidatePairStats.totalRoundTripTime}"));
  720. container.Add(new Label(
  721. $"{nameof(candidatePairStats.currentRoundTripTime)}: {candidatePairStats.currentRoundTripTime}"));
  722. container.Add(new Label(
  723. $"{nameof(candidatePairStats.availableOutgoingBitrate)}: {candidatePairStats.availableOutgoingBitrate}"));
  724. container.Add(new Label(
  725. $"{nameof(candidatePairStats.availableIncomingBitrate)}: {candidatePairStats.availableIncomingBitrate}"));
  726. container.Add(new Label(
  727. $"{nameof(candidatePairStats.requestsReceived)}: {candidatePairStats.requestsReceived}"));
  728. container.Add(
  729. new Label($"{nameof(candidatePairStats.requestsSent)}: {candidatePairStats.requestsSent}"));
  730. container.Add(new Label(
  731. $"{nameof(candidatePairStats.responsesReceived)}: {candidatePairStats.responsesReceived}"));
  732. container.Add(
  733. new Label($"{nameof(candidatePairStats.responsesSent)}: {candidatePairStats.responsesSent}"));
  734. container.Add(new Label(
  735. $"{nameof(candidatePairStats.retransmissionsReceived)}: {candidatePairStats.retransmissionsReceived}"));
  736. container.Add(new Label(
  737. $"{nameof(candidatePairStats.retransmissionsSent)}: {candidatePairStats.retransmissionsSent}"));
  738. container.Add(new Label(
  739. $"{nameof(candidatePairStats.consentRequestsReceived)}: {candidatePairStats.consentRequestsReceived}"));
  740. container.Add(new Label(
  741. $"{nameof(candidatePairStats.consentRequestsSent)}: {candidatePairStats.consentRequestsSent}"));
  742. container.Add(new Label(
  743. $"{nameof(candidatePairStats.consentResponsesReceived)}: {candidatePairStats.consentResponsesReceived}"));
  744. container.Add(new Label(
  745. $"{nameof(candidatePairStats.consentResponsesSent)}: {candidatePairStats.consentResponsesSent}"));
  746. graphView.AddInput(candidatePairStats);
  747. };
  748. root.Add(graphView.Create());
  749. return root;
  750. }
  751. private VisualElement CreateLocalCandidateView(string id)
  752. {
  753. var root = new VisualElement();
  754. var container = new VisualElement();
  755. root.Add(container);
  756. m_parent.OnStats += (peer, report) =>
  757. {
  758. if (peer != m_peerConnection)
  759. {
  760. return;
  761. }
  762. container.Clear();
  763. if (!report.TryGetValue(id, out var stats) ||
  764. !(stats is RTCIceCandidateStats candidateStats))
  765. {
  766. container.Add(new Label($"no stats report about {RTCStatsType.LocalCandidate}"));
  767. return;
  768. }
  769. container.Add(new Label($"{nameof(candidateStats.Id)}: {candidateStats.Id}"));
  770. container.Add(new Label($"{nameof(candidateStats.Timestamp)}: {candidateStats.Timestamp}"));
  771. container.Add(new Label($"{nameof(candidateStats.transportId)}: {candidateStats.transportId}"));
  772. container.Add(new Label($"{nameof(candidateStats.isRemote)}: {candidateStats.isRemote}"));
  773. container.Add(new Label($"{nameof(candidateStats.networkType)}: {candidateStats.networkType}"));
  774. container.Add(new Label($"{nameof(candidateStats.ip)}: {candidateStats.ip}"));
  775. container.Add(new Label($"{nameof(candidateStats.address)}: {candidateStats.address}"));
  776. container.Add(new Label($"{nameof(candidateStats.port)}: {candidateStats.port}"));
  777. container.Add(new Label($"{nameof(candidateStats.protocol)}: {candidateStats.protocol}"));
  778. container.Add(new Label($"{nameof(candidateStats.relayProtocol)}: {candidateStats.relayProtocol}"));
  779. container.Add(new Label($"{nameof(candidateStats.candidateType)}: {candidateStats.candidateType}"));
  780. container.Add(new Label($"{nameof(candidateStats.priority)}: {candidateStats.priority}"));
  781. container.Add(new Label($"{nameof(candidateStats.url)}: {candidateStats.url}"));
  782. };
  783. return root;
  784. }
  785. private VisualElement CreateRemoteCandidateView(string id)
  786. {
  787. var root = new VisualElement();
  788. var container = new VisualElement();
  789. root.Add(container);
  790. m_parent.OnStats += (peer, report) =>
  791. {
  792. if (peer != m_peerConnection)
  793. {
  794. return;
  795. }
  796. container.Clear();
  797. if (!report.TryGetValue(id, out var stats) ||
  798. !(stats is RTCIceCandidateStats candidateStats))
  799. {
  800. container.Add(new Label($"no stats report about {RTCStatsType.RemoteCandidate}"));
  801. return;
  802. }
  803. container.Add(new Label($"{nameof(candidateStats.Id)}: {candidateStats.Id}"));
  804. container.Add(new Label($"{nameof(candidateStats.Timestamp)}: {candidateStats.Timestamp}"));
  805. container.Add(new Label($"{nameof(candidateStats.transportId)}: {candidateStats.transportId}"));
  806. container.Add(new Label($"{nameof(candidateStats.isRemote)}: {candidateStats.isRemote}"));
  807. container.Add(new Label($"{nameof(candidateStats.networkType)}: {candidateStats.networkType}"));
  808. container.Add(new Label($"{nameof(candidateStats.ip)}: {candidateStats.ip}"));
  809. container.Add(new Label($"{nameof(candidateStats.address)}: {candidateStats.address}"));
  810. container.Add(new Label($"{nameof(candidateStats.port)}: {candidateStats.port}"));
  811. container.Add(new Label($"{nameof(candidateStats.protocol)}: {candidateStats.protocol}"));
  812. container.Add(new Label($"{nameof(candidateStats.relayProtocol)}: {candidateStats.relayProtocol}"));
  813. container.Add(new Label($"{nameof(candidateStats.candidateType)}: {candidateStats.candidateType}"));
  814. container.Add(new Label($"{nameof(candidateStats.priority)}: {candidateStats.priority}"));
  815. container.Add(new Label($"{nameof(candidateStats.url)}: {candidateStats.url}"));
  816. };
  817. return root;
  818. }
  819. private VisualElement CreateCertificateView(string id)
  820. {
  821. var root = new VisualElement();
  822. var container = new VisualElement();
  823. root.Add(container);
  824. m_parent.OnStats += (peer, report) =>
  825. {
  826. if (peer != m_peerConnection)
  827. {
  828. return;
  829. }
  830. container.Clear();
  831. if (!report.TryGetValue(id, out var stats) ||
  832. !(stats is RTCCertificateStats certificateStats))
  833. {
  834. container.Add(new Label($"no stats report about {RTCStatsType.Certificate}"));
  835. return;
  836. }
  837. container.Add(new Label($"{nameof(certificateStats.Id)}: {certificateStats.Id}"));
  838. container.Add(new Label($"{nameof(certificateStats.Timestamp)}: {certificateStats.Timestamp}"));
  839. container.Add(new Label($"{nameof(certificateStats.fingerprint)}: {certificateStats.fingerprint}"));
  840. container.Add(new Label(
  841. $"{nameof(certificateStats.fingerprintAlgorithm)}: {certificateStats.fingerprintAlgorithm}"));
  842. container.Add(
  843. new Label($"{nameof(certificateStats.base64Certificate)}: {certificateStats.base64Certificate}"));
  844. container.Add(new Label(
  845. $"{nameof(certificateStats.issuerCertificateId)}: {certificateStats.issuerCertificateId}"));
  846. };
  847. return root;
  848. }
  849. private VisualElement CreateIceServerView(string id)
  850. {
  851. var root = new VisualElement();
  852. var container = new VisualElement();
  853. root.Add(container);
  854. m_parent.OnStats += (peer, report) =>
  855. {
  856. if (peer != m_peerConnection)
  857. {
  858. return;
  859. }
  860. container.Clear();
  861. if (!report.TryGetValue(id, out var stats) ||
  862. !(stats is RTCCertificateStats outboundStats))
  863. {
  864. container.Add(new Label($"no stats report about {RTCStatsType.IceServer}"));
  865. return;
  866. }
  867. container.Add(new Label($"{nameof(outboundStats.Id)}: {outboundStats.Id}"));
  868. container.Add(new Label($"{nameof(outboundStats.Timestamp)}: {outboundStats.Timestamp}"));
  869. };
  870. return root;
  871. }
  872. }
  873. }