Example #1
0
        static RtmpMessage ReadCommandOrData(AmfReader r, Command command, RtmpHeader header = null)
        {
            var    methodName = (string)r.ReadAmf0Item();
            object temp       = r.ReadAmf0Item();

            if (header != null && methodName == "@setDataFrame")
            {
                command.ConnectionParameters = temp;
            }
            else
            {
                command.InvokeId             = Convert.ToInt32(temp);
                command.ConnectionParameters = r.ReadAmf0Item();
            }


            var parameters = new List <object>();

            while (r.DataAvailable)
            {
                parameters.Add(r.ReadAmf0Item());
            }

            command.MethodCall = new Method(methodName, parameters.ToArray());
            return(command);
        }
        public void Queue(RtmpMessage message, int streamId, int messageStreamId)
        {
            var header = new RtmpHeader();
            var packet = new RtmpPacket(header, message);

            header.StreamId        = streamId;
            header.Timestamp       = message.Timestamp;
            header.MessageStreamId = messageStreamId;
            header.MessageType     = message.MessageType;
            if (message.Header != null)
            {
                header.IsTimerRelative = message.Header.IsTimerRelative;
            }
            queuedPackets.Enqueue(packet);
            // packetAvailableEvent.Set();
            Interlocked.Exchange(ref packetAvailable, 1);
        }
        static ChunkMessageHeaderType GetMessageHeaderType(RtmpHeader header, RtmpHeader previousHeader)
        {
            if (previousHeader == null || header.MessageStreamId != previousHeader.MessageStreamId || !header.IsTimerRelative)
            {
                return(ChunkMessageHeaderType.New);
            }

            if (header.PacketLength != previousHeader.PacketLength || header.MessageType != previousHeader.MessageType)
            {
                return(ChunkMessageHeaderType.SameSource);
            }

            if (header.Timestamp != previousHeader.Timestamp)
            {
                return(ChunkMessageHeaderType.TimestampAdjustment);
            }

            return(ChunkMessageHeaderType.Continuation);
        }
        void WriteMessageHeaderAsync(RtmpHeader header, RtmpHeader previousHeader)
        {
            var headerType = GetMessageHeaderType(header, previousHeader);

            WriteBasicHeaderAsync(headerType, header.StreamId);

            var uint24Timestamp = header.Timestamp < 0xFFFFFF ? header.Timestamp : 0xFFFFFF;

            switch (headerType)
            {
            case ChunkMessageHeaderType.New:
                writer.WriteUInt24Async(uint24Timestamp);
                writer.WriteUInt24Async(header.PacketLength);
                writer.WriteByteAsync((byte)header.MessageType);
                writer.WriteReverseIntAsync(header.MessageStreamId);
                break;

            case ChunkMessageHeaderType.SameSource:
                writer.WriteUInt24Async(uint24Timestamp);
                writer.WriteUInt24Async(header.PacketLength);
                writer.WriteByteAsync((byte)header.MessageType);
                break;

            case ChunkMessageHeaderType.TimestampAdjustment:
                writer.WriteUInt24Async(uint24Timestamp);
                break;

            case ChunkMessageHeaderType.Continuation:
                break;

            default:
                throw new ArgumentException("headerType");
            }

            if (uint24Timestamp >= 0xFFFFFF)
            {
                writer.WriteInt32Async(header.Timestamp);
            }
        }
Example #5
0
 public RtmpPacket(RtmpHeader header, RtmpMessage body) : this(header)
 {
     Body   = body;
     Length = header.PacketLength;
 }
Example #6
0
 public RtmpPacket(RtmpHeader header)
 {
     Header = header;
     Length = header.PacketLength;
     Buffer = new byte[Length];
 }
Example #7
0
        RtmpHeader ReadHeader()
        {
            if (reader.underlying.BaseStream is NetworkStream)
            {
                var orig_stream = (NetworkStream)reader.underlying.BaseStream;

                if (!orig_stream.DataAvailable)
                {
                    return(null);
                }
            }
            if (reader.underlying.BaseStream is SslStream)
            {
                var orig_stream = (SslStream)reader.underlying.BaseStream;
                throw new NotImplementedException();
            }
            if (reader.underlying.BaseStream is WebsocketStream)
            {
                return(null);
                //var orig_stream = (WebsocketStream)reader.underlying.BaseStream;

                //if (!orig_stream.DataAvailable)
                //{
                //    return null;
                //}
            }

            // first byte of the chunk basic header
            var chunkBasicHeaderByte   = reader.ReadByte();
            var chunkStreamId          = GetChunkStreamId(chunkBasicHeaderByte, reader);
            var chunkMessageHeaderType = (ChunkMessageHeaderType)(chunkBasicHeaderByte >> 6);

            var header = new RtmpHeader()
            {
                StreamId        = chunkStreamId,
                IsTimerRelative = chunkMessageHeaderType != ChunkMessageHeaderType.New
            };

            RtmpHeader previousHeader;

            // don't need to clone if new header, as it contains all info
            if (!rtmpHeaders.TryGetValue(chunkStreamId, out previousHeader) && chunkMessageHeaderType != ChunkMessageHeaderType.New)
            {
                previousHeader = header.Clone();
            }

            switch (chunkMessageHeaderType)
            {
            // 11 bytes
            case ChunkMessageHeaderType.New:
                header.Timestamp       = reader.ReadUInt24();
                header.PacketLength    = reader.ReadUInt24();
                header.MessageType     = (MessageType)reader.ReadByte();
                header.MessageStreamId = reader.ReadReverseInt();
                break;

            // 7 bytes
            case ChunkMessageHeaderType.SameSource:
                header.Timestamp       = reader.ReadUInt24();
                header.PacketLength    = reader.ReadUInt24();
                header.MessageType     = (MessageType)reader.ReadByte();
                header.MessageStreamId = previousHeader.MessageStreamId;
                break;

            // 3 bytes
            case ChunkMessageHeaderType.TimestampAdjustment:
                header.Timestamp       = reader.ReadUInt24();
                header.PacketLength    = previousHeader.PacketLength;
                header.MessageType     = previousHeader.MessageType;
                header.MessageStreamId = previousHeader.MessageStreamId;
                break;

            // 0 bytes
            case ChunkMessageHeaderType.Continuation:
                header.Timestamp       = previousHeader.Timestamp;
                header.PacketLength    = previousHeader.PacketLength;
                header.MessageType     = previousHeader.MessageType;
                header.MessageStreamId = previousHeader.MessageStreamId;
                header.IsTimerRelative = previousHeader.IsTimerRelative;
                break;

            default:
                throw new SerializationException("Unexpected header type: " + (int)chunkMessageHeaderType);
            }

            // extended timestamp
            if (header.Timestamp == 0xFFFFFF)
            {
                header.Timestamp = reader.ReadInt32();
            }

            return(header);
        }
Example #8
0
        async Task <RtmpHeader> ReadHeaderAsync()
        {
            var chunkBasicHeaderByte = await reader.ReadByteAsync();

            var chunkStreamId = await GetChunkStreamIdAsync(chunkBasicHeaderByte, reader);

            var chunkMessageHeaderType = (ChunkMessageHeaderType)(chunkBasicHeaderByte >> 6);

            var header = new RtmpHeader()
            {
                StreamId        = chunkStreamId,
                IsTimerRelative = chunkMessageHeaderType != ChunkMessageHeaderType.New
            };

            RtmpHeader previousHeader;

            // don't need to clone if new header, as it contains all info
            if (!rtmpHeaders.TryGetValue(chunkStreamId, out previousHeader) && chunkMessageHeaderType != ChunkMessageHeaderType.New)
            {
                previousHeader = header.Clone();
            }

            switch (chunkMessageHeaderType)
            {
            // 11 bytes
            case ChunkMessageHeaderType.New:
                header.Timestamp = await reader.ReadUInt24Async();

                header.PacketLength = await reader.ReadUInt24Async();

                header.MessageType = (MessageType)await reader.ReadByteAsync();

                header.MessageStreamId = await reader.ReadReverseIntAsync();

                break;

            // 7 bytes
            case ChunkMessageHeaderType.SameSource:
                header.Timestamp = await reader.ReadUInt24Async();

                header.PacketLength = await reader.ReadUInt24Async();

                header.MessageType = (MessageType)await reader.ReadByteAsync();

                header.MessageStreamId = previousHeader.MessageStreamId;
                break;

            // 3 bytes
            case ChunkMessageHeaderType.TimestampAdjustment:
                header.Timestamp = await reader.ReadUInt24Async();

                header.PacketLength    = previousHeader.PacketLength;
                header.MessageType     = previousHeader.MessageType;
                header.MessageStreamId = previousHeader.MessageStreamId;
                break;

            // 0 bytes
            case ChunkMessageHeaderType.Continuation:
                header.Timestamp       = previousHeader.Timestamp;
                header.PacketLength    = previousHeader.PacketLength;
                header.MessageType     = previousHeader.MessageType;
                header.MessageStreamId = previousHeader.MessageStreamId;
                header.IsTimerRelative = previousHeader.IsTimerRelative;
                break;

            default:
                throw new SerializationException("Unexpected header type: " + (int)chunkMessageHeaderType);
            }

            // extended timestamp
            if (header.Timestamp == 0xFFFFFF)
            {
                header.Timestamp = await reader.ReadInt32Async();
            }

            return(header);
        }
        byte[] GetMessageBytes(RtmpHeader header, RtmpMessage message)
        {
            switch (header.MessageType)
            {
            case MessageType.SetChunkSize:
                return(GetMessageBytes(message, (w, o) => w.WriteInt32(((ChunkSize)o).Size)));

            case MessageType.AbortMessage:
                return(GetMessageBytes(message, (w, o) => w.WriteInt32(((Abort)o).StreamId)));

            case MessageType.Acknowledgement:
                return(GetMessageBytes(message, (w, o) => w.WriteInt32(((Acknowledgement)o).BytesRead)));

            case MessageType.UserControlMessage:
                return(GetMessageBytes(message, (w, o) =>
                {
                    var m = (UserControlMessage)o;
                    w.WriteUInt16((ushort)m.EventType);
                    foreach (var v in m.Values)
                    {
                        w.WriteInt32(v);
                    }
                }));

            case MessageType.WindowAcknowledgementSize:
                return(GetMessageBytes(message, (w, o) => w.WriteInt32(((WindowAcknowledgementSize)o).Count)));

            case MessageType.SetPeerBandwith:
                return(GetMessageBytes(message, (w, o) =>
                {
                    var m = (PeerBandwidth)o;
                    w.WriteInt32(m.AcknowledgementWindowSize);
                    w.WriteByte((byte)m.LimitType);
                }));

            case MessageType.Audio:
            case MessageType.Video:
                return(GetMessageBytes(message, (w, o) => WriteData(w, o, ObjectEncoding.Amf0)));


            case MessageType.DataAmf0:
                return(GetMessageBytes(message, (w, o) => WriteCommandOrData(w, o, ObjectEncoding.Amf0)));

            case MessageType.SharedObjectAmf0:
                return(new byte[0]);    // todo: `SharedObject`s

            case MessageType.CommandAmf0:
                return(GetMessageBytes(message, (w, o) => WriteCommandOrData(w, o, ObjectEncoding.Amf0)));


            case MessageType.DataAmf3:
                return(GetMessageBytes(message, (w, o) => WriteData(w, o, ObjectEncoding.Amf3)));

            case MessageType.SharedObjectAmf3:
                return(new byte[0]);    // todo: `SharedObject`s

            case MessageType.CommandAmf3:
                return(GetMessageBytes(message, (w, o) =>
                {
                    w.WriteByte(0);
                    WriteCommandOrData(w, o, ObjectEncoding.Amf3);
                }));

            case MessageType.Aggregate:
                // todo: Aggregate messages
                System.Diagnostics.Debugger.Break();
                return(new byte[0]);    // todo: `Aggregate`

            default:
                throw new ArgumentOutOfRangeException("Unknown RTMP message type: " + (int)header.MessageType);
            }
        }