using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Unity.WebRTC; using UnityEngine; namespace Unity.RenderStreaming { /// /// Enum representing the video render mode. /// public enum VideoRenderMode { /// /// Render to a RenderTexture. /// RenderTexture, /// /// API only, no rendering. /// APIOnly, } /// /// Component for receiving video streams. /// [AddComponentMenu("Render Streaming/Video Stream Receiver")] public class VideoStreamReceiver : StreamReceiverBase { internal const string CodecPropertyName = nameof(m_Codec); internal const string RenderModePropertyName = nameof(m_RenderMode); internal const string TargetTexturePropertyName = nameof(m_TargetTexture); /// /// Delegate for updating the received texture. /// /// The received texture. public delegate void OnUpdateReceiveTextureHandler(Texture receiveTexture); /// /// Event triggered when the received texture is updated. /// public OnUpdateReceiveTextureHandler OnUpdateReceiveTexture; [SerializeField, Codec] private VideoCodecInfo m_Codec; [SerializeField] private VideoRenderMode m_RenderMode; [SerializeField] private RenderTexture m_TargetTexture; /// /// Gets the codec information for the video stream. /// public VideoCodecInfo codec { get { return m_Codec; } } public VideoRenderMode renderMode { get { return m_RenderMode; } set { m_RenderMode = value; } } /// /// The width of the received video stream. /// public int width => m_texture.width; /// /// The height of the received video stream. /// public int height => m_texture.height; /// /// The texture of the received video stream. /// public Texture texture => m_texture; /// /// The target RenderTexture. /// public RenderTexture targetTexture { get { return m_TargetTexture; } set { m_TargetTexture = value; } } private Texture m_texture; private Coroutine m_coroutine; /// /// Gets the available video codecs. /// /// /// var codecs = VideoStreamSender.GetAvailableCodecs(); /// foreach (var codec in codecs) /// Debug.Log(codec.name); /// /// /// A list of available codecs. public static IEnumerable GetAvailableCodecs() { string[] excludeCodecMimeType = { "video/red", "video/ulpfec", "video/rtx", "video/flexfec-03" }; var capabilities = RTCRtpReceiver.GetCapabilities(TrackKind.Video); return capabilities.codecs.Where(codec => !excludeCodecMimeType.Contains(codec.mimeType)).Select(codec => VideoCodecInfo.Create(codec)); } /// /// Sets the codec for the video stream. /// /// /// /// x.mimeType.Contains("VP9")); /// videoStreamReceiver.SetCodec(codec); /// ]]> /// /// /// The codec information to set. /// Thrown if the transceiver is streaming or the track has ended. public void SetCodec(VideoCodecInfo codec) { m_Codec = codec; if (Transceiver == null) return; if (!string.IsNullOrEmpty(Transceiver.Mid)) throw new InvalidOperationException("Transceiver is streaming. This operation is invalid during the track is in use."); if (Transceiver.Sender.Track.ReadyState == TrackState.Ended) throw new InvalidOperationException("Track has already been ended."); var codecs = new VideoCodecInfo[] { m_Codec }; RTCErrorType error = Transceiver.SetCodecPreferences(SelectCodecCapabilities(codecs).ToArray()); if (error != RTCErrorType.None) throw new InvalidOperationException($"Set codec is failed. errorCode={error}"); } internal IEnumerable SelectCodecCapabilities(IEnumerable codecs) { return RTCRtpReceiver.GetCapabilities(TrackKind.Video).SelectCodecCapabilities(codecs); } private protected virtual void Awake() { OnStartedStream += StartedStream; OnStoppedStream += StoppedStream; } private void StartedStream(string connectionId) { if (Track is VideoStreamTrack videoTrack) { videoTrack.OnVideoReceived += texture => { m_texture = texture; OnUpdateReceiveTexture?.Invoke(m_texture); }; } m_coroutine = StartCoroutine(Render()); } private void StoppedStream(string connectionId) { m_texture = null; OnUpdateReceiveTexture?.Invoke(m_texture); if (m_coroutine != null) { StopCoroutine(m_coroutine); m_coroutine = null; } } private IEnumerator Render() { while (true) { yield return new WaitForEndOfFrame(); if (m_RenderMode != VideoRenderMode.RenderTexture || m_texture == null || m_TargetTexture == null) continue; Graphics.Blit(m_texture, m_TargetTexture); } } } }