// Copyright (c) 2012 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 REMOTING_HOST_VIDEO_SCHEDULER_H_
#define REMOTING_HOST_VIDEO_SCHEDULER_H_

#include <vector>

#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "remoting/codec/video_encoder.h"
#include "remoting/host/capture_scheduler.h"
#include "remoting/proto/video.pb.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h"

namespace base {
class SingleThreadTaskRunner;
}  // namespace base

namespace media {
class DesktopCapturer;
}  // namespace media

namespace remoting {

class CursorShapeInfo;

namespace protocol {
class CursorShapeInfo;
class CursorShapeStub;
class VideoStub;
}  // namespace protocol

// Class responsible for scheduling frame captures from a
// webrtc::DesktopCapturer, delivering them to a VideoEncoder to encode, and
// finally passing the encoded video packets to the specified VideoStub to send
// on the network.
//
// THREADING
//
// This class is supplied TaskRunners to use for capture, encode and network
// operations.  Capture, encode and network transmission tasks are interleaved
// as illustrated below:
//
// |       CAPTURE       ENCODE     NETWORK
// |    .............
// |    .  Capture  .
// |    .............
// |                  ............
// |                  .          .
// |    ............. .          .
// |    .  Capture  . .  Encode  .
// |    ............. .          .
// |                  .          .
// |                  ............
// |    ............. ............ ..........
// |    .  Capture  . .          . .  Send  .
// |    ............. .          . ..........
// |                  .  Encode  .
// |                  .          .
// |                  .          .
// |                  ............
// | Time
// v
//
// VideoScheduler would ideally schedule captures so as to saturate the slowest
// of the capture, encode and network processes.  However, it also needs to
// rate-limit captures to avoid overloading the host system, either by consuming
// too much CPU, or hogging the host's graphics subsystem.

class VideoScheduler : public base::RefCountedThreadSafe<VideoScheduler>,
                       public webrtc::DesktopCapturer::Callback,
                       public webrtc::MouseCursorMonitor::Callback {
 public:
  // Enables timestamps for generated frames. Used for testing.
  static void EnableTimestampsForTests();

  // Creates a VideoScheduler running capture, encode and network tasks on the
  // supplied TaskRunners.  Video and cursor shape updates will be pumped to
  // |video_stub| and |client_stub|, which must remain valid until Stop() is
  // called. |capturer| is used to capture frames.
  VideoScheduler(
      scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner,
      scoped_ptr<webrtc::DesktopCapturer> capturer,
      scoped_ptr<webrtc::MouseCursorMonitor> mouse_cursor_monitor,
      scoped_ptr<VideoEncoder> encoder,
      protocol::CursorShapeStub* cursor_stub,
      protocol::VideoStub* video_stub);

  // webrtc::DesktopCapturer::Callback implementation.
  virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE;
  virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE;

  // webrtc::MouseCursorMonitor::Callback implementation.
  virtual void OnMouseCursor(
      webrtc::MouseCursor* mouse_cursor) OVERRIDE;
  virtual void OnMouseCursorPosition(
      webrtc::MouseCursorMonitor::CursorState state,
      const webrtc::DesktopVector& position) OVERRIDE;

  // Starts scheduling frame captures.
  void Start();

  // Stop scheduling frame captures. This object cannot be re-used once
  // it has been stopped.
  void Stop();

  // Pauses or resumes scheduling of frame captures.  Pausing/resuming captures
  // only affects capture scheduling and does not stop/start the capturer.
  void Pause(bool pause);

  // Updates the sequence number embedded in VideoPackets.
  // Sequence numbers are used for performance measurements.
  void UpdateSequenceNumber(int64 sequence_number);

  // Sets whether the video encoder should be requested to encode losslessly,
  // or to use a lossless color space (typically requiring higher bandwidth).
  void SetLosslessEncode(bool want_lossless);
  void SetLosslessColor(bool want_lossless);

 private:
  friend class base::RefCountedThreadSafe<VideoScheduler>;
  virtual ~VideoScheduler();

  // Capturer thread ----------------------------------------------------------

  // Starts the capturer on the capture thread.
  void StartOnCaptureThread();

  // Stops scheduling frame captures on the capture thread.
  void StopOnCaptureThread();

  // Schedules the next call to CaptureNextFrame.
  void ScheduleNextCapture();

  // Starts the next frame capture, unless there are already too many pending.
  void CaptureNextFrame();

  // Called when a frame capture has been encoded & sent to the client.
  void FrameCaptureCompleted();

  // Network thread -----------------------------------------------------------

  // Send |packet| to the client, unless we are in the process of stopping.
  void SendVideoPacket(scoped_ptr<VideoPacket> packet);

  // Callback passed to |video_stub_| for the last packet in each frame, to
  // rate-limit frame captures to network throughput.
  void OnVideoPacketSent();

  // Called by |keep_alive_timer_|.
  void SendKeepAlivePacket();

  // Callback for |video_stub_| called after a keep-alive packet is sent.
  void OnKeepAlivePacketSent();

  // Send updated cursor shape to client.
  void SendCursorShape(scoped_ptr<protocol::CursorShapeInfo> cursor_shape);

  // Encoder thread -----------------------------------------------------------

  // Encode a frame, passing generated VideoPackets to SendVideoPacket().
  void EncodeFrame(scoped_ptr<webrtc::DesktopFrame> frame,
                   int64 sequence_number,
                   base::TimeTicks timestamp);

  void EncodedDataAvailableCallback(int64 sequence_number,
                                    scoped_ptr<VideoPacket> packet);

  // Task runners used by this class.
  scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_;
  scoped_refptr<base::SingleThreadTaskRunner> encode_task_runner_;
  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;

  // Used to capture frames. Always accessed on the capture thread.
  scoped_ptr<webrtc::DesktopCapturer> capturer_;

  // Used to capture mouse cursor shapes. Always accessed on the capture thread.
  scoped_ptr<webrtc::MouseCursorMonitor> mouse_cursor_monitor_;

  // Used to encode captured frames. Always accessed on the encode thread.
  scoped_ptr<VideoEncoder> encoder_;

  // Interfaces through which video frames and cursor shapes are passed to the
  // client. These members are always accessed on the network thread.
  protocol::CursorShapeStub* cursor_stub_;
  protocol::VideoStub* video_stub_;

  // Timer used to schedule CaptureNextFrame().
  scoped_ptr<base::OneShotTimer<VideoScheduler> > capture_timer_;

  // Timer used to ensure that we send empty keep-alive frames to the client
  // even when the video stream is paused or encoder is busy.
  scoped_ptr<base::DelayTimer<VideoScheduler> > keep_alive_timer_;

  // The number of frames being processed, i.e. frames that we are currently
  // capturing, encoding or sending. The value is capped at 2 to minimize
  // latency.
  int pending_frames_;

  // Set when the capturer is capturing a frame.
  bool capture_pending_;

  // True if the previous scheduled capture was skipped.
  bool did_skip_frame_;

  // True if capture of video frames is paused.
  bool is_paused_;

  // Number updated by the caller to trace performance.
  int64 sequence_number_;

  // An object to schedule capturing.
  CaptureScheduler scheduler_;

  DISALLOW_COPY_AND_ASSIGN(VideoScheduler);
};

}  // namespace remoting

#endif  // REMOTING_HOST_VIDEO_SCHEDULER_H_
