blob: 23b11269d0110f2227a24bfee7226464b58841bf [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// 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 "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) {
// TODO(miu): This is an abstraction violation. This needs to be a part of
// the overall packet (de)serialization consolidation.
static const 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