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> /// Message recieved. /// </summary> /// <param name="connection">Connection object.</param> /// <param name="obj">Message object.</param> public void MessageReceived(RtmpConnection connection, object obj) { IRtmpEvent message = null; RtmpPacket packet = null; RtmpHeader header = null; RtmpChannel channel = null; IClientStream stream = null; try { packet = obj as RtmpPacket; message = packet.Message; header = packet.Header; channel = connection.GetChannel(header.ChannelId); if (connection is IStreamCapableConnection) stream = (connection as IStreamCapableConnection).GetStreamById(header.StreamId); // Support stream ids #if !SILVERLIGHT FluorineContext.ValidateContext(); FluorineContext.Current.Connection.SetAttribute(FluorineContext.FluorineStreamIdKey, header.StreamId); #endif // Increase number of received messages connection.MessageReceived(); #if !SILVERLIGHT if (log != null && log.IsDebugEnabled) log.Debug("RtmpConnection message received, type = " + header.DataType); #endif if (message != null) message.Source = connection; switch (header.DataType) { case Constants.TypeInvoke: OnInvoke(connection, channel, header, message as Invoke); if (message.Header.StreamId != 0 && (message as Invoke).ServiceCall.ServiceName == null && (message as Invoke).ServiceCall.ServiceMethodName == BaseRtmpHandler.ACTION_PUBLISH) { if (stream != null) //Dispatch if stream was created (stream as IEventDispatcher).DispatchEvent(message); } break; case Constants.TypeFlexInvoke: OnFlexInvoke(connection, channel, header, message as FlexInvoke); if (message.Header.StreamId != 0 && (message as Invoke).ServiceCall.ServiceName == null && (message as Invoke).ServiceCall.ServiceMethodName == BaseRtmpHandler.ACTION_PUBLISH) { if (stream != null) //Dispatch if stream was created (stream as IEventDispatcher).DispatchEvent(message); } break; case Constants.TypeNotify:// just like invoke, but does not return if ((message as Notify).Data != null && stream != null) { // Stream metadata (stream as IEventDispatcher).DispatchEvent(message); } else OnInvoke(connection, channel, header, message as Notify); break; case Constants.TypePing: OnPing(connection, channel, header, message as Ping); break; case Constants.TypeBytesRead: OnStreamBytesRead(connection, channel, header, message as BytesRead); break; case Constants.TypeSharedObject: case Constants.TypeFlexSharedObject: OnSharedObject(connection, channel, header, message as SharedObjectMessage); break; case Constants.TypeFlexStreamEnd: if (stream != null) (stream as IEventDispatcher).DispatchEvent(message); break; case Constants.TypeChunkSize: OnChunkSize(connection, channel, header, message as ChunkSize); break; case Constants.TypeAudioData: case Constants.TypeVideoData: // NOTE: If we respond to "publish" with "NetStream.Publish.BadName", // the client sends a few stream packets before stopping. We need to // ignore them. if (stream != null) ((IEventDispatcher)stream).DispatchEvent(message); break; case Constants.TypeServerBandwidth: OnServerBW(connection, channel, header, message as ServerBW); break; case Constants.TypeClientBandwidth: OnClientBW(connection, channel, header, message as ClientBW); break; default: #if !SILVERLIGHT if (log != null && log.IsDebugEnabled) log.Debug("RtmpService event not handled: " + header.DataType); #endif break; } } catch (Exception ex) { #if !SILVERLIGHT if (log.IsErrorEnabled) { log.Error(__Res.GetString(__Res.Rtmp_HandlerError), ex); log.Error(__Res.GetString(__Res.Error_ContextDump)); //log.Error(Environment.NewLine); log.Error(packet); } #endif } }
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); }
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; } }