예제 #1
0
        /// <summary>
        /// Decode the amfobject when NetConnection has a successfull connnection.
        /// Don't know if it's of any use and if the same info is available for
        /// different RTMP servers. (Only tested with wowza 3.5.2)
        /// </summary>
        /// <param name="obj"></param>
        private void DecodeNetConnectionInfo_Connect_Result(AMFObject obj)
        {
            netConnectionConnectInfo.Clear();

            // WOWZA: In position 2 and 3 is the info we want as
            // Red5: has all it's info in [3] it seems
            if (obj.Count < 3)
            {
                return;
            }

            List<AMFObjectProperty> props = new List<AMFObjectProperty>();
            props.Clear();

            obj.FindMatchingProperty("fmsVer", props, 1);
            if (props.Count > 0)
            {
                netConnectionConnectInfo.FMSVer = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("capabilities", props, 1);
            if (props.Count > 0)
            {
                try
                {
                    netConnectionConnectInfo.Capabilities = Convert.ToInt32(props[0].NumberValue);
                }
                catch { }
            }
            props.Clear();
            obj.FindMatchingProperty("mode", props, 1);
            if (props.Count > 0)
            {
                try
                {
                    netConnectionConnectInfo.Mode = Convert.ToInt32(props[0].NumberValue);
                }
                catch { }
            }

            props.Clear();
            obj.FindMatchingProperty("code", props, 1);
            if (props.Count > 0)
            {
                netConnectionConnectInfo.Code = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("level", props, 1);
            if (props.Count > 0)
            {
                netConnectionConnectInfo.Level = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("description", props, 1);
            if (props.Count > 0)
            {
                netConnectionConnectInfo.Description = props[0].StringValue;
            }

            props.Clear();
            obj.FindMatchingProperty("data", props, 1);
            if (props.Count > 0)
            {
                AMFObject obj2 = props[0].ObjectValue;
                props.Clear();
                obj2.FindMatchingProperty("version", props, 1);
                if (props.Count > 0)
                {
                    try
                    {
                        netConnectionConnectInfo.Version = new Version(props[0].StringValue.Replace(',', '.'));
                        realRTMPServerVersion = netConnectionConnectInfo.Version;
                    }
                    catch { }
                }
            }

            // Red5 doesn't seem to have this property
            props.Clear();
            obj.FindMatchingProperty("clientid", props, 1);
            if (props.Count > 0)
            {
                try
                {
                    netConnectionConnectInfo.ClientID = Convert.ToInt64(props[0].NumberValue);
                }
                catch { }
            }
            // Red5 doesn't seem to have this property
            props.Clear();
            obj.FindMatchingProperty("objectEncoding", props, 1);
            if (props.Count > 0)
            {
                try
                {
                    netConnectionConnectInfo.ObjectEncoding = Convert.ToInt32(props[0].NumberValue);
                }
                catch { }
            }
        }
예제 #2
0
        [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion
        virtual internal bool HandleOnMetaData(RTMPPacket packet)
        {
            AMFObject obj = new AMFObject();
            int nRes = obj.Decode(packet.Body, 0, (int)packet.BodySize, false);
            if (nRes < 0)
            {
                LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetStream.HandleOnMetaData] Error decoding meta data packet");
                return false;
            }

            /* For video:
             *   canSeekToEnd = true
             *   videocodecid = 4
             *   framerate = 15
             *   videodatarate = 400
             *   height = 215
             *   width = 320
             *   duration = 7.347
             *   
             * For Audio (MP3): metastring =="onID3"
             * 
             */


            string metastring = obj.GetProperty(0).StringValue;
            switch (metastring)
            {
                case "onMetaData":
                    List<AMFObjectProperty> props = new List<AMFObjectProperty>();

                    props.Clear();
                    obj.FindMatchingProperty("audiodatarate", props, 1);
                    if (props.Count > 0)
                    {
                        int rate = (int)props[0].NumberValue;
                        audioDatarate += rate;
                        LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] audiodatarate: {0}", audioDatarate));
                    }
                    props.Clear();
                    obj.FindMatchingProperty("videodatarate", props, 1);
                    if (props.Count > 0)
                    {
                        int rate = (int)props[0].NumberValue;
                        videoDatarate += rate;
                        LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] videodatarate: {0}", videoDatarate));
                    }
                    if (audioDatarate == 0 && videoDatarate == 0)
                    {
                        props.Clear();
                        obj.FindMatchingProperty("filesize", props, int.MaxValue);
                        if (props.Count > 0)
                        {
                            combinedTracksLength = (int)props[0].NumberValue;
                            LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] Set CombinedTracksLength from filesize: {0}", combinedTracksLength));
                        }
                    }
                    if (combinedTracksLength == 0)
                    {
                        props.Clear();
                        obj.FindMatchingProperty("datasize", props, int.MaxValue);
                        if (props.Count > 0)
                        {
                            combinedTracksLength = (int)props[0].NumberValue;
                            LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] Set CombinedTracksLength from datasize: {0}", combinedTracksLength));
                        }
                    }
                    props.Clear();
                    obj.FindMatchingProperty("duration", props, 1);
                    if (props.Count > 0)
                    {
                        double duration = props[0].NumberValue;
                        LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] Set duration: {0}", duration));
                        lock (lockVAR)
                        {
                            metaDataDurationInMS = Convert.ToInt64(duration * 1000);
                        }

                        // Looks the same as the "onPlayStatus" (See NetConnection.HandleMetadata (we route it there through HandleOnStatusDecoded
                        // doing it here also)
                        Internal_HandleOnStatusDecoded(packet, "onStatus", "NetStream.Play.OnMetaData", "onPlayStatus", obj);
                    }
                    break;
                // Looks more as an invoke to me Let NetStream.OnStatus handle it
                case "onStatus": // -=> "NetStream.Data.Start"
                    Internal_HandleOnStatusDecoded(packet, "onStatus", obj.GetProperty(1).ObjectValue.GetProperty("code").StringValue, "", obj);
                    break;
                case "onPlayStatus":
                    // "code" = "NetStream.Play.Switch"
                    // "level"= "status" ,made it "onPlayStatus" 
                    // Has also "duration" and "bytes" as additional metadata (looks like a normal onMetaData to me with less options as the normal)
                    Internal_HandleOnStatusDecoded(packet, "onStatus", obj.GetProperty(1).ObjectValue.GetProperty("code").StringValue, "onPlayStatus", obj);
                    break;
                case "onID3":
                    return HandleOnID3(obj);
                default:
                    LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.NetStream.HandleOnMetaData] metastring= {0}", metastring));
                    break;
            } //switch

            return true;
        }
예제 #3
0
        /// <summary>
        /// Analyzes and responds if required to the given <see cref="RTMPPacket"/>.
        /// </summary>
        /// <param name="packet">The packet to inspect and react to.</param>
        /// <returns>0 (false) for OK/Failed/error, 1 for 'Stop or Complete' (true)</returns>
        private bool HandleInvoke(RTMPPacket packet)
        {
            bool ret = false;

            if (packet.Body[0] != 0x02) // make sure it is a string method name we start with
            {
                LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection.HandleInvoke] Sanity failed. no string method in invoke packet");
                return false;
            }

            AMFObject obj = new AMFObject();
            int nRes = obj.Decode(packet.Body, 0, (int)packet.BodySize, false);
            if (nRes < 0)
            {
                LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection.HandleInvoke] Error decoding invoke packet");
                return false;
            }

            obj.Dump();
            string method = obj.GetProperty(0).StringValue;
            double txn = obj.GetProperty(1).NumberValue;

            LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] Server invoking <{0}>", method));

            if (method == "_result")
            {
                int transactionResultNum = (int)obj.GetProperty(1).NumberValue;
                string methodInvoked = "";
                if (methodCallDictionary.ContainsKey(transactionResultNum))
                {
                    methodInvoked = methodCallDictionary[transactionResultNum];
                    methodCallDictionary.Remove(transactionResultNum);
                }

                LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] received result for method call <{0}>", methodInvoked));

                if (methodInvoked == "connect")
                {
                    // Get some info out of the result connection
                    DecodeNetConnectionInfo_Connect_Result(obj);

                    // Is SecureToken activate (when using wowza server)
                    string tmpSecureTokenPassword = string.Empty;
                    lock (lockVAR)
                    {
                        tmpSecureTokenPassword = secureTokenPassword;
                    }
                    if (!string.IsNullOrEmpty(tmpSecureTokenPassword))
                    {
                        List<AMFObjectProperty> props = new List<AMFObjectProperty>();
                        obj.FindMatchingProperty("secureToken", props, int.MaxValue);
                        if (props.Count > 0)
                        {
#if INCLUDE_TMPE
#endif
                        }
                    }
                    MQInternal_SendServerBW();

                    // Send OnConnect event
                }
                else if (methodInvoked == "createStream")
                {
                    int transactionNum = (int)obj.GetProperty(1).NumberValue;
                    int stream_id = (int)obj.GetProperty(3).NumberValue;

                    if (transactionIDReferenceTable.ContainsKey(transactionNum) && transactionIDReferenceTable[transactionNum] is NetStream)
                    {
                        NetStream netStream = (NetStream)transactionIDReferenceTable[transactionNum];
                        transactionIDReferenceTable.Remove(transactionNum);
                        LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] Received createStream(stream_id={0})", stream_id));

                        netStream.Stream_ID = stream_id;
                        // make sure we know which NetStreams use this NetCOnnection
                        RegisterNetStream(netStream);

                        int contentBufferTime = NetConnection.DefaultContentBufferTime;
                        netStream.HandleOnAssignStream_ID(packet, stream_id, out contentBufferTime);
                        if (contentBufferTime <= 0)
                        {
                            contentBufferTime = NetConnection.DefaultContentBufferTime;
                        }
                        // Tell buffer time we want to use for this channel
                        MQ_SendPing(3, (uint)netStream.Stream_ID, (uint)contentBufferTime);
                    }
                    else
                    {
                        // We haven't found a NetStream for which this is intended, so delete it again
                        MQ_SendDeleteStream(stream_id);
                    }
                }
                else if (methodInvoked == "play")
                {
                    // Server send the play command?
                }
            }
            else if (method == "onBWDone")
            {
                if (bwCheckCounter == 0)
                {
                    MQInternal_SendCheckBW();
                }
            }
            else if (method == "_onbwcheck")
            {
                MQInternal_SendCheckBWResult(txn);
            }
            else if (method == "_onbwdone")
            {
                int transactionResultNum = (int)obj.GetProperty(1).NumberValue;
                if (methodCallDictionary.ContainsValue("_checkbw"))
                {
                    var item = methodCallDictionary.First(x => x.Value == "_checkbw");
                    methodCallDictionary.Remove(item.Key);
                }
            }
            else if (method == "_error")
            {
                LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection.HandleInvoke] rtmp server sent error");
            }
            else if (method == "close")
            {
                LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection.HandleInvoke] rtmp server requested close");
                CloseConnection();
            }
            else if (method == "onStatus")
            {
                int stream_id = packet.InfoField2;
                string code = obj[3].ObjectValue.GetProperty("code").StringValue;
                string level = obj[3].ObjectValue.GetProperty("level").StringValue;

                LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] stream_id={0}, method={1}, code={2}, level={3}", stream_id, method, code, level));
                // Zoek NetStream op en geef OnStatus door
                if (netStreams[stream_id] != null)
                {
                    if (code == "NetStream.Pause.Notify")
                    {
                        // fix to help netstream
                        packet.TimeStamp = channelTimestamp[netStreams[stream_id].MediaChannel];
                    }
                    netStreams[stream_id].HandleOnStatus(packet);
                }
                else
                {
                    LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] UNHANDLED | stream_id={0}, method={1}, code={2}, level={3}", stream_id, method, code, level));
                }
            }
            else if (dMethodLookup.ContainsKey(method))
            {
                ret = true;

                SynchronizationContext sc;
                State_NC_MethodCall stateMethodCall = new State_NC_MethodCall();
                lock (lockVAR)
                {
                    sc = synchronizationContext;
                    stateMethodCall.Call = dMethodLookup[method];
                } //lock
                stateMethodCall.thisObject = this;
                stateMethodCall.MethodParam = obj;

                if (sc != null)
                {
                    switch (synchronizationContextMethod)
                    {
                        case SynchronizationContextMethod.Post:
                            sc.Post(HandleOnMethodCall, stateMethodCall);
                            break;
                        case SynchronizationContextMethod.Send:
                            sc.Send(HandleOnMethodCall, stateMethodCall);
                            break;
                    } //switch
                }
                else
                {
                    HandleOnMethodCall(stateMethodCall);
                }
            }
            else
            {
                LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] [EVENT]={0}", method));
            }

            return ret;
        }
예제 #4
0
        [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion
        virtual internal bool HandleOnID3(AMFObject obj)
        {
            LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection.NetStream.HandleOnID3]");
            obj.Dump();
            audioMetaData.Clear();
            audioMetaData.Valid = true;
            List<AMFObjectProperty> props = new List<AMFObjectProperty>();

            props.Clear();
            obj.FindMatchingProperty("v1SongTitle", props, 1);
            if (props.Count > 0)
            {
                audioMetaData.SongTitle = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("v1LeadArtist", props, 1);
            if (props.Count > 0)
            {
                audioMetaData.LeadArtist = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("v1AlbumTitle", props, 1);
            if (props.Count > 0)
            {
                audioMetaData.AlbumTitle = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("v1YearReleased", props, 1);
            if (props.Count > 0)
            {
                audioMetaData.YearReleased = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("v1SongComment", props, 1);
            if (props.Count > 0)
            {
                audioMetaData.SongComment = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("v1SongGenre", props, 1);
            if (props.Count > 0)
            {
                audioMetaData.SongGenre = props[0].StringValue;
            }
            props.Clear();
            obj.FindMatchingProperty("v1TrackNumberOnAlbum", props, 1);
            if (props.Count > 0)
            {
                audioMetaData.TrackNumberOnAlbum = props[0].StringValue;
            }


            // handle event
            if (OnID3 != null)
            {
                MQ_RTMPMessage message = new MQ_RTMPMessage();
                message.MethodCall = MethodCall.OnEventCallUserCode;
                message.Params = new object[] { OnID3, this, (AudioMetaData)audioMetaData.Clone(), obj };
                netConnection.PostOnEventUserCallCodeMessage(message);
            }

            return true;
        }