Пример #1
0
        public async Task Seek([FromOptionalArgument] double milliSeconds)
        {
            var resetData = new AmfObject
            {
                { "level", "status" },
                { "code", "NetStream.Seek.Notify" },
                { "description", "Seeking stream." },
                { "details", "seek" }
            };
            var resetStatus = RtmpSession.CreateCommandMessage <OnStatusCommandMessage>();

            resetStatus.InfoObject = resetData;
            await MessageStream.SendMessageAsync(ChunkStream, resetStatus);

            _playCts?.Cancel();
            while (_playing == 1)
            {
                await Task.Yield();
            }

            var cts = new CancellationTokenSource();

            _playCts?.Dispose();
            _playCts = cts;
            await SeekAndPlay(milliSeconds, cts.Token);
        }
Пример #2
0
        public void Publish([FromOptionalArgument] string publishingName, [FromOptionalArgument] string publishingType)
        {
            if (string.IsNullOrEmpty(publishingName))
            {
                throw new InvalidOperationException("empty publishing name");
            }
            if (!PublishingHelpers.IsTypeSupported(publishingType))
            {
                throw new InvalidOperationException($"not supported publishing type {publishingType}");
            }

            _publishingType = PublishingHelpers.PublishingTypes[publishingType];

            _publisherSessionService.RegisterPublisher(publishingName, this);

            RtmpSession.SendControlMessageAsync(new StreamBeginMessage()
            {
                StreamID = MessageStream.MessageStreamId
            });
            var onStatus = RtmpSession.CreateCommandMessage <OnStatusCommandMessage>();

            MessageStream.RegisterMessageHandler <DataMessage>(HandleDataMessage);
            MessageStream.RegisterMessageHandler <AudioMessage>(HandleAudioMessage);
            MessageStream.RegisterMessageHandler <VideoMessage>(HandleVideoMessage);
            onStatus.InfoObject = new AmfObject
            {
                { "level", "status" },
                { "code", "NetStream.Publish.Start" },
                { "description", "Stream is now published." },
                { "details", publishingName }
            };
            MessageStream.SendMessageAsync(ChunkStream, onStatus);
        }
Пример #3
0
 private void HandleData(DataMessage message)
 {
     try
     {
         _metaData = message;
         _metaData.Data.RemoveAt(0);
     }
     catch
     {
         RtmpSession.Close();
     }
 }
Пример #4
0
        private async void HandleAudioMessage(AudioMessage message)
        {
            try
            {
                _currentTimestamp = Math.Max(_currentTimestamp, message.MessageHeader.Timestamp);

                await SaveMessage(message);
            }
            catch
            {
                RtmpSession.Close();
            }
        }
Пример #5
0
        public async Task Play(
            [FromOptionalArgument] string streamName,
            [FromOptionalArgument] double start    = -1,
            [FromOptionalArgument] double duration = -1,
            [FromOptionalArgument] bool reset      = false)
        {
            _recordFile = new FileStream(_recordService.GetRecordFilename(streamName) + ".flv", FileMode.Open, FileAccess.Read);
            await FlvDemuxer.AttachStream(_recordFile);

            var resetData = new AmfObject
            {
                { "level", "status" },
                { "code", "NetStream.Play.Reset" },
                { "description", "Resetting and playing stream." },
                { "details", streamName }
            };
            var resetStatus = RtmpSession.CreateCommandMessage <OnStatusCommandMessage>();

            resetStatus.InfoObject = resetData;
            await MessageStream.SendMessageAsync(ChunkStream, resetStatus);

            var startData = new AmfObject
            {
                { "level", "status" },
                { "code", "NetStream.Play.Start" },
                { "description", "Started playing." },
                { "details", streamName }
            };

            var startStatus = RtmpSession.CreateCommandMessage <OnStatusCommandMessage>();

            startStatus.InfoObject = startData;
            await MessageStream.SendMessageAsync(ChunkStream, startStatus);

            var bandwidthLimit = new WindowAcknowledgementSizeMessage()
            {
                WindowSize = 500 * 1024
            };
            await RtmpSession.ControlMessageStream.SendMessageAsync(RtmpSession.ControlChunkStream, bandwidthLimit);

            VideoChunkStream = RtmpSession.CreateChunkStream();
            AudioChunkStream = RtmpSession.CreateChunkStream();

            var cts = new CancellationTokenSource();

            _playCts?.Dispose();
            _playCts = cts;
            start    = Math.Max(start, 0);
            await SeekAndPlay(start / 1000, cts.Token);
        }
Пример #6
0
        private async void SendVideo(VideoMessage message)
        {
            var video = message.Clone() as VideoMessage;

            try
            {
                await MessageStream.SendMessageAsync(_videoChunkStream, video);
            }
            catch
            {
                foreach (var a in _cleanupActions)
                {
                    a();
                }
                RtmpSession.Close();
            }
        }
Пример #7
0
        private async void SendAudio(AudioMessage message)
        {
            var audio = message.Clone();

            try
            {
                await MessageStream.SendMessageAsync(_audioChunkStream, audio as AudioMessage);
            }
            catch
            {
                foreach (var a in _cleanupActions)
                {
                    a();
                }
                RtmpSession.Close();
            }
        }
Пример #8
0
        public async Task Publish([FromOptionalArgument] string streamName, [FromOptionalArgument] string publishingType)
        {
            if (string.IsNullOrEmpty(streamName))
            {
                throw new InvalidOperationException("empty publishing name");
            }
            if (!PublishingHelpers.IsTypeSupported(publishingType))
            {
                throw new InvalidOperationException($"not supported publishing type {publishingType}");
            }

            _publishingType = PublishingHelpers.PublishingTypes[publishingType];

            await RtmpSession.SendControlMessageAsync(new StreamIsRecordedMessage()
            {
                StreamID = MessageStream.MessageStreamId
            });

            await RtmpSession.SendControlMessageAsync(new StreamBeginMessage()
            {
                StreamID = MessageStream.MessageStreamId
            });

            var onStatus = RtmpSession.CreateCommandMessage <OnStatusCommandMessage>();

            MessageStream.RegisterMessageHandler <DataMessage>(HandleData);
            MessageStream.RegisterMessageHandler <AudioMessage>(HandleAudioMessage);
            MessageStream.RegisterMessageHandler <VideoMessage>(HandleVideoMessage);
            MessageStream.RegisterMessageHandler <UserControlMessage>(HandleUserControlMessage);
            onStatus.InfoObject = new AmfObject
            {
                { "level", "status" },
                { "code", "NetStream.Publish.Start" },
                { "description", "Stream is now published." },
                { "details", streamName }
            };
            await MessageStream.SendMessageAsync(ChunkStream, onStatus);

            _recordFileData = new FileStream(_recordService.GetRecordFilename(streamName) + ".data", FileMode.OpenOrCreate);
            _recordFileData.SetLength(0);
            _keyframes             = new AmfObject();
            _keyframeTimes         = new List <object>();
            _keyframeFilePositions = new List <object>();
            _keyframes.Add("times", _keyframeTimes);
            _keyframes.Add("filepositions", _keyframeFilePositions);
        }
Пример #9
0
        private async void HandleVideoMessage(VideoMessage message)
        {
            try
            {
                _currentTimestamp = Math.Max(_currentTimestamp, message.MessageHeader.Timestamp);

                var head = message.Data.Span[0];

                var data = FlvDemuxer.DemultiplexVideoData(message);
                if (data.FrameType == FrameType.KeyFrame)
                {
                    _keyframeTimes.Add((double)message.MessageHeader.Timestamp / 1000);
                    _keyframeFilePositions.Add((double)_recordFileData.Position);
                }

                await SaveMessage(message);
            }
            catch
            {
                RtmpSession.Close();
            }
        }
Пример #10
0
 public void DeleteStream([FromOptionalArgument] double streamId)
 {
     RtmpSession.DeleteNetStream((uint)streamId);
 }
Пример #11
0
        public async Task Play(
            [FromOptionalArgument] string streamName,
            [FromOptionalArgument] double start    = -1,
            [FromOptionalArgument] double duration = -1,
            [FromOptionalArgument] bool reset      = false)
        {
            var publisher = _publisherSessionService.FindPublisher(streamName);

            if (publisher == null)
            {
                throw new KeyNotFoundException();
            }
            var resetData = new AmfObject
            {
                { "level", "status" },
                { "code", "NetStream.Play.Reset" },
                { "description", "Resetting and playing stream." },
                { "details", streamName }
            };
            var resetStatus = RtmpSession.CreateCommandMessage <OnStatusCommandMessage>();

            resetStatus.InfoObject = resetData;
            await MessageStream.SendMessageAsync(ChunkStream, resetStatus);

            var startData = new AmfObject
            {
                { "level", "status" },
                { "code", "NetStream.Play.Start" },
                { "description", "Started playing." },
                { "details", streamName }
            };
            var startStatus = RtmpSession.CreateCommandMessage <OnStatusCommandMessage>();

            startStatus.InfoObject = startData;
            await MessageStream.SendMessageAsync(ChunkStream, startStatus);

            var flvMetadata = RtmpSession.CreateData <DataMessage>();

            flvMetadata.MessageHeader = (MessageHeader)publisher.FlvMetadata.MessageHeader.Clone();
            flvMetadata.Data          = publisher.FlvMetadata.Data;
            await MessageStream.SendMessageAsync(ChunkStream, flvMetadata);

            _videoChunkStream = RtmpSession.CreateChunkStream();
            _audioChunkStream = RtmpSession.CreateChunkStream();

            if (publisher.AACConfigureRecord != null)
            {
                await MessageStream.SendMessageAsync(_audioChunkStream, publisher.AACConfigureRecord.Clone() as AudioMessage);
            }
            if (publisher.AVCConfigureRecord != null)
            {
                await MessageStream.SendMessageAsync(_videoChunkStream, publisher.AVCConfigureRecord.Clone() as VideoMessage);
            }

            publisher.OnAudioMessage += SendAudio;
            publisher.OnVideoMessage += SendVideo;
            _cleanupActions.Add(() =>
            {
                publisher.OnVideoMessage -= SendVideo;
                publisher.OnAudioMessage -= SendAudio;
            });
        }
Пример #12
0
        public uint CreateStream()
        {
            var stream = RtmpSession.CreateNetStream <LivingStream>();

            return(stream.MessageStream.MessageStreamId);
        }