aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/environment.h
blob: 606f408f04656a93d1b70e775f1fbdca59245438 (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
143
144
145
146
147
148
149
150
151
152
153
154
// 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.

#ifndef CAST_STREAMING_ENVIRONMENT_H_
#define CAST_STREAMING_ENVIRONMENT_H_

#include <stdint.h>

#include <functional>
#include <memory>
#include <vector>

#include "absl/types/span.h"
#include "platform/api/time.h"
#include "platform/api/udp_socket.h"
#include "platform/base/ip_address.h"

namespace openscreen {
namespace cast {

// Provides the common environment for operating system resources shared by
// multiple components.
class Environment : public UdpSocket::Client {
 public:
  class PacketConsumer {
   public:
    virtual void OnReceivedPacket(const IPEndpoint& source,
                                  Clock::time_point arrival_time,
                                  std::vector<uint8_t> packet) = 0;

   protected:
    virtual ~PacketConsumer();
  };

  // Consumers of the environment's UDP socket should be careful to check the
  // socket's state before accessing its methods, especially
  // GetBoundLocalEndpoint(). If the environment is |kStarting|, the
  // local endpoint may not be set yet and will be zero initialized.
  enum class SocketState {
    // Socket is still initializing. Usually the UDP socket bind is
    // the last piece.
    kStarting,

    // The socket is ready for use and has been bound.
    kReady,

    // The socket is either closed (normally or due to an error) or in an
    // invalid state. Currently the environment does not create a new socket
    // in this case, so to be used again the environment itself needs to be
    // recreated.
    kInvalid
  };

  // Classes concerned with the Environment's UDP socket state may inherit from
  // |Subscriber| and then |Subscribe|.
  class SocketSubscriber {
   public:
    // Event that occurs when the environment is ready for use.
    virtual void OnSocketReady() = 0;

    // Event that occurs when the environment has experienced a fatal error.
    virtual void OnSocketInvalid(Error error) = 0;
  };

  // Construct with the given clock source and TaskRunner. Creates and
  // internally-owns a UdpSocket, and immediately binds it to the given
  // |local_endpoint|. If embedders do not care what interface/address the UDP
  // socket is bound on, they may omit that argument.
  Environment(ClockNowFunctionPtr now_function,
              TaskRunner* task_runner,
              const IPEndpoint& local_endpoint = IPEndpoint::kAnyV6());

  ~Environment() override;

  ClockNowFunctionPtr now_function() const { return now_function_; }
  Clock::time_point now() const { return now_function_(); }
  TaskRunner* task_runner() const { return task_runner_; }

  // Returns the local endpoint the socket is bound to, or the zero IPEndpoint
  // if socket creation/binding failed.
  //
  // Note: This method is virtual to allow unit tests to fake that there really
  // is a bound socket.
  virtual IPEndpoint GetBoundLocalEndpoint() const;

  // Get/Set the remote endpoint. This is separate from the constructor because
  // the remote endpoint is, in some cases, discovered only after receiving a
  // packet.
  const IPEndpoint& remote_endpoint() const { return remote_endpoint_; }
  void set_remote_endpoint(const IPEndpoint& endpoint) {
    remote_endpoint_ = endpoint;
  }

  // Returns the current state of the UDP socket. This method is virtual
  // to allow tests to simulate socket state.
  SocketState socket_state() const { return state_; }
  void set_socket_state_for_testing(SocketState state) { state_ = state; }

  // Subscribe to socket changes. Callers can unsubscribe by passing
  // nullptr.
  void SetSocketSubscriber(SocketSubscriber* subscriber);

  // Start/Resume delivery of incoming packets to the given |packet_consumer|.
  // Delivery will continue until DropIncomingPackets() is called.
  void ConsumeIncomingPackets(PacketConsumer* packet_consumer);

  // Stop delivery of incoming packets, dropping any that do come in. All
  // internal references to the PacketConsumer that was provided in the last
  // call to ConsumeIncomingPackets() are cleared.
  void DropIncomingPackets();

  // Returns the maximum packet size for the network. This will always return a
  // value of at least kRequiredNetworkPacketSize.
  int GetMaxPacketSize() const;

  // Sends the given |packet| to the remote endpoint, best-effort.
  // set_remote_endpoint() must be called beforehand with a valid IPEndpoint.
  //
  // Note: This method is virtual to allow unit tests to intercept packets
  // before they actually head-out through the socket.
  virtual void SendPacket(absl::Span<const uint8_t> packet);

 protected:
  Environment() : now_function_(nullptr), task_runner_(nullptr) {}

  // Protected so that they can be set by the MockEnvironment for testing.
  ClockNowFunctionPtr now_function_;
  TaskRunner* task_runner_;

 private:
  // UdpSocket::Client implementation.
  void OnBound(UdpSocket* socket) final;
  void OnError(UdpSocket* socket, Error error) final;
  void OnSendError(UdpSocket* socket, Error error) final;
  void OnRead(UdpSocket* socket, ErrorOr<UdpPacket> packet_or_error) final;

  // The UDP socket bound to the local endpoint that was passed into the
  // constructor, or null if socket creation failed.
  const std::unique_ptr<UdpSocket> socket_;

  // These are externally set/cleared. Behaviors are described in getter/setter
  // method comments above.
  IPEndpoint local_endpoint_{};
  IPEndpoint remote_endpoint_{};
  PacketConsumer* packet_consumer_ = nullptr;
  SocketState state_ = SocketState::kStarting;
  SocketSubscriber* socket_subscriber_ = nullptr;
};

}  // namespace cast
}  // namespace openscreen

#endif  // CAST_STREAMING_ENVIRONMENT_H_