aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/receiver_session.h
diff options
context:
space:
mode:
Diffstat (limited to 'cast/streaming/receiver_session.h')
-rw-r--r--cast/streaming/receiver_session.h266
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