예제 #1
0
        /// <summary>
        /// Sets provided configuration to appropriate stream.
        /// </summary>
        /// <param name="config">StreamConfig</param>
        public Task SetStreamConfiguration(StreamConfig config)
        {
            var streamType = config.StreamType();

            logger.Info($"{streamType}: {config.GetType()}");

            if (config is BufferStreamConfig metaData)
            {
                // Use video for buffer depth control.
                if (streamType == StreamType.Video)
                {
                    _dataClock.BufferLimit = metaData.BufferDuration;
                }

                return(Task.CompletedTask);
            }

            if (esStreams[(int)streamType] == null)
            {
                logger.Warn($"Uninitialized stream {streamType}");
                return(Task.CompletedTask);
            }

            if (esStreams[(int)streamType].HaveConfiguration)
            {
                if (!esStreams[(int)streamType].Configuration.IsCompatible(config))
                {
                    esStreams[(int)streamType].Configuration = config;
                    return(Task.CompletedTask);
                }
                logger.Info($"{streamType}: Queuing configuration");
                return(AppendPacket(BufferConfigurationPacket.Create(config)));
            }

            if (_configurationsCollected == null)
            {
                esStreams[(int)streamType].SetStreamConfiguration(config);
            }
            else
            {
                esStreams[(int)streamType].Configuration = config;
                if (AllStreamsHaveConfiguration)
                {
                    _configurationsCollected.TrySetResult(null);
                }

                return(Task.CompletedTask);
            }

            // Check if all initialized streams have configuration &
            // can be started
            if (!AllStreamsHaveConfiguration)
            {
                logger.Info($"Needed config: Video {esStreams[(int)StreamType.Video].Configuration == null} Audio {esStreams[(int)StreamType.Audio].Configuration == null}");
                return(Task.CompletedTask);
            }

            return(PreparePlayback(activeTaskCts.Token));
        }
예제 #2
0
        public void SetStreamConfig(StreamConfig config)
        {
            logger.Info(config.StreamType().ToString());

            var configPacket = BufferConfigurationPacket.Create(config);

            streamControl.SetStreamConfiguration(configPacket);
        }
        /// <summary>
        /// Sets provided configuration to appropriate stream.
        /// </summary>
        /// <param name="config">StreamConfig</param>
        public void SetStreamConfiguration(BufferConfigurationPacket config)
        {
            var streamType = config.StreamType;

            logger.Info($"{streamType}:");

            try
            {
                if (config.Config is MetaDataStreamConfig metaData)
                {
                    bufferController.SetMetaDataConfiguration(metaData);
                    return;
                }

                var pushResult = esStreams[(int)streamType].SetStreamConfig(config);

                // Configuration queued. Do not prepare stream :)
                if (pushResult == EsStream.SetStreamConfigResult.ConfigQueued)
                {
                    return;
                }

                // Check if all initialized streams are configured
                if (!AllStreamsConfigured)
                {
                    return;
                }

                var token = activeTaskCts.Token;
                bufferController.ResetBuffers();
                StreamPrepare(token);
            }
            catch (NullReferenceException)
            {
                // packetQueue can hold ALL StreamTypes, but not all of them
                // have to be supported.
                logger.Warn($"Uninitialized Stream Type {streamType}");
            }
            catch (OperationCanceledException)
            {
                logger.Info($"{streamType}: Operation Cancelled");
            }
            catch (ObjectDisposedException)
            {
                logger.Info($"{streamType}: Operation Cancelled and disposed");
            }
            catch (InvalidOperationException)
            {
                // Queue has been marked as completed
                logger.Warn($"Data queue terminated for stream: {streamType}");
            }
            catch (UnsupportedStreamException use)
            {
                logger.Error(use, $"{streamType}");
                OnEsStreamError(use.Message);
            }
        }
예제 #4
0
        public static BufferConfigurationPacket Create(StreamConfig config)
        {
            var result = new BufferConfigurationPacket()
            {
                Config = config,
                StreamType = config.StreamType(),
                Pts = TimeSpan.MinValue
            };

            return result;
        }
예제 #5
0
        /// <summary>
        /// Sets provided configuration to appropriate stream.
        /// </summary>
        /// <param name="config">StreamConfig</param>
        public async Task SetStreamConfiguration(BufferConfigurationPacket config)
        {
            var streamType = config.StreamType;

            logger.Info($"{streamType}:");

            try
            {
                if (config.Config is BufferStreamConfig metaData)
                {
                    await _dataClock.UpdateBufferDepth(metaData.StreamType(), metaData.BufferDuration);
                }

                var pushResult = esStreams[(int)streamType].SetStreamConfiguration(config);

                if (pushResult == EsStream.SetStreamConfigResult.QueueConfiguration)
                {
                    AppendPacket(config);
                    return;
                }

                esStreams[(int)streamType].PushStreamConfiguration();

                // Check if all initialized streams are configured
                if (!AllStreamsConfigured)
                {
                    return;
                }

                var token = activeTaskCts.Token;
                await StreamPrepare(token);
            }
            catch (NullReferenceException)
            {
                // packetQueue can hold ALL StreamTypes, but not all of them
                // have to be supported.
                logger.Warn($"Uninitialized Stream Type {streamType}");
            }
            catch (ObjectDisposedException)
            {
                logger.Info($"{streamType}: Operation Cancelled and disposed");
            }
            catch (InvalidOperationException)
            {
                // Queue has been marked as completed
                logger.Warn($"Data queue terminated for stream: {streamType}");
            }
            catch (UnsupportedStreamException use)
            {
                logger.Error(use, $"{streamType}");
                OnEsStreamError(use.Message);
            }
        }
예제 #6
0
        /// <summary>
        /// Seek Task. Performs seek to specified seekId, followed by seek to specified
        /// seek position
        /// </summary>
        /// <param name="seekId">Seek ID to seek to</param>
        /// <param name="seekPosition">seek position to seek to</param>
        /// <param name="token">cancel token</param>
        /// <returns></returns>
        private SeekResult SeekTask(uint seekId, TimeSpan seekPosition, CancellationToken token)
        {
            logger.Info($"{streamType}: {seekId}");

            while (true)
            {
                try
                {
                    var packet = packetStorage.GetPacket(streamType, token);

                    switch (packet)
                    {
                    case BufferConfigurationPacket bufferConfigPacket:
                        var isCompatible = CurrentConfig.Compatible(bufferConfigPacket);
                        CurrentConfig = bufferConfigPacket;
                        if (CurrentConfig.StreamType == StreamType.Audio && !isCompatible)
                        {
                            return(SeekResult.RestartRequired);
                        }
                        break;

                    case SeekPacket seekPacket:
                        if (seekPacket.SeekId != seekId)
                        {
                            break;
                        }

                        logger.Info($"{streamType}: Seek Id {seekId} found. Looking for time {seekPosition}");
                        return(SeekResult.Ok);

                    default:
                        packet.Dispose();
                        break;
                    }
                }
                catch (InvalidOperationException)
                {
                    logger.Warn($"{streamType}: Stream completed");
                    return(SeekResult.Ok);
                }
                catch (OperationCanceledException)
                {
                    logger.Warn($"{streamType}: Seek cancelled");
                    return(SeekResult.Ok);
                }
                catch (Exception e)
                {
                    logger.Error(e, $"{streamType}");
                    throw;
                }
            }
        }
예제 #7
0
        /// <summary>
        /// Sets Stream configuration
        /// Non configured stream - stream config will be pushed directly to ES Player.
        /// Configured stream - stream config will be enqueue in packet storage
        /// and processed once retrieved.
        /// </summary>
        /// <param name="bufferConfig">BufferConfigurationPacket</param>
        /// <returns>SetStreamConfigResult</returns>
        public SetStreamConfigResult SetStreamConfiguration(BufferConfigurationPacket bufferConfig)
        {
            logger.Info($"{streamType}");

            LastQueuedConfig = bufferConfig;

            if (!IsConfigured)
            {
                return(SetStreamConfigResult.SetConfiguration);
            }

            logger.Info($"{streamType}: New configuration needs queuing");
            return(SetStreamConfigResult.QueueConfiguration);
        }
예제 #8
0
        public bool Compatible(BufferConfigurationPacket packet)
        {
            switch (packet.Config)
            {
                case AudioStreamConfig audioConfig:
                    return (Config as AudioStreamConfig)?.Compatible(audioConfig) ?? false;

                case VideoStreamConfig videoConfig:
                    return (Config as VideoStreamConfig)?.Compatible(videoConfig) ?? false;

                default:
                    return false;
            }
        }
예제 #9
0
        /// <summary>
        /// Sets Stream configuration
        /// Non configured stream - stream config will be pushed directly to ES Player.
        /// Configured stream - stream config will be enqueue in packet storage
        /// and processed once retrieved.
        /// </summary>
        /// <param name="bufferConfig">BufferConfigurationPacket</param>
        /// <returns>SetStreamConfigResult</returns>
        public SetStreamConfigResult SetStreamConfig(BufferConfigurationPacket bufferConfig)
        {
            // Depending on current configuration state, packets are either pushed
            // directly to player or queued in packet queue.
            // To make sure current state is known, sync this operation.
            //
            logger.Info($"{streamType}: Already Configured: {IsConfigured}");

            if (IsConfigured)
            {
                packetStorage.AddPacket(bufferConfig);
                logger.Info($"{streamType}: New configuration queued");
                return(SetStreamConfigResult.ConfigQueued);
            }

            CurrentConfig = bufferConfig;
            PushStreamConfig(CurrentConfig.Config);
            return(SetStreamConfigResult.ConfigPushed);
        }
예제 #10
0
        private async Task <bool> ProcessPacket(Packet packet, CancellationToken transferToken)
        {
            var continueProcessing = true;

            switch (packet)
            {
            case EOSPacket eosPacket:
                PushEosPacket(eosPacket, transferToken);
                continueProcessing = false;
                break;

            case BufferConfigurationPacket bufferConfigPacket:
                CurrentConfig = bufferConfigPacket;

                if (CurrentConfig.StreamType == StreamType.Audio && !CurrentConfig.Compatible(bufferConfigPacket))
                {
                    logger.Warn($"{streamType}: Incompatible Stream config change.");
                    streamReconfigureSubject.OnNext(Unit.Default);

                    // exit transfer task. This will prevent further transfers
                    // Stops/Restarts will be called by reconfiguration handler.
                    continueProcessing = false;
                }

                break;

            case EncryptedPacket encryptedPacket:
                await PushEncryptedPacket(encryptedPacket, transferToken);

                break;

            case Packet dataPacket:
                PushUnencryptedPacket(dataPacket, transferToken);
                break;

            default:
                throw new ArgumentException($"{streamType}: Unsupported packet type {packet.GetType()}");
            }

            return(continueProcessing);
        }
예제 #11
0
        /// <summary>
        /// Sets provided configuration to appropriate stream.
        /// </summary>
        /// <param name="config">StreamConfig</param>
        public async Task SetStreamConfiguration(StreamConfig config)
        {
            var streamType = config.StreamType();

            logger.Info($"{streamType}: {config.GetType()}");

            try
            {
                if (config is BufferStreamConfig metaData)
                {
                    // Use video for buffer depth control.
                    if (streamType == StreamType.Video)
                    {
                        _dataClock.UpdateBufferDepth(metaData.BufferDuration);
                    }
                    return;
                }

                if (esStreams[(int)streamType].IsConfigured)
                {
                    logger.Info($"{streamType}: Queuing configuration");
                    AppendPacket(BufferConfigurationPacket.Create(config));
                    return;
                }

                // Don't push config yet. Just store it. Configs may arrive
                // after player gets disowned. Configs should not be pushed, but
                // configuration is needed in order to restore player configuration.
                esStreams[(int)streamType].StoreStreamConfiguration(config);

                // Check if all initialized streams have configuration &
                // can be started
                if (!AllStreamsHaveConfiguration || activeTaskCts.IsCancellationRequested)
                {
                    return;
                }

                SetPlayerConfiguration();
                await PreparePlayback(activeTaskCts.Token);

                SetState(PlayerState.Prepared);
            }
            catch (OperationCanceledException)
            {
                logger.Info("Operation cancelled");
            }
            catch (NullReferenceException)
            {
                // packetQueue can hold ALL StreamTypes, but not all of them
                // have to be supported.
                logger.Warn($"Uninitialized Stream Type {streamType}");
            }
            catch (ObjectDisposedException)
            {
                logger.Info($"{streamType}: Operation Cancelled and disposed");
            }
            catch (InvalidOperationException)
            {
                // Queue has been marked as completed
                logger.Warn($"Data queue terminated for stream: {streamType}");
            }
            catch (UnsupportedStreamException use)
            {
                logger.Error(use, $"{streamType}");
                OnEsStreamError(use.Message);
            }
        }