| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media/cast/net/rtp/rtp_sender.h" |
| |
| #include <memory> |
| |
| #include "base/big_endian.h" |
| #include "base/logging.h" |
| #include "base/rand_util.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "media/cast/common/encoded_frame.h" |
| #include "media/cast/constants.h" |
| |
| namespace media { |
| namespace cast { |
| |
| namespace { |
| |
| // If there is only one referecne to the packet then copy the |
| // reference and return. |
| // Otherwise return a deep copy of the packet. |
| PacketRef FastCopyPacket(const PacketRef& packet) { |
| if (packet->HasOneRef()) |
| return packet; |
| return base::WrapRefCounted(new base::RefCountedData<Packet>(packet->data)); |
| } |
| |
| } // namespace |
| |
| RtpSender::RtpSender( |
| const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner, |
| PacedSender* const transport) |
| : transport_(transport), transport_task_runner_(transport_task_runner) { |
| // Randomly set sequence number start value. |
| config_.sequence_number = base::RandInt(0, 65535); |
| } |
| |
| RtpSender::~RtpSender() = default; |
| |
| bool RtpSender::Initialize(const CastTransportRtpConfig& config) { |
| config_.ssrc = config.ssrc; |
| // TODO(xjz): Android TV receivers expect the |payload_type| to be one of |
| // these two specific values. This constraint needs to be removed and the |
| // value of the |payload_type| can vary according to the spec: |
| // https://tools.ietf.org/html/rfc3551. |
| if (config.rtp_payload_type <= RtpPayloadType::AUDIO_LAST) |
| config_.payload_type = 127; |
| else |
| config_.payload_type = 96; |
| packetizer_ = std::make_unique<RtpPacketizer>(transport_, &storage_, config_); |
| return true; |
| } |
| |
| void RtpSender::SendFrame(const EncodedFrame& frame) { |
| DCHECK(packetizer_); |
| packetizer_->SendFrameAsPackets(frame); |
| LOG_IF(DFATAL, storage_.GetNumberOfStoredFrames() > kMaxUnackedFrames) |
| << "Possible bug: Frames are not being actively released from storage."; |
| } |
| |
| void RtpSender::ResendPackets( |
| const MissingFramesAndPacketsMap& missing_frames_and_packets, |
| bool cancel_rtx_if_not_in_list, const DedupInfo& dedup_info) { |
| // Iterate over all frames in the list. |
| for (auto it = missing_frames_and_packets.begin(); |
| it != missing_frames_and_packets.end(); ++it) { |
| SendPacketVector packets_to_resend; |
| FrameId frame_id = it->first; |
| // Set of packets that the receiver wants us to re-send. |
| // If empty, we need to re-send all packets for this frame. |
| const PacketIdSet& missing_packet_set = it->second; |
| |
| bool resend_all = missing_packet_set.find(kRtcpCastAllPacketsLost) != |
| missing_packet_set.end(); |
| bool resend_last = missing_packet_set.find(kRtcpCastLastPacket) != |
| missing_packet_set.end(); |
| |
| const SendPacketVector* stored_packets = storage_.GetFramePackets(frame_id); |
| if (!stored_packets) |
| continue; |
| |
| for (auto packet_it = stored_packets->begin(); |
| packet_it != stored_packets->end(); ++packet_it) { |
| const PacketKey& packet_key = packet_it->first; |
| const uint16_t packet_id = packet_key.packet_id; |
| |
| // Should we resend the packet? |
| bool resend = resend_all; |
| |
| // Should we resend it because it's in the missing_packet_set? |
| if (!resend && |
| missing_packet_set.find(packet_id) != missing_packet_set.end()) { |
| resend = true; |
| } |
| |
| // If we were asked to resend the last packet, check if it's the |
| // last packet. |
| if (!resend && resend_last && (packet_it + 1) == stored_packets->end()) { |
| resend = true; |
| } |
| |
| if (resend) { |
| // Resend packet to the network. |
| VLOG(3) << "Resend " << frame_id << ":" << packet_id; |
| // Set a unique incremental sequence number for every packet. |
| PacketRef packet_copy = FastCopyPacket(packet_it->second); |
| UpdateSequenceNumber(&packet_copy->data); |
| packets_to_resend.push_back(std::make_pair(packet_key, packet_copy)); |
| } else if (cancel_rtx_if_not_in_list) { |
| transport_->CancelSendingPacket(packet_it->first); |
| } |
| } |
| transport_->ResendPackets(packets_to_resend, dedup_info); |
| } |
| } |
| |
| void RtpSender::CancelSendingFrames(const std::vector<FrameId>& frame_ids) { |
| for (FrameId i : frame_ids) { |
| const SendPacketVector* stored_packets = storage_.GetFramePackets(i); |
| if (!stored_packets) |
| continue; |
| for (auto j = stored_packets->begin(); j != stored_packets->end(); ++j) { |
| transport_->CancelSendingPacket(j->first); |
| } |
| storage_.ReleaseFrame(i); |
| } |
| } |
| |
| void RtpSender::ResendFrameForKickstart(FrameId frame_id, |
| base::TimeDelta dedupe_window) { |
| // Send the last packet of the encoded frame to kick start |
| // retransmission. This gives enough information to the receiver what |
| // packets and frames are missing. |
| MissingFramesAndPacketsMap missing_frames_and_packets; |
| PacketIdSet missing; |
| missing.insert(kRtcpCastLastPacket); |
| missing_frames_and_packets.insert(std::make_pair(frame_id, missing)); |
| |
| // Sending this extra packet is to kick-start the session. There is |
| // no need to optimize re-transmission for this case. |
| DedupInfo dedup_info; |
| dedup_info.resend_interval = dedupe_window; |
| ResendPackets(missing_frames_and_packets, false, dedup_info); |
| } |
| |
| void RtpSender::UpdateSequenceNumber(Packet* packet) { |
| constexpr int kByteOffsetToSequenceNumber = 2; |
| base::BigEndianWriter big_endian_writer( |
| reinterpret_cast<char*>((&packet->front()) + kByteOffsetToSequenceNumber), |
| sizeof(uint16_t)); |
| big_endian_writer.WriteU16(packetizer_->NextSequenceNumber()); |
| } |
| |
| int64_t RtpSender::GetLastByteSentForFrame(FrameId frame_id) { |
| const SendPacketVector* stored_packets = storage_.GetFramePackets(frame_id); |
| if (!stored_packets) |
| return 0; |
| PacketKey last_packet_key = stored_packets->rbegin()->first; |
| return transport_->GetLastByteSentForPacket(last_packet_key); |
| } |
| |
| } // namespace cast |
| } // namespace media |