diff options
Diffstat (limited to 'cast/streaming/receiver_session.h')
-rw-r--r-- | cast/streaming/receiver_session.h | 266 |
1 files changed, 235 insertions, 31 deletions
diff --git a/cast/streaming/receiver_session.h b/cast/streaming/receiver_session.h index b8365a36..caf271b6 100644 --- a/cast/streaming/receiver_session.h +++ b/cast/streaming/receiver_session.h @@ -11,14 +11,15 @@ #include <vector> #include "cast/common/public/message_port.h" -#include "cast/streaming/answer_messages.h" #include "cast/streaming/capture_configs.h" +#include "cast/streaming/constants.h" #include "cast/streaming/offer_messages.h" #include "cast/streaming/receiver_packet_router.h" +#include "cast/streaming/resolution.h" +#include "cast/streaming/rpc_messenger.h" #include "cast/streaming/sender_message.h" #include "cast/streaming/session_config.h" -#include "cast/streaming/session_messager.h" -#include "util/json/json_serialization.h" +#include "cast/streaming/session_messenger.h" namespace openscreen { namespace cast { @@ -26,6 +27,18 @@ namespace cast { class Environment; class Receiver; +// This class is responsible for listening for streaming requests from Cast +// Sender devices, then negotiating capture constraints and instantiating audio +// and video Receiver objects. +// The owner of this session is expected to provide a client for +// updates, an environment for getting UDP socket information (as well as +// other OS dependencies), and a set of preferences to be used for +// negotiation. +// +// NOTE: In some cases, the session initialization may be pending waiting for +// the UDP socket to be ready. In this case, the receivers and the answer +// message will not be configured and sent until the UDP socket has finished +// binding. class ReceiverSession final : public Environment::SocketSubscriber { public: // Upon successful negotiation, a set of configured receivers is constructed @@ -50,35 +63,184 @@ class ReceiverSession final : public Environment::SocketSubscriber { VideoCaptureConfig video_config; }; + // This struct contains all of the information necessary to begin remoting + // once we get a remoting request from a Sender. + struct RemotingNegotiation { + // The configured receivers set to be used for handling audio and + // video streams. Unlike in the general streaming case, when we are remoting + // we don't know the codec and other information about the stream until + // the sender provices that information through the + // DemuxerStreamInitializeCallback RPC method. + ConfiguredReceivers receivers; + + // The RPC messenger to be used for subscribing to remoting proto messages. + // Unlike the SenderSession API, the RPC messenger is negotiation specific. + // The messenger is torn down when |OnReceiversDestroying| is called, and + // is owned by the ReceiverSession. + RpcMessenger* messenger; + }; + // The embedder should provide a client for handling connections. - // When a connection is established, the OnMirroringNegotiated callback is - // called. + // When a connection is established, the OnNegotiated callback is called. class Client { public: + // Currently we only care about the session ending or being renegotiated, + // which means that we don't have to tear down as much state. enum ReceiversDestroyingReason { kEndOfSession, kRenegotiated }; - // Called when a new set of receivers has been negotiated. This may be - // called multiple times during a session, as renegotiations occur. - virtual void OnMirroringNegotiated(const ReceiverSession* session, - ConfiguredReceivers receivers) = 0; + // Called when a set of streaming receivers has been negotiated. Both this + // and |OnRemotingNegotiated| may be called repeatedly as negotiations occur + // through the life of a session. + virtual void OnNegotiated(const ReceiverSession* session, + ConfiguredReceivers receivers) = 0; + + // Called when a set of remoting receivers has been negotiated. This will + // only be called if |RemotingPreferences| are provided as part of + // constructing the ReceiverSession object. + virtual void OnRemotingNegotiated(const ReceiverSession* session, + RemotingNegotiation negotiation) {} // Called immediately preceding the destruction of this session's receivers. - // If |reason| is |kEndOfSession|, OnMirroringNegotiated() will never be - // called again; if it is |kRenegotiated|, OnMirroringNegotiated() will be - // called again soon with a new set of Receivers to use. + // If |reason| is |kEndOfSession|, OnNegotiated() will never be called + // again; if it is |kRenegotiated|, OnNegotiated() will be called again + // soon with a new set of Receivers to use. // // Before returning, the implementation must ensure that all references to - // the Receivers, from the last call to OnMirroringNegotiated(), have been - // cleared. + // the Receivers, from the last call to OnNegotiated(), have been cleared. virtual void OnReceiversDestroying(const ReceiverSession* session, ReceiversDestroyingReason reason) = 0; + // Called whenever an error that the client may care about occurs. + // Recoverable errors are usually logged by the receiver session instead + // of reported here. virtual void OnError(const ReceiverSession* session, Error error) = 0; + // Called to verify whether a given codec parameter is supported by + // this client. If not overriden, this always assumes true. + // This method is used only for secondary matching, e.g. + // if you don't add VideoCodec::kHevc to the VideoCaptureConfig, then + // supporting codec parameter "hev1.1.6.L153.B0" does not matter. + // + // The codec parameter support callback is optional, however if provided + // then any offered streams that have a non-empty codec parameter field must + // match. If a stream does not have a codec parameter, this callback will + // not be called. + virtual bool SupportsCodecParameter(const std::string& parameter) { + return true; + } + protected: virtual ~Client(); }; + // Information about the display the receiver is attached to. + struct Display { + // Returns true if all configurations supported by |other| are also + // supported by this instance. + bool IsSupersetOf(const Display& other) const; + + // The display limitations of the actual screen, used to provide upper + // bounds on streams. For example, we will never + // send 60FPS if it is going to be displayed on a 30FPS screen. + // Note that we may exceed the display width and height for standard + // content sizes like 720p or 1080p. + Dimensions dimensions; + + // Whether the embedder is capable of scaling content. If set to false, + // the sender will manage the aspect ratio scaling. + bool can_scale_content = false; + }; + + // Codec-specific audio limits for playback. + struct AudioLimits { + // Returns true if all configurations supported by |other| are also + // supported by this instance. + bool IsSupersetOf(const AudioLimits& other) const; + + // Whether or not these limits apply to all codecs. + bool applies_to_all_codecs = false; + + // Audio codec these limits apply to. Note that if |applies_to_all_codecs| + // is true this field is ignored. + AudioCodec codec; + + // Maximum audio sample rate. + int max_sample_rate = kDefaultAudioSampleRate; + + // Maximum audio channels, default is currently stereo. + int max_channels = kDefaultAudioChannels; + + // Minimum and maximum bitrates. Generally capture is done at the maximum + // bit rate, since audio bandwidth is much lower than video for most + // content. + int min_bit_rate = kDefaultAudioMinBitRate; + int max_bit_rate = kDefaultAudioMaxBitRate; + + // Max playout delay in milliseconds. + std::chrono::milliseconds max_delay = kDefaultMaxDelayMs; + }; + + // Codec-specific video limits for playback. + struct VideoLimits { + // Returns true if all configurations supported by |other| are also + // supported by this instance. + bool IsSupersetOf(const VideoLimits& other) const; + + // Whether or not these limits apply to all codecs. + bool applies_to_all_codecs = false; + + // Video codec these limits apply to. Note that if |applies_to_all_codecs| + // is true this field is ignored. + VideoCodec codec; + + // Maximum pixels per second. Value is the standard amount of pixels + // for 1080P at 30FPS. + int max_pixels_per_second = 1920 * 1080 * 30; + + // Maximum dimensions. Minimum dimensions try to use the same aspect + // ratio and are generated from the spec. + Dimensions max_dimensions = {1920, 1080, {kDefaultFrameRate, 1}}; + + // Minimum and maximum bitrates. Default values are based on default min and + // max dimensions, embedders that support different display dimensions + // should strongly consider setting these fields. + int min_bit_rate = kDefaultVideoMinBitRate; + int max_bit_rate = kDefaultVideoMaxBitRate; + + // Max playout delay in milliseconds. + std::chrono::milliseconds max_delay = kDefaultMaxDelayMs; + }; + + // This struct is used to provide preferences for setting up and running + // remoting streams. These properties are based on the current control + // protocol and allow remoting with current senders. + struct RemotingPreferences { + // Returns true if all configurations supported by |other| are also + // supported by this instance. + bool IsSupersetOf(const RemotingPreferences& other) const; + + // Current remoting senders take an "all or nothing" support for audio + // codec support. While Opus and AAC support is handled in our Preferences' + // |audio_codecs| property, support for the following codecs must be + // enabled or disabled all together: + // MP3 + // PCM, including Mu-Law, S16BE, S24BE, and ALAW variants + // Ogg Vorbis + // FLAC + // AMR, including narrow band (NB) and wide band (WB) variants + // GSM Mobile Station (MS) + // EAC3 (Dolby Digital Plus) + // ALAC (Apple Lossless) + // AC-3 (Dolby Digital) + // These properties are tied directly to what Chrome supports. See: + // https://source.chromium.org/chromium/chromium/src/+/master:media/base/audio_codecs.h + bool supports_chrome_audio_codecs = false; + + // Current remoting senders assume that the receiver supports 4K for all + // video codecs supplied in |video_codecs|, or none of them. + bool supports_4k = false; + }; + // Note: embedders are required to implement the following // codecs to be Cast V2 compliant: H264, VP8, AAC, Opus. struct Preferences { @@ -87,22 +249,39 @@ class ReceiverSession final : public Environment::SocketSubscriber { std::vector<AudioCodec> audio_codecs); Preferences(std::vector<VideoCodec> video_codecs, std::vector<AudioCodec> audio_codecs, - std::unique_ptr<Constraints> constraints, - std::unique_ptr<DisplayDescription> description); + std::vector<AudioLimits> audio_limits, + std::vector<VideoLimits> video_limits, + std::unique_ptr<Display> description); Preferences(Preferences&&) noexcept; - Preferences(const Preferences&) = delete; + Preferences(const Preferences&); Preferences& operator=(Preferences&&) noexcept; - Preferences& operator=(const Preferences&) = delete; + Preferences& operator=(const Preferences&); + // Returns true if all configurations supported by |other| are also + // supported by this instance. + bool IsSupersetOf(const Preferences& other) const; + + // Audio and video codec preferences. Should be supplied in order of + // preference, e.g. in this example if we get both VP8 and H264 we will + // generally select the VP8 offer. If a codec is omitted from these fields + // it will never be selected in the OFFER/ANSWER negotiation. std::vector<VideoCodec> video_codecs{VideoCodec::kVp8, VideoCodec::kH264}; std::vector<AudioCodec> audio_codecs{AudioCodec::kOpus, AudioCodec::kAac}; - // The embedder has the option of directly specifying the display - // information and video/audio constraints that will be passed along to - // senders during the offer/answer exchange. If nullptr, these are ignored. - std::unique_ptr<Constraints> constraints; - std::unique_ptr<DisplayDescription> display_description; + // Optional limitation fields that help the sender provide a delightful + // cast experience. Although optional, highly recommended. + // NOTE: embedders that wish to apply the same limits for all codecs can + // pass a vector of size 1 with the |applies_to_all_codecs| field set to + // true. + std::vector<AudioLimits> audio_limits; + std::vector<VideoLimits> video_limits; + std::unique_ptr<Display> display_description; + + // Libcast remoting support is opt-in: embedders wishing to field remoting + // offers may provide a set of remoting preferences, or leave nullptr for + // all remoting OFFERs to be rejected in favor of continuing streaming. + std::unique_ptr<RemotingPreferences> remoting; }; ReceiverSession(Client* const client, @@ -122,9 +301,18 @@ class ReceiverSession final : public Environment::SocketSubscriber { void OnSocketInvalid(Error error) override; private: + // In some cases, such as waiting for the UDP socket to be bound, we + // may have a pending session that cannot start yet. This class provides + // all necessary info to instantiate a session. struct SessionProperties { + // The cast mode the OFFER was sent for. + CastMode mode; + + // The selected audio and video streams from the original OFFER message. std::unique_ptr<AudioStream> selected_audio; std::unique_ptr<VideoStream> selected_video; + + // The sequence number of the OFFER that produced these properties. int sequence_number; // To be valid either the audio or video must be selected, and we must @@ -134,6 +322,12 @@ class ReceiverSession final : public Environment::SocketSubscriber { // Specific message type handler methods. void OnOffer(SenderMessage message); + void OnCapabilitiesRequest(SenderMessage message); + void OnRpcMessage(SenderMessage message); + + // Selects streams from an offer based on its configuration, and sets + // them in the session properties. + void SelectStreams(const Offer& offer, SessionProperties* properties); // Creates receivers and sends an appropriate Answer message using the // session properties. @@ -146,9 +340,13 @@ class ReceiverSession final : public Environment::SocketSubscriber { // video streams. NOTE: either audio or video may be null, but not both. ConfiguredReceivers SpawnReceivers(const SessionProperties& properties); - // Callers of this method should ensure at least one stream is non-null. + // Creates an ANSWER object. Assumes at least one stream is not nullptr. Answer ConstructAnswer(const SessionProperties& properties); + // Creates a ReceiverCapability version 2 object. This will be deprecated + // as part of https://issuetracker.google.com/184429130. + ReceiverCapability CreateRemotingCapabilityV2(); + // Handles resetting receivers and notifying the client. void ResetReceivers(Client::ReceiversDestroyingReason reason); @@ -158,21 +356,27 @@ class ReceiverSession final : public Environment::SocketSubscriber { Client* const client_; Environment* const environment_; const Preferences preferences_; + // The sender_id of this session. const std::string session_id_; - ReceiverSessionMessager messager_; - // In some cases, the session initialization may be pending waiting for the - // UDP socket to be ready. In this case, the receivers and the answer - // message will not be configured and sent until the UDP socket has finished - // binding. - std::unique_ptr<SessionProperties> pending_session_; + // The session messenger used for the lifetime of this session. + ReceiverSessionMessenger messenger_; - bool supports_wifi_status_reporting_ = false; + // The packet router to be used for all Receivers spawned by this session. ReceiverPacketRouter packet_router_; + // Any session pending while the UDP socket is being bound. + std::unique_ptr<SessionProperties> pending_session_; + + // The negotiated receivers we own, clients are notified of destruction + // through |Client::OnReceiversDestroying|. std::unique_ptr<Receiver> current_audio_receiver_; std::unique_ptr<Receiver> current_video_receiver_; + + // If remoting, we store the RpcMessenger used by the embedder to send RPC + // messages from the remoting protobuf specification. + std::unique_ptr<RpcMessenger> rpc_messenger_; }; } // namespace cast |