Home | History | Annotate | Download | only in sender
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/cast/sender/video_sender.h"
      6 
      7 #include <algorithm>
      8 #include <cstring>
      9 
     10 #include "base/bind.h"
     11 #include "base/debug/trace_event.h"
     12 #include "base/logging.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "media/cast/cast_defines.h"
     15 #include "media/cast/net/cast_transport_config.h"
     16 #include "media/cast/sender/external_video_encoder.h"
     17 #include "media/cast/sender/video_encoder_impl.h"
     18 
     19 namespace media {
     20 namespace cast {
     21 
     22 namespace {
     23 
     24 // The following two constants are used to adjust the target
     25 // playout delay (when allowed). They were calculated using
     26 // a combination of cast_benchmark runs and manual testing.
     27 //
     28 // This is how many round trips we think we need on the network.
     29 const int kRoundTripsNeeded = 4;
     30 // This is an estimate of all the the constant time needed independent of
     31 // network quality (e.g., additional time that accounts for encode and decode
     32 // time).
     33 const int kConstantTimeMs = 75;
     34 
     35 }  // namespace
     36 
     37 // Note, we use a fixed bitrate value when external video encoder is used.
     38 // Some hardware encoder shows bad behavior if we set the bitrate too
     39 // frequently, e.g. quality drop, not abiding by target bitrate, etc.
     40 // See details: crbug.com/392086.
     41 VideoSender::VideoSender(
     42     scoped_refptr<CastEnvironment> cast_environment,
     43     const VideoSenderConfig& video_config,
     44     const CastInitializationCallback& initialization_cb,
     45     const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
     46     const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
     47     CastTransportSender* const transport_sender,
     48     const PlayoutDelayChangeCB& playout_delay_change_cb)
     49     : FrameSender(
     50         cast_environment,
     51         false,
     52         transport_sender,
     53         base::TimeDelta::FromMilliseconds(video_config.rtcp_interval),
     54         kVideoFrequency,
     55         video_config.ssrc,
     56         video_config.max_frame_rate,
     57         video_config.min_playout_delay,
     58         video_config.max_playout_delay,
     59         NewFixedCongestionControl(
     60             (video_config.min_bitrate + video_config.max_bitrate) / 2)),
     61       frames_in_encoder_(0),
     62       last_bitrate_(0),
     63       playout_delay_change_cb_(playout_delay_change_cb),
     64       weak_factory_(this) {
     65   cast_initialization_status_ = STATUS_VIDEO_UNINITIALIZED;
     66   VLOG(1) << "max_unacked_frames is " << max_unacked_frames_
     67           << " for target_playout_delay="
     68           << target_playout_delay_.InMilliseconds() << " ms"
     69           << " and max_frame_rate=" << video_config.max_frame_rate;
     70   DCHECK_GT(max_unacked_frames_, 0);
     71 
     72   if (video_config.use_external_encoder) {
     73     video_encoder_.reset(new ExternalVideoEncoder(
     74         cast_environment,
     75         video_config,
     76         base::Bind(&VideoSender::OnEncoderInitialized,
     77                    weak_factory_.GetWeakPtr(), initialization_cb),
     78         create_vea_cb,
     79         create_video_encode_mem_cb));
     80   } else {
     81     // Software encoder is initialized immediately.
     82     congestion_control_.reset(
     83         NewAdaptiveCongestionControl(cast_environment->Clock(),
     84                                      video_config.max_bitrate,
     85                                      video_config.min_bitrate,
     86                                      max_unacked_frames_));
     87     video_encoder_.reset(new VideoEncoderImpl(
     88         cast_environment, video_config, max_unacked_frames_));
     89     cast_initialization_status_ = STATUS_VIDEO_INITIALIZED;
     90   }
     91 
     92   if (cast_initialization_status_ == STATUS_VIDEO_INITIALIZED) {
     93     cast_environment->PostTask(
     94         CastEnvironment::MAIN,
     95         FROM_HERE,
     96         base::Bind(initialization_cb, cast_initialization_status_));
     97   }
     98 
     99   media::cast::CastTransportRtpConfig transport_config;
    100   transport_config.ssrc = video_config.ssrc;
    101   transport_config.feedback_ssrc = video_config.incoming_feedback_ssrc;
    102   transport_config.rtp_payload_type = video_config.rtp_payload_type;
    103   transport_config.aes_key = video_config.aes_key;
    104   transport_config.aes_iv_mask = video_config.aes_iv_mask;
    105 
    106   transport_sender->InitializeVideo(
    107       transport_config,
    108       base::Bind(&VideoSender::OnReceivedCastFeedback,
    109                  weak_factory_.GetWeakPtr()),
    110       base::Bind(&VideoSender::OnMeasuredRoundTripTime,
    111                  weak_factory_.GetWeakPtr()));
    112 }
    113 
    114 VideoSender::~VideoSender() {
    115 }
    116 
    117 void VideoSender::InsertRawVideoFrame(
    118     const scoped_refptr<media::VideoFrame>& video_frame,
    119     const base::TimeTicks& capture_time) {
    120   DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
    121   if (cast_initialization_status_ != STATUS_VIDEO_INITIALIZED) {
    122     NOTREACHED();
    123     return;
    124   }
    125   DCHECK(video_encoder_.get()) << "Invalid state";
    126 
    127   RtpTimestamp rtp_timestamp = GetVideoRtpTimestamp(capture_time);
    128   cast_environment_->Logging()->InsertFrameEvent(
    129       capture_time, FRAME_CAPTURE_BEGIN, VIDEO_EVENT,
    130       rtp_timestamp, kFrameIdUnknown);
    131   cast_environment_->Logging()->InsertFrameEvent(
    132       cast_environment_->Clock()->NowTicks(),
    133       FRAME_CAPTURE_END, VIDEO_EVENT,
    134       rtp_timestamp,
    135       kFrameIdUnknown);
    136 
    137   // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
    138   TRACE_EVENT_INSTANT2(
    139       "cast_perf_test", "InsertRawVideoFrame",
    140       TRACE_EVENT_SCOPE_THREAD,
    141       "timestamp", capture_time.ToInternalValue(),
    142       "rtp_timestamp", rtp_timestamp);
    143 
    144   // Drop the frame if its reference timestamp is not an increase over the last
    145   // frame's.  This protects: 1) the duration calculations that assume
    146   // timestamps are monotonically non-decreasing, and 2) assumptions made deeper
    147   // in the implementation where each frame's RTP timestamp needs to be unique.
    148   if (!last_enqueued_frame_reference_time_.is_null() &&
    149       capture_time <= last_enqueued_frame_reference_time_) {
    150     VLOG(1) << "Dropping video frame: Reference time did not increase.";
    151     return;
    152   }
    153 
    154   // Two video frames are needed to compute the exact media duration added by
    155   // the next frame.  If there are no frames in the encoder, compute a guess
    156   // based on the configured |max_frame_rate_|.  Any error introduced by this
    157   // guess will be eliminated when |duration_in_encoder_| is updated in
    158   // OnEncodedVideoFrame().
    159   const base::TimeDelta duration_added_by_next_frame = frames_in_encoder_ > 0 ?
    160       capture_time - last_enqueued_frame_reference_time_ :
    161       base::TimeDelta::FromSecondsD(1.0 / max_frame_rate_);
    162 
    163   if (ShouldDropNextFrame(duration_added_by_next_frame)) {
    164     base::TimeDelta new_target_delay = std::min(
    165         current_round_trip_time_ * kRoundTripsNeeded +
    166         base::TimeDelta::FromMilliseconds(kConstantTimeMs),
    167         max_playout_delay_);
    168     if (new_target_delay > target_playout_delay_) {
    169       VLOG(1) << "New target delay: " << new_target_delay.InMilliseconds();
    170       playout_delay_change_cb_.Run(new_target_delay);
    171     }
    172     return;
    173   }
    174 
    175   uint32 bitrate = congestion_control_->GetBitrate(
    176         capture_time + target_playout_delay_, target_playout_delay_);
    177   if (bitrate != last_bitrate_) {
    178     video_encoder_->SetBitRate(bitrate);
    179     last_bitrate_ = bitrate;
    180   }
    181 
    182   if (video_encoder_->EncodeVideoFrame(
    183           video_frame,
    184           capture_time,
    185           base::Bind(&VideoSender::OnEncodedVideoFrame,
    186                      weak_factory_.GetWeakPtr(),
    187                      bitrate))) {
    188     frames_in_encoder_++;
    189     duration_in_encoder_ += duration_added_by_next_frame;
    190     last_enqueued_frame_reference_time_ = capture_time;
    191   } else {
    192     VLOG(1) << "Encoder rejected a frame.  Skipping...";
    193   }
    194 }
    195 
    196 int VideoSender::GetNumberOfFramesInEncoder() const {
    197   return frames_in_encoder_;
    198 }
    199 
    200 base::TimeDelta VideoSender::GetInFlightMediaDuration() const {
    201   if (GetUnacknowledgedFrameCount() > 0) {
    202     const uint32 oldest_unacked_frame_id = latest_acked_frame_id_ + 1;
    203     return last_enqueued_frame_reference_time_ -
    204         GetRecordedReferenceTime(oldest_unacked_frame_id);
    205   } else {
    206     return duration_in_encoder_;
    207   }
    208 }
    209 
    210 void VideoSender::OnAck(uint32 frame_id) {
    211   video_encoder_->LatestFrameIdToReference(frame_id);
    212 }
    213 
    214 void VideoSender::OnEncoderInitialized(
    215     const CastInitializationCallback& initialization_cb,
    216     CastInitializationStatus status) {
    217   cast_initialization_status_ = status;
    218   initialization_cb.Run(status);
    219 }
    220 
    221 void VideoSender::OnEncodedVideoFrame(
    222     int encoder_bitrate,
    223     scoped_ptr<EncodedFrame> encoded_frame) {
    224   DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
    225 
    226   frames_in_encoder_--;
    227   DCHECK_GE(frames_in_encoder_, 0);
    228 
    229   duration_in_encoder_ =
    230       last_enqueued_frame_reference_time_ - encoded_frame->reference_time;
    231 
    232   SendEncodedFrame(encoder_bitrate, encoded_frame.Pass());
    233 }
    234 
    235 }  // namespace cast
    236 }  // namespace media
    237