/// <summary> /// Performs the actions that this command implements. /// </summary> internal override void ExecuteInternal() { var m = Manager.MediaElement; m.Clock.Reset(); var pause = new PauseCommand(Manager); pause.ExecuteInternal(); var seek = new SeekCommand(Manager, TimeSpan.Zero); seek.ExecuteInternal(); foreach (var renderer in m.Renderers.Values) renderer.Stop(); m.MediaState = CoreMediaState.Stop; }
/// <summary> /// Performs the actions that this command implements. /// </summary> internal override void ExecuteInternal() { var m = Manager.MediaCore; m.Clock.Reset(); m.MediaState = MediaEngineState.Stop; var seek = new SeekCommand(Manager, TimeSpan.Zero); seek.ExecuteInternal(); foreach (var renderer in m.Renderers.Values) { renderer.Stop(); } }
/// <summary> /// Performs the actions that this command implements. /// </summary> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> internal override async Task ExecuteInternal() { var m = Manager.MediaCore; m.Clock.Reset(); m.State.UpdateMediaState(PlaybackStatus.Manual); var seek = new SeekCommand(Manager, TimeSpan.Zero); await seek.ExecuteInternal(); m.State.UpdateMediaState(PlaybackStatus.Stop, m.WallClock); foreach (var renderer in m.Renderers.Values) { renderer.Stop(); } }
/// <summary> /// Performs the actions that this command implements. /// </summary> internal override void Execute() { var m = Manager.MediaElement; m.Clock.Reset(); var seek = new SeekCommand(this.Manager, TimeSpan.Zero); seek.Execute(); foreach (var renderer in m.Renderers.Values) { renderer.Stop(); } m.MediaState = System.Windows.Controls.MediaState.Stop; }
/// <summary> /// Performs the actions represented by this deferred task. /// </summary> protected override void PerformActions() { var m = MediaCore; m.Clock.Reset(); m.State.UpdateMediaState(PlaybackStatus.Manual); var seek = new SeekCommand(m, TimeSpan.Zero); seek.Execute(); m.State.UpdateMediaState(PlaybackStatus.Stop, m.WallClock); foreach (var renderer in m.Renderers.Values) { renderer.Stop(); } }
/// <summary> /// Seeks to the specified position within the media. /// This command is a queued command /// </summary> /// <param name="position">The position.</param> public void Seek(TimeSpan position) { SeekCommand command = null; lock (SyncLock) { command = Commands.LastOrDefault(c => c.CommandType == MediaCommandType.Seek) as SeekCommand; if (command == null || command.IsRunning) { command = new SeekCommand(this, position); EnqueueCommand(command); } else { command.TargetPosition = position; } } }
/// <summary> /// Seeks to the specified position within the media. /// </summary> /// <param name="position">The position.</param> /// <returns></returns> public async Task Seek(TimeSpan position) { SeekCommand command = null; lock (SyncLock) { command = Commands.FirstOrDefault(c => c.CommandType == MediaCommandType.Seek) as SeekCommand; if (command == null) { command = new SeekCommand(this, position); Commands.Add(command); } else { command.TargetPosition = position; } } await command.Promise; }
/// <summary> /// Performs the actions represented by this deferred task. /// </summary> protected override void PerformActions() { var m = MediaCore; m.Log(MediaLogMessageType.Debug, $"Command {CommandType}: Entered"); try { m.State.UpdateMediaState(PlaybackStatus.Manual); // Signal the start of a sync-buffering scenario m.Clock.Pause(); // Wait for the cycles to complete var workerEvents = new IWaitEvent[] { m.BlockRenderingCycle, m.PacketReadingCycle }; foreach (var workerEvent in workerEvents) { workerEvent.Wait(); } // Signal a change so the user get the chance to update // selected streams and options m.SendOnMediaChanging(); // Side load subtitles m.PreloadSubtitles(); // Capture the current media types before components change var oldMediaTypes = m.Container.Components.MediaTypes.ToArray(); // Recreate selected streams as media components var mediaTypes = m.Container.UpdateComponents(); m.State.UpdateFixedContainerProperties(); // find all existing component blocks and renderers that no longer exist // We always remove the audio component in case there is a change in audio device var removableMediaTypes = oldMediaTypes .Where(t => mediaTypes.Contains(t) == false) .Union(new[] { MediaType.Audio }) .Distinct() .ToArray(); // find all existing component blocks and renderers that no longer exist foreach (var t in removableMediaTypes) { // Remove the renderer for the component if (m.Renderers.ContainsKey(t)) { m.Renderers[t].Close(); m.Renderers.Remove(t); } // Remove the block buffer for the component if (m.Blocks.ContainsKey(t)) { m.Blocks[t]?.Dispose(); m.Blocks.Remove(t); } } // Create the block buffers and renderers as necessary foreach (var t in mediaTypes) { if (m.Blocks.ContainsKey(t) == false) { m.Blocks[t] = new MediaBlockBuffer(Constants.MaxBlocks[t], t); } if (m.Renderers.ContainsKey(t) == false) { m.Renderers[t] = MediaEngine.Platform.CreateRenderer(t, m); } m.Blocks[t].Clear(); m.Renderers[t].WaitForReadyState(); } // Depending on whether or not the media is seekable // perform either a seek operation or a quick buffering operation. if (m.State.IsSeekable) { // Let's simply do an automated seek var seekCommand = new SeekCommand(m, m.WallClock); seekCommand.Execute(); return; } else { // Let's perform quick-buffering m.Container.Components.RunQuickBuffering(m); // Mark the renderers as invalidated foreach (var t in mediaTypes) { m.InvalidateRenderer(t); } } } catch (Exception ex) { ErrorException = ex; } }
/// <summary> /// Provides the implementation of the command /// </summary> /// <returns>The awaitable task.</returns> internal override async Task ExecuteInternal() { var m = Manager.MediaCore; // Avoid running the command if run conditions are not met if (m == null || m.IsDisposed || m.State.IsOpen == false || m.State.IsOpening) { return; } var resumeClock = false; var isSeeking = m.State.IsSeeking; try { // Signal the start of a changing event m.MediaChangingDone.Begin(); m.State.IsSeeking = true; // Signal the start of a sync-buffering scenario m.HasDecoderSeeked = true; resumeClock = m.Clock.IsRunning; m.Clock.Pause(); // Wait for the cycles to complete var workerEvents = new IWaitEvent[] { m.BlockRenderingCycle, m.PacketReadingCycle }; foreach (var workerEvent in workerEvents) { workerEvent.Wait(); } // Send the changing event to the connector var beforeComponentTypes = m.Container.Components.MediaTypes; await m.SendOnMediaChanging(); m.Container.UpdateComponents(); var afterComponentTypes = m.Container.Components.MediaTypes; var disposableComponentTypes = beforeComponentTypes .Where(c => afterComponentTypes.Contains(c) == false) .ToArray(); // Remove components that are no longer needed foreach (var t in disposableComponentTypes) { if (m.Renderers.ContainsKey(t)) { m.Renderers[t].Close(); m.Renderers.Remove(t); } if (m.Blocks.ContainsKey(t)) { m.Blocks[t]?.Dispose(); m.Blocks.Remove(t); } } // Create the block buffers and renderers as necessary // TODO: For smoother transition, only invalidate/change the components // that actually changed. foreach (var t in afterComponentTypes) { if (m.Blocks.ContainsKey(t) == false) { m.Blocks[t] = new MediaBlockBuffer(Constants.MaxBlocks[t], t); } if (m.Renderers.ContainsKey(t) == false) { m.Renderers[t] = MediaEngine.Platform.CreateRenderer(t, m); } m.Blocks[t].Clear(); m.Renderers[t].WaitForReadyState(); } if (m.State.IsSeekable) { var seekCommand = new SeekCommand(Manager, m.WallClock); seekCommand.RunSynchronously(); } else { foreach (var t in afterComponentTypes) { m.InvalidateRenderer(t); } } } catch { // TODO: Handle errors here } finally { if (resumeClock) { m?.Clock?.Play(); } m.State.IsSeeking = isSeeking; m.MediaChangingDone.Complete(); } }
/// <summary> /// Provides the implementation of the command /// </summary> /// <returns> /// The awaitable task. /// </returns> internal override async Task ExecuteInternal() { var m = Manager.MediaCore; // Avoid running the command if run conditions are not met if (m == null || m.IsDisposed || m.State.IsOpen == false || m.State.IsOpening || m.State.IsChanging) { return; } m.State.IsChanging = true; var resumeClock = false; var isSeeking = m.State.IsSeeking; try { // Signal the start of a changing event m.MediaChangingDone.Begin(); m.State.IsSeeking = true; // Signal the start of a sync-buffering scenario m.HasDecoderSeeked = true; resumeClock = m.Clock.IsRunning; m.Clock.Pause(); // Wait for the cycles to complete var workerEvents = new IWaitEvent[] { m.BlockRenderingCycle, m.PacketReadingCycle }; foreach (var workerEvent in workerEvents) { workerEvent.Wait(); } // Signal a change so the user get the chance to update // selected streams and options await m.SendOnMediaChanging(); // Capture the current media types before components change var oldMediaTypes = m.Container.Components.MediaTypes.ToArray(); // Recreate selected streams as media components var mediaTypes = m.Container.UpdateComponents(); // remove all exiting component blocks and renderers that no longer exist var removableMediaTypes = oldMediaTypes .Where(t => mediaTypes.Contains(t) == false).ToArray(); foreach (var t in removableMediaTypes) { if (m.Renderers.ContainsKey(t)) { m.Renderers[t].Close(); m.Renderers.Remove(t); } if (m.Blocks.ContainsKey(t)) { m.Blocks[t]?.Dispose(); m.Blocks.Remove(t); } } // Create the block buffers and renderers as necessary foreach (var t in mediaTypes) { if (m.Blocks.ContainsKey(t) == false) { m.Blocks[t] = new MediaBlockBuffer(Constants.MaxBlocks[t], t); } if (m.Renderers.ContainsKey(t) == false) { m.Renderers[t] = MediaEngine.Platform.CreateRenderer(t, m); } m.Blocks[t].Clear(); m.Renderers[t].WaitForReadyState(); } // Mark a seek operation in order to invalidate renderers if (m.State.IsSeekable) { // Let's simply do an automated seek var seekCommand = new SeekCommand(Manager, m.WallClock); seekCommand.RunSynchronously(); return; } // We need to perform some packet reading and decoding var main = m.Container.Components.Main.MediaType; var auxs = m.Container.Components.MediaTypes.ExcludeMediaType(main); // Read and decode blocks until the main component is half full while (m.ShouldReadMorePackets && m.CanReadMorePackets) { // Read some packets m.Container.Read(); // Decode frames and add the blocks foreach (var t in mediaTypes) { var frames = m.Container.Components[t].ReceiveFrames(); foreach (var frame in frames) { if (frame != null) { m.Blocks[t].Add(frame, m.Container); } } } // Check if we have at least a half a buffer on main if (m.Blocks[main].CapacityPercent >= 0.5) { break; } } // Check if we have a valid range. If not, just set it what the main component is dictating if (m.Blocks[main].Count > 0 && m.Blocks[main].IsInRange(m.WallClock) == false) { m.Clock.Update(m.Blocks[main].RangeStartTime); } // Have the other components catch up foreach (var t in auxs) { if (m.Blocks[main].Count <= 0) { break; } if (t != MediaType.Audio && t != MediaType.Video) { continue; } while (m.Blocks[t].RangeEndTime < m.Blocks[main].RangeEndTime) { if (m.ShouldReadMorePackets == false || m.CanReadMorePackets == false) { break; } // Read some packets m.Container.Read(); // Decode frames and add the blocks var frames = m.Container.Components[t].ReceiveFrames(); foreach (var frame in frames) { if (frame != null) { m.Blocks[t].Add(frame, m.Container); } } } } foreach (var t in mediaTypes) { m.InvalidateRenderer(t); } m.HasDecoderSeeked = true; } catch { // TODO: Handle errors here } finally { if (resumeClock) { m?.Clock?.Play(); } m.State.IsSeeking = isSeeking; m.MediaChangingDone.Complete(); m.State.IsChanging = false; await m.SendOnMediaChanged(); } }