public override void ConnectionOpened(RtmpConnection connection) { base.ConnectionOpened(connection); if (connection.Context.Mode == RtmpMode.Server) { connection.StartWaitForHandshake(); } }
public RtmptRequest(RtmpConnection connection, string url, string protocol, string httpMethod, Hashtable headers) { _connection = connection; _url = url; _protocol = protocol; _httpMethod = httpMethod; _headers = headers; }
public override void MessageSent(RtmpConnection connection, object message) { base.MessageSent(connection, message); RtmpPacket sent = message as RtmpPacket; int channelId = sent.Header.ChannelId; IClientStream stream = null; if( connection is IStreamCapableConnection ) stream = (connection as IStreamCapableConnection).GetStreamByChannelId(channelId); // XXX we'd better use new event model for notification if (stream != null && (stream is PlaylistSubscriberStream)) { (stream as PlaylistSubscriberStream).Written(sent.Message); } }
protected override void OnSharedObject(RtmpConnection connection, RtmpChannel channel, RtmpHeader header, SharedObjectMessage message) { ISharedObject so = null; string name = message.Name; IScope scope = connection.Scope; bool persistent = message.IsPersistent; if(scope == null) { // The scope already has been deleted. SendSOCreationFailed(connection, name, persistent); return; } ISharedObjectService sharedObjectService = ScopeUtils.GetScopeService(scope, typeof(ISharedObjectService)) as ISharedObjectService; if (!sharedObjectService.HasSharedObject(scope, name)) { ISharedObjectSecurityService securityService = ScopeUtils.GetScopeService(scope, typeof(ISharedObjectSecurityService)) as ISharedObjectSecurityService; if (securityService != null) { // Check handlers to see if creation is allowed IEnumerator enumerator = securityService.GetSharedObjectSecurity(); while(enumerator.MoveNext()) { ISharedObjectSecurity handler = enumerator.Current as ISharedObjectSecurity; if (!handler.IsCreationAllowed(scope, name, persistent)) { SendSOCreationFailed(connection, name, persistent); return; } } } if (!sharedObjectService.CreateSharedObject(scope, name, persistent)) { SendSOCreationFailed(connection, name, persistent); return; } } so = sharedObjectService.GetSharedObject(scope, name); if (so.IsPersistentObject != persistent) { log.Debug(string.Format("Shared object '{0}' persistence mismatch", name)); SendSOPersistenceMismatch(connection, name, persistent); return; } so.DispatchEvent(message); }
protected override void OnInvoke(RtmpConnection connection, RtmpChannel channel, RtmpHeader header, Notify invoke) { IServiceCall serviceCall = invoke.ServiceCall; // If it's a callback for server remote call then pass it over to callbacks handler // and return if(serviceCall.ServiceMethodName.Equals("_result") || serviceCall.ServiceMethodName.Equals("_error")) { HandlePendingCallResult(connection, invoke); return; } bool disconnectOnReturn = false; string action = null; if (serviceCall.ServiceName == null) { action = serviceCall.ServiceMethodName; switch (action) { case ACTION_CONNECT: { if (!connection.IsConnected) { IDictionary parameters = invoke.ConnectionParameters; string host = null; if( parameters.Contains("tcUrl") ) host = GetHostname(parameters["tcUrl"] as string); if (host != null && host.IndexOf(":") != -1) { // Remove default port from connection string host = host.Substring(0, host.IndexOf(":")); } string app = parameters["app"] as string; string path = parameters["app"] as string; // App name as path, but without query string if there is one if (path != null && path.IndexOf("?") != -1) { int idx = path.IndexOf("?"); parameters["queryString"] = path.Substring(idx); path = path.Substring(0, idx); } parameters["path"] = path; connection.Setup(host, path, parameters); try { //IGlobalScope global = this.Endpoint.LookupGlobal(host, path); IGlobalScope global = this.Endpoint.GetMessageBroker().GlobalScope; if (global == null) { serviceCall.Status = Call.STATUS_SERVICE_NOT_FOUND; if (serviceCall is IPendingServiceCall) { StatusASO status = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_INVALID_APPLICATION, connection.ObjectEncoding); status.description = "No global scope on this server."; (serviceCall as IPendingServiceCall).Result = status; } log.Info(string.Format("No application scope found for {0} on host {1}. Misspelled or missing application folder?", path, host)); disconnectOnReturn = true; } else { IScopeContext context = global.Context; IScope scope = null; try { scope = context.ResolveScope(global, path); } catch (ScopeNotFoundException /*exception*/) { if (log.IsErrorEnabled) log.Error(__Res.GetString(__Res.Scope_NotFound, path)); serviceCall.Status = Call.STATUS_SERVICE_NOT_FOUND; if (serviceCall is IPendingServiceCall) { StatusASO status = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_REJECTED, connection.ObjectEncoding); status.description = "No scope \"" + path + "\" on this server."; (serviceCall as IPendingServiceCall).Result = status; } disconnectOnReturn = true; } catch (ScopeShuttingDownException) { serviceCall.Status = Call.STATUS_APP_SHUTTING_DOWN; if (serviceCall is IPendingServiceCall) { StatusASO status = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_APPSHUTDOWN, connection.ObjectEncoding); status.description = "Application at \"" + path + "\" is currently shutting down."; (serviceCall as IPendingServiceCall).Result = status; } log.Info(string.Format("Application at {0} currently shutting down on {1}", path, host)); disconnectOnReturn = true; } if (scope != null) { if (log.IsInfoEnabled) log.Info(__Res.GetString(__Res.Scope_Connect, scope.Name)); bool okayToConnect; try { //The only way to differentiate NetConnection.connect() and Consumer.subscribe() seems to be the app name if (app == string.Empty) { connection.SetIsFlexClient(true); okayToConnect = connection.Connect(scope, serviceCall.Arguments); if (okayToConnect) { if (serviceCall.Arguments != null && serviceCall.Arguments.Length >= 3) { string credentials = serviceCall.Arguments[2] as string; if (credentials != null && credentials != string.Empty) { MessageBroker messageBroker = this.Endpoint.GetMessageBroker(); AuthenticationService authenticationService = messageBroker.GetService(AuthenticationService.ServiceId) as AuthenticationService; authenticationService.Authenticate(credentials); } } //FDS 2.0.1 fds.swc if (serviceCall.Arguments != null && serviceCall.Arguments.Length == 1) { string credentials = serviceCall.Arguments[0] as string; if (credentials != null && credentials != string.Empty) { MessageBroker messageBroker = this.Endpoint.GetMessageBroker(); AuthenticationService authenticationService = messageBroker.GetService(AuthenticationService.ServiceId) as AuthenticationService; authenticationService.Authenticate(credentials); } } } } else { connection.SetIsFlexClient(false); okayToConnect = connection.Connect(scope, serviceCall.Arguments); } if (okayToConnect) { if (log.IsDebugEnabled) log.Debug("Connected RtmpClient: " + connection.Client.Id); serviceCall.Status = Call.STATUS_SUCCESS_RESULT; if (serviceCall is IPendingServiceCall) { StatusASO statusASO = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_SUCCESS, connection.ObjectEncoding); statusASO.Add("id", connection.Client.Id); (serviceCall as IPendingServiceCall).Result = statusASO; } // Measure initial roundtrip time after connecting connection.GetChannel((byte)2).Write(new Ping(Ping.StreamBegin, 0, -1)); connection.StartRoundTripMeasurement(); } else { if (log.IsDebugEnabled) log.Debug("Connect failed"); serviceCall.Status = Call.STATUS_ACCESS_DENIED; if (serviceCall is IPendingServiceCall) (serviceCall as IPendingServiceCall).Result = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_REJECTED, connection.ObjectEncoding); disconnectOnReturn = true; } } catch (ClientRejectedException rejected) { if (log.IsDebugEnabled) log.Debug("Connect rejected"); serviceCall.Status = Call.STATUS_ACCESS_DENIED; if (serviceCall is IPendingServiceCall) { StatusASO statusASO = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_REJECTED, connection.ObjectEncoding); statusASO.Application = rejected.Reason; (serviceCall as IPendingServiceCall).Result = statusASO; } disconnectOnReturn = true; } } } } catch (Exception ex) { if (log.IsErrorEnabled) log.Error("Error connecting", ex); serviceCall.Status = Call.STATUS_GENERAL_EXCEPTION; if (serviceCall is IPendingServiceCall) (serviceCall as IPendingServiceCall).Result = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_FAILED, connection.ObjectEncoding); disconnectOnReturn = true; } } else { // Service calls, must be connected. InvokeCall(connection, serviceCall); } } break; case ACTION_DISCONNECT: connection.Close(); break; case ACTION_CREATE_STREAM: case ACTION_DELETE_STREAM: case ACTION_RELEASE_STREAM: case ACTION_PUBLISH: case ACTION_PLAY: case ACTION_SEEK: case ACTION_PAUSE: case ACTION_CLOSE_STREAM: case ACTION_RECEIVE_VIDEO: case ACTION_RECEIVE_AUDIO: { IStreamService streamService = ScopeUtils.GetScopeService(connection.Scope, typeof(IStreamService)) as IStreamService; StatusASO status = null; try { if (!InvokeCall(connection, serviceCall, streamService)) { status = StatusASO.GetStatusObject(StatusASO.NS_INVALID_ARGUMENT, connection.ObjectEncoding); status.description = "Failed to " + action + " (stream ID: " + header.StreamId + ")"; } } catch (Exception ex) { log.Error("Error while invoking " + action + " on stream service.", ex); status = StatusASO.GetStatusObject(StatusASO.NS_FAILED, connection.ObjectEncoding); status.description = "Error while invoking " + action + " (stream ID: " + header.StreamId + ")"; status.details = ex.Message; } if (status != null) channel.SendStatus(status); } break; default: if (connection.IsConnected) InvokeCall(connection, serviceCall); else { // Warn user attemps to call service without being connected if (log.IsWarnEnabled) log.Warn("Not connected, closing connection"); connection.Close(); } break; } } /* if(invoke is FlexInvoke) { FlexInvoke reply = new FlexInvoke(); reply.InvokeId = invoke.InvokeId; reply.SetResponseSuccess(); //TODO if( serviceCall is IPendingServiceCall ) { IPendingServiceCall pendingCall = (IPendingServiceCall)serviceCall; reply.Response = pendingCall.Result; } channel.Write(reply); } else if(invoke is Invoke) */ if (invoke is Invoke) { if((header.StreamId != 0) && (serviceCall.Status == Call.STATUS_SUCCESS_VOID || serviceCall.Status == Call.STATUS_SUCCESS_NULL)) { if (log.IsDebugEnabled) log.Debug("Method does not have return value, do not reply"); return; } // The client expects a result for the method call. Invoke reply = new Invoke(); reply.ServiceCall = serviceCall; reply.InvokeId = invoke.InvokeId; //sending reply channel.Write(reply); } if (disconnectOnReturn) { connection.Close(); } if (action == ACTION_CONNECT) { connection.Context.ObjectEncoding = connection.ObjectEncoding; } }
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="source"></param> /// <param name="streamBytesRead"></param> protected void OnStreamBytesRead(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, BytesRead streamBytesRead) { connection.ReceivedBytesRead(streamBytesRead.Bytes); }
public static RtmptRequest DecodeBuffer(RtmpConnection connection, ByteBuffer stream) { RtmpContext context = connection.Context; int position = (int)stream.Position; try { BufferStreamReader sr = new BufferStreamReader(stream); string request = sr.ReadLine(); string[] tokens = request.Split(new char[] { ' ' }); string method = tokens[0]; string url = tokens[1]; // Decode all encoded parts of the URL using the built in URI processing class int i = 0; while ((i = url.IndexOf("%", i)) != -1) { url = url.Substring(0, i) + Uri.HexUnescape(url, ref i) + url.Substring(i); } // Lets just make sure we are using HTTP, thats about all I care about string protocol = tokens[2];// "HTTP/" //Read headers Hashtable headers = new Hashtable(); string line; string name = null; while ((line = sr.ReadLine()) != null && line != string.Empty) { // If the value begins with a space or a hard tab then this // is an extension of the value of the previous header and // should be appended if (name != null && Char.IsWhiteSpace(line[0])) { headers[name] += line; continue; } // Headers consist of [NAME]: [VALUE] + possible extension lines int firstColon = line.IndexOf(":"); if (firstColon != -1) { name = line.Substring(0, firstColon); string value = line.Substring(firstColon + 1).Trim(); headers[name] = value; } else { //400, "Bad header: " + line break; } } RtmptRequest rtmptRequest = new RtmptRequest(connection, url, protocol, method, headers); if (stream.Remaining == rtmptRequest.ContentLength) { stream.Compact(); rtmptRequest.Data = ByteBuffer.Wrap(stream.ToArray()); stream.Flip(); return rtmptRequest; } else { // Move the position back to the start stream.Position = position; } } catch { // Move the position back to the start stream.Position = position; throw; } return null; }
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="source"></param> /// <param name="ping"></param> protected abstract void OnPing(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, Ping ping);
/// <summary> /// Remoting call invocation handler. /// </summary> /// <param name="connection">RTMP connection.</param> /// <param name="serviceCall">Service call.</param> /// <param name="service">Server-side service object.</param> /// <returns><code>true</code> if the call was performed, otherwise <code>false</code>.</returns> private static bool InvokeCall(RtmpConnection connection, IServiceCall serviceCall, object service) { IScope scope = connection.Scope; IScopeContext context = scope.Context; if (log.IsDebugEnabled) { log.Debug("Scope: " + scope); log.Debug("Service: " + service); log.Debug("Context: " + context); } return context.ServiceInvoker.Invoke(serviceCall, service); }
protected override void OnFlexInvoke(RtmpConnection connection, RtmpChannel channel, RtmpHeader header, FlexInvoke invoke) { IMessage message = null; if (invoke.ServiceCall.Arguments != null && invoke.ServiceCall.Arguments.Length > 0) message = invoke.ServiceCall.Arguments[0] as IMessage; if( message != null ) { MessageBroker messageBroker = this.Endpoint.GetMessageBroker(); if( message.clientId == null ) { message.clientId = Guid.NewGuid().ToString("D"); /* if( !(message is CommandMessage) ) { //producer may send messages without subscribing CommandMessage commandMessageSubscribe = new CommandMessage(CommandMessage.SubscribeOperation); commandMessageSubscribe.messageId = Guid.NewGuid().ToString("D"); commandMessageSubscribe.headers = message.headers.Clone() as Hashtable; commandMessageSubscribe.messageRefType = message.GetType().FullName;//"flex.messaging.messages.AsyncMessage" commandMessageSubscribe.destination = message.destination; IMessage subscribeResponse = messageBroker.RouteMessage(commandMessageSubscribe, _endpoint, connection); message.clientId = subscribeResponse.clientId; } } */ } IMessage response = messageBroker.RouteMessage(message, this.Endpoint); invoke.ServiceCall.Status = response is ErrorMessage ? Call.STATUS_INVOCATION_EXCEPTION : Call.STATUS_SUCCESS_RESULT; if (invoke.ServiceCall is IPendingServiceCall) (invoke.ServiceCall as IPendingServiceCall).Result = response; FlexInvoke reply = new FlexInvoke(); reply.InvokeId = invoke.InvokeId; reply.ServiceCall = invoke.ServiceCall; /* if( response is ErrorMessage ) reply.SetResponseFailure(); else reply.SetResponseSuccess(); reply.Response = response; */ channel.Write(reply); } else { // If it's a callback for server remote call then pass it over to callbacks handler and return OnInvoke(connection, channel, header, invoke); } }
internal static void Push(RtmpConnection connection, IMessage message, IMessageClient messageClient) { if (connection != null) { object response = message; if (message is BinaryMessage) { BinaryMessage binaryMessage = message as BinaryMessage; binaryMessage.Update(messageClient); byte[] binaryContent = binaryMessage.body as byte[]; //byte[] destClientBinaryId = messageClient.GetBinaryId(); //Array.Copy(destClientBinaryId, 0, binaryContent, binaryMessage.PatternPosition, destClientBinaryId.Length); RawBinary result = new RawBinary(binaryContent); response = result; } else { //This should be a clone of the original message message.SetHeader(MessageBase.DestinationClientIdHeader, messageClient.ClientId); message.clientId = messageClient.ClientId; } RtmpChannel channel = connection.GetChannel(3); FlexInvoke reply = new FlexInvoke(); Call call = new Call("receive", new object[] { response }); reply.ServiceCall = call; //reply.Cmd = "receive"; //reply.Response = response; reply.InvokeId = connection.InvokeId; channel.Write(reply); } }
/// <summary> /// Handler for pending call result. Dispatches results to all pending call handlers. /// </summary> /// <param name="connection">Connection.</param> /// <param name="invoke">Pending call result event context.</param> protected void HandlePendingCallResult(RtmpConnection connection, Notify invoke) { IServiceCall call = invoke.ServiceCall; IPendingServiceCall pendingCall = connection.RetrievePendingCall(invoke.InvokeId); if (pendingCall != null) { pendingCall.Status = call.Status; // The client sent a response to a previously made call. object[] args = call.Arguments; if ((args != null) && (args.Length > 0)) { pendingCall.Result = args[0]; } IPendingServiceCallback[] callbacks = pendingCall.GetCallbacks(); if (callbacks != null && callbacks.Length > 0) { foreach (IPendingServiceCallback callback in callbacks) { try { callback.ResultReceived(pendingCall); } catch (Exception ex) { #if !SILVERLIGHT log.Error("Error while executing callback " + callback, ex); #endif } } } } }
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="source"></param> /// <param name="clientBW"></param> protected abstract void OnClientBW(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, ClientBW clientBW);
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="source"></param> /// <param name="serverBW"></param> protected abstract void OnServerBW(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, ServerBW serverBW);
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="header"></param> /// <param name="invoke"></param> protected abstract void OnFlexInvoke(RtmpConnection connection, RtmpChannel channel, RtmpHeader header, FlexInvoke invoke);
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="header"></param> /// <param name="message"></param> protected abstract void OnSharedObject(RtmpConnection connection, RtmpChannel channel, RtmpHeader header, SharedObjectMessage message);
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="header"></param> /// <param name="invoke"></param> protected abstract void OnInvoke(RtmpConnection connection, RtmpChannel channel, RtmpHeader header, Notify invoke);
private static void SendSOCreationFailed(RtmpConnection connection, string name, bool persistent) { SharedObjectMessage msg; if (connection.ObjectEncoding == ObjectEncoding.AMF0) msg = new SharedObjectMessage(name, 0, persistent); else msg = new FlexSharedObjectMessage(name, 0, persistent); msg.AddEvent(new SharedObjectEvent(SharedObjectEventType.CLIENT_STATUS, StatusASO.SO_CREATION_FAILED, "error")); connection.GetChannel((byte)3).Write(msg); }
private static void SendSOPersistenceMismatch(RtmpConnection connection, string name, bool persistent) { SharedObjectMessage msg; if (connection.ObjectEncoding == ObjectEncoding.AMF0) msg = new SharedObjectMessage(name, 0, persistent); else msg = new FlexSharedObjectMessage(name, 0, persistent); msg.AddEvent(new SharedObjectEvent(SharedObjectEventType.CLIENT_STATUS, StatusASO.SO_PERSISTENCE_MISMATCH, "error")); connection.GetChannel((byte)3).Write(msg); }
internal RtmpChannel(RtmpConnection connection, int channelId) { _connection = connection; _channelId = channelId; }
public static void InvokeCall(RtmpConnection connection, IServiceCall serviceCall) { IScope scope = connection.Scope; if(scope.HasHandler) { IScopeHandler handler = scope.Handler; if(!handler.ServiceCall(connection, serviceCall)) { // What do do here? Return an error? return; } } IScopeContext context = scope.Context; context.ServiceInvoker.Invoke(serviceCall, scope); }
public KeepAliveJob(RtmpConnection connection) { _connection = connection; }
/* FlexInvoke flexInvoke = new FlexInvoke(); flexInvoke.Cmd = "onstatus"; flexInvoke.DataType = DataType.TypeUnknown; StatusASO statusASO = StatusASO.GetStatusObject(StatusASO.NC_CONNECT_CLOSED, connection.ObjectEncoding); flexInvoke.Parameters = new object[]{ statusASO }; RtmpChannel channel = connection.GetChannel(3); channel.Write(flexInvoke); */ protected override void OnChunkSize(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, ChunkSize chunkSize) { if (connection is IStreamCapableConnection) { IStreamCapableConnection streamCapableConnection = connection as IStreamCapableConnection; { foreach (IClientStream stream in streamCapableConnection.GetStreams()) { if (stream is IClientBroadcastStream) { IClientBroadcastStream bs = stream as IClientBroadcastStream; IBroadcastScope scope = bs.Scope.GetBasicScope(Constants.BroadcastScopeType, bs.PublishedName) as IBroadcastScope; if (scope == null) continue; OOBControlMessage setChunkSize = new OOBControlMessage(); setChunkSize.Target = "ClientBroadcastStream"; setChunkSize.ServiceName = "chunkSize"; setChunkSize.ServiceParameterMap.Add("chunkSize", chunkSize.Size); scope.SendOOBControlMessage((IConsumer)null, setChunkSize); if (log.IsDebugEnabled) { log.Debug("Sending chunksize " + chunkSize + " to " + bs.Provider); } } } } } }
/// <summary> /// This method supports the Fluorine infrastructure and is not intended to be used directly from your code. /// </summary> /// <param name="connection"></param> /// <param name="channel"></param> /// <param name="source"></param> /// <param name="chunkSize"></param> protected abstract void OnChunkSize(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, ChunkSize chunkSize);
public WaitForHandshakeJob(RtmpConnection connection) { _connection = connection; }
protected override void OnPing(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, Ping ping) { switch (ping.PingType) { case Ping.ClientBuffer: IClientStream stream = null; // Get the stream id int streamId = ping.Value2; // Get requested buffer size in milliseconds int buffer = ping.Value3; if (streamId != 0) { // The client wants to set the buffer time stream = connection.GetStreamById(streamId); if (stream != null) { stream.SetClientBufferDuration(buffer); if (log.IsDebugEnabled) log.Debug(string.Format("Client sent a buffer size: {0} ms for stream id: {1}", buffer, streamId )); } } // Catch-all to make sure buffer size is set if (stream == null) { // Remember buffer time until stream is created connection.RememberStreamBufferDuration(streamId, buffer); if (log.IsDebugEnabled) log.Debug(string.Format("Remembering client buffer size: {0} on stream id: {1} ", buffer, streamId)); } break; case Ping.PongServer: // This is the response to an IConnection.Ping request connection.PingReceived(ping); break; default: log.Warn("Unhandled ping: " + ping); break; } }
protected override void OnServerBW(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, ServerBW serverBW) { }
protected override void OnClientBW(RtmpConnection connection, RtmpChannel channel, RtmpHeader source, ClientBW clientBW) { }
public CreateStreamCallBack(NetStream stream, RtmpConnection connection, IPendingServiceCallback callback) { ValidationUtils.ArgumentNotNull(connection, "connection"); _stream = stream; _connection = connection; _callback = callback; }
/// <summary> /// Connection closed. /// </summary> /// <param name="connection">Connection object.</param> public virtual void ConnectionClosed(RtmpConnection connection) { connection.Close(); }