using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Unity.WebRTC; using UnityEngine; namespace Unity.RenderStreaming { /// /// /// public abstract class StreamSenderBase : MonoBehaviour, IStreamSender { internal class WaitForCreateTrack : CustomYieldInstruction { public MediaStreamTrack Track { get { return m_track; } } MediaStreamTrack m_track; bool m_keepWaiting = true; public override bool keepWaiting { get { return m_keepWaiting; } } public WaitForCreateTrack() { } public void Done(MediaStreamTrack track) { m_track = track; m_keepWaiting = false; } } internal Coroutine StartCoroutineWithCallback(T coroutine, Action callback) where T : IEnumerator { if (coroutine == null) throw new ArgumentNullException("coroutine"); if (callback == null) throw new ArgumentNullException("callback"); return StartCoroutine(_Coroutine(coroutine, callback)); } internal IEnumerator _Coroutine(T coroutine, Action callback) where T : IEnumerator { yield return StartCoroutine(coroutine); callback(coroutine); } /// /// /// public IReadOnlyDictionary Transceivers => m_transceivers; /// /// /// public OnStartedStreamHandler OnStartedStream { get; set; } /// /// /// public OnStoppedStreamHandler OnStoppedStream { get; set; } /// /// /// /// internal abstract WaitForCreateTrack CreateTrack(); internal virtual void ReplaceTrack(MediaStreamTrack newTrack) { if (newTrack == null) throw new ArgumentNullException("track", "This argument must be not null."); if (m_track == newTrack) throw new ArgumentException("track", "The value of this argument has already been set."); /// todo:: If not disposing the old track here, the app will crash. /// This problem is caused by the MediaStreamTrack when it is destroyed on the thread other than the main thread. m_track?.Dispose(); m_track = newTrack; foreach (var transceiver in Transceivers.Values) { transceiver.Sender.ReplaceTrack(m_track); } } internal void SetTrack(MediaStreamTrack newTrack) { if (newTrack == null) throw new ArgumentNullException("track", "This argument must be not null."); if (m_track != null) throw new InvalidOperationException("Track is not null. Use ReplaceTrack method."); m_track = newTrack; } private MediaStreamTrack m_track; private Dictionary m_transceivers = new Dictionary(); /// /// /// public MediaStreamTrack Track => m_track; /// /// /// public bool isPlaying { get { foreach (var transceiver in Transceivers.Values) { if (string.IsNullOrEmpty(transceiver.Mid)) continue; if (transceiver.Sender.Track.ReadyState == TrackState.Ended) continue; return true; } return false; } } private protected virtual void OnDestroy() { m_track?.Dispose(); m_track = null; } private protected virtual void OnEnable() { if (m_track?.ReadyState == TrackState.Live) { m_track.Enabled = true; } } private protected virtual void OnDisable() { if (m_track?.ReadyState == TrackState.Live) { m_track.Enabled = false; } } /// /// /// /// /// public virtual void SetTransceiver(string connectionId, RTCRtpTransceiver transceiver) { if (connectionId == null) throw new ArgumentNullException("connectionId is null"); if (transceiver == null) { m_transceivers.Remove(connectionId); OnStoppedStream?.Invoke(connectionId); if (!m_transceivers.Any()) { m_track.Dispose(); m_track = null; } } else { m_transceivers.Add(connectionId, transceiver); OnStartedStream?.Invoke(connectionId); } } } }