| 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 const bool is_audio; |
| 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 sessions_[config.ssrc] = std::move(session); |
| 162 } | 191 transport_client_->OnStatusChanged(TRANSPORT_STREAM_INITIALIZED); |
| 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 (const auto& session : sessions_) { |
| 325 audio_rtcp_session_->IncomingRtcpPacket(data, length)) { | 324 if (session.second->rtcp_session->IncomingRtcpPacket(data, length)) |
| 326 return true; | 325 return true; |
| 327 } | 326 } |
| 328 if (video_rtcp_session_ && | 327 |
| 329 video_rtcp_session_->IncomingRtcpPacket(data, length)) { | |
| 330 return true; | |
| 331 } | |
| 332 transport_client_->ProcessRtpPacket(std::move(packet)); | 328 transport_client_->ProcessRtpPacket(std::move(packet)); |
| 333 return true; | 329 return true; |
| 334 } | 330 } |
| 335 | 331 |
| 336 void CastTransportImpl::OnReceivedLogMessage( | 332 void CastTransportImpl::OnReceivedLogMessage( |
| 337 EventMediaType media_type, | 333 EventMediaType media_type, |
| 338 const RtcpReceiverLogMessage& log) { | 334 const RtcpReceiverLogMessage& log) { |
| 339 if (logging_flush_interval_ <= base::TimeDelta()) | 335 if (logging_flush_interval_ <= base::TimeDelta()) |
| 340 return; | 336 return; |
| 341 | 337 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 } | 370 } |
| 375 } | 371 } |
| 376 } | 372 } |
| 377 } | 373 } |
| 378 | 374 |
| 379 void CastTransportImpl::OnReceivedCastMessage( | 375 void CastTransportImpl::OnReceivedCastMessage( |
| 380 uint32_t ssrc, | 376 uint32_t ssrc, |
| 381 const RtcpCastMessage& cast_message) { | 377 const RtcpCastMessage& cast_message) { |
| 382 | 378 |
| 383 DedupInfo dedup_info; | 379 DedupInfo dedup_info; |
| 384 if (audio_sender_ && audio_sender_->ssrc() == ssrc) { | 380 auto it = sessions_.find(ssrc); |
| 385 const int64_t acked_bytes = | 381 if (it == sessions_.end() || !it->second->rtp_sender) |
| 386 audio_sender_->GetLastByteSentForFrame(cast_message.ack_frame_id); | 382 return; |
| 383 |
| 384 if (it->second->is_audio) { |
| 385 const int64_t acked_bytes = it->second->rtp_sender->GetLastByteSentForFrame( |
| 386 cast_message.ack_frame_id); |
| 387 last_byte_acked_for_audio_ = | 387 last_byte_acked_for_audio_ = |
| 388 std::max(acked_bytes, last_byte_acked_for_audio_); | 388 std::max(acked_bytes, last_byte_acked_for_audio_); |
| 389 } else if (video_sender_ && video_sender_->ssrc() == ssrc) { | 389 } else { |
| 390 dedup_info.resend_interval = video_rtcp_session_->current_round_trip_time(); | 390 dedup_info.resend_interval = |
| 391 it->second->rtcp_session->current_round_trip_time(); |
| 391 | 392 |
| 392 // Only use audio stream to dedup if there is one. | 393 // Only use audio stream to dedup if there is one. |
| 393 if (audio_sender_) { | 394 if (last_byte_acked_for_audio_) { |
| 394 dedup_info.last_byte_acked_for_audio = last_byte_acked_for_audio_; | 395 dedup_info.last_byte_acked_for_audio = last_byte_acked_for_audio_; |
| 395 } | 396 } |
| 396 } | 397 } |
| 397 | 398 |
| 398 if (!cast_message.missing_frames_and_packets.empty()) { | 399 if (!cast_message.missing_frames_and_packets.empty()) { |
| 399 VLOG(2) << "feedback_count: " | 400 VLOG(2) << "feedback_count: " |
| 400 << static_cast<uint32_t>(cast_message.feedback_count); | 401 << static_cast<uint32_t>(cast_message.feedback_count); |
| 401 // This call does two things. | 402 // This call does two things. |
| 402 // 1. Specifies that retransmissions for packets not listed in the set are | 403 // 1. Specifies that retransmissions for packets not listed in the set are |
| 403 // cancelled. | 404 // cancelled. |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 509 "calling CastTransportImpl::SendRtcpFromRtpReceiver."; | 510 "calling CastTransportImpl::SendRtcpFromRtpReceiver."; |
| 510 return; | 511 return; |
| 511 } | 512 } |
| 512 pacer_.SendRtcpPacket(rtcp_builder_at_rtp_receiver_->local_ssrc(), | 513 pacer_.SendRtcpPacket(rtcp_builder_at_rtp_receiver_->local_ssrc(), |
| 513 rtcp_builder_at_rtp_receiver_->Finish()); | 514 rtcp_builder_at_rtp_receiver_->Finish()); |
| 514 rtcp_builder_at_rtp_receiver_.reset(); | 515 rtcp_builder_at_rtp_receiver_.reset(); |
| 515 } | 516 } |
| 516 | 517 |
| 517 } // namespace cast | 518 } // namespace cast |
| 518 } // namespace media | 519 } // namespace media |
| OLD | NEW |