| // 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_RECEIVER_SESSION_H_ |
| #define CAST_STREAMING_RECEIVER_SESSION_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "cast/common/public/message_port.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_messenger.h" |
| |
| namespace openscreen { |
| 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 |
| // for handling audio and video. Note that either receiver may be null. |
| struct ConfiguredReceivers { |
| // In practice, we may have 0, 1, or 2 receivers configured, depending |
| // on if the device supports audio and video, and if we were able to |
| // successfully negotiate a receiver configuration. |
| |
| // NOTES ON LIFETIMES: The audio and video Receiver pointers are owned by |
| // ReceiverSession, not the Client, and references to these pointers must be |
| // cleared before a call to Client::OnReceiversDestroying() returns. |
| |
| // If the receiver is audio- or video-only, or we failed to negotiate |
| // an acceptable session configuration with the sender, then either of the |
| // receivers may be nullptr. In this case, the associated config is default |
| // initialized and should be ignored. |
| Receiver* audio_receiver; |
| AudioCaptureConfig audio_config; |
| |
| Receiver* video_receiver; |
| 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 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 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|, 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 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 { |
| Preferences(); |
| Preferences(std::vector<VideoCodec> video_codecs, |
| std::vector<AudioCodec> audio_codecs); |
| Preferences(std::vector<VideoCodec> video_codecs, |
| std::vector<AudioCodec> audio_codecs, |
| std::vector<AudioLimits> audio_limits, |
| std::vector<VideoLimits> video_limits, |
| std::unique_ptr<Display> description); |
| |
| Preferences(Preferences&&) noexcept; |
| Preferences(const Preferences&); |
| Preferences& operator=(Preferences&&) noexcept; |
| 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}; |
| |
| // 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, |
| Environment* environment, |
| MessagePort* message_port, |
| Preferences preferences); |
| ReceiverSession(const ReceiverSession&) = delete; |
| ReceiverSession(ReceiverSession&&) noexcept = delete; |
| ReceiverSession& operator=(const ReceiverSession&) = delete; |
| ReceiverSession& operator=(ReceiverSession&&) = delete; |
| ~ReceiverSession(); |
| |
| const std::string& session_id() const { return session_id_; } |
| |
| // Environment::SocketSubscriber event callbacks. |
| void OnSocketReady() override; |
| 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 |
| // have a sequence number we can reference. |
| bool IsValid() const; |
| }; |
| |
| // 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. |
| void InitializeSession(const SessionProperties& properties); |
| |
| // Used by SpawnReceivers to generate a receiver for a specific stream. |
| std::unique_ptr<Receiver> ConstructReceiver(const Stream& stream); |
| |
| // Creates a set of configured receivers from a given pair of audio and |
| // video streams. NOTE: either audio or video may be null, but not both. |
| ConfiguredReceivers SpawnReceivers(const SessionProperties& properties); |
| |
| // 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); |
| |
| // Sends an error answer reply and notifies the client of the error. |
| void SendErrorAnswerReply(int sequence_number, const char* message); |
| |
| Client* const client_; |
| Environment* const environment_; |
| const Preferences preferences_; |
| |
| // The sender_id of this session. |
| const std::string session_id_; |
| |
| // The session messenger used for the lifetime of this session. |
| ReceiverSessionMessenger messenger_; |
| |
| // 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 |
| } // namespace openscreen |
| |
| #endif // CAST_STREAMING_RECEIVER_SESSION_H_ |