示例#1
0
 public void ReleaseChannel(Channel pChannel)
 {
     if (pChannel == null) return;
     _channels.Remove(pChannel.id);
     _channelPool.Push(pChannel.id);
     pChannel.ReturnPool();
 }
示例#2
0
 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;
            }
        }