private bool ProcessSharedObjectPrimitive(BaseRTMPProtocol pFrom, SO pSO, string name, Variant request, int primitiveId) { var primitive = request[Defines.RM_SHAREDOBJECT,Defines.RM_SHAREDOBJECT_PRIMITIVES][ primitiveId]; switch ((byte)primitive[Defines.RM_SHAREDOBJECTPRIMITIVE_TYPE]) { case Defines.SOT_CS_CONNECT: pSO.RegisterProtocol(pFrom); return true; case Defines.SOT_CS_DISCONNECT: UnRegisterProtocol(pFrom); return true; case Defines.SOT_CSC_DELETE_DATA: pSO.UnSet(primitive[Defines.RM_SHAREDOBJECTPRIMITIVE_PAYLOAD],pFrom); return true; case Defines.SOT_CS_SET_ATTRIBUTE: if (pSO == null) { Logger.FATAL("SO is null"); return false; } foreach (KeyValuePair<string,Variant> item in primitive [Defines.RM_SHAREDOBJECTPRIMITIVE_PAYLOAD]) { pSO.Set(item.Key, item.Value.Clone(), pFrom); } return true; case Defines.SOT_BW_SEND_MESSAGE: pSO?.Send(primitive, pFrom); return true; default: Logger.FATAL("SO primitive not allowed here"); return false; } }
public OutNetRTMP4TSStream(BaseRTMPProtocol pProtocol, StreamsManager pStreamsManager, string name, uint rtmpStreamId, uint chunkSize) : base(pProtocol, pStreamsManager, name, rtmpStreamId, chunkSize) { _pSPSPPS[0] = 0x17; //0x10 - key frame; 0x07 - H264_CODEC_ID _pSPSPPS[1] = 0; //0: AVC sequence header; 1: AVC NALU; 2: AVC end of sequence _pSPSPPS[2] = 0; //CompositionTime _pSPSPPS[3] = 0; //CompositionTime _pSPSPPS[4] = 0; //CompositionTime _pSPSPPS[5] = 1; //version _pSPSPPS[9] = 0xff; //6 bits reserved (111111) + 2 bits nal size length - 1 (11) _pSPSPPS[10] = 0xe1; //3 bits reserved (111) + 5 bits number of sps (00001) }
public bool Process(BaseRTMPProtocol pFrom, Variant request) { string name = request [ Defines.RM_SHAREDOBJECT,Defines.RM_SHAREDOBJECT_NAME]; var so = this[name, request[Defines.RM_SHAREDOBJECT,Defines.RM_SHAREDOBJECT_PERSISTENCE]]; var ps = request[Defines.RM_SHAREDOBJECT,Defines.RM_SHAREDOBJECT_PRIMITIVES]; if(ps != null) for (var i = 0; i < ps.Count; i++) { if (ProcessSharedObjectPrimitive(pFrom, so, name, request, i)) continue; Logger.FATAL("Unable to process primitive {0} from {1}",i,request); return false; } if (_sos.TryGetValue(name, out so)) so.Track(); return true; }
private bool ProcessSharedObjectPrimitive(BaseRTMPProtocol pFrom, SO pSO, string name, Variant request, int primitiveId) { var primitive = request[Defines.RM_SHAREDOBJECT, Defines.RM_SHAREDOBJECT_PRIMITIVES][primitiveId]; switch ((byte)primitive[Defines.RM_SHAREDOBJECTPRIMITIVE_TYPE]) { case Defines.SOT_CS_CONNECT: pSO.RegisterProtocol(pFrom); return(true); case Defines.SOT_CS_DISCONNECT: UnRegisterProtocol(pFrom); return(true); case Defines.SOT_CSC_DELETE_DATA: pSO.UnSet(primitive[Defines.RM_SHAREDOBJECTPRIMITIVE_PAYLOAD], pFrom); return(true); case Defines.SOT_CS_SET_ATTRIBUTE: if (pSO == null) { Logger.FATAL("SO is null"); return(false); } foreach (KeyValuePair <string, Variant> item in primitive[Defines.RM_SHAREDOBJECTPRIMITIVE_PAYLOAD]) { pSO.Set(item.Key, item.Value.Clone(), pFrom); } return(true); case Defines.SOT_BW_SEND_MESSAGE: pSO?.Send(primitive, pFrom); return(true); default: Logger.FATAL("SO primitive not allowed here"); return(false); } }
public bool Process(BaseRTMPProtocol pFrom, Variant request) { string name = request [Defines.RM_SHAREDOBJECT, Defines.RM_SHAREDOBJECT_NAME]; var so = this[name, request[Defines.RM_SHAREDOBJECT, Defines.RM_SHAREDOBJECT_PERSISTENCE]]; var ps = request[Defines.RM_SHAREDOBJECT, Defines.RM_SHAREDOBJECT_PRIMITIVES]; if (ps != null) { for (var i = 0; i < ps.Count; i++) { if (ProcessSharedObjectPrimitive(pFrom, so, name, request, i)) { continue; } Logger.FATAL("Unable to process primitive {0} from {1}", i, request); return(false); } } if (_sos.TryGetValue(name, out so)) { so.Track(); } return(true); }
public OutNetRTMP4RTMPStream(BaseRTMPProtocol pProtocol, StreamsManager pStreamsManager, string name, uint rtmpStreamId, uint chunkSize) : base(pProtocol, pStreamsManager, name, rtmpStreamId, chunkSize) { }
private bool NeedsToPushLocalStream(BaseRTMPProtocol pFrom) { var parameters = pFrom.CustomParameters; if (parameters["customParameters","localStreamConfig","targetUri"] == null) return false; return true; }
//private bool PlayAnotherStream(BaseRTMPProtocol pFrom,string streamName) //{ // var FCSubscribeRequest = StreamMessageFactory.GetInvokeFCSubscribe(streamName); // if (!SendRTMPMessage(pFrom, FCSubscribeRequest, true)) // { // Logger.FATAL("Unable to send request:\n{0}", FCSubscribeRequest.ToString()); // return false; // } // var createStreamRequest = StreamMessageFactory.GetInvokeCreateStream(); // if (!SendRTMPMessage(pFrom, createStreamRequest, true)) // { // Logger.FATAL("Unable to send request:\n{0}", createStreamRequest.ToString()); // return false; // } // return true; //} private bool AuthenticateInbound(BaseRTMPProtocol pFrom, AmfMessage request, Variant authState) { if (_authMethod == Defines.CONF_APPLICATION_AUTH_TYPE_ADOBE) return AuthenticateInboundAdobe(pFrom, request, authState); Logger.FATAL("Auth scheme not supported: {0}", _authMethod); return false; }
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; } }
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; }
virtual protected bool ProcessInvokeGeneric(BaseRTMPProtocol pFrom, AmfMessage request) { //Logger.WARN("Default implementation of ProcessInvokeGeneric: Request: {0}", request.InvokeFunction); try { var result = Application.CallCustomFunction(pFrom, request.InvokeFunction,request.InvokeParam); return SendRTMPMessage(pFrom, GenericMessageFactory.GetInvokeResult(request.ChannelId, request.StreamId, request.InvokeId, Variant.Get(), result)); } catch (CallErrorException ex) { return SendRTMPMessage(pFrom, GenericMessageFactory.GetInvokeError(request.ChannelId, request.StreamId, request.InvokeId, Variant.Get(), Variant.Get(ex.Message))); } catch (Exception ex) { return SendRTMPMessage(pFrom, GenericMessageFactory.GetInvokeCallFailedError(request)); } }
private bool ProcessInvokeOnStatus(BaseRTMPProtocol pFrom, AmfMessage request) { if ((!NeedsToPullExternalStream(pFrom)) && (!NeedsToPushLocalStream(pFrom))) { Logger.WARN("Default implementation of ProcessInvokeOnStatus in application {0}: Request:\n{1}", Application.Name, request.ToString()); return true; } //1. Test and see if this connection is an outbound RTMP connection //and get a pointer to it if (pFrom.Type != ProtocolTypes.PT_OUTBOUND_RTMP) { Logger.FATAL("This is not an outbound connection"); return false; } var pProtocol = (OutboundRTMPProtocol)pFrom; //2. Validate the request if (request.InvokeParam[1]["code"] != VariantType.String) { Logger.FATAL("invalid onStatus:\n{0}", request.Body.ToString()); return false; } //6. Get our hands on streaming parameters var path = NeedsToPullExternalStream(pFrom) ? "externalStreamConfig" : "localStreamConfig"; Variant parameters = pFrom.CustomParameters["customParameters"][path]; if (NeedsToPullExternalStream(pFrom)) { if (request.InvokeParam[1]["code"] != "NetStream.Play.Start") { Logger.WARN("onStatus message ignored:\n{0}", request.Body.ToString()); return true; } if (!Application.StreamNameAvailable(parameters["localStreamName"],pProtocol)) { Logger.WARN("Stream name {0} already occupied and application doesn't allow duplicated inbound network streams", parameters["localStreamName"]); return false; } var pStream = pProtocol.CreateINS(request.ChannelId,request.StreamId, parameters["localStreamName"]); if (pStream == null) { Logger.FATAL("Unable to create stream"); return false; } var waitingSubscribers = Application.StreamsManager.GetWaitingSubscribers(pStream.Name, pStream.Type); foreach (var waitingSubscriber in waitingSubscribers) { pStream.Link(waitingSubscriber); } } else { if (request.InvokeParam[1]["code"] != "NetStream.Publish.Start") { Logger.WARN("onStatus message ignored:\n{0}", request.ToString()); return true; } var pBaseInStream = (IInStream)Application.StreamsManager.FindByUniqueId(parameters["localUniqueStreamId"]); if (pBaseInStream == null) { Logger.FATAL("Unable to find the inbound stream with id {0}", parameters["localUniqueStreamId"]); return false; } //5. Create the network outbound stream var pBaseOutNetRTMPStream = pProtocol.CreateONS( request.StreamId, pBaseInStream.Name, pBaseInStream.Type); if (pBaseOutNetRTMPStream == null) { Logger.FATAL("Unable to create outbound stream"); return false; } pBaseOutNetRTMPStream.SendOnStatusPlayMessages = false; //6. Link and return if (!pBaseInStream.Link(pBaseOutNetRTMPStream)) { Logger.FATAL("Unable to link streams"); return false; } } return true; }
private bool TryLinkToFileStream(BaseRTMPProtocol pFrom, uint streamId, Variant metadata, string streamName, double startTime, double length, out bool linked) { linked = false; //1. Try to create the in file streams InFileRTMPStream pRTMPInFileStream = pFrom.CreateIFS(metadata); if (pRTMPInFileStream == null) { Logger.WARN("No file streams found: {0}", streamName); return true; } //2. Try to create the out net stream BaseOutNetRTMPStream pBaseOutNetRTMPStream = pFrom.CreateONS( streamId, streamName, pRTMPInFileStream.Type); if (pBaseOutNetRTMPStream == null) { Logger.FATAL("Unable to create network outbound stream"); return false; } //3. Link them if (!pRTMPInFileStream.Link(pBaseOutNetRTMPStream)) { Logger.FATAL("Link failed"); return false; } //4. Register it to the signaled streams //pFrom.SignalONS(pBaseOutNetRTMPStream); //5. Fire up the play routine if (!pRTMPInFileStream.Play(startTime, length)) { Logger.FATAL("Unable to start the playback"); return false; } //6. Done linked = true; return true; }
private bool ProcessInvokeCreateStreamResult(BaseRTMPProtocol pFrom,AmfMessage request, AmfMessage response) { //1. Do we need to push/pull a stream? if ((!NeedsToPullExternalStream(pFrom)) && (!NeedsToPushLocalStream(pFrom))) { Logger.WARN("Default implementation of ProcessInvokeCreateStreamResult: Request:\n{0}\nResponse:\n{1}", request.ToString(), response.ToString()); return true; } //2. Test and see if this connection is an outbound RTMP connection //and get a pointer to it if (pFrom.Type != ProtocolTypes.PT_OUTBOUND_RTMP) { Logger.FATAL("This is not an outbound connection"); return false; } var pProtocol = (OutboundRTMPProtocol)pFrom; //3. Test the response if (response.InvokeFunction != Defines.RM_INVOKE_FUNCTION_RESULT || response.InvokeParam[1] != VariantType.Numberic) { Logger.FATAL("createStream failed:\n{0}", response.ToString()); return false; } //4. Get the assigned stream ID uint rtmpStreamId = response.InvokeParam[1]; //5. Create the neutral stream var pStream = pProtocol.CreateNeutralStream(ref rtmpStreamId); if (pStream == null) { Logger.FATAL("Unable to create neutral stream"); return false; } //6. Get our hands on streaming parameters var path = NeedsToPullExternalStream(pFrom) ? "externalStreamConfig" : "localStreamConfig"; var parameters = pFrom.CustomParameters["customParameters"][path]; //7. Create publish/play request AmfMessage publishPlayRequest; if (NeedsToPullExternalStream(pFrom)) { publishPlayRequest = StreamMessageFactory.GetInvokePlay(3, rtmpStreamId, parameters["localStreamName"], -2, -1); } else { string targetStreamType = parameters["targetStreamType"]; if ((targetStreamType != "live") && (targetStreamType != "record") && (targetStreamType != "append")) { targetStreamType = "live"; } publishPlayRequest = StreamMessageFactory.GetInvokePublish(3, rtmpStreamId, parameters["targetStreamName"], targetStreamType); } //8. Send it if (!SendRTMPMessage(pFrom, publishPlayRequest, true)) { Logger.FATAL("Unable to send request:\n{0}", publishPlayRequest.ToString()); return false; } //9. Done return true; }
private bool ConnectForPullPush(BaseRTMPProtocol pFrom, string uriPath, Variant streamConfig, bool isPull) { var uri = new Uri(streamConfig[uriPath]); var appName = isPull ? uri.Segments[1] : uri.PathAndQuery; if (!string.IsNullOrEmpty(appName)) { appName = appName.Trim('/'); } if (string.IsNullOrEmpty(appName)) { Logger.FATAL("invalid uri:{0}", uri.OriginalString); return false; } if (!string.IsNullOrEmpty(uri.UserInfo)) { var userInfo = uri.UserInfo.Split(':'); if (streamConfig["auth"] != null) { var user = userInfo[0]; var password = userInfo[1]; var salt = (string)streamConfig["auth"]["salt"]; var opaque = (string)streamConfig["auth"]["opaque"]; var challenge = (string)streamConfig["auth"]["challenge"]; var md5 = MD5.Create(); var response = Convert.ToBase64String( md5.ComputeHash( Encoding.ASCII.GetBytes( Convert.ToBase64String( md5.ComputeHash(Encoding.ASCII.GetBytes(user + salt + password))) + opaque + challenge))); appName += "?authmod=abobe" + "&user="******"&challenge=" + challenge + "&opaque=" + opaque + "&salt=" + salt + "&response=" + response; streamConfig["emulateUserAgent"] = "FMLE/3.0 (compatible; FMSc/1.0)"; } else { appName = appName + "?authmod=adobe&user="******"emulateUserAgent"] = "FMLE/3.0 (compatible; FMSc/1.0)"; } } // //3. Compute tcUrl: rtmp://host/appName // string tcUrl = format("%s://%s%s/%s", // STR(uri.scheme()), // STR(uri.host()), // STR(uri.portSpecified() ? format(":%"PRIu32) : ""), // STR(appName)); //4. Get the user agent var userAgent = (string)streamConfig["emulateUserAgent"]; if (string.IsNullOrEmpty(userAgent)) { userAgent = Defines.HTTP_HEADERS_SERVER_US; } var tcUrl = (string)streamConfig["tcUrl"]; var swfUrl = (string)streamConfig["swfUrl"]; var pageUrl = (string)streamConfig["pageUrl"]; //6. Prepare the connect request var connectRequest = ConnectionMessageFactory.GetInvokeConnect( appName, //string appName tcUrl, //string tcUrl 3191, //double audioCodecs 239, //double capabilities userAgent, //string flashVer false, //bool fPad pageUrl, //string pageUrl swfUrl, //string swfUrl 252, //double videoCodecs 1, //double videoFunction 0 //double objectEncoding ); //7. Send it if (!SendRTMPMessage(pFrom, connectRequest, true)) { Logger.FATAL("Unable to send request:\n{0}", connectRequest.ToString()); return false; } return true; }
public bool PushLocalStream(BaseRTMPProtocol pFrom) { //1. Get the stream configuration and the URI from it var streamConfig = pFrom.CustomParameters["customParameters"]["localStreamConfig"]; //2. Issue the connect invoke return ConnectForPullPush(pFrom, "targetUri", streamConfig, false); }
public bool PullExternalStream(BaseRTMPProtocol pFrom) { //1. Get the stream configuration and the URI from it var streamConfig = pFrom.CustomParameters["customParameters"]["externalStreamConfig"]; _externalStreamProtocol = pFrom; //2. Issue the connect invoke return ConnectForPullPush(pFrom, "uri", streamConfig, true); }
private bool ProcessInvokeConnectResult(BaseRTMPProtocol pFrom, AmfMessage request, AmfMessage response) { //1. Do we need to push/pull a stream? if ((!NeedsToPullExternalStream(pFrom)) && (!NeedsToPushLocalStream(pFrom))) { Logger.WARN("Default implementation of ProcessInvokeConnectResult: Request:\n{0}\nResponse:\n{1}", request.ToString(), response.ToString()); return true; } //2. See if the result is OK or not if (response.InvokeFunction != Defines.RM_INVOKE_FUNCTION_RESULT) { if (response.InvokeFunction != Defines.RM_INVOKE_FUNCTION_ERROR || response.InvokeParam != VariantType.Map || response.InvokeParam.ArrayLength < 2 || response.InvokeParam[1] != VariantType.Map || response.InvokeParam[1]["level"] != "error" || response.InvokeParam[1]["code"] != "NetConnection.Connect.Rejected" || response.InvokeParam[1]["description"] == "" ) { Logger.FATAL("Connect failed:\n{0}", response.ToString()); return false; } string description = response.InvokeParam[1]["description"]; var parts = description.Split('?'); if (parts.Length != 2) { Logger.FATAL("Connect failed:\n{0}", response.ToString()); return false; } description = parts[1]; var _params = description.GetURLParam(); if (!_params.ContainsKey("reason") || !_params.ContainsKey("user") || !_params.ContainsKey("salt") || !_params.ContainsKey("challenge") || !_params.ContainsKey("opaque") || _params["reason"] != "needauth") { Logger.FATAL("Connect failed:\n{0}", response.ToString()); return false; } var customParameters = pFrom.CustomParameters; var streamConfig = NeedsToPullExternalStream(pFrom) ? customParameters["customParameters", "externalStreamConfig"] : customParameters["customParameters", "localStreamConfig"]; foreach (var param in _params) { streamConfig["auth"].Add(param.Key, param.Value); } return false; } if (response.InvokeParam[1] != VariantType.Map || response.InvokeParam[1]["code"] != "NetConnection.Connect.Success") { Logger.FATAL("Connect failed:\n{0}", response.ToString()); return false; } if (NeedsToPullExternalStream(pFrom)) { var parameters = pFrom.CustomParameters["customParameters","externalStreamConfig"]; if (!SendRTMPMessage(pFrom, StreamMessageFactory.GetInvokeFCSubscribe(parameters["localStreamName"]), true)) { return false; } } //3. Create the createStream request //4. Send it if (!SendRTMPMessage(pFrom, StreamMessageFactory.GetInvokeCreateStream(), true)) { return false; } //5. Done 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; } }
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; } }
private bool NeedsToPullExternalStream(BaseRTMPProtocol pFrom) { var parameters = pFrom.CustomParameters; if (parameters["customParameters","externalStreamConfig","uri"] == null) return false; return true; }
private bool TryLinkToLiveStream(BaseRTMPProtocol pFrom, uint streamId, string streamName, out bool linked) { linked = false; //1. Get get the short version of the stream name var parts = streamName.Split('?'); var shortName = parts[0]; //2. Search for the long version first var inboundStreams = Application.StreamsManager.FindByTypeByName(StreamTypes.ST_IN_NET, streamName, true, false); //3. Search for the short version if necessary if (inboundStreams.Count == 0) inboundStreams = Application.StreamsManager.FindByTypeByName(StreamTypes.ST_IN_NET, shortName + "?", true, true); //4. Do we have some streams? if (inboundStreams.Count == 0) { Logger.WARN("No live streams found: `{0}` or `{1}`", streamName, shortName); return true; } //5. Get the first stream in the inboundStreams var pBaseInNetStream = inboundStreams.Values.OfType<IInStream>() .FirstOrDefault( x => x.IsCompatibleWithType(StreamTypes.ST_OUT_NET_RTMP_4_TS) || x.IsCompatibleWithType(StreamTypes.ST_OUT_NET_RTMP_4_RTMP)); if (pBaseInNetStream == null) { Logger.WARN("No live streams found: `{0}` or `{1}`", streamName, shortName); return true; } //6. Create the outbound stream var pBaseOutNetRTMPStream = pFrom.CreateONS(streamId, streamName, (pBaseInNetStream as InClusterStream)?.ContentStreamType ?? pBaseInNetStream.Type); if (pBaseOutNetRTMPStream == null) { Logger.FATAL("Unable to create network outbound stream"); return false; } //7. Link them if (!pBaseInNetStream.Link(pBaseOutNetRTMPStream)) { Logger.FATAL("Link failed"); return false; } //8. Done linked = true; return true; }