blob: 9ffe8a34c07f1139d19a4337f9894cc36507aa48 [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
Per83d09102016-04-15 14:59:13 +020037static const int kMinSendSidePacketHistorySize = 600;
38
39namespace {
40
41std::vector<RtpRtcp*> CreateRtpRtcpModules(
42 Transport* outgoing_transport,
43 RtcpIntraFrameObserver* intra_frame_callback,
44 RtcpBandwidthObserver* bandwidth_callback,
45 TransportFeedbackObserver* transport_feedback_callback,
46 RtcpRttStats* rtt_stats,
47 RtpPacketSender* paced_sender,
48 TransportSequenceNumberAllocator* transport_sequence_number_allocator,
49 SendStatisticsProxy* stats_proxy,
asapersson35151f32016-05-02 23:44:01 -070050 SendDelayStats* send_delay_stats,
Per83d09102016-04-15 14:59:13 +020051 size_t num_modules) {
52 RTC_DCHECK_GT(num_modules, 0u);
53 RtpRtcp::Configuration configuration;
54 ReceiveStatistics* null_receive_statistics = configuration.receive_statistics;
55 configuration.audio = false;
56 configuration.receiver_only = false;
57 configuration.receive_statistics = null_receive_statistics;
58 configuration.outgoing_transport = outgoing_transport;
59 configuration.intra_frame_callback = intra_frame_callback;
60 configuration.rtt_stats = rtt_stats;
61 configuration.rtcp_packet_type_counter_observer = stats_proxy;
62 configuration.paced_sender = paced_sender;
63 configuration.transport_sequence_number_allocator =
64 transport_sequence_number_allocator;
65 configuration.send_bitrate_observer = stats_proxy;
66 configuration.send_frame_count_observer = stats_proxy;
67 configuration.send_side_delay_observer = stats_proxy;
asapersson35151f32016-05-02 23:44:01 -070068 configuration.send_packet_observer = send_delay_stats;
Per83d09102016-04-15 14:59:13 +020069 configuration.bandwidth_callback = bandwidth_callback;
70 configuration.transport_feedback_callback = transport_feedback_callback;
71
72 std::vector<RtpRtcp*> modules;
73 for (size_t i = 0; i < num_modules; ++i) {
74 RtpRtcp* rtp_rtcp = RtpRtcp::CreateRtpRtcp(configuration);
75 rtp_rtcp->SetSendingStatus(false);
76 rtp_rtcp->SetSendingMediaStatus(false);
77 rtp_rtcp->SetRTCPStatus(RtcpMode::kCompound);
78 modules.push_back(rtp_rtcp);
79 }
80 return modules;
81}
82
83} // namespace
84
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000085std::string
pbos@webrtc.org024e4d52014-05-15 10:03:24 +000086VideoSendStream::Config::EncoderSettings::ToString() const {
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000087 std::stringstream ss;
88 ss << "{payload_name: " << payload_name;
89 ss << ", payload_type: " << payload_type;
Peter Boström74f6e9e2016-04-04 17:56:10 +020090 ss << ", encoder: " << (encoder ? "(VideoEncoder)" : "nullptr");
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000091 ss << '}';
92 return ss.str();
93}
94
pbos@webrtc.org024e4d52014-05-15 10:03:24 +000095std::string VideoSendStream::Config::Rtp::Rtx::ToString()
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000096 const {
97 std::stringstream ss;
pbos@webrtc.org32e85282015-01-15 10:09:39 +000098 ss << "{ssrcs: [";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +000099 for (size_t i = 0; i < ssrcs.size(); ++i) {
100 ss << ssrcs[i];
101 if (i != ssrcs.size() - 1)
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000102 ss << ", ";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000103 }
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000104 ss << ']';
andrew@webrtc.org8f27fcc2015-01-09 20:22:46 +0000105
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000106 ss << ", payload_type: " << payload_type;
107 ss << '}';
108 return ss.str();
109}
110
pbos@webrtc.org024e4d52014-05-15 10:03:24 +0000111std::string VideoSendStream::Config::Rtp::ToString() const {
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000112 std::stringstream ss;
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000113 ss << "{ssrcs: [";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000114 for (size_t i = 0; i < ssrcs.size(); ++i) {
115 ss << ssrcs[i];
116 if (i != ssrcs.size() - 1)
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000117 ss << ", ";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000118 }
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000119 ss << ']';
Taylor Brandstetter5f0b83b2016-03-18 15:02:07 -0700120 ss << ", rtcp_mode: "
121 << (rtcp_mode == RtcpMode::kCompound ? "RtcpMode::kCompound"
122 : "RtcpMode::kReducedSize");
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000123 ss << ", max_packet_size: " << max_packet_size;
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000124 ss << ", extensions: [";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000125 for (size_t i = 0; i < extensions.size(); ++i) {
126 ss << extensions[i].ToString();
127 if (i != extensions.size() - 1)
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000128 ss << ", ";
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000129 }
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000130 ss << ']';
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000131
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000132 ss << ", nack: {rtp_history_ms: " << nack.rtp_history_ms << '}';
133 ss << ", fec: " << fec.ToString();
134 ss << ", rtx: " << rtx.ToString();
135 ss << ", c_name: " << c_name;
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000136 ss << '}';
137 return ss.str();
138}
139
pbos@webrtc.org024e4d52014-05-15 10:03:24 +0000140std::string VideoSendStream::Config::ToString() const {
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000141 std::stringstream ss;
142 ss << "{encoder_settings: " << encoder_settings.ToString();
143 ss << ", rtp: " << rtp.ToString();
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000144 ss << ", pre_encode_callback: "
Peter Boström74f6e9e2016-04-04 17:56:10 +0200145 << (pre_encode_callback ? "(I420FrameCallback)" : "nullptr");
146 ss << ", post_encode_callback: "
147 << (post_encode_callback ? "(EncodedFrameObserver)" : "nullptr");
148 ss << ", local_renderer: "
149 << (local_renderer ? "(VideoRenderer)" : "nullptr");
pbos@webrtc.org32e85282015-01-15 10:09:39 +0000150 ss << ", render_delay_ms: " << render_delay_ms;
151 ss << ", target_delay_ms: " << target_delay_ms;
152 ss << ", suspend_below_min_bitrate: " << (suspend_below_min_bitrate ? "on"
153 : "off");
pbos@webrtc.org1e92b0a2014-05-15 09:35:06 +0000154 ss << '}';
155 return ss.str();
156}
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000157
Peter Boströme4499152016-02-05 11:13:28 +0100158namespace {
159
Peter Boström39593972016-02-15 11:27:15 +0100160VideoCodecType PayloadNameToCodecType(const std::string& payload_name) {
161 if (payload_name == "VP8")
162 return kVideoCodecVP8;
163 if (payload_name == "VP9")
164 return kVideoCodecVP9;
165 if (payload_name == "H264")
166 return kVideoCodecH264;
167 return kVideoCodecGeneric;
168}
169
170bool PayloadTypeSupportsSkippingFecPackets(const std::string& payload_name) {
171 switch (PayloadNameToCodecType(payload_name)) {
172 case kVideoCodecVP8:
173 case kVideoCodecVP9:
174 return true;
175 case kVideoCodecH264:
176 case kVideoCodecGeneric:
177 return false;
178 case kVideoCodecI420:
179 case kVideoCodecRED:
180 case kVideoCodecULPFEC:
181 case kVideoCodecUnknown:
182 RTC_NOTREACHED();
183 return false;
184 }
185 RTC_NOTREACHED();
186 return false;
187}
188
Peter Boström23353ab2016-02-24 15:19:55 +0100189// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
190// pipelining encoders better (multiple input frames before something comes
191// out). This should effectively turn off CPU adaptations for systems that
192// remotely cope with the load right now.
Peter Boströme4499152016-02-05 11:13:28 +0100193CpuOveruseOptions GetCpuOveruseOptions(bool full_overuse_time) {
194 CpuOveruseOptions options;
195 if (full_overuse_time) {
Peter Boström23353ab2016-02-24 15:19:55 +0100196 options.low_encode_usage_threshold_percent = 150;
197 options.high_encode_usage_threshold_percent = 200;
Peter Boströme4499152016-02-05 11:13:28 +0100198 }
199 return options;
200}
pbos14fe7082016-04-20 06:35:56 -0700201
202VideoCodec VideoEncoderConfigToVideoCodec(const VideoEncoderConfig& config,
203 const std::string& payload_name,
204 int payload_type) {
205 const std::vector<VideoStream>& streams = config.streams;
206 static const int kEncoderMinBitrateKbps = 30;
207 RTC_DCHECK(!streams.empty());
208 RTC_DCHECK_GE(config.min_transmit_bitrate_bps, 0);
209
210 VideoCodec video_codec;
211 memset(&video_codec, 0, sizeof(video_codec));
212 video_codec.codecType = PayloadNameToCodecType(payload_name);
213
214 switch (config.content_type) {
215 case VideoEncoderConfig::ContentType::kRealtimeVideo:
216 video_codec.mode = kRealtimeVideo;
217 break;
218 case VideoEncoderConfig::ContentType::kScreen:
219 video_codec.mode = kScreensharing;
220 if (config.streams.size() == 1 &&
221 config.streams[0].temporal_layer_thresholds_bps.size() == 1) {
222 video_codec.targetBitrate =
223 config.streams[0].temporal_layer_thresholds_bps[0] / 1000;
224 }
225 break;
226 }
227
228 switch (video_codec.codecType) {
229 case kVideoCodecVP8: {
230 if (config.encoder_specific_settings) {
231 video_codec.codecSpecific.VP8 = *reinterpret_cast<const VideoCodecVP8*>(
232 config.encoder_specific_settings);
233 } else {
234 video_codec.codecSpecific.VP8 = VideoEncoder::GetDefaultVp8Settings();
235 }
236 video_codec.codecSpecific.VP8.numberOfTemporalLayers =
237 static_cast<unsigned char>(
238 streams.back().temporal_layer_thresholds_bps.size() + 1);
239 break;
240 }
241 case kVideoCodecVP9: {
242 if (config.encoder_specific_settings) {
243 video_codec.codecSpecific.VP9 = *reinterpret_cast<const VideoCodecVP9*>(
244 config.encoder_specific_settings);
245 if (video_codec.mode == kScreensharing) {
246 video_codec.codecSpecific.VP9.flexibleMode = true;
247 // For now VP9 screensharing use 1 temporal and 2 spatial layers.
248 RTC_DCHECK_EQ(video_codec.codecSpecific.VP9.numberOfTemporalLayers,
249 1);
250 RTC_DCHECK_EQ(video_codec.codecSpecific.VP9.numberOfSpatialLayers, 2);
251 }
252 } else {
253 video_codec.codecSpecific.VP9 = VideoEncoder::GetDefaultVp9Settings();
254 }
255 video_codec.codecSpecific.VP9.numberOfTemporalLayers =
256 static_cast<unsigned char>(
257 streams.back().temporal_layer_thresholds_bps.size() + 1);
258 break;
259 }
260 case kVideoCodecH264: {
261 if (config.encoder_specific_settings) {
262 video_codec.codecSpecific.H264 =
263 *reinterpret_cast<const VideoCodecH264*>(
264 config.encoder_specific_settings);
265 } else {
266 video_codec.codecSpecific.H264 = VideoEncoder::GetDefaultH264Settings();
267 }
268 break;
269 }
270 default:
271 // TODO(pbos): Support encoder_settings codec-agnostically.
272 RTC_DCHECK(!config.encoder_specific_settings)
273 << "Encoder-specific settings for codec type not wired up.";
274 break;
275 }
276
277 strncpy(video_codec.plName, payload_name.c_str(), kPayloadNameSize - 1);
278 video_codec.plName[kPayloadNameSize - 1] = '\0';
279 video_codec.plType = payload_type;
280 video_codec.numberOfSimulcastStreams =
281 static_cast<unsigned char>(streams.size());
282 video_codec.minBitrate = streams[0].min_bitrate_bps / 1000;
283 if (video_codec.minBitrate < kEncoderMinBitrateKbps)
284 video_codec.minBitrate = kEncoderMinBitrateKbps;
285 RTC_DCHECK_LE(streams.size(), static_cast<size_t>(kMaxSimulcastStreams));
286 if (video_codec.codecType == kVideoCodecVP9) {
287 // If the vector is empty, bitrates will be configured automatically.
288 RTC_DCHECK(config.spatial_layers.empty() ||
289 config.spatial_layers.size() ==
290 video_codec.codecSpecific.VP9.numberOfSpatialLayers);
291 RTC_DCHECK_LE(video_codec.codecSpecific.VP9.numberOfSpatialLayers,
292 kMaxSimulcastStreams);
293 for (size_t i = 0; i < config.spatial_layers.size(); ++i)
294 video_codec.spatialLayers[i] = config.spatial_layers[i];
295 }
296 for (size_t i = 0; i < streams.size(); ++i) {
297 SimulcastStream* sim_stream = &video_codec.simulcastStream[i];
298 RTC_DCHECK_GT(streams[i].width, 0u);
299 RTC_DCHECK_GT(streams[i].height, 0u);
300 RTC_DCHECK_GT(streams[i].max_framerate, 0);
301 // Different framerates not supported per stream at the moment.
302 RTC_DCHECK_EQ(streams[i].max_framerate, streams[0].max_framerate);
303 RTC_DCHECK_GE(streams[i].min_bitrate_bps, 0);
304 RTC_DCHECK_GE(streams[i].target_bitrate_bps, streams[i].min_bitrate_bps);
305 RTC_DCHECK_GE(streams[i].max_bitrate_bps, streams[i].target_bitrate_bps);
306 RTC_DCHECK_GE(streams[i].max_qp, 0);
307
308 sim_stream->width = static_cast<uint16_t>(streams[i].width);
309 sim_stream->height = static_cast<uint16_t>(streams[i].height);
310 sim_stream->minBitrate = streams[i].min_bitrate_bps / 1000;
311 sim_stream->targetBitrate = streams[i].target_bitrate_bps / 1000;
312 sim_stream->maxBitrate = streams[i].max_bitrate_bps / 1000;
313 sim_stream->qpMax = streams[i].max_qp;
314 sim_stream->numberOfTemporalLayers = static_cast<unsigned char>(
315 streams[i].temporal_layer_thresholds_bps.size() + 1);
316
317 video_codec.width = std::max(video_codec.width,
318 static_cast<uint16_t>(streams[i].width));
319 video_codec.height = std::max(
320 video_codec.height, static_cast<uint16_t>(streams[i].height));
321 video_codec.minBitrate =
322 std::min(static_cast<uint16_t>(video_codec.minBitrate),
323 static_cast<uint16_t>(streams[i].min_bitrate_bps / 1000));
324 video_codec.maxBitrate += streams[i].max_bitrate_bps / 1000;
325 video_codec.qpMax = std::max(video_codec.qpMax,
326 static_cast<unsigned int>(streams[i].max_qp));
327 }
328
329 if (video_codec.maxBitrate == 0) {
330 // Unset max bitrate -> cap to one bit per pixel.
331 video_codec.maxBitrate =
332 (video_codec.width * video_codec.height * video_codec.maxFramerate) /
333 1000;
334 }
335 if (video_codec.maxBitrate < kEncoderMinBitrateKbps)
336 video_codec.maxBitrate = kEncoderMinBitrateKbps;
337
338 RTC_DCHECK_GT(streams[0].max_framerate, 0);
339 video_codec.maxFramerate = streams[0].max_framerate;
340
341 return video_codec;
342}
343
Peter Boströme4499152016-02-05 11:13:28 +0100344} // namespace
345
pbos@webrtc.org024e4d52014-05-15 10:03:24 +0000346namespace internal {
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000347VideoSendStream::VideoSendStream(
Peter Boström45553ae2015-05-08 13:54:38 +0200348 int num_cpu_cores,
Peter Boströmf16fcbe2015-04-30 12:16:05 +0200349 ProcessThread* module_process_thread,
mflodmane3787022015-10-21 13:24:28 +0200350 CallStats* call_stats,
mflodman0c478b32015-10-21 15:52:16 +0200351 CongestionController* congestion_controller,
mflodman0e7e2592015-11-12 21:02:42 -0800352 BitrateAllocator* bitrate_allocator,
asapersson35151f32016-05-02 23:44:01 -0700353 SendDelayStats* send_delay_stats,
mflodman86aabb22016-03-11 15:44:32 +0100354 VieRemb* remb,
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000355 const VideoSendStream::Config& config,
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000356 const VideoEncoderConfig& encoder_config,
Peter Boström45553ae2015-05-08 13:54:38 +0200357 const std::map<uint32_t, RtpState>& suspended_ssrcs)
sprangb4a1ae52015-12-03 08:10:08 -0800358 : stats_proxy_(Clock::GetRealTimeClock(),
359 config,
360 encoder_config.content_type),
sprang@webrtc.org40709352013-11-26 11:41:59 +0000361 encoded_frame_proxy_(config.post_encode_callback),
pbos@webrtc.org64887612013-11-14 08:58:14 +0000362 config_(config),
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000363 suspended_ssrcs_(suspended_ssrcs),
Peter Boströmf16fcbe2015-04-30 12:16:05 +0200364 module_process_thread_(module_process_thread),
mflodmane3787022015-10-21 13:24:28 +0200365 call_stats_(call_stats),
mflodman0c478b32015-10-21 15:52:16 +0200366 congestion_controller_(congestion_controller),
mflodman86aabb22016-03-11 15:44:32 +0100367 bitrate_allocator_(bitrate_allocator),
Stefan Holmer58c664c2016-02-08 14:31:30 +0100368 remb_(remb),
Peter Boströma4c76882016-03-03 16:29:02 +0100369 encoder_thread_(EncoderThreadFunction, this, "EncoderThread"),
370 encoder_wakeup_event_(false, false),
371 stop_encoder_thread_(0),
Peter Boströme4499152016-02-05 11:13:28 +0100372 overuse_detector_(
373 Clock::GetRealTimeClock(),
374 GetCpuOveruseOptions(config.encoder_settings.full_overuse_time),
375 this,
376 config.post_encode_callback,
377 &stats_proxy_),
perkjbc75d972016-05-02 06:31:25 -0700378 vie_encoder_(num_cpu_cores,
379 config_.rtp.ssrcs,
380 module_process_thread_,
381 &stats_proxy_,
Per28a44562016-05-04 17:12:51 +0200382 &overuse_detector_),
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200383 video_sender_(vie_encoder_.video_sender()),
Per83d09102016-04-15 14:59:13 +0200384 bandwidth_observer_(congestion_controller_->GetBitrateController()
385 ->CreateRtcpBandwidthObserver()),
386 rtp_rtcp_modules_(CreateRtpRtcpModules(
387 config.send_transport,
388 &encoder_feedback_,
389 bandwidth_observer_.get(),
390 congestion_controller_->GetTransportFeedbackObserver(),
391 call_stats_->rtcp_rtt_stats(),
392 congestion_controller_->pacer(),
393 congestion_controller_->packet_router(),
394 &stats_proxy_,
asapersson35151f32016-05-02 23:44:01 -0700395 send_delay_stats,
Per83d09102016-04-15 14:59:13 +0200396 config_.rtp.ssrcs.size())),
kjellander02b3d272016-04-20 05:05:54 -0700397 payload_router_(rtp_rtcp_modules_, config.encoder_settings.payload_type),
Peter Boströma4c76882016-03-03 16:29:02 +0100398 input_(&encoder_wakeup_event_,
Peter Boström8c66a002016-02-11 13:51:10 +0100399 config_.local_renderer,
400 &stats_proxy_,
401 &overuse_detector_) {
pbosa2f30de2015-10-15 05:22:13 -0700402 LOG(LS_INFO) << "VideoSendStream: " << config_.ToString();
Stefan Holmer58c664c2016-02-08 14:31:30 +0100403
henrikg91d6ede2015-09-17 00:24:34 -0700404 RTC_DCHECK(!config_.rtp.ssrcs.empty());
Stefan Holmer58c664c2016-02-08 14:31:30 +0100405 RTC_DCHECK(module_process_thread_);
406 RTC_DCHECK(call_stats_);
407 RTC_DCHECK(congestion_controller_);
408 RTC_DCHECK(remb_);
mflodman949c2f02015-10-16 02:31:11 -0700409
Peter Boström45c44f02016-02-19 17:36:01 +0100410 encoder_feedback_.Init(config_.rtp.ssrcs, &vie_encoder_);
mflodman949c2f02015-10-16 02:31:11 -0700411
Per83d09102016-04-15 14:59:13 +0200412 // RTP/RTCP initialization.
413 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
414 module_process_thread_->RegisterModule(rtp_rtcp);
415 congestion_controller_->packet_router()->AddRtpModule(rtp_rtcp);
416 }
mflodman949c2f02015-10-16 02:31:11 -0700417
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200418 video_sender_->RegisterProtectionCallback(this);
mflodman949c2f02015-10-16 02:31:11 -0700419
pbos@webrtc.org29023282013-09-11 10:14:56 +0000420 for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
421 const std::string& extension = config_.rtp.extensions[i].name;
422 int id = config_.rtp.extensions[i].id;
Peter Boström23914fe2015-03-31 15:08:04 +0200423 // One-byte-extension local identifiers are in the range 1-14 inclusive.
henrikg91d6ede2015-09-17 00:24:34 -0700424 RTC_DCHECK_GE(id, 1);
425 RTC_DCHECK_LE(id, 14);
Peter Boström9c017252016-02-26 16:26:20 +0100426 RTC_DCHECK(RtpExtension::IsSupportedForVideo(extension));
427 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
428 RTC_CHECK_EQ(0, rtp_rtcp->RegisterSendRtpHeaderExtension(
429 StringToRtpExtensionType(extension), id));
pbos@webrtc.org29023282013-09-11 10:14:56 +0000430 }
431 }
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000432
Peter Boström723ead82016-02-22 15:14:01 +0100433 remb_->AddRembSender(rtp_rtcp_modules_[0]);
434 rtp_rtcp_modules_[0]->SetREMBStatus(true);
mflodman@webrtc.org92c27932013-12-13 16:36:28 +0000435
Per83d09102016-04-15 14:59:13 +0200436 ConfigureProtection();
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000437 ConfigureSsrcs();
438
Peter Boström723ead82016-02-22 15:14:01 +0100439 // TODO(pbos): Should we set CNAME on all RTP modules?
440 rtp_rtcp_modules_.front()->SetCNAME(config_.rtp.c_name.c_str());
sprang@webrtc.org25fce9a2013-10-16 13:29:14 +0000441 // 28 to match packet overhead in ModuleRtpRtcpImpl.
Peter Boström723ead82016-02-22 15:14:01 +0100442 static const size_t kRtpPacketSizeOverhead = 28;
443 RTC_DCHECK_LE(config_.rtp.max_packet_size, 0xFFFFu + kRtpPacketSizeOverhead);
444 const uint16_t mtu = static_cast<uint16_t>(config_.rtp.max_packet_size +
445 kRtpPacketSizeOverhead);
446 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
447 rtp_rtcp->RegisterRtcpStatisticsCallback(&stats_proxy_);
448 rtp_rtcp->RegisterSendChannelRtpStatisticsCallback(&stats_proxy_);
449 rtp_rtcp->SetMaxTransferUnit(mtu);
Peter Boström8b79b072016-02-26 16:31:37 +0100450 rtp_rtcp->RegisterVideoSendPayload(
451 config_.encoder_settings.payload_type,
452 config_.encoder_settings.payload_name.c_str());
Peter Boström723ead82016-02-22 15:14:01 +0100453 }
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000454
Peter Boström74f6e9e2016-04-04 17:56:10 +0200455 RTC_DCHECK(config.encoder_settings.encoder);
henrikg91d6ede2015-09-17 00:24:34 -0700456 RTC_DCHECK_GE(config.encoder_settings.payload_type, 0);
457 RTC_DCHECK_LE(config.encoder_settings.payload_type, 127);
Peter Boström905f8e72016-03-02 16:59:56 +0100458 ReconfigureVideoEncoder(encoder_config);
pbos@webrtc.orgfe1ef932013-10-21 10:34:43 +0000459
Peter Boströme4499152016-02-05 11:13:28 +0100460 module_process_thread_->RegisterModule(&overuse_detector_);
Peter Boströma4c76882016-03-03 16:29:02 +0100461
462 encoder_thread_.Start();
463 encoder_thread_.SetPriority(rtc::kHighPriority);
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000464}
465
466VideoSendStream::~VideoSendStream() {
pbosa2f30de2015-10-15 05:22:13 -0700467 LOG(LS_INFO) << "~VideoSendStream: " << config_.ToString();
mflodman86aabb22016-03-11 15:44:32 +0100468
Peter Boströmca835252016-02-11 15:59:46 +0100469 Stop();
470
Peter Boströma4c76882016-03-03 16:29:02 +0100471 // Stop the encoder thread permanently.
472 rtc::AtomicOps::ReleaseStore(&stop_encoder_thread_, 1);
473 encoder_wakeup_event_.Set();
474 encoder_thread_.Stop();
475
deadbeef62411a22016-03-20 14:24:49 -0700476 // This needs to happen after stopping the encoder thread,
477 // since the encoder thread calls AddObserver.
478 bitrate_allocator_->RemoveObserver(this);
479
Peter Boströme4499152016-02-05 11:13:28 +0100480 module_process_thread_->DeRegisterModule(&overuse_detector_);
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000481
Peter Boström723ead82016-02-22 15:14:01 +0100482 rtp_rtcp_modules_[0]->SetREMBStatus(false);
483 remb_->RemoveRembSender(rtp_rtcp_modules_[0]);
mflodman949c2f02015-10-16 02:31:11 -0700484
Per83d09102016-04-15 14:59:13 +0200485 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
486 congestion_controller_->packet_router()->RemoveRtpModule(rtp_rtcp);
487 module_process_thread_->DeRegisterModule(rtp_rtcp);
488 delete rtp_rtcp;
489 }
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000490}
491
pbos1ba8d392016-05-01 20:18:34 -0700492void VideoSendStream::SignalNetworkState(NetworkState state) {
493 // When network goes up, enable RTCP status before setting transmission state.
494 // When it goes down, disable RTCP afterwards. This ensures that any packets
495 // sent due to the network state changed will not be dropped.
496 if (state == kNetworkUp) {
497 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_)
498 rtp_rtcp->SetRTCPStatus(config_.rtp.rtcp_mode);
499 }
500 vie_encoder_.SetNetworkTransmissionState(state == kNetworkUp);
501 if (state == kNetworkDown) {
502 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_)
503 rtp_rtcp->SetRTCPStatus(RtcpMode::kOff);
504 }
505}
506
507bool VideoSendStream::DeliverRtcp(const uint8_t* packet, size_t length) {
508 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_)
509 rtp_rtcp->IncomingRtcpPacket(packet, length);
510 return true;
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000511}
512
pbos@webrtc.orga5c8d2c2014-04-24 11:13:21 +0000513void VideoSendStream::Start() {
Peter Boström0a9fc052016-03-02 16:24:10 +0100514 if (payload_router_.active())
515 return;
Peter Boströmdabc9442016-04-11 11:45:14 +0200516 TRACE_EVENT_INSTANT0("webrtc", "VideoSendStream::Start");
Peter Boström0a9fc052016-03-02 16:24:10 +0100517 payload_router_.set_active(true);
518 // Was not already started, trigger a keyframe.
519 vie_encoder_.SendKeyFrame();
perkjbc75d972016-05-02 06:31:25 -0700520 vie_encoder_.Start();
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000521}
522
pbos@webrtc.orga5c8d2c2014-04-24 11:13:21 +0000523void VideoSendStream::Stop() {
Peter Boström0a9fc052016-03-02 16:24:10 +0100524 if (!payload_router_.active())
525 return;
Peter Boströmdabc9442016-04-11 11:45:14 +0200526 TRACE_EVENT_INSTANT0("webrtc", "VideoSendStream::Stop");
perkjbc75d972016-05-02 06:31:25 -0700527 vie_encoder_.Pause();
Peter Boström0a9fc052016-03-02 16:24:10 +0100528 payload_router_.set_active(false);
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000529}
530
pbos1ba8d392016-05-01 20:18:34 -0700531VideoCaptureInput* VideoSendStream::Input() {
532 return &input_;
533}
534
Peter Boströma4c76882016-03-03 16:29:02 +0100535bool VideoSendStream::EncoderThreadFunction(void* obj) {
536 static_cast<VideoSendStream*>(obj)->EncoderProcess();
537 // We're done, return false to abort.
538 return false;
539}
540
541void VideoSendStream::EncoderProcess() {
pbos14fe7082016-04-20 06:35:56 -0700542 RTC_CHECK_EQ(0, vie_encoder_.RegisterExternalEncoder(
543 config_.encoder_settings.encoder,
544 config_.encoder_settings.payload_type,
545 config_.encoder_settings.internal_source));
546
Peter Boströma4c76882016-03-03 16:29:02 +0100547 while (true) {
548 encoder_wakeup_event_.Wait(rtc::Event::kForever);
549 if (rtc::AtomicOps::AcquireLoad(&stop_encoder_thread_))
pbos14fe7082016-04-20 06:35:56 -0700550 break;
551 rtc::Optional<EncoderSettings> encoder_settings;
552 {
553 rtc::CritScope lock(&encoder_settings_crit_);
554 if (pending_encoder_settings_) {
555 encoder_settings = pending_encoder_settings_;
556 pending_encoder_settings_ = rtc::Optional<EncoderSettings>();
557 }
558 }
559 if (encoder_settings) {
560 encoder_settings->video_codec.startBitrate =
561 bitrate_allocator_->AddObserver(
562 this, encoder_settings->video_codec.minBitrate * 1000,
563 encoder_settings->video_codec.maxBitrate * 1000) /
564 1000;
perkjbc75d972016-05-02 06:31:25 -0700565
566 payload_router_.SetSendStreams(encoder_settings->streams);
pbos14fe7082016-04-20 06:35:56 -0700567 vie_encoder_.SetEncoder(encoder_settings->video_codec,
perkjbc75d972016-05-02 06:31:25 -0700568 encoder_settings->min_transmit_bitrate_bps,
569 payload_router_.MaxPayloadLength(), this);
pbos14fe7082016-04-20 06:35:56 -0700570 if (config_.suspend_below_min_bitrate) {
Peter Boströmcd5c25c2016-04-21 16:48:08 +0200571 video_sender_->SuspendBelowMinBitrate();
pbos14fe7082016-04-20 06:35:56 -0700572 bitrate_allocator_->EnforceMinBitrate(false);
573 }
574 // We might've gotten new settings while configuring the encoder settings,
575 // restart from the top to see if that's the case before trying to encode
576 // a frame (which might correspond to the last frame size).
577 encoder_wakeup_event_.Set();
578 continue;
579 }
Peter Boströma4c76882016-03-03 16:29:02 +0100580
581 VideoFrame frame;
Per28a44562016-05-04 17:12:51 +0200582 if (input_.GetVideoFrame(&frame)) {
583 // TODO(perkj): |pre_encode_callback| is only used by tests. Tests should
584 // register as a sink to the VideoSource instead.
585 if (config_.pre_encode_callback) {
586 config_.pre_encode_callback->OnFrame(frame);
587 }
Peter Boströma4c76882016-03-03 16:29:02 +0100588 vie_encoder_.EncodeVideoFrame(frame);
Per28a44562016-05-04 17:12:51 +0200589 }
Peter Boströma4c76882016-03-03 16:29:02 +0100590 }
pbos14fe7082016-04-20 06:35:56 -0700591 vie_encoder_.DeRegisterExternalEncoder(config_.encoder_settings.payload_type);
Peter Boströma4c76882016-03-03 16:29:02 +0100592}
593
Peter Boström905f8e72016-03-02 16:59:56 +0100594void VideoSendStream::ReconfigureVideoEncoder(
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000595 const VideoEncoderConfig& config) {
pbos@webrtc.org50fe3592015-01-29 12:33:07 +0000596 TRACE_EVENT0("webrtc", "VideoSendStream::(Re)configureVideoEncoder");
pbos@webrtc.orgad3b5a52014-10-24 09:23:21 +0000597 LOG(LS_INFO) << "(Re)configureVideoEncoder: " << config.ToString();
pbos14fe7082016-04-20 06:35:56 -0700598 RTC_DCHECK_GE(config_.rtp.ssrcs.size(), config.streams.size());
599 VideoCodec video_codec = VideoEncoderConfigToVideoCodec(
600 config, config_.encoder_settings.payload_name,
601 config_.encoder_settings.payload_type);
602 {
603 rtc::CritScope lock(&encoder_settings_crit_);
604 pending_encoder_settings_ = rtc::Optional<EncoderSettings>(
perkjbc75d972016-05-02 06:31:25 -0700605 {video_codec, config.min_transmit_bitrate_bps, config.streams});
pbos@webrtc.orgbbe0a852014-09-19 12:30:25 +0000606 }
pbos14fe7082016-04-20 06:35:56 -0700607 encoder_wakeup_event_.Set();
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000608}
609
pbos@webrtc.org273a4142014-12-01 15:23:21 +0000610VideoSendStream::Stats VideoSendStream::GetStats() {
stefan@webrtc.org168f23f2014-07-11 13:44:02 +0000611 return stats_proxy_.GetStats();
sprang@webrtc.orgccd42842014-01-07 09:54:34 +0000612}
613
solenberge5269742015-09-08 05:13:22 -0700614void VideoSendStream::OveruseDetected() {
615 if (config_.overuse_callback)
616 config_.overuse_callback->OnLoadUpdate(LoadObserver::kOveruse);
617}
618
619void VideoSendStream::NormalUsage() {
620 if (config_.overuse_callback)
621 config_.overuse_callback->OnLoadUpdate(LoadObserver::kUnderuse);
622}
623
perkjbc75d972016-05-02 06:31:25 -0700624int32_t VideoSendStream::Encoded(const EncodedImage& encoded_image,
625 const CodecSpecificInfo* codec_specific_info,
626 const RTPFragmentationHeader* fragmentation) {
627 // |encoded_frame_proxy_| forwards frames to |config_.post_encode_callback|;
628 encoded_frame_proxy_.Encoded(encoded_image, codec_specific_info,
629 fragmentation);
630 return payload_router_.Encoded(encoded_image, codec_specific_info,
631 fragmentation);
632}
633
Per83d09102016-04-15 14:59:13 +0200634void VideoSendStream::ConfigureProtection() {
635 // Enable NACK, FEC or both.
636 const bool enable_protection_nack = config_.rtp.nack.rtp_history_ms > 0;
637 bool enable_protection_fec = config_.rtp.fec.red_payload_type != -1;
638 // Payload types without picture ID cannot determine that a stream is complete
639 // without retransmitting FEC, so using FEC + NACK for H.264 (for instance) is
640 // a waste of bandwidth since FEC packets still have to be transmitted. Note
641 // that this is not the case with FLEXFEC.
642 if (enable_protection_nack &&
643 !PayloadTypeSupportsSkippingFecPackets(
644 config_.encoder_settings.payload_name)) {
645 LOG(LS_WARNING) << "Transmitting payload type without picture ID using"
646 "NACK+FEC is a waste of bandwidth since FEC packets "
647 "also have to be retransmitted. Disabling FEC.";
648 enable_protection_fec = false;
649 }
650
651 // Set to valid uint8_ts to be castable later without signed overflows.
652 uint8_t payload_type_red = 0;
653 uint8_t payload_type_fec = 0;
654 // TODO(changbin): Should set RTX for RED mapping in RTP sender in future.
655 // Validate payload types. If either RED or FEC payload types are set then
656 // both should be. If FEC is enabled then they both have to be set.
657 if (enable_protection_fec || config_.rtp.fec.red_payload_type != -1 ||
658 config_.rtp.fec.ulpfec_payload_type != -1) {
659 RTC_DCHECK_GE(config_.rtp.fec.red_payload_type, 0);
660 RTC_DCHECK_GE(config_.rtp.fec.ulpfec_payload_type, 0);
661 RTC_DCHECK_LE(config_.rtp.fec.red_payload_type, 127);
662 RTC_DCHECK_LE(config_.rtp.fec.ulpfec_payload_type, 127);
663 payload_type_red = static_cast<uint8_t>(config_.rtp.fec.red_payload_type);
664 payload_type_fec =
665 static_cast<uint8_t>(config_.rtp.fec.ulpfec_payload_type);
666 } else {
667 // Payload types unset.
668 RTC_DCHECK_EQ(config_.rtp.fec.red_payload_type, -1);
669 RTC_DCHECK_EQ(config_.rtp.fec.ulpfec_payload_type, -1);
670 }
671
672 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
673 // Set NACK.
674 rtp_rtcp->SetStorePacketsStatus(
675 enable_protection_nack || congestion_controller_->pacer(),
676 kMinSendSidePacketHistorySize);
677 // Set FEC.
678 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
679 rtp_rtcp->SetGenericFECStatus(enable_protection_fec, payload_type_red,
680 payload_type_fec);
681 }
682 }
683
684 vie_encoder_.SetProtectionMethod(enable_protection_nack,
685 enable_protection_fec);
686}
687
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000688void VideoSendStream::ConfigureSsrcs() {
Peter Boström723ead82016-02-22 15:14:01 +0100689 // Configure regular SSRCs.
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000690 for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
691 uint32_t ssrc = config_.rtp.ssrcs[i];
Peter Boström723ead82016-02-22 15:14:01 +0100692 RtpRtcp* const rtp_rtcp = rtp_rtcp_modules_[i];
693 rtp_rtcp->SetSSRC(ssrc);
694
695 // Restore RTP state if previous existed.
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000696 RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
697 if (it != suspended_ssrcs_.end())
Per83d09102016-04-15 14:59:13 +0200698 rtp_rtcp->SetRtpState(it->second);
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000699 }
700
Peter Boström723ead82016-02-22 15:14:01 +0100701 // Set up RTX if available.
702 if (config_.rtp.rtx.ssrcs.empty())
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000703 return;
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000704
Peter Boström723ead82016-02-22 15:14:01 +0100705 // Configure RTX SSRCs.
henrikg91d6ede2015-09-17 00:24:34 -0700706 RTC_DCHECK_EQ(config_.rtp.rtx.ssrcs.size(), config_.rtp.ssrcs.size());
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000707 for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
708 uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
Peter Boström723ead82016-02-22 15:14:01 +0100709 RtpRtcp* const rtp_rtcp = rtp_rtcp_modules_[i];
710 rtp_rtcp->SetRtxSsrc(ssrc);
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000711 RtpStateMap::iterator it = suspended_ssrcs_.find(ssrc);
712 if (it != suspended_ssrcs_.end())
Per83d09102016-04-15 14:59:13 +0200713 rtp_rtcp->SetRtxState(it->second);
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000714 }
715
Peter Boström723ead82016-02-22 15:14:01 +0100716 // Configure RTX payload types.
henrikg91d6ede2015-09-17 00:24:34 -0700717 RTC_DCHECK_GE(config_.rtp.rtx.payload_type, 0);
Peter Boström723ead82016-02-22 15:14:01 +0100718 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
719 rtp_rtcp->SetRtxSendPayloadType(config_.rtp.rtx.payload_type,
720 config_.encoder_settings.payload_type);
721 rtp_rtcp->SetRtxSendStatus(kRtxRetransmitted | kRtxRedundantPayloads);
722 }
Stefan Holmer10880012016-02-03 13:29:59 +0100723 if (config_.rtp.fec.red_payload_type != -1 &&
724 config_.rtp.fec.red_rtx_payload_type != -1) {
Peter Boström723ead82016-02-22 15:14:01 +0100725 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
726 rtp_rtcp->SetRtxSendPayloadType(config_.rtp.fec.red_rtx_payload_type,
727 config_.rtp.fec.red_payload_type);
728 }
Stefan Holmer10880012016-02-03 13:29:59 +0100729 }
pbos@webrtc.orgbe9d2a42014-06-30 13:19:09 +0000730}
731
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000732std::map<uint32_t, RtpState> VideoSendStream::GetRtpStates() const {
733 std::map<uint32_t, RtpState> rtp_states;
734 for (size_t i = 0; i < config_.rtp.ssrcs.size(); ++i) {
735 uint32_t ssrc = config_.rtp.ssrcs[i];
Per83d09102016-04-15 14:59:13 +0200736 RTC_DCHECK_EQ(ssrc, rtp_rtcp_modules_[i]->SSRC());
737 rtp_states[ssrc] = rtp_rtcp_modules_[i]->GetRtpState();
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000738 }
739
740 for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
741 uint32_t ssrc = config_.rtp.rtx.ssrcs[i];
Per83d09102016-04-15 14:59:13 +0200742 rtp_states[ssrc] = rtp_rtcp_modules_[i]->GetRtxState();
pbos@webrtc.org2bb1bda2014-07-07 13:06:48 +0000743 }
744
745 return rtp_states;
746}
747
mflodman0e7e2592015-11-12 21:02:42 -0800748int VideoSendStream::GetPaddingNeededBps() const {
Peter Boström8c66a002016-02-11 13:51:10 +0100749 return vie_encoder_.GetPaddingNeededBps();
mflodman0e7e2592015-11-12 21:02:42 -0800750}
mflodman86aabb22016-03-11 15:44:32 +0100751
752void VideoSendStream::OnBitrateUpdated(uint32_t bitrate_bps,
753 uint8_t fraction_loss,
754 int64_t rtt) {
perkjbc75d972016-05-02 06:31:25 -0700755 payload_router_.SetTargetSendBitrate(bitrate_bps);
mflodman86aabb22016-03-11 15:44:32 +0100756 vie_encoder_.OnBitrateUpdated(bitrate_bps, fraction_loss, rtt);
757}
758
Per83d09102016-04-15 14:59:13 +0200759int VideoSendStream::ProtectionRequest(const FecProtectionParams* delta_params,
760 const FecProtectionParams* key_params,
761 uint32_t* sent_video_rate_bps,
762 uint32_t* sent_nack_rate_bps,
763 uint32_t* sent_fec_rate_bps) {
764 *sent_video_rate_bps = 0;
765 *sent_nack_rate_bps = 0;
766 *sent_fec_rate_bps = 0;
767 for (RtpRtcp* rtp_rtcp : rtp_rtcp_modules_) {
768 uint32_t not_used = 0;
769 uint32_t module_video_rate = 0;
770 uint32_t module_fec_rate = 0;
771 uint32_t module_nack_rate = 0;
772 rtp_rtcp->SetFecParameters(delta_params, key_params);
773 rtp_rtcp->BitrateSent(&not_used, &module_video_rate, &module_fec_rate,
774 &module_nack_rate);
775 *sent_video_rate_bps += module_video_rate;
776 *sent_nack_rate_bps += module_nack_rate;
777 *sent_fec_rate_bps += module_fec_rate;
778 }
779 return 0;
780}
781
pbos@webrtc.org29d58392013-05-16 12:08:03 +0000782} // namespace internal
783} // namespace webrtc