blob: 658cd495ba28181165516ab514da4c0702b0f63a [file] [log] [blame]
pbos@webrtc.org29d58392013-05-16 12:08:03 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000011#include "webrtc/video/video_send_stream.h"
pbos@webrtc.org29d58392013-05-16 12:08:03 +000012
pbos@webrtc.orgdde16f12014-08-05 23:35:43 +000013#include <algorithm>
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000014#include <sstream>
henrik.lundin@webrtc.orgba975e22013-10-23 11:04:57 +000015#include <string>
pbos@webrtc.org29d58392013-05-16 12:08:03 +000016#include <vector>
17
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +000018#include "webrtc/base/checks.h"
Peter Boström415d2cd2015-10-26 11:35:17 +010019#include "webrtc/base/logging.h"
tommie4f96502015-10-20 23:00:48 -070020#include "webrtc/base/trace_event.h"
pbos@webrtc.org29d58392013-05-16 12:08:03 +000021#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
mflodman0e7e2592015-11-12 21:02:42 -080022#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
Stefan Holmer80e12072016-02-23 13:30:42 +010023#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
Henrik Kjellander0b9e29c2015-11-16 11:12:24 +010024#include "webrtc/modules/pacing/packet_router.h"
Peter Boström9c017252016-02-26 16:26:20 +010025#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
Peter Boströme4499152016-02-05 11:13:28 +010026#include "webrtc/modules/utility/include/process_thread.h"
Peter Boström7623ce42015-12-09 12:13:30 +010027#include "webrtc/video/call_stats.h"
Peter Boström4b91bd02015-06-26 06:58:16 +020028#include "webrtc/video/video_capture_input.h"
Stefan Holmer58c664c2016-02-08 14:31:30 +010029#include "webrtc/video/vie_remb.h"
pbos@webrtc.org16e03b72013-10-28 16:32:01 +000030#include "webrtc/video_send_stream.h"
pbos@webrtc.org29d58392013-05-16 12:08:03 +000031
32namespace webrtc {
mflodman949c2f02015-10-16 02:31:11 -070033
mflodman949c2f02015-10-16 02:31:11 -070034class RtcpIntraFrameObserver;
35class TransportFeedbackObserver;
36
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000037std::string
pbos@webrtc.org024e4d52014-05-15 10:03:24 +000038VideoSendStream::Config::EncoderSettings::ToString() const {
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000039 std::stringstream ss;
40 ss << "{payload_name: " << payload_name;
41 ss << ", payload_type: " << payload_type;
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +000042 ss << ", encoder: " << (encoder != nullptr ? "(VideoEncoder)" : "nullptr");
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000043 ss << '}';
44 return ss.str();
45}
46
pbos@webrtc.org024e4d52014-05-15 10:03:24 +000047std::string VideoSendStream::Config::Rtp::Rtx::ToString()
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000048 const {
49 std::stringstream ss;
pbos@webrtc.org32e85282015-01-15 10:09:39 +000050 ss << "{ssrcs: [";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000051 for (size_t i = 0; i < ssrcs.size(); ++i) {
52 ss << ssrcs[i];
53 if (i != ssrcs.size() - 1)
pbos@webrtc.org32e85282015-01-15 10:09:39 +000054 ss << ", ";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000055 }
pbos@webrtc.org32e85282015-01-15 10:09:39 +000056 ss << ']';
andrew@webrtc.org8f27fcc2015-01-09 20:22:46 +000057
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000058 ss << ", payload_type: " << payload_type;
59 ss << '}';
60 return ss.str();
61}
62
pbos@webrtc.org024e4d52014-05-15 10:03:24 +000063std::string VideoSendStream::Config::Rtp::ToString() const {
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000064 std::stringstream ss;
pbos@webrtc.org32e85282015-01-15 10:09:39 +000065 ss << "{ssrcs: [";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000066 for (size_t i = 0; i < ssrcs.size(); ++i) {
67 ss << ssrcs[i];
68 if (i != ssrcs.size() - 1)
pbos@webrtc.org32e85282015-01-15 10:09:39 +000069 ss << ", ";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000070 }
pbos@webrtc.org32e85282015-01-15 10:09:39 +000071 ss << ']';
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000072 ss << ", max_packet_size: " << max_packet_size;
pbos@webrtc.org32e85282015-01-15 10:09:39 +000073 ss << ", extensions: [";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000074 for (size_t i = 0; i < extensions.size(); ++i) {
75 ss << extensions[i].ToString();
76 if (i != extensions.size() - 1)
pbos@webrtc.org32e85282015-01-15 10:09:39 +000077 ss << ", ";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000078 }
pbos@webrtc.org32e85282015-01-15 10:09:39 +000079 ss << ']';
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000080
pbos@webrtc.org32e85282015-01-15 10:09:39 +000081 ss << ", nack: {rtp_history_ms: " << nack.rtp_history_ms << '}';
82 ss << ", fec: " << fec.ToString();
83 ss << ", rtx: " << rtx.ToString();
84 ss << ", c_name: " << c_name;
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000085 ss << '}';
86 return ss.str();
87}
88
pbos@webrtc.org024e4d52014-05-15 10:03:24 +000089std::string VideoSendStream::Config::ToString() const {
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000090 std::stringstream ss;
91 ss << "{encoder_settings: " << encoder_settings.ToString();
92 ss << ", rtp: " << rtp.ToString();
pbos@webrtc.org32e85282015-01-15 10:09:39 +000093 ss << ", pre_encode_callback: "
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +000094 << (pre_encode_callback != nullptr ? "(I420FrameCallback)" : "nullptr");
95 ss << ", post_encode_callback: " << (post_encode_callback != nullptr
96 ? "(EncodedFrameObserver)"
97 : "nullptr");
solenberg566ef242015-11-06 15:34:49 -080098 ss << ", local_renderer: " << (local_renderer != nullptr ? "(VideoRenderer)"
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +000099 : "nullptr");
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000100 ss << ", render_delay_ms: " << render_delay_ms;
101 ss << ", target_delay_ms: " << target_delay_ms;
102 ss << ", suspend_below_min_bitrate: " << (suspend_below_min_bitrate ? "on"
103 : "off");
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000104 ss << '}';
105 return ss.str();
106}
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000107
Peter Boströme4499152016-02-05 11:13:28 +0100108namespace {
109
Peter Boström39593972016-02-15 11:27:15 +0100110VideoCodecType PayloadNameToCodecType(const std::string& payload_name) {
111 if (payload_name == "VP8")
112 return kVideoCodecVP8;
113 if (payload_name == "VP9")
114 return kVideoCodecVP9;
115 if (payload_name == "H264")
116 return kVideoCodecH264;
117 return kVideoCodecGeneric;
118}
119
120bool PayloadTypeSupportsSkippingFecPackets(const std::string& payload_name) {
121 switch (PayloadNameToCodecType(payload_name)) {
122 case kVideoCodecVP8:
123 case kVideoCodecVP9:
124 return true;
125 case kVideoCodecH264:
126 case kVideoCodecGeneric:
127 return false;
128 case kVideoCodecI420:
129 case kVideoCodecRED:
130 case kVideoCodecULPFEC:
131 case kVideoCodecUnknown:
132 RTC_NOTREACHED();
133 return false;
134 }
135 RTC_NOTREACHED();
136 return false;
137}
138
Peter Boström23353ab2016-02-24 15:19:55 +0100139// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
140// pipelining encoders better (multiple input frames before something comes
141// out). This should effectively turn off CPU adaptations for systems that
142// remotely cope with the load right now.
Peter Boströme4499152016-02-05 11:13:28 +0100143CpuOveruseOptions GetCpuOveruseOptions(bool full_overuse_time) {
144 CpuOveruseOptions options;
145 if (full_overuse_time) {
Peter Boström23353ab2016-02-24 15:19:55 +0100146 options.low_encode_usage_threshold_percent = 150;
147 options.high_encode_usage_threshold_percent = 200;
Peter Boströme4499152016-02-05 11:13:28 +0100148 }
149 return options;
150}
151} // namespace
152
pbos@webrtc.org024e4d52014-05-15 10:03:24 +0000153namespace internal {
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000154VideoSendStream::VideoSendStream(
Peter Boström45553ae2015-05-08 13:54:38 +0200155 int num_cpu_cores,
Peter Boströmf16fcbe2015-04-30 12:16:05 +0200156 ProcessThread* module_process_thread,
mflodmane3787022015-10-21 13:24:28 +0200157 CallStats* call_stats,
mflodman0c478b32015-10-21 15:52:16 +0200158 CongestionController* congestion_controller,
mflodman0e7e2592015-11-12 21:02:42 -0800159 BitrateAllocator* bitrate_allocator,
mflodman86aabb22016-03-11 15:44:32 +0100160 VieRemb* remb,
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000161 const VideoSendStream::Config& config,
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000162 const VideoEncoderConfig& encoder_config,
Peter Boström45553ae2015-05-08 13:54:38 +0200163 const std::map<uint32_t, RtpState>& suspended_ssrcs)
sprangb4a1ae52015-12-03 08:10:08 -0800164 : stats_proxy_(Clock::GetRealTimeClock(),
165 config,
166 encoder_config.content_type),
sprang@webrtc.org40709352013-11-26 11:41:59 +0000167 encoded_frame_proxy_(config.post_encode_callback),
pbos@webrtc.org64887612013-11-14 08:58:14 +0000168 config_(config),
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000169 suspended_ssrcs_(suspended_ssrcs),
Peter Boströmf16fcbe2015-04-30 12:16:05 +0200170 module_process_thread_(module_process_thread),
mflodmane3787022015-10-21 13:24:28 +0200171 call_stats_(call_stats),
mflodman0c478b32015-10-21 15:52:16 +0200172 congestion_controller_(congestion_controller),
mflodman86aabb22016-03-11 15:44:32 +0100173 bitrate_allocator_(bitrate_allocator),
Stefan Holmer58c664c2016-02-08 14:31:30 +0100174 remb_(remb),
Peter Boströma4c76882016-03-03 16:29:02 +0100175 encoder_thread_(EncoderThreadFunction, this, "EncoderThread"),
176 encoder_wakeup_event_(false, false),
177 stop_encoder_thread_(0),
Peter Boströme4499152016-02-05 11:13:28 +0100178 overuse_detector_(
179 Clock::GetRealTimeClock(),
180 GetCpuOveruseOptions(config.encoder_settings.full_overuse_time),
181 this,
182 config.post_encode_callback,
183 &stats_proxy_),
Peter Boström8c66a002016-02-11 13:51:10 +0100184 vie_channel_(config.send_transport,
185 module_process_thread_,
186 &payload_router_,
187 nullptr,
Peter Boström45c44f02016-02-19 17:36:01 +0100188 &encoder_feedback_,
Peter Boström8c66a002016-02-11 13:51:10 +0100189 congestion_controller_->GetBitrateController()
190 ->CreateRtcpBandwidthObserver(),
191 congestion_controller_->GetTransportFeedbackObserver(),
192 nullptr,
193 call_stats_->rtcp_rtt_stats(),
194 congestion_controller_->pacer(),
195 congestion_controller_->packet_router(),
196 config_.rtp.ssrcs.size(),
197 true),
Peter Boström1f7d77f2016-02-11 15:30:14 +0100198 vie_receiver_(vie_channel_.vie_receiver()),
Peter Boström579e8322016-02-12 16:30:04 +0100199 vie_encoder_(num_cpu_cores,
Peter Boström0013dcc2016-02-19 20:42:19 +0100200 config_.rtp.ssrcs,
Peter Boström579e8322016-02-12 16:30:04 +0100201 module_process_thread_,
202 &stats_proxy_,
203 config.pre_encode_callback,
204 &overuse_detector_,
205 congestion_controller_->pacer(),
mflodman86aabb22016-03-11 15:44:32 +0100206 &payload_router_),
Peter Boström579e8322016-02-12 16:30:04 +0100207 vcm_(vie_encoder_.vcm()),
Peter Boström723ead82016-02-22 15:14:01 +0100208 rtp_rtcp_modules_(vie_channel_.rtp_rtcp()),
Peter Boströma4c76882016-03-03 16:29:02 +0100209 input_(&encoder_wakeup_event_,
Peter Boström8c66a002016-02-11 13:51:10 +0100210 config_.local_renderer,
211 &stats_proxy_,
212 &overuse_detector_) {
pbosa2f30de2015-10-15 05:22:13 -0700213 LOG(LS_INFO) << "VideoSendStream: " << config_.ToString();
Stefan Holmer58c664c2016-02-08 14:31:30 +0100214
henrikg91d6ede2015-09-17 00:24:34 -0700215 RTC_DCHECK(!config_.rtp.ssrcs.empty());
Stefan Holmer58c664c2016-02-08 14:31:30 +0100216 RTC_DCHECK(module_process_thread_);
217 RTC_DCHECK(call_stats_);
218 RTC_DCHECK(congestion_controller_);
219 RTC_DCHECK(remb_);
mflodman949c2f02015-10-16 02:31:11 -0700220
Peter Boström8b79b072016-02-26 16:31:37 +0100221 payload_router_.Init(rtp_rtcp_modules_);
Peter Boström8c66a002016-02-11 13:51:10 +0100222 RTC_CHECK(vie_encoder_.Init());
Peter Boström45c44f02016-02-19 17:36:01 +0100223 encoder_feedback_.Init(config_.rtp.ssrcs, &vie_encoder_);
Peter Boström8c66a002016-02-11 13:51:10 +0100224 RTC_CHECK(vie_channel_.Init() == 0);
mflodman949c2f02015-10-16 02:31:11 -0700225
Peter Boström8c66a002016-02-11 13:51:10 +0100226 vcm_->RegisterProtectionCallback(vie_channel_.vcm_protection_callback());
mflodman949c2f02015-10-16 02:31:11 -0700227
Peter Boström8c66a002016-02-11 13:51:10 +0100228 call_stats_->RegisterStatsObserver(vie_channel_.GetStatsObserver());
mflodman949c2f02015-10-16 02:31:11 -0700229
pbos@webrtc.org29023282013-09-11 10:14:56 +0000230 for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
231 const std::string& extension = config_.rtp.extensions[i].name;
232 int id = config_.rtp.extensions[i].id;
Peter Boström23914fe2015-03-31 15:08:04 +0200233 // One-byte-extension local identifiers are in the range 1-14 inclusive.
henrikg91d6ede2015-09-17 00:24:34 -0700234 RTC_DCHECK_GE(id, 1);
235 RTC_DCHECK_LE(id, 14);
Peter Boström9c017252016-02-26 16:26:20 +0100236 RTC_DCHECK(RtpExtension::IsSupportedForVideo(extension));
237 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
238 RTC_CHECK_EQ(0, rtp_rtcp->RegisterSendRtpHeaderExtension(
239 StringToRtpExtensionType(extension), id));
pbos@webrtc.org29023282013-09-11 10:14:56 +0000240 }
241 }
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000242
Peter Boström723ead82016-02-22 15:14:01 +0100243 remb_->AddRembSender(rtp_rtcp_modules_[0]);
244 rtp_rtcp_modules_[0]->SetREMBStatus(true);
mflodman@webrtc.org92c27932013-12-13 16:36:28 +0000245
pbos@webrtc.org0e63e762013-09-20 11:56:26 +0000246 // Enable NACK, FEC or both.
pbosba8c15b2015-07-14 09:36:34 -0700247 const bool enable_protection_nack = config_.rtp.nack.rtp_history_ms > 0;
Peter Boström39593972016-02-15 11:27:15 +0100248 bool enable_protection_fec = config_.rtp.fec.red_payload_type != -1;
249 // Payload types without picture ID cannot determine that a stream is complete
250 // without retransmitting FEC, so using FEC + NACK for H.264 (for instance) is
251 // a waste of bandwidth since FEC packets still have to be transmitted. Note
252 // that this is not the case with FLEXFEC.
253 if (enable_protection_nack &&
254 !PayloadTypeSupportsSkippingFecPackets(
255 config_.encoder_settings.payload_name)) {
256 LOG(LS_WARNING) << "Transmitting payload type without picture ID using"
257 "NACK+FEC is a waste of bandwidth since FEC packets "
258 "also have to be retransmitted. Disabling FEC.";
259 enable_protection_fec = false;
260 }
pbosba8c15b2015-07-14 09:36:34 -0700261 // TODO(changbin): Should set RTX for RED mapping in RTP sender in future.
Peter Boström8c66a002016-02-11 13:51:10 +0100262 vie_channel_.SetProtectionMode(enable_protection_nack, enable_protection_fec,
Peter Boström723ead82016-02-22 15:14:01 +0100263 config_.rtp.fec.red_payload_type,
264 config_.rtp.fec.ulpfec_payload_type);
Peter Boström8c66a002016-02-11 13:51:10 +0100265 vie_encoder_.SetProtectionMethod(enable_protection_nack,
Peter Boström723ead82016-02-22 15:14:01 +0100266 enable_protection_fec);
pbos@webrtc.org0e63e762013-09-20 11:56:26 +0000267
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000268 ConfigureSsrcs();
269
Peter Boström723ead82016-02-22 15:14:01 +0100270 // TODO(pbos): Should we set CNAME on all RTP modules?
271 rtp_rtcp_modules_.front()->SetCNAME(config_.rtp.c_name.c_str());
sprang@webrtc.org25fce9a2013-10-16 13:29:14 +0000272 // 28 to match packet overhead in ModuleRtpRtcpImpl.
Peter Boström723ead82016-02-22 15:14:01 +0100273 static const size_t kRtpPacketSizeOverhead = 28;
274 RTC_DCHECK_LE(config_.rtp.max_packet_size, 0xFFFFu + kRtpPacketSizeOverhead);
275 const uint16_t mtu = static_cast<uint16_t>(config_.rtp.max_packet_size +
276 kRtpPacketSizeOverhead);
277 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
278 rtp_rtcp->RegisterRtcpStatisticsCallback(&stats_proxy_);
279 rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(&stats_proxy_);
280 rtp_rtcp->SetMaxTransferUnit(mtu);
Peter Boström8b79b072016-02-26 16:31:37 +0100281 rtp_rtcp->RegisterVideoSendPayload(
282 config_.encoder_settings.payload_type,
283 config_.encoder_settings.payload_name.c_str());
Peter Boström723ead82016-02-22 15:14:01 +0100284 }
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000285
henrikg91d6ede2015-09-17 00:24:34 -0700286 RTC_DCHECK(config.encoder_settings.encoder != nullptr);
287 RTC_DCHECK_GE(config.encoder_settings.payload_type, 0);
288 RTC_DCHECK_LE(config.encoder_settings.payload_type, 127);
Peter Boström8c66a002016-02-11 13:51:10 +0100289 RTC_CHECK_EQ(0, vie_encoder_.RegisterExternalEncoder(
henrikg91d6ede2015-09-17 00:24:34 -0700290 config.encoder_settings.encoder,
291 config.encoder_settings.payload_type,
292 config.encoder_settings.internal_source));
stefan@webrtc.org360e3762013-08-22 09:29:56 +0000293
Peter Boström905f8e72016-03-02 16:59:56 +0100294 ReconfigureVideoEncoder(encoder_config);
pbos@webrtc.orgfe1ef932013-10-21 10:34:43 +0000295
Peter Boström8c66a002016-02-11 13:51:10 +0100296 vie_channel_.RegisterSendSideDelayObserver(&stats_proxy_);
stefan@webrtc.org168f23f2014-07-11 13:44:02 +0000297
Peter Boström94cc1fe2015-04-29 14:08:41 +0200298 if (config_.post_encode_callback)
Peter Boström8c66a002016-02-11 13:51:10 +0100299 vie_encoder_.RegisterPostEncodeImageCallback(&encoded_frame_proxy_);
henrik.lundin@webrtc.orgba975e22013-10-23 11:04:57 +0000300
mflodman86aabb22016-03-11 15:44:32 +0100301 if (config_.suspend_below_min_bitrate) {
302 vcm_->SuspendBelowMinBitrate();
303 bitrate_allocator_->EnforceMinBitrate(false);
304 }
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000305
Peter Boström8c66a002016-02-11 13:51:10 +0100306 vie_channel_.RegisterRtcpPacketTypeCounterObserver(&stats_proxy_);
307 vie_channel_.RegisterSendBitrateObserver(&stats_proxy_);
308 vie_channel_.RegisterSendFrameCountObserver(&stats_proxy_);
Peter Boströme4499152016-02-05 11:13:28 +0100309
310 module_process_thread_->RegisterModule(&overuse_detector_);
Peter Boströma4c76882016-03-03 16:29:02 +0100311
312 encoder_thread_.Start();
313 encoder_thread_.SetPriority(rtc::kHighPriority);
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000314}
315
316VideoSendStream::~VideoSendStream() {
pbosa2f30de2015-10-15 05:22:13 -0700317 LOG(LS_INFO) << "~VideoSendStream: " << config_.ToString();
mflodman86aabb22016-03-11 15:44:32 +0100318
319 bitrate_allocator_->RemoveObserver(this);
Peter Boströmca835252016-02-11 15:59:46 +0100320 Stop();
321
Peter Boströma4c76882016-03-03 16:29:02 +0100322 // Stop the encoder thread permanently.
323 rtc::AtomicOps::ReleaseStore(&stop_encoder_thread_, 1);
324 encoder_wakeup_event_.Set();
325 encoder_thread_.Stop();
326
Peter Boströme4499152016-02-05 11:13:28 +0100327 module_process_thread_->DeRegisterModule(&overuse_detector_);
Peter Boström8c66a002016-02-11 13:51:10 +0100328 vie_channel_.RegisterSendFrameCountObserver(nullptr);
329 vie_channel_.RegisterSendBitrateObserver(nullptr);
330 vie_channel_.RegisterRtcpPacketTypeCounterObserver(nullptr);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000331
Peter Boström723ead82016-02-22 15:14:01 +0100332 vie_encoder_.DeRegisterExternalEncoder(config_.encoder_settings.payload_type);
stefan@webrtc.org360e3762013-08-22 09:29:56 +0000333
Peter Boström8c66a002016-02-11 13:51:10 +0100334 call_stats_->DeregisterStatsObserver(vie_channel_.GetStatsObserver());
Peter Boström723ead82016-02-22 15:14:01 +0100335 rtp_rtcp_modules_[0]->SetREMBStatus(false);
336 remb_->RemoveRembSender(rtp_rtcp_modules_[0]);
mflodman949c2f02015-10-16 02:31:11 -0700337
Peter Boström45c44f02016-02-19 17:36:01 +0100338 // ViEChannel outlives ViEEncoder so remove encoder from feedback before
339 // destruction.
340 encoder_feedback_.TearDown();
mflodman949c2f02015-10-16 02:31:11 -0700341
mflodman0c478b32015-10-21 15:52:16 +0200342 congestion_controller_->GetRemoteBitrateEstimator(false)->RemoveStream(
Peter Boström1f7d77f2016-02-11 15:30:14 +0100343 vie_receiver_->GetRemoteSsrc());
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000344}
345
Peter Boström4b91bd02015-06-26 06:58:16 +0200346VideoCaptureInput* VideoSendStream::Input() {
Peter Boström8c66a002016-02-11 13:51:10 +0100347 return &input_;
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000348}
349
pbos@webrtc.orga5c8d2c2014-04-24 11:13:21 +0000350void VideoSendStream::Start() {
Peter Boström0a9fc052016-03-02 16:24:10 +0100351 if (payload_router_.active())
352 return;
Peter Boström8c66a002016-02-11 13:51:10 +0100353 vie_encoder_.Pause();
Peter Boström0a9fc052016-03-02 16:24:10 +0100354 payload_router_.set_active(true);
355 // Was not already started, trigger a keyframe.
356 vie_encoder_.SendKeyFrame();
Peter Boström8c66a002016-02-11 13:51:10 +0100357 vie_encoder_.Restart();
Peter Boströmca835252016-02-11 15:59:46 +0100358 vie_receiver_->StartReceive();
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000359}
360
pbos@webrtc.orga5c8d2c2014-04-24 11:13:21 +0000361void VideoSendStream::Stop() {
Peter Boström0a9fc052016-03-02 16:24:10 +0100362 if (!payload_router_.active())
363 return;
Peter Boström45553ae2015-05-08 13:54:38 +0200364 // TODO(pbos): Make sure the encoder stops here.
Peter Boström0a9fc052016-03-02 16:24:10 +0100365 payload_router_.set_active(false);
Peter Boströmca835252016-02-11 15:59:46 +0100366 vie_receiver_->StopReceive();
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000367}
368
Peter Boströma4c76882016-03-03 16:29:02 +0100369bool VideoSendStream::EncoderThreadFunction(void* obj) {
370 static_cast<VideoSendStream*>(obj)->EncoderProcess();
371 // We're done, return false to abort.
372 return false;
373}
374
375void VideoSendStream::EncoderProcess() {
376 while (true) {
377 encoder_wakeup_event_.Wait(rtc::Event::kForever);
378 if (rtc::AtomicOps::AcquireLoad(&stop_encoder_thread_))
379 return;
380
381 VideoFrame frame;
382 if (input_.GetVideoFrame(&frame))
383 vie_encoder_.EncodeVideoFrame(frame);
384 }
385}
386
Peter Boström905f8e72016-03-02 16:59:56 +0100387void VideoSendStream::ReconfigureVideoEncoder(
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000388 const VideoEncoderConfig& config) {
pbos@webrtc.org50fe3592015-01-29 12:33:07 +0000389 TRACE_EVENT0("webrtc", "VideoSendStream::(Re)configureVideoEncoder");
pbos@webrtc.orgad3b5a52014-10-24 09:23:21 +0000390 LOG(LS_INFO) << "(Re)configureVideoEncoder: " << config.ToString();
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000391 const std::vector<VideoStream>& streams = config.streams;
Peter Boström905f8e72016-03-02 16:59:56 +0100392 static const int kEncoderMinBitrateKbps = 30;
henrikg91d6ede2015-09-17 00:24:34 -0700393 RTC_DCHECK(!streams.empty());
394 RTC_DCHECK_GE(config_.rtp.ssrcs.size(), streams.size());
Peter Boströma03785e2016-03-03 11:36:18 +0100395 RTC_DCHECK_GE(config.min_transmit_bitrate_bps, 0);
pbos@webrtc.org64887612013-11-14 08:58:14 +0000396
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000397 VideoCodec video_codec;
398 memset(&video_codec, 0, sizeof(video_codec));
Peter Boström39593972016-02-15 11:27:15 +0100399 video_codec.codecType =
400 PayloadNameToCodecType(config_.encoder_settings.payload_name);
pbos@webrtc.orgb7ed7792014-10-31 13:08:10 +0000401
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000402 switch (config.content_type) {
Erik Språng143cec12015-04-28 10:01:41 +0200403 case VideoEncoderConfig::ContentType::kRealtimeVideo:
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000404 video_codec.mode = kRealtimeVideo;
405 break;
Erik Språng143cec12015-04-28 10:01:41 +0200406 case VideoEncoderConfig::ContentType::kScreen:
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000407 video_codec.mode = kScreensharing;
pbos@webrtc.orgb7ed7792014-10-31 13:08:10 +0000408 if (config.streams.size() == 1 &&
409 config.streams[0].temporal_layer_thresholds_bps.size() == 1) {
410 video_codec.targetBitrate =
411 config.streams[0].temporal_layer_thresholds_bps[0] / 1000;
412 }
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000413 break;
414 }
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000415
416 if (video_codec.codecType == kVideoCodecVP8) {
pbos@webrtc.org6cd6ba82014-09-18 12:42:28 +0000417 video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings();
marpan@webrtc.org5b883172014-11-01 06:10:48 +0000418 } else if (video_codec.codecType == kVideoCodecVP9) {
419 video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings();
stefan@webrtc.org79c33592014-08-06 09:24:53 +0000420 } else if (video_codec.codecType == kVideoCodecH264) {
pbos@webrtc.org6cd6ba82014-09-18 12:42:28 +0000421 video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings();
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000422 }
423
pbos@webrtc.org91f17522014-07-10 10:13:37 +0000424 if (video_codec.codecType == kVideoCodecVP8) {
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +0000425 if (config.encoder_specific_settings != nullptr) {
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000426 video_codec.codecSpecific.VP8 = *reinterpret_cast<const VideoCodecVP8*>(
427 config.encoder_specific_settings);
pbos@webrtc.org91f17522014-07-10 10:13:37 +0000428 }
pbos@webrtc.org759982d2014-09-22 09:32:46 +0000429 video_codec.codecSpecific.VP8.numberOfTemporalLayers =
pbos@webrtc.orgb7ed7792014-10-31 13:08:10 +0000430 static_cast<unsigned char>(
431 streams.back().temporal_layer_thresholds_bps.size() + 1);
pbos@webrtc.orgb9557a92015-03-20 19:52:56 +0000432 } else if (video_codec.codecType == kVideoCodecVP9) {
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +0000433 if (config.encoder_specific_settings != nullptr) {
pbos@webrtc.orgb9557a92015-03-20 19:52:56 +0000434 video_codec.codecSpecific.VP9 = *reinterpret_cast<const VideoCodecVP9*>(
435 config.encoder_specific_settings);
philipelcfc319b2015-11-10 07:17:23 -0800436 if (video_codec.mode == kScreensharing) {
437 video_codec.codecSpecific.VP9.flexibleMode = true;
438 // For now VP9 screensharing use 1 temporal and 2 spatial layers.
439 RTC_DCHECK_EQ(video_codec.codecSpecific.VP9.numberOfTemporalLayers, 1);
440 RTC_DCHECK_EQ(video_codec.codecSpecific.VP9.numberOfSpatialLayers, 2);
441 }
pbos@webrtc.orgb9557a92015-03-20 19:52:56 +0000442 }
443 video_codec.codecSpecific.VP9.numberOfTemporalLayers =
444 static_cast<unsigned char>(
445 streams.back().temporal_layer_thresholds_bps.size() + 1);
446 } else if (video_codec.codecType == kVideoCodecH264) {
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +0000447 if (config.encoder_specific_settings != nullptr) {
pbos@webrtc.orgb9557a92015-03-20 19:52:56 +0000448 video_codec.codecSpecific.H264 = *reinterpret_cast<const VideoCodecH264*>(
449 config.encoder_specific_settings);
450 }
pbos@webrtc.org91f17522014-07-10 10:13:37 +0000451 } else {
452 // TODO(pbos): Support encoder_settings codec-agnostically.
henrikg91d6ede2015-09-17 00:24:34 -0700453 RTC_DCHECK(config.encoder_specific_settings == nullptr)
pbos@webrtc.org2b4ce3a2015-03-23 13:12:24 +0000454 << "Encoder-specific settings for codec type not wired up.";
pbos@webrtc.org91f17522014-07-10 10:13:37 +0000455 }
456
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000457 strncpy(video_codec.plName,
458 config_.encoder_settings.payload_name.c_str(),
459 kPayloadNameSize - 1);
460 video_codec.plName[kPayloadNameSize - 1] = '\0';
461 video_codec.plType = config_.encoder_settings.payload_type;
462 video_codec.numberOfSimulcastStreams =
463 static_cast<unsigned char>(streams.size());
464 video_codec.minBitrate = streams[0].min_bitrate_bps / 1000;
Peter Boström905f8e72016-03-02 16:59:56 +0100465 if (video_codec.minBitrate < kEncoderMinBitrateKbps)
466 video_codec.minBitrate = kEncoderMinBitrateKbps;
henrikg91d6ede2015-09-17 00:24:34 -0700467 RTC_DCHECK_LE(streams.size(), static_cast<size_t>(kMaxSimulcastStreams));
sprangce4aef12015-11-02 07:23:20 -0800468 if (video_codec.codecType == kVideoCodecVP9) {
469 // If the vector is empty, bitrates will be configured automatically.
470 RTC_DCHECK(config.spatial_layers.empty() ||
471 config.spatial_layers.size() ==
472 video_codec.codecSpecific.VP9.numberOfSpatialLayers);
473 RTC_DCHECK_LE(video_codec.codecSpecific.VP9.numberOfSpatialLayers,
474 kMaxSimulcastStreams);
475 for (size_t i = 0; i < config.spatial_layers.size(); ++i)
476 video_codec.spatialLayers[i] = config.spatial_layers[i];
477 }
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000478 for (size_t i = 0; i < streams.size(); ++i) {
479 SimulcastStream* sim_stream = &video_codec.simulcastStream[i];
henrikg91d6ede2015-09-17 00:24:34 -0700480 RTC_DCHECK_GT(streams[i].width, 0u);
481 RTC_DCHECK_GT(streams[i].height, 0u);
482 RTC_DCHECK_GT(streams[i].max_framerate, 0);
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000483 // Different framerates not supported per stream at the moment.
henrikg91d6ede2015-09-17 00:24:34 -0700484 RTC_DCHECK_EQ(streams[i].max_framerate, streams[0].max_framerate);
485 RTC_DCHECK_GE(streams[i].min_bitrate_bps, 0);
486 RTC_DCHECK_GE(streams[i].target_bitrate_bps, streams[i].min_bitrate_bps);
487 RTC_DCHECK_GE(streams[i].max_bitrate_bps, streams[i].target_bitrate_bps);
488 RTC_DCHECK_GE(streams[i].max_qp, 0);
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000489
mflodmand1590b22015-12-09 07:07:59 -0800490 sim_stream->width = static_cast<uint16_t>(streams[i].width);
491 sim_stream->height = static_cast<uint16_t>(streams[i].height);
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000492 sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000;
493 sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
494 sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
495 sim_stream->qpMax = streams[i].max_qp;
pbos@webrtc.orgb7ed7792014-10-31 13:08:10 +0000496 sim_stream->numberOfTemporalLayers = static_cast<unsigned char>(
497 streams[i].temporal_layer_thresholds_bps.size() + 1);
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000498
499 video_codec.width = std::max(video_codec.width,
mflodmand1590b22015-12-09 07:07:59 -0800500 static_cast<uint16_t>(streams[i].width));
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000501 video_codec.height = std::max(
mflodmand1590b22015-12-09 07:07:59 -0800502 video_codec.height, static_cast<uint16_t>(streams[i].height));
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000503 video_codec.minBitrate =
mflodmand1590b22015-12-09 07:07:59 -0800504 std::min(static_cast<uint16_t>(video_codec.minBitrate),
505 static_cast<uint16_t>(streams[i].min_bitrate_bps / 1000));
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000506 video_codec.maxBitrate += streams[i].max_bitrate_bps / 1000;
507 video_codec.qpMax = std::max(video_codec.qpMax,
508 static_cast<unsigned int>(streams[i].max_qp));
509 }
Stefan Holmere5904162015-03-26 11:11:06 +0100510
Peter Boström905f8e72016-03-02 16:59:56 +0100511 if (video_codec.maxBitrate == 0) {
512 // Unset max bitrate -> cap to one bit per pixel.
513 video_codec.maxBitrate =
514 (video_codec.width * video_codec.height * video_codec.maxFramerate) /
515 1000;
516 }
517 if (video_codec.maxBitrate < kEncoderMinBitrateKbps)
518 video_codec.maxBitrate = kEncoderMinBitrateKbps;
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000519
henrikg91d6ede2015-09-17 00:24:34 -0700520 RTC_DCHECK_GT(streams[0].max_framerate, 0);
pbos@webrtc.org6ae48c62014-06-06 10:49:19 +0000521 video_codec.maxFramerate = streams[0].max_framerate;
pbos@webrtc.orgf577ae92014-03-19 08:43:57 +0000522
mflodman86aabb22016-03-11 15:44:32 +0100523 video_codec.startBitrate =
524 bitrate_allocator_->AddObserver(this,
525 video_codec.minBitrate * 1000,
526 video_codec.maxBitrate * 1000) / 1000;
Peter Boströma03785e2016-03-03 11:36:18 +0100527 vie_encoder_.SetEncoder(video_codec, config.min_transmit_bitrate_bps);
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000528}
529
pbos@webrtc.orgbbb07e62013-08-05 12:01:36 +0000530bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
Peter Boström1f7d77f2016-02-11 15:30:14 +0100531 return vie_receiver_->DeliverRtcp(packet, length);
pbos@webrtc.orgbbb07e62013-08-05 12:01:36 +0000532}
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000533
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000534VideoSendStream::Stats VideoSendStream::GetStats() {
stefan@webrtc.org168f23f2014-07-11 13:44:02 +0000535 return stats_proxy_.GetStats();
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000536}
537
solenberge5269742015-09-08 05:13:22 -0700538void VideoSendStream::OveruseDetected() {
539 if (config_.overuse_callback)
540 config_.overuse_callback->OnLoadUpdate(LoadObserver::kOveruse);
541}
542
543void VideoSendStream::NormalUsage() {
544 if (config_.overuse_callback)
545 config_.overuse_callback->OnLoadUpdate(LoadObserver::kUnderuse);
546}
547
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000548void VideoSendStream::ConfigureSsrcs() {
Peter Boström723ead82016-02-22 15:14:01 +0100549 // Configure regular SSRCs.
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000550 for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
551 uint32_t ssrc = config_.rtp.ssrcs[i];
Peter Boström723ead82016-02-22 15:14:01 +0100552 RtpRtcp* const rtp_rtcp = rtp_rtcp_modules_[i];
553 rtp_rtcp->SetSSRC(ssrc);
554
555 // Restore RTP state if previous existed.
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000556 RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
557 if (it != suspended_ssrcs_.end())
Peter Boström723ead82016-02-22 15:14:01 +0100558 rtp_rtcp->SetRtpStateForSsrc(ssrc, it->second);
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000559 }
560
Peter Boström723ead82016-02-22 15:14:01 +0100561 // Set up RTX if available.
562 if (config_.rtp.rtx.ssrcs.empty())
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000563 return;
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000564
Peter Boström723ead82016-02-22 15:14:01 +0100565 // Configure RTX SSRCs.
henrikg91d6ede2015-09-17 00:24:34 -0700566 RTC_DCHECK_EQ(config_.rtp.rtx.ssrcs.size(), config_.rtp.ssrcs.size());
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000567 for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
568 uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
Peter Boström723ead82016-02-22 15:14:01 +0100569 RtpRtcp* const rtp_rtcp = rtp_rtcp_modules_[i];
570 rtp_rtcp->SetRtxSsrc(ssrc);
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000571 RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
572 if (it != suspended_ssrcs_.end())
Peter Boström723ead82016-02-22 15:14:01 +0100573 rtp_rtcp->SetRtpStateForSsrc(ssrc, it->second);
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000574 }
575
Peter Boström723ead82016-02-22 15:14:01 +0100576 // Configure RTX payload types.
henrikg91d6ede2015-09-17 00:24:34 -0700577 RTC_DCHECK_GE(config_.rtp.rtx.payload_type, 0);
Peter Boström723ead82016-02-22 15:14:01 +0100578 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
579 rtp_rtcp->SetRtxSendPayloadType(config_.rtp.rtx.payload_type,
580 config_.encoder_settings.payload_type);
581 rtp_rtcp->SetRtxSendStatus(kRtxRetransmitted | kRtxRedundantPayloads);
582 }
Stefan Holmer10880012016-02-03 13:29:59 +0100583 if (config_.rtp.fec.red_payload_type != -1 &&
584 config_.rtp.fec.red_rtx_payload_type != -1) {
Peter Boström723ead82016-02-22 15:14:01 +0100585 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
586 rtp_rtcp->SetRtxSendPayloadType(config_.rtp.fec.red_rtx_payload_type,
587 config_.rtp.fec.red_payload_type);
588 }
Stefan Holmer10880012016-02-03 13:29:59 +0100589 }
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000590}
591
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000592std::map<uint32_t, RtpState> VideoSendStream::GetRtpStates() const {
593 std::map<uint32_t, RtpState> rtp_states;
594 for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
595 uint32_t ssrc = config_.rtp.ssrcs[i];
Peter Boström8c66a002016-02-11 13:51:10 +0100596 rtp_states[ssrc] = vie_channel_.GetRtpStateForSsrc(ssrc);
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000597 }
598
599 for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
600 uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
Peter Boström8c66a002016-02-11 13:51:10 +0100601 rtp_states[ssrc] = vie_channel_.GetRtpStateForSsrc(ssrc);
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000602 }
603
604 return rtp_states;
605}
606
Jelena Marusiccd670222015-07-16 09:30:09 +0200607void VideoSendStream::SignalNetworkState(NetworkState state) {
pbos@webrtc.org26c0c412014-09-03 16:17:12 +0000608 // When network goes up, enable RTCP status before setting transmission state.
609 // When it goes down, disable RTCP afterwards. This ensures that any packets
610 // sent due to the network state changed will not be dropped.
Peter Boström723ead82016-02-22 15:14:01 +0100611 if (state == kNetworkUp) {
612 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_)
613 rtp_rtcp->SetRTCPStatus(config_.rtp.rtcp_mode);
614 }
Peter Boström8c66a002016-02-11 13:51:10 +0100615 vie_encoder_.SetNetworkTransmissionState(state == kNetworkUp);
Peter Boström723ead82016-02-22 15:14:01 +0100616 if (state == kNetworkDown) {
617 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_)
618 rtp_rtcp->SetRTCPStatus(RtcpMode::kOff);
619 }
stefan@webrtc.org0bae1fa2014-11-05 14:05:29 +0000620}
pbos@webrtc.org2b19f062014-12-11 13:26:09 +0000621
mflodman0e7e2592015-11-12 21:02:42 -0800622int VideoSendStream::GetPaddingNeededBps() const {
Peter Boström8c66a002016-02-11 13:51:10 +0100623 return vie_encoder_.GetPaddingNeededBps();
mflodman0e7e2592015-11-12 21:02:42 -0800624}
mflodman86aabb22016-03-11 15:44:32 +0100625
626void VideoSendStream::OnBitrateUpdated(uint32_t bitrate_bps,
627 uint8_t fraction_loss,
628 int64_t rtt) {
629 vie_encoder_.OnBitrateUpdated(bitrate_bps, fraction_loss, rtt);
630}
631
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000632} // namespace internal
633} // namespace webrtc