// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_RENDERER_MEDIA_WEBMEDIAPLAYER_MS_COMPOSITOR_H
#define CONTENT_RENDERER_MEDIA_WEBMEDIAPLAYER_MS_COMPOSITOR_H

#include <stddef.h>

#include <map>
#include <vector>

#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "cc/layers/video_frame_provider.h"
#include "content/common/content_export.h"

namespace base {
class SingleThreadTaskRunner;
}

namespace blink {
class WebURL;
}

namespace gfx {
class Size;
}

namespace media {
class SkCanvasVideoRenderer;
class VideoRendererAlgorithm;
}

namespace content {
class WebMediaPlayerMS;

// This class is designed to handle the work load on compositor thread for
// WebMediaPlayerMS. It will be instantiated on the main thread, but destroyed
// on the compositor thread.
//
// WebMediaPlayerMSCompositor utilizes VideoRendererAlgorithm to store the
// incoming frames and select the best frame for rendering to maximize the
// smoothness, if REFERENCE_TIMEs are populated for incoming VideoFrames.
// Otherwise, WebMediaPlayerMSCompositor will simply store the most recent
// frame, and submit it whenever asked by the compositor.
class CONTENT_EXPORT WebMediaPlayerMSCompositor
    : public NON_EXPORTED_BASE(cc::VideoFrameProvider) {
 public:
  // This |url| represents the media stream we are rendering. |url| is used to
  // find out what web stream this WebMediaPlayerMSCompositor is playing, and
  // together with flag "--disable-rtc-smoothness-algorithm" determine whether
  // we enable algorithm or not.
  WebMediaPlayerMSCompositor(
      const scoped_refptr<base::SingleThreadTaskRunner>& compositor_task_runner,
      const blink::WebURL& url,
      const base::WeakPtr<WebMediaPlayerMS>& player);

  ~WebMediaPlayerMSCompositor() override;

  void EnqueueFrame(const scoped_refptr<media::VideoFrame>& frame);

  // Statistical data
  gfx::Size GetCurrentSize();
  base::TimeDelta GetCurrentTime();
  size_t total_frame_count() const;
  size_t dropped_frame_count() const;

  // VideoFrameProvider implementation.
  void SetVideoFrameProviderClient(
      cc::VideoFrameProvider::Client* client) override;
  bool UpdateCurrentFrame(base::TimeTicks deadline_min,
                          base::TimeTicks deadline_max) override;
  bool HasCurrentFrame() override;
  scoped_refptr<media::VideoFrame> GetCurrentFrame() override;
  void PutCurrentFrame() override;

  void StartRendering();
  void StopRendering();
  void ReplaceCurrentFrameWithACopy();

 private:
  friend class WebMediaPlayerMSTest;

  bool MapTimestampsToRenderTimeTicks(
      const std::vector<base::TimeDelta>& timestamps,
      std::vector<base::TimeTicks>* wall_clock_times);

  void SetCurrentFrame(const scoped_refptr<media::VideoFrame>& frame);

  // For algorithm enabled case only: given the render interval, update
  // current_frame_ and dropped_frame_count_.
  void Render(base::TimeTicks deadline_min, base::TimeTicks deadline_max);

  void StartRenderingInternal();
  void StopRenderingInternal();

  void SetAlgorithmEnabledForTesting(bool algorithm_enabled);

  // Used for DCHECKs to ensure method calls executed in the correct thread.
  base::ThreadChecker thread_checker_;

  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
  base::MessageLoop* main_message_loop_;

  base::WeakPtr<WebMediaPlayerMS> player_;

  size_t serial_;

  // A pointer back to the compositor to inform it about state changes. This
  // is not |nullptr| while the compositor is actively using this
  // VideoFrameProvider. This will be set to |nullptr| when the compositor stops
  // serving this VideoFrameProvider.
  cc::VideoFrameProvider::Client* video_frame_provider_client_;

  // |current_frame_| is updated only on compositor thread. The object it
  // holds can be freed on the compositor thread if it is the last to hold a
  // reference but media::VideoFrame is a thread-safe ref-pointer. It is
  // however read on the compositor and main thread so locking is required
  // around all modifications and all reads on the any thread.
  scoped_refptr<media::VideoFrame> current_frame_;

  // |rendering_frame_buffer_| stores the incoming frames, and provides a frame
  // selection method which returns the best frame for the render interval.
  scoped_ptr<media::VideoRendererAlgorithm> rendering_frame_buffer_;

  // |current_frame_used_by_compositor_| is updated on compositor thread only.
  // It's used to track whether |current_frame_| was painted for detecting
  // when to increase |dropped_frame_count_|.
  bool current_frame_used_by_compositor_;

  // Historical data about last rendering. These are for detecting whether
  // rendering is paused (one reason is that the tab is not in the front), in
  // which case we need to do background rendering.
  base::TimeTicks last_deadline_max_;
  base::TimeDelta last_render_length_;

  size_t total_frame_count_;
  size_t dropped_frame_count_;

  bool stopped_;

  std::map<base::TimeDelta, base::TimeTicks> timestamps_to_clock_times_;

  // |current_frame_lock_| protects |current_frame_used_by_compositor_|,
  // |current_frame_|, and |rendering_frame_buffer_|.
  base::Lock current_frame_lock_;

  // Make sure the weak pointer factory member is the last member of the class.
  base::WeakPtrFactory<WebMediaPlayerMSCompositor> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerMSCompositor);
};
}

#endif  // CONTENT_RENDERER_MEDIA_WEBMEDIAPLAYER_MS_COMPOSITOR_H
