Ejemplo n.º 1
0
        /// <summary>
        /// Starts playing the open media URI.
        /// </summary>
        public void Play()
        {
            PlayCommand command = null;

            lock (SyncLock)
            {
                command = Commands.FirstOrDefault(c => c.CommandType == MediaCommandType.Play) as PlayCommand;
                if (command == null)
                {
                    command = new PlayCommand(this);
                    EnqueueCommand(command);
                }
            }

            WaitFor(command);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Performs the actions that this command implements.
        /// </summary>
        internal override void ExecuteInternal()
        {
            var m = Manager.MediaElement;

            WasPlaying = m.IsPlaying;

            var pause = new PauseCommand(Manager);

            pause.ExecuteInternal();

            var initialPosition = m.Clock.Position;

            m.SeekingDone?.Reset();
            var startTime = DateTime.UtcNow;

            try
            {
                var main = m.Container.Components.Main.MediaType;
                var t    = MediaType.None;

                // 1. Check if we already have the block. If we do, simply set the clock position to the target position
                // we don't need anything else.
                if (m.Blocks[main].IsInRange(TargetPosition))
                {
                    m.Clock.Position = TargetPosition;
                    return;
                }

                // Signal to wait one more frame dcoding cycle before
                // sending blocks to the renderer.
                m.HasDecoderSeeked = true;

                // wait for the current reading and decoding cycles
                // to finish. We don't want to interfere with reading in progress
                // or decoding in progress
                m.PacketReadingCycle?.WaitOne();

                // Capture seek target adjustment
                var adjustedSeekTarget = TargetPosition;
                if (main == MediaType.Video && m.Blocks[main].IsMonotonic)
                {
                    var targetSkewTicks = (long)Math.Round(
                        m.Blocks[main][0].Duration.Ticks * (m.Blocks[main].Capacity / 2d), 2);
                    adjustedSeekTarget = TimeSpan.FromTicks(adjustedSeekTarget.Ticks - targetSkewTicks);
                }

                // Clear Blocks and frames, reset the render times
                foreach (var mt in m.Container.Components.MediaTypes)
                {
                    m.Blocks[mt].Clear();
                    m.LastRenderTime[mt] = TimeSpan.MinValue;
                }

                // Populate frame queues with after-seek operation
                var frames = m.Container.Seek(adjustedSeekTarget);
                m.HasMediaEnded = false;

                // Clear all the blocks. We don't need them
                foreach (var kvp in m.Blocks)
                {
                    kvp.Value.Clear();
                }

                // Create the blocks from the obtained seek frames
                foreach (var frame in frames)
                {
                    m.Blocks[frame.MediaType]?.Add(frame, m.Container);
                }

                // Now read blocks until we have reached at least the Target Position
                while (m.Container.IsAtEndOfStream == false &&
                       m.Blocks[main].IsFull == false &&
                       m.Blocks[main].IsInRange(TargetPosition) == false)
                {
                    // Read the next packet
                    t = m.Container.Read();

                    // Ignore if we don't have an acceptable packet
                    if (m.Blocks.ContainsKey(t) == false)
                    {
                        continue;
                    }

                    // move on if we have plenty
                    if (m.Blocks[t].IsFull)
                    {
                        continue;
                    }

                    // Decode and add the frames to the corresponding output
                    frames.Clear();
                    frames.AddRange(m.Container.Components[t].ReceiveFrames());

                    foreach (var frame in frames)
                    {
                        m.Blocks[t].Add(frame, m.Container);
                    }
                }

                // Handle out-of sync scenarios
                if (m.Blocks[main].IsInRange(TargetPosition) == false)
                {
                    var minStartTime = m.Blocks[main].RangeStartTime.Ticks;
                    var maxStartTime = m.Blocks[main].RangeEndTime.Ticks;

                    m.Logger.Log(MediaLogMessageType.Warning,
                                 $"SEEK TP: Target Pos {TargetPosition.Format()} not between {m.Blocks[main].RangeStartTime.TotalSeconds:0.000} and {m.Blocks[main].RangeEndTime.TotalSeconds:0.000}");

                    if (adjustedSeekTarget.Ticks < minStartTime)
                    {
                        m.Clock.Position = TimeSpan.FromTicks(minStartTime);
                    }
                    else if (adjustedSeekTarget.Ticks > maxStartTime)
                    {
                        m.Clock.Position = TimeSpan.FromTicks(maxStartTime);
                    }
                    else
                    {
                        m.Clock.Position = TargetPosition;
                    }
                }
                else
                {
                    // TODO: handle this case correctly. The way this is handled currently sucks.
                    if (m.Blocks[main].Count == 0 && TargetPosition != TimeSpan.Zero)
                    {
                        m.Clock.Position = initialPosition;
                    }
                    else
                    {
                        m.Clock.Position = TargetPosition;
                    }
                }
            }
            catch (Exception ex)
            {
                // Log the exception
                m.Logger.Log(MediaLogMessageType.Error,
                             $"SEEK E: {ex.GetType()} - {ex.Message}. Stack Trace:\r\n{ex.StackTrace}");
            }
            finally
            {
                if (m.HasDecoderSeeked)
                {
                    m.Logger.Log(MediaLogMessageType.Debug,
                                 $"SEEK D: Elapsed: {startTime.FormatElapsed()} | Target: {TargetPosition.Format()}");
                }

                m.SeekingDone?.Set();

                if (WasPlaying)
                {
                    var play = new PlayCommand(this.Manager);
                    play.ExecuteInternal();
                }
            }
        }