public Sdp.SessionDescription CreateSessionDescription(int version = 0) { Sdp.SessionDescription sdp = new Sdp.SessionDescription(version); foreach (Sdp.MediaDescription md in MediaDescriptions.Values) { sdp.Add(new Sdp.MediaDescription(md)); } return(sdp); }
//Writes a .Sdp file public virtual void WriteDescription(IMedia stream, Sdp.SessionDescription sdp) { if (!IsArchiving(stream)) { return; } //Add lines with Alias info? System.IO.File.WriteAllText(BaseDirectory + '/' + stream.Id + '/' + "SessionDescription.sdp", sdp.ToString()); }
/// <summary> /// /// </summary> /// <param name="name"></param> /// <param name="sessionDescription"></param> public RtpSource(string name, Sdp.SessionDescription sessionDescription) : base(name, new System.Uri(string.Join(System.Uri.SchemeDelimiter, Rtp.RtpClient.RtpProtcolScheme, ((Sdp.Lines.SessionConnectionLine)sessionDescription.ConnectionLine).Host))) { if (IDisposedExtensions.IsNullOrDisposed(sessionDescription)) { throw new ExceptionExtensions.ArgumentNullOrDisposedException("sessionDescription", sessionDescription); } RtpClient = Rtp.RtpClient.FromSessionDescription(SessionDescription = sessionDescription); RtpClient.FrameChangedEventsEnabled = PerPacket == false; }
public RtpSource(string name, Sdp.SessionDescription sessionDescription) : base(name, new Uri(Rtp.RtpClient.RtpProtcolScheme + "://" + ((Sdp.Lines.SessionConnectionLine)sessionDescription.ConnectionLine).IPAddress)) { if (sessionDescription == null) { throw new ArgumentNullException("sessionDescription"); } RtpClient = Rtp.RtpClient.FromSessionDescription(SessionDescription = sessionDescription); RtpClient.FrameChangedEventsEnabled = PerPacket == false; }
//Todo, cleanup and allow existing Rtp and Rtcp socket. /// <summary> /// Will create a <see cref="RtpClient"/> based on the given parameters /// </summary> /// <param name="sessionDescription"></param> /// <param name="sharedMemory"></param> /// <param name="incomingEvents"></param> /// <param name="rtcpEnabled"></param> /// <returns></returns> public static RtpClient FromSessionDescription(Sdp.SessionDescription sessionDescription, Common.MemorySegment sharedMemory = null, bool incomingEvents = true, bool rtcpEnabled = true, System.Net.Sockets.Socket existingSocket = null, int?rtpPort = null, int?rtcpPort = null, int remoteSsrc = 0, int minimumSequentialRtpPackets = 2, bool connect = true, System.Action <System.Net.Sockets.Socket> configure = null) { if (Common.IDisposedExtensions.IsNullOrDisposed(sessionDescription)) { throw new System.ArgumentNullException("sessionDescription"); } Sdp.Lines.SessionConnectionLine connectionLine = new Sdp.Lines.SessionConnectionLine(sessionDescription.ConnectionLine); System.Net.IPAddress remoteIp = System.Net.IPAddress.Parse(connectionLine.Host), localIp; System.Net.NetworkInformation.NetworkInterface localInterface; //If the socket is NOT null and IS BOUND use the localIp of the same address family if (object.ReferenceEquals(existingSocket, null).Equals(false) && existingSocket.IsBound) { //If the socket is IP based if (existingSocket.LocalEndPoint is System.Net.IPEndPoint) { //Take the localIp from the LocalEndPoint localIp = (existingSocket.LocalEndPoint as System.Net.IPEndPoint).Address; } else { throw new System.NotSupportedException("Please create an issue for your use case."); } } else // There is no socket existing. { //If the remote address is the broadcast address or the remote address is multicast if (System.Net.IPAddress.Broadcast.Equals(remoteIp) || IPAddressExtensions.IsMulticast(remoteIp)) { //This interface should be the interface you plan on using for the Rtp communication localIp = SocketExtensions.GetFirstMulticastIPAddress(remoteIp.AddressFamily, out localInterface); } else { //This interface should be the interface you plan on using for the Rtp communication localIp = SocketExtensions.GetFirstUnicastIPAddress(remoteIp.AddressFamily, out localInterface); } } RtpClient client = new RtpClient(sharedMemory, incomingEvents); byte lastChannel = 0; //Todo, check for session level ssrc //if (remoteSsrc.Equals(0)) //{ // //Sdp.SessionDescriptionLine ssrcLine = sessionDescription.SsrcGroupLine; // SsrcLine @ the session level could imply Group //} //For each MediaDescription in the SessionDescription foreach (Media.Sdp.MediaDescription md in sessionDescription.MediaDescriptions) { //Make a RtpClient.TransportContext from the MediaDescription being parsed. TransportContext tc = TransportContext.FromMediaDescription(sessionDescription, lastChannel++, lastChannel++, md, rtcpEnabled, remoteSsrc, minimumSequentialRtpPackets, localIp, remoteIp, //The localIp and remoteIp rtpPort, rtcpPort, //The remote ports to receive data from connect, existingSocket, configure); //Try to add the context try { client.AddContext(tc); } catch (System.Exception ex) { TaggedExceptionExtensions.RaiseTaggedException(tc, "See Tag, Could not add the created TransportContext.", ex); } } //Return the participant return(client); }
/// <summary> /// Dynamically creates a Sdp.SessionDescription for the given SourceStream using the information already present and only re-writing the necessary values. /// </summary> /// <param name="stream">The source stream to create a SessionDescription for</param> /// <returns>The created SessionDescription</returns> internal Sdp.SessionDescription CreateSessionDescription(SourceMedia stream) { if (stream == null) throw new ArgumentNullException("stream"); //else if (SessionDescription != null) throw new NotImplementedException("There is already a m_SessionDescription for this session, updating is not implemented at this time"); string sessionId = Media.Ntp.NetworkTimeProtocol.DateTimeToNptTimestamp(DateTime.UtcNow).ToString(), sessionVersion = Media.Ntp.NetworkTimeProtocol.DateTimeToNptTimestamp(DateTime.UtcNow).ToString(); string originatorString = "ASTI-Media-Server " + sessionId + " " + sessionVersion + " IN " + (m_RtspSocket.AddressFamily == AddressFamily.InterNetworkV6 ? "IP6 " : "IP4 " ) + ((IPEndPoint)m_RtspSocket.LocalEndPoint).Address.ToString(); string sessionName = "ASTI-Streaming-Session-" + stream.Name; Sdp.SessionDescription sdp; RtpClient sourceClient; if (stream is RtpSource) { RtpSource rtpSource = stream as RtpSource; //Make the new SessionDescription sdp = new Sdp.SessionDescription(rtpSource.SessionDescription.ToString()); //Remove the old connection lines if they exist while(sdp.ConnectionLine != null) sdp.Remove(sdp.ConnectionLine, false); sourceClient = rtpSource.RtpClient; } else { sdp = new Sdp.SessionDescription(0); } //Change the DocumentVersion and update the name sdp.SessionName = sessionName; //Change the DocumentVersion and update the originator sdp.OriginatorAndSessionIdentifier = originatorString; //Type = broadcast //charset string protcol = RtspMessage.MessageIdentifier.ToLowerInvariant(), controlLineBase = "a=control:" + protcol + "://" + ((IPEndPoint)(m_RtspSocket.LocalEndPoint)).Address.ToString() + "/live/" + stream.Id; //check for rtspu later... //Find an existing control line Media.Sdp.SessionDescriptionLine controlLine = sdp.ControlLine; //If there was one remove it while (controlLine != null) { sdp.Remove(controlLine); controlLine = sdp.ControlLine; } //Determine if session level control line should be present //Rewrite a new connection line string addressString = LocalEndPoint.Address.ToString();// +"/127/2"; //int lastPort = Utility.FindOpenPort( stream.m_ForceTCP ? ProtocolType.Tcp : ProtocolType.Udp); //Indicate a port in the sdp, setup should also use this port, this should essentially reserve the port for the setup process... //if (!stream.m_ForceTCP) //addressString += "/127" +'/' + lastPort + 1; //else //addressString += + ((IPEndPoint)RemoteEndPoint).Port; //Check for the existing connectionLine Sdp.Lines.SessionConnectionLine connectionLine = sdp.ConnectionLine as Sdp.Lines.SessionConnectionLine; //Add the new line if needed if (connectionLine == null) sdp.ConnectionLine = connectionLine = new Sdp.Lines.SessionConnectionLine() { ConnectionAddress = addressString, ConnectionAddressType = m_RtspSocket.AddressFamily == AddressFamily.InterNetworkV6 ? Media.Sdp.Lines.SessionConnectionLine.IP6 : Media.Sdp.Lines.SessionConnectionLine.IP4, ConnectionNetworkType = Media.Sdp.Lines.SessionConnectionLine.InConnectionToken }; IEnumerable<Sdp.SessionDescriptionLine> bandwithLines; //Indicate that the server will not accept media as input for this session //Put the attribute in the Session Description, //Should check that its not already set? //sdp.Add(new Sdp.SessionDescriptionLine("a=recvonly")); //Remove any existing session range lines, don't upate the version while (sdp.RangeLine != null) sdp.Remove(sdp.RangeLine, false); //Todo add a Range line which shows the length of this media. //Iterate the source MediaDescriptions, could just create a new one with the fmt which contains the profile level information foreach (Sdp.MediaDescription md in sdp.MediaDescriptions) { //Find a control line controlLine = md.ControlLine; //Rewrite it if present to reflect the appropriate MediaDescription while (controlLine != null) { md.Remove(controlLine); controlLine = md.ControlLine; } //Remove old bandwith lines bandwithLines = md.BandwidthLines; //Remove existing bandwidth information, should check for AS if(stream.m_DisableQOS) foreach (Sdp.SessionDescriptionLine line in bandwithLines) md.Remove(line); //Remove all other alternate information //Should probably only remove certain ones. foreach (Sdp.SessionDescriptionLine line in md.Lines.Where(l => l.Parts.Any(p => p.Contains("alt"))).ToArray()) md.Remove(line); //Add a control line for the MedaiDescription (which is `rtsp://./Id/audio` (video etc) //Should be a TrackId and not the media type to allow more then one media type to be controlled. //e.g. Two audio streams or text streams is valid. md.Add(new Sdp.SessionDescriptionLine("a=control:" + "/live/" + stream.Id + '/' + md.MediaType)); //Add the connection line for the media //md.Add(connectionLine); //Should check for Timing Info and update for playing streams if (stream.m_DisableQOS) { md.Add(Sdp.Lines.SessionBandwidthLine.DisabledSendLine); md.Add(Sdp.Lines.SessionBandwidthLine.DisabledReceiveLine); md.Add(Sdp.Lines.SessionBandwidthLine.DisabledApplicationSpecificLine); } //else //{ // //ToDo use whatever values are defined for the session's bandwidth atp // md.Add(new Sdp.SessionDescriptionLine("b=RS:140")); // md.Add(new Sdp.SessionDescriptionLine("b=RR:140")); // md.Add(new Sdp.SessionDescriptionLine("b=AS:0")); //Determine if AS needs to be forwarded //} //Should actually reflect outgoing port for this session md.MediaPort = 0; //Determine if attached and set the MediaPort. if (m_RtpClient != null) { var context = m_RtpClient.GetContextForMediaDescription(md); if (context != null) md.MediaPort = ((IPEndPoint)context.RemoteRtp).Port; //Set any other variables which may be been changed in the session } #region Independent TCP //if (!stream.m_ForceTCP) //{ // //md.MediaPort = lastPort; // //lastPort += 2; //} //else //{ // //VLC `Blows up` when this happens // //bad SDP "m=" line: m=audio 40563 TCP/RTP/AVP 96 // //md.MediaProtocol = "TCP/RTP/AVP"; // //fmt should be the same // //This mainly implies that stand-alone RTP over TCP is occuring anyway. // //Since this code supports the RtspServer this is fine for now. // //The RtpClient also deals with the framing from RTSP when used in conjunction with so.. // //This needs to be addressed in the RtpClient which allows currently allows Rtp and Rtcp to be duplexed in TCP and UDP // //but does not handle the case of TCP when a sender wants to connect with 2 seperate TCP sockets as per RFC4571. // //a=setup:passive // //a=connection:new // //The RtpClient would then need to have a RtcpSocket ready on the 'standby' just in case the remote end point connected and began sending the data. // //The other way to hanle this would be use only a single socket in both cases and change the remote endpoint = 0.... and decypher the data based on the end point... e.g. the port. //} #endregion //Verify Timing lines. //Lines should have a startTime equal the Uptime of the stream //Lines should have a stopTime equal to the EndTime of the stream. } //Top level stream control line (Should only be added if Aggregate Control of the stream is allowed. //sdp.Add(new Sdp.SessionDescriptionLine(controlLineBase)); return sdp; }
//SourceStream Implementation public override void Start() { if (m_RtpClient != null) { return; } //Create a RtpClient so events can be sourced from the Server to many clients without this Client knowing about all participants //If this class was used to send directly to one person it would be setup with the recievers address m_RtpClient = new Rtp.RtpClient(); SessionDescription = new Sdp.SessionDescription(0, "v√ƒ", Name); SessionDescription.Add(new Sdp.Lines.SessionConnectionLine() { ConnectionNetworkType = Sdp.Lines.SessionConnectionLine.InConnectionToken, ConnectionAddressType = Sdp.SessionDescription.WildcardString, ConnectionAddress = System.Net.IPAddress.Any.ToString() }); //Add a MediaDescription to our Sdp on any available port for RTP/AVP Transport using the RtpJpegPayloadType SessionDescription.Add(new Sdp.MediaDescription(Sdp.MediaType.video, 0, Rtp.RtpClient.RtpAvpProfileIdentifier, RFC2435Media.RFC2435Frame.RtpJpegPayloadType)); //Indicate control to each media description contained SessionDescription.Add(new Sdp.SessionDescriptionLine("a=control:*")); //Ensure the session members know they can only receive SessionDescription.Add(new Sdp.SessionDescriptionLine("a=sendonly")); //recvonly? //that this a broadcast. SessionDescription.Add(new Sdp.SessionDescriptionLine("a=type:broadcast")); //Add a Interleave (We are not sending Rtcp Packets becaues the Server is doing that) We would use that if we wanted to use this ImageSteam without the server. //See the notes about having a Dictionary to support various tracks m_RtpClient.TryAddContext(new Rtp.RtpClient.TransportContext(0, 1, sourceId, SessionDescription.MediaDescriptions.First(), false, 0)); //Add the control line SessionDescription.MediaDescriptions.First().Add(new Sdp.SessionDescriptionLine("a=control:trackID=1")); //Add the line with the clock rate in ms, obtained by TimeSpan.TicksPerMillisecond * clockRate //Make the thread m_RtpClient.m_WorkerThread = new System.Threading.Thread(SendPackets); m_RtpClient.m_WorkerThread.TrySetApartmentState(System.Threading.ApartmentState.MTA); //m_RtpClient.m_WorkerThread.IsBackground = true; //m_RtpClient.m_WorkerThread.Priority = System.Threading.ThreadPriority.BelowNormal; m_RtpClient.m_WorkerThread.Name = "SourceStream-" + Id; //If we are watching and there are already files in the directory then add them to the Queue if (m_Watcher != null && !string.IsNullOrWhiteSpace(base.Source.LocalPath) && System.IO.Directory.Exists(base.Source.LocalPath)) { foreach (string file in System.IO.Directory.GetFiles(base.Source.LocalPath)) { if (false == SupportedImageFormats.Any(ext => file.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) { continue; } } //If we have not been stopped already if (/*State != StreamState.Started && */ m_RtpClient.m_WorkerThread != null) { //Only ready after all pictures are in the queue Ready = true; m_RtpClient.m_WorkerThread.Start(); } } else { //We are ready Ready = true; m_RtpClient.m_WorkerThread.Start(); } base.Start(); }