aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/environment.cc
blob: f2351281713b93fe71ac8c383681945fe6b090ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// 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