OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 12 matching lines...) Expand all Loading... |
23 | 23 |
24 namespace webrtc { | 24 namespace webrtc { |
25 namespace vcm { | 25 namespace vcm { |
26 | 26 |
27 VideoSender::VideoSender(Clock* clock, | 27 VideoSender::VideoSender(Clock* clock, |
28 EncodedImageCallback* post_encode_callback, | 28 EncodedImageCallback* post_encode_callback, |
29 VideoEncoderRateObserver* encoder_rate_observer, | 29 VideoEncoderRateObserver* encoder_rate_observer, |
30 VCMQMSettingsCallback* qm_settings_callback) | 30 VCMQMSettingsCallback* qm_settings_callback) |
31 : clock_(clock), | 31 : clock_(clock), |
32 process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), | 32 process_crit_sect_(CriticalSectionWrapper::CreateCriticalSection()), |
33 _sendCritSect(CriticalSectionWrapper::CreateCriticalSection()), | |
34 _encoder(nullptr), | 33 _encoder(nullptr), |
35 _encodedFrameCallback(post_encode_callback), | 34 _encodedFrameCallback(post_encode_callback), |
36 _nextFrameTypes(1, kVideoFrameDelta), | 35 _nextFrameTypes(1, kVideoFrameDelta), |
37 _mediaOpt(clock_), | 36 _mediaOpt(clock_), |
38 _sendStatsCallback(nullptr), | 37 _sendStatsCallback(nullptr), |
39 _codecDataBase(encoder_rate_observer), | 38 _codecDataBase(encoder_rate_observer), |
40 frame_dropper_enabled_(true), | 39 frame_dropper_enabled_(true), |
41 _sendStatsTimer(1000, clock_), | 40 _sendStatsTimer(1000, clock_), |
42 current_codec_(), | 41 current_codec_(), |
43 qm_settings_callback_(qm_settings_callback), | 42 qm_settings_callback_(qm_settings_callback), |
44 protection_callback_(nullptr) { | 43 protection_callback_(nullptr) { |
45 encoder_params_ = {0, 0, 0, 0, false}; | 44 encoder_params_ = {0, 0, 0, 0, false}; |
46 // Allow VideoSender to be created on one thread but used on another, post | 45 // Allow VideoSender to be created on one thread but used on another, post |
47 // construction. This is currently how this class is being used by at least | 46 // construction. This is currently how this class is being used by at least |
48 // one external project (diffractor). | 47 // one external project (diffractor). |
49 _mediaOpt.EnableQM(qm_settings_callback_ != nullptr); | 48 _mediaOpt.EnableQM(qm_settings_callback_ != nullptr); |
50 _mediaOpt.Reset(); | 49 _mediaOpt.Reset(); |
51 main_thread_.DetachFromThread(); | 50 main_thread_.DetachFromThread(); |
52 } | 51 } |
53 | 52 |
54 VideoSender::~VideoSender() { | 53 VideoSender::~VideoSender() {} |
55 delete _sendCritSect; | |
56 } | |
57 | 54 |
58 int32_t VideoSender::Process() { | 55 int32_t VideoSender::Process() { |
59 int32_t returnValue = VCM_OK; | 56 int32_t returnValue = VCM_OK; |
60 | 57 |
61 if (_sendStatsTimer.TimeUntilProcess() == 0) { | 58 if (_sendStatsTimer.TimeUntilProcess() == 0) { |
62 _sendStatsTimer.Processed(); | 59 _sendStatsTimer.Processed(); |
63 CriticalSectionScoped cs(process_crit_sect_.get()); | 60 CriticalSectionScoped cs(process_crit_sect_.get()); |
64 if (_sendStatsCallback != nullptr) { | 61 if (_sendStatsCallback != nullptr) { |
65 uint32_t bitRate = _mediaOpt.SentBitRate(); | 62 uint32_t bitRate = _mediaOpt.SentBitRate(); |
66 uint32_t frameRate = _mediaOpt.SentFrameRate(); | 63 uint32_t frameRate = _mediaOpt.SentFrameRate(); |
(...skipping 14 matching lines...) Expand all Loading... |
81 | 78 |
82 int64_t VideoSender::TimeUntilNextProcess() { | 79 int64_t VideoSender::TimeUntilNextProcess() { |
83 return _sendStatsTimer.TimeUntilProcess(); | 80 return _sendStatsTimer.TimeUntilProcess(); |
84 } | 81 } |
85 | 82 |
86 // Register the send codec to be used. | 83 // Register the send codec to be used. |
87 int32_t VideoSender::RegisterSendCodec(const VideoCodec* sendCodec, | 84 int32_t VideoSender::RegisterSendCodec(const VideoCodec* sendCodec, |
88 uint32_t numberOfCores, | 85 uint32_t numberOfCores, |
89 uint32_t maxPayloadSize) { | 86 uint32_t maxPayloadSize) { |
90 DCHECK(main_thread_.CalledOnValidThread()); | 87 DCHECK(main_thread_.CalledOnValidThread()); |
91 CriticalSectionScoped cs(_sendCritSect); | 88 rtc::CritScope lock(&send_crit_); |
92 if (sendCodec == nullptr) { | 89 if (sendCodec == nullptr) { |
93 return VCM_PARAMETER_ERROR; | 90 return VCM_PARAMETER_ERROR; |
94 } | 91 } |
95 | 92 |
96 bool ret = _codecDataBase.SetSendCodec( | 93 bool ret = _codecDataBase.SetSendCodec( |
97 sendCodec, numberOfCores, maxPayloadSize, &_encodedFrameCallback); | 94 sendCodec, numberOfCores, maxPayloadSize, &_encodedFrameCallback); |
98 | 95 |
99 // Update encoder regardless of result to make sure that we're not holding on | 96 // Update encoder regardless of result to make sure that we're not holding on |
100 // to a deleted instance. | 97 // to a deleted instance. |
101 _encoder = _codecDataBase.GetEncoder(); | 98 _encoder = _codecDataBase.GetEncoder(); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 maxPayloadSize); | 131 maxPayloadSize); |
135 return VCM_OK; | 132 return VCM_OK; |
136 } | 133 } |
137 | 134 |
138 const VideoCodec& VideoSender::GetSendCodec() const { | 135 const VideoCodec& VideoSender::GetSendCodec() const { |
139 DCHECK(main_thread_.CalledOnValidThread()); | 136 DCHECK(main_thread_.CalledOnValidThread()); |
140 return current_codec_; | 137 return current_codec_; |
141 } | 138 } |
142 | 139 |
143 int32_t VideoSender::SendCodecBlocking(VideoCodec* currentSendCodec) const { | 140 int32_t VideoSender::SendCodecBlocking(VideoCodec* currentSendCodec) const { |
144 CriticalSectionScoped cs(_sendCritSect); | 141 rtc::CritScope lock(&send_crit_); |
145 if (currentSendCodec == nullptr) { | 142 if (currentSendCodec == nullptr) { |
146 return VCM_PARAMETER_ERROR; | 143 return VCM_PARAMETER_ERROR; |
147 } | 144 } |
148 return _codecDataBase.SendCodec(currentSendCodec) ? 0 : -1; | 145 return _codecDataBase.SendCodec(currentSendCodec) ? 0 : -1; |
149 } | 146 } |
150 | 147 |
151 VideoCodecType VideoSender::SendCodecBlocking() const { | 148 VideoCodecType VideoSender::SendCodecBlocking() const { |
152 CriticalSectionScoped cs(_sendCritSect); | 149 rtc::CritScope lock(&send_crit_); |
153 return _codecDataBase.SendCodec(); | 150 return _codecDataBase.SendCodec(); |
154 } | 151 } |
155 | 152 |
156 // Register an external decoder object. | 153 // Register an external decoder object. |
157 // This can not be used together with external decoder callbacks. | 154 // This can not be used together with external decoder callbacks. |
158 int32_t VideoSender::RegisterExternalEncoder(VideoEncoder* externalEncoder, | 155 int32_t VideoSender::RegisterExternalEncoder(VideoEncoder* externalEncoder, |
159 uint8_t payloadType, | 156 uint8_t payloadType, |
160 bool internalSource /*= false*/) { | 157 bool internalSource /*= false*/) { |
161 DCHECK(main_thread_.CalledOnValidThread()); | 158 DCHECK(main_thread_.CalledOnValidThread()); |
162 | 159 |
163 CriticalSectionScoped cs(_sendCritSect); | 160 rtc::CritScope lock(&send_crit_); |
164 | 161 |
165 if (externalEncoder == nullptr) { | 162 if (externalEncoder == nullptr) { |
166 bool wasSendCodec = false; | 163 bool wasSendCodec = false; |
167 const bool ret = | 164 const bool ret = |
168 _codecDataBase.DeregisterExternalEncoder(payloadType, &wasSendCodec); | 165 _codecDataBase.DeregisterExternalEncoder(payloadType, &wasSendCodec); |
169 if (wasSendCodec) { | 166 if (wasSendCodec) { |
170 // Make sure the VCM doesn't use the de-registered codec | 167 // Make sure the VCM doesn't use the de-registered codec |
171 _encoder = nullptr; | 168 _encoder = nullptr; |
172 } | 169 } |
173 return ret ? 0 : -1; | 170 return ret ? 0 : -1; |
174 } | 171 } |
175 _codecDataBase.RegisterExternalEncoder( | 172 _codecDataBase.RegisterExternalEncoder( |
176 externalEncoder, payloadType, internalSource); | 173 externalEncoder, payloadType, internalSource); |
177 return 0; | 174 return 0; |
178 } | 175 } |
179 | 176 |
180 // Get codec config parameters | 177 // Get codec config parameters |
181 int32_t VideoSender::CodecConfigParameters(uint8_t* buffer, | 178 int32_t VideoSender::CodecConfigParameters(uint8_t* buffer, |
182 int32_t size) const { | 179 int32_t size) const { |
183 CriticalSectionScoped cs(_sendCritSect); | 180 rtc::CritScope lock(&send_crit_); |
184 if (_encoder != nullptr) { | 181 if (_encoder != nullptr) { |
185 return _encoder->CodecConfigParameters(buffer, size); | 182 return _encoder->CodecConfigParameters(buffer, size); |
186 } | 183 } |
187 return VCM_UNINITIALIZED; | 184 return VCM_UNINITIALIZED; |
188 } | 185 } |
189 | 186 |
190 // TODO(andresp): Make const once media_opt is thread-safe and this has a | 187 // TODO(andresp): Make const once media_opt is thread-safe and this has a |
191 // pointer to it. | 188 // pointer to it. |
192 int32_t VideoSender::SentFrameCount(VCMFrameCount* frameCount) { | 189 int32_t VideoSender::SentFrameCount(VCMFrameCount* frameCount) { |
193 *frameCount = _mediaOpt.SentFrameCount(); | 190 *frameCount = _mediaOpt.SentFrameCount(); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 | 228 |
232 uint32_t input_frame_rate = _mediaOpt.InputFrameRate(); | 229 uint32_t input_frame_rate = _mediaOpt.InputFrameRate(); |
233 | 230 |
234 rtc::CritScope cs(¶ms_lock_); | 231 rtc::CritScope cs(¶ms_lock_); |
235 encoder_params_ = | 232 encoder_params_ = |
236 EncoderParameters{target_rate, lossRate, rtt, input_frame_rate, true}; | 233 EncoderParameters{target_rate, lossRate, rtt, input_frame_rate, true}; |
237 | 234 |
238 return VCM_OK; | 235 return VCM_OK; |
239 } | 236 } |
240 | 237 |
241 int32_t VideoSender::UpdateEncoderParameters() { | 238 void VideoSender::SetEncoderParameters(EncoderParameters params) { |
242 EncoderParameters params; | |
243 { | |
244 rtc::CritScope cs(¶ms_lock_); | |
245 params = encoder_params_; | |
246 encoder_params_.updated = false; | |
247 } | |
248 | |
249 if (!params.updated || params.target_bitrate == 0) | 239 if (!params.updated || params.target_bitrate == 0) |
250 return VCM_OK; | 240 return; |
251 | |
252 CriticalSectionScoped sendCs(_sendCritSect); | |
253 int32_t ret = VCM_UNINITIALIZED; | |
254 static_assert(VCM_UNINITIALIZED < 0, "VCM_UNINITIALIZED must be negative."); | |
255 | 241 |
256 if (params.input_frame_rate == 0) { | 242 if (params.input_frame_rate == 0) { |
257 // No frame rate estimate available, use default. | 243 // No frame rate estimate available, use default. |
258 params.input_frame_rate = current_codec_.maxFramerate; | 244 params.input_frame_rate = current_codec_.maxFramerate; |
259 } | 245 } |
260 if (_encoder != nullptr) { | 246 if (_encoder != nullptr) { |
261 ret = _encoder->SetChannelParameters(params.loss_rate, params.rtt); | 247 _encoder->SetChannelParameters(params.loss_rate, params.rtt); |
262 if (ret >= 0) { | 248 _encoder->SetRates(params.target_bitrate, params.input_frame_rate); |
263 ret = _encoder->SetRates(params.target_bitrate, params.input_frame_rate); | |
264 } | |
265 } | 249 } |
266 return ret; | 250 return; |
267 } | 251 } |
268 | 252 |
269 int32_t VideoSender::RegisterTransportCallback( | 253 int32_t VideoSender::RegisterTransportCallback( |
270 VCMPacketizationCallback* transport) { | 254 VCMPacketizationCallback* transport) { |
271 CriticalSectionScoped cs(_sendCritSect); | 255 rtc::CritScope lock(&send_crit_); |
272 _encodedFrameCallback.SetMediaOpt(&_mediaOpt); | 256 _encodedFrameCallback.SetMediaOpt(&_mediaOpt); |
273 _encodedFrameCallback.SetTransportCallback(transport); | 257 _encodedFrameCallback.SetTransportCallback(transport); |
274 return VCM_OK; | 258 return VCM_OK; |
275 } | 259 } |
276 | 260 |
277 // Register video output information callback which will be called to deliver | 261 // Register video output information callback which will be called to deliver |
278 // information about the video stream produced by the encoder, for instance the | 262 // information about the video stream produced by the encoder, for instance the |
279 // average frame rate and bit rate. | 263 // average frame rate and bit rate. |
280 int32_t VideoSender::RegisterSendStatisticsCallback( | 264 int32_t VideoSender::RegisterSendStatisticsCallback( |
281 VCMSendStatisticsCallback* sendStats) { | 265 VCMSendStatisticsCallback* sendStats) { |
282 CriticalSectionScoped cs(process_crit_sect_.get()); | 266 CriticalSectionScoped cs(process_crit_sect_.get()); |
283 _sendStatsCallback = sendStats; | 267 _sendStatsCallback = sendStats; |
284 return VCM_OK; | 268 return VCM_OK; |
285 } | 269 } |
286 | 270 |
287 // Register a video protection callback which will be called to deliver the | 271 // Register a video protection callback which will be called to deliver the |
288 // requested FEC rate and NACK status (on/off). | 272 // requested FEC rate and NACK status (on/off). |
289 // Note: this callback is assumed to only be registered once and before it is | 273 // Note: this callback is assumed to only be registered once and before it is |
290 // used in this class. | 274 // used in this class. |
291 int32_t VideoSender::RegisterProtectionCallback( | 275 int32_t VideoSender::RegisterProtectionCallback( |
292 VCMProtectionCallback* protection_callback) { | 276 VCMProtectionCallback* protection_callback) { |
293 DCHECK(protection_callback == nullptr || protection_callback_ == nullptr); | 277 DCHECK(protection_callback == nullptr || protection_callback_ == nullptr); |
294 protection_callback_ = protection_callback; | 278 protection_callback_ = protection_callback; |
295 return VCM_OK; | 279 return VCM_OK; |
296 } | 280 } |
297 | 281 |
298 // Enable or disable a video protection method. | 282 // Enable or disable a video protection method. |
299 void VideoSender::SetVideoProtection(VCMVideoProtection videoProtection) { | 283 void VideoSender::SetVideoProtection(VCMVideoProtection videoProtection) { |
300 CriticalSectionScoped cs(_sendCritSect); | 284 rtc::CritScope lock(&send_crit_); |
301 switch (videoProtection) { | 285 switch (videoProtection) { |
302 case kProtectionNone: | 286 case kProtectionNone: |
303 _mediaOpt.SetProtectionMethod(media_optimization::kNone); | 287 _mediaOpt.SetProtectionMethod(media_optimization::kNone); |
304 break; | 288 break; |
305 case kProtectionNack: | 289 case kProtectionNack: |
306 _mediaOpt.SetProtectionMethod(media_optimization::kNack); | 290 _mediaOpt.SetProtectionMethod(media_optimization::kNack); |
307 break; | 291 break; |
308 case kProtectionNackFEC: | 292 case kProtectionNackFEC: |
309 _mediaOpt.SetProtectionMethod(media_optimization::kNackFec); | 293 _mediaOpt.SetProtectionMethod(media_optimization::kNackFec); |
310 break; | 294 break; |
311 case kProtectionFEC: | 295 case kProtectionFEC: |
312 _mediaOpt.SetProtectionMethod(media_optimization::kFec); | 296 _mediaOpt.SetProtectionMethod(media_optimization::kFec); |
313 break; | 297 break; |
314 } | 298 } |
315 } | 299 } |
316 // Add one raw video frame to the encoder, blocking. | 300 // Add one raw video frame to the encoder, blocking. |
317 int32_t VideoSender::AddVideoFrame(const VideoFrame& videoFrame, | 301 int32_t VideoSender::AddVideoFrame(const VideoFrame& videoFrame, |
318 const VideoContentMetrics* contentMetrics, | 302 const VideoContentMetrics* contentMetrics, |
319 const CodecSpecificInfo* codecSpecificInfo) { | 303 const CodecSpecificInfo* codecSpecificInfo) { |
320 UpdateEncoderParameters(); | 304 EncoderParameters encoder_params; |
321 CriticalSectionScoped cs(_sendCritSect); | 305 { |
| 306 rtc::CritScope lock(¶ms_lock_); |
| 307 encoder_params = encoder_params_; |
| 308 encoder_params_.updated = false; |
| 309 } |
| 310 rtc::CritScope lock(&send_crit_); |
| 311 SetEncoderParameters(encoder_params); |
322 if (_encoder == nullptr) { | 312 if (_encoder == nullptr) { |
323 return VCM_UNINITIALIZED; | 313 return VCM_UNINITIALIZED; |
324 } | 314 } |
325 // TODO(holmer): Add support for dropping frames per stream. Currently we | 315 // TODO(holmer): Add support for dropping frames per stream. Currently we |
326 // only have one frame dropper for all streams. | 316 // only have one frame dropper for all streams. |
327 if (_nextFrameTypes[0] == kFrameEmpty) { | 317 if (_nextFrameTypes[0] == kFrameEmpty) { |
328 return VCM_OK; | 318 return VCM_OK; |
329 } | 319 } |
330 if (_mediaOpt.DropFrame()) { | 320 if (_mediaOpt.DropFrame()) { |
331 _encoder->OnDroppedFrame(); | 321 _encoder->OnDroppedFrame(); |
(...skipping 23 matching lines...) Expand all Loading... |
355 } | 345 } |
356 for (size_t i = 0; i < _nextFrameTypes.size(); ++i) { | 346 for (size_t i = 0; i < _nextFrameTypes.size(); ++i) { |
357 _nextFrameTypes[i] = kVideoFrameDelta; // Default frame type. | 347 _nextFrameTypes[i] = kVideoFrameDelta; // Default frame type. |
358 } | 348 } |
359 if (qm_settings_callback_) | 349 if (qm_settings_callback_) |
360 qm_settings_callback_->SetTargetFramerate(_encoder->GetTargetFramerate()); | 350 qm_settings_callback_->SetTargetFramerate(_encoder->GetTargetFramerate()); |
361 return VCM_OK; | 351 return VCM_OK; |
362 } | 352 } |
363 | 353 |
364 int32_t VideoSender::IntraFrameRequest(int stream_index) { | 354 int32_t VideoSender::IntraFrameRequest(int stream_index) { |
365 CriticalSectionScoped cs(_sendCritSect); | 355 rtc::CritScope lock(&send_crit_); |
366 if (stream_index < 0 || | 356 if (stream_index < 0 || |
367 static_cast<unsigned int>(stream_index) >= _nextFrameTypes.size()) { | 357 static_cast<unsigned int>(stream_index) >= _nextFrameTypes.size()) { |
368 return -1; | 358 return -1; |
369 } | 359 } |
370 _nextFrameTypes[stream_index] = kVideoFrameKey; | 360 _nextFrameTypes[stream_index] = kVideoFrameKey; |
371 if (_encoder != nullptr && _encoder->InternalSource()) { | 361 if (_encoder != nullptr && _encoder->InternalSource()) { |
372 // Try to request the frame if we have an external encoder with | 362 // Try to request the frame if we have an external encoder with |
373 // internal source since AddVideoFrame never will be called. | 363 // internal source since AddVideoFrame never will be called. |
374 if (_encoder->RequestFrame(_nextFrameTypes) == WEBRTC_VIDEO_CODEC_OK) { | 364 if (_encoder->RequestFrame(_nextFrameTypes) == WEBRTC_VIDEO_CODEC_OK) { |
375 _nextFrameTypes[stream_index] = kVideoFrameDelta; | 365 _nextFrameTypes[stream_index] = kVideoFrameDelta; |
376 } | 366 } |
377 } | 367 } |
378 return VCM_OK; | 368 return VCM_OK; |
379 } | 369 } |
380 | 370 |
381 int32_t VideoSender::EnableFrameDropper(bool enable) { | 371 int32_t VideoSender::EnableFrameDropper(bool enable) { |
382 CriticalSectionScoped cs(_sendCritSect); | 372 rtc::CritScope lock(&send_crit_); |
383 frame_dropper_enabled_ = enable; | 373 frame_dropper_enabled_ = enable; |
384 _mediaOpt.EnableFrameDropper(enable); | 374 _mediaOpt.EnableFrameDropper(enable); |
385 return VCM_OK; | 375 return VCM_OK; |
386 } | 376 } |
387 | 377 |
388 void VideoSender::SuspendBelowMinBitrate() { | 378 void VideoSender::SuspendBelowMinBitrate() { |
389 DCHECK(main_thread_.CalledOnValidThread()); | 379 DCHECK(main_thread_.CalledOnValidThread()); |
390 int threshold_bps; | 380 int threshold_bps; |
391 if (current_codec_.numberOfSimulcastStreams == 0) { | 381 if (current_codec_.numberOfSimulcastStreams == 0) { |
392 threshold_bps = current_codec_.minBitrate * 1000; | 382 threshold_bps = current_codec_.minBitrate * 1000; |
393 } else { | 383 } else { |
394 threshold_bps = current_codec_.simulcastStream[0].minBitrate * 1000; | 384 threshold_bps = current_codec_.simulcastStream[0].minBitrate * 1000; |
395 } | 385 } |
396 // Set the hysteresis window to be at 10% of the threshold, but at least | 386 // Set the hysteresis window to be at 10% of the threshold, but at least |
397 // 10 kbps. | 387 // 10 kbps. |
398 int window_bps = std::max(threshold_bps / 10, 10000); | 388 int window_bps = std::max(threshold_bps / 10, 10000); |
399 _mediaOpt.SuspendBelowMinBitrate(threshold_bps, window_bps); | 389 _mediaOpt.SuspendBelowMinBitrate(threshold_bps, window_bps); |
400 } | 390 } |
401 | 391 |
402 bool VideoSender::VideoSuspended() const { | 392 bool VideoSender::VideoSuspended() const { |
403 CriticalSectionScoped cs(_sendCritSect); | 393 rtc::CritScope lock(&send_crit_); |
404 return _mediaOpt.IsVideoSuspended(); | 394 return _mediaOpt.IsVideoSuspended(); |
405 } | 395 } |
406 } // namespace vcm | 396 } // namespace vcm |
407 } // namespace webrtc | 397 } // namespace webrtc |
OLD | NEW |