aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/sender_session.h
blob: ef68df73bdd6fffd01cf235b83d3a5f8469e61b1 (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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// Copyright 2020 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_SENDER_SESSION_H_
#define CAST_STREAMING_SENDER_SESSION_H_

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "cast/common/public/message_port.h"
#include "cast/streaming/answer_messages.h"
#include "cast/streaming/capture_configs.h"
#include "cast/streaming/capture_recommendations.h"
#include "cast/streaming/offer_messages.h"
#include "cast/streaming/remoting_capabilities.h"
#include "cast/streaming/rpc_messenger.h"
#include "cast/streaming/sender.h"
#include "cast/streaming/sender_packet_router.h"
#include "cast/streaming/session_config.h"
#include "cast/streaming/session_messenger.h"
#include "json/value.h"
#include "util/json/json_serialization.h"

namespace openscreen {
namespace cast {

class Environment;
class Sender;

class SenderSession final {
 public:
  // Upon successful negotiation, a set of configured senders is constructed
  // for handling audio and video. Note that either sender may be null.
  struct ConfiguredSenders {
    // In practice, we may have 0, 1, or 2 senders configured, depending
    // on if the device supports audio and video, and if we were able to
    // successfully negotiate a sender configuration.

    // If the sender is audio- or video-only, either of the senders
    // may be nullptr. However, in the majority of cases they will be populated.
    Sender* audio_sender = nullptr;
    AudioCaptureConfig audio_config;

    Sender* video_sender = nullptr;
    VideoCaptureConfig video_config;
  };

  // This struct contains all of the information necessary to begin remoting
  // after we receive the capabilities from the receiver.
  struct RemotingNegotiation {
    ConfiguredSenders senders;

    // The capabilities reported by the connected receiver. NOTE: SenderSession
    // reports the capabilities as-is from the Receiver, so clients concerned
    // about legacy devices, such as pre-1.27 Earth receivers should do
    // a version check when using these capabilities to offer remoting.
    RemotingCapabilities capabilities;
  };

  // The embedder should provide a client for handling negotiation events.
  // The client is required to implement a mirorring handler, and may choose
  // to provide a remoting negotiation if it supports remoting.
  // When the negotiation is complete, the appropriate |On*Negotiated| handler
  // is called.
  class Client {
   public:
    // Called when a new set of senders has been negotiated. This may be
    // called multiple times during a session, once for every time Negotiate()
    // is called on the SenderSession object. The negotiation call also includes
    // capture recommendations that can be used by the sender to provide
    // an optimal video stream for the receiver.
    virtual void OnNegotiated(
        const SenderSession* session,
        ConfiguredSenders senders,
        capture_recommendations::Recommendations capture_recommendations) = 0;

    // Called when a new set of remoting senders has been negotiated. Since
    // remoting is an optional feature, the default behavior here is to leave
    // this method unhandled.
    virtual void OnRemotingNegotiated(const SenderSession* session,
                                      RemotingNegotiation negotiation) {}

    // Called whenever an error occurs. Ends the ongoing session, and the caller
    // must call Negotiate() again if they wish to re-establish streaming.
    virtual void OnError(const SenderSession* session, Error error) = 0;

   protected:
    virtual ~Client();
  };

  // The configuration information required to set up the session.
  struct Configuration {
    // The remote address of the receiver to connect to. NOTE: we do eventually
    // set the remote endpoint on the |environment| object, but only after
    // getting the port information from a successful ANSWER message.
    IPAddress remote_address;

    // The client for notifying of successful negotiations and errors. Required.
    Client* const client;

    // The cast environment used to access operating system resources, such
    // as the UDP socket for RTP/RTCP messaging. Required.
    Environment* environment;

    // The message port used to send streaming control protocol messages.
    MessagePort* message_port;

    // The message source identifier (e.g. this sender).
    std::string message_source_id;

    // The message destination identifier (e.g. the receiver we are connected
    // to).
    std::string message_destination_id;

    // Whether or not the android RTP value hack should be used (for legacy
    // android devices). For more information, see https://crbug.com/631828.
    bool use_android_rtp_hack = true;
  };

  // The SenderSession assumes that the passed in client, environment, and
  // message port persist for at least the lifetime of the SenderSession. If
  // one of these classes needs to be reset, a new SenderSession should be
  // created.
  //
  // |message_source_id| and |message_destination_id| are the local and remote
  // ID, respectively, to use when sending or receiving control messages (e.g.,
  // OFFERs or ANSWERs) over the |message_port|. |message_port|'s SetClient()
  // method will be called.
  explicit SenderSession(Configuration config);
  SenderSession(const SenderSession&) = delete;
  SenderSession(SenderSession&&) noexcept = delete;
  SenderSession& operator=(const SenderSession&) = delete;
  SenderSession& operator=(SenderSession&&) = delete;
  ~SenderSession();

  // Starts a mirroring OFFER/ANSWER exchange with the already configured
  // receiver over the message port. The caller should assume any configured
  // senders become invalid when calling this method.
  Error Negotiate(std::vector<AudioCaptureConfig> audio_configs,
                  std::vector<VideoCaptureConfig> video_configs);

  // Remoting negotiation is actually very similar to mirroring negotiation--
  // an OFFER/ANSWER exchange still occurs, however only one audio and video
  // codec should be presented based on the encoding of the media element that
  // should be remoted. Note: the codec fields in |audio_config| and
  // |video_config| are ignored in favor of |kRemote|.
  Error NegotiateRemoting(AudioCaptureConfig audio_config,
                          VideoCaptureConfig video_config);

  // Get the current network usage (in bits per second). This includes all
  // senders managed by this session, and is a best guess based on receiver
  // feedback. Embedders may use this information to throttle capture devices.
  int GetEstimatedNetworkBandwidth() const;

  // The RPC messenger for this session. NOTE: RPC messages may come at
  // any time from the receiver, so subscriptions to RPC remoting messages
  // should be done before calling |NegotiateRemoting|.
  RpcMessenger* rpc_messenger() { return &rpc_messenger_; }

 private:
  // We store the current negotiation, so that when we get an answer from the
  // receiver we can line up the selected streams with the original
  // configuration.
  struct InProcessNegotiation {
    // The offer, which should always be valid if we have an in process
    // negotiation.
    Offer offer;

    // The configs used to derive the offer.
    std::vector<AudioCaptureConfig> audio_configs;
    std::vector<VideoCaptureConfig> video_configs;

    // The answer message for this negotiation, which may be invalid if we
    // haven't received an answer yet.
    Answer answer;
  };

  // The state of the session.
  enum class State {
    // Not sending content--may be in the middle of negotiation, or just
    // waiting.
    kIdle,

    // Currently mirroring content to a receiver.
    kStreaming,

    // Currently remoting content to a receiver.
    kRemoting
  };

  // Reset the state and tear down the current negotiation/negotiated mirroring
  // or remoting session. After reset, the SenderSession is still connected to
  // the same |remote_address_|, and the |packet_router_| and sequence number
  // will be unchanged.
  void ResetState();

  // Uses the passed in configs and offer to send an OFFER/ANSWER negotiation
  // and cache the new InProcessNavigation.
  Error StartNegotiation(std::vector<AudioCaptureConfig> audio_configs,
                         std::vector<VideoCaptureConfig> video_configs,
                         Offer offer);

  // Specific message type handler methods.
  void OnAnswer(ReceiverMessage message);
  void OnCapabilitiesResponse(ReceiverMessage message);
  void OnRpcMessage(ReceiverMessage message);
  void HandleErrorMessage(ReceiverMessage message, const std::string& text);

  // Used by SpawnSenders to generate a sender for a specific stream.
  std::unique_ptr<Sender> CreateSender(Ssrc receiver_ssrc,
                                       const Stream& stream,
                                       RtpPayloadType type);

  // Helper methods for spawning specific senders from the Answer message.
  void SpawnAudioSender(ConfiguredSenders* senders,
                        Ssrc receiver_ssrc,
                        int send_index,
                        int config_index);
  void SpawnVideoSender(ConfiguredSenders* senders,
                        Ssrc receiver_ssrc,
                        int send_index,
                        int config_index);

  // Spawn a set of configured senders from the currently stored negotiation.
  ConfiguredSenders SpawnSenders(const Answer& answer);

  // Used by the RPC messenger to send outbound messages.
  void SendRpcMessage(std::vector<uint8_t> message_body);

  // This session's configuration.
  Configuration config_;

  // The session messenger, which uses the message port for sending control
  // messages. For message formats, see
  // cast/protocol/castv2/streaming_schema.json.
  SenderSessionMessenger messenger_;

  // The RPC messenger, which uses the session messager for sending RPC messages
  // and handles subscriptions to RPC messages.
  RpcMessenger rpc_messenger_;

  // The packet router used for RTP/RTCP messaging across all senders.
  SenderPacketRouter packet_router_;

  // Each negotiation has its own sequence number, and the receiver replies
  // with the same sequence number that we send. Each message to the receiver
  // advances our current sequence number.
  int current_sequence_number_ = 0;

  // The current negotiation. If present, we are expected an ANSWER from
  // the receiver. If not present, any provided ANSWERS are rejected.
  std::unique_ptr<InProcessNegotiation> current_negotiation_;

  // The current state of the session. Note that the state is intentionally
  // limited. |kStreaming| or |kRemoting| means that we are either starting
  // a negotiation or actively sending to a receiver.
  State state_ = State::kIdle;

  // If the negotiation has succeeded, we store the current audio and video
  // senders used for this session. Either or both may be nullptr.
  std::unique_ptr<Sender> current_audio_sender_;
  std::unique_ptr<Sender> current_video_sender_;
};  // namespace cast

}  // namespace cast
}  // namespace openscreen

#endif  // CAST_STREAMING_SENDER_SESSION_H_