private bool MQInternal_SendBytesReceived()
        {
            RTMPPacket packet = new RTMPPacket();
            packet.Channel = 0x02;   // control channel (invoke)
            packet.HeaderType = HeaderType.Medium;
            packet.PacketType = PacketType.BytesReadReport;

            packet.AllocPacket(4);
            packet.BodySize = 4;

            List<byte> enc = new List<byte>();
            lock (lockVAR)
            {
                RTMPHelper.EncodeInt32(enc, bytesReadTotal);
                packet.BodySize = (uint)enc.Count;
                packet.Body = enc.ToArray();

                lastSentBytesRead = bytesReadTotal;
                LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] Send bytes report. ({0} bytes)", bytesReadTotal));
            }
            return MQInternal_SendPacket(packet, false);
        }
        private bool MQInternal_SendCheckBWResult(double txn)
        {
            RTMPPacket packet = new RTMPPacket();
            packet.Channel = 0x03;   // control channel (invoke)
            packet.HeaderType = HeaderType.Medium;
            packet.PacketType = PacketType.Invoke;
            packet.TimeStamp = (uint)(0x16 * bwCheckCounter); // temp inc value. till we figure it out.

            packet.AllocPacket(256); // should be enough
            List<byte> enc = new List<byte>();
            RTMPHelper.EncodeString(enc, "_result");
            RTMPHelper.EncodeNumber(enc, txn);
            enc.Add(0x05); // NULL
            RTMPHelper.EncodeNumber(enc, (double)bwCheckCounter++);

            packet.BodySize = (uint)enc.Count;
            packet.Body = enc.ToArray();

            return MQInternal_SendPacket(packet, false);
        }
        private bool MQInternal_SendServerBW()
        {
            RTMPPacket packet = new RTMPPacket();
            packet.Channel = 0x02;   // control channel (invoke)
            packet.HeaderType = HeaderType.Large;
            packet.PacketType = PacketType.ServerBW;

            packet.AllocPacket(4);
            packet.BodySize = 4;

            List<byte> bytesToSend = new List<byte>();
            RTMPHelper.EncodeInt32(bytesToSend, serverBW); // was hard coded : 0x001312d0
            packet.Body = bytesToSend.ToArray();
            return MQInternal_SendPacket(packet, false);
        }
        private bool MQ_SendCreateStream(NetStream netStream)
        {
            int transactionNum = ++numInvokes;
            // Put netStream in transaction reference table so we can match it up when the rtmp server
            // give us the result back
            transactionIDReferenceTable[transactionNum] = netStream;

            RTMPPacket packet = new RTMPPacket();
            packet.Channel = 0x03;   // control channel (invoke)
            packet.HeaderType = HeaderType.Medium;
            packet.PacketType = PacketType.Invoke;

            LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection] Sending createStream");
            packet.AllocPacket(256); // should be enough
            List<byte> enc = new List<byte>();
            RTMPHelper.EncodeString(enc, "createStream");
            RTMPHelper.EncodeNumber(enc, transactionNum);
            enc.Add(0x05); // NULL

            packet.BodySize = (uint)enc.Count;
            packet.Body = enc.ToArray();

            methodCallDictionary.Add(transactionNum, "createStream");

            return MQInternal_SendPacket(packet);
        }
        /// <summary>
        /// Send Connect after NetConnection Handshake has finished
        /// </summary>
        private bool MQInternal_NetConnection_SendConnect(params AMFObjectProperty[] amfProperties)
        {
            // Work with copy of var so we don't have to lock to much, for to long
            ServerLink link;
            lock (lockVAR)
            {
                link = (ServerLink)serverLink.Clone();
            }

            RTMPPacket packet = new RTMPPacket();
            packet.Channel = 0x03;   // control channel (invoke)
            packet.HeaderType = HeaderType.Large;
            packet.PacketType = PacketType.Invoke;
            packet.AllocPacket(4096);

            LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection] Sending connect");
            List<byte> enc = new List<byte>();
            RTMPHelper.EncodeString(enc, "connect");
            int transactionNum = ++numInvokes;
            RTMPHelper.EncodeNumber(enc, transactionNum);
            methodCallDictionary.Add(transactionNum, "connect");
            enc.Add(0x03); //Object Datatype
            RTMPHelper.EncodeString(enc, "app", link.Application); LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] app : {0}", link.Application));
            if (String.IsNullOrEmpty(swfFlashVer))
            {
                RTMPHelper.EncodeString(enc, "flashVer", "WIN 10,0,32,18");
            }
            else
            {
                RTMPHelper.EncodeString(enc, "flashVer", swfFlashVer);
            }
            if (!string.IsNullOrEmpty(swfURL)) RTMPHelper.EncodeString(enc, "swfUrl", swfURL);
            RTMPHelper.EncodeString(enc, "tcUrl", link.URL); LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] tcUrl : {0}", link.URL));
            RTMPHelper.EncodeBoolean(enc, "fpad", false);
            RTMPHelper.EncodeNumber(enc, "capabilities", 15.0);
            RTMPHelper.EncodeNumber(enc, "audioCodecs", 3191.0);
            RTMPHelper.EncodeNumber(enc, "videoCodecs", 252.0);
            RTMPHelper.EncodeNumber(enc, "videoFunction", 1.0);
            if (!string.IsNullOrEmpty(swfPageURL))
            {
                RTMPHelper.EncodeString(enc, "pageUrl", swfPageURL);
            }
            enc.Add(0); enc.Add(0); enc.Add(0x09); // end of object - 0x00 0x00 0x09
            // add auth string
            if (!string.IsNullOrEmpty(swfAuth))
            {
                RTMPHelper.EncodeBoolean(enc, true);
                RTMPHelper.EncodeString(enc, swfAuth);
            }
            //EncodeNumber(enc, "objectEncoding", 0.0);
            if (amfProperties != null)
            {
                foreach (AMFObjectProperty prop in amfProperties)
                {
                    prop.Encode(enc);
                } //foreach

                List<byte> objEnc = new List<byte>();
            }

            Array.Copy(enc.ToArray(), packet.Body, enc.Count);
            packet.BodySize = (uint)enc.Count;

            return MQInternal_SendPacket(packet);
        }
        /// <summary>
        /// The type of Ping packet is 0x4 and contains two mandatory parameters and two optional parameters.
        /// The first parameter is the type of Ping (short integer).
        /// The second parameter is the target of the ping.
        /// As Ping is always sent in Channel 2 (control channel) and the target object in RTMP header is always 0
        /// which means the Connection object,
        /// it's necessary to put an extra parameter to indicate the exact target object the Ping is sent to.
        /// The second parameter takes this responsibility.
        /// The value has the same meaning as the target object field in RTMP header.
        /// (The second value could also be used as other purposes, like RTT Ping/Pong. It is used as the timestamp.)
        /// The third and fourth parameters are optional and could be looked upon as the parameter of the Ping packet.
        /// Below is an unexhausted list of Ping messages.
        /// type 0: Clear the stream. No third and fourth parameters.
        ///         The second parameter could be 0. After the connection
        ///         is established, a Ping 0,0 will be sent from server
        ///         to client. The message will also be sent to client on
        ///         the start of Play and in response of a Seek or
        ///         Pause/Resume request. This Ping tells client
        ///         to re-calibrate the clock with the timestamp of the
        ///         next packet server sends.
        /// type 1: Tell the stream to clear the playing buffer.
        /// type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
        /// type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
        /// type 6: Ping the client from server. The second parameter is the current time.
        /// type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
        /// type 26: SWFVerification request
        /// type 27: SWFVerification response
        /// type 31: Buffer empty
        /// type 32: Buffer full
        /// </summary>
        /// <param name="nType"></param>
        /// <param name="nObject"></param>
        /// <param name="nTime"></param>
        /// <returns></returns>
        private bool MQ_SendPing(short nType, uint nObject, uint nTime)
        {
            LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.MQ_SendPing] Sending ping type: {0}", nType));

            RTMPPacket packet = new RTMPPacket();
            packet.Channel = 0x02;   // control channel (ping)
            packet.HeaderType = HeaderType.Medium;
            packet.PacketType = PacketType.Control;
            //packet.m_nInfoField1 = System.Environment.TickCount;

            int nSize = (nType == 0x03 ? 10 : 6); // type 3 is the buffer time and requires all 3 parameters. all in all 10 bytes.
            if (nType == 0x1B) nSize = 44;
            packet.AllocPacket(nSize);
            packet.BodySize = (uint)nSize;

            List<byte> buf = new List<byte>();
            RTMPHelper.EncodeInt16(buf, nType);

            if (nType == 0x1B)
            {
                buf.AddRange(swfVerificationResponse);
            }
            else
            {
                if (nSize > 2)
                {
                    RTMPHelper.EncodeInt32(buf, (int)nObject);
                }

                if (nSize > 6)
                {
                    RTMPHelper.EncodeInt32(buf, (int)nTime);
                }
            }
            packet.Body = buf.ToArray();
            return MQInternal_SendPacket(packet, false);
        }