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

#ifndef COMPONENTS_MIRRORING_SERVICE_REMOTING_SENDER_H_
#define COMPONENTS_MIRRORING_SERVICE_REMOTING_SENDER_H_

#include <memory>

#include "base/component_export.h"
#include "base/containers/queue.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "media/cast/sender/frame_sender.h"
#include "media/mojo/mojom/remoting.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"

namespace base {
class TickClock;
}  // namespace base

namespace media {
class DecoderBuffer;
}  // namespace media

namespace media::cast {
class DecoderBufferReader;
}  // namespace media::cast

namespace openscreen::cast {
class Sender;
}  // namespace openscreen::cast

namespace mirroring {

// RTP sender for a single Cast Remoting RTP stream. The client calls Send() to
// instruct the sender to read from a Mojo data pipe and transmit the data using
// a CastTransport.
class COMPONENT_EXPORT(MIRRORING_SERVICE) RemotingSender final
    : public media::mojom::RemotingDataStreamSender,
      public media::cast::FrameSender::Client {
 public:
  // New way of instantiating using an openscreen::cast::Sender. Since the
  // |Sender| instance is destroyed when renegotiation is complete, |this|
  // is also invalid and should be immediately torn down.
  RemotingSender(scoped_refptr<media::cast::CastEnvironment> cast_environment,
                 std::unique_ptr<openscreen::cast::Sender> sender,
                 const media::cast::FrameSenderConfig& config,
                 mojo::ScopedDataPipeConsumerHandle pipe,
                 mojo::PendingReceiver<media::mojom::RemotingDataStreamSender>
                     stream_sender,
                 base::OnceClosure error_callback);

  RemotingSender(const RemotingSender&) = delete;
  RemotingSender& operator=(const RemotingSender&) = delete;

  ~RemotingSender() override;

 private:
  // Ctor that takes a media::cast::FrameSender for unit tests.
  // TODO(issues.chromium.org/329781397): Remove unnecessary wrapper objects in
  // Chrome's implementation of the Cast sender.
  RemotingSender(scoped_refptr<media::cast::CastEnvironment> cast_environment,
                 std::unique_ptr<media::cast::FrameSender> sender,
                 const media::cast::FrameSenderConfig& config,
                 mojo::ScopedDataPipeConsumerHandle pipe,
                 mojo::PendingReceiver<media::mojom::RemotingDataStreamSender>
                     stream_sender,
                 base::OnceClosure error_callback);

  // Friend class for unit tests.
  friend class RemotingSenderTest;

  // Creates SenderEncodedFrames.
  class SenderEncodedFrameFactory;

  // media::mojom::RemotingDataStreamSender implementation.
  void SendFrame(media::mojom::DecoderBufferPtr buffer,
                 SendFrameCallback callback) override;
  void CancelInFlightData() override;

  // FrameSender::Client overrides.
  int GetNumberOfFramesInEncoder() const override;
  base::TimeDelta GetEncoderBacklogDuration() const override;
  void OnFrameCanceled(media::cast::FrameId frame_id) override;

  // Sends out the frame to the receiver over network if |frame_sender_| has
  // available space to handle it.
  void TrySendFrame();

  // Sets |next_frame_| once it has been read from the data pipe.
  void OnFrameRead(scoped_refptr<media::DecoderBuffer> buffer);

  // Called when |stream_sender_| is disconnected.
  void OnRemotingDataStreamError();

  // Clears the current frame and requests a new one be sent.
  void ClearCurrentFrame();

  // Returns true if OnRemotingDataStreamError was called.
  bool HadError() const {
    DCHECK_EQ(!decoder_buffer_reader_, !stream_sender_.is_bound());
    return !decoder_buffer_reader_;
  }

  SEQUENCE_CHECKER(sequence_checker_);

  // The backing frame sender implementation.
  std::unique_ptr<media::cast::FrameSender> frame_sender_;

  raw_ref<const base::TickClock> clock_;

  // Callback that is run to notify when a fatal error occurs.
  base::OnceClosure error_callback_;

  // Reads media::DecoderBuffer instances and passes them to OnFrameRead().
  std::unique_ptr<media::cast::DecoderBufferReader> decoder_buffer_reader_;

  // Mojo receiver for this instance. Implementation at the other end of the
  // message pipe uses the RemotingDataStreamSender remote to control when
  // this RemotingSender consumes from |pipe_|.
  mojo::Receiver<media::mojom::RemotingDataStreamSender> stream_sender_;

  // Whether this is an audio sender (true) or a video sender (false).
  const bool is_audio_;

  // Responsible for creating encoded frames.
  std::unique_ptr<SenderEncodedFrameFactory> frame_factory_;

  // The next frame. Populated by call to OnFrameRead() when reading succeeded.
  scoped_refptr<media::DecoderBuffer> next_frame_;

  // To be called once a frame has been successfully read and this instance is
  // ready to process a new one.
  SendFrameCallback read_complete_cb_;

  // Set to true if the first frame has not yet been sent, or if a
  // CancelInFlightData() operation just completed. This causes TrySendFrame()
  // to mark the next frame as the start of a new sequence.
  bool flow_restart_pending_ = true;

  // Number of EnqueueFrame() calls that have failed since the last successful
  // call.
  int consecutive_enqueue_frame_failure_count_ = 0;

  // The next frame's ID. Before any frames are sent, this will be the ID of
  // the first frame.
  media::cast::FrameId next_frame_id_ = media::cast::FrameId::first();

  // NOTE: Weak pointers must be invalidated before all other member variables.
  base::WeakPtrFactory<RemotingSender> weak_factory_{this};
};

}  // namespace mirroring

#endif  // COMPONENTS_MIRRORING_SERVICE_REMOTING_SENDER_H_
