143 lines
4.5 KiB
C++
143 lines
4.5 KiB
C++
// Copyright 2019 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 "cast/streaming/environment.h"
|
|
|
|
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "cast/streaming/rtp_defines.h"
|
|
#include "platform/api/task_runner.h"
|
|
#include "util/osp_logging.h"
|
|
|
|
namespace openscreen {
|
|
namespace cast {
|
|
|
|
Environment::Environment(ClockNowFunctionPtr now_function,
|
|
TaskRunner* task_runner,
|
|
const IPEndpoint& local_endpoint)
|
|
: now_function_(now_function), task_runner_(task_runner) {
|
|
OSP_DCHECK(now_function_);
|
|
OSP_DCHECK(task_runner_);
|
|
ErrorOr<std::unique_ptr<UdpSocket>> result =
|
|
UdpSocket::Create(task_runner_, this, local_endpoint);
|
|
if (result.is_error()) {
|
|
OSP_LOG_ERROR << "Unable to create a UDP socket bound to " << local_endpoint
|
|
<< ": " << result.error();
|
|
return;
|
|
}
|
|
const_cast<std::unique_ptr<UdpSocket>&>(socket_) = std::move(result.value());
|
|
OSP_DCHECK(socket_);
|
|
socket_->Bind();
|
|
}
|
|
|
|
Environment::~Environment() = default;
|
|
|
|
IPEndpoint Environment::GetBoundLocalEndpoint() const {
|
|
if (socket_) {
|
|
return socket_->GetLocalEndpoint();
|
|
}
|
|
return IPEndpoint{};
|
|
}
|
|
|
|
void Environment::SetSocketSubscriber(SocketSubscriber* subscriber) {
|
|
socket_subscriber_ = subscriber;
|
|
}
|
|
|
|
void Environment::ConsumeIncomingPackets(PacketConsumer* packet_consumer) {
|
|
OSP_DCHECK(packet_consumer);
|
|
OSP_DCHECK(!packet_consumer_);
|
|
packet_consumer_ = packet_consumer;
|
|
}
|
|
|
|
void Environment::DropIncomingPackets() {
|
|
packet_consumer_ = nullptr;
|
|
}
|
|
|
|
int Environment::GetMaxPacketSize() const {
|
|
// Return hard-coded values for UDP over wired Ethernet (which is a smaller
|
|
// MTU than typical defaults for UDP over 802.11 wireless). Performance would
|
|
// be more-optimized if the network were probed for the actual value. See
|
|
// discussion in rtp_defines.h.
|
|
switch (remote_endpoint_.address.version()) {
|
|
case IPAddress::Version::kV4:
|
|
return kMaxRtpPacketSizeForIpv4UdpOnEthernet;
|
|
case IPAddress::Version::kV6:
|
|
return kMaxRtpPacketSizeForIpv6UdpOnEthernet;
|
|
default:
|
|
OSP_NOTREACHED();
|
|
}
|
|
}
|
|
|
|
void Environment::SendPacket(absl::Span<const uint8_t> packet) {
|
|
OSP_DCHECK(remote_endpoint_.address);
|
|
OSP_DCHECK_NE(remote_endpoint_.port, 0);
|
|
if (socket_) {
|
|
socket_->SendMessage(packet.data(), packet.size(), remote_endpoint_);
|
|
}
|
|
}
|
|
|
|
Environment::PacketConsumer::~PacketConsumer() = default;
|
|
|
|
void Environment::OnBound(UdpSocket* socket) {
|
|
OSP_DCHECK(socket == socket_.get());
|
|
state_ = SocketState::kReady;
|
|
|
|
if (socket_subscriber_) {
|
|
socket_subscriber_->OnSocketReady();
|
|
}
|
|
}
|
|
|
|
void Environment::OnError(UdpSocket* socket, Error error) {
|
|
OSP_DCHECK(socket == socket_.get());
|
|
// Usually OnError() is only called for non-recoverable Errors. However,
|
|
// OnSendError() and OnRead() delegate to this method, to handle their hard
|
|
// error cases as well. So, return early here if |error| is recoverable.
|
|
if (error.ok() || error.code() == Error::Code::kAgain) {
|
|
return;
|
|
}
|
|
|
|
state_ = SocketState::kInvalid;
|
|
if (socket_subscriber_) {
|
|
socket_subscriber_->OnSocketInvalid(error);
|
|
} else {
|
|
// Default behavior when there are no subscribers.
|
|
OSP_LOG_ERROR << "For UDP socket bound to " << socket_->GetLocalEndpoint()
|
|
<< ": " << error;
|
|
}
|
|
}
|
|
|
|
void Environment::OnSendError(UdpSocket* socket, Error error) {
|
|
OnError(socket, error);
|
|
}
|
|
|
|
void Environment::OnRead(UdpSocket* socket,
|
|
ErrorOr<UdpPacket> packet_or_error) {
|
|
if (!packet_consumer_) {
|
|
return;
|
|
}
|
|
|
|
if (packet_or_error.is_error()) {
|
|
OnError(socket, packet_or_error.error());
|
|
return;
|
|
}
|
|
|
|
// Ideally, the arrival time would come from the operating system's network
|
|
// stack (e.g., by using the SO_TIMESTAMP sockopt on POSIX systems). However,
|
|
// there would still be the problem of mapping the timestamp to a value in
|
|
// terms of Clock::time_point. So, just sample the Clock here and call that
|
|
// the "arrival time." While this can add variance within the system, it
|
|
// should be minimal, assuming not too much time has elapsed between the
|
|
// actual packet receive event and the when this code here is executing.
|
|
const Clock::time_point arrival_time = now_function_();
|
|
|
|
UdpPacket packet = std::move(packet_or_error.value());
|
|
packet_consumer_->OnReceivedPacket(
|
|
packet.source(), arrival_time,
|
|
std::move(static_cast<std::vector<uint8_t>&>(packet)));
|
|
}
|
|
|
|
} // namespace cast
|
|
} // namespace openscreen
|