/// <summary> /// Writes packet from event data to the RTMP connection using the specified stream id. /// </summary> /// <param name="message">Event data.</param> /// <param name="streamId">Stream id.</param> private void Write(IRtmpEvent message, int streamId) { RtmpHeader header = new RtmpHeader(); RtmpPacket packet = new RtmpPacket(header, message); header.ChannelId = _channelId; header.Timer = message.Timestamp; header.StreamId = streamId; header.DataType = message.DataType; if (message.Header != null) header.IsTimerRelative = message.Header.IsTimerRelative; _connection.Write(packet); }
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; }
/// <summary> /// Writes packet from event data to the RTMP connection using the specified stream id. /// </summary> /// <param name="message">Event data.</param> /// <param name="streamId">Stream id.</param> private void Write(IRtmpEvent message, int streamId) { RtmpHeader header = new RtmpHeader(); RtmpPacket packet = new RtmpPacket(header, message); header.ChannelId = _channelId; header.Timer = message.Timestamp; header.StreamId = streamId; header.DataType = message.DataType; if (message.Header != null) { header.IsTimerRelative = message.Header.IsTimerRelative; } _connection.Write(packet); }
public override void MessageSent(RtmpConnection connection, object message) { base.MessageSent(connection, message); RtmpPacket sent = message as RtmpPacket; int channelId = sent.Header.ChannelId; IClientStream stream = null; if (connection is IStreamCapableConnection) { stream = (connection as IStreamCapableConnection).GetStreamByChannelId(channelId); } // XXX we'd better use new event model for notification if (stream != null && (stream is PlaylistSubscriberStream)) { (stream as PlaylistSubscriberStream).Written(sent.Message); } }
internal void SetLastWritePacket(int channelId, RtmpPacket packet) { try { ReaderWriterLock.AcquireWriterLock(); RtmpPacket prevPacket = null; if (_writePackets.ContainsKey(channelId)) { prevPacket = _writePackets[channelId] as RtmpPacket; } if (prevPacket != null && prevPacket.Data != null) { prevPacket.Data = null; } _writePackets[channelId] = packet; } finally { ReaderWriterLock.ReleaseWriterLock(); } }
public override void Write(RtmpPacket packet) { _lock.AcquireReaderLock(); try { if (IsClosed || IsClosing || IsDisconnecting) { return; // Already shutting down. } } finally { _lock.ReleaseReaderLock(); } if (log.IsDebugEnabled) { log.Debug(__Res.GetString(__Res.Rtmp_WritePacket, _connectionId, packet)); } if (!this.IsTunneled) { //encode WritingMessage(packet); ByteBuffer outputStream = null; _lock.AcquireWriterLock(); try { outputStream = RtmpProtocolEncoder.Encode(this.Context, packet); } finally { _lock.ReleaseWriterLock(); } Write(outputStream); _rtmpServer.RtmpHandler.MessageSent(this, packet); } else { //We should never get here System.Diagnostics.Debug.Assert(false); } }
public override void Write(RtmpPacket packet) { _lock.AcquireReaderLock(); try { if (IsClosed || IsClosing || IsDisconnecting) return; // Already shutting down. } finally { _lock.ReleaseReaderLock(); } if (log.IsDebugEnabled) log.Debug(__Res.GetString(__Res.Rtmp_WritePacket, _connectionId, packet)); if (!this.IsTunneled) { //encode WritingMessage(packet); ByteBuffer outputStream = null; _lock.AcquireWriterLock(); try { outputStream = RtmpProtocolEncoder.Encode(this.Context, packet); } finally { _lock.ReleaseWriterLock(); } Write(outputStream); _rtmpServer.RtmpHandler.MessageSent(this, packet); } else { //We should never get here System.Diagnostics.Debug.Assert(false); } }
/// <summary> /// Message recieved. /// </summary> /// <param name="connection">Connection object.</param> /// <param name="obj">Message object.</param> public void MessageReceived(RtmpConnection connection, object obj) { IRtmpEvent message = null; RtmpPacket packet = null; RtmpHeader header = null; RtmpChannel channel = null; IClientStream stream = null; try { packet = obj as RtmpPacket; message = packet.Message; header = packet.Header; channel = connection.GetChannel(header.ChannelId); if (connection is IStreamCapableConnection) { stream = (connection as IStreamCapableConnection).GetStreamById(header.StreamId); } // Support stream ids #if !SILVERLIGHT FluorineContext.ValidateContext(); FluorineContext.Current.Connection.SetAttribute(FluorineContext.FluorineStreamIdKey, header.StreamId); #endif // Increase number of received messages connection.MessageReceived(); #if !SILVERLIGHT if (log != null && log.IsDebugEnabled) { log.Debug("RtmpConnection message received, type = " + header.DataType); } #endif if (message != null) { message.Source = connection; } switch (header.DataType) { case Constants.TypeInvoke: OnInvoke(connection, channel, header, message as Invoke); if (message.Header.StreamId != 0 && (message as Invoke).ServiceCall.ServiceName == null && (message as Invoke).ServiceCall.ServiceMethodName == BaseRtmpHandler.ACTION_PUBLISH) { if (stream != null) //Dispatch if stream was created { (stream as IEventDispatcher).DispatchEvent(message); } } break; case Constants.TypeFlexInvoke: OnFlexInvoke(connection, channel, header, message as FlexInvoke); if (message.Header.StreamId != 0 && (message as Invoke).ServiceCall.ServiceName == null && (message as Invoke).ServiceCall.ServiceMethodName == BaseRtmpHandler.ACTION_PUBLISH) { if (stream != null) //Dispatch if stream was created { (stream as IEventDispatcher).DispatchEvent(message); } } break; case Constants.TypeNotify: // just like invoke, but does not return if ((message as Notify).Data != null && stream != null) { // Stream metadata (stream as IEventDispatcher).DispatchEvent(message); } else { OnInvoke(connection, channel, header, message as Notify); } break; case Constants.TypePing: OnPing(connection, channel, header, message as Ping); break; case Constants.TypeBytesRead: OnStreamBytesRead(connection, channel, header, message as BytesRead); break; case Constants.TypeSharedObject: case Constants.TypeFlexSharedObject: OnSharedObject(connection, channel, header, message as SharedObjectMessage); break; case Constants.TypeFlexStreamEnd: if (stream != null) { (stream as IEventDispatcher).DispatchEvent(message); } break; case Constants.TypeChunkSize: OnChunkSize(connection, channel, header, message as ChunkSize); break; case Constants.TypeAudioData: case Constants.TypeVideoData: // NOTE: If we respond to "publish" with "NetStream.Publish.BadName", // the client sends a few stream packets before stopping. We need to // ignore them. if (stream != null) { ((IEventDispatcher)stream).DispatchEvent(message); } break; case Constants.TypeServerBandwidth: OnServerBW(connection, channel, header, message as ServerBW); break; case Constants.TypeClientBandwidth: OnClientBW(connection, channel, header, message as ClientBW); break; default: #if !SILVERLIGHT if (log != null && log.IsDebugEnabled) { log.Debug("RtmpService event not handled: " + header.DataType); } #endif break; } } catch (Exception ex) { #if !SILVERLIGHT if (log.IsErrorEnabled) { log.Error(__Res.GetString(__Res.Rtmp_HandlerError), ex); log.Error(__Res.GetString(__Res.Error_ContextDump)); //log.Error(Environment.NewLine); log.Error(packet); } #endif } }
/// <summary> /// Mark message as being written. /// </summary> /// <param name="packet">The RTMP packet</param> protected virtual void WritingMessage(RtmpPacket packet) { if (packet.Message is VideoData) { int streamId = packet.Header.StreamId; AtomicInteger value = new AtomicInteger(); AtomicInteger old = _pendingVideos.AddIfAbsent(streamId, value) as AtomicInteger; if (old == null) old = value; old.Increment(); } }
/// <summary> /// Write a RTMP packet. /// </summary> /// <param name="packet">The RTMP packet.</param> public abstract void Write(RtmpPacket packet);
internal virtual void MessageSent(RtmpPacket packet) { if (packet.Message is VideoData) { int streamId = packet.Header.StreamId; AtomicInteger pending = null; _pendingVideos.TryGetValue(streamId, out pending); if (pending != null) pending.Decrement(); } _writtenMessages.Increment(); }
public PendingData(object buffer, RtmpPacket packet) { _buffer = buffer; _packet = packet; }
/// <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 = GodLesZ.Library.Amf.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); }
public override void Write(RtmpPacket packet) { _lock.AcquireReaderLock(); try { if (IsClosed || IsClosing) return; } finally { _lock.ReleaseReaderLock(); } try { _lock.AcquireWriterLock(); ByteBuffer data; try { data = RtmpProtocolEncoder.Encode(this.Context, packet); } catch (Exception ex) { log.Error("Could not encode message " + packet, ex); return; } // Mark packet as being written WritingMessage(packet); if (_pendingMessages == null) _pendingMessages = new LinkedList(); _pendingMessages.Add(new PendingData(data, packet)); } finally { _lock.ReleaseWriterLock(); } }
internal void SetLastWritePacket(int channelId, RtmpPacket packet) { try { ReaderWriterLock.AcquireWriterLock(); RtmpPacket prevPacket = null; if (_writePackets.ContainsKey(channelId)) prevPacket = _writePackets[channelId] as RtmpPacket; if (prevPacket != null && prevPacket.Data != null) { prevPacket.Data = null; } _writePackets[channelId] = packet; } finally { ReaderWriterLock.ReleaseWriterLock(); } }
/// <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 = GodLesZ.Library.Amf.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; }
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); }
public override void Write(RtmpPacket packet) { if (!IsClosed) { #if !SILVERLIGHT if (log.IsDebugEnabled) log.Debug("Write " + packet.Header); #endif //encode WritingMessage(packet); ByteBuffer outputStream = RtmpProtocolEncoder.Encode(Context, packet); Write(outputStream); _handler.MessageSent(this, packet); } }