Replace() public method

Replaces the internally-held frames with the specified new frames.
Buffer does not support the capacity of new elements
public Replace ( FFmpegMediaFrameCache newFrames ) : void
newFrames FFmpegMediaFrameCache The new frames.
return void
Beispiel #1
0
        /// <summary>
        /// Internals the seek input.
        /// </summary>
        /// <param name="renderTime">The render time.</param>
        private void InternalSeekInput(decimal renderTime)
        {
            if (IsLiveStream)
            {
                if (LeadingFramesCache.IsEmpty == false)
                {
                    RealtimeClock.Seek(LeadingFramesCache.FirstFrameTime);
                }

                return;
            }

#if DEBUG
            var seekStopwatch = new System.Diagnostics.Stopwatch();
            seekStopwatch.Start();
#endif

            if (renderTime < StartTime)
            {
                renderTime = StartTime;
            }

            RealtimeClock.Seek(renderTime);

            var allowedThreshold = Constants.SeekThresholdSeconds;
            var seekOffsetLength = Constants.SeekOffsetSeconds;

            var seekTime         = renderTime - seekOffsetLength;
            var maxSeekStartTime = seekTime - allowedThreshold;

            var bufferedLeadingFrames = new FFmpegMediaFrameCache(LeadingFramesCache);
            var bufferedLaggingFrames = new FFmpegMediaFrameCache(LaggingFramesCache);

            var outerLoopCount        = 0;
            var innerLoopCount        = 0;
            var frameReleaseCount     = 0;
            var doSeekInStream        = true;
            var doSeekByPullingFrames = true;
            var seekFlag            = 0;
            var seekFrameResult     = 0;
            var startTime           = DateTime.UtcNow;
            var lastFailedTimestamp = long.MinValue;
            var seekToLastFrame     = false;

            var seekTimeBase      = LeadingFramesCache.Type == MediaFrameType.Video ? InputVideoStream->time_base : InputAudioStream->time_base;
            var seekStreamIndex   = LeadingFramesCache.Type == MediaFrameType.Video ? InputVideoStream->index : InputAudioStream->index;
            var leadingFrameIndex = -1;

            try
            {
                while (doSeekInStream)
                {
                    outerLoopCount++;

                    if (seekTime < StartTime)
                    {
                        seekTime = StartTime;
                    }

                    if (lastFailedTimestamp == StartDts)
                    {
                        if (LeadingFramesCache.IsEmpty == false)
                        {
                            RealtimeClock.Seek(LeadingFramesCache.FirstFrameTime);
                        }

                        ErrorOccurredCallback(this, new MediaPlaybackException(MediaPlaybackErrorSources.InternalSeekInput, MediaPlaybackErrorCode.SeekFailedCritical,
                                                                               string.Format("Target Postion @ {0:0.000}s has already failed to seek. First DTS {1} also failed and will not retry.", seekTime, StartDts)));

                        return;
                    }

                    var targetTimestamp = Helper.SecondsToTimestamp(seekTime, seekTimeBase);
                    if (lastFailedTimestamp == targetTimestamp)
                    {
                        ErrorOccurredCallback(this, new MediaPlaybackException(MediaPlaybackErrorSources.InternalSeekInput, MediaPlaybackErrorCode.SeekFailedWillRetry,
                                                                               string.Format("Target Postion @ {0:0.000}s has already failed to seek. Target timestamp will now be First DTS {1}.", seekTime, StartDts)));

                        targetTimestamp = StartDts;
                    }

                    seekFlag = (seekTime < renderTime || seekTime <= StartTime ? (int)ffmpeg.AVSEEK_FLAG_BACKWARD : 0) | 0; // FFmpegInvoke.AVSEEK_FLAG_ANY;
                    //seekFlag = FFmpegInvoke.AVSEEK_FLAG_BACKWARD; // | FFmpegInvoke.AVSEEK_FLAG_ANY;

                    seekFrameResult = ffmpeg.av_seek_frame(InputFormatContext, seekStreamIndex, targetTimestamp, seekFlag); // significantly faster than seek_file
                    //seekFrameResult = FFmpegInvoke.avformat_seek_file(InputFormatContext, streamIndex, long.MinValue, targetTimestamp, long.MaxValue, seekFlag);
                    if (seekFrameResult < Constants.SuccessCode)
                    {
                        if (LeadingFramesCache.IsEmpty == false)
                        {
                            RealtimeClock.Seek(LeadingFramesCache.FirstFrameTime);
                        }

                        var errorMessage = Helper.GetFFmpegErrorMessage(seekFrameResult);
                        ErrorOccurredCallback(this, new MediaPlaybackException(MediaPlaybackErrorSources.InternalSeekInput, MediaPlaybackErrorCode.SeekFailedFFmpeg,
                                                                               string.Format("FFmpeg av_seek_frame @ {1:0.000}: Failed with error code {0}. {2}", seekFrameResult, seekTime, errorMessage)));

                        return;
                    }
                    else
                    {
                        if (VideoCodecContext != null)
                        {
                            ffmpeg.avcodec_flush_buffers(VideoCodecContext);
                        }

                        if (AudioCodecContext != null)
                        {
                            ffmpeg.avcodec_flush_buffers(AudioCodecContext);
                        }
                    }

                    leadingFrameIndex = -1;
                    bufferedLeadingFrames.Clear();
                    bufferedLaggingFrames.Clear();

                    doSeekInStream        = false;
                    doSeekByPullingFrames = true;

                    while (doSeekByPullingFrames)
                    {
                        innerLoopCount++;

                        var frame = this.PullMediaFrame();
                        if (frame != null)
                        {
                            if (frame.StartTime < maxSeekStartTime)
                            {
                                frame.EnqueueRelease();
                                frameReleaseCount++;
                                continue;
                            }

                            if (frame.Type == bufferedLeadingFrames.Type)
                            {
                                leadingFrameIndex++;
                                if (leadingFrameIndex == 0 && frame.Type == bufferedLeadingFrames.Type && frame.StartTime - frame.Duration > maxSeekStartTime && maxSeekStartTime > 0M)
                                {
                                    seekTime -= seekOffsetLength;
                                    frame.EnqueueRelease();
                                    doSeekInStream      = true;
                                    lastFailedTimestamp = targetTimestamp;
                                    break;
                                }

                                // We are Full minus 1 at this point. We'll stop buffering
                                if (bufferedLeadingFrames.Count >= bufferedLeadingFrames.Capacity - 1)
                                {
                                    doSeekByPullingFrames = false;
                                }

                                bufferedLeadingFrames.Add(frame);
                            }
                            else if (frame.Type == bufferedLaggingFrames.Type)
                            {
                                // add the lagging frame no matter what
                                if (bufferedLaggingFrames.IsFull)
                                {
                                    bufferedLaggingFrames.RemoveFirst();
                                }

                                bufferedLaggingFrames.Add(frame);
                            }

                            // Find out if we have the frame
                            var seekFrameIndex    = bufferedLeadingFrames.IndexOf(renderTime, true);
                            var minimumFrameCount = (seekFrameIndex - 1) * 2;

                            // if we have more than enough frames in the buffer or we have reached a full or end condition, stop buffering frames
                            if (seekFrameIndex > 0)
                            {
                                if (bufferedLeadingFrames.Count >= minimumFrameCount || bufferedLeadingFrames.IsFull || IsAtEndOfStream)
                                {
                                    doSeekByPullingFrames = false;
                                }
                            }
                        }

                        // We're already padt the end of the stream. Natural duration was wron for the leading frames cache.
                        if (IsAtEndOfStream && bufferedLeadingFrames.Count <= 0)
                        {
                            doSeekInStream   = true;
                            seekTime        -= seekOffsetLength;
                            maxSeekStartTime = seekTime - allowedThreshold;
                            seekToLastFrame  = true;
                        }

                        if (doSeekInStream)
                        {
                            break;
                        }

                        if (doSeekByPullingFrames == false || IsAtEndOfStream)
                        {
                            LeadingFramesCache.Replace(bufferedLeadingFrames);
                            LaggingFramesCache.Replace(bufferedLaggingFrames);

                            if (seekToLastFrame && LeadingFramesCache.Count > 0)
                            {
                                RealtimeClock.Seek(LeadingFramesCache.LastFrameTime);
                            }

                            return;
                        }
                    }
                }
            }
            finally
            {
#if DEBUG
                seekStopwatch.Stop();
                SeekTimes.Add(seekStopwatch.ElapsedMilliseconds);
                InnerLoopCounts.Add(innerLoopCount);
                System.Diagnostics.Debug.WriteLine("Seek @ {6:0.000} = Long: {0:00}\t Short: {1:000}\t Short (AVG): {2:0.000}\t Waste Count: {3:000}\t Elapsed: {4}\tElapsed (AVG): {5:0.000}",
                                                   outerLoopCount, innerLoopCount, InnerLoopCounts.Average(), frameReleaseCount, seekStopwatch.ElapsedMilliseconds, SeekTimes.Average(), renderTime);
#endif
            }
        }