| /* | |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | |
| * | |
| * Use of this source code is governed by a BSD-style license | |
| * that can be found in the LICENSE file in the root of the source | |
| * tree. An additional intellectual property rights grant can be found | |
| * in the file PATENTS. All contributing project authors may | |
| * be found in the AUTHORS file in the root of the source tree. | |
| */ | |
| #include "call/fake_network_pipe.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <memory> | |
| #include <optional> | |
| #include <queue> | |
| #include <utility> | |
| #include <vector> | |
| #include "api/array_view.h" | |
| #include "api/call/transport.h" | |
| #include "api/media_types.h" | |
| #include "api/test/simulated_network.h" | |
| #include "api/units/timestamp.h" | |
| #include "modules/rtp_rtcp/source/rtp_packet_received.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/copy_on_write_buffer.h" | |
| #include "rtc_base/logging.h" | |
| #include "rtc_base/synchronization/mutex.h" | |
| #include "system_wrappers/include/clock.h" | |
| namespace webrtc { | |
| namespace { | |
| constexpr int64_t kLogIntervalMs = 5000; | |
| } // namespace | |
| NetworkPacket::NetworkPacket(CopyOnWriteBuffer packet, | |
| int64_t send_time, | |
| int64_t arrival_time, | |
| std::optional<PacketOptions> packet_options, | |
| bool is_rtcp, | |
| MediaType media_type, | |
| std::optional<int64_t> packet_time_us, | |
| Transport* transport) | |
| : packet_(std::move(packet)), | |
| send_time_(send_time), | |
| arrival_time_(arrival_time), | |
| packet_options_(packet_options), | |
| is_rtcp_(is_rtcp), | |
| media_type_(media_type), | |
| packet_time_us_(packet_time_us), | |
| transport_(transport) {} | |
| NetworkPacket::NetworkPacket(RtpPacketReceived packet_received, | |
| MediaType media_type, | |
| int64_t send_time, | |
| int64_t arrival_time) | |
| : packet_(packet_received.Buffer()), | |
| send_time_(send_time), | |
| arrival_time_(arrival_time), | |
| is_rtcp_(false), | |
| media_type_(media_type), | |
| packet_time_us_(packet_received.arrival_time().us()), | |
| packet_received_(std::move(packet_received)), | |
| transport_(nullptr) {} | |
| NetworkPacket::NetworkPacket(NetworkPacket&& o) | |
| : packet_(std::move(o.packet_)), | |
| send_time_(o.send_time_), | |
| arrival_time_(o.arrival_time_), | |
| packet_options_(o.packet_options_), | |
| is_rtcp_(o.is_rtcp_), | |
| media_type_(o.media_type_), | |
| packet_time_us_(o.packet_time_us_), | |
| packet_received_(std::move(o.packet_received_)), | |
| transport_(o.transport_) {} | |
| NetworkPacket::~NetworkPacket() = default; | |
| NetworkPacket& NetworkPacket::operator=(NetworkPacket&& o) { | |
| packet_ = std::move(o.packet_); | |
| send_time_ = o.send_time_; | |
| arrival_time_ = o.arrival_time_; | |
| packet_options_ = o.packet_options_; | |
| is_rtcp_ = o.is_rtcp_; | |
| media_type_ = o.media_type_; | |
| packet_time_us_ = o.packet_time_us_; | |
| packet_received_ = o.packet_received_; | |
| transport_ = o.transport_; | |
| return *this; | |
| } | |
| FakeNetworkPipe::FakeNetworkPipe( | |
| Clock* clock, | |
| std::unique_ptr<NetworkBehaviorInterface> network_behavior) | |
| : FakeNetworkPipe(clock, std::move(network_behavior), nullptr, 1) {} | |
| FakeNetworkPipe::FakeNetworkPipe( | |
| Clock* clock, | |
| std::unique_ptr<NetworkBehaviorInterface> network_behavior, | |
| PacketReceiver* receiver) | |
| : FakeNetworkPipe(clock, std::move(network_behavior), receiver, 1) {} | |
| FakeNetworkPipe::FakeNetworkPipe( | |
| Clock* clock, | |
| std::unique_ptr<NetworkBehaviorInterface> network_behavior, | |
| PacketReceiver* receiver, | |
| uint64_t /* seed */) | |
| : clock_(clock), | |
| network_behavior_(std::move(network_behavior)), | |
| receiver_(receiver), | |
| clock_offset_ms_(0), | |
| dropped_packets_(0), | |
| sent_packets_(0), | |
| total_packet_delay_us_(0), | |
| last_log_time_us_(clock_->TimeInMicroseconds()) {} | |
| FakeNetworkPipe::~FakeNetworkPipe() { | |
| RTC_DCHECK(active_transports_.empty()); | |
| } | |
| void FakeNetworkPipe::SetReceiver(PacketReceiver* receiver) { | |
| MutexLock lock(&config_lock_); | |
| receiver_ = receiver; | |
| } | |
| void FakeNetworkPipe::AddActiveTransport(Transport* transport) { | |
| MutexLock lock(&config_lock_); | |
| active_transports_[transport]++; | |
| } | |
| void FakeNetworkPipe::RemoveActiveTransport(Transport* transport) { | |
| MutexLock lock(&config_lock_); | |
| auto it = active_transports_.find(transport); | |
| RTC_CHECK(it != active_transports_.end()); | |
| if (--(it->second) == 0) { | |
| active_transports_.erase(it); | |
| } | |
| } | |
| bool FakeNetworkPipe::SendRtp(ArrayView<const uint8_t> packet, | |
| const PacketOptions& options, | |
| Transport* transport) { | |
| RTC_DCHECK(transport); | |
| EnqueuePacket(CopyOnWriteBuffer(packet), options, false, transport); | |
| return true; | |
| } | |
| bool FakeNetworkPipe::SendRtcp(ArrayView<const uint8_t> packet, | |
| Transport* transport) { | |
| RTC_DCHECK(transport); | |
| EnqueuePacket(CopyOnWriteBuffer(packet), std::nullopt, true, transport); | |
| return true; | |
| } | |
| void FakeNetworkPipe::DeliverRtpPacket( | |
| MediaType media_type, | |
| RtpPacketReceived packet, | |
| OnUndemuxablePacketHandler /* undemuxable_packet_handler */) { | |
| MutexLock lock(&process_lock_); | |
| int64_t time_now_us = clock_->TimeInMicroseconds(); | |
| EnqueuePacket( | |
| NetworkPacket(std::move(packet), media_type, time_now_us, time_now_us)); | |
| } | |
| void FakeNetworkPipe::DeliverRtcpPacket(CopyOnWriteBuffer packet) { | |
| EnqueuePacket(std::move(packet), std::nullopt, true, MediaType::ANY, | |
| std::nullopt); | |
| } | |
| void FakeNetworkPipe::SetClockOffset(int64_t offset_ms) { | |
| MutexLock lock(&config_lock_); | |
| clock_offset_ms_ = offset_ms; | |
| } | |
| FakeNetworkPipe::StoredPacket::StoredPacket(NetworkPacket&& packet) | |
| : packet(std::move(packet)) {} | |
| bool FakeNetworkPipe::EnqueuePacket(CopyOnWriteBuffer packet, | |
| std::optional<PacketOptions> options, | |
| bool is_rtcp, | |
| MediaType media_type, | |
| std::optional<int64_t> packet_time_us) { | |
| MutexLock lock(&process_lock_); | |
| int64_t time_now_us = clock_->TimeInMicroseconds(); | |
| return EnqueuePacket(NetworkPacket(std::move(packet), time_now_us, | |
| time_now_us, options, is_rtcp, media_type, | |
| packet_time_us, nullptr)); | |
| } | |
| bool FakeNetworkPipe::EnqueuePacket(CopyOnWriteBuffer packet, | |
| std::optional<PacketOptions> options, | |
| bool is_rtcp, | |
| Transport* transport) { | |
| MutexLock lock(&process_lock_); | |
| int64_t time_now_us = clock_->TimeInMicroseconds(); | |
| return EnqueuePacket(NetworkPacket(std::move(packet), time_now_us, | |
| time_now_us, options, is_rtcp, | |
| MediaType::ANY, std::nullopt, transport)); | |
| } | |
| bool FakeNetworkPipe::EnqueuePacket(NetworkPacket&& net_packet) { | |
| int64_t send_time_us = net_packet.send_time(); | |
| size_t packet_size = net_packet.data_length(); | |
| packets_in_flight_.emplace_back(StoredPacket(std::move(net_packet))); | |
| int64_t packet_id = reinterpret_cast<uint64_t>(&packets_in_flight_.back()); | |
| bool sent = network_behavior_->EnqueuePacket( | |
| PacketInFlightInfo(packet_size, send_time_us, packet_id)); | |
| if (!sent) { | |
| packets_in_flight_.pop_back(); | |
| ++dropped_packets_; | |
| } | |
| return sent; | |
| } | |
| float FakeNetworkPipe::PercentageLoss() { | |
| MutexLock lock(&process_lock_); | |
| if (sent_packets_ == 0) | |
| return 0; | |
| return static_cast<float>(dropped_packets_) / | |
| (sent_packets_ + dropped_packets_); | |
| } | |
| int FakeNetworkPipe::AverageDelay() { | |
| MutexLock lock(&process_lock_); | |
| if (sent_packets_ == 0) | |
| return 0; | |
| return static_cast<int>(total_packet_delay_us_ / | |
| (1000 * static_cast<int64_t>(sent_packets_))); | |
| } | |
| size_t FakeNetworkPipe::DroppedPackets() { | |
| MutexLock lock(&process_lock_); | |
| return dropped_packets_; | |
| } | |
| size_t FakeNetworkPipe::SentPackets() { | |
| MutexLock lock(&process_lock_); | |
| return sent_packets_; | |
| } | |
| void FakeNetworkPipe::Process() { | |
| int64_t time_now_us; | |
| std::queue<NetworkPacket> packets_to_deliver; | |
| { | |
| MutexLock lock(&process_lock_); | |
| time_now_us = clock_->TimeInMicroseconds(); | |
| if (time_now_us - last_log_time_us_ > kLogIntervalMs * 1000) { | |
| int64_t queueing_delay_us = 0; | |
| if (!packets_in_flight_.empty()) | |
| queueing_delay_us = | |
| time_now_us - packets_in_flight_.front().packet.send_time(); | |
| RTC_LOG(LS_INFO) << "Network queue: " << queueing_delay_us / 1000 | |
| << " ms."; | |
| last_log_time_us_ = time_now_us; | |
| } | |
| std::vector<PacketDeliveryInfo> delivery_infos = | |
| network_behavior_->DequeueDeliverablePackets(time_now_us); | |
| for (auto& delivery_info : delivery_infos) { | |
| // In the common case where no reordering happens, find will return early | |
| // as the first packet will be a match. | |
| auto packet_it = | |
| std::find_if(packets_in_flight_.begin(), packets_in_flight_.end(), | |
| [&delivery_info](StoredPacket& packet_ref) { | |
| return reinterpret_cast<uint64_t>(&packet_ref) == | |
| delivery_info.packet_id; | |
| }); | |
| // Check that the packet is in the deque of packets in flight. | |
| RTC_CHECK(packet_it != packets_in_flight_.end()); | |
| // Check that the packet is not already removed. | |
| RTC_DCHECK(!packet_it->removed); | |
| NetworkPacket packet = std::move(packet_it->packet); | |
| packet_it->removed = true; | |
| // Cleanup of removed packets at the beginning of the deque. | |
| while (!packets_in_flight_.empty() && | |
| packets_in_flight_.front().removed) { | |
| packets_in_flight_.pop_front(); | |
| } | |
| if (delivery_info.receive_time_us != PacketDeliveryInfo::kNotReceived) { | |
| int64_t added_delay_us = | |
| delivery_info.receive_time_us - packet.send_time(); | |
| packet.IncrementArrivalTime(added_delay_us); | |
| packets_to_deliver.emplace(std::move(packet)); | |
| // `time_now_us` might be later than when the packet should have | |
| // arrived, due to NetworkProcess being called too late. For stats, use | |
| // the time it should have been on the link. | |
| total_packet_delay_us_ += added_delay_us; | |
| ++sent_packets_; | |
| } else { | |
| ++dropped_packets_; | |
| } | |
| } | |
| } | |
| MutexLock lock(&config_lock_); | |
| while (!packets_to_deliver.empty()) { | |
| NetworkPacket packet = std::move(packets_to_deliver.front()); | |
| packets_to_deliver.pop(); | |
| DeliverNetworkPacket(&packet); | |
| } | |
| } | |
| void FakeNetworkPipe::DeliverNetworkPacket(NetworkPacket* packet) { | |
| Transport* transport = packet->transport(); | |
| if (transport) { | |
| RTC_DCHECK(!receiver_); | |
| if (active_transports_.find(transport) == active_transports_.end()) { | |
| // Transport has been destroyed, ignore this packet. | |
| return; | |
| } | |
| if (packet->is_rtcp()) { | |
| transport->SendRtcp(MakeArrayView(packet->data(), packet->data_length()), | |
| packet->packet_options()); | |
| } else { | |
| transport->SendRtp(MakeArrayView(packet->data(), packet->data_length()), | |
| packet->packet_options()); | |
| } | |
| } else if (receiver_) { | |
| int64_t packet_time_us = packet->packet_time_us().value_or(-1); | |
| if (packet_time_us != -1) { | |
| int64_t queue_time_us = packet->arrival_time() - packet->send_time(); | |
| RTC_CHECK(queue_time_us >= 0); | |
| packet_time_us += queue_time_us; | |
| packet_time_us += (clock_offset_ms_ * 1000); | |
| } | |
| if (packet->is_rtcp()) { | |
| receiver_->DeliverRtcpPacket(std::move(*packet->raw_packet())); | |
| } else if (packet->packet_received()) { | |
| packet->packet_received()->set_arrival_time( | |
| Timestamp::Micros(packet_time_us)); | |
| receiver_->DeliverRtpPacket( | |
| packet->media_type(), *packet->packet_received(), | |
| [](const RtpPacketReceived& packet) { | |
| RTC_LOG(LS_WARNING) | |
| << "Unexpected failed demuxing packet in FakeNetworkPipe, " | |
| "Ssrc: " | |
| << packet.Ssrc() << " seq : " << packet.SequenceNumber(); | |
| return false; | |
| }); | |
| } | |
| } | |
| } | |
| std::optional<int64_t> FakeNetworkPipe::TimeUntilNextProcess() { | |
| MutexLock lock(&process_lock_); | |
| std::optional<int64_t> delivery_us = network_behavior_->NextDeliveryTimeUs(); | |
| if (delivery_us) { | |
| int64_t delay_us = *delivery_us - clock_->TimeInMicroseconds(); | |
| return std::max<int64_t>((delay_us + 500) / 1000, 0); | |
| } | |
| return std::nullopt; | |
| } | |
| bool FakeNetworkPipe::HasReceiver() const { | |
| MutexLock lock(&config_lock_); | |
| return receiver_ != nullptr; | |
| } | |
| void FakeNetworkPipe::DeliverPacketWithLock(NetworkPacket* packet) { | |
| MutexLock lock(&config_lock_); | |
| DeliverNetworkPacket(packet); | |
| } | |
| void FakeNetworkPipe::ResetStats() { | |
| MutexLock lock(&process_lock_); | |
| dropped_packets_ = 0; | |
| sent_packets_ = 0; | |
| total_packet_delay_us_ = 0; | |
| } | |
| int64_t FakeNetworkPipe::GetTimeInMicroseconds() const { | |
| return clock_->TimeInMicroseconds(); | |
| } | |
| } // namespace webrtc |