public object Clone() { RTMPPacket clone = new RTMPPacket(); clone.headerType = headerType; clone.packetType = packetType; clone.hasAbsTimestamp = hasAbsTimestamp; clone.channel = channel; clone.timeStamp = timeStamp; clone.infoField2 = infoField2; clone.bytesRead = bytesRead; clone.bodySize = bodySize; clone.body = (byte[])body.Clone(); return clone; }
public object Clone() { RTMPPacket clone = new RTMPPacket(); clone.headerType = headerType; clone.packetType = packetType; clone.hasAbsTimestamp = hasAbsTimestamp; clone.channel = channel; clone.timeStamp = timeStamp; clone.infoField2 = infoField2; clone.bytesRead = bytesRead; clone.bodySize = bodySize; clone.body = (byte[])body.Clone(); return(clone); }
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_SendPause(NetStream netStream, bool doPause) { int transactionNum = ++numInvokes; RTMPPacket packet = new RTMPPacket(); packet.Channel = netStream.CommandChannel; // 0x08 ??? packet.HeaderType = HeaderType.Medium; packet.PacketType = PacketType.Invoke; List<byte> enc = new List<byte>(); RTMPHelper.EncodeString(enc, "pause"); RTMPHelper.EncodeNumber(enc, transactionNum); enc.Add(0x05); // NULL RTMPHelper.EncodeBoolean(enc, doPause); RTMPHelper.EncodeNumber(enc, (double)channelTimestamp[netStream.MediaChannel]); packet.Body = enc.ToArray(); packet.BodySize = (uint)enc.Count; LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection] Sending pause: ({0}), Time = {1}/{2}", doPause.ToString(), channelTimestamp[netStream.MediaChannel], TimeSpan.FromMilliseconds(Convert.ToInt32(channelTimestamp[netStream.MediaChannel])))); methodCallDictionary.Add(transactionNum, "pause"); return MQInternal_SendPacket(packet); }
private bool MQInternal_SendCheckBW() { int transactionNum = ++numInvokes; RTMPPacket packet = new RTMPPacket(); packet.Channel = 0x03; // control channel (invoke) packet.HeaderType = HeaderType.Large; packet.PacketType = PacketType.Invoke; //packet.m_nInfoField1 = System.Environment.TickCount; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection] Sending _checkbw"); List<byte> enc = new List<byte>(); RTMPHelper.EncodeString(enc, "_checkbw"); RTMPHelper.EncodeNumber(enc, transactionNum); enc.Add(0x05); // NULL packet.BodySize = (uint)enc.Count; packet.Body = enc.ToArray(); methodCallDictionary.Add(transactionNum, "_checkbw"); // triggers _onbwcheck and eventually results in _onbwdone return MQInternal_SendPacket(packet, false); }
private bool MQInternal_SendSecureTokenResponse(string resp) { RTMPPacket packet = new RTMPPacket(); packet.Channel = 0x03; /* control channel (invoke) */ packet.HeaderType = HeaderType.Medium; packet.PacketType = PacketType.Invoke; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] Sending SecureTokenResponse: {0}", resp)); List<byte> enc = new List<byte>(); RTMPHelper.EncodeString(enc, "secureTokenResponse"); RTMPHelper.EncodeNumber(enc, 0.0); enc.Add(0x05); // NULL RTMPHelper.EncodeString(enc, resp); packet.BodySize = (uint)enc.Count; packet.Body = enc.ToArray(); return MQInternal_SendPacket(packet, false); }
/// <summary> /// Stream_id checks "netStreams" /// Is orginal call pattern: private bool MQInternal_SendPlay(NetStream netStream, string mediafile, int start, int lenToPlay, bool resetPlayList, AMFObjectProperty properties) /// </summary> private bool MQInternal_SendPlay(NetStream netStream, string mediaFile, int start, int lenToPlay, bool resetPlayList, AMFObjectProperty properties) { RTMPPacket packet = new RTMPPacket(); packet.Channel = netStream.CommandChannel; // 0x08 ??? packet.HeaderType = HeaderType.Large; packet.PacketType = PacketType.Invoke; packet.InfoField2 = netStream.Stream_ID; List<byte> enc = new List<byte>(); RTMPHelper.EncodeString(enc, "play"); RTMPHelper.EncodeNumber(enc, 0); // 0 according to spec adobe enc.Add(0x05); // NULL RTMPHelper.EncodeString(enc, mediaFile); /* Optional parameters start and len. * * start: -2, -1, 0, positive number * -2: looks for a live stream, then a recorded stream, if not found any open a live stream * -1: plays a live stream * >=0: plays a recorded streams from 'start' milliseconds */ // RTMPHelper.EncodeNumber(enc, -1000.0d); (liveStream) if (start > 0) { RTMPHelper.EncodeNumber(enc, start); } else { RTMPHelper.EncodeNumber(enc, 0.0d); } // len: -1, 0, positive number // -1: plays live or recorded stream to the end (default) // 0: plays a frame 'start' ms away from the beginning // >0: plays a live or recoded stream for 'len' milliseconds RTMPHelper.EncodeNumber(enc, lenToPlay); // Reset. Optional wether to flush previous playlist if (properties == null) { RTMPHelper.EncodeBoolean(enc, resetPlayList); } else { properties.Encode(enc); } packet.Body = enc.ToArray(); packet.BodySize = (uint)enc.Count; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] Sending play: '{0}'", mediaFile)); return MQInternal_SendPacket(packet); }
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> /// Handle a received packet. /// </summary> /// <returns>0 - no media packet, 1 - media packet, 2 - play complete</returns> private HandlePacketResult HandleClientPacket(RTMPPacket packet) { HandlePacketResult result = HandlePacketResult.NoneMediaPacket; switch (packet.PacketType) { case PacketType.ChunkSize: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: ChunkSize.")); HandleChangeChunkSize(packet); break; case PacketType.BytesReadReport: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: Bytes read report.")); break; case PacketType.Control: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: Control.")); HandlePing(packet); break; case PacketType.ServerBW: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: ServerBW.")); HandleServerBW(packet); break; case PacketType.ClientBW: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: ClientBW.")); HandleClientBW(packet); break; case PacketType.Audio: case PacketType.Video: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: [Media(Audio/Video)] {0} bytes.", packet.BodySize)); HandleMedia(packet); result = HandlePacketResult.MediaPacket; break; case PacketType.Metadata: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: [MetaData] {0} bytes.", packet.BodySize)); HandleMetadata(packet); result = HandlePacketResult.MediaPacket; break; case PacketType.Invoke: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: [Invoke] {0} bytes.", packet.BodySize)); HandleInvoke(packet); break; case PacketType.FlvTags: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: [FlvTags] {0} bytes.", packet.BodySize)); HandleFlvTags(packet); result = HandlePacketResult.MediaPacket; break; default: LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection.HandleClientPacket] PACKET received: [Unknown packet] type {0}.", packet.PacketType)); break; } return result; }
/// <summary> /// Decode a RTMP packet from the stream /// </summary> private bool ReadPacket(out RTMPPacket packet) { packet = null; // eerst checken of er wel dat is if (!MQInternal_IsConnected) { RemoteServerDisconnected(); // niks te doen return false; } // Chunk Basic Header (1, 2 or 3 bytes) // the two most significant bits hold the chunk type // value in the 6 least significant bits gives the chunk stream id (0,1,2 are reserved): 0 -> 3 byte header | 1 -> 2 byte header | 2 -> low level protocol message | 3-63 -> stream id byte[] singleByteToReadBuffer = new byte[1]; if (ReadN(singleByteToReadBuffer, 0, 1) != 1) { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection] failed to read RTMP packet header"); return false; } byte type = singleByteToReadBuffer[0]; byte headerType = (byte)((type & 0xc0) >> 6); int channel = (byte)(type & 0x3f); if (channel == 0) { if (ReadN(singleByteToReadBuffer, 0, 1) != 1) { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection] failed to read RTMP packet header 2nd byte"); return false; } channel = singleByteToReadBuffer[0]; channel += 64; //header++; } else if (channel == 1) { int tmp; byte[] hbuf = new byte[2]; if (ReadN(hbuf, 0, 2) != 2) { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection] failed to read RTMP packet header 3rd and 4th byte"); return false; } tmp = ((hbuf[2]) << 8) + hbuf[1]; channel = tmp + 64; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] channel: {0}", channel)); //header += 2; } uint nSize = RTMPConst.PacketSize[headerType]; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] reading RTMP packet chunk on channel {0}, headersz {1}", channel, nSize)); if (nSize < RTMPConst.RTMP_LARGE_HEADER_SIZE) { // using values from the last message of this channel packet = vecChannelsIn[channel]; } else { packet = new RTMPPacket() { HeaderType = (HeaderType)headerType, Channel = channel, HasAbsTimestamp = true }; // new packet } nSize--; byte[] header = new byte[RTMPConst.RTMP_LARGE_HEADER_SIZE]; if (nSize > 0 && ReadN(header, 0, (int)nSize) != nSize) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection] failed to read RTMP packet header. type: {0}", type)); return false; } if (nSize >= 3) { packet.TimeStamp = (uint)RTMPHelper.ReadInt24(header, 0); if (nSize >= 6) { packet.BodySize = (uint)RTMPHelper.ReadInt24(header, 3); LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] new packet body to read {0}", packet.BodySize)); packet.BytesRead = 0; packet.Free(); // new packet body if (nSize > 6) { if (Enum.IsDefined(typeof(PacketType), header[6])) { packet.PacketType = (PacketType)header[6]; } else { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection] Unknown packet type received: {0}", header[6])); } if (nSize == 11) { packet.InfoField2 = RTMPHelper.ReadInt32LE(header, 7); } } } if (packet.TimeStamp == 0xffffff) { byte[] extendedTimestampDate = new byte[4]; if (ReadN(extendedTimestampDate, 0, 4) != 4) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection] failed to read extended timestamp"); return false; } packet.TimeStamp = (uint)RTMPHelper.ReadInt32(extendedTimestampDate, 0); } } if (packet.BodySize >= 0 && packet.Body == null && !packet.AllocPacket((int)packet.BodySize)) { //CLog::Log(LOGDEBUG,"%s, failed to allocate packet", __FUNCTION__); return false; } uint nToRead = packet.BodySize - packet.BytesRead; uint nChunk = (uint)InChunkSize; if (nToRead < nChunk) nChunk = nToRead; int read = ReadN(packet.Body, (int)packet.BytesRead, (int)nChunk); if (read != nChunk) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection] failed to read RTMP packet body. total:{0}/{1} chunk:{2}/{3}", packet.BytesRead, packet.BodySize, read, nChunk)); packet.Body = null; // we dont want it deleted since its pointed to from the stored packets (m_vecChannelsIn) return false; } packet.BytesRead += nChunk; // keep the packet as ref for other packets on this channel vecChannelsIn[packet.Channel] = packet.ShallowCopy(); if (packet.IsReady()) { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] packet with {0} bytes read", packet.BytesRead)); // make packet's timestamp absolute if (!packet.HasAbsTimestamp) { packet.TimeStamp += channelTimestamp[packet.Channel]; // timestamps seem to be always relative!! } channelTimestamp[packet.Channel] = packet.TimeStamp; // reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel // arrives and requests to re-use some info (small packet header) vecChannelsIn[packet.Channel].Body = null; vecChannelsIn[packet.Channel].BytesRead = 0; vecChannelsIn[packet.Channel].HasAbsTimestamp = false; // can only be false if we reuse header } return true; }
[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; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion virtual protected bool Internal_HandleOnStatusDecoded(RTMPPacket packet, string eventStr, string codeStr, string levelStr, AMFObject obj) { //LibRTMPLogger.Log(LibRTMPLogLevel.Error, string.Format("[CDR.LibRTMP.NetStream.Internal_HandleOnStatusDecoded] event={0} code={1} level={2} Timestamp={3}", eventStr, codeStr, levelStr, packet.TimeStamp)); //Console.WriteLine(string.Format("event={0} code={1} level={2} Timestamp={3} seekIsActive={4}", eventStr, codeStr, levelStr, packet.TimeStamp, seekIsActive.ToString())); if (OnStatus != null) { NetStreamStatusEvent netStreamStatusEvent = new NetStreamStatusEvent(); netStreamStatusEvent.Clear(); netStreamStatusEvent.Event = eventStr; netStreamStatusEvent.Code = codeStr; netStreamStatusEvent.Level = levelStr; netStreamStatusEvent.EventInfo = (AMFObject)obj.Clone(); // for thread safety MQ_RTMPMessage message = new MQ_RTMPMessage(); message.MethodCall = MethodCall.OnEventCallUserCode; message.Params = new object[] { OnStatus, this, netStreamStatusEvent }; netConnection.PostOnEventUserCallCodeMessage(message); } // Make sure we point to the right record which is buffered! if (codeStr == "NetStream.Play.Switch") { // end of stream (adjust duration to correct for inaccuracy!) lock (lockVAR) { seekIsActive = false; } //lock } // Now do our thing switch (codeStr) { // We need to flush the existing buffers and wait until // new data arrives case "NetStream.Seek.Notify": lock (lockVAR) { seekIsActive = true; // is probably already set blockMediaPackets++; deltaTimeStampInMS = packet.TimeStamp; } Internal_OnSeekNotify(packet.TimeStamp); break; case "NetStream.Play.Reset": // send when playlist starts at the beginning lock (lockVAR) { atBeginOfAudio = true; mediaBytesReceived = 0; deltaTimeStampInMS = 0; } //lock break; case "NetStream.Play.Switch": // Tell we have to stop playing (and drain the buffers!) if (mediaBytesReceived > 0) // only when we are streaming already { blockMediaPackets++; } break; case "NetStream.Data.Start": mediaBytesReceived = 0; break; // stream begins to play (that is data is send) // deblock if needed case "NetStream.Play.Start": lock (lockVAR) { if (blockMediaPackets > 0) { blockMediaPackets--; } seekIsActive = false; // is probably already set } //lock break; case "NetStream.Play.Stop": ReplaySavedPackets(); // needed in case packets where saved (we're at the end so a byte sync will not occure anymore) lock (lockVAR) { atBeginOfAudio = true; } break; // Pause logic case "NetStream.Pause.Notify": ReplaySavedPackets(); // needed in case packets where saved (we're at the end so a byte sync will not occure anymore) lock (lockVAR) { if (mediaBytesReceived > 0) // we're buffering { pauseIsActive = true; Internal_OnPauseStream(true); if (OnPauseStream != null) { MQ_RTMPMessage message = new MQ_RTMPMessage(); message.MethodCall = MethodCall.OnEventCallUserCode; message.Params = new object[] { OnPauseStream, this, true }; netConnection.PostOnEventUserCallCodeMessage(message); } } } //lock break; case "NetStream.Unpause.Notify": lock (lockVAR) { syncAfterPauseNeeded = true; pauseIsActive = false; Internal_OnPauseStream(false); if (OnPauseStream != null) { MQ_RTMPMessage message = new MQ_RTMPMessage(); message.MethodCall = MethodCall.OnEventCallUserCode; message.Params = new object[] { OnPauseStream, this, false }; netConnection.PostOnEventUserCallCodeMessage(message); } } //lock break; case "NetStream.Play.Failed": case "NetStream.Play.StreamNotFound": case "NetStream.Failed": break; case "NetStream.Play.Complete": // all data is send lock (lockVAR) { seekIsActive = false; // safety } break; } //switch // Code can be eg: // "NetStream.Failed" // "NetStream.Play.Failed" // "NetStream.Play.StreamNotFound" // "NetConnection.Connect.InvalidApp" // "NetStream.Play.Start" // "NetStream.Publish.Start" // "NetStream.Play.Complete" //audio // "NetStream.Play.Stop" // audio // "NetStream.Pause.Notify" // "NetStream.Seek.Notify" return true; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion internal bool HandleOnStatus(RTMPPacket packet) { AMFObject obj = new AMFObject(); int nRes = obj.Decode(packet.Body, 0, (int)packet.BodySize, false); if (nRes < 0) { return false; } // string method = obj.GetProperty(0).GetString(); // always onStatus int stream_id = packet.InfoField2; string code = obj.GetProperty(3).ObjectValue.GetProperty("code").StringValue; string level = obj.GetProperty(3).ObjectValue.GetProperty("level").StringValue; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnStatus] code :{0}, level: {1}", code, level)); return Internal_HandleOnStatusDecoded(packet, "onStatus", code, level, obj); }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion internal bool HandleOnAssignStream_ID(RTMPPacket packet, int stream_id, out int contentBufferTime) { this.stream_id = stream_id; // assign stream id this.contentBufferTime = NetConnection.DefaultContentBufferTime; NS_OnAssignStream_ID onAssignStream_ID = OnAssignStream_ID; if (onAssignStream_ID != null) { // ----------------------------------------------------------------------------------------- SendOrPostCallback callback = new SendOrPostCallback(delegate(object state) { // This is specified other thread onAssignStream_ID(this, this.stream_id, ref this.contentBufferTime); }); // ----------------------------------------------------------------------------------------- SynchronizationContext sc = netConnection.SynchronizationContext; if (sc != null) { // Use send because we need the changed "contentBufferTime" sto be able to continue sc.Send(callback, null); } else { callback(null); } } if (this.contentBufferTime <= 0) { this.contentBufferTime = NetConnection.DefaultContentBufferTime; } contentBufferTime = this.contentBufferTime; // NetConnection can handle it now return true; }
/// <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); }
private void HandleChangeChunkSize(RTMPPacket packet) { if (packet.BodySize >= 4) { InChunkSize = RTMPHelper.ReadInt32(packet.Body, 0); LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleChangeChunkSize] Chunk size change to {0}", InChunkSize)); } }
private bool MQ_SendCall(NetStream netStream, string methodName, object[] values) { // Let's try to encode the values infpo AMF List<byte> enc = new List<byte>(); foreach (object value in values) { switch (value.GetType().ToString()) { case "System.Boolean": RTMPHelper.EncodeBoolean(enc, Convert.ToBoolean(value)); break; case "System.Byte": case "System.SByte": case "System.Decimal": case "System.Double": case "System.Single": case "System.Int32": case "System.UInt32": case "System.Int64": case "System.UInt64": case "System.Int16": case "System.UInt16": RTMPHelper.EncodeNumber(enc, Convert.ToDouble(value)); break; case "System.Char": case "System.String": RTMPHelper.EncodeString(enc, Convert.ToString(value)); break; case "System.Object": default: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.MQ_SendCall] call(\"{0}\") parameter type not supported for encoding", methodName)); break; } //switch } //for int transactionNum = ++numInvokes; // needed incase there is a result which is send back // 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, string.Format("[CDR.LibRTMP.NetConnection] Sending call(\"{0}\")", methodName)); RTMPHelper.EncodeString(enc, methodName); RTMPHelper.EncodeNumber(enc, transactionNum); enc.Add(0x05); // NULL packet.BodySize = (uint)enc.Count; packet.Body = enc.ToArray(); methodCallDictionary.Add(transactionNum, methodName); 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> private void HandlePing(RTMPPacket packet) { short nType = -1; if (packet.Body != null && packet.BodySize >= 2) { nType = (short)RTMPHelper.ReadInt16(packet.Body, 0); } if (packet.BodySize >= 6) { uint nTime = (uint)RTMPHelper.ReadInt32(packet.Body, 2); switch (nType) { case 0: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Stream Begin {0}", nTime)); break; case 1: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Stream EOF {0}", nTime)); break; case 2: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Stream Dry {0}", nTime)); break; case 4: // when this control message is sent, the stream is recorded if (netStreams[nTime] != null) //nTime=stream_id { netStreams[nTime].LiveStream = false; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] stream_id={0}", nTime)); } break; case 6: // server ping. reply with pong. LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Ping {0}", nTime)); MQ_SendPing(0x07, nTime, 0); break; case 7: // server pong. Do nothing LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Pong received {0}", nTime)); break; case 31: LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Stream BufferEmpty {0}", nTime)); //if (!mediaLiveStream) { // ---------------------- TODO!!!!!!!!!!!!!!! --------------------------- /* if (pausing == MediaStreamingState.Unknown) { MQInternal_SendPause(true); pausing = MediaStreamingState.Pausing; } else if (pausing == MediaStreamingState.ReceivedEOFStream) { MQInternal_SendPause(false); pausing = MediaStreamingState.ReadyForData; } */ // ---------------------- TODO!!!!!!!!!!!!!!! --------------------------- } break; case 32: LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Stream BufferReady {0}", nTime)); break; default: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] Stream xx {0}", nTime)); break; } } if (nType == 0x1A) { if (packet.BodySize > 2 && packet.Body[2] > 0x01) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection.HandlePing] SWFVerification Type {0} request not supported! Patches welcome...", packet.Body[2])); } else if (swfHash != null) // respond with HMAC SHA256 of decompressed SWF, key is the 30 byte player key, also the last 30 bytes of the server handshake are applied { MQ_SendPing(0x1B, 0, 0); } else { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection.HandlePing] Ignoring SWFVerification request, swfhash and swfsize parameters not set!"); } } }
private bool MQ_SendDeleteStream(int stream_id) { 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 deleteStream"); List<byte> enc = new List<byte>(); RTMPHelper.EncodeString(enc, "deleteStream"); RTMPHelper.EncodeNumber(enc, 0); // can be 0, because server send no response back enc.Add(0x05); // NULL RTMPHelper.EncodeNumber(enc, stream_id); packet.BodySize = (uint)enc.Count; packet.Body = enc.ToArray(); // no response expected return MQInternal_SendPacket(packet, false); }
private void HandleServerBW(RTMPPacket packet) { serverBW = RTMPHelper.ReadInt32(packet.Body, 0); LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleServerBW] server BW = {0}", serverBW)); }
private bool MQ_SendCloseStream(NetStream netStream) { RTMPPacket packet = new RTMPPacket(); packet.Channel = netStream.CommandChannel;// netStream.CommandChannel; or 0x03 both work!? packet.HeaderType = HeaderType.Large; packet.PacketType = PacketType.Invoke; packet.InfoField2 = netStream.Stream_ID; List<byte> enc = new List<byte>(); RTMPHelper.EncodeString(enc, "closeStream"); RTMPHelper.EncodeNumber(enc, 0); // 0 according to spec adobe enc.Add(0x05); // NULL packet.Body = enc.ToArray(); packet.BodySize = (uint)enc.Count; return MQInternal_SendPacket(packet); }
private void HandleClientBW(RTMPPacket packet) { clientBW = RTMPHelper.ReadInt32(packet.Body, 0); if (packet.BodySize > 4) { clientBW2 = packet.Body[4]; } else { clientBW2 = 0; } LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleClientBW] client BW = {0} {1}", clientBW, clientBW2)); }
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 void HandleMedia(RTMPPacket packet) { int stream_id = packet.InfoField2; if (netStreams[stream_id] != null) { netStreams[stream_id].HandleOnMediaPacket(packet); } else { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection.HandleMedia] No NetStream object found in channel {0} ignoring media packet", stream_id)); } }
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 void HandleMetadata(RTMPPacket packet) { int stream_id = packet.InfoField2; // Find matching NetStream and forward this packet. if (netStreams[stream_id] == null) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection.HandleMetadata] No matching NetStream found for stream_id {0}", stream_id)); return; } 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.HandleMetadata] Error decoding meta data packet"); return; } string metastring = obj.GetProperty(0).StringValue; switch (metastring) { case "|RtmpSampleAccess": // Ignore this, unknown return; // Looks more as an invoke to me Let NetStream.OnStatus handle it case "onStatus": // -=> "NetStream.Data.Start" case "onPlayStatus": // -=> "NetStream.Play.Switch" case "onMetaData": // This seams to me te real metadata case "onID3": // Let NetStream Handle it netStreams[stream_id].HandleOnMetaData(packet); break; default: LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection.HandleMetadata] Unknown metadata {0}", metastring)); break; } //switch return; }
private bool MQInternal_SendSeek(NetStream netStream, long seekTimeInMS) { int transactionNum = ++numInvokes; RTMPPacket packet = new RTMPPacket(); packet.Channel = netStream.CommandChannel; // 0x08 ??? packet.HeaderType = HeaderType.Medium; packet.PacketType = PacketType.Invoke; packet.TimeStamp = 0; packet.InfoField2 = 2; packet.HasAbsTimestamp = false; List<byte> enc = new List<byte>(); RTMPHelper.EncodeString(enc, "seek"); RTMPHelper.EncodeNumber(enc, transactionNum); enc.Add(0x05); // NULL RTMPHelper.EncodeNumber(enc, seekTimeInMS); // number of milliseconds to seek into playlist packet.Body = enc.ToArray(); packet.BodySize = (uint)enc.Count; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection] Sending seek: ({0})", seekTimeInMS.ToString())); methodCallDictionary.Add(transactionNum, "seek"); return MQInternal_SendPacket(packet); }
private void HandleFlvTags(RTMPPacket packet) { // go through FLV packets and handle metadata packets int pos = 0; uint nTimeStamp = packet.TimeStamp; while (pos + 11 < packet.BodySize) { int dataSize = RTMPHelper.ReadInt24(packet.Body, pos + 1); // size without header (11) and prevTagSize (4) if (pos + 11 + dataSize + 4 > packet.BodySize) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection.HandleFlvTags] Stream corrupt?!"); break; } if (packet.Body[pos] == 0x12) { // Create "fake packet" convert to a metadatapacket RTMPPacket tmpPacket = (RTMPPacket)packet.Clone(); tmpPacket.PacketType = PacketType.Metadata; byte[] newBody = new byte[dataSize]; Buffer.BlockCopy(packet.Body, pos + 11, newBody, 0, dataSize); tmpPacket.Body = newBody; tmpPacket.BodySize = Convert.ToUInt32(dataSize); HandleMetadata(tmpPacket); } else if (packet.Body[pos] == 8 || packet.Body[pos] == 9) { nTimeStamp = (uint)RTMPHelper.ReadInt24(packet.Body, pos + 4); nTimeStamp |= (uint)(packet.Body[pos + 7] << 24); } pos += (11 + dataSize + 4); } }
/// <summary> /// Do the last assembling and send the packet on it's way /// </summary> private bool MQInternal_SendPacket(RTMPPacket packet, bool queue = true) { uint last = 0; uint t = 0; RTMPPacket prevPacket = vecChannelsOut[packet.Channel]; if (packet.HeaderType != HeaderType.Large && prevPacket != null) { // compress a bit by using the prev packet's attributes if (prevPacket.BodySize == packet.BodySize && prevPacket.PacketType == packet.PacketType && packet.HeaderType == HeaderType.Medium) { packet.HeaderType = HeaderType.Small; } if (prevPacket.TimeStamp == packet.TimeStamp && packet.HeaderType == HeaderType.Small) { packet.HeaderType = HeaderType.Minimum; } last = prevPacket.TimeStamp; } uint nSize = RTMPConst.PacketSize[(byte)packet.HeaderType]; t = packet.TimeStamp - last; List<byte> header = new List<byte>();//byte[RTMP_LARGE_HEADER_SIZE]; byte c = (byte)(((byte)packet.HeaderType << 6) | packet.Channel); header.Add(c); if (nSize > 1) { RTMPHelper.EncodeInt24(header, (int)t); } if (nSize > 4) { RTMPHelper.EncodeInt24(header, (int)packet.BodySize); header.Add((byte)packet.PacketType); } if (nSize > 8) { RTMPHelper.EncodeInt32LE(header, packet.InfoField2); } uint hSize = nSize; byte[] headerBuffer = header.ToArray(); nSize = packet.BodySize; byte[] buffer = packet.Body; uint bufferOffset = 0; uint nChunkSize = (uint)outChunkSize; while (nSize + hSize > 0) { if (nSize < nChunkSize) { nChunkSize = nSize; } if (hSize > 0) { byte[] combinedBuffer = new byte[headerBuffer.Length + nChunkSize]; Array.Copy(headerBuffer, combinedBuffer, headerBuffer.Length); Array.Copy(buffer, (int)bufferOffset, combinedBuffer, headerBuffer.Length, (int)nChunkSize); WriteN(combinedBuffer, 0, combinedBuffer.Length); hSize = 0; } else { WriteN(buffer, (int)bufferOffset, (int)nChunkSize); } nSize -= nChunkSize; bufferOffset += nChunkSize; if (nSize > 0) { byte sep = (byte)(0xc0 | c); hSize = 1; headerBuffer = new byte[1] { sep }; } } //while vecChannelsOut[packet.Channel] = packet; return true; }
/// <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; }