/// <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)); }
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); } }
public static BufferConfigurationPacket Create(StreamConfig config) { var result = new BufferConfigurationPacket() { Config = config, StreamType = config.StreamType(), Pts = TimeSpan.MinValue }; return result; }
/// <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); } }
/// <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; } } }
/// <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); }
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; } }
/// <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); }
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); }
/// <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); } }