AudioStreamSender.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. using System;
  2. using System.Linq;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using Unity.Collections;
  6. using Unity.WebRTC;
  7. using UnityEngine;
  8. namespace Unity.RenderStreaming
  9. {
  10. /// <summary>
  11. ///
  12. /// </summary>
  13. public enum AudioStreamSource
  14. {
  15. /// <summary>
  16. ///
  17. /// </summary>
  18. AudioListener = 0,
  19. /// <summary>
  20. ///
  21. /// </summary>
  22. AudioSource = 1,
  23. /// <summary>
  24. ///
  25. /// </summary>
  26. Microphone = 2,
  27. /// <summary>
  28. ///
  29. /// </summary>
  30. APIOnly = 3
  31. }
  32. /// <summary>
  33. /// Attach AudioListerner or AudioSource
  34. /// </summary>
  35. [AddComponentMenu("Render Streaming/Audio Stream Sender")]
  36. public class AudioStreamSender : StreamSenderBase
  37. {
  38. static readonly uint s_defaultMinBitrate = 0;
  39. static readonly uint s_defaultMaxBitrate = 200;
  40. [SerializeField]
  41. private AudioStreamSource m_Source;
  42. [SerializeField]
  43. private AudioListener m_AudioListener;
  44. [SerializeField]
  45. private AudioSource m_AudioSource;
  46. [SerializeField]
  47. private int m_MicrophoneDeviceIndex;
  48. [SerializeField]
  49. private bool m_AutoRequestUserAuthorization = true;
  50. [SerializeField, Codec]
  51. private AudioCodecInfo m_Codec;
  52. [SerializeField, Bitrate(0, 1000)]
  53. private Range m_Bitrate = new Range(s_defaultMinBitrate, s_defaultMaxBitrate);
  54. private int m_sampleRate = 0;
  55. private AudioStreamSourceImpl m_sourceImpl = null;
  56. private int m_frequency = 48000;
  57. /// <summary>
  58. ///
  59. /// </summary>
  60. public AudioStreamSource source
  61. {
  62. get { return m_Source; }
  63. set
  64. {
  65. if (m_Source == value)
  66. return;
  67. m_Source = value;
  68. if (!isPlaying)
  69. return;
  70. var op = CreateTrack();
  71. StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
  72. }
  73. }
  74. /// <summary>
  75. ///
  76. /// </summary>
  77. public AudioCodecInfo codec
  78. {
  79. get { return m_Codec; }
  80. }
  81. /// <summary>
  82. ///
  83. /// </summary>
  84. public uint minBitrate
  85. {
  86. get { return m_Bitrate.min; }
  87. }
  88. /// <summary>
  89. ///
  90. /// </summary>
  91. public uint maxBitrate
  92. {
  93. get { return m_Bitrate.max; }
  94. }
  95. /// <summary>
  96. /// The index of WebCamTexture.devices.
  97. /// </summary>
  98. public int sourceDeviceIndex
  99. {
  100. get { return m_MicrophoneDeviceIndex; }
  101. set
  102. {
  103. if (m_MicrophoneDeviceIndex == value)
  104. return;
  105. m_MicrophoneDeviceIndex = value;
  106. if (!isPlaying || m_Source != AudioStreamSource.Microphone)
  107. return;
  108. var op = CreateTrack();
  109. StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
  110. }
  111. }
  112. /// <summary>
  113. ///
  114. /// </summary>
  115. public AudioSource audioSource
  116. {
  117. get { return m_AudioSource; }
  118. set
  119. {
  120. if (m_AudioSource == value)
  121. return;
  122. m_AudioSource = value;
  123. if (!isPlaying || m_Source != AudioStreamSource.AudioSource)
  124. return;
  125. var op = CreateTrack();
  126. StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
  127. }
  128. }
  129. /// <summary>
  130. ///
  131. /// </summary>
  132. public AudioListener audioListener
  133. {
  134. get { return m_AudioListener; }
  135. set
  136. {
  137. if (m_AudioListener == value)
  138. return;
  139. m_AudioListener = value;
  140. if (!isPlaying || m_Source != AudioStreamSource.AudioListener)
  141. return;
  142. var op = CreateTrack();
  143. StartCoroutineWithCallback(op, _ => ReplaceTrack(_.Track));
  144. }
  145. }
  146. /// <summary>
  147. ///
  148. /// </summary>
  149. /// <returns></returns>
  150. static public IEnumerable<AudioCodecInfo> GetAvailableCodecs()
  151. {
  152. var excludeCodecMimeType = new[] { "audio/CN", "audio/telephone-event" };
  153. var capabilities = RTCRtpSender.GetCapabilities(TrackKind.Audio);
  154. return capabilities.codecs.Where(codec => !excludeCodecMimeType.Contains(codec.mimeType)).Select(codec => AudioCodecInfo.Create(codec));
  155. }
  156. /// <summary>
  157. ///
  158. /// </summary>
  159. /// <param name="minBitrate"></param>
  160. /// <param name="maxBitrate"></param>
  161. public void SetBitrate(uint minBitrate, uint maxBitrate)
  162. {
  163. if (minBitrate > maxBitrate)
  164. throw new ArgumentException("The maxBitrate must be greater than minBitrate.", "maxBitrate");
  165. m_Bitrate.min = minBitrate;
  166. m_Bitrate.max = maxBitrate;
  167. foreach (var transceiver in Transceivers.Values)
  168. {
  169. RTCError error = transceiver.Sender.SetBitrate(m_Bitrate.min, m_Bitrate.max);
  170. if (error.errorType != RTCErrorType.None)
  171. Debug.LogError(error.message);
  172. }
  173. }
  174. /// <summary>
  175. ///
  176. /// </summary>
  177. /// <param name="codec"></param>
  178. public void SetCodec(AudioCodecInfo codec)
  179. {
  180. m_Codec = codec;
  181. foreach (var transceiver in Transceivers.Values)
  182. {
  183. if (!string.IsNullOrEmpty(transceiver.Mid))
  184. continue;
  185. if (transceiver.Sender.Track.ReadyState == TrackState.Ended)
  186. continue;
  187. var codecs = new AudioCodecInfo[] { m_Codec };
  188. RTCErrorType error = transceiver.SetCodecPreferences(SelectCodecCapabilities(codecs).ToArray());
  189. if (error != RTCErrorType.None)
  190. throw new InvalidOperationException($"Set codec is failed. errorCode={error}");
  191. }
  192. }
  193. internal IEnumerable<RTCRtpCodecCapability> SelectCodecCapabilities(IEnumerable<AudioCodecInfo> codecs)
  194. {
  195. return RTCRtpSender.GetCapabilities(TrackKind.Audio).SelectCodecCapabilities(codecs);
  196. }
  197. private protected virtual void Awake()
  198. {
  199. OnStartedStream += _OnStartedStream;
  200. OnStoppedStream += _OnStoppedStream;
  201. }
  202. private protected override void OnDestroy()
  203. {
  204. base.OnDestroy();
  205. m_sourceImpl?.Dispose();
  206. m_sourceImpl = null;
  207. }
  208. void OnAudioConfigurationChanged(bool deviceWasChanged)
  209. {
  210. m_sampleRate = AudioSettings.outputSampleRate;
  211. }
  212. void _OnStartedStream(string connectionId)
  213. {
  214. }
  215. void _OnStoppedStream(string connectionId)
  216. {
  217. m_sourceImpl?.Dispose();
  218. m_sourceImpl = null;
  219. }
  220. internal override WaitForCreateTrack CreateTrack()
  221. {
  222. m_sourceImpl?.Dispose();
  223. m_sourceImpl = CreateAudioStreamSource();
  224. return m_sourceImpl.CreateTrack();
  225. }
  226. AudioStreamSourceImpl CreateAudioStreamSource()
  227. {
  228. switch (m_Source)
  229. {
  230. case AudioStreamSource.AudioListener:
  231. return new AudioStreamSourceAudioListener(this);
  232. case AudioStreamSource.AudioSource:
  233. return new AudioStreamSourceAudioSource(this);
  234. case AudioStreamSource.Microphone:
  235. return new AudioStreamSourceMicrophone(this);
  236. case AudioStreamSource.APIOnly:
  237. return new AudioStreamSourceAPIOnly(this);
  238. }
  239. throw new InvalidOperationException("");
  240. }
  241. private protected override void OnEnable()
  242. {
  243. OnAudioConfigurationChanged(false);
  244. AudioSettings.OnAudioConfigurationChanged += OnAudioConfigurationChanged;
  245. base.OnEnable();
  246. }
  247. private protected override void OnDisable()
  248. {
  249. AudioSettings.OnAudioConfigurationChanged -= OnAudioConfigurationChanged;
  250. base.OnDisable();
  251. }
  252. public void SetData(ref NativeArray<float> nativeArray, int channels)
  253. {
  254. if (m_Source != AudioStreamSource.APIOnly)
  255. throw new InvalidOperationException("To use this method, please set AudioStreamSource.APIOnly to source property");
  256. if (!isPlaying)
  257. return;
  258. (m_sourceImpl as AudioStreamSourceAPIOnly)?.SetData(ref nativeArray, channels, m_sampleRate);
  259. }
  260. abstract class AudioStreamSourceImpl : IDisposable
  261. {
  262. protected AudioStreamSourceImpl(AudioStreamSender parent)
  263. {
  264. }
  265. public abstract WaitForCreateTrack CreateTrack();
  266. public abstract void Dispose();
  267. }
  268. class AudioStreamSourceAudioListener : AudioStreamSourceImpl
  269. {
  270. private AudioListener m_audioListener;
  271. public AudioStreamSourceAudioListener(AudioStreamSender parent) : base(parent)
  272. {
  273. m_audioListener = parent.m_AudioListener;
  274. if (m_audioListener == null)
  275. throw new InvalidOperationException("The audioListener is not assigned.");
  276. }
  277. public override WaitForCreateTrack CreateTrack()
  278. {
  279. var instruction = new WaitForCreateTrack();
  280. instruction.Done(new AudioStreamTrack(m_audioListener));
  281. return instruction;
  282. }
  283. public override void Dispose()
  284. {
  285. GC.SuppressFinalize(this);
  286. }
  287. ~AudioStreamSourceAudioListener()
  288. {
  289. Dispose();
  290. }
  291. }
  292. class AudioStreamSourceAudioSource : AudioStreamSourceImpl
  293. {
  294. private AudioSource m_audioSource;
  295. public AudioStreamSourceAudioSource(AudioStreamSender parent) : base(parent)
  296. {
  297. m_audioSource = parent.m_AudioSource;
  298. if (m_audioSource == null)
  299. throw new InvalidOperationException("The audioSource is not assigned.");
  300. }
  301. public override WaitForCreateTrack CreateTrack()
  302. {
  303. var instruction = new WaitForCreateTrack();
  304. instruction.Done(new AudioStreamTrack(m_audioSource));
  305. return instruction;
  306. }
  307. public override void Dispose()
  308. {
  309. GC.SuppressFinalize(this);
  310. }
  311. ~AudioStreamSourceAudioSource()
  312. {
  313. Dispose();
  314. }
  315. }
  316. class AudioStreamSourceMicrophone : AudioStreamSourceImpl
  317. {
  318. int m_deviceIndex;
  319. bool m_autoRequestUserAuthorization;
  320. int m_frequency;
  321. string m_deviceName;
  322. AudioSource m_audioSource;
  323. GameObject m_audioSourceObj;
  324. AudioStreamSender m_parent;
  325. public AudioStreamSourceMicrophone(AudioStreamSender parent) : base(parent)
  326. {
  327. int deviceIndex = parent.m_MicrophoneDeviceIndex;
  328. if (deviceIndex < 0 || Microphone.devices.Length <= deviceIndex)
  329. throw new ArgumentOutOfRangeException("deviceIndex", deviceIndex, "The deviceIndex is out of range");
  330. m_parent = parent;
  331. m_deviceIndex = deviceIndex;
  332. m_frequency = parent.m_frequency;
  333. m_autoRequestUserAuthorization = parent.m_AutoRequestUserAuthorization;
  334. }
  335. public override WaitForCreateTrack CreateTrack()
  336. {
  337. var instruction = new WaitForCreateTrack();
  338. m_parent.StartCoroutine(CreateTrackCoroutine(instruction));
  339. return instruction;
  340. }
  341. IEnumerator CreateTrackCoroutine(WaitForCreateTrack instruction)
  342. {
  343. if (m_autoRequestUserAuthorization)
  344. {
  345. AsyncOperation op = Application.RequestUserAuthorization(UserAuthorization.Microphone);
  346. yield return op;
  347. }
  348. if (!Application.HasUserAuthorization(UserAuthorization.Microphone))
  349. throw new InvalidOperationException("Call Application.RequestUserAuthorization before creating track with Microphone.");
  350. m_deviceName = Microphone.devices[m_deviceIndex];
  351. Microphone.GetDeviceCaps(m_deviceName, out int minFreq, out int maxFreq);
  352. var micClip = Microphone.Start(m_deviceName, true, 1, m_frequency);
  353. // set the latency to “0” samples before the audio starts to play.
  354. yield return new WaitUntil(() => Microphone.GetPosition(m_deviceName) > 0);
  355. m_audioSourceObj = new GameObject("Audio");
  356. m_audioSourceObj.hideFlags = HideFlags.HideInHierarchy;
  357. DontDestroyOnLoad(m_audioSourceObj);
  358. m_audioSource = m_audioSourceObj.AddComponent<AudioSource>();
  359. m_audioSource.clip = micClip;
  360. m_audioSource.loop = true;
  361. m_audioSource.Play();
  362. instruction.Done(new AudioStreamTrack(m_audioSource));
  363. }
  364. public override void Dispose()
  365. {
  366. if (m_audioSourceObj != null)
  367. {
  368. m_audioSource.Stop();
  369. var clip = m_audioSource.clip;
  370. if (clip != null)
  371. {
  372. Destroy(clip);
  373. }
  374. m_audioSource.clip = null;
  375. Destroy(m_audioSourceObj);
  376. m_audioSourceObj = null;
  377. m_audioSource = null;
  378. }
  379. if (Microphone.IsRecording(m_deviceName))
  380. Microphone.End(m_deviceName);
  381. GC.SuppressFinalize(this);
  382. }
  383. ~AudioStreamSourceMicrophone()
  384. {
  385. Dispose();
  386. }
  387. }
  388. class AudioStreamSourceAPIOnly : AudioStreamSourceImpl
  389. {
  390. AudioStreamTrack m_audioTrack;
  391. public AudioStreamSourceAPIOnly(AudioStreamSender parent) : base(parent)
  392. {
  393. }
  394. public override WaitForCreateTrack CreateTrack()
  395. {
  396. var instruction = new WaitForCreateTrack();
  397. m_audioTrack = new AudioStreamTrack();
  398. instruction.Done(new AudioStreamTrack());
  399. return instruction;
  400. }
  401. public void SetData(ref NativeArray<float> nativeArray, int channels, int sampleRate)
  402. {
  403. m_audioTrack?.SetData(ref nativeArray, channels, sampleRate);
  404. }
  405. public override void Dispose()
  406. {
  407. GC.SuppressFinalize(this);
  408. }
  409. ~AudioStreamSourceAPIOnly()
  410. {
  411. Dispose();
  412. }
  413. }
  414. }
  415. }