386 lines
16 KiB
C++
386 lines
16 KiB
C++
// 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_
|