CodecDrawer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #if UNITY_EDITOR
  2. using System;
  3. using System.Linq;
  4. using System.Collections.Generic;
  5. using UnityEditor;
  6. using UnityEngine;
  7. using System.Reflection;
  8. namespace Unity.RenderStreaming.Editor
  9. {
  10. [CustomPropertyDrawer(typeof(CodecAttribute))]
  11. class CodecDrawer : PropertyDrawer
  12. {
  13. interface Codec
  14. {
  15. string name { get; }
  16. string mimeType { get; }
  17. string sdpFmtpLine { get; }
  18. int channelCount { get; }
  19. int sampleRate { get; }
  20. string optionTitle { get; }
  21. int order { get; }
  22. }
  23. class AudioCodec : Codec
  24. {
  25. public string name { get { return codec_.name; } }
  26. public string mimeType { get { return codec_.mimeType; } }
  27. public string sdpFmtpLine { get { return codec_.sdpFmtpLine; } }
  28. public int channelCount { get { return codec_.channelCount; } }
  29. public int sampleRate { get { return codec_.sampleRate; } }
  30. public string optionTitle
  31. {
  32. get
  33. {
  34. return $"{codec_.channelCount} channel";
  35. }
  36. }
  37. public int order { get { return codec_.channelCount; } }
  38. public AudioCodec(AudioCodecInfo codec)
  39. {
  40. codec_ = codec;
  41. }
  42. AudioCodecInfo codec_;
  43. }
  44. class VideoCodec : Codec
  45. {
  46. public string name { get { return codec_.name; } }
  47. public string mimeType { get { return codec_.mimeType; } }
  48. public string sdpFmtpLine { get { return codec_.sdpFmtpLine; } }
  49. public string optionTitle
  50. {
  51. get
  52. {
  53. switch(codec_)
  54. {
  55. case H264CodecInfo h264Codec:
  56. return $"{h264Codec.profile} Profile, Level {h264Codec.level.ToString().Insert(1, ".")}";
  57. case VP9CodecInfo vp9codec:
  58. return $"Profile {(int)vp9codec.profile}";
  59. case AV1CodecInfo av1codec:
  60. return $"Profile {(int)av1codec.profile}";
  61. }
  62. return null;
  63. }
  64. }
  65. public int channelCount { get { throw new NotSupportedException(); } }
  66. public int sampleRate { get { throw new NotSupportedException(); } }
  67. public int order
  68. {
  69. get
  70. {
  71. switch (codec_)
  72. {
  73. case H264CodecInfo h264Codec:
  74. return (int)h264Codec.profile;
  75. case VP9CodecInfo vp9codec:
  76. return (int)vp9codec.profile;
  77. case AV1CodecInfo av1codec:
  78. return (int)av1codec.profile;
  79. }
  80. return 0;
  81. }
  82. }
  83. public VideoCodec(VideoCodecInfo codec)
  84. {
  85. codec_ = codec;
  86. }
  87. VideoCodecInfo codec_;
  88. }
  89. SerializedProperty propertyMimeType;
  90. SerializedProperty propertySdpFmtpLine;
  91. SerializedProperty propertyChannelCount;
  92. SerializedProperty propertySampleRate;
  93. IEnumerable<Codec> codecs;
  94. string[] codecNames = new string[] { "Default" };
  95. string[] codecOptions = new string[] {};
  96. IEnumerable<Codec> selectedCodecs;
  97. GUIContent codecLabel;
  98. int selectCodecIndex = 0;
  99. int selectCodecOptionIndex = 0;
  100. bool hasCodecOptions = false;
  101. bool cache = false;
  102. bool changed = false;
  103. static readonly GUIContent s_audioCodecLabel =
  104. EditorGUIUtility.TrTextContent("Audio Codec", "Audio encoding codec.");
  105. static readonly GUIContent s_videoCodecLabel =
  106. EditorGUIUtility.TrTextContent("Video Codec", "Video encoding codec.");
  107. static IEnumerable<Codec> GetAvailableCodecs(UnityEngine.Object target)
  108. {
  109. if(target is VideoStreamSender)
  110. {
  111. return VideoStreamSender.GetAvailableCodecs().Select(codec => new VideoCodec(codec));
  112. }
  113. else if (target is VideoStreamReceiver)
  114. {
  115. return VideoStreamReceiver.GetAvailableCodecs().Select(codec => new VideoCodec(codec));
  116. }
  117. else if (target is AudioStreamSender)
  118. {
  119. return AudioStreamSender.GetAvailableCodecs().Select(codec => new AudioCodec(codec));
  120. }
  121. else if (target is AudioStreamReceiver)
  122. {
  123. return AudioStreamReceiver.GetAvailableCodecs().Select(codec => new AudioCodec(codec));
  124. }
  125. throw new ArgumentException();
  126. }
  127. static GUIContent GetCodecLabel(UnityEngine.Object target)
  128. {
  129. if (target is VideoStreamSender || target is VideoStreamReceiver)
  130. {
  131. return s_videoCodecLabel;
  132. }
  133. else if (target is AudioStreamSender || target is AudioStreamReceiver)
  134. {
  135. return s_audioCodecLabel;
  136. }
  137. throw new ArgumentException();
  138. }
  139. int FindOptionIndex(IEnumerable<Codec> codecs)
  140. {
  141. return Array.FindIndex(codecs.ToArray(), codec =>
  142. {
  143. if (codec is VideoCodec)
  144. return codec.sdpFmtpLine == propertySdpFmtpLine.stringValue;
  145. else
  146. return
  147. codec.sdpFmtpLine == propertySdpFmtpLine.stringValue &&
  148. codec.channelCount == propertyChannelCount.intValue &&
  149. codec.sampleRate == propertySampleRate.intValue;
  150. });
  151. }
  152. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  153. {
  154. if (!cache)
  155. {
  156. propertyMimeType = property.FindPropertyInChildren("m_MimeType");
  157. propertySdpFmtpLine = property.FindPropertyInChildren("m_SdpFmtpLine");
  158. propertyChannelCount = property.FindPropertyInChildren("m_ChannelCount");
  159. propertySampleRate = property.FindPropertyInChildren("m_SampleRate");
  160. codecs = GetAvailableCodecs(property.serializedObject.targetObject);
  161. codecNames = codecNames.Concat(codecs.Select(codec => codec.name)).Distinct().ToArray();
  162. var mimeType = propertyMimeType.stringValue;
  163. var codecName = mimeType.GetCodecName();
  164. selectedCodecs = codecs.Where(codec => codec.name == codecName).OrderBy(codec => codec.order);
  165. codecOptions = selectedCodecs.Select(codec => codec.optionTitle).ToArray();
  166. if (!selectedCodecs.Any())
  167. selectCodecIndex = 0;
  168. else
  169. selectCodecIndex = Array.FindIndex(codecNames, codec => codec == codecName);
  170. codecLabel = GetCodecLabel(property.serializedObject.targetObject);
  171. hasCodecOptions = codecOptions.Length > 1;
  172. if (hasCodecOptions)
  173. selectCodecOptionIndex = FindOptionIndex(selectedCodecs);
  174. cache = true;
  175. }
  176. var rect = position;
  177. rect.height = EditorGUIUtility.singleLineHeight;
  178. EditorGUI.BeginProperty(rect, label, propertyMimeType);
  179. rect = EditorGUI.PrefixLabel(rect, codecLabel);
  180. EditorGUI.BeginChangeCheck();
  181. selectCodecIndex = EditorGUI.Popup(rect, selectCodecIndex, codecNames);
  182. if (EditorGUI.EndChangeCheck())
  183. {
  184. if(0 < selectCodecIndex)
  185. {
  186. string codecName = codecNames[selectCodecIndex];
  187. selectedCodecs = codecs.Where(codec => codec.name == codecName).OrderBy(codec => codec.order);
  188. codecOptions = selectedCodecs.Select(codec => codec.optionTitle).ToArray();
  189. hasCodecOptions = codecOptions.Length > 1;
  190. var codec = selectedCodecs.First();
  191. propertyMimeType.stringValue = codec.mimeType;
  192. propertySdpFmtpLine.stringValue = codec.sdpFmtpLine;
  193. if(propertyChannelCount != null)
  194. propertyChannelCount.intValue = codec.channelCount;
  195. if (propertySampleRate != null)
  196. propertySampleRate.intValue = codec.sampleRate;
  197. }
  198. else
  199. {
  200. propertyMimeType.stringValue = null;
  201. propertySdpFmtpLine.stringValue = null;
  202. if (propertyChannelCount != null)
  203. propertyChannelCount.intValue = 0;
  204. if (propertySampleRate != null)
  205. propertySampleRate.intValue = 0;
  206. hasCodecOptions = false;
  207. }
  208. }
  209. EditorGUI.EndProperty();
  210. int codecIndex = selectCodecIndex - 1;
  211. if (0 < codecIndex)
  212. {
  213. if (hasCodecOptions && 0 < codecOptions.Length)
  214. {
  215. // sdp fmtp line
  216. rect.y += EditorGUIUtility.singleLineHeight;
  217. EditorGUI.BeginProperty(rect, label, propertySdpFmtpLine);
  218. EditorGUI.BeginChangeCheck();
  219. selectCodecOptionIndex = EditorGUI.Popup(rect, selectCodecOptionIndex, codecOptions);
  220. if (EditorGUI.EndChangeCheck())
  221. {
  222. var codec = selectedCodecs.ElementAt(selectCodecOptionIndex);
  223. propertySdpFmtpLine.stringValue = codec.sdpFmtpLine;
  224. if (propertyChannelCount != null)
  225. propertyChannelCount.intValue = codec.channelCount;
  226. if (propertySampleRate != null)
  227. propertySampleRate.intValue = codec.sampleRate;
  228. }
  229. EditorGUI.EndProperty();
  230. }
  231. }
  232. if (changed)
  233. {
  234. // todo: not supported changing codecs in play mode.
  235. //if (Application.isPlaying)
  236. //{
  237. // var objectReferenceValue = property.serializedObject.targetObject;
  238. // var type = objectReferenceValue.GetType();
  239. // var attribute = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
  240. // var methodName = "SetCodec";
  241. // var method = type.GetMethod(methodName, attribute);
  242. // method.Invoke(objectReferenceValue, new object[] { newValue });
  243. //}
  244. changed = false;
  245. }
  246. }
  247. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  248. {
  249. if (property == null)
  250. throw new System.ArgumentNullException(nameof(property));
  251. int lineCount = hasCodecOptions ? 2 : 1;
  252. return EditorGUIUtility.singleLineHeight * lineCount;
  253. }
  254. }
  255. }
  256. #endif