public SendMessage ( |
||
message | ||
enqueueForOutbound | bool | |
recycleMessageBody | bool | |
return | bool |
private bool AuthenticateInboundAdobe(BaseRTMPProtocol pFrom, AmfMessage request, Variant authState) { if (authState["stage"] == null) authState["stage"] = "inProgress"; else if (authState["stage"] == "authenticated") return true; if (authState["stage"] != "inProgress") { Logger.FATAL("This protocol in not in the authenticating mode"); return false; } //1. Validate the type of request if (request.MessageType != Defines.RM_HEADER_MESSAGETYPE_INVOKE) { this.Log().Info("This is not an invoke. Wait for it..."); return true; } //2. Validate the invoke function name if (request.InvokeFunction != Defines.RM_INVOKE_FUNCTION_CONNECT) { Logger.FATAL("This is not a connect invoke"); return false; } //3. Pick up the first param in the invoke Variant connectParams = request.InvokeParam[0]; if (connectParams != VariantType.Map) { Logger.FATAL("first invoke param must be a map"); return false; } //4. pick up the agent name if ((connectParams[Defines.RM_INVOKE_PARAMS_CONNECT_FLASHVER] == null) || (connectParams[Defines.RM_INVOKE_PARAMS_CONNECT_FLASHVER] != VariantType.String)) { Logger.WARN("Incorrect user agent"); authState["stage"] = "authenticated"; authState["canPublish"] = false; authState["canOverrideStreamName"] = false; return true; } string flashVer = connectParams[Defines.RM_INVOKE_PARAMS_CONNECT_FLASHVER]; //6. test the flash ver against the allowed encoder agents if (_adobeAuthSettings[Defines.CONF_APPLICATION_AUTH_ENCODER_AGENTS, flashVer] == null) { Logger.WARN("This agent is not on the list of allowed encoders: `{0}`", flashVer); authState["stage"] = "authenticated"; authState["canPublish"] = false; authState["canOverrideStreamName"] = false; return true; } //7. pick up the tcUrl from the first param if ((connectParams[Defines.RM_INVOKE_PARAMS_CONNECT_APP] == null) || (connectParams[Defines.RM_INVOKE_PARAMS_CONNECT_APP] != VariantType.String)) { Logger.WARN("Incorrect app url"); authState["stage"] = "authenticated"; authState["canPublish"] = (bool)false; authState["canOverrideStreamName"] = (bool)false; return true; } string appUrl = connectParams[Defines.RM_INVOKE_PARAMS_CONNECT_APP]; //8. Split the URI into parts var appUrlParts = appUrl.Split('?'); if (appUrlParts.Length == 1) { //bare request. We need to tell him that he needs auth if (!pFrom.SendMessage( ConnectionMessageFactory.GetInvokeConnectError(request, "[ AccessManager.Reject ] : [ code=403 need auth; authmod=adobe ] : "))) { Logger.FATAL("Unable to send message"); return false; } if (!pFrom.SendMessage( ConnectionMessageFactory.GetInvokeClose())) { Logger.FATAL("Unable to send message"); return false; } //pFrom.SendMessagesBlock.TriggerBatch(); pFrom.EnqueueForOutbound(pFrom.OutputBuffer); pFrom.GracefullyEnqueueForDelete(); return true; } else if (appUrlParts.Length == 2) { var _params = appUrlParts[1].GetURLParam(); if ((!_params.ContainsKey("authmod")) || (!_params.ContainsKey("user"))) { Logger.WARN("Invalid appUrl: {0}", appUrl); authState["stage"] = "authenticated"; authState["canPublish"] = false; authState["canOverrideStreamName"] = false; return true; } string user = _params["user"]; if (_params.ContainsKey("challenge") && _params.ContainsKey("response") && _params.ContainsKey("opaque")) { string challenge = _params["challenge"]; string response = _params["response"]; string opaque = _params["opaque"]; string password = GetAuthPassword(user); if (password == "") { Logger.WARN("No such user: `{0}`", user); if (!pFrom.SendMessage( ConnectionMessageFactory.GetInvokeConnectError(request, "[ AccessManager.Reject ] : [ authmod=adobe ] : ?reason=authfailed&opaque=vgoAAA=="))) { Logger.FATAL("Unable to send message"); return false; } if (!pFrom.SendMessage( ConnectionMessageFactory.GetInvokeClose())) { Logger.FATAL("Unable to send message"); return false; } //pFrom.SendMessagesBlock.TriggerBatch(); pFrom.EnqueueForOutbound(pFrom.OutputBuffer); pFrom.GracefullyEnqueueForDelete(); return true; } var md5 = MD5.Create(); string str1 = user + _adobeAuthSalt + password; string hash1 = Convert.ToBase64String(md5.ComputeHash(Encoding.ASCII.GetBytes(str1))); string str2 = hash1 + opaque + challenge; string wanted = Convert.ToBase64String(md5.ComputeHash(Encoding.ASCII.GetBytes(str2))); if (response == wanted) { authState["stage"] = "authenticated"; authState["canPublish"] = true; authState["canOverrideStreamName"] = true; Logger.WARN("User `{0}` authenticated", user); return true; } else { Logger.WARN("Invalid password for user `{0}`", user); if (!pFrom.SendMessage( ConnectionMessageFactory.GetInvokeConnectError(request, "[ AccessManager.Reject ] : [ authmod=adobe ] : ?reason=authfailed&opaque=vgoAAA=="))) { Logger.FATAL("Unable to send message"); return false; } if (!pFrom.SendMessage( ConnectionMessageFactory.GetInvokeClose())) { Logger.FATAL("Unable to send message"); return false; } //pFrom.SendMessagesBlock.TriggerBatch(); pFrom.EnqueueForOutbound(pFrom.OutputBuffer); pFrom.GracefullyEnqueueForDelete(); return true; } } else { string challenge = Utils.GenerateRandomString(6) + "=="; string opaque = challenge; string description = "[ AccessManager.Reject ] : [ authmod=adobe ] : ?reason=needauth&user={0}&salt={1}&challenge={2}&opaque={3}"; description = string.Format(description, user, _adobeAuthSalt, challenge, opaque); if (!pFrom.SendMessage(ConnectionMessageFactory.GetInvokeConnectError(request, description))) { Logger.FATAL("Unable to send message"); return false; } if (!pFrom.SendMessage(ConnectionMessageFactory.GetInvokeClose())) { Logger.FATAL("Unable to send message"); return false; } //pFrom.SendMessagesBlock.TriggerBatch(); pFrom.EnqueueForOutbound(pFrom.OutputBuffer); pFrom.GracefullyEnqueueForDelete(); return true; } } else { Logger.FATAL("Invalid appUrl: {0}", appUrl); return false; } }
public bool InboundMessageAvailable(BaseRTMPProtocol pFrom, Variant messageBody, Channel channel,out bool recycleBody) { recycleBody = true; AmfMessage message; message.Header = channel.lastInHeader; message.Body = messageBody; var parameters = pFrom.CustomParameters; if (parameters["authState"] == null) { parameters["authState"] = Variant.GetMap(new VariantMapHelper { {"stage","authenticated"}, {"canPublish",true}, {"canOverrideStreamName",false} }); } var authState = parameters["authState"]; if (pFrom.Type == ProtocolTypes.PT_INBOUND_RTMP && !string.IsNullOrEmpty(_authMethod)) { if (!AuthenticateInbound(pFrom, message, authState)) { Logger.FATAL("Unable to authenticate"); return false; } } uint size; Dictionary<uint, IStream> possibleStreams; InNetRTMPStream pInNetRTMPStream; switch (message.MessageType) { case Defines.RM_HEADER_MESSAGETYPE_WINACKSIZE: if (messageBody[Defines.RM_WINACKSIZE] != VariantType.Numberic) { Logger.FATAL("Invalid message:{0}", messageBody.ToString()); return false; } size = messageBody[Defines.RM_WINACKSIZE]; if ((size > 4 * 1024 * 1024) || size == 0) { Logger.FATAL("Invalid message:{0}", messageBody.ToString()); return false; } pFrom.SetWinAckSize(size); return true; case Defines.RM_HEADER_MESSAGETYPE_PEERBW: Logger.WARN("ProcessPeerBW"); return true; case Defines.RM_HEADER_MESSAGETYPE_ACK: return true; case Defines.RM_HEADER_MESSAGETYPE_CHUNKSIZE: if (messageBody[Defines.RM_CHUNKSIZE] != VariantType.Numberic) { Logger.FATAL("Invalid message:{0}", messageBody.ToString()); return false; } size = messageBody[Defines.RM_CHUNKSIZE]; if ((size > 4 * 1024 * 1024) || size == 0) { Logger.FATAL("Invalid message:{0}", messageBody.ToString()); return false; } if (!pFrom.SetInboundChunkSize(size)) { Logger.FATAL("Unable to set chunk size:{0}", messageBody.ToString()); return false; } return true; case Defines.RM_HEADER_MESSAGETYPE_USRCTRL: switch ((ushort)messageBody[Defines.RM_USRCTRL, Defines.RM_USRCTRL_TYPE]) { case Defines.RM_USRCTRL_TYPE_PING_REQUEST: return SendRTMPMessage(pFrom, ConnectionMessageFactory.GetPong()); case Defines.RM_USRCTRL_TYPE_STREAM_BEGIN: case Defines.RM_USRCTRL_TYPE_STREAM_SET_BUFFER_LENGTH: case Defines.RM_USRCTRL_TYPE_STREAM_IS_RECORDED: case Defines.RM_USRCTRL_TYPE_PING_RESPONSE: Logger.WARN("User control message type: {0}", messageBody[Defines.RM_USRCTRL, Defines.RM_USRCTRL_TYPE_STRING]); return true; case Defines.RM_USRCTRL_TYPE_UNKNOWN1: case Defines.RM_USRCTRL_TYPE_UNKNOWN2: return true; default: Logger.FATAL("Invalid user ctrl:\n{0}", messageBody.ToString()); return false; } case Defines.RM_HEADER_MESSAGETYPE_NOTIFY: //1. Find the corresponding inbound stream possibleStreams = Application.StreamsManager.FindByProtocolIdByType(pFrom.Id, StreamTypes.ST_IN_NET_RTMP, false); pInNetRTMPStream = possibleStreams.Select(x => x.Value as InNetRTMPStream) .SingleOrDefault( x => x.RtmpStreamId == message.StreamId); if (pInNetRTMPStream == null) { Logger.WARN("No stream found. Serached for {0}:{1}. Message was:{2}", pFrom.Id, message.StreamId, messageBody.ToString()); return true; } //2. Remove all string values starting with @ foreach (var item in messageBody[Defines.RM_NOTIFY, Defines.RM_NOTIFY_PARAMS].Children.Where( x => x.Value == VariantType.String && ((string)x.Value).StartsWith("@")).Select(x => x.Key).ToArray()) { messageBody[Defines.RM_NOTIFY, Defines.RM_NOTIFY_PARAMS].Children.Remove(item); } recycleBody = false; //3. Brodcast the message on the inbound stream pInNetRTMPStream.SendStreamMessage(new BufferWithOffset(((MemoryStream)channel.inputData.BaseStream).GetBuffer(), length: (int)message.MessageLength)); return true; case Defines.RM_HEADER_MESSAGETYPE_FLEXSTREAMSEND: recycleBody = false; //1. Find the corresponding inbound stream possibleStreams = Application.StreamsManager.FindByProtocolIdByType(pFrom.Id, StreamTypes.ST_IN_NET_RTMP, false); pInNetRTMPStream = possibleStreams.Select(x => x.Value as InNetRTMPStream) .SingleOrDefault(x => x.RtmpStreamId == message.StreamId); if (pInNetRTMPStream == null) { Logger.WARN("No stream found. Serached for {0}:{1}. Message was:{2}", pFrom.Id, message.StreamId, messageBody.ToString()); return true; } //2. Remove all string values starting with @ foreach (var item in messageBody[Defines.RM_FLEXSTREAMSEND, Defines.RM_FLEXSTREAMSEND_PARAMS].Children.Where( x => x.Value == VariantType.String && ((string)x.Value).StartsWith("@")).Select(x => x.Key).ToArray()) { messageBody[Defines.RM_FLEXSTREAMSEND, Defines.RM_FLEXSTREAMSEND_PARAMS].Children.Remove(item); } //写入文件 //pInNetRTMPStream.SendStreamMessage(channel); //3. Brodcast the message on the inbound stream pInNetRTMPStream.SendStreamMessage(new BufferWithOffset(((MemoryStream)channel.inputData.BaseStream).GetBuffer(),length: (int)message.MessageLength)); return true; case Defines.RM_HEADER_MESSAGETYPE_SHAREDOBJECT: case Defines.RM_HEADER_MESSAGETYPE_FLEXSHAREDOBJECT: return Application.SOManager.Process(pFrom, messageBody); case Defines.RM_HEADER_MESSAGETYPE_INVOKE: case Defines.RM_HEADER_MESSAGETYPE_FLEX: string functionName = messageBody[Defines.RM_INVOKE][Defines.RM_INVOKE_FUNCTION]; Logger.INFO(functionName); uint currentInvokeId = messageBody[Defines.RM_INVOKE, Defines.RM_INVOKE_ID]; if (currentInvokeId != 0 && _nextInvokeId[pFrom.Id] <= currentInvokeId) { _nextInvokeId[pFrom.Id] = currentInvokeId + 1; } string streamName; BaseOutNetRTMPStream pBaseOutNetRTMPStream = null; double timeOffset; Variant metadata; switch (functionName) { case Defines.RM_INVOKE_FUNCTION_CONNECT: return ProcessInvokeConnect(pFrom, message); case Defines.RM_INVOKE_FUNCTION_CREATESTREAM: //1. Create the neutral stream uint id = 0; if (pFrom.CreateNeutralStream(ref id) == null) { Logger.FATAL("Unable to create stream"); return false; } //2. Send the response return SendRTMPMessage(pFrom, StreamMessageFactory.GetInvokeCreateStreamResult(message, id)); case Defines.RM_INVOKE_FUNCTION_PUBLISH: //1. gather the required data from the request var param1 = messageBody[Defines.RM_INVOKE, Defines.RM_INVOKE_PARAMS][1]; if (param1 != VariantType.String && param1 != VariantType.Boolean) { Logger.FATAL("Invalid request:{0}", messageBody.ToString()); return false; } if (param1 == VariantType.Boolean) { if (param1 != false) { Logger.FATAL("Invalid request {0}", messageBody.ToString()); return false; } this.Log().Info("Closing stream via publish(false)"); return pFrom.CloseStream(message.StreamId, true); } streamName = param1; //2. Check to see if we are allowed to create inbound streams if (!pFrom.CustomParameters["authState"]["canPublish"]) { return pFrom.SendMessage(StreamMessageFactory.GetInvokeOnStatusStreamPublishBadName( message, streamName),true); } var recording = string.Equals(message.InvokeParam[2], Defines.RM_INVOKE_PARAMS_PUBLISH_TYPERECORD); var appending = string.Equals(message.InvokeParam[2], Defines.RM_INVOKE_PARAMS_PUBLISH_TYPEAPPEND); //3. Test to see if this stream name is already published somewhere if (Application.AllowDuplicateInboundNetworkStreams) { var existingStreams = Application.StreamsManager.FindByTypeByName(StreamTypes.ST_IN_NET_RTMP, streamName,false, false); if (existingStreams.Count > 0) { if (pFrom.CustomParameters["authState"]["canOverrideStreamName"]) { foreach (var existingStream in existingStreams.Values.OfType<InNetRTMPStream>().Where(existingStream => existingStream.Protocol != null)) { Logger.WARN( "Overriding stream {0}:{1} with name {2} from conneciton {3}", existingStream.RtmpStreamId, existingStream.UniqueId, existingStream.Name, existingStream.Protocol.Id); (existingStream.Protocol as BaseRTMPProtocol).CloseStream( existingStream.RtmpStreamId, true); } } else { Logger.WARN( "Unable to override stream {0} because this connection dosen't have the right", streamName); return pFrom.SendMessage( StreamMessageFactory.GetInvokeOnStatusStreamPublishBadName(message, streamName),true); } } } else if (!Application.StreamNameAvailable(streamName, pFrom)) { Logger.WARN("Stream name {0} already occupied and application doesn't allow duplicated inbound network streams", streamName); return pFrom.SendMessage( StreamMessageFactory.GetInvokeOnStatusStreamPublishBadName( message, streamName), true); } //4. Create the network inbound stream pInNetRTMPStream = pFrom.CreateINS(message.ChannelId, message.StreamId, streamName); if (pInNetRTMPStream == null) { Logger.FATAL("Unable to create inbound stream"); return false; } //7. Bind the waiting subscribers Application.OnPublish(pFrom, pInNetRTMPStream, message.InvokeParam[2]); //8. Send the streamPublished status message if (!pInNetRTMPStream.SendOnStatusStreamPublished()) { Logger.FATAL("Unable to send OnStatusStreamPublished"); return false; } //9. Start recording if necessary //if (recording || appending) //{ // var meta = GetMetaData(streamName, false); // var pOutFileStream = CreateOutFileStream(pFrom, meta, appending); // if (pOutFileStream != null && pOutFileStream.Link(pInNetRTMPStream)) return true; // Logger.FATAL("Unable to bind the recording stream"); // return false; //} return true; case Defines.RM_INVOKE_FUNCTION_PLAY: //1. Minimal validation if (message.InvokeParam[1] != VariantType.String) { Logger.FATAL("Invalid request:{0}", message.Body.ToString()); return false; } //2. Close any streams left open if (!pFrom.CloseStream(message.StreamId, true)) { Logger.FATAL("Unable to close stream {0} {1}", pFrom.Id, message.StreamId); return false; } //3. Gather required data from the request streamName = message.InvokeParam[1]; double startTime = message.InvokeParam[2] == VariantType.Double ? (double)message.InvokeParam[2] : -2000; double length = message.InvokeParam[3] == VariantType.Double ? (double)message.InvokeParam[3] : -1000; if (startTime < 0 && startTime != -2000 && startTime != -1000) startTime = -2000; if (length < 0 && length != -1) length = -1; Logger.INFO("Play request for stream name `{0}`. Start:{1}; length: {2}", streamName, startTime, length); //6. bind the network outbound stream to the inbound stream //depending on the type of the outbound stream switch ((int)startTime) { case -2000: bool linked; //7. try to link to live stream if (!TryLinkToLiveStream(pFrom, message.StreamId, streamName, out linked)) { Logger.FATAL("Unable to link streams"); return false; } if (linked) return true; metadata = GetMetaData(streamName, true); //8. try to link to file stream if (!TryLinkToFileStream(pFrom, message.StreamId, metadata, streamName, startTime, length, out linked)) { Logger.FATAL("Unable to link streams"); return false; } if (linked) return true; //9. Ok, no live/file stream. Just wait for the live stream now... Logger.WARN("We are going to wait for the live stream `{0}`", streamName); pBaseOutNetRTMPStream = pFrom.CreateONS(message.StreamId, streamName, StreamTypes.ST_IN_NET_RTMP); goto case -999; case -1000://only live if (!TryLinkToLiveStream(pFrom, message.StreamId, streamName, out linked)) { Logger.FATAL("Unable to link streams"); return false; } if (linked) return true; Logger.WARN("We are going to wait for the live stream `%s`", streamName); pBaseOutNetRTMPStream = pFrom.CreateONS( message.StreamId, streamName, StreamTypes.ST_IN_NET_RTMP); goto case -999; case -999: //Application.ClusterAppProtocolHandler.PlayStream(Application.InstanceName,streamName); if (ClientApplicationManager.ClusterApplication != null) { ClientApplicationManager.ClusterApplication.GetProtocolHandler<BaseClusterAppProtocolHandler>().PlayStream(Application.Id, streamName); } //request["waitForLiveStream"] = true; //request["streamName"] = streamName; //if (pFrom.CustomParameters["origin"] != null) //{ //if (_externalStreamProtocol == null) // Application.PullExternalStream(new Variant // { // {"uri", "rtmp://192.168.20.56/live"}, // {"localStreamName", streamName}, // {"emulateUserAgent", "MAC 10,1,82,76"}, // {"swfUrl", "my crtmpserver"}, // {"pageUrl", "linkage"} // }); //else //{ // PlayAnotherStream(_externalStreamProtocol, streamName); //} //} return pBaseOutNetRTMPStream != null; default://only recorded metadata = GetMetaData(streamName, true); //12. Perform little adjustment on metadata if ((string)metadata[Defines.META_MEDIA_TYPE] == Defines.MEDIA_TYPE_LIVE_OR_FLV) { metadata[Defines.META_MEDIA_TYPE] = Defines.MEDIA_TYPE_FLV; } //13. try to link to file stream if (!TryLinkToFileStream(pFrom, message.StreamId, metadata, streamName, startTime, length, out linked)) { Logger.FATAL("Unable to link streams"); return false; } return linked; } case Defines.RM_INVOKE_FUNCTION_PAUSERAW: case Defines.RM_INVOKE_FUNCTION_PAUSE: pBaseOutNetRTMPStream = Application.StreamsManager.FindByProtocolIdByType(pFrom.Id, StreamTypes.ST_OUT_NET_RTMP, true).Values.OfType<BaseOutNetRTMPStream>().SingleOrDefault(x => x.RTMPStreamId == message.StreamId); if (pBaseOutNetRTMPStream == null) { Logger.FATAL("No out stream"); return false; } //3. get the operation bool pause = message.InvokeParam[1]; if (pause) { //4. Pause it return pBaseOutNetRTMPStream.Pause(); } else { timeOffset = 0.0; if (message.InvokeParam[2] == VariantType.Numberic) timeOffset = message.InvokeParam[2]; //8. Perform seek if (!pBaseOutNetRTMPStream.Seek(timeOffset)) { Logger.FATAL("Unable to seek"); return false; } //9. Resume return pBaseOutNetRTMPStream.Resume(); } case Defines.RM_INVOKE_FUNCTION_CLOSESTREAM: return pFrom.CloseStream(message.StreamId, true); case Defines.RM_INVOKE_FUNCTION_SEEK: //1. Read stream index and offset in millisecond timeOffset = 0.0; if (message.InvokeParam[1] == VariantType.Numberic) timeOffset = message.InvokeParam[1]; //2. Find the corresponding outbound stream pBaseOutNetRTMPStream = Application.StreamsManager.FindByProtocolIdByType(pFrom.Id, StreamTypes.ST_OUT_NET_RTMP, true).Values.OfType<BaseOutNetRTMPStream>().SingleOrDefault(x => x.RTMPStreamId == message.StreamId); if (pBaseOutNetRTMPStream == null) { Logger.FATAL("No out stream"); return false; } return pBaseOutNetRTMPStream.Seek(timeOffset); case Defines.RM_INVOKE_FUNCTION_RELEASESTREAM: //1. Attempt to find the stream var streams = Application.StreamsManager.FindByProtocolIdByName(pFrom.Id, message.InvokeParam[1], false); uint streamId = 0; if (streams.Count > 0) { //2. Is this the correct kind? if (streams.Values.First().Type.TagKindOf(StreamTypes.ST_IN_NET_RTMP)) { //3. get the rtmp stream id pInNetRTMPStream = (InNetRTMPStream)streams.Values.First(); streamId = pInNetRTMPStream.RtmpStreamId; //4. close the stream if (!pFrom.CloseStream(streamId, true)) { Logger.FATAL("Unable to close stream"); return true; } } } if (streamId > 0) { //5. Send the response if (!pFrom.SendMessage( StreamMessageFactory.GetInvokeReleaseStreamResult(3, streamId, message.InvokeId, streamId), true)) { Logger.FATAL("Unable to send message to client"); return false; } } else { if (!pFrom.SendMessage( StreamMessageFactory.GetInvokeReleaseStreamErrorNotFound(message), true)) { Logger.FATAL("Unable to send message to client"); return false; } } //3. Done return true; case Defines.RM_INVOKE_FUNCTION_DELETESTREAM: return pFrom.CloseStream(message.InvokeParam[1], false); case Defines.RM_INVOKE_FUNCTION_RESULT: case Defines.RM_INVOKE_FUNCTION_ERROR: if (!_resultMessageTracking.ContainsKey(pFrom.Id) || !_resultMessageTracking[pFrom.Id].ContainsKey(message.InvokeId)) return true; var request0 = _resultMessageTracking[pFrom.Id][message.InvokeId]; switch (request0.InvokeFunction) { case Defines.RM_INVOKE_FUNCTION_CONNECT: return ProcessInvokeConnectResult(pFrom, request0, message); case Defines.RM_INVOKE_FUNCTION_CREATESTREAM: return ProcessInvokeCreateStreamResult(pFrom, request0, message); case Defines.RM_INVOKE_FUNCTION_FCSUBSCRIBE: return true; case Defines.RM_INVOKE_FUNCTION_ONBWCHECK: startTime = pFrom.CustomParameters["lastOnnBWCheckMessage"]; double totalTime = (DateTime.Now.Ticks - startTime) / (double)1000000; var speed = (int)(ONBWCHECK_SIZE / totalTime / 1024.0 * 8.0); return SendRTMPMessage(pFrom, GenericMessageFactory.GetInvokeOnBWDone(speed)); default: Logger.WARN("Invoke result not yet implemented: Request:{0} Response:{1}", request0.ToString(), message.ToString()); return true; } case Defines.RM_INVOKE_FUNCTION_ONSTATUS: return ProcessInvokeOnStatus(pFrom, message); case Defines.RM_INVOKE_FUNCTION_FCPUBLISH: //1. Get the stream name streamName = message.InvokeParam[1]; //2. Send the release stream response. Is identical to the one //needed by this f****r //TODO: this is a nasty hack if (!pFrom.SendMessage( StreamMessageFactory.GetInvokeReleaseStreamResult(3, 0, message.InvokeId, 0), true)) { Logger.FATAL("Unable to send message to client"); return false; } //3. send the onFCPublish message if (!SendRTMPMessage(pFrom, StreamMessageFactory.GetInvokeOnFCPublish(3, 0, 0, false, 0, Defines.RM_INVOKE_PARAMS_ONSTATUS_CODE_NETSTREAMPUBLISHSTART, streamName))) { Logger.FATAL("Unable to send message to client"); return false; } //4. Done return true; case Defines.RM_INVOKE_FUNCTION_GETSTREAMLENGTH: metadata = GetMetaData(message.InvokeParam[1], true); var _params = Variant.GetList(Variant.Get(),metadata == VariantType.Map ? metadata[Defines.META_FILE_DURATION]/1000.00 : 0.0); if (!SendRTMPMessage(pFrom, GenericMessageFactory.GetInvokeResult(message, _params))) { Logger.FATAL("Unable to send message to client"); return false; } return true; case Defines.RM_INVOKE_FUNCTION_ONBWDONE: return true; case Defines.RM_INVOKE_FUNCTION_CHECKBANDWIDTH: case "_checkbw": if (!_enableCheckBandwidth) { Logger.WARN("checkBandwidth is disabled."); return true; } if (!SendRTMPMessage(pFrom, _onBWCheckMessage,true, false)) { Logger.FATAL("Unable to send message to flash player"); return false; } pFrom.CustomParameters["lastOnnBWCheckMessage"] = DateTime.Now.Ticks; return true; case "receiveAudio": pBaseOutNetRTMPStream = Application.StreamsManager.FindByProtocolIdByType(pFrom.Id, StreamTypes.ST_OUT_NET_RTMP, true).Values.OfType<BaseOutNetRTMPStream>().SingleOrDefault(x => x.RTMPStreamId ==message.StreamId); if (pBaseOutNetRTMPStream != null) pBaseOutNetRTMPStream.ReceiveAudio = message.InvokeParam[1]; return true; case "receiveVideo": pBaseOutNetRTMPStream = Application.StreamsManager.FindByProtocolIdByType(pFrom.Id, StreamTypes.ST_OUT_NET_RTMP, true).Values.OfType<BaseOutNetRTMPStream>().SingleOrDefault(x => x.RTMPStreamId == message.StreamId); if (pBaseOutNetRTMPStream != null) pBaseOutNetRTMPStream.ReceiveVideo = message.InvokeParam[1]; return true; default: return ProcessInvokeGeneric(pFrom, message); } case Defines.RM_HEADER_MESSAGETYPE_ABORTMESSAGE: if (messageBody[Defines.RM_ABORTMESSAGE] != VariantType.Numberic) { Logger.FATAL("Invalid message {0}", messageBody.ToString()); return false; } return pFrom.ResetChannel(messageBody[Defines.RM_ABORTMESSAGE]); default: Logger.FATAL("Request type not yet implemented:{0}", messageBody.ToString()); return false; } }
protected virtual bool ProcessInvokeConnect(BaseRTMPProtocol pFrom,AmfMessage message) { string appName = message.InvokeParam[0][Defines.RM_INVOKE_PARAMS_CONNECT_APP]; //var parameters = pFrom.CustomParameters; //var instanceName = index == -1?"_default_": appName.Substring(index + 1); var oldApplication = pFrom.Application; var newApp = ClientApplicationManager.SwitchRoom(pFrom, appName, Configuration); if (newApp != null && newApp != oldApplication) { var handler = newApp.GetProtocolHandler<BaseRTMPAppProtocolHandler>(pFrom); return handler.ProcessInvokeConnect(pFrom, message); } if (newApp == null || (newApp == oldApplication && !Application.OnConnect(pFrom, message.InvokeParam))) { if (!pFrom.SendMessage(ConnectionMessageFactory.GetInvokeConnectError(message, ""))) { return false; } if (!pFrom.SendMessage(ConnectionMessageFactory.GetInvokeClose())) { return false; } // pFrom.EnqueueForOutbound(); return true; } //1. Send the channel specific messages if (!SendRTMPMessage(pFrom, GenericMessageFactory.GetWinAckSize(2500000))) return false; if (!SendRTMPMessage(pFrom, GenericMessageFactory.GetPeerBW(2500000, Defines.RM_PEERBW_TYPE_DYNAMIC))) return false; //2. Initialize stream 0 //if (!SendRTMPMessage(pFrom, StreamMessageFactory.GetUserControlStreamBegin(0))) //{ // return false; //} if (!pFrom.SendMessage(GenericMessageFactory.GetChunkSize(pFrom._outboundChunkSize))) return false; //3. Send the connect result if (!SendRTMPMessage(pFrom, ConnectionMessageFactory.GetInvokeConnectResult(message))) return false; //4. Send onBWDone if (!SendRTMPMessage(pFrom, GenericMessageFactory.GetInvokeOnBWDone(1024 * 8))) return false; return true; }
protected bool SendRTMPMessage(BaseRTMPProtocol pTo, AmfMessage message, bool trackResponse = false, bool recycleMessageBody = true) { switch (message.MessageType) { case Defines.RM_HEADER_MESSAGETYPE_INVOKE: if (message.InvokeFunction != Defines.RM_INVOKE_FUNCTION_RESULT) { if (!_nextInvokeId.ContainsKey(pTo.Id)) { Logger.FATAL("Unable to get next invoke ID"); return false; } if (trackResponse) { uint invokeId = _nextInvokeId[pTo.Id]; _nextInvokeId[pTo.Id] = invokeId + 1; message.InvokeId = invokeId; if (!_resultMessageTracking.ContainsKey(pTo.Id)) _resultMessageTracking[pTo.Id] = new Dictionary<uint, AmfMessage>(); //do not store stupid useless amount of data needed by onbwcheck if (message.InvokeFunction == Defines.RM_INVOKE_FUNCTION_ONBWCHECK) _resultMessageTracking[pTo.Id][invokeId] = _onBWCheckStrippedMessage; else _resultMessageTracking[pTo.Id][invokeId] = message; recycleMessageBody = false; } else { message.InvokeId = 0; } //return pTo.SendMessage(message,true); } return pTo.SendMessage( message, true, recycleMessageBody); case Defines.RM_HEADER_MESSAGETYPE_FLEXSTREAMSEND: case Defines.RM_HEADER_MESSAGETYPE_WINACKSIZE: case Defines.RM_HEADER_MESSAGETYPE_PEERBW: case Defines.RM_HEADER_MESSAGETYPE_USRCTRL: case Defines.RM_HEADER_MESSAGETYPE_ABORTMESSAGE: return pTo.SendMessage( message, true, recycleMessageBody); default: Logger.FATAL("Unable to send message:\n{0}", message.ToString()); return false; } }