static ByteBuffer EncodeChunkSize(RtmpContext context, ChunkSize chunkSize) { ByteBuffer output = ByteBuffer.Allocate(4); output.PutInt(chunkSize.Size); return(output); }
/// <summary> /// Encode server-side bandwidth event /// </summary> /// <param name="context"></param> /// <param name="serverBW"></param> /// <returns></returns> static ByteBuffer EncodeServerBW(RtmpContext context, ServerBW serverBW) { ByteBuffer output = ByteBuffer.Allocate(4); output.PutInt(serverBW.Bandwidth); return(output); }
static ByteBuffer EncodePing(RtmpContext context, Ping ping) { int len = 6; if (ping.Value3 != Ping.Undefined) { len += 4; } if (ping.Value4 != Ping.Undefined) { len += 4; } ByteBuffer output = ByteBuffer.Allocate(len); output.PutShort(ping.PingType); output.PutInt(ping.Value2); if (ping.Value3 != Ping.Undefined) { output.PutInt(ping.Value3); } if (ping.Value4 != Ping.Undefined) { output.PutInt(ping.Value4); } return(output); }
static ByteBuffer EncodeBytesRead(RtmpContext context, BytesRead bytesRead) { ByteBuffer output = ByteBuffer.Allocate(4); output.PutInt(bytesRead.Bytes); return(output); }
public static ByteBuffer EncodeMessage(RtmpContext context, RtmpHeader header, IRtmpEvent message) { switch (header.DataType) { case Constants.TypeChunkSize: return(EncodeChunkSize(context, message as ChunkSize)); case Constants.TypeInvoke: return(EncodeInvoke(context, message as Invoke)); case Constants.TypeFlexInvoke: return(EncodeFlexInvoke(context, message as FlexInvoke)); case Constants.TypeSharedObject: return(EncodeSharedObject(context, message as ISharedObjectMessage)); case Constants.TypeFlexSharedObject: return(EncodeFlexSharedObject(context, message as ISharedObjectMessage)); case Constants.TypeNotify: if ((message as Notify).ServiceCall == null) { return(EncodeStreamMetadata(context, message as Notify)); } else { return(EncodeNotify(context, message as Notify)); } case Constants.TypePing: return(EncodePing(context, message as Ping)); case Constants.TypeBytesRead: return(EncodeBytesRead(context, message as BytesRead)); case Constants.TypeAudioData: return(EncodeAudioData(context, message as AudioData)); case Constants.TypeVideoData: return(EncodeVideoData(context, message as VideoData)); case Constants.TypeServerBandwidth: return(EncodeServerBW(context, message as ServerBW)); case Constants.TypeClientBandwidth: return(EncodeClientBW(context, message as ClientBW)); case Constants.TypeFlexStreamEnd: return(EncodeFlexStreamSend(context, message as FlexStreamSend)); default: #if !SILVERLIGHT if (_log.IsErrorEnabled) { _log.Error("Unknown object type: " + header.DataType); } #endif return(null); } }
static ByteBuffer EncodeClientBW(RtmpContext context, ClientBW clientBW) { ByteBuffer output = ByteBuffer.Allocate(5); output.PutInt(clientBW.Bandwidth); output.Put(clientBW.Value2); return(output); }
static ByteBuffer EncodeSharedObject(RtmpContext context, ISharedObjectMessage so) { ByteBuffer output = ByteBuffer.Allocate(128); output.AutoExpand = true; EncodeSharedObject(context, so, output); return(output); }
static ByteBuffer EncodeNotifyOrInvoke(RtmpContext context, Notify invoke) { //MemoryStreamEx output = new MemoryStreamEx(); ByteBuffer output = ByteBuffer.Allocate(1024); output.AutoExpand = true; RtmpWriter writer = new RtmpWriter(output); //Set legacy collection flag from context writer.UseLegacyCollection = context.UseLegacyCollection; writer.UseLegacyThrowable = context.UseLegacyThrowable; IServiceCall serviceCall = invoke.ServiceCall; bool isPending = serviceCall.Status == Call.STATUS_PENDING; if (!isPending) { //log.debug("Call has been executed, send result"); writer.WriteData(context.ObjectEncoding, serviceCall.IsSuccess ? "_result" : "_error"); } else { //log.debug("This is a pending call, send request"); string action = (serviceCall.ServiceName == null) ? serviceCall.ServiceMethodName : serviceCall.ServiceName + "." + serviceCall.ServiceMethodName; writer.WriteData(context.ObjectEncoding, action); } if (invoke is Invoke) { writer.WriteData(context.ObjectEncoding, invoke.InvokeId); writer.WriteData(context.ObjectEncoding, invoke.ConnectionParameters); } if (!isPending && (invoke is Invoke)) { IPendingServiceCall pendingCall = (IPendingServiceCall)serviceCall; if (!serviceCall.IsSuccess && !(pendingCall.Result is StatusASO)) { StatusASO status = GenerateErrorResult(StatusASO.NC_CALL_FAILED, serviceCall.Exception); pendingCall.Result = status; } writer.WriteData(context.ObjectEncoding, pendingCall.Result); } else { //log.debug("Writing params"); object[] args = invoke.ServiceCall.Arguments; if (args != null) { foreach (object element in args) { writer.WriteData(context.ObjectEncoding, element); } } } return(output); }
private static object Decode(RtmpContext context, ByteBuffer stream) { //long start = stream.Position; switch (context.State) { case RtmpState.Connected: return(DecodePacket(context, stream)); case RtmpState.Error: // Attempt to correct error return(null); case RtmpState.Connect: case RtmpState.Handshake: return(DecodeHandshake(context, stream)); default: return(null); } }
public static ByteBuffer Encode(RtmpContext context, object message) { try { if (message is ByteBuffer) { return((ByteBuffer)message); } else { return(EncodePacket(context, message as RtmpPacket)); } } catch (Exception ex) { #if !SILVERLIGHT if (_log != null) { _log.Fatal("Error encoding object. ", ex); } #endif } return(null); }
static ByteBuffer EncodeClientBW(RtmpContext context, ClientBW clientBW) { ByteBuffer output = ByteBuffer.Allocate(5); output.PutInt(clientBW.Bandwidth); output.Put(clientBW.Value2); return output; }
public static ByteBuffer EncodeMessage(RtmpContext context, RtmpHeader header, IRtmpEvent message) { switch(header.DataType) { case Constants.TypeChunkSize: return EncodeChunkSize(context, message as ChunkSize); case Constants.TypeInvoke: return EncodeInvoke(context, message as Invoke); case Constants.TypeFlexInvoke: return EncodeFlexInvoke(context, message as FlexInvoke); case Constants.TypeSharedObject: return EncodeSharedObject(context, message as ISharedObjectMessage); case Constants.TypeFlexSharedObject: return EncodeFlexSharedObject(context, message as ISharedObjectMessage); case Constants.TypeNotify: if ((message as Notify).ServiceCall == null) { return EncodeStreamMetadata(context, message as Notify); } else { return EncodeNotify(context, message as Notify); } case Constants.TypePing: return EncodePing(context, message as Ping); case Constants.TypeBytesRead: return EncodeBytesRead(context, message as BytesRead); case Constants.TypeAudioData: return EncodeAudioData(context, message as AudioData); case Constants.TypeVideoData: return EncodeVideoData(context, message as VideoData); case Constants.TypeServerBandwidth: return EncodeServerBW(context, message as ServerBW); case Constants.TypeClientBandwidth: return EncodeClientBW(context, message as ClientBW); case Constants.TypeFlexStreamEnd: return EncodeFlexStreamSend(context, message as FlexStreamSend); default: #if !SILVERLIGHT if( _log.IsErrorEnabled ) _log.Error("Unknown object type: " + header.DataType); #endif return null; } }
public static ByteBuffer Encode(RtmpContext context, object message) { try { if (message is ByteBuffer) return (ByteBuffer) message; else return EncodePacket(context, message as RtmpPacket); } catch(Exception ex) { #if !SILVERLIGHT if( _log != null ) _log.Fatal("Error encoding object. ", ex); #endif } return null; }
/// <summary> /// Decodes RTMP packet header. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="lastHeader">Previous header.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>Decoded RTMP header.</returns> public static RtmpHeader DecodeHeader(RtmpContext context, RtmpHeader lastHeader, ByteBuffer stream) { byte headerByte = stream.Get(); int headerValue; int byteCount = 1; if ((headerByte & 0x3f) == 0) { // Two byte header headerValue = ((int) headerByte & 0xff) << 8 | ((int) stream.Get() & 0xff); byteCount = 2; } else if ((headerByte & 0x3f) == 1) { // Three byte header 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); byte headerSize = DecodeHeaderSize(headerValue, byteCount); RtmpHeader header = new RtmpHeader(); header.ChannelId = channelId; header.IsTimerRelative = (HeaderType)headerSize != HeaderType.HeaderNew; if ((HeaderType)headerSize != HeaderType.HeaderNew && lastHeader == null) { #if !SILVERLIGHT if(log.IsErrorEnabled) log.Error(string.Format("Last header null not new, headerSize: {0}, channelId {1}", headerSize, channelId)); #endif lastHeader = new RtmpHeader(); lastHeader.ChannelId = channelId; lastHeader.IsTimerRelative = (HeaderType)headerSize != HeaderType.HeaderNew; } #if !SILVERLIGHT if( log.IsDebugEnabled ) log.Debug(__Res.GetString(__Res.Rtmp_DecodeHeader, Enum.GetName(typeof(HeaderType), (HeaderType)headerSize))); #endif switch((HeaderType)headerSize) { case HeaderType.HeaderNew: header.Timer = stream.ReadUInt24();// ReadUnsignedMediumInt(); header.Size = stream.ReadUInt24();// ReadMediumInt(); header.DataType = stream.Get(); header.StreamId = stream.ReadReverseInt(); break; case HeaderType.HeaderSameSource: header.Timer = stream.ReadUInt24();// ReadUnsignedMediumInt(); header.Size = stream.ReadUInt24();// ReadMediumInt(); header.DataType = stream.Get(); header.StreamId = lastHeader.StreamId; break; case HeaderType.HeaderTimerChange: header.Timer = stream.ReadUInt24();//ReadUnsignedMediumInt(); header.Size = lastHeader.Size; header.DataType = lastHeader.DataType; header.StreamId = lastHeader.StreamId; break; case HeaderType.HeaderContinue: header.Timer = lastHeader.Timer; header.Size = lastHeader.Size; header.DataType = lastHeader.DataType; header.StreamId = lastHeader.StreamId; header.IsTimerRelative = lastHeader.IsTimerRelative; break; default: #if !SILVERLIGHT log.Error("Unexpected header size: " + headerSize); #endif return null; } if (header.Timer >= 0xffffff) { //Extended timestamp header.Timer = stream.GetInt(); } return header; }
/// <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; }
public static ArrayList DecodeBuffer(RtmpContext context, ByteBuffer stream) #endif { // >> HEADER[1] + CLIENT_HANDSHAKE[1536] // << HEADER[1] + SERVER_HANDSHAKE[1536] + CLIENT_HANDSHAKE[1536]; // >> SERVER_HANDSHAKE[1536] + AMF[n] #if !(NET_1_1) List<object> result = null; #else ArrayList result = null; #endif try { while (true) { long remaining = stream.Remaining; if (context.CanStartDecoding(remaining)) context.StartDecoding(); else break; if (context.State == RtmpState.Disconnected) break; object decodedObject = Decode(context, stream); if (context.HasDecodedObject) { #if !(NET_1_1) if (result == null) result = new List<object>(); #else if (result == null) result = new ArrayList(); #endif result.Add(decodedObject); } else if (context.CanContinueDecoding) continue; else break; if (!stream.HasRemaining) break; } } catch (HandshakeFailedException) { throw; } catch (Exception) { Dump(stream); throw; } finally { stream.Compact(); } return result; }
public static ByteBuffer EncodePacket(RtmpContext context, RtmpPacket packet) { RtmpHeader header = packet.Header; int channelId = header.ChannelId; IRtmpEvent message = packet.Message; ByteBuffer data; if (message is ChunkSize) { ChunkSize chunkSizeMsg = (ChunkSize)message; context.SetWriteChunkSize(chunkSizeMsg.Size); } data = EncodeMessage(context, header, message); if (data.Position != 0) { data.Flip(); } else { data.Rewind(); } header.Size = (int)data.Limit; RtmpHeader lastHeader = context.GetLastWriteHeader(channelId); int headerSize = CalculateHeaderSize(header, lastHeader); context.SetLastWriteHeader(channelId, header); context.SetLastWritePacket(channelId, packet); int chunkSize = context.GetWriteChunkSize(); int chunkHeaderSize = 1; if (header.ChannelId > 320) { chunkHeaderSize = 3; } else if (header.ChannelId > 63) { chunkHeaderSize = 2; } int numChunks = (int)Math.Ceiling(header.Size / (float)chunkSize); int bufSize = (int)header.Size + headerSize + (numChunks > 0 ? (numChunks - 1) * chunkHeaderSize : 0); ByteBuffer output = ByteBuffer.Allocate(bufSize); EncodeHeader(header, lastHeader, output); if (numChunks == 1) { // we can do it with a single copy ByteBuffer.Put(output, data, output.Remaining); } else { for (int i = 0; i < numChunks - 1; i++) { ByteBuffer.Put(output, data, chunkSize); EncodeHeaderByte(output, (byte)HeaderType.HeaderContinue, header.ChannelId); } ByteBuffer.Put(output, data, output.Remaining); } //data.Close(); output.Flip(); return(output); }
static ByteBuffer EncodeAudioData(RtmpContext context, AudioData audioData) { ByteBuffer output = audioData.Data; return(output); }
public static ArrayList DecodeBuffer(RtmpContext context, ByteBuffer stream) #endif { // >> HEADER[1] + CLIENT_HANDSHAKE[1536] // << HEADER[1] + SERVER_HANDSHAKE[1536] + CLIENT_HANDSHAKE[1536]; // >> SERVER_HANDSHAKE[1536] + AMF[n] #if !(NET_1_1) List <object> result = null; #else ArrayList result = null; #endif try { while (true) { long remaining = stream.Remaining; if (context.CanStartDecoding(remaining)) { context.StartDecoding(); } else { break; } if (context.State == RtmpState.Disconnected) { break; } object decodedObject = Decode(context, stream); if (context.HasDecodedObject) { #if !(NET_1_1) if (result == null) { result = new List <object>(); } #else if (result == null) { result = new ArrayList(); } #endif result.Add(decodedObject); } else if (context.CanContinueDecoding) { continue; } else { break; } if (!stream.HasRemaining) { break; } } } catch (HandshakeFailedException) { throw; } catch (Exception) { Dump(stream); throw; } finally { stream.Compact(); } return(result); }
/// <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 RTMP packet header. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="lastHeader">Previous header.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>Decoded RTMP header.</returns> public static RtmpHeader DecodeHeader(RtmpContext context, RtmpHeader lastHeader, ByteBuffer stream) { byte headerByte = stream.Get(); int headerValue; int byteCount = 1; if ((headerByte & 0x3f) == 0) { // Two byte header headerValue = ((int)headerByte & 0xff) << 8 | ((int)stream.Get() & 0xff); byteCount = 2; } else if ((headerByte & 0x3f) == 1) { // Three byte header 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); byte headerSize = DecodeHeaderSize(headerValue, byteCount); RtmpHeader header = new RtmpHeader(); header.ChannelId = channelId; header.IsTimerRelative = (HeaderType)headerSize != HeaderType.HeaderNew; if ((HeaderType)headerSize != HeaderType.HeaderNew && lastHeader == null) { #if !SILVERLIGHT if (log.IsErrorEnabled) { log.Error(string.Format("Last header null not new, headerSize: {0}, channelId {1}", headerSize, channelId)); } #endif lastHeader = new RtmpHeader(); lastHeader.ChannelId = channelId; lastHeader.IsTimerRelative = (HeaderType)headerSize != HeaderType.HeaderNew; } #if !SILVERLIGHT if (log.IsDebugEnabled) { log.Debug(__Res.GetString(__Res.Rtmp_DecodeHeader, Enum.GetName(typeof(HeaderType), (HeaderType)headerSize))); } #endif switch ((HeaderType)headerSize) { case HeaderType.HeaderNew: header.Timer = stream.ReadUInt24(); // ReadUnsignedMediumInt(); header.Size = stream.ReadUInt24(); // ReadMediumInt(); header.DataType = stream.Get(); header.StreamId = stream.ReadReverseInt(); break; case HeaderType.HeaderSameSource: header.Timer = stream.ReadUInt24(); // ReadUnsignedMediumInt(); header.Size = stream.ReadUInt24(); // ReadMediumInt(); header.DataType = stream.Get(); header.StreamId = lastHeader.StreamId; break; case HeaderType.HeaderTimerChange: header.Timer = stream.ReadUInt24(); //ReadUnsignedMediumInt(); header.Size = lastHeader.Size; header.DataType = lastHeader.DataType; header.StreamId = lastHeader.StreamId; break; case HeaderType.HeaderContinue: header.Timer = lastHeader.Timer; header.Size = lastHeader.Size; header.DataType = lastHeader.DataType; header.StreamId = lastHeader.StreamId; header.IsTimerRelative = lastHeader.IsTimerRelative; break; default: #if !SILVERLIGHT log.Error("Unexpected header size: " + headerSize); #endif return(null); } if (header.Timer >= 0xffffff) { //Extended timestamp header.Timer = stream.GetInt(); } return(header); }
/// <summary> /// Decodes RTMP message event. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="header">RTMP header.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>Decoded RTMP event.</returns> public static IRtmpEvent DecodeMessage(RtmpContext context, RtmpHeader header, ByteBuffer stream) { IRtmpEvent message = null; /* * if(header.Timer == 0xffffff) * { * // Skip first four bytes * byte[] extendedTimestamp = new byte[4]; * stream.Read(extendedTimestamp, 0, 4); * log.Warn("Discarding extended timestamp"); * //int unknown = stream.ReadInt32(); * } */ switch (header.DataType) { case Constants.TypeChunkSize: message = DecodeChunkSize(stream); break; case Constants.TypeInvoke: message = DecodeInvoke(stream); break; case Constants.TypeFlexInvoke: message = DecodeFlexInvoke(stream); break; case Constants.TypeNotify: if (header.StreamId == 0) { message = DecodeNotify(stream, header); } else { message = DecodeStreamMetadata(stream); } break; case Constants.TypePing: message = DecodePing(stream); break; case Constants.TypeBytesRead: message = DecodeBytesRead(stream); break; case Constants.TypeAudioData: message = DecodeAudioData(stream); break; case Constants.TypeVideoData: message = DecodeVideoData(stream); break; case Constants.TypeSharedObject: message = DecodeSharedObject(stream); break; case Constants.TypeFlexSharedObject: message = DecodeFlexSharedObject(stream); break; case Constants.TypeServerBandwidth: message = DecodeServerBW(stream); break; case Constants.TypeClientBandwidth: message = DecodeClientBW(stream); break; default: #if !SILVERLIGHT log.Warn("Unknown object type: " + header.DataType); #endif message = DecodeUnknown(stream); break; } message.Header = header; message.Timestamp = header.Timer; return(message); }
static ByteBuffer EncodePing(RtmpContext context, Ping ping) { int len = 6; if (ping.Value3 != Ping.Undefined) { len += 4; } if (ping.Value4 != Ping.Undefined) { len += 4; } ByteBuffer output = ByteBuffer.Allocate(len); output.PutShort(ping.PingType); output.PutInt(ping.Value2); if (ping.Value3 != Ping.Undefined) { output.PutInt(ping.Value3); } if (ping.Value4 != Ping.Undefined) { output.PutInt(ping.Value4); } return output; }
static ByteBuffer EncodeAudioData(RtmpContext context, AudioData audioData) { ByteBuffer output = audioData.Data; return output; }
static ByteBuffer EncodeVideoData(RtmpContext context, VideoData videoData) { ByteBuffer output = videoData.Data; return(output); }
/// <summary> /// Decodes all available objects in the buffer. /// </summary> /// <param name="context">State for the protocol.</param> /// <param name="stream">Buffer of data to be decoded.</param> /// <returns>A list of decoded objects, may be empty if nothing could be decoded.</returns> public static List<object> DecodeBuffer(RtmpContext context, ByteBuffer stream)
static ByteBuffer EncodeStreamMetadata(RtmpContext context, Notify metaData) { ByteBuffer output = metaData.Data; return(output); }
private static object Decode(RtmpContext context, ByteBuffer stream) { //long start = stream.Position; switch(context.State) { case RtmpState.Connected: return DecodePacket(context, stream); case RtmpState.Error: // Attempt to correct error return null; case RtmpState.Connect: case RtmpState.Handshake: return DecodeHandshake(context, stream); default: return null; } }
static ByteBuffer EncodeFlexStreamSend(RtmpContext context, FlexStreamSend msg) { ByteBuffer output = msg.Data; return(output); }
/// <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; }
static ByteBuffer EncodeInvoke(RtmpContext context, Invoke invoke) { return(EncodeNotifyOrInvoke(context, invoke)); }
/// <summary> /// Decodes RTMP message event. /// </summary> /// <param name="context">RTMP protocol state.</param> /// <param name="header">RTMP header.</param> /// <param name="stream">Buffer to be decoded.</param> /// <returns>Decoded RTMP event.</returns> public static IRtmpEvent DecodeMessage(RtmpContext context, RtmpHeader header, ByteBuffer stream) { IRtmpEvent message = null; /* if(header.Timer == 0xffffff) { // Skip first four bytes byte[] extendedTimestamp = new byte[4]; stream.Read(extendedTimestamp, 0, 4); log.Warn("Discarding extended timestamp"); //int unknown = stream.ReadInt32(); } */ switch(header.DataType) { case Constants.TypeChunkSize: message = DecodeChunkSize(stream); break; case Constants.TypeInvoke: message = DecodeInvoke(stream); break; case Constants.TypeFlexInvoke: message = DecodeFlexInvoke(stream); break; case Constants.TypeNotify: if( header.StreamId == 0 ) message = DecodeNotify(stream, header); else message = DecodeStreamMetadata(stream); break; case Constants.TypePing: message = DecodePing(stream); break; case Constants.TypeBytesRead: message = DecodeBytesRead(stream); break; case Constants.TypeAudioData: message = DecodeAudioData(stream); break; case Constants.TypeVideoData: message = DecodeVideoData(stream); break; case Constants.TypeSharedObject: message = DecodeSharedObject(stream); break; case Constants.TypeFlexSharedObject: message = DecodeFlexSharedObject(stream); break; case Constants.TypeServerBandwidth: message = DecodeServerBW(stream); break; case Constants.TypeClientBandwidth: message = DecodeClientBW(stream); break; default: #if !SILVERLIGHT log.Warn("Unknown object type: " + header.DataType); #endif message = DecodeUnknown(stream); break; } message.Header = header; message.Timestamp = header.Timer; return message; }
static ByteBuffer EncodeNotify(RtmpContext context, Notify notify) { return(EncodeNotifyOrInvoke(context, notify)); }
public static ByteBuffer EncodePacket(RtmpContext context, RtmpPacket packet) { RtmpHeader header = packet.Header; int channelId = header.ChannelId; IRtmpEvent message = packet.Message; ByteBuffer data; if (message is ChunkSize) { ChunkSize chunkSizeMsg = (ChunkSize)message; context.SetWriteChunkSize(chunkSizeMsg.Size); } data = EncodeMessage(context, header, message); if(data.Position != 0) data.Flip(); else data.Rewind(); header.Size = (int)data.Limit; RtmpHeader lastHeader = context.GetLastWriteHeader(channelId); int headerSize = CalculateHeaderSize(header, lastHeader); context.SetLastWriteHeader(channelId, header); context.SetLastWritePacket(channelId, packet); int chunkSize = context.GetWriteChunkSize(); int chunkHeaderSize = 1; if (header.ChannelId > 320) chunkHeaderSize = 3; else if (header.ChannelId > 63) chunkHeaderSize = 2; int numChunks = (int)Math.Ceiling(header.Size / (float)chunkSize); int bufSize = (int)header.Size + headerSize + (numChunks > 0 ? (numChunks - 1) * chunkHeaderSize : 0); ByteBuffer output = ByteBuffer.Allocate(bufSize); EncodeHeader(header, lastHeader, output); if (numChunks == 1) { // we can do it with a single copy ByteBuffer.Put(output, data, output.Remaining); } else { for(int i = 0; i < numChunks - 1; i++) { ByteBuffer.Put(output, data, chunkSize); EncodeHeaderByte(output, (byte)HeaderType.HeaderContinue, header.ChannelId); } ByteBuffer.Put(output, data, output.Remaining); } //data.Close(); output.Flip(); return output; }
static ByteBuffer EncodeStreamMetadata(RtmpContext context, Notify metaData) { ByteBuffer output = metaData.Data; return output; }
/// <summary> /// Encode server-side bandwidth event /// </summary> /// <param name="context"></param> /// <param name="serverBW"></param> /// <returns></returns> static ByteBuffer EncodeServerBW(RtmpContext context, ServerBW serverBW) { ByteBuffer output = ByteBuffer.Allocate(4); output.PutInt(serverBW.Bandwidth); return output; }
static ByteBuffer EncodeInvoke(RtmpContext context, Invoke invoke) { return EncodeNotifyOrInvoke(context, invoke); }
static ByteBuffer EncodeChunkSize(RtmpContext context, ChunkSize chunkSize) { ByteBuffer output = ByteBuffer.Allocate(4); output.PutInt(chunkSize.Size); return output; }
static ByteBuffer EncodeNotifyOrInvoke(RtmpContext context, Notify invoke) { //MemoryStreamEx output = new MemoryStreamEx(); ByteBuffer output = ByteBuffer.Allocate(1024); output.AutoExpand = true; RtmpWriter writer = new RtmpWriter(output); //Set legacy collection flag from context writer.UseLegacyCollection = context.UseLegacyCollection; writer.UseLegacyThrowable = context.UseLegacyThrowable; IServiceCall serviceCall = invoke.ServiceCall; bool isPending = serviceCall.Status == Call.STATUS_PENDING; if (!isPending) { //log.debug("Call has been executed, send result"); writer.WriteData(context.ObjectEncoding, serviceCall.IsSuccess ? "_result" : "_error"); } else { //log.debug("This is a pending call, send request"); string action = (serviceCall.ServiceName == null) ? serviceCall.ServiceMethodName : serviceCall.ServiceName + "." + serviceCall.ServiceMethodName; writer.WriteData(context.ObjectEncoding, action); } if(invoke is Invoke) { writer.WriteData(context.ObjectEncoding, invoke.InvokeId); writer.WriteData(context.ObjectEncoding, invoke.ConnectionParameters); } if(!isPending && (invoke is Invoke)) { IPendingServiceCall pendingCall = (IPendingServiceCall)serviceCall; if (!serviceCall.IsSuccess && !(pendingCall.Result is StatusASO)) { StatusASO status = GenerateErrorResult(StatusASO.NC_CALL_FAILED, serviceCall.Exception); pendingCall.Result = status; } writer.WriteData(context.ObjectEncoding, pendingCall.Result); } else { //log.debug("Writing params"); object[] args = invoke.ServiceCall.Arguments; if (args != null) { foreach(object element in args) { writer.WriteData(context.ObjectEncoding, element); } } } return output; }
static ByteBuffer EncodeBytesRead(RtmpContext context, BytesRead bytesRead) { ByteBuffer output = ByteBuffer.Allocate(4); output.PutInt(bytesRead.Bytes); return output; }
static ByteBuffer EncodeSharedObject(RtmpContext context, ISharedObjectMessage so) { ByteBuffer output = ByteBuffer.Allocate(128); output.AutoExpand = true; EncodeSharedObject(context, so, output); return output; }
static ByteBuffer EncodeVideoData(RtmpContext context, VideoData videoData) { ByteBuffer output = videoData.Data; return output; }
/// <summary> /// Decodes all available objects in the buffer. /// </summary> /// <param name="context">State for the protocol.</param> /// <param name="stream">Buffer of data to be decoded.</param> /// <returns>A list of decoded objects, may be empty if nothing could be decoded.</returns> public static List <object> DecodeBuffer(RtmpContext context, ByteBuffer stream)
static ByteBuffer EncodeFlexStreamSend(RtmpContext context, FlexStreamSend msg) { ByteBuffer output = msg.Data; return output; }
static ByteBuffer EncodeFlexInvoke(RtmpContext context, FlexInvoke invoke) { ByteBuffer output = ByteBuffer.Allocate(1024); output.AutoExpand = true; RtmpWriter writer = new RtmpWriter(output); //Set legacy collection flag from context writer.UseLegacyCollection = context.UseLegacyCollection; writer.UseLegacyThrowable = context.UseLegacyThrowable; writer.WriteByte(0); //writer.WriteData(context.ObjectEncoding, invoke.Cmd); IServiceCall serviceCall = invoke.ServiceCall; bool isPending = serviceCall.Status == Call.STATUS_PENDING; if (!isPending) { //log.debug("Call has been executed, send result"); if (serviceCall.IsSuccess) { writer.WriteData(context.ObjectEncoding, "_result"); } else { writer.WriteData(context.ObjectEncoding, "_error"); } } else { //log.debug("This is a pending call, send request"); writer.WriteData(context.ObjectEncoding, serviceCall.ServiceMethodName); } writer.WriteData(context.ObjectEncoding, invoke.InvokeId); writer.WriteData(context.ObjectEncoding, invoke.CmdData); //object response = invoke.Response; //writer.WriteData(context.ObjectEncoding, response); if (!isPending) { IPendingServiceCall pendingCall = (IPendingServiceCall)serviceCall; /* * if (!serviceCall.IsSuccess) * { * StatusASO status = GenerateErrorResult(StatusASO.NC_CALL_FAILED, serviceCall.Exception); * pendingCall.Result = status; * } */ writer.WriteData(context.ObjectEncoding, pendingCall.Result); } else { //log.debug("Writing params"); object[] args = invoke.ServiceCall.Arguments; if (args != null) { foreach (object element in args) { writer.WriteData(context.ObjectEncoding, element); } } } return(output); }
static ByteBuffer EncodeNotify(RtmpContext context, Notify notify) { return EncodeNotifyOrInvoke(context, notify); }
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; } } }
static ByteBuffer EncodeFlexInvoke(RtmpContext context, FlexInvoke invoke) { ByteBuffer output = ByteBuffer.Allocate(1024); output.AutoExpand = true; RtmpWriter writer = new RtmpWriter(output); //Set legacy collection flag from context writer.UseLegacyCollection = context.UseLegacyCollection; writer.UseLegacyThrowable = context.UseLegacyThrowable; writer.WriteByte(0); //writer.WriteData(context.ObjectEncoding, invoke.Cmd); IServiceCall serviceCall = invoke.ServiceCall; bool isPending = serviceCall.Status == Call.STATUS_PENDING; if (!isPending) { //log.debug("Call has been executed, send result"); if (serviceCall.IsSuccess) writer.WriteData(context.ObjectEncoding, "_result"); else writer.WriteData(context.ObjectEncoding, "_error"); } else { //log.debug("This is a pending call, send request"); writer.WriteData(context.ObjectEncoding, serviceCall.ServiceMethodName); } writer.WriteData(context.ObjectEncoding, invoke.InvokeId); writer.WriteData(context.ObjectEncoding, invoke.CmdData); //object response = invoke.Response; //writer.WriteData(context.ObjectEncoding, response); if (!isPending) { IPendingServiceCall pendingCall = (IPendingServiceCall)serviceCall; /* if (!serviceCall.IsSuccess) { StatusASO status = GenerateErrorResult(StatusASO.NC_CALL_FAILED, serviceCall.Exception); pendingCall.Result = status; } */ writer.WriteData(context.ObjectEncoding, pendingCall.Result); } else { //log.debug("Writing params"); object[] args = invoke.ServiceCall.Arguments; if (args != null) { foreach (object element in args) { writer.WriteData(context.ObjectEncoding, element); } } } return output; }
internal RtmpConnection(IRtmpHandler handler, RtmpMode mode, string path, IDictionary parameters) : base(path, parameters) { _handler = handler; _channels = new CopyOnWriteDictionary<int,RtmpChannel>(4); _streams = new CopyOnWriteDictionary<int,IClientStream>(); _pendingVideos = new CopyOnWriteDictionary<int,AtomicInteger>(); _streamCount = new AtomicInteger(); _streamBuffers = new CopyOnWriteDictionary<int,int>(); _reservedStreams = new BitArray(0); _pendingCalls = new CopyOnWriteDictionary<int, IServiceCall>(); // We start with an anonymous connection without a scope. // These parameters will be set during the call of "connect" later. _context = new RtmpContext(mode); //Transaction id depends on server/client mode //When server mode is set we cannot push messages with transaction id = 1 (connect) _invokeId = mode == RtmpMode.Server ? new AtomicInteger(1) : new AtomicInteger(0); }
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; } } }
public static RtmptRequest DecodeBuffer(RtmpConnection connection, ByteBuffer stream) { RtmpContext context = connection.Context; int position = (int)stream.Position; try { BufferStreamReader sr = new BufferStreamReader(stream); string request = sr.ReadLine(); string[] tokens = request.Split(new char[] { ' ' }); string method = tokens[0]; string url = tokens[1]; // Decode all encoded parts of the URL using the built in URI processing class int i = 0; while ((i = url.IndexOf("%", i)) != -1) { url = url.Substring(0, i) + Uri.HexUnescape(url, ref i) + url.Substring(i); } // Lets just make sure we are using HTTP, thats about all I care about string protocol = tokens[2];// "HTTP/" //Read headers Hashtable headers = new Hashtable(); string line; string name = null; while ((line = sr.ReadLine()) != null && line != string.Empty) { // If the value begins with a space or a hard tab then this // is an extension of the value of the previous header and // should be appended if (name != null && Char.IsWhiteSpace(line[0])) { headers[name] += line; continue; } // Headers consist of [NAME]: [VALUE] + possible extension lines int firstColon = line.IndexOf(":"); if (firstColon != -1) { name = line.Substring(0, firstColon); string value = line.Substring(firstColon + 1).Trim(); headers[name] = value; } else { //400, "Bad header: " + line break; } } RtmptRequest rtmptRequest = new RtmptRequest(connection, url, protocol, method, headers); if (stream.Remaining == rtmptRequest.ContentLength) { stream.Compact(); rtmptRequest.Data = ByteBuffer.Wrap(stream.ToArray()); stream.Flip(); return(rtmptRequest); } else { // Move the position back to the start stream.Position = position; } } catch { // Move the position back to the start stream.Position = position; throw; } return(null); }