summaryrefslogtreecommitdiff
path: root/chromium/media/cast/video_sender/video_sender.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/cast/video_sender/video_sender.cc')
-rw-r--r--chromium/media/cast/video_sender/video_sender.cc346
1 files changed, 346 insertions, 0 deletions
diff --git a/chromium/media/cast/video_sender/video_sender.cc b/chromium/media/cast/video_sender/video_sender.cc
new file mode 100644
index 00000000000..1b422388324
--- /dev/null
+++ b/chromium/media/cast/video_sender/video_sender.cc
@@ -0,0 +1,346 @@
+// 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/video_sender/video_sender.h"
+
+#include <list>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "media/cast/cast_defines.h"
+#include "media/cast/pacing/paced_sender.h"
+#include "media/cast/video_sender/video_encoder.h"
+
+namespace media {
+namespace cast {
+
+const int64 kMinSchedulingDelayMs = 1;
+
+class LocalRtcpVideoSenderFeedback : public RtcpSenderFeedback {
+ public:
+ explicit LocalRtcpVideoSenderFeedback(VideoSender* video_sender)
+ : video_sender_(video_sender) {
+ }
+
+ virtual void OnReceivedSendReportRequest() OVERRIDE {}
+
+ virtual void OnReceivedReportBlock(
+ const RtcpReportBlock& report_block) OVERRIDE {}
+
+ virtual void OnReceivedRpsi(uint8 payload_type,
+ uint64 picture_id) OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void OnReceivedRemb(uint32 bitrate) OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void OnReceivedNackRequest(
+ const std::list<uint16>& nack_sequence_numbers) OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void OnReceivedIntraFrameRequest() OVERRIDE {
+ video_sender_->OnReceivedIntraFrameRequest();
+ }
+
+ virtual void OnReceivedCastFeedback(
+ const RtcpCastMessage& cast_feedback) OVERRIDE {
+ video_sender_->OnReceivedCastFeedback(cast_feedback);
+ }
+
+ private:
+ VideoSender* video_sender_;
+};
+
+class LocalRtpVideoSenderStatistics : public RtpSenderStatistics {
+ public:
+ explicit LocalRtpVideoSenderStatistics(RtpSender* rtp_sender)
+ : rtp_sender_(rtp_sender) {
+ }
+
+ virtual void GetStatistics(const base::TimeTicks& now,
+ RtcpSenderInfo* sender_info) OVERRIDE {
+ rtp_sender_->RtpStatistics(now, sender_info);
+ }
+
+ private:
+ RtpSender* rtp_sender_;
+};
+
+VideoSender::VideoSender(
+ scoped_refptr<CastThread> cast_thread,
+ const VideoSenderConfig& video_config,
+ VideoEncoderController* const video_encoder_controller,
+ PacedPacketSender* const paced_packet_sender)
+ : incoming_feedback_ssrc_(video_config.incoming_feedback_ssrc),
+ rtp_max_delay_(
+ base::TimeDelta::FromMilliseconds(video_config.rtp_max_delay_ms)),
+ max_frame_rate_(video_config.max_frame_rate),
+ cast_thread_(cast_thread),
+ rtcp_feedback_(new LocalRtcpVideoSenderFeedback(this)),
+ rtp_sender_(new RtpSender(NULL, &video_config, paced_packet_sender)),
+ last_acked_frame_id_(-1),
+ last_sent_frame_id_(-1),
+ last_sent_key_frame_id_(-1),
+ duplicate_ack_(0),
+ last_skip_count_(0),
+ congestion_control_(video_config.congestion_control_back_off,
+ video_config.max_bitrate,
+ video_config.min_bitrate,
+ video_config.start_bitrate),
+ clock_(&default_tick_clock_),
+ weak_factory_(this) {
+ max_unacked_frames_ = static_cast<uint8>(video_config.rtp_max_delay_ms *
+ video_config.max_frame_rate / 1000);
+ DCHECK(max_unacked_frames_ > 0) << "Invalid argument";
+
+ rtp_video_sender_statistics_.reset(
+ new LocalRtpVideoSenderStatistics(rtp_sender_.get()));
+
+ if (video_config.use_external_encoder) {
+ DCHECK(video_encoder_controller) << "Invalid argument";
+ video_encoder_controller_ = video_encoder_controller;
+ } else {
+ video_encoder_ = new VideoEncoder(cast_thread, video_config,
+ max_unacked_frames_);
+ video_encoder_controller_ = video_encoder_.get();
+ }
+ rtcp_.reset(new Rtcp(
+ rtcp_feedback_.get(),
+ paced_packet_sender,
+ rtp_video_sender_statistics_.get(),
+ NULL,
+ video_config.rtcp_mode,
+ base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
+ true,
+ video_config.sender_ssrc,
+ video_config.rtcp_c_name));
+
+ rtcp_->SetRemoteSSRC(video_config.incoming_feedback_ssrc);
+ ScheduleNextRtcpReport();
+ ScheduleNextResendCheck();
+ ScheduleNextSkippedFramesCheck();
+}
+
+VideoSender::~VideoSender() {}
+
+void VideoSender::InsertRawVideoFrame(
+ const I420VideoFrame* video_frame,
+ const base::TimeTicks& capture_time,
+ const base::Closure callback) {
+ DCHECK(video_encoder_.get()) << "Invalid state";
+
+ if (!video_encoder_->EncodeVideoFrame(video_frame, capture_time,
+ base::Bind(&VideoSender::SendEncodedVideoFrameMainThread,
+ weak_factory_.GetWeakPtr()), callback)) {
+ VLOG(1) << "Failed to InsertRawVideoFrame";
+ }
+}
+
+void VideoSender::InsertCodedVideoFrame(const EncodedVideoFrame* encoded_frame,
+ const base::TimeTicks& capture_time,
+ const base::Closure callback) {
+ DCHECK(!video_encoder_.get()) << "Invalid state";
+ DCHECK(encoded_frame) << "Invalid argument";
+
+ SendEncodedVideoFrame(encoded_frame, capture_time);
+ callback.Run();
+}
+
+void VideoSender::SendEncodedVideoFrameMainThread(
+ scoped_ptr<EncodedVideoFrame> video_frame,
+ const base::TimeTicks& capture_time) {
+ SendEncodedVideoFrame(video_frame.get(), capture_time);
+}
+
+void VideoSender::SendEncodedVideoFrame(const EncodedVideoFrame* encoded_frame,
+ const base::TimeTicks& capture_time) {
+ last_send_time_ = clock_->NowTicks();
+ rtp_sender_->IncomingEncodedVideoFrame(encoded_frame, capture_time);
+ if (encoded_frame->key_frame) {
+ last_sent_key_frame_id_ = encoded_frame->frame_id;
+ }
+ last_sent_frame_id_ = encoded_frame->frame_id;
+ UpdateFramesInFlight();
+}
+
+void VideoSender::OnReceivedIntraFrameRequest() {
+ if (last_sent_key_frame_id_ != -1) {
+ uint8 frames_in_flight = static_cast<uint8>(last_sent_frame_id_) -
+ static_cast<uint8>(last_sent_key_frame_id_);
+ if (frames_in_flight < (max_unacked_frames_ - 1)) return;
+ }
+ video_encoder_controller_->GenerateKeyFrame();
+ last_acked_frame_id_ = -1;
+ last_sent_frame_id_ = -1;
+}
+
+void VideoSender::IncomingRtcpPacket(const uint8* packet, int length,
+ const base::Closure callback) {
+ rtcp_->IncomingRtcpPacket(packet, length);
+ cast_thread_->PostTask(CastThread::MAIN, FROM_HERE, callback);
+}
+
+void VideoSender::ScheduleNextRtcpReport() {
+ base::TimeDelta time_to_next =
+ rtcp_->TimeToSendNextRtcpReport() - clock_->NowTicks();
+
+ time_to_next = std::max(time_to_next,
+ base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
+
+ cast_thread_->PostDelayedTask(CastThread::MAIN, FROM_HERE,
+ base::Bind(&VideoSender::SendRtcpReport, weak_factory_.GetWeakPtr()),
+ time_to_next);
+}
+
+void VideoSender::SendRtcpReport() {
+ rtcp_->SendRtcpReport(incoming_feedback_ssrc_);
+ ScheduleNextRtcpReport();
+}
+
+void VideoSender::ScheduleNextResendCheck() {
+ base::TimeDelta time_to_next;
+ if (last_send_time_.is_null()) {
+ time_to_next = rtp_max_delay_;
+ } else {
+ time_to_next = last_send_time_ - clock_->NowTicks() + rtp_max_delay_;
+ }
+ time_to_next = std::max(time_to_next,
+ base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
+
+ cast_thread_->PostDelayedTask(CastThread::MAIN, FROM_HERE,
+ base::Bind(&VideoSender::ResendCheck, weak_factory_.GetWeakPtr()),
+ time_to_next);
+}
+
+void VideoSender::ResendCheck() {
+ if (!last_send_time_.is_null() && last_sent_frame_id_ != -1) {
+ base::TimeDelta time_to_next =
+ last_send_time_ - clock_->NowTicks() + rtp_max_delay_;
+
+ if (last_acked_frame_id_ == -1) {
+ // We have not received any ack, send a key frame.
+ video_encoder_controller_->GenerateKeyFrame();
+ last_acked_frame_id_ = -1;
+ last_sent_frame_id_ = -1;
+ UpdateFramesInFlight();
+ } else {
+ ResendFrame(static_cast<uint8>(last_acked_frame_id_ + 1));
+ }
+ }
+ ScheduleNextResendCheck();
+}
+
+void VideoSender::ScheduleNextSkippedFramesCheck() {
+ base::TimeDelta time_to_next;
+ if (last_checked_skip_count_time_.is_null()) {
+ time_to_next =
+ base::TimeDelta::FromMilliseconds(kSkippedFramesCheckPeriodkMs);
+ } else {
+ time_to_next = last_checked_skip_count_time_ - clock_->NowTicks() +
+ base::TimeDelta::FromMilliseconds(kSkippedFramesCheckPeriodkMs);
+ }
+ time_to_next = std::max(time_to_next,
+ base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs));
+
+ cast_thread_->PostDelayedTask(CastThread::MAIN, FROM_HERE,
+ base::Bind(&VideoSender::SkippedFramesCheck, weak_factory_.GetWeakPtr()),
+ time_to_next);
+}
+
+void VideoSender::SkippedFramesCheck() {
+ int skip_count = video_encoder_controller_->NumberOfSkippedFrames();
+ if (skip_count - last_skip_count_ >
+ kSkippedFramesThreshold * max_frame_rate_) {
+ // TODO(pwestin): Propagate this up to the application.
+ }
+ last_skip_count_ = skip_count;
+ last_checked_skip_count_time_ = clock_->NowTicks();
+ ScheduleNextSkippedFramesCheck();
+}
+
+void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
+ base::TimeDelta rtt;
+ base::TimeDelta avg_rtt;
+ base::TimeDelta min_rtt;
+ base::TimeDelta max_rtt;
+
+ if (rtcp_->Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt)) {
+ // Don't use a RTT lower than our average.
+ rtt = std::max(rtt, avg_rtt);
+ } else {
+ // We have no measured value use default.
+ rtt = base::TimeDelta::FromMilliseconds(kStartRttMs);
+ }
+ if (cast_feedback.missing_frames_and_packets_.empty()) {
+ // No lost packets.
+ int resend_frame = -1;
+ if (last_sent_frame_id_ == -1) return;
+
+ video_encoder_controller_->LatestFrameIdToReference(
+ cast_feedback.ack_frame_id_);
+
+ if (static_cast<uint8>(last_acked_frame_id_ + 1) ==
+ cast_feedback.ack_frame_id_) {
+ uint32 new_bitrate = 0;
+ if (congestion_control_.OnAck(rtt, &new_bitrate)) {
+ video_encoder_controller_->SetBitRate(new_bitrate);
+ }
+ }
+ if (last_acked_frame_id_ == cast_feedback.ack_frame_id_ &&
+ // We only count duplicate ACKs when we have sent newer frames.
+ IsNewerFrameId(last_sent_frame_id_, last_acked_frame_id_)) {
+ duplicate_ack_++;
+ } else {
+ duplicate_ack_ = 0;
+ }
+ if (duplicate_ack_ >= 2 && duplicate_ack_ % 3 == 2) {
+ // Resend last ACK + 1 frame.
+ resend_frame = static_cast<uint8>(last_acked_frame_id_ + 1);
+ }
+ if (resend_frame != -1) {
+ ResendFrame(static_cast<uint8>(resend_frame));
+ }
+ } else {
+ rtp_sender_->ResendPackets(cast_feedback.missing_frames_and_packets_);
+ last_send_time_ = clock_->NowTicks();
+
+ uint32 new_bitrate = 0;
+ if (congestion_control_.OnNack(rtt, &new_bitrate)) {
+ video_encoder_controller_->SetBitRate(new_bitrate);
+ }
+ }
+ ReceivedAck(cast_feedback.ack_frame_id_);
+}
+
+void VideoSender::ReceivedAck(uint8 acked_frame_id) {
+ last_acked_frame_id_ = acked_frame_id;
+ UpdateFramesInFlight();
+}
+
+void VideoSender::UpdateFramesInFlight() {
+ if (last_sent_frame_id_ != -1) {
+ uint8 frames_in_flight = static_cast<uint8>(last_sent_frame_id_) -
+ static_cast<uint8>(last_acked_frame_id_);
+ if (frames_in_flight >= max_unacked_frames_) {
+ video_encoder_controller_->SkipNextFrame(true);
+ return;
+ }
+ }
+ video_encoder_controller_->SkipNextFrame(false);
+}
+
+void VideoSender::ResendFrame(uint8 resend_frame_id) {
+ MissingFramesAndPacketsMap missing_frames_and_packets;
+ PacketIdSet missing;
+ missing_frames_and_packets.insert(std::make_pair(resend_frame_id, missing));
+ rtp_sender_->ResendPackets(missing_frames_and_packets);
+ last_send_time_ = clock_->NowTicks();
+}
+
+} // namespace cast
+} // namespace media