OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cast/net/cast_transport_impl.h" | 5 #include "media/cast/net/cast_transport_impl.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <algorithm> | 8 #include <algorithm> |
9 #include <string> | 9 #include <string> |
10 #include <utility> | 10 #include <utility> |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
84 | 84 |
85 private: | 85 private: |
86 const uint32_t rtp_sender_ssrc_; | 86 const uint32_t rtp_sender_ssrc_; |
87 const std::unique_ptr<RtcpObserver> rtcp_observer_; | 87 const std::unique_ptr<RtcpObserver> rtcp_observer_; |
88 const EventMediaType media_type_; | 88 const EventMediaType media_type_; |
89 CastTransportImpl* const cast_transport_impl_; | 89 CastTransportImpl* const cast_transport_impl_; |
90 | 90 |
91 DISALLOW_COPY_AND_ASSIGN(RtcpClient); | 91 DISALLOW_COPY_AND_ASSIGN(RtcpClient); |
92 }; | 92 }; |
93 | 93 |
94 struct CastTransportImpl::RtpStreamSession { | |
95 explicit RtpStreamSession(bool is_audio_stream) : is_audio(is_audio_stream) {} | |
96 | |
97 // Packetizer for audio and video frames. | |
98 std::unique_ptr<RtpSender> rtp_sender; | |
99 | |
100 // Maintains RTCP session for audio and video. | |
101 std::unique_ptr<SenderRtcpSession> rtcp_session; | |
102 | |
103 // RTCP observer for SenderRtcpSession. | |
104 std::unique_ptr<RtcpObserver> rtcp_observer; | |
105 | |
106 // Encrypts data in EncodedFrames before they are sent. Note that it's | |
107 // important for the encryption to happen here, in code that would execute in | |
108 // the main browser process, for security reasons. This helps to mitigate | |
109 // the damage that could be caused by a compromised renderer process. | |
110 TransportEncryptionHandler encryptor; | |
111 | |
112 bool is_audio; | |
miu
2016/07/06 21:45:42
const please ;)
xjz
2016/07/13 20:31:17
Done.
| |
113 }; | |
114 | |
94 CastTransportImpl::CastTransportImpl( | 115 CastTransportImpl::CastTransportImpl( |
95 base::TickClock* clock, | 116 base::TickClock* clock, |
96 base::TimeDelta logging_flush_interval, | 117 base::TimeDelta logging_flush_interval, |
97 std::unique_ptr<Client> client, | 118 std::unique_ptr<Client> client, |
98 std::unique_ptr<PacketTransport> transport, | 119 std::unique_ptr<PacketTransport> transport, |
99 const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner) | 120 const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner) |
100 : clock_(clock), | 121 : clock_(clock), |
101 logging_flush_interval_(logging_flush_interval), | 122 logging_flush_interval_(logging_flush_interval), |
102 transport_client_(std::move(client)), | 123 transport_client_(std::move(client)), |
103 transport_(std::move(transport)), | 124 transport_(std::move(transport)), |
(...skipping 18 matching lines...) Expand all Loading... | |
122 logging_flush_interval_); | 143 logging_flush_interval_); |
123 } | 144 } |
124 transport_->StartReceiving( | 145 transport_->StartReceiving( |
125 base::Bind(&CastTransportImpl::OnReceivedPacket, base::Unretained(this))); | 146 base::Bind(&CastTransportImpl::OnReceivedPacket, base::Unretained(this))); |
126 } | 147 } |
127 | 148 |
128 CastTransportImpl::~CastTransportImpl() { | 149 CastTransportImpl::~CastTransportImpl() { |
129 transport_->StopReceiving(); | 150 transport_->StopReceiving(); |
130 } | 151 } |
131 | 152 |
132 void CastTransportImpl::InitializeAudio( | 153 void CastTransportImpl::InitializeStream( |
133 const CastTransportRtpConfig& config, | 154 const CastTransportRtpConfig& config, |
134 std::unique_ptr<RtcpObserver> rtcp_observer) { | 155 std::unique_ptr<RtcpObserver> rtcp_observer) { |
156 if (sessions_.find(config.ssrc) != sessions_.end()) | |
157 DVLOG(1) << "Initialize an existing stream on RTP sender." << config.ssrc; | |
158 | |
135 LOG_IF(WARNING, config.aes_key.empty() || config.aes_iv_mask.empty()) | 159 LOG_IF(WARNING, config.aes_key.empty() || config.aes_iv_mask.empty()) |
136 << "Unsafe to send audio with encryption DISABLED."; | 160 << "Unsafe to send stream with encryption DISABLED."; |
137 if (!audio_encryptor_.Initialize(config.aes_key, config.aes_iv_mask)) { | 161 |
138 transport_client_->OnStatusChanged(TRANSPORT_AUDIO_UNINITIALIZED); | 162 bool is_audio = config.rtp_payload_type <= RtpPayloadType::AUDIO_LAST; |
163 std::unique_ptr<RtpStreamSession> session(new RtpStreamSession(is_audio)); | |
164 | |
165 if (!session->encryptor.Initialize(config.aes_key, config.aes_iv_mask)) { | |
166 transport_client_->OnStatusChanged(TRANSPORT_STREAM_UNINITIALIZED); | |
139 return; | 167 return; |
140 } | 168 } |
141 | 169 |
142 audio_sender_.reset(new RtpSender(transport_task_runner_, &pacer_)); | 170 session->rtp_sender.reset(new RtpSender(transport_task_runner_, &pacer_)); |
143 if (audio_sender_->Initialize(config)) { | 171 if (!session->rtp_sender->Initialize(config)) { |
144 // Audio packets have a higher priority. | 172 session->rtp_sender.reset(); |
145 pacer_.RegisterAudioSsrc(config.ssrc); | 173 transport_client_->OnStatusChanged(TRANSPORT_STREAM_UNINITIALIZED); |
146 pacer_.RegisterPrioritySsrc(config.ssrc); | |
147 transport_client_->OnStatusChanged(TRANSPORT_AUDIO_INITIALIZED); | |
148 } else { | |
149 audio_sender_.reset(); | |
150 transport_client_->OnStatusChanged(TRANSPORT_AUDIO_UNINITIALIZED); | |
151 return; | 174 return; |
152 } | 175 } |
153 | 176 |
154 audio_rtcp_observer_.reset( | 177 pacer_.RegisterSsrc(config.ssrc, is_audio); |
155 new RtcpClient(std::move(rtcp_observer), config.ssrc, AUDIO_EVENT, this)); | 178 // Audio packets have a higher priority. |
156 audio_rtcp_session_.reset( | 179 if (is_audio) |
157 new SenderRtcpSession(clock_, &pacer_, audio_rtcp_observer_.get(), | 180 pacer_.RegisterPrioritySsrc(config.ssrc); |
181 | |
182 session->rtcp_observer.reset( | |
183 new RtcpClient(std::move(rtcp_observer), config.ssrc, | |
184 is_audio ? AUDIO_EVENT : VIDEO_EVENT, this)); | |
185 session->rtcp_session.reset( | |
186 new SenderRtcpSession(clock_, &pacer_, session->rtcp_observer.get(), | |
158 config.ssrc, config.feedback_ssrc)); | 187 config.ssrc, config.feedback_ssrc)); |
159 pacer_.RegisterAudioSsrc(config.ssrc); | 188 |
160 valid_sender_ssrcs_.insert(config.feedback_ssrc); | 189 valid_sender_ssrcs_.insert(config.feedback_ssrc); |
161 transport_client_->OnStatusChanged(TRANSPORT_AUDIO_INITIALIZED); | 190 transport_client_->OnStatusChanged(TRANSPORT_STREAM_INITIALIZED); |
162 } | 191 sessions_[config.ssrc] = std::move(session); |
163 | |
164 void CastTransportImpl::InitializeVideo( | |
165 const CastTransportRtpConfig& config, | |
166 std::unique_ptr<RtcpObserver> rtcp_observer) { | |
167 LOG_IF(WARNING, config.aes_key.empty() || config.aes_iv_mask.empty()) | |
168 << "Unsafe to send video with encryption DISABLED."; | |
169 if (!video_encryptor_.Initialize(config.aes_key, config.aes_iv_mask)) { | |
170 transport_client_->OnStatusChanged(TRANSPORT_VIDEO_UNINITIALIZED); | |
171 return; | |
172 } | |
173 | |
174 video_sender_.reset(new RtpSender(transport_task_runner_, &pacer_)); | |
175 if (!video_sender_->Initialize(config)) { | |
176 video_sender_.reset(); | |
177 transport_client_->OnStatusChanged(TRANSPORT_VIDEO_UNINITIALIZED); | |
178 return; | |
179 } | |
180 | |
181 video_rtcp_observer_.reset( | |
182 new RtcpClient(std::move(rtcp_observer), config.ssrc, VIDEO_EVENT, this)); | |
183 video_rtcp_session_.reset( | |
184 new SenderRtcpSession(clock_, &pacer_, video_rtcp_observer_.get(), | |
185 config.ssrc, config.feedback_ssrc)); | |
186 pacer_.RegisterVideoSsrc(config.ssrc); | |
187 valid_sender_ssrcs_.insert(config.feedback_ssrc); | |
188 transport_client_->OnStatusChanged(TRANSPORT_VIDEO_INITIALIZED); | |
189 } | 192 } |
190 | 193 |
191 namespace { | 194 namespace { |
192 void EncryptAndSendFrame(const EncodedFrame& frame, | 195 void EncryptAndSendFrame(const EncodedFrame& frame, |
193 TransportEncryptionHandler* encryptor, | 196 TransportEncryptionHandler* encryptor, |
194 RtpSender* sender) { | 197 RtpSender* sender) { |
195 // TODO(miu): We probably shouldn't attempt to send an empty frame, but this | 198 // TODO(miu): We probably shouldn't attempt to send an empty frame, but this |
196 // issue is still under investigation. http://crbug.com/519022 | 199 // issue is still under investigation. http://crbug.com/519022 |
197 if (encryptor->is_activated() && !frame.data.empty()) { | 200 if (encryptor->is_activated() && !frame.data.empty()) { |
198 EncodedFrame encrypted_frame; | 201 EncodedFrame encrypted_frame; |
199 frame.CopyMetadataTo(&encrypted_frame); | 202 frame.CopyMetadataTo(&encrypted_frame); |
200 if (encryptor->Encrypt(frame.frame_id, frame.data, &encrypted_frame.data)) { | 203 if (encryptor->Encrypt(frame.frame_id, frame.data, &encrypted_frame.data)) { |
201 sender->SendFrame(encrypted_frame); | 204 sender->SendFrame(encrypted_frame); |
202 } else { | 205 } else { |
203 LOG(ERROR) << "Encryption failed. Not sending frame with ID " | 206 LOG(ERROR) << "Encryption failed. Not sending frame with ID " |
204 << frame.frame_id; | 207 << frame.frame_id; |
205 } | 208 } |
206 } else { | 209 } else { |
207 sender->SendFrame(frame); | 210 sender->SendFrame(frame); |
208 } | 211 } |
209 } | 212 } |
210 } // namespace | 213 } // namespace |
211 | 214 |
212 void CastTransportImpl::InsertFrame(uint32_t ssrc, const EncodedFrame& frame) { | 215 void CastTransportImpl::InsertFrame(uint32_t ssrc, const EncodedFrame& frame) { |
213 if (audio_sender_ && ssrc == audio_sender_->ssrc()) { | 216 auto it = sessions_.find(ssrc); |
214 audio_rtcp_session_->WillSendFrame(frame.frame_id); | 217 if (it == sessions_.end()) { |
215 EncryptAndSendFrame(frame, &audio_encryptor_, audio_sender_.get()); | |
216 } else if (video_sender_ && ssrc == video_sender_->ssrc()) { | |
217 video_rtcp_session_->WillSendFrame(frame.frame_id); | |
218 EncryptAndSendFrame(frame, &video_encryptor_, video_sender_.get()); | |
219 } else { | |
220 NOTREACHED() << "Invalid InsertFrame call."; | 218 NOTREACHED() << "Invalid InsertFrame call."; |
219 return; | |
221 } | 220 } |
221 | |
222 it->second->rtcp_session->WillSendFrame(frame.frame_id); | |
223 EncryptAndSendFrame(frame, &it->second->encryptor, | |
224 it->second->rtp_sender.get()); | |
222 } | 225 } |
223 | 226 |
224 void CastTransportImpl::SendSenderReport( | 227 void CastTransportImpl::SendSenderReport( |
225 uint32_t ssrc, | 228 uint32_t ssrc, |
226 base::TimeTicks current_time, | 229 base::TimeTicks current_time, |
227 RtpTimeTicks current_time_as_rtp_timestamp) { | 230 RtpTimeTicks current_time_as_rtp_timestamp) { |
228 if (audio_sender_ && ssrc == audio_sender_->ssrc()) { | 231 auto it = sessions_.find(ssrc); |
229 audio_rtcp_session_->SendRtcpReport( | 232 if (it == sessions_.end()) { |
230 current_time, current_time_as_rtp_timestamp, | |
231 audio_sender_->send_packet_count(), audio_sender_->send_octet_count()); | |
232 } else if (video_sender_ && ssrc == video_sender_->ssrc()) { | |
233 video_rtcp_session_->SendRtcpReport( | |
234 current_time, current_time_as_rtp_timestamp, | |
235 video_sender_->send_packet_count(), video_sender_->send_octet_count()); | |
236 } else { | |
237 NOTREACHED() << "Invalid request for sending RTCP packet."; | 233 NOTREACHED() << "Invalid request for sending RTCP packet."; |
234 return; | |
238 } | 235 } |
236 | |
237 it->second->rtcp_session->SendRtcpReport( | |
238 current_time, current_time_as_rtp_timestamp, | |
239 it->second->rtp_sender->send_packet_count(), | |
240 it->second->rtp_sender->send_octet_count()); | |
239 } | 241 } |
240 | 242 |
241 void CastTransportImpl::CancelSendingFrames( | 243 void CastTransportImpl::CancelSendingFrames( |
242 uint32_t ssrc, | 244 uint32_t ssrc, |
243 const std::vector<FrameId>& frame_ids) { | 245 const std::vector<FrameId>& frame_ids) { |
244 if (audio_sender_ && ssrc == audio_sender_->ssrc()) { | 246 auto it = sessions_.find(ssrc); |
245 audio_sender_->CancelSendingFrames(frame_ids); | 247 if (it == sessions_.end()) { |
246 } else if (video_sender_ && ssrc == video_sender_->ssrc()) { | |
247 video_sender_->CancelSendingFrames(frame_ids); | |
248 } else { | |
249 NOTREACHED() << "Invalid request for cancel sending."; | 248 NOTREACHED() << "Invalid request for cancel sending."; |
249 return; | |
250 } | 250 } |
251 | |
252 it->second->rtp_sender->CancelSendingFrames(frame_ids); | |
251 } | 253 } |
252 | 254 |
253 void CastTransportImpl::ResendFrameForKickstart(uint32_t ssrc, | 255 void CastTransportImpl::ResendFrameForKickstart(uint32_t ssrc, |
254 FrameId frame_id) { | 256 FrameId frame_id) { |
255 if (audio_sender_ && ssrc == audio_sender_->ssrc()) { | 257 auto it = sessions_.find(ssrc); |
256 DCHECK(audio_rtcp_session_); | 258 if (it == sessions_.end()) { |
257 audio_sender_->ResendFrameForKickstart( | |
258 frame_id, audio_rtcp_session_->current_round_trip_time()); | |
259 } else if (video_sender_ && ssrc == video_sender_->ssrc()) { | |
260 DCHECK(video_rtcp_session_); | |
261 video_sender_->ResendFrameForKickstart( | |
262 frame_id, video_rtcp_session_->current_round_trip_time()); | |
263 } else { | |
264 NOTREACHED() << "Invalid request for kickstart."; | 259 NOTREACHED() << "Invalid request for kickstart."; |
260 return; | |
265 } | 261 } |
262 | |
263 DCHECK(it->second->rtcp_session); | |
264 it->second->rtp_sender->ResendFrameForKickstart( | |
265 frame_id, it->second->rtcp_session->current_round_trip_time()); | |
266 } | 266 } |
267 | 267 |
268 void CastTransportImpl::ResendPackets( | 268 void CastTransportImpl::ResendPackets( |
269 uint32_t ssrc, | 269 uint32_t ssrc, |
270 const MissingFramesAndPacketsMap& missing_packets, | 270 const MissingFramesAndPacketsMap& missing_packets, |
271 bool cancel_rtx_if_not_in_list, | 271 bool cancel_rtx_if_not_in_list, |
272 const DedupInfo& dedup_info) { | 272 const DedupInfo& dedup_info) { |
273 if (audio_sender_ && ssrc == audio_sender_->ssrc()) { | 273 auto it = sessions_.find(ssrc); |
274 audio_sender_->ResendPackets(missing_packets, cancel_rtx_if_not_in_list, | 274 if (it == sessions_.end()) { |
275 dedup_info); | |
276 } else if (video_sender_ && ssrc == video_sender_->ssrc()) { | |
277 video_sender_->ResendPackets(missing_packets, cancel_rtx_if_not_in_list, | |
278 dedup_info); | |
279 } else { | |
280 NOTREACHED() << "Invalid request for retransmission."; | 275 NOTREACHED() << "Invalid request for retransmission."; |
276 return; | |
281 } | 277 } |
278 | |
279 it->second->rtp_sender->ResendPackets(missing_packets, | |
280 cancel_rtx_if_not_in_list, dedup_info); | |
282 } | 281 } |
283 | 282 |
284 PacketReceiverCallback CastTransportImpl::PacketReceiverForTesting() { | 283 PacketReceiverCallback CastTransportImpl::PacketReceiverForTesting() { |
285 return base::Bind(base::IgnoreResult(&CastTransportImpl::OnReceivedPacket), | 284 return base::Bind(base::IgnoreResult(&CastTransportImpl::OnReceivedPacket), |
286 weak_factory_.GetWeakPtr()); | 285 weak_factory_.GetWeakPtr()); |
287 } | 286 } |
288 | 287 |
289 void CastTransportImpl::SendRawEvents() { | 288 void CastTransportImpl::SendRawEvents() { |
290 DCHECK(logging_flush_interval_ > base::TimeDelta()); | 289 DCHECK(logging_flush_interval_ > base::TimeDelta()); |
291 | 290 |
(...skipping 22 matching lines...) Expand all Loading... | |
314 ssrc = GetSsrcOfSender(data, length); | 313 ssrc = GetSsrcOfSender(data, length); |
315 } else if (!RtpParser::ParseSsrc(data, length, &ssrc)) { | 314 } else if (!RtpParser::ParseSsrc(data, length, &ssrc)) { |
316 VLOG(1) << "Invalid RTP packet."; | 315 VLOG(1) << "Invalid RTP packet."; |
317 return false; | 316 return false; |
318 } | 317 } |
319 if (valid_sender_ssrcs_.find(ssrc) == valid_sender_ssrcs_.end()) { | 318 if (valid_sender_ssrcs_.find(ssrc) == valid_sender_ssrcs_.end()) { |
320 VLOG(1) << "Stale packet received."; | 319 VLOG(1) << "Stale packet received."; |
321 return false; | 320 return false; |
322 } | 321 } |
323 | 322 |
324 if (audio_rtcp_session_ && | 323 for (auto it = sessions_.begin(); it != sessions_.end(); ++it) { |
miu
2016/07/06 21:45:42
nit: for (const auto& e : sessions_) { if (e.secon
xjz
2016/07/13 20:31:17
Done.
| |
325 audio_rtcp_session_->IncomingRtcpPacket(data, length)) { | 324 if (it->second->rtcp_session && |
miu
2016/07/06 21:45:42
IIUC, you don't need to null-check the rtcp_sessio
xjz
2016/07/13 20:31:17
Done.
| |
326 return true; | 325 it->second->rtcp_session->IncomingRtcpPacket(data, length)) |
326 return true; | |
327 } | 327 } |
328 if (video_rtcp_session_ && | 328 |
329 video_rtcp_session_->IncomingRtcpPacket(data, length)) { | |
330 return true; | |
331 } | |
332 transport_client_->ProcessRtpPacket(std::move(packet)); | 329 transport_client_->ProcessRtpPacket(std::move(packet)); |
333 return true; | 330 return true; |
334 } | 331 } |
335 | 332 |
336 void CastTransportImpl::OnReceivedLogMessage( | 333 void CastTransportImpl::OnReceivedLogMessage( |
337 EventMediaType media_type, | 334 EventMediaType media_type, |
338 const RtcpReceiverLogMessage& log) { | 335 const RtcpReceiverLogMessage& log) { |
339 if (logging_flush_interval_ <= base::TimeDelta()) | 336 if (logging_flush_interval_ <= base::TimeDelta()) |
340 return; | 337 return; |
341 | 338 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
374 } | 371 } |
375 } | 372 } |
376 } | 373 } |
377 } | 374 } |
378 | 375 |
379 void CastTransportImpl::OnReceivedCastMessage( | 376 void CastTransportImpl::OnReceivedCastMessage( |
380 uint32_t ssrc, | 377 uint32_t ssrc, |
381 const RtcpCastMessage& cast_message) { | 378 const RtcpCastMessage& cast_message) { |
382 | 379 |
383 DedupInfo dedup_info; | 380 DedupInfo dedup_info; |
384 if (audio_sender_ && audio_sender_->ssrc() == ssrc) { | 381 auto it = sessions_.find(ssrc); |
385 const int64_t acked_bytes = | 382 if (it == sessions_.end() || !it->second->rtp_sender) |
386 audio_sender_->GetLastByteSentForFrame(cast_message.ack_frame_id); | 383 return; |
384 | |
385 if (it->second->is_audio) { | |
386 const int64_t acked_bytes = it->second->rtp_sender->GetLastByteSentForFrame( | |
387 cast_message.ack_frame_id); | |
387 last_byte_acked_for_audio_ = | 388 last_byte_acked_for_audio_ = |
388 std::max(acked_bytes, last_byte_acked_for_audio_); | 389 std::max(acked_bytes, last_byte_acked_for_audio_); |
389 } else if (video_sender_ && video_sender_->ssrc() == ssrc) { | 390 } else { |
390 dedup_info.resend_interval = video_rtcp_session_->current_round_trip_time(); | 391 dedup_info.resend_interval = |
392 it->second->rtcp_session->current_round_trip_time(); | |
391 | 393 |
392 // Only use audio stream to dedup if there is one. | 394 // Only use audio stream to dedup if there is one. |
393 if (audio_sender_) { | 395 if (last_byte_acked_for_audio_) { |
394 dedup_info.last_byte_acked_for_audio = last_byte_acked_for_audio_; | 396 dedup_info.last_byte_acked_for_audio = last_byte_acked_for_audio_; |
395 } | 397 } |
396 } | 398 } |
397 | 399 |
398 if (!cast_message.missing_frames_and_packets.empty()) { | 400 if (!cast_message.missing_frames_and_packets.empty()) { |
399 VLOG(2) << "feedback_count: " | 401 VLOG(2) << "feedback_count: " |
400 << static_cast<uint32_t>(cast_message.feedback_count); | 402 << static_cast<uint32_t>(cast_message.feedback_count); |
401 // This call does two things. | 403 // This call does two things. |
402 // 1. Specifies that retransmissions for packets not listed in the set are | 404 // 1. Specifies that retransmissions for packets not listed in the set are |
403 // cancelled. | 405 // cancelled. |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
509 "calling CastTransportImpl::SendRtcpFromRtpReceiver."; | 511 "calling CastTransportImpl::SendRtcpFromRtpReceiver."; |
510 return; | 512 return; |
511 } | 513 } |
512 pacer_.SendRtcpPacket(rtcp_builder_at_rtp_receiver_->local_ssrc(), | 514 pacer_.SendRtcpPacket(rtcp_builder_at_rtp_receiver_->local_ssrc(), |
513 rtcp_builder_at_rtp_receiver_->Finish()); | 515 rtcp_builder_at_rtp_receiver_->Finish()); |
514 rtcp_builder_at_rtp_receiver_.reset(); | 516 rtcp_builder_at_rtp_receiver_.reset(); |
515 } | 517 } |
516 | 518 |
517 } // namespace cast | 519 } // namespace cast |
518 } // namespace media | 520 } // namespace media |
OLD | NEW |