Bug 1235301 part 2 - implement FrameStepVideoSink; r?jwwang draft
authorKaku Kuo <tkuo@mozilla.com>
Tue, 03 May 2016 17:49:58 +0800
changeset 363224 4750be51ae2b831bb9a9af222f66863a3f06abc7
parent 363223 07a331c0c29ddeb66ae782b7d8fd15f1560d1167
child 363225 1aeaf662346e778da8b28a5b0e94073881115475
push id17141
push usertkuo@mozilla.com
push dateWed, 04 May 2016 08:55:00 +0000
reviewersjwwang
bugs1235301
milestone49.0a1
Bug 1235301 part 2 - implement FrameStepVideoSink; r?jwwang MozReview-Commit-ID: xQFISwRMYz
dom/media/mediasink/FrameStepVideoSink.cpp
dom/media/mediasink/FrameStepVideoSink.h
dom/media/mediasink/MediaSink.h
dom/media/mediasink/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasink/FrameStepVideoSink.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageContainer.h"
+#include "FrameStepVideoSink.h"
+#include "MediaQueue.h"
+#include "VideoFrameContainer.h"
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+
+namespace media {
+
+FrameStepVideoSink::FrameStepVideoSink(MediaQueue<MediaData>& aAudioQueue,
+                                       MediaQueue<MediaData>& aVideoQueue,
+                                       VideoFrameContainer* aContainer)
+  : mIsStarted(false)
+  , mIsPlaying(false)
+  , mAudioQueue(aAudioQueue)
+  , mVideoQueue(aVideoQueue)
+  , mContainer(aContainer)
+  , mPosition(0)
+{
+}
+
+FrameStepVideoSink::~FrameStepVideoSink()
+{
+}
+
+int64_t
+FrameStepVideoSink::GetEndTime(TrackType aType) const
+{
+  MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
+
+  if (aType == TrackInfo::kVideoTrack) {
+    return mPosition;
+  }
+  return -1;
+}
+
+int64_t
+FrameStepVideoSink::GetPosition(TimeStamp* aTimeStamp) const
+{
+  MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
+  if (aTimeStamp) {
+    *aTimeStamp = TimeStamp::Now();
+  }
+  return mPosition;
+}
+
+void
+FrameStepVideoSink::SetPlaying(bool aPlaying)
+{
+  mIsPlaying = aPlaying;
+}
+
+void
+FrameStepVideoSink::Start(int64_t aStartTime, const MediaInfo& aInfo)
+{
+  MOZ_ASSERT(!mIsStarted, "playback already started.");
+  mIsStarted = true;
+  mPosition = aStartTime;
+}
+
+void
+FrameStepVideoSink::Stop()
+{
+  MOZ_ASSERT(mIsStarted, "playback not started.");
+  mIsStarted = false;
+}
+
+bool
+FrameStepVideoSink::IsStarted() const
+{
+  return mIsStarted;
+}
+
+bool
+FrameStepVideoSink::IsPlaying() const
+{
+  return mIsStarted && mIsPlaying;
+}
+
+void
+FrameStepVideoSink::Shutdown()
+{
+  MOZ_ASSERT(!mIsStarted, "Must be called after playback stopped.");
+}
+
+void
+FrameStepVideoSink::Redraw()
+{
+  if (!mContainer || mVideoQueue.GetSize() == 0) {
+    return;
+  }
+
+  VideoData* frame = mVideoQueue.PeekFront()->As<VideoData>();
+  frame->mSentToCompositor = true;
+  if (!frame->mImage || !frame->mImage->IsValid() || frame->mTime < 0) {
+    return;
+  }
+
+  mContainer->SetCurrentFrame(frame->mDisplay, frame->mImage.get(), TimeStamp::Now());
+}
+
+static int64_t
+FindNextFrame(MediaQueue<MediaData>& aQueue, int64_t aTime)
+{
+  AutoTArray<RefPtr<MediaData>, 16> frames;
+  aQueue.GetFirstElements(aQueue.GetSize(), &frames);
+  for (auto&& frame : frames) {
+    VideoData* v = frame->As<VideoData>();
+    if (v->mTime > aTime) {
+      return v->mTime;
+    }
+  }
+  return -1;
+}
+
+static void
+DropFramesUntil(MediaQueue<MediaData>& aQueue, int64_t aTime) {
+  while (aQueue.GetSize() > 0) {
+    if (aQueue.PeekFront()->mTime < aTime) {
+      RefPtr<MediaData> releaseMe = aQueue.PopFront();
+      continue;
+    }
+    break;
+  }
+}
+
+static void
+DropAllFrames(MediaQueue<MediaData>& aQueue) {
+  while(aQueue.GetSize() > 0) {
+    RefPtr<MediaData> releaseMe = aQueue.PopFront();
+  }
+  return;
+}
+
+bool
+FrameStepVideoSink::AdvanceFrame()
+{
+  int64_t newPos = FindNextFrame(mVideoQueue, mPosition);
+  if (newPos < 0) {
+    if (mVideoQueue.IsFinished()) {
+      DropAllFrames(mVideoQueue);
+      DropAllFrames(mAudioQueue);
+    }
+    return false;
+  }
+
+  mPosition = newPos;
+  DropFramesUntil(mVideoQueue, newPos);
+  DropFramesUntil(mAudioQueue, newPos);
+  Redraw();
+
+  return true;
+}
+
+MediaQueue<MediaData>&
+FrameStepVideoSink::VideoQueue() {
+  return mVideoQueue;
+}
+
+bool
+FrameStepVideoSink::HasUnplayedFrames(TrackType aType) const
+{
+  int64_t newPos = FindNextFrame(mVideoQueue, mPosition);
+  if (newPos < 0) {
+    return false;
+  }
+
+  return true;
+}
+
+} // namespace media
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasink/FrameStepVideoSink.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef FrameStepVideoSink_h_
+#define FrameStepVideoSink_h_
+
+#include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+
+#include "MediaSink.h"
+
+namespace mozilla {
+
+class VideoFrameContainer;
+template <class T> class MediaQueue;
+
+namespace media {
+
+class FrameStepVideoSink : public MediaSink {
+public:
+  FrameStepVideoSink(MediaQueue<MediaData>& aAudioQueue,
+                     MediaQueue<MediaData>& aVideoQueue,
+                     VideoFrameContainer* aContainer);
+
+  const PlaybackParams& GetPlaybackParams() const override {
+    return mParams;
+  }
+
+  void SetPlaybackParams(const PlaybackParams& aParams) override {
+    mParams = aParams;
+  }
+
+  RefPtr<GenericPromise> OnEnded(TrackType aType) override {
+    return nullptr;
+  }
+
+  int64_t GetEndTime(TrackType aType) const override;
+
+  int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
+
+  bool HasUnplayedFrames(TrackType aType) const override;
+
+  void SetPlaybackRate(double aPlaybackRate) override {}
+
+  void SetVolume(double aVolume) override {}
+
+  void SetPreservesPitch(bool aPreservesPitch) override {}
+
+  void SetPlaying(bool aPlaying) override;
+
+  void Redraw() override;
+
+  void Start(int64_t aStartTime, const MediaInfo& aInfo) override;
+
+  void Stop() override;
+
+  bool IsStarted() const override;
+
+  bool IsPlaying() const override;
+
+  void Shutdown() override;
+
+  bool AdvanceFrame() override;
+
+  MediaQueue<MediaData>& VideoQueue();
+
+private:
+  virtual ~FrameStepVideoSink();
+
+  PlaybackParams mParams;
+  bool mIsStarted;
+  bool mIsPlaying;
+  MediaQueue<MediaData>& mAudioQueue;
+  MediaQueue<MediaData>& mVideoQueue;
+  VideoFrameContainer* mContainer;
+  int64_t mPosition;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif // FrameStepVideoSink_h_
--- a/dom/media/mediasink/MediaSink.h
+++ b/dom/media/mediasink/MediaSink.h
@@ -114,16 +114,20 @@ public:
   // Can be called in any state.
   virtual bool IsPlaying() const = 0;
 
   // Called on the state machine thread to shut down the sink. All resources
   // allocated by this sink should be released.
   // Must be called after playback stopped.
   virtual void Shutdown() {}
 
+  // Advance playback position and draw next frame.
+  // Must be called after playback starts.
+  virtual bool AdvanceFrame() { return false; }
+
 protected:
   virtual ~MediaSink() {}
 };
 
 } // namespace media
 } // namespace mozilla
 
 #endif //MediaSink_h_
--- a/dom/media/mediasink/moz.build
+++ b/dom/media/mediasink/moz.build
@@ -3,13 +3,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'AudioSinkWrapper.cpp',
     'DecodedAudioDataSink.cpp',
     'DecodedStream.cpp',
+    'FrameStepVideoSink.cpp',
     'OutputStreamManager.cpp',
     'VideoSink.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'