Exemple #1
0
        public Task ProcessMessageAsync(RtmpMessage message)
        {
            BinaryPrimitives.TryReadInt32BigEndian(message.Payload.Span, out var value);
            switch (message.Message.MessageType)
            {
            case RtmpMessageType.SetChunkSize:
                SetChunkSize(value);
                break;

            case RtmpMessageType.SetPeerBandwidth:
                SetPeerBandwidth(value);
                break;

            case RtmpMessageType.WindowAcknowledgementSize:
                SetWindowAcknowledgementSize(value);
                break;

            case RtmpMessageType.Acknowledgement:
                break;

            default:
                throw new Exception($"Unknown control command {message.Message.MessageType}");
            }

            return(Task.CompletedTask);
        }
Exemple #2
0
        public async Task ProcessMessageAsync(RtmpMessage message)
        {
            var command = new AmfDataMessage();

            command.Decode(message.Payload.Span);
            await HandleDataCommandAsync(command, message);
        }
        private bool AssembleMessage(RtmpMessageEntry messageEntry, RtmpChunk chunk, out RtmpMessage message)
        {
            message = null;
            var currentMessage = messageEntry.Message;

            if (currentMessage == null)
            {
                var payload = _memoryPool.Rent(chunk.Message.Length);
                currentMessage       = new RtmpMessage(chunk.Header, chunk.Message, payload);
                messageEntry.Message = currentMessage;
            }

            var memory = currentMessage.Payload.Slice(messageEntry.BytesRead, chunk.PayloadLength);
            var reader = new SequenceReader <byte>(chunk.Payload);

            reader.TryCopyTo(memory.Span);
            messageEntry.BytesRead += chunk.PayloadLength;
            // if message is complete .. return it.
            if (messageEntry.BytesRemaining == 0)
            {
                message = messageEntry.Message;
                _logger.LogInformation($"Parsed message :{message.Header}: Message: {message.Message}");
                messageEntry.BytesRead = 0;
                messageEntry.Message   = null;
                return(true);
            }
            return(false);
        }
        private async Task HandlePublishStreamAsync(RtmpMessage message, AmfCommandMessage command)
        {
            var    streamName = command.AdditionalArguments[0] as string;
            var    streamPath = $"/{_application}/{streamName}";
            var    streamId = (int)message.Message.StreamId;
            var    stream = new RtmpStream(streamId, streamName, streamPath);
            string code, level, description;

            if (!_context.TryAddPublishser(streamPath, _session.Id))
            {
                _logger.LogError($@"[rtmp publish] Already has a stream. id ={_session.Id} streamPath ={streamPath} streamId ={streamId}");
                level       = "error";
                code        = "NetStream.Publish.BadName";
                description = "Stream already publishing";
            }
            else if (_session.PublishStream != null)
            {
                level       = "error";
                description = $"{streamPath} is already published.";
                code        = "NetStream.Publish.BadConnection";
            }
            else
            {
                _session.PublishStream = stream;
                level       = "status";
                description = $"{streamPath} is now published.";
                code        = "NetStream.Publish.Start";
            }
            await SendStatusMessageAsync(stream.Id, level, code, description);

            await _session.SendStartToAllPlayersAsync();
        }
        public Task DispatchMessageAsync(RtmpMessage message)
        {
            switch (message.Message.MessageType)
            {
            case RtmpMessageType.SetChunkSize:
            case RtmpMessageType.Acknowledgement:
            case RtmpMessageType.WindowAcknowledgementSize:
            case RtmpMessageType.SetPeerBandwidth:
                return(ProcessProtocolMessageAsync(message));

            case RtmpMessageType.CommandAMF0:
            case RtmpMessageType.CommandAMF3:
                return(ProcessCommandMessageAsync(message));

            case RtmpMessageType.DataAMF0:
            case RtmpMessageType.DataAMF3:
                return(ProcessDataMessageAsync(message));

            case RtmpMessageType.Audio:
            case RtmpMessageType.Video:
                return(ProcessMediaMessageAsync(message));

            case RtmpMessageType.UserCtrlMessage:
                return(ProcessUserControlMessageAsync(message));

            default:
                throw new Exception($"Unknown message of type: {message.Message.MessageType}");
            }
        }
        public async Task ProcessMessageAsync(RtmpMessage message)
        {
            var command = new AmfCommandMessage();

            command.Decode(message.Payload.Span);
            _logger.LogInformation($"Procesing command:{command.Name} for session {_session.Id}s");
            await ProcessCommandAsync(command, message);
        }
 public async Task SendProtocolControlMessageAsync(RtmpMessageType messageType, int data)
 {
     var header        = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Protocol);
     var messageHeader = new RtmpMessageHeader(0, 4, messageType, 0);
     var body          = BitConverter.GetBytes(BinaryPrimitives.ReverseEndianness(data));
     var message       = new RtmpMessage(header, messageHeader, body);
     await _session.SendMessageAsync(message);
 }
Exemple #8
0
        public async Task SendDataCommandAsync(AmfDataMessage data)
        {
            var body = new byte[data.GetLength()];

            data.Encode(body.AsSpan());
            var header        = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Data);
            var messageHeader = new RtmpMessageHeader(0, body.Length, RtmpMessageType.DataAMF0, 0);
            var message       = new RtmpMessage(header, messageHeader, body);
            await _session.SendMessageAsync(message);
        }
        public async Task SendCommandMessageAsync(int streamId, AmfCommandMessage command)
        {
            var body = new byte[command.GetLength()];

            command.Encode(body.AsSpan());
            var header        = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Invoke);
            var messageHeader = new RtmpMessageHeader(0, body.Length, RtmpMessageType.CommandAMF0, streamId);
            var message       = new RtmpMessage(header, messageHeader, body);
            await _session.SendMessageAsync(message);
        }
        protected override async Task ProcessCommandAsync(AmfCommandMessage command, RtmpMessage message)
        {
            switch (command.Name)
            {
            case "connect":
                await HandleConnectAsync(command);

                break;

            case "createStream":
                await HandleCreateStreamAsync(command);

                break;

            case "closeStream":
                await HandleCloseStreamAsync(command);

                break;

            case "deleteStream":
                await HandleDeleteStreamAsync(command);

                break;

            case "releaseStream":
                // nothting to do.
                break;

            case "FCPublish":
            case "FCUnpublish":
                break;

            case "publish":
                await HandlePublishStreamAsync(message, command);

                break;

            case "play":
                await HandlePlayAsync(message, command);

                break;

            case "pause":
                await HandlePauseAsync(message, command);

                break;

            case "getStreamLength":
                break;

            default:
                throw new InvalidOperationException($"Unknown command {command.Name} {command.CommandObject} ");
            }
        }
        private async Task SendUserControlMessageAsync(UserControlMessageType type, int data)
        {
            var header        = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Protocol);
            var messageHeader = new RtmpMessageHeader(0, 6, RtmpMessageType.UserCtrlMessage, 0);
            var payload       = new byte[6];

            BinaryPrimitives.WriteInt16BigEndian(payload.AsSpan(), (short)type);
            BinaryPrimitives.WriteInt32BigEndian(payload.AsSpan().Slice(2), data);
            var message = new RtmpMessage(header, messageHeader, payload);
            await _session.SendMessageAsync(message);
        }
Exemple #12
0
        private async Task SendMessageAsync(RtmpMessage message)
        {
            var chunkPayloadLength = Math.Min(message.Payload.Length, _session.OutgoingChunkLength);

            using var buffer = _memoryPool.Rent(chunkPayloadLength + RtmpChunk.MaxHeaderLength);
            foreach (var chunk in ChunkMessage(message))
            {
                var memory = buffer.Memory.Slice(0, chunk.Length);
                chunk.Write(memory.Span);
                await _stream.WriteAsync(memory);
            }
        }
        public async Task SendResultAsync(object data)
        {
            var chunkHeader = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Invoke);
            var response    = new AmfCommandMessage
            {
                Name          = "_result",
                CommandObject = data
            };
            var length = response.GetLength();
            var memory = new byte[length];

            response.Encode(memory);
            var messageHeader = new RtmpMessageHeader(0, length, RtmpMessageType.CommandAMF0, 0);
            var message       = new RtmpMessage(chunkHeader, messageHeader, memory);
            await _session.SendMessageAsync(message);
        }
Exemple #14
0
        public async Task ProcessMessageAsync(RtmpMessage message)
        {
            if (message.Message.MessageType == RtmpMessageType.Audio)
            {
                ProcessAudio(message);
            }
            else if (message.Message.MessageType == RtmpMessageType.Video)
            {
                ProcessVideo(message);
            }

            if (_session.IsPublishing)
            {
                await _session.SendToAllPalyersAsync(message);
            }
        }
 /// <summary>
 /// Send a mesage to the other side of this session.
 /// </summary>
 /// <param name="message">The Rtmp message to send.</param>
 /// <returns></returns>
 public override async Task SendMessageAsync(RtmpMessage message)
 {
     if (IsPlaying)
     {
         if (!_keyFrameSent && message.Message.MessageType == RtmpMessageType.Video)
         {
             var frame_type = (message.Payload.Span[0] >> 4) & 0x0f;
             _keyFrameSent = frame_type == 1;
         }
         if (!_keyFrameSent &&
             (message.Message.MessageType == RtmpMessageType.Audio || message.Message.MessageType == RtmpMessageType.Video))
         {
             //return;
         }
     }
     await base.SendMessageAsync(message);
 }
Exemple #16
0
        public IEnumerable <RtmpChunk> ChunkMessage(RtmpMessage message)
        {
            var length = message.Message.Length;
            var memory = message.Payload;
            var offset = 0;

            while (length != 0)
            {
                var chunkHeader = new RtmpChunkHeader(
                    offset == 0 ? RtmpChunkHeaderType.Type0 : RtmpChunkHeaderType.Type2,
                    message.Header.StreamId);
                var chunkLength = Math.Min(_session.OutgoingChunkLength, length);
                var chunk       = new RtmpChunk(chunkHeader, message.Message, memory.Slice(offset, chunkLength));
                yield return(chunk);

                length -= chunkLength;
                offset += chunkLength;
            }
        }
        public async Task  OnStartPlayAsync(ServerSession publisher, RtmpStream stream)
        {
            if (publisher.MetaData.Length != 0)
            {
                var payload       = publisher.MetaData;
                var header        = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Data);
                var messageHeader = new RtmpMessageHeader(0, payload.Length, RtmpMessageType.DataAMF0, stream.Id);
                var message       = new RtmpMessage(header, messageHeader, payload);
                await _session.SendMessageAsync(message);
            }

            if (publisher.AudioCodec?.CodecId == RtmpConstants.AacAudio)
            {
                var payload       = publisher.AudioCodec.AacSequenceHeader;
                var header        = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Audio);
                var messageHeader = new RtmpMessageHeader(0, payload.Length, RtmpMessageType.Audio, stream.Id);
                var message       = new RtmpMessage(header, messageHeader, payload);
                await _session.SendMessageAsync(message);
            }

            if (publisher.VideoCodec?.CodecId == RtmpConstants.H264Video || publisher.VideoCodec?.CodecId == RtmpConstants.H265Video)
            {
                var payload       = publisher.VideoCodec.AvcSequenceHeader;
                var header        = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Video);
                var messageHeader = new RtmpMessageHeader(0, payload.Length, RtmpMessageType.Video, stream.Id);
                var message       = new RtmpMessage(header, messageHeader, payload);
                await _session.SendMessageAsync(message);
            }

            if (publisher.RtmpGopCacheQueue != null)
            {
                foreach (var message in publisher.RtmpGopCacheQueue)
                {
                    var modifiedMessage = new RtmpMessage(
                        message.Header,
                        message.Message.Translate(stream.Id),
                        message.Payload);
                    await _session.SendMessageAsync(modifiedMessage);
                }
            }

            _logger.LogInformation($@"[rtmp play] Join stream. id ={_session.Id} streamPath ={stream.Path} streamId ={stream.Id} ");
        }
        private async Task HandleSetDataFrame(AmfDataMessage command)
        {
            _logger.LogInformation("Metadata found {0}", command.AdditionalArguments);
            var dataObj  = (dynamic)command.AdditionalArguments[1];
            var metadata = new AmfDataMessage
            {
                Name = "onMetaData",
                AdditionalArguments =
                {
                    dataObj
                }
            };

            _session.MetaData = new byte[metadata.GetLength()];
            metadata.Encode(_session.MetaData.Span);
            if (((IDictionary <string, object>)dataObj).ContainsKey("audiocodecid"))
            {
                _session.AudioCodec = new AudioCodecInfo
                {
                    CodecId    = dataObj.audiocodecid is double?(int)dataObj.audiocodecid : 0,
                    Channels   = dataObj.stereo ? 2                                       : 1,
                    Samplerate = (int)dataObj.audiosamplerate,
                    Bitrate    = (int)dataObj.audiodatarate
                };
            }
            if (((IDictionary <string, object>)dataObj).ContainsKey("videocodecid"))
            {
                _session.VideoCodec = new VideoCodecInfo
                {
                    CodecId   = dataObj.videocodecid is double?(int)dataObj.videocodecid : 0,
                    Width     = (int)dataObj.width,
                    Height    = (int)dataObj.height,
                    Framerate = (int)dataObj.framerate,
                    Bitrate   = (int)dataObj.videodatarate
                };
            }

            var header         = new RtmpChunkHeader(RtmpChunkHeaderType.Type0, RtmpConstants.RtmpChannel_Data);
            var messsageHeader = new RtmpMessageHeader(0, _session.MetaData.Length, RtmpMessageType.DataAMF0, 0);
            var message        = new RtmpMessage(header, messsageHeader, _session.MetaData);
            await _session.SendToAllPalyersAsync(message);
        }
        public async Task ProcessMessageAsync(RtmpMessage message)
        {
            var command = new AmfDataMessage();

            command.Decode(message.Payload.Span);
            switch (command.Name)
            {
            case "@setDataFrame":
                await HandleSetDataFrame(command);

                break;

            case "onMetaData":
                await HandleMetadtataAsync(message, command);

                break;

            default:
                throw new InvalidOperationException($"Unknown command {command.Name} {command.AdditionalArguments} ");
            }
        }
        public async Task SendToAllPalyersAsync(RtmpMessage message)
        {
            var players = _context.GetPlayers(PublishStream.Path);

            foreach (var player in players)
            {
                var stream        = player.PlayStream;
                var header        = message.Header;
                var messageHeader = message.Message.Translate(stream.Id);
                var playerMessage = new RtmpMessage(header, messageHeader, message.Payload);
                try
                {
                    await player.SendMessageAsync(playerMessage);
                }
                catch (Exception ex)
                {
                    //remove failing player.
                    _logger.LogError(ex, $"Failed to send to player {player.Id}");
                    _context.RemovePlayer(PublishStream.Path, player.Id);
                }
            }
        }
Exemple #21
0
        private void ProcessVideo(RtmpMessage message)
        {
            var payload    = message.Payload.Span;
            var frame_type = (payload[0] >> 4) & 0x0f;
            var codec_id   = payload[0] & 0x0f;
            var video      = _session.VideoCodec;

            if (video == null)
            {
                video = new VideoCodecInfo
                {
                    CodecId = codec_id,
                };
                _logger.LogInformation(
                    $@"[rtmp publish] Handle video. id ={_session.Id}
                streamPath ={_session.PublishStream.Path}
                frame_type ={frame_type}
                codec_id ={codec_id}
                codec_name ={video.CodecName} {video.Width} x {video.Height}");
            }


            if (codec_id == RtmpConstants.H264Video || codec_id == RtmpConstants.H265Video)
            {
                //cache avc sequence header
                if (frame_type == 1 && payload[1] == 0)
                {
                    video.AvcSequenceHeader = new byte[payload.Length];
                    payload.CopyTo(video.AvcSequenceHeader.Span);
                    dynamic info = AVUtil.ReadAVCSpecificConfig(payload);
                    video.Width       = info.width;
                    video.Height      = info.height;
                    video.ProfileName = AVUtil.GetAVCProfileName(info);
                    video.Level       = info.level;
                }
            }
        }
        private async Task HandlePlayAsync(RtmpMessage message, AmfCommandMessage command)
        {
            var streamName = command.AdditionalArguments[0] as string;
            var streamPath = $"/{_application}/{streamName.Split("?")[0]}";
            var streamId   = message.Message.StreamId;
            var stream     = new RtmpStream(streamId, streamName, streamPath);

            if (_session.IsPlaying)
            {
                _logger.LogInformation($@"[rtmp play] NetConnection is playing.id ={_session.Id}
                streamPath ={streamPath}
                streamId ={streamId} ");
                await SendStatusMessageAsync(streamId, "error", "NetStream.Play.BadConnection", "Connection already playing");

                return;
            }
            else
            {
                _session.PlayStream = stream;
                await RespondPlayAsync(stream);
            }

            if (_context.TryGetPublishser(streamPath, out var publisher))
            {
                await OnStartPlayAsync(publisher, stream);

                _context.AddPlayer(streamPath, _session.Id);
            }
            else
            {
                _logger.LogInformation($@"[rtmp play] Stream not found.id ={_session.Id}
                streamPath ={streamPath}
                streamId ={streamId}");
                _context.AddIdlePlayer(streamPath, _session.Id);
            }
        }
 protected abstract Task ProcessCommandAsync(AmfCommandMessage command, RtmpMessage message);
Exemple #24
0
 public async Task ProcessMessageAsync(RtmpMessage message)
 {
     await _channel.Writer.WriteAsync(message);
 }
 protected abstract Task ProcessMediaMessageAsync(RtmpMessage message);
 protected abstract Task ProcessCommandMessageAsync(RtmpMessage message);
 protected virtual async Task ProcessUserControlMessageAsync(RtmpMessage message)
 {
     await _eventProcessor.ProcessMessageAsync(message);
 }
 protected virtual async Task ProcessProtocolMessageAsync(RtmpMessage message)
 {
     await _controlMessageProcessor.ProcessMessageAsync(message);
 }
Exemple #29
0
 protected virtual Task HandleDataCommandAsync(AmfDataMessage command, RtmpMessage message)
 {
     throw new NotImplementedException($"Do not know how to process {command.Name}");
 }
 /// <summary>
 /// Send a mesage to the other side of this session.
 /// </summary>
 /// <param name="message">The Rtmp message to send.</param>
 /// <returns></returns>
 public virtual async Task SendMessageAsync(RtmpMessage message)
 {
     await _writer.ProcessMessageAsync(message);
 }