public void ParseMediaDescriptionUnitTest() { string testVector = @" m=audio 49230 RTP/AVP 96 97 98 a=rtpmap:96 L8/8000 a=rtpmap:97 L16/8000 a=rtpmap:98 L16/11025/2"; using (var md = new Media.Sdp.MediaDescription(testVector)) { System.Diagnostics.Debug.Assert(md.Lines.Count() == 4, "MediaDescription must have 4 lines"); //CLR not assert correctly with == .... //md.MediaDescriptionLine.ToString() == "m=audio 49230 RTP/AVP 96 97 98" System.Diagnostics.Debug.Assert(md.PayloadTypes.Count() == 3, "Could not read the Payload List"); System.Diagnostics.Debug.Assert(md.PayloadTypes.First() == 96, "Could not read the Payload List"); System.Diagnostics.Debug.Assert(md.PayloadTypes.ToArray()[1] == 97, "Could not read the Payload List"); System.Diagnostics.Debug.Assert(md.PayloadTypes.Last() == 98, "Could not read the Payload List"); System.Diagnostics.Debug.Assert(string.Compare(md.MediaDescriptionLine.ToString(), "m=audio 49230 RTP/AVP 96 97 98\r\n") == 0, "Did not handle Payload List Correct"); } }
public void CreateMediaDesciptionTest() { //RtpClient has the following property //Media.Rtp.RtpClient.AvpProfileIdentifier //I don't think it should be specified in the SDP Classes but I can figure out something else if desired. string profile = "RTP/AVP"; Media.Sdp.MediaType mediaType = Media.Sdp.MediaType.audio; int mediaPort = 15000; //Iterate all possible byte values (should do a seperate test for the list of values?) for (int mediaFormat = 0; mediaFormat <= 999; ++mediaFormat) { //Create a MediaDescription using (var mediaDescription = new Media.Sdp.MediaDescription(mediaType, mediaPort, profile, mediaFormat)) { System.Diagnostics.Debug.Assert(mediaDescription.MediaProtocol == profile, "Did not find MediaProtocol '" + profile + "'"); System.Diagnostics.Debug.Assert(mediaDescription.PayloadTypes.Count() == 1, "Found more then 1 payload type in the PayloadTypes List"); System.Diagnostics.Debug.Assert(mediaDescription.PayloadTypes.First() == mediaFormat, "Did not find correct MediaFormat"); System.Diagnostics.Debug.Assert(mediaDescription.ToString() == string.Format("m={0} {1} RTP/AVP {2}\r\n", mediaType, mediaPort, mediaFormat), "Did not output correct result"); } } }
//Naming is weird, this returns the logical 0 based index of the given description within the sessionDescription's property of the same type. //E.g. This index can be used in GetMediaDescription(index) public static int GetIndexFor(this SessionDescription sdp, MediaDescription md) { if (sdp == null || md == null) { return(-1); } return(sdp.m_MediaDescriptions.IndexOf(md)); }
public MediaDescription(MediaDescription other, bool shouldDispose = true) : base(shouldDispose) { if (Common.IDisposedExtensions.IsNullOrDisposed(other)) { throw new ArgumentNullException(nameof(other)); } MediaDescriptionLine = other.MediaDescriptionLine; foreach (Sdp.SessionDescriptionLine line in other.Lines) { Add(line); } }
//public SessionDescriptionLine GetLine(int index) //{ // //Some lines are backed by properties //} public void Add(MediaDescription mediaDescription, bool updateVersion = true) { if (UnderModification || mediaDescription == null) { return; } var token = BeginUpdate(); m_MediaDescriptions.Add(mediaDescription); EndUpdate(token, updateVersion); }
public bool Remove(MediaDescription mediaDescription, bool updateVersion = true) { if (UnderModification || mediaDescription == null) { return(false); } var token = BeginUpdate(); bool result = m_MediaDescriptions.Remove(mediaDescription); EndUpdate(token, updateVersion); return(result); }
public bool Equals(MediaDescription other) { return(Media.Common.Extensions.EnumerableExtensions.SequenceEquals(this, other)); //using (var one = other.GetEnumerator()) //{ // using (var two = GetEnumerator()) // { // while (one.MoveNext() && two.MoveNext()) // { // if (one.Current.Equals(two.Current).Equals(false)) return false; // } // return true; // } //} }
public static TimeDescription GetTimeDescription(this MediaDescription mediaDescription, SessionDescription sessionDescription) { if (mediaDescription == null || sessionDescription == null) { return(null); } //Get index of mediaDescription //Needs a better way to get the index of the media description int index = sessionDescription.GetIndexFor(mediaDescription); //Array.IndexOf(sessionDescription.MediaDescriptions.ToArray(), mediaDescription); if (index == -1) { return(null); } return(sessionDescription.GetTimeDescription(index)); }
public static TimeDescription GetTimeDescription(this MediaDescription mediaDescription, SessionDescription sessionDescription) { if (Common.IDisposedExtensions.IsNullOrDisposed(mediaDescription) || Common.IDisposedExtensions.IsNullOrDisposed(sessionDescription)) { return(null); } //Get index of mediaDescription //Needs a better way to get the index of the media description int index = sessionDescription.GetIndexFor(mediaDescription); //Array.IndexOf(sessionDescription.MediaDescriptions.ToArray(), mediaDescription); if (index == -1) { return(null); } return(sessionDescription.GetTimeDescription(index)); }
//Should have a date when or should return the date playable, which would then be used by another method to compare against a time. public static bool IsPlayable(this MediaDescription mediaDescription, SessionDescription sessionDescription) //, DateTime? check = null) ,TimeSpan within = TimeSpan.Zero { if (mediaDescription == null || sessionDescription == null) { return(false); } //Get index of mediaDesription //Check TimeDescription @ index. TimeDescription td = GetTimeDescription(mediaDescription, sessionDescription); if (td == null) { return(true); } //Unbound start and end ? if (td.IsPermanent) { return(true); } //Notes multiple calls to UtcNow... (avoid with a within parameter)? try { //Ensure not a bounded end and that the end time is less than now if (td.StopTime != 0 && td.NtpStopDateTime >= DateTime.UtcNow) { return(false); } //Ensure start time is not bounded and that the start time is greater than now if (td.StartTime != 0 && td.NtpStartDateTime > DateTime.UtcNow) { return(false); } //Check repeat times. //td.RepeatTimes; } catch { //Out of range values for conversion, assume true if end is unbounded if (td.StopTime != 0) { return(false); } } finally { td = null; } return(true); }
/// <summary> /// Parses the <see cref="MediaDescription.ControlLine"/> and if present /// </summary> /// <param name="mediaDescription"></param> /// <param name="source"></param> /// <returns></returns> public static Uri GetAbsoluteControlUri(this MediaDescription mediaDescription, Uri source, SessionDescription sessionDescription = null) { if (source == null) { throw new ArgumentNullException("source"); } if (mediaDescription == null) { return(source); } if (false == source.IsAbsoluteUri) { throw new InvalidOperationException("source.IsAbsoluteUri must be true."); } SessionDescriptionLine controlLine = mediaDescription.ControlLine; //If there is a control line in the SDP it contains the URI used to setup and control the media if (controlLine != null) { //Todo, make typed line for controlLine // GC: Rewrote this somewhat because it was destroying valid absolute URL provided by AXIS media server in the MediaDescription Control field. // This make break media servers which use a relative path. TODO: Test with other servers. string controlPart = ""; if (controlLine.Parts.Count() > 2) { foreach (string part in controlLine.Parts) { if (part == "control") { } else if (part == "rtsp") { controlPart += part + ":"; } else { controlPart += part; } } } else { controlPart = controlLine.Parts.Last(); } //If there is a controlPart in the controlLine if (false == string.IsNullOrWhiteSpace(controlPart)) { //Prepare the part // GC: Commented this out because it was destroying valid absolute URL provided by AXIS media server in the MediaDescription Control field. // This make break media servers which use a relative path. TODO: Test with other servers. //controlPart = controlPart.Split(Media.Sdp.SessionDescription.ColonSplit, 2, StringSplitOptions.RemoveEmptyEntries).Last(); //Create a uri Uri controlUri = new Uri(controlPart, UriKind.RelativeOrAbsolute); //Determine if its a Absolute Uri if (controlUri.IsAbsoluteUri) { return(controlUri); } //Return a new uri using the original string and the controlUri relative path. //Hopefully the direction of the braces matched.. //string.Join(source.OriginalString, controlUri.OriginalString); return(new Uri(source.OriginalString.EndsWith(SessionDescription.ForwardSlashString) ? source.OriginalString + controlUri.OriginalString : string.Join(SessionDescription.ForwardSlashString, source.OriginalString, controlUri.OriginalString))); //Todo, ensure that any parameters have also been restored... #region Explination //I wonder if Mr./(Dr) Fielding is happy... //Let source = //rtsp://alt1.v7.cache3.c.youtube.com/CigLENy73wIaHwmddh2T-s8niRMYDSANFEgGUgx1c2VyX3VwbG9hZHMM/0/0/0/1/video.3gp/trackID=0 //Call //return new Uri(source, controlUri); //Result = //rtsp://alt1.v7.cache3.c.youtube.com/CigLENy73wIaHwmddh2T-s8niRMYDSANFEgGUgx1c2VyX3VwbG9hZHMM/0/0/0/1/trackID=0 //Useless when the source doesn't end with '/', e.g. same problem with Uri constructor. //System.UriBuilder builder = new UriBuilder(source); //builder.Path += controlUri.ToString(); //"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov/trackID=1" #endregion } } //Try to take the session level control uri Uri sessionControlUri; //If there was a session description given and it supports aggregate media control then return that uri if (sessionDescription != null && sessionDescription.SupportsAggregateMediaControl(out sessionControlUri, source)) { return(sessionControlUri); } //There is no control line, just return the source. return(source); }
/// <summary> /// Parses the <see cref="MediaDescription.ControlLine"/> and if present /// </summary> /// <param name="mediaDescription"></param> /// <param name="source"></param> /// <returns></returns> public static Uri GetAbsoluteControlUri(this MediaDescription mediaDescription, Uri source, SessionDescription sessionDescription = null) { if (object.ReferenceEquals(source, null)) { throw new ArgumentNullException("source"); } if (Common.IDisposedExtensions.IsNullOrDisposed(mediaDescription)) { return(source); } if (source.IsAbsoluteUri.Equals(false)) { throw new InvalidOperationException("source.IsAbsoluteUri must be true."); } SessionDescriptionLine controlLine = mediaDescription.ControlLine; //If there is a control line in the SDP it contains the URI used to setup and control the media if (object.ReferenceEquals(controlLine, null).Equals(false)) { //Todo, make typed line for controlLine string controlPart = controlLine.Parts.Last(); //controlLine.Parts.Where(p => p.StartsWith(AttributeFields.Control)).FirstOrDefault(); //If there is a controlPart in the controlLine if (string.IsNullOrWhiteSpace(controlPart).Equals(false)) { //Prepare the part controlPart = controlPart.Split(Media.Sdp.SessionDescription.ColonSplit, 2, StringSplitOptions.RemoveEmptyEntries).Last(); //Create a uri Uri controlUri = new Uri(controlPart, UriKind.RelativeOrAbsolute); //Determine if its a Absolute Uri if (controlUri.IsAbsoluteUri) { return(controlUri); } //Return a new uri using the original string and the controlUri relative path. //Hopefully the direction of the braces matched.. //string.Join(source.OriginalString, controlUri.OriginalString); return(new Uri(source.OriginalString.EndsWith(SessionDescription.ForwardSlashString) ? source.OriginalString + controlUri.OriginalString : string.Join(SessionDescription.ForwardSlashString, source.OriginalString, controlUri.OriginalString))); //Todo, ensure that any parameters have also been restored... #region Explination //I wonder if Mr./(Dr) Fielding is happy... //Let source = //rtsp://alt1.v7.cache3.c.youtube.com/CigLENy73wIaHwmddh2T-s8niRMYDSANFEgGUgx1c2VyX3VwbG9hZHMM/0/0/0/1/video.3gp/trackID=0 //Call //return new Uri(source, controlUri); //Result = //rtsp://alt1.v7.cache3.c.youtube.com/CigLENy73wIaHwmddh2T-s8niRMYDSANFEgGUgx1c2VyX3VwbG9hZHMM/0/0/0/1/trackID=0 //Useless when the source doesn't end with '/', e.g. same problem with Uri constructor. //System.UriBuilder builder = new UriBuilder(source); //builder.Path += controlUri.ToString(); //"rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov/trackID=1" #endregion } } //Try to take the session level control uri Uri sessionControlUri; //If there was a session description given and it supports aggregate media control then return that uri if (Common.IDisposedExtensions.IsNullOrDisposed(sessionDescription).Equals(false) && sessionDescription.SupportsAggregateMediaControl(out sessionControlUri, source)) { return(sessionControlUri); } //There is no control line, just return the source. return(source); }
//Should have a date when or should return the date playable, which would then be used by another method to compare against a time. public static bool IsPlayable(this MediaDescription mediaDescription, SessionDescription sessionDescription) //, DateTime? check = null) ,TimeSpan within = TimeSpan.Zero { if (Common.IDisposedExtensions.IsNullOrDisposed(mediaDescription) || Common.IDisposedExtensions.IsNullOrDisposed(sessionDescription)) { return(false); } //Get index of mediaDesription //Check TimeDescription @ index. TimeDescription td = GetTimeDescription(mediaDescription, sessionDescription); //Assume true if (Common.IDisposedExtensions.IsNullOrDisposed(td)) { return(true); } //Unbound start and end ? if (td.IsPermanent) { return(true); } //Notes multiple calls to UtcNow... (avoid with a within parameter)? try { //Ensure not a bounded end and that the end time is less than now if (false.Equals(td.StopTime.Equals(0)) && td.NtpStopDateTime >= DateTime.UtcNow) { return(false); } //Ensure start time is not bounded and that the start time is greater than now if (false.Equals(td.StartTime.Equals(0)) && td.NtpStartDateTime > DateTime.UtcNow) { return(false); } //Check repeat times. //td.RepeatTimes; } //Todo, should not access property again during exception especially when out of range is potential. catch { //Out of range values for conversion, assume true if end is unbounded if (false.Equals(td.StopTime.Equals(0))) { return(false); } } finally { td = null; } return(true); }