예제 #1
0
void
VideoSink::SetPlaying(bool aPlaying)
{
  AssertOwnerThread();
  VSINK_LOG_V(" playing (%d) -> (%d)", mAudioSink->IsPlaying(), aPlaying);

  if (!aPlaying) {
    // Reset any update timer if paused.
    mUpdateScheduler.Reset();
    // Since playback is paused, tell compositor to render only current frame.
    RenderVideoFrames(1);
    if (mContainer) {
      mContainer->ClearCachedResources();
    }
  }

  mAudioSink->SetPlaying(aPlaying);

  if (mHasVideo && aPlaying) {
    // There's no thread in VideoSink for pulling video frames, need to trigger
    // rendering while becoming playing status. because the VideoQueue may be
    // full already.
    TryUpdateRenderedVideoFrames();
  }
}
예제 #2
0
void
VideoSink::UpdateRenderedVideoFrames()
{
  AssertOwnerThread();
  MOZ_ASSERT(mAudioSink->IsPlaying(), "should be called while playing.");

  // Get the current playback position.
  TimeStamp nowTime;
  const int64_t clockTime = mAudioSink->GetPosition(&nowTime);
  NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");

  // Skip frames up to the playback position.
  int64_t lastDisplayedFrameEndTime = 0;
  while (VideoQueue().GetSize() > mMinVideoQueueSize &&
         clockTime >= VideoQueue().PeekFront()->GetEndTime()) {
    RefPtr<MediaData> frame = VideoQueue().PopFront();
    if (frame->As<VideoData>()->mSentToCompositor) {
      lastDisplayedFrameEndTime = frame->GetEndTime();
      mFrameStats.NotifyPresentedFrame();
    } else {
      mFrameStats.NotifyDecodedFrames({ 0, 0, 1 });
      VSINK_LOG_V("discarding video frame mTime=%lld clock_time=%lld",
                  frame->mTime, clockTime);
    }
  }

  // The presentation end time of the last video frame displayed is either
  // the end time of the current frame, or if we dropped all frames in the
  // queue, the end time of the last frame we removed from the queue.
  RefPtr<MediaData> currentFrame = VideoQueue().PeekFront();
  mVideoFrameEndTime = std::max(mVideoFrameEndTime,
    currentFrame ? currentFrame->GetEndTime() : lastDisplayedFrameEndTime);

  MaybeResolveEndPromise();

  RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);

  // Get the timestamp of the next frame. Schedule the next update at
  // the start time of the next frame. If we don't have a next frame,
  // we will run render loops again upon incoming frames.
  nsTArray<RefPtr<MediaData>> frames;
  VideoQueue().GetFirstElements(2, &frames);
  if (frames.Length() < 2) {
    return;
  }

  int64_t nextFrameTime = frames[1]->mTime;
  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
    (nextFrameTime - clockTime) / mAudioSink->GetPlaybackParams().mPlaybackRate);

  RefPtr<VideoSink> self = this;
  mUpdateScheduler.Ensure(target, [self] () {
    self->UpdateRenderedVideoFramesByTimer();
  }, [self] () {
    self->UpdateRenderedVideoFramesByTimer();
  });
}
예제 #3
0
void
VideoSink::UpdateRenderedVideoFrames()
{
  AssertOwnerThread();
  MOZ_ASSERT(mAudioSink->IsPlaying(), "should be called while playing.");

  TimeStamp nowTime;
  const int64_t clockTime = mAudioSink->GetPosition(&nowTime);
  // Skip frames up to the frame at the playback position, and figure out
  // the time remaining until it's time to display the next frame and drop
  // the current frame.
  NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");

  int64_t remainingTime = mDelayDuration;
  if (VideoQueue().GetSize() > 0) {
    RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
    int32_t framesRemoved = 0;
    while (VideoQueue().GetSize() > 0) {
      MediaData* nextFrame = VideoQueue().PeekFront();
      if (!mRealTime && nextFrame->mTime > clockTime) {
        remainingTime = nextFrame->mTime - clockTime;
        break;
      }
      ++framesRemoved;
      if (!currentFrame->As<VideoData>()->mSentToCompositor) {
        mFrameStats.NotifyDecodedFrames(0, 0, 1);
        VSINK_LOG_V("discarding video frame mTime=%lld clock_time=%lld",
                    currentFrame->mTime, clockTime);
      }
      currentFrame = VideoQueue().PopFront();
    }
    VideoQueue().PushFront(currentFrame);
    if (framesRemoved > 0) {
      mVideoFrameEndTime = currentFrame->GetEndTime();
      mFrameStats.NotifyPresentedFrame();
    }
  }

  RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);

  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(remainingTime);

  RefPtr<VideoSink> self = this;
  mUpdateScheduler.Ensure(target, [self] () {
    self->UpdateRenderedVideoFramesByTimer();
  }, [self] () {
    self->UpdateRenderedVideoFramesByTimer();
  });
}
예제 #4
0
void
VideoSink::Redraw(const VideoInfo& aInfo)
{
  AssertOwnerThread();

  // No video track, nothing to draw.
  if (!aInfo.IsValid() || !mContainer) {
    return;
  }

  if (VideoQueue().GetSize() > 0) {
    RenderVideoFrames(1);
    return;
  }

  // When we reach here, it means there are no frames in this video track.
  // Draw a blank frame to ensure there is something in the image container
  // to fire 'loadeddata'.
  RefPtr<Image> blank =
    mContainer->GetImageContainer()->CreatePlanarYCbCrImage();
  mContainer->SetCurrentFrame(aInfo.mDisplay, blank, TimeStamp::Now());
}
예제 #5
0
void
VideoSink::UpdateRenderedVideoFrames()
{
  AssertOwnerThread();
  MOZ_ASSERT(mAudioSink->IsPlaying(), "should be called while playing.");

  TimeStamp nowTime;
  const int64_t clockTime = mAudioSink->GetPosition(&nowTime);
  // Skip frames up to the frame at the playback position, and figure out
  // the time remaining until it's time to display the next frame and drop
  // the current frame.
  NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");

  int64_t remainingTime = -1;
  if (VideoQueue().GetSize() > 0) {
    RefPtr<MediaData> currentFrame = VideoQueue().PopFront();
    int32_t framesRemoved = 0;
    while (VideoQueue().GetSize() > 0) {
      RefPtr<MediaData> nextFrame = VideoQueue().PeekFront();
      if (nextFrame->mTime > clockTime) {
        remainingTime = nextFrame->mTime - clockTime;
        break;
      }
      ++framesRemoved;
      if (!currentFrame->As<VideoData>()->mSentToCompositor) {
        mFrameStats.NotifyDecodedFrames(0, 0, 1);
        VSINK_LOG_V("discarding video frame mTime=%lld clock_time=%lld",
                    currentFrame->mTime, clockTime);
      }
      currentFrame = VideoQueue().PopFront();
    }
    VideoQueue().PushFront(currentFrame);
    if (framesRemoved > 0) {
      mVideoFrameEndTime = currentFrame->GetEndTime();
      mFrameStats.NotifyPresentedFrame();
    }
  }

  // All frames are rendered, Let's resolve the promise.
  if (VideoQueue().IsFinished() &&
      VideoQueue().GetSize() <= 1 &&
      !mVideoSinkEndRequest.Exists()) {
    mEndPromiseHolder.ResolveIfExists(true, __func__);
  }

  RenderVideoFrames(mVideoQueueSendToCompositorSize, clockTime, nowTime);

  // No next fame to render. There is no need to schedule next render
  // loop. We will run render loops again upon incoming frames.
  if (remainingTime < 0) {
    return;
  }

  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(
    remainingTime / mAudioSink->GetPlaybackParams().mPlaybackRate);

  RefPtr<VideoSink> self = this;
  mUpdateScheduler.Ensure(target, [self] () {
    self->UpdateRenderedVideoFramesByTimer();
  }, [self] () {
    self->UpdateRenderedVideoFramesByTimer();
  });
}
예제 #6
0
void
VideoSink::Redraw()
{
  AssertOwnerThread();
  RenderVideoFrames(1);
}