/// <summary> /// Decodes handshake message. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>Buffer with handshake response.</returns> public static object DecodeHandshake(RtmpContext context, ByteBuffer stream) { long remaining = stream.Remaining; if(context.Mode == RtmpMode.Server) { if(context.State == RtmpState.Connect) { if(remaining < HandshakeSize + 1) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSInitBuffering, remaining)); #endif context.SetBufferDecoding(HandshakeSize + 1); return null; } else { #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug("Handshake 1st phase"); #endif stream.Get();// skip the header byte byte[] handshake = RtmpHandshake.GetHandshakeResponse(stream); context.SetHandshake(handshake); context.State = RtmpState.Handshake; return handshake; } } if(context.State == RtmpState.Handshake) { //if (log.IsDebugEnabled) // log.Debug("Handshake reply"); if(remaining < HandshakeSize) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSReplyBuffering, remaining)); #endif context.SetBufferDecoding(HandshakeSize); return null; } else { // Skip first 8 bytes when comparing the handshake, they seem to be changed when connecting from a Mac client. if (!context.ValidateHandshakeReply(stream, 8, HandshakeSize - 8)) { #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug("Handshake reply validation failed, disconnecting client."); #endif stream.Skip(HandshakeSize); context.State = RtmpState.Error; throw new HandshakeFailedException("Handshake validation failed"); } stream.Skip(HandshakeSize); context.State = RtmpState.Connected; context.ContinueDecoding(); return null; } } } else { //Client mode if(context.State == RtmpState.Connect) { int size = (2 * HandshakeSize) + 1; if(remaining < size) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSInitBuffering, remaining)); #endif context.SetBufferDecoding(size); return null; } else { ByteBuffer hs = ByteBuffer.Allocate(size); ByteBuffer.Put(hs, stream, size); hs.Flip(); context.State = RtmpState.Handshake; return hs; } } } return null; }
/// <summary> /// Decodes a RTMP packet. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>The decoded RTMP packet.</returns> public static RtmpPacket DecodePacket(RtmpContext context, ByteBuffer stream) { int remaining = stream.Remaining; // We need at least one byte if(remaining < 1) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_DataBuffering, remaining, 1)); #endif context.SetBufferDecoding(1); return null; } int position = (int)stream.Position; byte headerByte = stream.Get(); int headerValue; int byteCount; if((headerByte & 0x3f) == 0) { // Two byte header if (remaining < 2) { stream.Position = position; #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug(__Res.GetString(__Res.Rtmp_DataBuffering, remaining, 2)); #endif context.SetBufferDecoding(2); return null; } headerValue = ((int) headerByte & 0xff) << 8 | ((int) stream.Get() & 0xff); byteCount = 2; } else if ((headerByte & 0x3f) == 1) { // Three byte header if (remaining < 3) { stream.Position = position; #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug(__Res.GetString(__Res.Rtmp_DataBuffering, remaining, 3)); #endif context.SetBufferDecoding(3); return null; } headerValue = ((int) headerByte & 0xff) << 16 | ((int) stream.Get() & 0xff) << 8 | ((int) stream.Get() & 0xff); byteCount = 3; } else { // Single byte header headerValue = (int) headerByte & 0xff; byteCount = 1; } byte channelId = DecodeChannelId(headerValue, byteCount); if (channelId < 0) throw new ProtocolException("Bad channel id: " + channelId); byte headerSize = DecodeHeaderSize(headerValue, byteCount); int headerLength = GetHeaderLength(headerSize); headerLength += byteCount - 1; //if(headerLength > remaining) if (headerLength + byteCount - 1 > remaining) { #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug(__Res.GetString(__Res.Rtmp_HeaderBuffering, remaining)); #endif stream.Position = position; //context.SetBufferDecoding(headerLength); context.SetBufferDecoding(headerLength + byteCount - 1); return null; } // Move the position back to the start stream.Position = position; RtmpHeader header = DecodeHeader(context, context.GetLastReadHeader(channelId), stream); #if !SILVERLIGHT log.Debug("Decoded " + header); #endif if (header == null) throw new ProtocolException("Header is null, check for error"); // Save the header context.SetLastReadHeader(channelId, header); // Check to see if this is a new packet or continue decoding an existing one. RtmpPacket packet = context.GetLastReadPacket(channelId); if(packet == null) { packet = new RtmpPacket(header); context.SetLastReadPacket(channelId, packet); } ByteBuffer buf = packet.Data; int addSize = (header.Timer == 0xffffff ? 4 : 0); //int addSize = 0; int readRemaining = header.Size + addSize - (int)buf.Position; int chunkSize = context.GetReadChunkSize(); int readAmount = (readRemaining > chunkSize) ? chunkSize : readRemaining; if(stream.Remaining < readAmount) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_ChunkSmall, stream.Remaining, readAmount)); #endif //Skip the position back to the start stream.Position = position; context.SetBufferDecoding(headerLength + readAmount); //string path = FluorineFx.Context.FluorineContext.Current.GetFullPath(@"log\chunk.bin"); //stream.Dump(path); return null; } //http://osflash.org/pipermail/free_osflash.org/2005-September/000261.html //http://www.acmewebworks.com/Downloads/openCS/091305-initialMeeting.txt ByteBuffer.Put(buf, stream, readAmount); if(buf.Position < header.Size + addSize) { context.ContinueDecoding(); return null; } if (buf.Position > header.Size + addSize) { #if !SILVERLIGHT log.Warn(string.Format("Packet size expanded from {0} to {1} ({2})", header.Size + addSize, buf.Position, header)); #endif } buf.Flip(); try { IRtmpEvent message = DecodeMessage(context, packet.Header, buf); packet.Message = message; if (message is ChunkSize) { ChunkSize chunkSizeMsg = message as ChunkSize; context.SetReadChunkSize(chunkSizeMsg.Size); } } finally { context.SetLastReadPacket(channelId, null); } #if !SILVERLIGHT if (log.IsDebugEnabled) { log.Debug("Decoded " + packet.ToString()); } #endif return packet; }
/// <summary> /// Decodes handshake message. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>Buffer with handshake response.</returns> public static object DecodeHandshake(RtmpContext context, ByteBuffer stream) { long remaining = stream.Remaining; if(context.Mode == RtmpMode.Server) { if(context.State == RtmpState.Connect) { if(remaining < HandshakeSize + 1) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSInitBuffering, remaining)); #endif context.SetBufferDecoding(HandshakeSize + 1); return null; } else { #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug("Handshake 1st phase"); #endif ByteBuffer hs = ByteBuffer.Allocate(HandshakeSize + 1); ByteBuffer.Put(hs, stream, HandshakeSize + 1); hs.Flip(); context.State = RtmpState.Handshake; return hs; } } if(context.State == RtmpState.Handshake) { //if (log.IsDebugEnabled) // log.Debug("Handshake reply"); if(remaining < HandshakeSize) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSReplyBuffering, remaining)); #endif context.SetBufferDecoding(HandshakeSize); return null; } else { ByteBuffer hs = ByteBuffer.Allocate(HandshakeSize); ByteBuffer.Put(hs, stream, HandshakeSize); hs.Flip(); context.State = RtmpState.Connected; return hs; } } } else { //Client mode if(context.State == RtmpState.Connect) { int size = (2 * HandshakeSize) + 1; if(remaining < size) { #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_HSInitBuffering, remaining)); #endif context.SetBufferDecoding(size); return null; } else { ByteBuffer hs = ByteBuffer.Allocate(size); ByteBuffer.Put(hs, stream, size); hs.Flip(); context.State = RtmpState.Connected; return hs; } } } return null; }