public static byte[] GetHandshakeResponse(ByteBuffer input)
 {
     int position = (int)input.Position;
     if (input[position + 4] == 0)
     {
         //Using old style handshake
         byte[] output = new byte[2 * RtmpProtocolDecoder.HandshakeSize + 1];
         output[0] = 0x03;
         int tick = System.Environment.TickCount;
         output[1] = (byte)((tick >> 24) & 0xff);
         output[2] = (byte)((tick >> 16) & 0xff);
         output[3] = (byte)((tick >> 8) & 0xff);
         output[4] = (byte)(tick & 0xff);
         HandshakePadBytes.CopyTo(output, 5);
         input.Read(output, RtmpProtocolDecoder.HandshakeSize + 1, RtmpProtocolDecoder.HandshakeSize);
         return output;
     }
     else
     {
         //This method is broken by FP 10.0.32.18
         //int index = (input[8] + input[9] + input[10] + input[11]) % 728 + 12;
         int index = 0;
         if (input[position + 5] == 0 && input[position + 6] == 3 && input[position + 7] == 2)
             index = ((input[position + 772] & 0x0ff) + (input[position + 773] & 0x0ff) + (input[position + 774] & 0x0ff) + (input[position + 775] & 0x0ff)) % 728 + 776;
         else
             index = ((input[position + 8] & 0x0ff) + (input[position + 9] & 0x0ff) + (input[position + 10] & 0x0ff) + (input[position + 11] & 0x0ff)) % 728 + 12;
         byte[] part = new byte[32];
         for (int i = 0; i < 32; ++i)
         {
             part[i] = input[position + index + i];
         }
         HMACSHA256 hmacsha256 = new HMACSHA256();
         hmacsha256.Key = SecretKey;
         byte[] newKey = hmacsha256.ComputeHash(part);
         byte[] randBytes = new byte[RtmpProtocolDecoder.HandshakeSize - 32];
         Random random = new Random();
         random.NextBytes(randBytes);
         hmacsha256.Key = newKey;
         byte[] hashedBytes = hmacsha256.ComputeHash(randBytes);
         byte[] output = new byte[2 * RtmpProtocolDecoder.HandshakeSize + 1];
         output[0] = 0x03;
         HandshakeServerBytes.CopyTo(output, 1);
         //randBytes.CopyTo(output, RtmpProtocolDecoder.HandshakeSize + 1);
         randBytes.CopyTo(output, 1 + HandshakeServerBytes.Length);
         //hashedBytes.CopyTo(output, RtmpProtocolDecoder.HandshakeSize + 1 + randBytes.Length);
         hashedBytes.CopyTo(output, 1 + HandshakeServerBytes.Length + randBytes.Length);
         input.Skip(RtmpProtocolDecoder.HandshakeSize);
         return output;
     }
 }
		static ISharedObjectMessage DecodeFlexSharedObject(ByteBuffer stream)
		{
			// Unknown byte, always 0?
			stream.Skip(1);
			RtmpReader reader = new RtmpReader(stream);
			string name = reader.ReadString();
			// Read version of SO to modify
			int version = reader.ReadInt32();
			// Read persistence informations
			bool persistent = reader.ReadInt32() == 2;
			// Skip unknown bytes
			reader.ReadInt32();

			SharedObjectMessage so = new FlexSharedObjectMessage(null, name, version, persistent);
			DecodeSharedObject(so, stream, reader);
			return so;
		}
		static void EncodeSharedObject(RtmpContext context, ISharedObjectMessage so, ByteBuffer output)
		{
			RtmpWriter writer = new RtmpWriter(output);
            //Set legacy collection flag from context
            writer.UseLegacyCollection = context.UseLegacyCollection;
            writer.UseLegacyThrowable = context.UseLegacyThrowable;

			writer.WriteUTF(so.Name);
			// SO version
			writer.WriteInt32(so.Version);
			// Encoding (this always seems to be 2 for persistent shared objects)
			writer.WriteInt32(so.IsPersistent ? 2 : 0);
			// unknown field
			writer.WriteInt32(0);
			
			int mark, len = 0;

			foreach(ISharedObjectEvent sharedObjectEvent in so.Events)
			{
				byte type = SharedObjectTypeMapping.ToByte(sharedObjectEvent.Type);
				switch(sharedObjectEvent.Type) 
				{
                    case SharedObjectEventType.SERVER_CONNECT:
                    case SharedObjectEventType.CLIENT_INITIAL_DATA:
					case SharedObjectEventType.CLIENT_CLEAR_DATA:
						writer.WriteByte(type);
						writer.WriteInt32(0);
						break;
                    case SharedObjectEventType.SERVER_DELETE_ATTRIBUTE:
                    case SharedObjectEventType.CLIENT_DELETE_DATA:
					case SharedObjectEventType.CLIENT_UPDATE_ATTRIBUTE:
						writer.WriteByte(type);
						mark = (int)output.Position;
						output.Skip(4); // we will be back
						writer.WriteUTF(sharedObjectEvent.Key);
						len = (int)output.Position - mark - 4;
						output.PutInt(mark, len);
						break;
					case SharedObjectEventType.SERVER_SET_ATTRIBUTE:
					case SharedObjectEventType.CLIENT_UPDATE_DATA:
						if (sharedObjectEvent.Key == null) 
						{
							// Update multiple attributes in one request
							IDictionary initialData = sharedObjectEvent.Value as IDictionary;
							foreach(DictionaryEntry entry in initialData)
							{
								writer.WriteByte(type);
								mark = (int)output.Position;
								output.Skip(4); // we will be back
								string key = entry.Key as string;
								object value = entry.Value;
								writer.WriteUTF(key);
								writer.WriteData(context.ObjectEncoding, value);
								
								len = (int)output.Position - mark - 4;
								output.PutInt(mark, len);
							}
						} 
						else 
						{
							writer.WriteByte(type);
							mark = (int)output.Position;
							output.Skip(4); // we will be back
							writer.WriteUTF(sharedObjectEvent.Key);
							writer.WriteData(context.ObjectEncoding, sharedObjectEvent.Value);
							//writer.WriteData(sharedObjectEvent.Value);

							len = (int)output.Position - mark - 4;
							output.PutInt(mark, len);
						}
						break;
					case SharedObjectEventType.CLIENT_SEND_MESSAGE:
					case SharedObjectEventType.SERVER_SEND_MESSAGE:
						// Send method name and value
						writer.WriteByte(type);
						mark = (int)output.Position;
						output.Skip(4); // we will be back

						// Serialize name of the handler to call
						writer.WriteData(context.ObjectEncoding, sharedObjectEvent.Key);
						//writer.WriteUTF(sharedObjectEvent.Key);
						// Serialize the arguments
						foreach(object arg in sharedObjectEvent.Value as IList)
						{
							writer.WriteData(context.ObjectEncoding, arg);
						}
						//writer.WriteData(sharedObjectEvent.Value as IList);
						len = (int)output.Position - mark - 4;
						//output.PutInt(mark, len);
						output.PutInt(mark, len);
						break;
					case SharedObjectEventType.CLIENT_STATUS:
						writer.WriteByte(type);
						mark = (int)output.Position;
						output.Skip(4); // we will be back
						writer.WriteUTF(sharedObjectEvent.Key);
						writer.WriteUTF(sharedObjectEvent.Value as string);
						len = (int)output.Position - mark - 4;
						output.PutInt(mark, len);
						break;
                    case SharedObjectEventType.SERVER_DISCONNECT:
                        writer.WriteByte(type);
                        output.PutInt((int)output.Position, 0);
                        break;
					default:
#if !SILVERLIGHT
                        _log.Error("Unknown event " + sharedObjectEvent.Type.ToString());
#endif
						writer.WriteByte(type);
						mark = (int)output.Position;
						output.Skip(4); // we will be back
                        if (sharedObjectEvent.Key != null)
                        {
                            writer.WriteUTF(sharedObjectEvent.Key);
                            writer.WriteData(context.ObjectEncoding, sharedObjectEvent.Value);
                        }
						len = (int)output.Position - mark - 4;
						output.PutInt(mark, len);
						break;
				}
			}
		}
        /// <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;
		}