// Copyright 2013 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.
#include "media/cast/cast_sender_impl.h"

#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "media/base/video_frame.h"

namespace media {
namespace cast {

// The LocalFrameInput class posts all incoming frames; audio and video to the
// main cast thread for processing.
// This make the cast sender interface thread safe.
class LocalFrameInput : public FrameInput {
 public:
  LocalFrameInput(scoped_refptr<CastEnvironment> cast_environment,
                  base::WeakPtr<AudioSender> audio_sender,
                  base::WeakPtr<VideoSender> video_sender)
     : cast_environment_(cast_environment),
       audio_sender_(audio_sender),
       video_sender_(video_sender) {}

  virtual void InsertRawVideoFrame(
      const scoped_refptr<media::VideoFrame>& video_frame,
      const base::TimeTicks& capture_time) OVERRIDE {
    cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
        base::Bind(&VideoSender::InsertRawVideoFrame, video_sender_,
            video_frame, capture_time));
  }

  virtual void InsertCodedVideoFrame(const EncodedVideoFrame* video_frame,
                                     const base::TimeTicks& capture_time,
                                     const base::Closure callback) OVERRIDE {
    cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
        base::Bind(&VideoSender::InsertCodedVideoFrame, video_sender_,
            video_frame, capture_time, callback));
  }

  virtual void InsertAudio(const AudioBus* audio_bus,
                           const base::TimeTicks& recorded_time,
                           const base::Closure& done_callback) OVERRIDE {
    cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
        base::Bind(&AudioSender::InsertAudio, audio_sender_,
            audio_bus, recorded_time, done_callback));
  }

  virtual void InsertCodedAudioFrame(const EncodedAudioFrame* audio_frame,
                                     const base::TimeTicks& recorded_time,
                                     const base::Closure callback) OVERRIDE {
    cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
        base::Bind(&AudioSender::InsertCodedAudioFrame, audio_sender_,
            audio_frame, recorded_time, callback));
  }

 protected:
  virtual ~LocalFrameInput() {}

 private:
  friend class base::RefCountedThreadSafe<LocalFrameInput>;

  scoped_refptr<CastEnvironment> cast_environment_;
  base::WeakPtr<AudioSender> audio_sender_;
  base::WeakPtr<VideoSender> video_sender_;
};

// LocalCastSenderPacketReceiver handle the incoming packets to the cast sender
// it's only expected to receive RTCP feedback packets from the remote cast
// receiver. The class verifies that that it is a RTCP packet and based on the
// SSRC of the incoming packet route the packet to the correct sender; audio or
// video.
//
// Definition of SSRC as defined in RFC 3550.
// Synchronization source (SSRC): The source of a stream of RTP
//    packets, identified by a 32-bit numeric SSRC identifier carried in
//    the RTP header so as not to be dependent upon the network address.
//    All packets from a synchronization source form part of the same
//    timing and sequence number space, so a receiver groups packets by
//    synchronization source for playback.  Examples of synchronization
//    sources include the sender of a stream of packets derived from a
//    signal source such as a microphone or a camera, or an RTP mixer
//    (see below).  A synchronization source may change its data format,
//    e.g., audio encoding, over time.  The SSRC identifier is a
//    randomly chosen value meant to be globally unique within a
//    particular RTP session (see Section 8).  A participant need not
//    use the same SSRC identifier for all the RTP sessions in a
//    multimedia session; the binding of the SSRC identifiers is
//    provided through RTCP (see Section 6.5.1).  If a participant
//    generates multiple streams in one RTP session, for example from
//    separate video cameras, each MUST be identified as a different
//    SSRC.

class LocalCastSenderPacketReceiver : public PacketReceiver {
 public:
  LocalCastSenderPacketReceiver(scoped_refptr<CastEnvironment> cast_environment,
                                base::WeakPtr<AudioSender> audio_sender,
                                base::WeakPtr<VideoSender> video_sender,
                                uint32 ssrc_of_audio_sender,
                                uint32 ssrc_of_video_sender)
      : cast_environment_(cast_environment),
        audio_sender_(audio_sender),
        video_sender_(video_sender),
        ssrc_of_audio_sender_(ssrc_of_audio_sender),
        ssrc_of_video_sender_(ssrc_of_video_sender) {}

  virtual void ReceivedPacket(const uint8* packet,
                              size_t length,
                              const base::Closure callback) OVERRIDE {
    if (!Rtcp::IsRtcpPacket(packet, length)) {
      // We should have no incoming RTP packets.
      // No action; just log and call the callback informing that we are done
      // with the packet.
      VLOG(1) << "Unexpectedly received a RTP packet in the cast sender";
      cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
      return;
    }
    uint32 ssrc_of_sender = Rtcp::GetSsrcOfSender(packet, length);
    if (ssrc_of_sender == ssrc_of_audio_sender_) {
      cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
          base::Bind(&AudioSender::IncomingRtcpPacket, audio_sender_,
              packet, length, callback));
    } else if (ssrc_of_sender == ssrc_of_video_sender_) {
      cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
          base::Bind(&VideoSender::IncomingRtcpPacket, video_sender_,
              packet, length, callback));
    } else {
      // No action; just log and call the callback informing that we are done
      // with the packet.
      VLOG(1) << "Received a RTCP packet with a non matching sender SSRC "
              << ssrc_of_sender;

      cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, callback);
    }
  }

 protected:
  virtual ~LocalCastSenderPacketReceiver() {}

 private:
  friend class base::RefCountedThreadSafe<LocalCastSenderPacketReceiver>;

  scoped_refptr<CastEnvironment> cast_environment_;
  base::WeakPtr<AudioSender> audio_sender_;
  base::WeakPtr<VideoSender> video_sender_;
  const uint32 ssrc_of_audio_sender_;
  const uint32 ssrc_of_video_sender_;
};

CastSender* CastSender::CreateCastSender(
    scoped_refptr<CastEnvironment> cast_environment,
    const AudioSenderConfig& audio_config,
    const VideoSenderConfig& video_config,
    VideoEncoderController* const video_encoder_controller,
    PacketSender* const packet_sender) {
  return new CastSenderImpl(cast_environment,
                            audio_config,
                            video_config,
                            video_encoder_controller,
                            packet_sender);
}

CastSenderImpl::CastSenderImpl(
    scoped_refptr<CastEnvironment> cast_environment,
    const AudioSenderConfig& audio_config,
    const VideoSenderConfig& video_config,
    VideoEncoderController* const video_encoder_controller,
    PacketSender* const packet_sender)
    : pacer_(cast_environment, packet_sender),
      audio_sender_(cast_environment, audio_config, &pacer_),
      video_sender_(cast_environment, video_config, video_encoder_controller,
                    &pacer_),
      frame_input_(new LocalFrameInput(cast_environment,
                                       audio_sender_.AsWeakPtr(),
                                       video_sender_.AsWeakPtr())),
      packet_receiver_(new LocalCastSenderPacketReceiver(cast_environment,
          audio_sender_.AsWeakPtr(), video_sender_.AsWeakPtr(),
          audio_config.incoming_feedback_ssrc,
          video_config.incoming_feedback_ssrc)) {}

CastSenderImpl::~CastSenderImpl() {}

scoped_refptr<FrameInput> CastSenderImpl::frame_input() {
  return frame_input_;
}

scoped_refptr<PacketReceiver> CastSenderImpl::packet_receiver() {
  return packet_receiver_;
}

}  // namespace cast
}  // namespace media