public void ReleaseChannel(Channel pChannel) { if (pChannel == null) return; _channels.Remove(pChannel.id); _channelPool.Push(pChannel.id); pChannel.ReturnPool(); }
public bool Write(Channel channel, Stream writer) { if (channel.lastOutStreamId == StreamId) { if (IsAbsolute) { if (channel.lastOutProcBytes == 0) { HeaderType = HT_FULL; channel.lastOutAbsTs = TimeStramp; } else { HeaderType = HT_CONTINUATION; } } else { if (channel.lastOutProcBytes == 0) { HeaderType = HT_SAME_STREAM; if (MessageType == channel.lastOutHeader.MessageType && MessageLength == channel.lastOutHeader.MessageLength) { HeaderType = HT_SAME_LENGTH_AND_STREAM; if (TimeStramp == channel.lastOutHeader.TimeStramp) { HeaderType = HT_CONTINUATION; } } channel.lastOutAbsTs += TimeStramp; } else { HeaderType = HT_CONTINUATION; } } } else { HeaderType = HT_FULL; IsAbsolute = true; channel.lastOutAbsTs = TimeStramp; channel.lastOutStreamId = StreamId; } channel.lastOutHeader = this; return Write(writer); }
public bool Serialize(Channel channel, AmfMessage message, Stream stream, uint chuckSize) { Serialize(ref message); var header = message.Header; long available; InternalBuffer.Position = 0; while ((available = InternalBuffer.Length - InternalBuffer.Position) != 0) { header.Write(channel, stream); if (available >= chuckSize) { InternalBuffer.CopyPartTo(stream, (int)chuckSize); channel.lastOutProcBytes += chuckSize; } else { InternalBuffer.CopyPartTo(stream, (int)available); channel.lastOutProcBytes += (uint)available; } } //Debug.WriteLine(channel.lastOutProcBytes); channel.lastOutProcBytes = 0; InternalBuffer.Recycle(); return true; }
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; } }