using System; using System.Collections.Generic; using UnityEngine; using Unity.WebRTC; namespace Unity.RenderStreaming { /// /// /// [Serializable] public class VideoCodecInfo : IEquatable { static readonly string KeyCodecImplementation = "implementation_name"; [SerializeField] private string m_MimeType; [SerializeField] private string m_SdpFmtpLine; readonly Dictionary m_parameters = new Dictionary(); /// /// /// public string name { get { return m_MimeType.GetCodecName(); } } /// /// /// public string mimeType { get { return m_MimeType; } } /// /// /// public string codecImplementation { get { return parameters[KeyCodecImplementation]; } } /// /// /// public string sdpFmtpLine { get { return m_SdpFmtpLine; } } /// /// /// /// /// public bool Equals(VideoCodecInfo other) { if (other == null) return false; return this.mimeType == other.mimeType && this.sdpFmtpLine == other.sdpFmtpLine; } /// /// /// /// /// public override bool Equals(object obj) { return obj is VideoCodecInfo ? Equals((VideoCodecInfo)obj) : base.Equals(obj); } /// /// /// /// public override int GetHashCode() { return new { mimeType, sdpFmtpLine }.GetHashCode(); } /// /// /// /// /// /// public static bool operator ==(VideoCodecInfo left, VideoCodecInfo right) { if (ReferenceEquals(left, null)) { return ReferenceEquals(left, null); } else { return left.Equals(right); } } /// /// /// /// /// /// public static bool operator !=(VideoCodecInfo left, VideoCodecInfo right) { return !(left == right); } protected Dictionary parameters { get { if (m_parameters != null) return m_parameters; if (string.IsNullOrEmpty(m_SdpFmtpLine)) return null; string[] subs = m_SdpFmtpLine.Split(';'); foreach (string sub in subs) { string[] pair = sub.Split('='); m_parameters.Add(pair[0], pair[1]); } return m_parameters; } } static internal VideoCodecInfo Create(RTCRtpCodecCapability caps) { switch(caps.mimeType) { case "video/H264": return new H264CodecInfo(caps); case "video/VP9": return new VP9CodecInfo(caps); case "video/AV1": return new AV1CodecInfo(caps); default: return new VideoCodecInfo(caps); } } internal bool Equals(RTCRtpCodecCapability other) { if (other == null) return false; return this.mimeType == other.mimeType && this.sdpFmtpLine == other.sdpFmtpLine; } internal VideoCodecInfo(RTCRtpCodecCapability caps) { m_MimeType = caps.mimeType; m_SdpFmtpLine = caps.sdpFmtpLine; string[] subs = m_SdpFmtpLine.Split(';'); foreach(string sub in subs) { string[] pair = sub.Split('='); parameters.Add(pair[0], pair[1]); } } } /// /// /// public enum VP9Profile { /// /// /// Profile0 = 0, /// /// /// Profile1 = 1, /// /// /// Profile2 = 2, /// /// /// Profile3 = 3, } /// /// /// public class VP9CodecInfo : VideoCodecInfo { const string KeyProfileId = "profile-id"; /// /// /// public VP9Profile? profile { get { if(parameters.TryGetValue(KeyProfileId, out var value)) { return (VP9Profile)Enum.ToObject(typeof(VP9Profile), Convert.ToInt32(value)); } return null; } } internal VP9CodecInfo(RTCRtpCodecCapability caps) : base(caps) { } } /// /// /// public enum H264Profile { /// /// Constrained Baseline Profile. /// ConstrainedBaseline = 0x42e0, /// /// Baseline Profile. /// Baseline = 0x4200, /// /// Main Profile. /// Main = 0x4d00, /// /// Constrained High Profile. /// ConstrainedHigh = 0x640c, /// /// High Profile. /// High = 0x6400, } /// /// /// public class H264CodecInfo : VideoCodecInfo { const string KeyProfileLevelId = "profile-level-id"; /// /// /// public H264Profile profile { get { return (H264Profile)Enum.ToObject(typeof(H264Profile), Convert.ToInt32(parameters[KeyProfileLevelId], 16) >> 8); } } /// /// /// public int level { get { return Convert.ToInt32(parameters[KeyProfileLevelId], 16) & 0xFF; } } internal H264CodecInfo(RTCRtpCodecCapability caps) : base(caps) { } } /// /// /// public enum AV1Profile { /// /// /// Profile0 = 0, /// /// /// Profile1 = 1, /// /// /// Profile2 = 2, } /// /// /// public class AV1CodecInfo : VideoCodecInfo { const string KeyProfile = "profile"; /// /// /// public AV1Profile profile { get { if (parameters.TryGetValue(KeyProfile, out var value)) { return (AV1Profile)Enum.ToObject(typeof(AV1Profile), Convert.ToInt32(value)); } // If the parameter is not present, it MUST be inferred to be 0 (“Main” profile). // https://aomediacodec.github.io/av1-rtp-spec/#72-sdp-parameters return AV1Profile.Profile0; } } internal AV1CodecInfo(RTCRtpCodecCapability caps) : base(caps) { } } }