Relanding: Adding support for Unified Plan offer/answer negotiation to the mediasession layer.
This layer takes in a simplified "options" struct and the current local description,
and generates a new offer/answer. Previously the options struct assumed there would
only be one media description per media type (audio/video), but it now supports
N number of audio/video descriptions.
The |add_legacy_stream| options is removed from the mediasession.cc/.h
in this CL.
The next step is to add the ability for PeerConnection/WebRtcSession to create
"options" to represent multiple RtpTransceivers, and apply the Unified Plan
descriptions correctly. Right now, only Plan B descriptions will be
generated in unit tests.
BUG=chromium:465349
Review-Url: https://codereview.webrtc.org/2991693002
Cr-Original-Commit-Position: refs/heads/master@{#19343}
Committed: a77e6bbd30
Review-Url: https://codereview.webrtc.org/2991693002
Cr-Commit-Position: refs/heads/master@{#19394}
This commit is contained in:
parent
825f65e9d2
commit
1c378ed83b
File diff suppressed because it is too large
Load Diff
@ -102,83 +102,72 @@ RtpTransceiverDirection
|
||||
NegotiateRtpTransceiverDirection(RtpTransceiverDirection offer,
|
||||
RtpTransceiverDirection wants);
|
||||
|
||||
struct MediaSessionOptions {
|
||||
MediaSessionOptions()
|
||||
: recv_audio(true),
|
||||
recv_video(false),
|
||||
data_channel_type(DCT_NONE),
|
||||
is_muc(false),
|
||||
vad_enabled(true), // When disabled, removes all CN codecs from SDP.
|
||||
rtcp_mux_enabled(true),
|
||||
bundle_enabled(false),
|
||||
video_bandwidth(kAutoBandwidth),
|
||||
data_bandwidth(kDataMaxBandwidth),
|
||||
rtcp_cname(kDefaultRtcpCname) {}
|
||||
// Options for an RtpSender contained with an media description/"m=" section.
|
||||
struct SenderOptions {
|
||||
std::string track_id;
|
||||
std::string stream_id;
|
||||
int num_sim_layers;
|
||||
};
|
||||
|
||||
bool has_audio() const {
|
||||
return recv_audio || HasSendMediaStream(MEDIA_TYPE_AUDIO);
|
||||
}
|
||||
bool has_video() const {
|
||||
return recv_video || HasSendMediaStream(MEDIA_TYPE_VIDEO);
|
||||
}
|
||||
bool has_data() const { return data_channel_type != DCT_NONE; }
|
||||
// Options for an individual media description/"m=" section.
|
||||
struct MediaDescriptionOptions {
|
||||
MediaDescriptionOptions(MediaType type,
|
||||
const std::string& mid,
|
||||
RtpTransceiverDirection direction,
|
||||
bool stopped)
|
||||
: type(type), mid(mid), direction(direction), stopped(stopped) {}
|
||||
|
||||
// Add a stream with MediaType type and id.
|
||||
// All streams with the same sync_label will get the same CNAME.
|
||||
// All ids must be unique.
|
||||
void AddSendStream(MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label);
|
||||
void AddSendVideoStream(const std::string& id,
|
||||
const std::string& sync_label,
|
||||
// TODO(deadbeef): When we don't support Plan B, there will only be one
|
||||
// sender per media description and this can be simplified.
|
||||
void AddAudioSender(const std::string& track_id,
|
||||
const std::string& stream_id);
|
||||
void AddVideoSender(const std::string& track_id,
|
||||
const std::string& stream_id,
|
||||
int num_sim_layers);
|
||||
void RemoveSendStream(MediaType type, const std::string& id);
|
||||
|
||||
// Internally just uses sender_options.
|
||||
void AddRtpDataChannel(const std::string& track_id,
|
||||
const std::string& stream_id);
|
||||
|
||||
// Helper function.
|
||||
void AddSendStreamInternal(MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label,
|
||||
MediaType type;
|
||||
std::string mid;
|
||||
RtpTransceiverDirection direction;
|
||||
bool stopped;
|
||||
TransportOptions transport_options;
|
||||
// Note: There's no equivalent "RtpReceiverOptions" because only send
|
||||
// stream information goes in the local descriptions.
|
||||
std::vector<SenderOptions> sender_options;
|
||||
|
||||
private:
|
||||
// Doesn't DCHECK on |type|.
|
||||
void AddSenderInternal(const std::string& track_id,
|
||||
const std::string& stream_id,
|
||||
int num_sim_layers);
|
||||
};
|
||||
|
||||
bool HasSendMediaStream(MediaType type) const;
|
||||
// Provides a mechanism for describing how m= sections should be generated.
|
||||
// The m= section with index X will use media_description_options[X]. There
|
||||
// must be an option for each existing section if creating an answer, or a
|
||||
// subsequent offer.
|
||||
struct MediaSessionOptions {
|
||||
MediaSessionOptions() {}
|
||||
|
||||
// TODO(deadbeef): Put all the audio/video/data-specific options into a map
|
||||
// structure (content name -> options).
|
||||
// MediaSessionDescriptionFactory assumes there will never be more than one
|
||||
// audio/video/data content, but this will change with unified plan.
|
||||
bool recv_audio;
|
||||
bool recv_video;
|
||||
DataChannelType data_channel_type;
|
||||
bool is_muc;
|
||||
bool vad_enabled;
|
||||
bool rtcp_mux_enabled;
|
||||
bool bundle_enabled;
|
||||
// bps. -1 == auto.
|
||||
int video_bandwidth;
|
||||
int data_bandwidth;
|
||||
bool enable_ice_renomination = false;
|
||||
// content name ("mid") => options.
|
||||
std::map<std::string, TransportOptions> transport_options;
|
||||
std::string rtcp_cname;
|
||||
bool has_audio() const { return HasMediaDescription(MEDIA_TYPE_AUDIO); }
|
||||
bool has_video() const { return HasMediaDescription(MEDIA_TYPE_VIDEO); }
|
||||
bool has_data() const { return HasMediaDescription(MEDIA_TYPE_DATA); }
|
||||
|
||||
bool HasMediaDescription(MediaType type) const;
|
||||
|
||||
DataChannelType data_channel_type = DCT_NONE;
|
||||
bool is_muc = false;
|
||||
bool vad_enabled = true; // When disabled, removes all CN codecs from SDP.
|
||||
bool rtcp_mux_enabled = true;
|
||||
bool bundle_enabled = false;
|
||||
std::string rtcp_cname = kDefaultRtcpCname;
|
||||
rtc::CryptoOptions crypto_options;
|
||||
|
||||
struct Stream {
|
||||
Stream(MediaType type,
|
||||
const std::string& id,
|
||||
const std::string& sync_label,
|
||||
int num_sim_layers)
|
||||
: type(type), id(id), sync_label(sync_label),
|
||||
num_sim_layers(num_sim_layers) {
|
||||
}
|
||||
MediaType type;
|
||||
std::string id;
|
||||
std::string sync_label;
|
||||
int num_sim_layers;
|
||||
};
|
||||
|
||||
typedef std::vector<Stream> Streams;
|
||||
Streams streams;
|
||||
// List of media description options in the same order that the media
|
||||
// descriptions will be generated.
|
||||
std::vector<MediaDescriptionOptions> media_description_options;
|
||||
};
|
||||
|
||||
// "content" (as used in XEP-0166) descriptions for voice and video.
|
||||
@ -277,7 +266,6 @@ class MediaContentDescription : public ContentDescription {
|
||||
streams_.push_back(sp);
|
||||
}
|
||||
// Sets the CNAME of all StreamParams if it have not been set.
|
||||
// This can be used to set the CNAME of legacy streams.
|
||||
void SetCnameIfEmpty(const std::string& cname) {
|
||||
for (cricket::StreamParamsVec::iterator it = streams_.begin();
|
||||
it != streams_.end(); ++it) {
|
||||
@ -469,11 +457,6 @@ class MediaSessionDescriptionFactory {
|
||||
void set_data_codecs(const DataCodecs& codecs) { data_codecs_ = codecs; }
|
||||
SecurePolicy secure() const { return secure_; }
|
||||
void set_secure(SecurePolicy s) { secure_ = s; }
|
||||
// Decides if a StreamParams shall be added to the audio and video media
|
||||
// content in SessionDescription when CreateOffer and CreateAnswer is called
|
||||
// even if |options| don't include a Stream. This is needed to support legacy
|
||||
// applications. |add_legacy_| is true per default.
|
||||
void set_add_legacy_streams(bool add_legacy) { add_legacy_ = add_legacy; }
|
||||
|
||||
void set_enable_encrypted_rtp_header_extensions(bool enable) {
|
||||
enable_encrypted_rtp_header_extensions_ = enable;
|
||||
@ -493,13 +476,15 @@ class MediaSessionDescriptionFactory {
|
||||
const AudioCodecs& GetAudioCodecsForAnswer(
|
||||
const RtpTransceiverDirection& offer,
|
||||
const RtpTransceiverDirection& answer) const;
|
||||
void GetCodecsToOffer(const SessionDescription* current_description,
|
||||
const AudioCodecs& supported_audio_codecs,
|
||||
const VideoCodecs& supported_video_codecs,
|
||||
const DataCodecs& supported_data_codecs,
|
||||
AudioCodecs* audio_codecs,
|
||||
VideoCodecs* video_codecs,
|
||||
DataCodecs* data_codecs) const;
|
||||
void GetCodecsForOffer(const SessionDescription* current_description,
|
||||
AudioCodecs* audio_codecs,
|
||||
VideoCodecs* video_codecs,
|
||||
DataCodecs* data_codecs) const;
|
||||
void GetCodecsForAnswer(const SessionDescription* current_description,
|
||||
const SessionDescription* remote_offer,
|
||||
AudioCodecs* audio_codecs,
|
||||
VideoCodecs* video_codecs,
|
||||
DataCodecs* data_codecs) const;
|
||||
void GetRtpHdrExtsToOffer(const SessionDescription* current_description,
|
||||
RtpHeaderExtensions* audio_extensions,
|
||||
RtpHeaderExtensions* video_extensions) const;
|
||||
@ -526,7 +511,9 @@ class MediaSessionDescriptionFactory {
|
||||
// error.
|
||||
|
||||
bool AddAudioContentForOffer(
|
||||
const MediaSessionOptions& options,
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const RtpHeaderExtensions& audio_rtp_extensions,
|
||||
const AudioCodecs& audio_codecs,
|
||||
@ -534,7 +521,9 @@ class MediaSessionDescriptionFactory {
|
||||
SessionDescription* desc) const;
|
||||
|
||||
bool AddVideoContentForOffer(
|
||||
const MediaSessionOptions& options,
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const RtpHeaderExtensions& video_rtp_extensions,
|
||||
const VideoCodecs& video_codecs,
|
||||
@ -542,43 +531,66 @@ class MediaSessionDescriptionFactory {
|
||||
SessionDescription* desc) const;
|
||||
|
||||
bool AddDataContentForOffer(
|
||||
const MediaSessionOptions& options,
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
DataCodecs* data_codecs,
|
||||
const DataCodecs& data_codecs,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* desc) const;
|
||||
|
||||
bool AddAudioContentForAnswer(const SessionDescription* offer,
|
||||
const MediaSessionOptions& options,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer) const;
|
||||
bool AddAudioContentForAnswer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* offer_content,
|
||||
const SessionDescription* offer_description,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
const AudioCodecs& audio_codecs,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer) const;
|
||||
|
||||
bool AddVideoContentForAnswer(const SessionDescription* offer,
|
||||
const MediaSessionOptions& options,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer) const;
|
||||
bool AddVideoContentForAnswer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* offer_content,
|
||||
const SessionDescription* offer_description,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
const VideoCodecs& video_codecs,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer) const;
|
||||
|
||||
bool AddDataContentForAnswer(const SessionDescription* offer,
|
||||
const MediaSessionOptions& options,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer) const;
|
||||
bool AddDataContentForAnswer(
|
||||
const MediaDescriptionOptions& media_description_options,
|
||||
const MediaSessionOptions& session_options,
|
||||
const ContentInfo* offer_content,
|
||||
const SessionDescription* offer_description,
|
||||
const ContentInfo* current_content,
|
||||
const SessionDescription* current_description,
|
||||
const TransportInfo* bundle_transport,
|
||||
const DataCodecs& data_codecs,
|
||||
StreamParamsVec* current_streams,
|
||||
SessionDescription* answer) const;
|
||||
|
||||
void ComputeAudioCodecsIntersectionAndUnion();
|
||||
|
||||
AudioCodecs audio_send_codecs_;
|
||||
AudioCodecs audio_recv_codecs_;
|
||||
// Intersection of send and recv.
|
||||
AudioCodecs audio_sendrecv_codecs_;
|
||||
// Union of send and recv.
|
||||
AudioCodecs all_audio_codecs_;
|
||||
RtpHeaderExtensions audio_rtp_extensions_;
|
||||
VideoCodecs video_codecs_;
|
||||
RtpHeaderExtensions video_rtp_extensions_;
|
||||
DataCodecs data_codecs_;
|
||||
SecurePolicy secure_;
|
||||
bool add_legacy_;
|
||||
bool enable_encrypted_rtp_header_extensions_ = false;
|
||||
// TODO(zhihuang): Rename secure_ to sdec_policy_; rename the related getter
|
||||
// and setter.
|
||||
SecurePolicy secure_ = SEC_DISABLED;
|
||||
std::string lang_;
|
||||
const TransportDescriptionFactory* transport_desc_factory_;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -133,32 +133,45 @@ bool IsValidOfferToReceiveMedia(int value) {
|
||||
(value <= Options::kMaxOfferToReceiveMedia);
|
||||
}
|
||||
|
||||
// Add the stream and RTP data channel info to |session_options|.
|
||||
void AddSendStreams(
|
||||
cricket::MediaSessionOptions* session_options,
|
||||
// Add options to |[audio/video]_media_description_options| from |senders|.
|
||||
void AddRtpSenderOptions(
|
||||
const std::vector<rtc::scoped_refptr<
|
||||
RtpSenderProxyWithInternal<RtpSenderInternal>>>& senders,
|
||||
const std::map<std::string, rtc::scoped_refptr<DataChannel>>&
|
||||
rtp_data_channels) {
|
||||
session_options->streams.clear();
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options,
|
||||
cricket::MediaDescriptionOptions* video_media_description_options) {
|
||||
for (const auto& sender : senders) {
|
||||
session_options->AddSendStream(sender->media_type(), sender->id(),
|
||||
sender->internal()->stream_id());
|
||||
if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
|
||||
if (audio_media_description_options) {
|
||||
audio_media_description_options->AddAudioSender(
|
||||
sender->id(), sender->internal()->stream_id());
|
||||
}
|
||||
} else {
|
||||
RTC_DCHECK(sender->media_type() == cricket::MEDIA_TYPE_VIDEO);
|
||||
if (video_media_description_options) {
|
||||
video_media_description_options->AddVideoSender(
|
||||
sender->id(), sender->internal()->stream_id(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add options to |session_options| from |rtp_data_channels|.
|
||||
void AddRtpDataChannelOptions(
|
||||
const std::map<std::string, rtc::scoped_refptr<DataChannel>>&
|
||||
rtp_data_channels,
|
||||
cricket::MediaDescriptionOptions* data_media_description_options) {
|
||||
if (!data_media_description_options) {
|
||||
return;
|
||||
}
|
||||
// Check for data channels.
|
||||
for (const auto& kv : rtp_data_channels) {
|
||||
const DataChannel* channel = kv.second;
|
||||
if (channel->state() == DataChannel::kConnecting ||
|
||||
channel->state() == DataChannel::kOpen) {
|
||||
// |streamid| and |sync_label| are both set to the DataChannel label
|
||||
// here so they can be signaled the same way as MediaStreams and Tracks.
|
||||
// For MediaStreams, the sync_label is the MediaStream label and the
|
||||
// track label is the same as |streamid|.
|
||||
const std::string& streamid = channel->label();
|
||||
const std::string& sync_label = channel->label();
|
||||
session_options->AddSendStream(cricket::MEDIA_TYPE_DATA, streamid,
|
||||
sync_label);
|
||||
// Legacy RTP data channels are signaled with the track/stream ID set to
|
||||
// the data channel's label.
|
||||
data_media_description_options->AddRtpDataChannel(channel->label(),
|
||||
channel->label());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,92 +327,62 @@ std::string GenerateRtcpCname() {
|
||||
return cname;
|
||||
}
|
||||
|
||||
bool ExtractMediaSessionOptions(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
|
||||
bool is_offer,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
typedef PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions;
|
||||
if (!IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) ||
|
||||
!IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If constraints don't prevent us, we always accept video.
|
||||
if (rtc_options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
|
||||
session_options->recv_audio = (rtc_options.offer_to_receive_audio > 0);
|
||||
} else {
|
||||
session_options->recv_audio = true;
|
||||
}
|
||||
// For offers, we only offer video if we have it or it's forced by options.
|
||||
// For answers, we will always accept video (if offered).
|
||||
if (rtc_options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
|
||||
session_options->recv_video = (rtc_options.offer_to_receive_video > 0);
|
||||
} else if (is_offer) {
|
||||
session_options->recv_video = false;
|
||||
} else {
|
||||
session_options->recv_video = true;
|
||||
}
|
||||
|
||||
session_options->vad_enabled = rtc_options.voice_activity_detection;
|
||||
session_options->bundle_enabled = rtc_options.use_rtp_mux;
|
||||
for (auto& kv : session_options->transport_options) {
|
||||
kv.second.ice_restart = rtc_options.ice_restart;
|
||||
}
|
||||
|
||||
return true;
|
||||
bool ValidateOfferAnswerOptions(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options) {
|
||||
return IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_audio) &&
|
||||
IsValidOfferToReceiveMedia(rtc_options.offer_to_receive_video);
|
||||
}
|
||||
|
||||
bool ParseConstraintsForAnswer(const MediaConstraintsInterface* constraints,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
bool value = false;
|
||||
size_t mandatory_constraints_satisfied = 0;
|
||||
|
||||
// kOfferToReceiveAudio defaults to true according to spec.
|
||||
if (!FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kOfferToReceiveAudio, &value,
|
||||
&mandatory_constraints_satisfied) ||
|
||||
value) {
|
||||
session_options->recv_audio = true;
|
||||
}
|
||||
|
||||
// kOfferToReceiveVideo defaults to false according to spec. But
|
||||
// if it is an answer and video is offered, we should still accept video
|
||||
// per default.
|
||||
value = false;
|
||||
if (!FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kOfferToReceiveVideo, &value,
|
||||
&mandatory_constraints_satisfied) ||
|
||||
value) {
|
||||
session_options->recv_video = true;
|
||||
}
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kVoiceActivityDetection, &value,
|
||||
&mandatory_constraints_satisfied)) {
|
||||
session_options->vad_enabled = value;
|
||||
}
|
||||
|
||||
if (FindConstraint(constraints, MediaConstraintsInterface::kUseRtpMux, &value,
|
||||
&mandatory_constraints_satisfied)) {
|
||||
session_options->bundle_enabled = value;
|
||||
} else {
|
||||
// kUseRtpMux defaults to true according to spec.
|
||||
session_options->bundle_enabled = true;
|
||||
}
|
||||
|
||||
bool ice_restart = false;
|
||||
if (FindConstraint(constraints, MediaConstraintsInterface::kIceRestart,
|
||||
&value, &mandatory_constraints_satisfied)) {
|
||||
// kIceRestart defaults to false according to spec.
|
||||
ice_restart = true;
|
||||
}
|
||||
for (auto& kv : session_options->transport_options) {
|
||||
kv.second.ice_restart = ice_restart;
|
||||
}
|
||||
// From |rtc_options|, fill parts of |session_options| shared by all generated
|
||||
// m= sections (in other words, nothing that involves a map/array).
|
||||
void ExtractSharedMediaSessionOptions(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
session_options->vad_enabled = rtc_options.voice_activity_detection;
|
||||
session_options->bundle_enabled = rtc_options.use_rtp_mux;
|
||||
}
|
||||
|
||||
bool ConvertConstraintsToOfferAnswerOptions(
|
||||
const MediaConstraintsInterface* constraints,
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions* offer_answer_options) {
|
||||
if (!constraints) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool value = false;
|
||||
size_t mandatory_constraints_satisfied = 0;
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kOfferToReceiveAudio, &value,
|
||||
&mandatory_constraints_satisfied)) {
|
||||
offer_answer_options->offer_to_receive_audio =
|
||||
value ? PeerConnectionInterface::RTCOfferAnswerOptions::
|
||||
kOfferToReceiveMediaTrue
|
||||
: 0;
|
||||
}
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kOfferToReceiveVideo, &value,
|
||||
&mandatory_constraints_satisfied)) {
|
||||
offer_answer_options->offer_to_receive_video =
|
||||
value ? PeerConnectionInterface::RTCOfferAnswerOptions::
|
||||
kOfferToReceiveMediaTrue
|
||||
: 0;
|
||||
}
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kVoiceActivityDetection, &value,
|
||||
&mandatory_constraints_satisfied)) {
|
||||
offer_answer_options->voice_activity_detection = value;
|
||||
}
|
||||
if (FindConstraint(constraints, MediaConstraintsInterface::kUseRtpMux, &value,
|
||||
&mandatory_constraints_satisfied)) {
|
||||
offer_answer_options->use_rtp_mux = value;
|
||||
}
|
||||
if (FindConstraint(constraints, MediaConstraintsInterface::kIceRestart,
|
||||
&value, &mandatory_constraints_satisfied)) {
|
||||
offer_answer_options->ice_restart = value;
|
||||
}
|
||||
|
||||
return mandatory_constraints_satisfied == constraints->GetMandatory().size();
|
||||
}
|
||||
|
||||
@ -842,49 +825,17 @@ void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||
LOG(LS_ERROR) << "CreateOffer - observer is NULL.";
|
||||
return;
|
||||
}
|
||||
RTCOfferAnswerOptions options;
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options;
|
||||
// Always create an offer even if |ConvertConstraintsToOfferAnswerOptions|
|
||||
// returns false for now. Because |ConvertConstraintsToOfferAnswerOptions|
|
||||
// compares the mandatory fields parsed with the mandatory fields added in the
|
||||
// |constraints| and some downstream applications might create offers with
|
||||
// mandatory fields which would not be parsed in the helper method. For
|
||||
// example, in Chromium/remoting, |kEnableDtlsSrtp| is added to the
|
||||
// |constraints| as a mandatory field but it is not parsed.
|
||||
ConvertConstraintsToOfferAnswerOptions(constraints, &offer_answer_options);
|
||||
|
||||
bool value;
|
||||
size_t mandatory_constraints = 0;
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kOfferToReceiveAudio,
|
||||
&value,
|
||||
&mandatory_constraints)) {
|
||||
options.offer_to_receive_audio =
|
||||
value ? RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0;
|
||||
}
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kOfferToReceiveVideo,
|
||||
&value,
|
||||
&mandatory_constraints)) {
|
||||
options.offer_to_receive_video =
|
||||
value ? RTCOfferAnswerOptions::kOfferToReceiveMediaTrue : 0;
|
||||
}
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kVoiceActivityDetection,
|
||||
&value,
|
||||
&mandatory_constraints)) {
|
||||
options.voice_activity_detection = value;
|
||||
}
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kIceRestart,
|
||||
&value,
|
||||
&mandatory_constraints)) {
|
||||
options.ice_restart = value;
|
||||
}
|
||||
|
||||
if (FindConstraint(constraints,
|
||||
MediaConstraintsInterface::kUseRtpMux,
|
||||
&value,
|
||||
&mandatory_constraints)) {
|
||||
options.use_rtp_mux = value;
|
||||
}
|
||||
|
||||
CreateOffer(observer, options);
|
||||
CreateOffer(observer, offer_answer_options);
|
||||
}
|
||||
|
||||
void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||
@ -895,14 +846,15 @@ void PeerConnection::CreateOffer(CreateSessionDescriptionObserver* observer,
|
||||
return;
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions session_options;
|
||||
if (!GetOptionsForOffer(options, &session_options)) {
|
||||
if (!ValidateOfferAnswerOptions(options)) {
|
||||
std::string error = "CreateOffer called with invalid options.";
|
||||
LOG(LS_ERROR) << error;
|
||||
PostCreateSessionDescriptionFailure(observer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions session_options;
|
||||
GetOptionsForOffer(options, &session_options);
|
||||
session_->CreateOffer(observer, options, session_options);
|
||||
}
|
||||
|
||||
@ -915,14 +867,26 @@ void PeerConnection::CreateAnswer(
|
||||
return;
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions session_options;
|
||||
if (!GetOptionsForAnswer(constraints, &session_options)) {
|
||||
if (!session_->remote_description() ||
|
||||
session_->remote_description()->type() !=
|
||||
SessionDescriptionInterface::kOffer) {
|
||||
std::string error = "CreateAnswer called without remote offer.";
|
||||
LOG(LS_ERROR) << error;
|
||||
PostCreateSessionDescriptionFailure(observer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options;
|
||||
if (!ConvertConstraintsToOfferAnswerOptions(constraints,
|
||||
&offer_answer_options)) {
|
||||
std::string error = "CreateAnswer called with invalid constraints.";
|
||||
LOG(LS_ERROR) << error;
|
||||
PostCreateSessionDescriptionFailure(observer, error);
|
||||
return;
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions session_options;
|
||||
GetOptionsForAnswer(offer_answer_options, &session_options);
|
||||
session_->CreateAnswer(observer, session_options);
|
||||
}
|
||||
|
||||
@ -935,12 +899,7 @@ void PeerConnection::CreateAnswer(CreateSessionDescriptionObserver* observer,
|
||||
}
|
||||
|
||||
cricket::MediaSessionOptions session_options;
|
||||
if (!GetOptionsForAnswer(options, &session_options)) {
|
||||
std::string error = "CreateAnswer called with invalid options.";
|
||||
LOG(LS_ERROR) << error;
|
||||
PostCreateSessionDescriptionFailure(observer, error);
|
||||
return;
|
||||
}
|
||||
GetOptionsForAnswer(options, &session_options);
|
||||
|
||||
session_->CreateAnswer(observer, session_options);
|
||||
}
|
||||
@ -1698,121 +1657,242 @@ void PeerConnection::PostCreateSessionDescriptionFailure(
|
||||
MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
|
||||
}
|
||||
|
||||
bool PeerConnection::GetOptionsForOffer(
|
||||
void PeerConnection::GetOptionsForOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// TODO(deadbeef): Once we have transceivers, enumerate them here instead of
|
||||
// ContentInfos.
|
||||
ExtractSharedMediaSessionOptions(rtc_options, session_options);
|
||||
|
||||
// Figure out transceiver directional preferences.
|
||||
bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO);
|
||||
bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO);
|
||||
|
||||
// By default, generate sendrecv/recvonly m= sections.
|
||||
bool recv_audio = true;
|
||||
bool recv_video = true;
|
||||
|
||||
// By default, only offer a new m= section if we have media to send with it.
|
||||
bool offer_new_audio_description = send_audio;
|
||||
bool offer_new_video_description = send_video;
|
||||
bool offer_new_data_description = HasDataChannels();
|
||||
|
||||
// The "offer_to_receive_X" options allow those defaults to be overridden.
|
||||
if (rtc_options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_audio = (rtc_options.offer_to_receive_audio > 0);
|
||||
offer_new_audio_description =
|
||||
offer_new_audio_description || (rtc_options.offer_to_receive_audio > 0);
|
||||
}
|
||||
if (rtc_options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_video = (rtc_options.offer_to_receive_video > 0);
|
||||
offer_new_video_description =
|
||||
offer_new_video_description || (rtc_options.offer_to_receive_video > 0);
|
||||
}
|
||||
|
||||
rtc::Optional<size_t> audio_index;
|
||||
rtc::Optional<size_t> video_index;
|
||||
rtc::Optional<size_t> data_index;
|
||||
// If a current description exists, generate m= sections in the same order,
|
||||
// using the first audio/video/data section that appears and rejecting
|
||||
// extraneous ones.
|
||||
if (session_->local_description()) {
|
||||
for (const cricket::ContentInfo& content :
|
||||
session_->local_description()->description()->contents()) {
|
||||
session_options->transport_options[content.name] =
|
||||
cricket::TransportOptions();
|
||||
}
|
||||
}
|
||||
session_options->enable_ice_renomination =
|
||||
configuration_.enable_ice_renomination;
|
||||
|
||||
if (!ExtractMediaSessionOptions(rtc_options, true, session_options)) {
|
||||
return false;
|
||||
GenerateMediaDescriptionOptions(
|
||||
session_->local_description(),
|
||||
cricket::RtpTransceiverDirection(send_audio, recv_audio),
|
||||
cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index,
|
||||
&video_index, &data_index, session_options);
|
||||
}
|
||||
|
||||
AddSendStreams(session_options, senders_, rtp_data_channels_);
|
||||
// Offer to receive audio/video if the constraint is not set and there are
|
||||
// send streams, or we're currently receiving.
|
||||
if (rtc_options.offer_to_receive_audio == RTCOfferAnswerOptions::kUndefined) {
|
||||
session_options->recv_audio =
|
||||
session_options->HasSendMediaStream(cricket::MEDIA_TYPE_AUDIO) ||
|
||||
!remote_audio_tracks_.empty();
|
||||
// Add audio/video/data m= sections to the end if needed.
|
||||
if (!audio_index && offer_new_audio_description) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
|
||||
cricket::RtpTransceiverDirection(send_audio, recv_audio), false));
|
||||
audio_index = rtc::Optional<size_t>(
|
||||
session_options->media_description_options.size() - 1);
|
||||
}
|
||||
if (rtc_options.offer_to_receive_video == RTCOfferAnswerOptions::kUndefined) {
|
||||
session_options->recv_video =
|
||||
session_options->HasSendMediaStream(cricket::MEDIA_TYPE_VIDEO) ||
|
||||
!remote_video_tracks_.empty();
|
||||
if (!video_index && offer_new_video_description) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
|
||||
cricket::RtpTransceiverDirection(send_video, recv_video), false));
|
||||
video_index = rtc::Optional<size_t>(
|
||||
session_options->media_description_options.size() - 1);
|
||||
}
|
||||
if (!data_index && offer_new_data_description) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_DATA, cricket::CN_DATA,
|
||||
cricket::RtpTransceiverDirection(true, true), false));
|
||||
data_index = rtc::Optional<size_t>(
|
||||
session_options->media_description_options.size() - 1);
|
||||
}
|
||||
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options =
|
||||
!audio_index ? nullptr
|
||||
: &session_options->media_description_options[*audio_index];
|
||||
cricket::MediaDescriptionOptions* video_media_description_options =
|
||||
!video_index ? nullptr
|
||||
: &session_options->media_description_options[*video_index];
|
||||
cricket::MediaDescriptionOptions* data_media_description_options =
|
||||
!data_index ? nullptr
|
||||
: &session_options->media_description_options[*data_index];
|
||||
|
||||
// Apply ICE restart flag and renomination flag.
|
||||
for (auto& options : session_options->media_description_options) {
|
||||
options.transport_options.ice_restart = rtc_options.ice_restart;
|
||||
options.transport_options.enable_ice_renomination =
|
||||
configuration_.enable_ice_renomination;
|
||||
}
|
||||
|
||||
AddRtpSenderOptions(senders_, audio_media_description_options,
|
||||
video_media_description_options);
|
||||
AddRtpDataChannelOptions(rtp_data_channels_, data_media_description_options);
|
||||
|
||||
// Intentionally unset the data channel type for RTP data channel with the
|
||||
// second condition. Otherwise the RTP data channels would be successfully
|
||||
// negotiated by default and the unit tests in WebRtcDataBrowserTest will fail
|
||||
// when building with chromium. We want to leave RTP data channels broken, so
|
||||
// people won't try to use them.
|
||||
if (HasDataChannels() && session_->data_channel_type() != cricket::DCT_RTP) {
|
||||
if (!rtp_data_channels_.empty() ||
|
||||
session_->data_channel_type() != cricket::DCT_RTP) {
|
||||
session_options->data_channel_type = session_->data_channel_type();
|
||||
}
|
||||
|
||||
session_options->bundle_enabled =
|
||||
session_options->bundle_enabled &&
|
||||
(session_options->has_audio() || session_options->has_video() ||
|
||||
session_options->has_data());
|
||||
|
||||
session_options->rtcp_cname = rtcp_cname_;
|
||||
session_options->crypto_options = factory_->options().crypto_options;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnection::InitializeOptionsForAnswer(
|
||||
void PeerConnection::GetOptionsForAnswer(
|
||||
const RTCOfferAnswerOptions& rtc_options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
session_options->recv_audio = false;
|
||||
session_options->recv_video = false;
|
||||
session_options->enable_ice_renomination =
|
||||
configuration_.enable_ice_renomination;
|
||||
}
|
||||
ExtractSharedMediaSessionOptions(rtc_options, session_options);
|
||||
|
||||
void PeerConnection::FinishOptionsForAnswer(
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
// TODO(deadbeef): Once we have transceivers, enumerate them here instead of
|
||||
// ContentInfos.
|
||||
if (session_->remote_description()) {
|
||||
// Initialize the transport_options map.
|
||||
for (const cricket::ContentInfo& content :
|
||||
session_->remote_description()->description()->contents()) {
|
||||
session_options->transport_options[content.name] =
|
||||
cricket::TransportOptions();
|
||||
}
|
||||
// Figure out transceiver directional preferences.
|
||||
bool send_audio = HasRtpSender(cricket::MEDIA_TYPE_AUDIO);
|
||||
bool send_video = HasRtpSender(cricket::MEDIA_TYPE_VIDEO);
|
||||
|
||||
// By default, generate sendrecv/recvonly m= sections. The direction is also
|
||||
// restricted by the direction in the offer.
|
||||
bool recv_audio = true;
|
||||
bool recv_video = true;
|
||||
|
||||
// The "offer_to_receive_X" options allow those defaults to be overridden.
|
||||
if (rtc_options.offer_to_receive_audio != RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_audio = (rtc_options.offer_to_receive_audio > 0);
|
||||
}
|
||||
AddSendStreams(session_options, senders_, rtp_data_channels_);
|
||||
// RTP data channel is handled in MediaSessionOptions::AddStream. SCTP streams
|
||||
// are not signaled in the SDP so does not go through that path and must be
|
||||
// handled here.
|
||||
if (rtc_options.offer_to_receive_video != RTCOfferAnswerOptions::kUndefined) {
|
||||
recv_video = (rtc_options.offer_to_receive_video > 0);
|
||||
}
|
||||
|
||||
rtc::Optional<size_t> audio_index;
|
||||
rtc::Optional<size_t> video_index;
|
||||
rtc::Optional<size_t> data_index;
|
||||
// There should be a pending remote description that's an offer...
|
||||
RTC_DCHECK(session_->remote_description());
|
||||
RTC_DCHECK(session_->remote_description()->type() ==
|
||||
SessionDescriptionInterface::kOffer);
|
||||
// Generate m= sections that match those in the offer.
|
||||
// Note that mediasession.cc will handle intersection our preferred direction
|
||||
// with the offered direction.
|
||||
GenerateMediaDescriptionOptions(
|
||||
session_->remote_description(),
|
||||
cricket::RtpTransceiverDirection(send_audio, recv_audio),
|
||||
cricket::RtpTransceiverDirection(send_video, recv_video), &audio_index,
|
||||
&video_index, &data_index, session_options);
|
||||
|
||||
cricket::MediaDescriptionOptions* audio_media_description_options =
|
||||
!audio_index ? nullptr
|
||||
: &session_options->media_description_options[*audio_index];
|
||||
cricket::MediaDescriptionOptions* video_media_description_options =
|
||||
!video_index ? nullptr
|
||||
: &session_options->media_description_options[*video_index];
|
||||
cricket::MediaDescriptionOptions* data_media_description_options =
|
||||
!data_index ? nullptr
|
||||
: &session_options->media_description_options[*data_index];
|
||||
|
||||
// Apply ICE renomination flag.
|
||||
for (auto& options : session_options->media_description_options) {
|
||||
options.transport_options.enable_ice_renomination =
|
||||
configuration_.enable_ice_renomination;
|
||||
}
|
||||
|
||||
AddRtpSenderOptions(senders_, audio_media_description_options,
|
||||
video_media_description_options);
|
||||
AddRtpDataChannelOptions(rtp_data_channels_, data_media_description_options);
|
||||
|
||||
// Intentionally unset the data channel type for RTP data channel. Otherwise
|
||||
// the RTP data channels would be successfully negotiated by default and the
|
||||
// unit tests in WebRtcDataBrowserTest will fail when building with chromium.
|
||||
// We want to leave RTP data channels broken, so people won't try to use them.
|
||||
if (session_->data_channel_type() != cricket::DCT_RTP) {
|
||||
if (!rtp_data_channels_.empty() ||
|
||||
session_->data_channel_type() != cricket::DCT_RTP) {
|
||||
session_options->data_channel_type = session_->data_channel_type();
|
||||
}
|
||||
session_options->bundle_enabled =
|
||||
session_options->bundle_enabled &&
|
||||
(session_options->has_audio() || session_options->has_video() ||
|
||||
session_options->has_data());
|
||||
|
||||
session_options->rtcp_cname = rtcp_cname_;
|
||||
session_options->crypto_options = factory_->options().crypto_options;
|
||||
}
|
||||
|
||||
bool PeerConnection::GetOptionsForAnswer(
|
||||
const MediaConstraintsInterface* constraints,
|
||||
void PeerConnection::GenerateMediaDescriptionOptions(
|
||||
const SessionDescriptionInterface* session_desc,
|
||||
cricket::RtpTransceiverDirection audio_direction,
|
||||
cricket::RtpTransceiverDirection video_direction,
|
||||
rtc::Optional<size_t>* audio_index,
|
||||
rtc::Optional<size_t>* video_index,
|
||||
rtc::Optional<size_t>* data_index,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
InitializeOptionsForAnswer(session_options);
|
||||
if (!ParseConstraintsForAnswer(constraints, session_options)) {
|
||||
return false;
|
||||
for (const cricket::ContentInfo& content :
|
||||
session_desc->description()->contents()) {
|
||||
if (IsAudioContent(&content)) {
|
||||
// If we already have an audio m= section, reject this extra one.
|
||||
if (*audio_index) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_AUDIO, content.name,
|
||||
cricket::RtpTransceiverDirection(false, false), true));
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_AUDIO, content.name, audio_direction,
|
||||
!audio_direction.send && !audio_direction.recv));
|
||||
*audio_index = rtc::Optional<size_t>(
|
||||
session_options->media_description_options.size() - 1);
|
||||
}
|
||||
} else if (IsVideoContent(&content)) {
|
||||
// If we already have an video m= section, reject this extra one.
|
||||
if (*video_index) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_VIDEO, content.name,
|
||||
cricket::RtpTransceiverDirection(false, false), true));
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_VIDEO, content.name, video_direction,
|
||||
!video_direction.send && !video_direction.recv));
|
||||
*video_index = rtc::Optional<size_t>(
|
||||
session_options->media_description_options.size() - 1);
|
||||
}
|
||||
} else {
|
||||
RTC_DCHECK(IsDataContent(&content));
|
||||
// If we already have an data m= section, reject this extra one.
|
||||
if (*data_index) {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_DATA, content.name,
|
||||
cricket::RtpTransceiverDirection(false, false), true));
|
||||
} else {
|
||||
session_options->media_description_options.push_back(
|
||||
cricket::MediaDescriptionOptions(
|
||||
cricket::MEDIA_TYPE_DATA, content.name,
|
||||
// Direction for data sections is meaningless, but legacy
|
||||
// endpoints might expect sendrecv.
|
||||
cricket::RtpTransceiverDirection(true, true), false));
|
||||
*data_index = rtc::Optional<size_t>(
|
||||
session_options->media_description_options.size() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
session_options->rtcp_cname = rtcp_cname_;
|
||||
|
||||
FinishOptionsForAnswer(session_options);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PeerConnection::GetOptionsForAnswer(
|
||||
const RTCOfferAnswerOptions& options,
|
||||
cricket::MediaSessionOptions* session_options) {
|
||||
InitializeOptionsForAnswer(session_options);
|
||||
if (!ExtractMediaSessionOptions(options, false, session_options)) {
|
||||
return false;
|
||||
}
|
||||
session_options->rtcp_cname = rtcp_cname_;
|
||||
|
||||
FinishOptionsForAnswer(session_options);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnection::RemoveTracks(cricket::MediaType media_type) {
|
||||
@ -2285,6 +2365,15 @@ void PeerConnection::OnDataChannelOpenMessage(
|
||||
observer_->OnDataChannel(std::move(proxy_channel));
|
||||
}
|
||||
|
||||
bool PeerConnection::HasRtpSender(cricket::MediaType type) const {
|
||||
return std::find_if(
|
||||
senders_.begin(), senders_.end(),
|
||||
[type](const rtc::scoped_refptr<
|
||||
RtpSenderProxyWithInternal<RtpSenderInternal>>& sender) {
|
||||
return sender->media_type() == type;
|
||||
}) != senders_.end();
|
||||
}
|
||||
|
||||
RtpSenderInternal* PeerConnection::FindSenderById(const std::string& id) {
|
||||
auto it = std::find_if(
|
||||
senders_.begin(), senders_.end(),
|
||||
|
||||
@ -32,28 +32,12 @@ class MediaStreamObserver;
|
||||
class VideoRtpReceiver;
|
||||
class RtcEventLog;
|
||||
|
||||
// Populates |session_options| from |rtc_options|, and returns true if options
|
||||
// are valid.
|
||||
// |session_options|->transport_options map entries must exist in order for
|
||||
// them to be populated from |rtc_options|.
|
||||
bool ExtractMediaSessionOptions(
|
||||
// TODO(zhihuang): Remove this declaration when the WebRtcSession tests don't
|
||||
// need it.
|
||||
void ExtractSharedMediaSessionOptions(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
|
||||
bool is_offer,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
// Populates |session_options| from |constraints|, and returns true if all
|
||||
// mandatory constraints are satisfied.
|
||||
// Assumes that |session_options|->transport_options map entries exist.
|
||||
// Will also set defaults if corresponding constraints are not present:
|
||||
// recv_audio=true, recv_video=true, bundle_enabled=true.
|
||||
// Other fields will be left with existing values.
|
||||
//
|
||||
// Deprecated. Will be removed once callers that use constraints are gone.
|
||||
// TODO(hta): Remove when callers are gone.
|
||||
// https://bugs.chromium.org/p/webrtc/issues/detail?id=5617
|
||||
bool ParseConstraintsForAnswer(const MediaConstraintsInterface* constraints,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
// PeerConnection implements the PeerConnectionInterface interface.
|
||||
// It uses WebRtcSession to implement the PeerConnection functionality.
|
||||
class PeerConnection : public PeerConnectionInterface,
|
||||
@ -244,26 +228,24 @@ class PeerConnection : public PeerConnectionInterface,
|
||||
|
||||
// Returns a MediaSessionOptions struct with options decided by |options|,
|
||||
// the local MediaStreams and DataChannels.
|
||||
virtual bool GetOptionsForOffer(
|
||||
void GetOptionsForOffer(
|
||||
const PeerConnectionInterface::RTCOfferAnswerOptions& rtc_options,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
// Returns a MediaSessionOptions struct with options decided by
|
||||
// |constraints|, the local MediaStreams and DataChannels.
|
||||
// Deprecated, use version without constraints.
|
||||
virtual bool GetOptionsForAnswer(
|
||||
const MediaConstraintsInterface* constraints,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
virtual bool GetOptionsForAnswer(
|
||||
const RTCOfferAnswerOptions& options,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
void GetOptionsForAnswer(const RTCOfferAnswerOptions& options,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
void InitializeOptionsForAnswer(
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
// Helper function for options processing.
|
||||
// Deprecated.
|
||||
virtual void FinishOptionsForAnswer(
|
||||
// Generates MediaDescriptionOptions for the |session_opts| based on existing
|
||||
// local description or remote description.
|
||||
void GenerateMediaDescriptionOptions(
|
||||
const SessionDescriptionInterface* session_desc,
|
||||
cricket::RtpTransceiverDirection audio_direction,
|
||||
cricket::RtpTransceiverDirection video_direction,
|
||||
rtc::Optional<size_t>* audio_index,
|
||||
rtc::Optional<size_t>* video_index,
|
||||
rtc::Optional<size_t>* data_index,
|
||||
cricket::MediaSessionOptions* session_options);
|
||||
|
||||
// Remove all local and remote tracks of type |media_type|.
|
||||
@ -361,6 +343,7 @@ class PeerConnection : public PeerConnectionInterface,
|
||||
void OnDataChannelOpenMessage(const std::string& label,
|
||||
const InternalDataChannelInit& config);
|
||||
|
||||
bool HasRtpSender(cricket::MediaType type) const;
|
||||
RtpSenderInternal* FindSenderById(const std::string& id);
|
||||
|
||||
std::vector<rtc::scoped_refptr<
|
||||
|
||||
@ -1184,6 +1184,57 @@ class PeerConnectionInterfaceTest : public testing::Test {
|
||||
return audio_desc->streams()[0].cname;
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> CreateOfferWithOptions(
|
||||
const RTCOfferAnswerOptions& offer_answer_options) {
|
||||
RTC_DCHECK(pc_);
|
||||
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
|
||||
new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>());
|
||||
pc_->CreateOffer(observer, offer_answer_options);
|
||||
EXPECT_EQ_WAIT(true, observer->called(), kTimeout);
|
||||
return observer->MoveDescription();
|
||||
}
|
||||
|
||||
void CreateOfferWithOptionsAsRemoteDescription(
|
||||
std::unique_ptr<SessionDescriptionInterface>* desc,
|
||||
const RTCOfferAnswerOptions& offer_answer_options) {
|
||||
*desc = CreateOfferWithOptions(offer_answer_options);
|
||||
ASSERT_TRUE(desc != nullptr);
|
||||
std::string sdp;
|
||||
EXPECT_TRUE((*desc)->ToString(&sdp));
|
||||
SessionDescriptionInterface* remote_offer =
|
||||
webrtc::CreateSessionDescription(SessionDescriptionInterface::kOffer,
|
||||
sdp, NULL);
|
||||
EXPECT_TRUE(DoSetRemoteDescription(remote_offer));
|
||||
EXPECT_EQ(PeerConnectionInterface::kHaveRemoteOffer, observer_.state_);
|
||||
}
|
||||
|
||||
void CreateOfferWithOptionsAsLocalDescription(
|
||||
std::unique_ptr<SessionDescriptionInterface>* desc,
|
||||
const RTCOfferAnswerOptions& offer_answer_options) {
|
||||
*desc = CreateOfferWithOptions(offer_answer_options);
|
||||
ASSERT_TRUE(desc != nullptr);
|
||||
std::string sdp;
|
||||
EXPECT_TRUE((*desc)->ToString(&sdp));
|
||||
SessionDescriptionInterface* new_offer = webrtc::CreateSessionDescription(
|
||||
SessionDescriptionInterface::kOffer, sdp, NULL);
|
||||
|
||||
EXPECT_TRUE(DoSetLocalDescription(new_offer));
|
||||
EXPECT_EQ(PeerConnectionInterface::kHaveLocalOffer, observer_.state_);
|
||||
}
|
||||
|
||||
bool HasCNCodecs(const cricket::ContentInfo* content) {
|
||||
const cricket::ContentDescription* description = content->description;
|
||||
RTC_DCHECK(description);
|
||||
const cricket::AudioContentDescription* audio_content_desc =
|
||||
static_cast<const cricket::AudioContentDescription*>(description);
|
||||
RTC_DCHECK(audio_content_desc);
|
||||
for (size_t i = 0; i < audio_content_desc->codecs().size(); ++i) {
|
||||
if (audio_content_desc->codecs()[i].name == "CN")
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<rtc::VirtualSocketServer> vss_;
|
||||
rtc::AutoSocketServerThread main_;
|
||||
cricket::FakePortAllocator* port_allocator_ = nullptr;
|
||||
@ -3494,6 +3545,224 @@ TEST_F(PeerConnectionInterfaceTest, SetBitrateCurrentLessThanImplicitMin) {
|
||||
EXPECT_TRUE(pc_->SetBitrate(bitrate).ok());
|
||||
}
|
||||
|
||||
// The following tests verify that the offer can be created correctly.
|
||||
TEST_F(PeerConnectionInterfaceTest,
|
||||
CreateOfferFailsWithInvalidOfferToReceiveAudio) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
|
||||
// Setting offer_to_receive_audio to a value lower than kUndefined or greater
|
||||
// than kMaxOfferToReceiveMedia should be treated as invalid.
|
||||
rtc_options.offer_to_receive_audio = RTCOfferAnswerOptions::kUndefined - 1;
|
||||
CreatePeerConnection();
|
||||
EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
|
||||
|
||||
rtc_options.offer_to_receive_audio =
|
||||
RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
|
||||
EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionInterfaceTest,
|
||||
CreateOfferFailsWithInvalidOfferToReceiveVideo) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
|
||||
// Setting offer_to_receive_video to a value lower than kUndefined or greater
|
||||
// than kMaxOfferToReceiveMedia should be treated as invalid.
|
||||
rtc_options.offer_to_receive_video = RTCOfferAnswerOptions::kUndefined - 1;
|
||||
CreatePeerConnection();
|
||||
EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
|
||||
|
||||
rtc_options.offer_to_receive_video =
|
||||
RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
|
||||
EXPECT_FALSE(CreateOfferWithOptions(rtc_options));
|
||||
}
|
||||
|
||||
// Test that the audio and video content will be added to an offer if both
|
||||
// |offer_to_receive_audio| and |offer_to_receive_video| options are 1.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithAudioVideoOptions) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
rtc_options.offer_to_receive_video = 1;
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreatePeerConnection();
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
|
||||
EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
|
||||
}
|
||||
|
||||
// Test that only audio content will be added to the offer if only
|
||||
// |offer_to_receive_audio| options is 1.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithAudioOnlyOptions) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
rtc_options.offer_to_receive_video = 0;
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreatePeerConnection();
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
|
||||
EXPECT_EQ(nullptr, GetFirstVideoContent(offer->description()));
|
||||
}
|
||||
|
||||
// Test that only video content will be added if only |offer_to_receive_video|
|
||||
// options is 1.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithVideoOnlyOptions) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 0;
|
||||
rtc_options.offer_to_receive_video = 1;
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreatePeerConnection();
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
EXPECT_EQ(nullptr, GetFirstAudioContent(offer->description()));
|
||||
EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
|
||||
}
|
||||
|
||||
// Test that if |voice_activity_detection| is false, no CN codec is added to the
|
||||
// offer.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithVADOptions) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
rtc_options.offer_to_receive_video = 0;
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreatePeerConnection();
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
const cricket::ContentInfo* audio_content =
|
||||
offer->description()->GetContentByName(cricket::CN_AUDIO);
|
||||
ASSERT_TRUE(audio_content);
|
||||
// |voice_activity_detection| is true by default.
|
||||
EXPECT_TRUE(HasCNCodecs(audio_content));
|
||||
|
||||
rtc_options.voice_activity_detection = false;
|
||||
CreatePeerConnection();
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
audio_content = offer->description()->GetContentByName(cricket::CN_AUDIO);
|
||||
ASSERT_TRUE(audio_content);
|
||||
EXPECT_FALSE(HasCNCodecs(audio_content));
|
||||
}
|
||||
|
||||
// Test that no media content will be added to the offer if using default
|
||||
// RTCOfferAnswerOptions.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithDefaultOfferAnswerOptions) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreatePeerConnection();
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
EXPECT_EQ(nullptr, GetFirstAudioContent(offer->description()));
|
||||
EXPECT_EQ(nullptr, GetFirstVideoContent(offer->description()));
|
||||
}
|
||||
|
||||
// Test that if |ice_restart| is true, the ufrag/pwd will change, otherwise
|
||||
// ufrag/pwd will be the same in the new offer.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithIceRestart) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.ice_restart = false;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreatePeerConnection();
|
||||
CreateOfferWithOptionsAsLocalDescription(&offer, rtc_options);
|
||||
auto ufrag1 = offer->description()
|
||||
->GetTransportInfoByName(cricket::CN_AUDIO)
|
||||
->description.ice_ufrag;
|
||||
auto pwd1 = offer->description()
|
||||
->GetTransportInfoByName(cricket::CN_AUDIO)
|
||||
->description.ice_pwd;
|
||||
|
||||
// |ice_restart| is false, the ufrag/pwd shouldn't change.
|
||||
CreateOfferWithOptionsAsLocalDescription(&offer, rtc_options);
|
||||
auto ufrag2 = offer->description()
|
||||
->GetTransportInfoByName(cricket::CN_AUDIO)
|
||||
->description.ice_ufrag;
|
||||
auto pwd2 = offer->description()
|
||||
->GetTransportInfoByName(cricket::CN_AUDIO)
|
||||
->description.ice_pwd;
|
||||
|
||||
// |ice_restart| is true, the ufrag/pwd should change.
|
||||
rtc_options.ice_restart = true;
|
||||
CreateOfferWithOptionsAsLocalDescription(&offer, rtc_options);
|
||||
auto ufrag3 = offer->description()
|
||||
->GetTransportInfoByName(cricket::CN_AUDIO)
|
||||
->description.ice_ufrag;
|
||||
auto pwd3 = offer->description()
|
||||
->GetTransportInfoByName(cricket::CN_AUDIO)
|
||||
->description.ice_pwd;
|
||||
|
||||
EXPECT_EQ(ufrag1, ufrag2);
|
||||
EXPECT_EQ(pwd1, pwd2);
|
||||
EXPECT_NE(ufrag2, ufrag3);
|
||||
EXPECT_NE(pwd2, pwd3);
|
||||
}
|
||||
|
||||
// Test that if |use_rtp_mux| is true, the bundling will be enabled in the
|
||||
// offer; if it is false, there won't be any bundle group in the offer.
|
||||
TEST_F(PeerConnectionInterfaceTest, CreateOfferWithRtpMux) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
rtc_options.offer_to_receive_video = 1;
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreatePeerConnection();
|
||||
|
||||
rtc_options.use_rtp_mux = true;
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
|
||||
EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
|
||||
EXPECT_TRUE(offer->description()->HasGroup(cricket::GROUP_TYPE_BUNDLE));
|
||||
|
||||
rtc_options.use_rtp_mux = false;
|
||||
offer = CreateOfferWithOptions(rtc_options);
|
||||
ASSERT_TRUE(offer);
|
||||
EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
|
||||
EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
|
||||
EXPECT_FALSE(offer->description()->HasGroup(cricket::GROUP_TYPE_BUNDLE));
|
||||
}
|
||||
|
||||
// If SetMandatoryReceiveAudio(false) and SetMandatoryReceiveVideo(false) are
|
||||
// called for the answer constraints, but an audio and a video section were
|
||||
// offered, there will still be an audio and a video section in the answer.
|
||||
TEST_F(PeerConnectionInterfaceTest,
|
||||
RejectAudioAndVideoInAnswerWithConstraints) {
|
||||
// Offer both audio and video.
|
||||
RTCOfferAnswerOptions rtc_offer_options;
|
||||
rtc_offer_options.offer_to_receive_audio = 1;
|
||||
rtc_offer_options.offer_to_receive_video = 1;
|
||||
|
||||
CreatePeerConnection();
|
||||
std::unique_ptr<SessionDescriptionInterface> offer;
|
||||
CreateOfferWithOptionsAsRemoteDescription(&offer, rtc_offer_options);
|
||||
EXPECT_NE(nullptr, GetFirstAudioContent(offer->description()));
|
||||
EXPECT_NE(nullptr, GetFirstVideoContent(offer->description()));
|
||||
|
||||
// Since an offer has been created with both audio and video,
|
||||
// Answers will contain the media types that exist in the offer regardless of
|
||||
// the value of |answer_options.has_audio| and |answer_options.has_video|.
|
||||
FakeConstraints answer_c;
|
||||
// Reject both audio and video.
|
||||
answer_c.SetMandatoryReceiveAudio(false);
|
||||
answer_c.SetMandatoryReceiveVideo(false);
|
||||
|
||||
std::unique_ptr<SessionDescriptionInterface> answer;
|
||||
ASSERT_TRUE(DoCreateAnswer(&answer, &answer_c));
|
||||
const cricket::ContentInfo* audio_content =
|
||||
GetFirstAudioContent(answer->description());
|
||||
const cricket::ContentInfo* video_content =
|
||||
GetFirstVideoContent(answer->description());
|
||||
ASSERT_NE(nullptr, audio_content);
|
||||
ASSERT_NE(nullptr, video_content);
|
||||
EXPECT_TRUE(audio_content->rejected);
|
||||
EXPECT_TRUE(video_content->rejected);
|
||||
}
|
||||
|
||||
class PeerConnectionMediaConfigTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
@ -3502,8 +3771,7 @@ class PeerConnectionMediaConfigTest : public testing::Test {
|
||||
}
|
||||
const cricket::MediaConfig TestCreatePeerConnection(
|
||||
const PeerConnectionInterface::RTCConfiguration& config,
|
||||
const MediaConstraintsInterface *constraints) {
|
||||
|
||||
const MediaConstraintsInterface* constraints) {
|
||||
rtc::scoped_refptr<PeerConnectionInterface> pc(pcf_->CreatePeerConnection(
|
||||
config, constraints, nullptr, nullptr, &observer_));
|
||||
EXPECT_TRUE(pc.get());
|
||||
@ -3585,177 +3853,6 @@ TEST_F(PeerConnectionMediaConfigTest,
|
||||
EXPECT_TRUE(media_config.video.suspend_below_min_bitrate);
|
||||
}
|
||||
|
||||
// The following tests verify that session options are created correctly.
|
||||
// TODO(deadbeef): Convert these tests to be more end-to-end. Instead of
|
||||
// "verify options are converted correctly", should be "pass options into
|
||||
// CreateOffer and verify the correct offer is produced."
|
||||
|
||||
TEST(CreateSessionOptionsTest, GetOptionsForOfferWithInvalidAudioOption) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = RTCOfferAnswerOptions::kUndefined - 1;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
|
||||
rtc_options.offer_to_receive_audio =
|
||||
RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
|
||||
EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
}
|
||||
|
||||
TEST(CreateSessionOptionsTest, GetOptionsForOfferWithInvalidVideoOption) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_video = RTCOfferAnswerOptions::kUndefined - 1;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
|
||||
rtc_options.offer_to_receive_video =
|
||||
RTCOfferAnswerOptions::kMaxOfferToReceiveMedia + 1;
|
||||
EXPECT_FALSE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
}
|
||||
|
||||
// Test that a MediaSessionOptions is created for an offer if
|
||||
// OfferToReceiveAudio and OfferToReceiveVideo options are set.
|
||||
TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithAudioVideo) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
rtc_options.offer_to_receive_video = 1;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
EXPECT_TRUE(options.has_audio());
|
||||
EXPECT_TRUE(options.has_video());
|
||||
EXPECT_TRUE(options.bundle_enabled);
|
||||
}
|
||||
|
||||
// Test that a correct MediaSessionOptions is created for an offer if
|
||||
// OfferToReceiveAudio is set.
|
||||
TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithAudio) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
EXPECT_TRUE(options.has_audio());
|
||||
EXPECT_FALSE(options.has_video());
|
||||
EXPECT_TRUE(options.bundle_enabled);
|
||||
}
|
||||
|
||||
// Test that a correct MediaSessionOptions is created for an offer if
|
||||
// the default OfferOptions are used.
|
||||
TEST(CreateSessionOptionsTest, GetDefaultMediaSessionOptionsForOffer) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
options.transport_options["audio"] = cricket::TransportOptions();
|
||||
options.transport_options["video"] = cricket::TransportOptions();
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
EXPECT_TRUE(options.has_audio());
|
||||
EXPECT_FALSE(options.has_video());
|
||||
EXPECT_TRUE(options.bundle_enabled);
|
||||
EXPECT_TRUE(options.vad_enabled);
|
||||
EXPECT_FALSE(options.transport_options["audio"].ice_restart);
|
||||
EXPECT_FALSE(options.transport_options["video"].ice_restart);
|
||||
}
|
||||
|
||||
// Test that a correct MediaSessionOptions is created for an offer if
|
||||
// OfferToReceiveVideo is set.
|
||||
TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithVideo) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 0;
|
||||
rtc_options.offer_to_receive_video = 1;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
EXPECT_FALSE(options.has_audio());
|
||||
EXPECT_TRUE(options.has_video());
|
||||
EXPECT_TRUE(options.bundle_enabled);
|
||||
}
|
||||
|
||||
// Test that a correct MediaSessionOptions is created for an offer if
|
||||
// UseRtpMux is set to false.
|
||||
TEST(CreateSessionOptionsTest,
|
||||
GetMediaSessionOptionsForOfferWithBundleDisabled) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_audio = 1;
|
||||
rtc_options.offer_to_receive_video = 1;
|
||||
rtc_options.use_rtp_mux = false;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
EXPECT_TRUE(options.has_audio());
|
||||
EXPECT_TRUE(options.has_video());
|
||||
EXPECT_FALSE(options.bundle_enabled);
|
||||
}
|
||||
|
||||
// Test that a correct MediaSessionOptions is created to restart ice if
|
||||
// IceRestart is set. It also tests that subsequent MediaSessionOptions don't
|
||||
// have |audio_transport_options.ice_restart| etc. set.
|
||||
TEST(CreateSessionOptionsTest, GetMediaSessionOptionsForOfferWithIceRestart) {
|
||||
RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.ice_restart = true;
|
||||
|
||||
cricket::MediaSessionOptions options;
|
||||
options.transport_options["audio"] = cricket::TransportOptions();
|
||||
options.transport_options["video"] = cricket::TransportOptions();
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
EXPECT_TRUE(options.transport_options["audio"].ice_restart);
|
||||
EXPECT_TRUE(options.transport_options["video"].ice_restart);
|
||||
|
||||
rtc_options = RTCOfferAnswerOptions();
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(rtc_options, true, &options));
|
||||
EXPECT_FALSE(options.transport_options["audio"].ice_restart);
|
||||
EXPECT_FALSE(options.transport_options["video"].ice_restart);
|
||||
}
|
||||
|
||||
// Test that the MediaConstraints in an answer don't affect if audio and video
|
||||
// is offered in an offer but that if kOfferToReceiveAudio or
|
||||
// kOfferToReceiveVideo constraints are true in an offer, the media type will be
|
||||
// included in subsequent answers.
|
||||
TEST(CreateSessionOptionsTest, MediaConstraintsInAnswer) {
|
||||
FakeConstraints answer_c;
|
||||
answer_c.SetMandatoryReceiveAudio(true);
|
||||
answer_c.SetMandatoryReceiveVideo(true);
|
||||
|
||||
cricket::MediaSessionOptions answer_options;
|
||||
EXPECT_TRUE(ParseConstraintsForAnswer(&answer_c, &answer_options));
|
||||
EXPECT_TRUE(answer_options.has_audio());
|
||||
EXPECT_TRUE(answer_options.has_video());
|
||||
|
||||
RTCOfferAnswerOptions rtc_offer_options;
|
||||
|
||||
cricket::MediaSessionOptions offer_options;
|
||||
EXPECT_TRUE(
|
||||
ExtractMediaSessionOptions(rtc_offer_options, false, &offer_options));
|
||||
EXPECT_TRUE(offer_options.has_audio());
|
||||
EXPECT_TRUE(offer_options.has_video());
|
||||
|
||||
RTCOfferAnswerOptions updated_rtc_offer_options;
|
||||
updated_rtc_offer_options.offer_to_receive_audio = 1;
|
||||
updated_rtc_offer_options.offer_to_receive_video = 1;
|
||||
|
||||
cricket::MediaSessionOptions updated_offer_options;
|
||||
EXPECT_TRUE(ExtractMediaSessionOptions(updated_rtc_offer_options, false,
|
||||
&updated_offer_options));
|
||||
EXPECT_TRUE(updated_offer_options.has_audio());
|
||||
EXPECT_TRUE(updated_offer_options.has_video());
|
||||
|
||||
// Since an offer has been created with both audio and video, subsequent
|
||||
// offers and answers should contain both audio and video.
|
||||
// Answers will only contain the media types that exist in the offer
|
||||
// regardless of the value of |updated_answer_options.has_audio| and
|
||||
// |updated_answer_options.has_video|.
|
||||
FakeConstraints updated_answer_c;
|
||||
answer_c.SetMandatoryReceiveAudio(false);
|
||||
answer_c.SetMandatoryReceiveVideo(false);
|
||||
|
||||
cricket::MediaSessionOptions updated_answer_options;
|
||||
EXPECT_TRUE(
|
||||
ParseConstraintsForAnswer(&updated_answer_c, &updated_answer_options));
|
||||
EXPECT_TRUE(updated_answer_options.has_audio());
|
||||
EXPECT_TRUE(updated_answer_options.has_video());
|
||||
}
|
||||
|
||||
// Tests a few random fields being different.
|
||||
TEST(RTCConfigurationTest, ComparisonOperators) {
|
||||
PeerConnectionInterface::RTCConfiguration a;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -30,24 +30,30 @@ static const char kFailedDueToSessionShutdown[] =
|
||||
|
||||
static const uint64_t kInitSessionVersion = 2;
|
||||
|
||||
static bool CompareStream(const MediaSessionOptions::Stream& stream1,
|
||||
const MediaSessionOptions::Stream& stream2) {
|
||||
return stream1.id < stream2.id;
|
||||
static bool CompareSenderOptions(const cricket::SenderOptions& sender1,
|
||||
const cricket::SenderOptions& sender2) {
|
||||
return sender1.track_id < sender2.track_id;
|
||||
}
|
||||
|
||||
static bool SameId(const MediaSessionOptions::Stream& stream1,
|
||||
const MediaSessionOptions::Stream& stream2) {
|
||||
return stream1.id == stream2.id;
|
||||
static bool SameId(const cricket::SenderOptions& sender1,
|
||||
const cricket::SenderOptions& sender2) {
|
||||
return sender1.track_id == sender2.track_id;
|
||||
}
|
||||
|
||||
// Checks if each Stream within the |streams| has unique id.
|
||||
static bool ValidStreams(const MediaSessionOptions::Streams& streams) {
|
||||
MediaSessionOptions::Streams sorted_streams = streams;
|
||||
std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
|
||||
MediaSessionOptions::Streams::iterator it =
|
||||
std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
|
||||
SameId);
|
||||
return it == sorted_streams.end();
|
||||
// Check that each sender has a unique ID.
|
||||
static bool ValidMediaSessionOptions(
|
||||
const cricket::MediaSessionOptions& session_options) {
|
||||
std::vector<cricket::SenderOptions> sorted_senders;
|
||||
for (const cricket::MediaDescriptionOptions& media_description_options :
|
||||
session_options.media_description_options) {
|
||||
sorted_senders.insert(sorted_senders.end(),
|
||||
media_description_options.sender_options.begin(),
|
||||
media_description_options.sender_options.end());
|
||||
}
|
||||
std::sort(sorted_senders.begin(), sorted_senders.end(), CompareSenderOptions);
|
||||
std::vector<cricket::SenderOptions>::iterator it =
|
||||
std::adjacent_find(sorted_senders.begin(), sorted_senders.end(), SameId);
|
||||
return it == sorted_senders.end();
|
||||
}
|
||||
|
||||
enum {
|
||||
@ -128,7 +134,6 @@ WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
|
||||
session_id_(session_id),
|
||||
certificate_request_state_(CERTIFICATE_NOT_NEEDED) {
|
||||
RTC_DCHECK(signaling_thread_);
|
||||
session_desc_factory_.set_add_legacy_streams(false);
|
||||
bool dtls_enabled = cert_generator_ || certificate;
|
||||
// SRTP-SDES is disabled if DTLS is on.
|
||||
SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED);
|
||||
@ -237,8 +242,8 @@ void WebRtcSessionDescriptionFactory::CreateOffer(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidStreams(session_options.streams)) {
|
||||
error += " called with invalid media streams.";
|
||||
if (!ValidMediaSessionOptions(session_options)) {
|
||||
error += " called with invalid session options";
|
||||
LOG(LS_ERROR) << error;
|
||||
PostCreateSessionDescriptionFailed(observer, error);
|
||||
return;
|
||||
@ -279,8 +284,8 @@ void WebRtcSessionDescriptionFactory::CreateAnswer(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidStreams(session_options.streams)) {
|
||||
error += " called with invalid media streams.";
|
||||
if (!ValidMediaSessionOptions(session_options)) {
|
||||
error += " called with invalid session options.";
|
||||
LOG(LS_ERROR) << error;
|
||||
PostCreateSessionDescriptionFailed(observer, error);
|
||||
return;
|
||||
@ -340,13 +345,12 @@ void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
|
||||
void WebRtcSessionDescriptionFactory::InternalCreateOffer(
|
||||
CreateSessionDescriptionRequest request) {
|
||||
if (session_->local_description()) {
|
||||
for (const cricket::TransportInfo& transport :
|
||||
session_->local_description()->description()->transport_infos()) {
|
||||
// If the needs-ice-restart flag is set as described by JSEP, we should
|
||||
// generate an offer with a new ufrag/password to trigger an ICE restart.
|
||||
if (session_->NeedsIceRestart(transport.content_name)) {
|
||||
request.options.transport_options[transport.content_name].ice_restart =
|
||||
true;
|
||||
// If the needs-ice-restart flag is set as described by JSEP, we should
|
||||
// generate an offer with a new ufrag/password to trigger an ICE restart.
|
||||
for (cricket::MediaDescriptionOptions& options :
|
||||
request.options.media_description_options) {
|
||||
if (session_->NeedsIceRestart(options.mid)) {
|
||||
options.transport_options.ice_restart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -375,13 +379,11 @@ void WebRtcSessionDescriptionFactory::InternalCreateOffer(
|
||||
return;
|
||||
}
|
||||
if (session_->local_description()) {
|
||||
for (const cricket::ContentInfo& content :
|
||||
session_->local_description()->description()->contents()) {
|
||||
// Include all local ICE candidates in the SessionDescription unless
|
||||
// an ICE restart was requested.
|
||||
if (!request.options.transport_options[content.name].ice_restart) {
|
||||
for (const cricket::MediaDescriptionOptions& options :
|
||||
request.options.media_description_options) {
|
||||
if (!options.transport_options.ice_restart) {
|
||||
CopyCandidatesFromSessionDescription(session_->local_description(),
|
||||
content.name, offer);
|
||||
options.mid, offer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -391,18 +393,18 @@ void WebRtcSessionDescriptionFactory::InternalCreateOffer(
|
||||
void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
|
||||
CreateSessionDescriptionRequest request) {
|
||||
if (session_->remote_description()) {
|
||||
for (const cricket::ContentInfo& content :
|
||||
session_->remote_description()->description()->contents()) {
|
||||
for (cricket::MediaDescriptionOptions& options :
|
||||
request.options.media_description_options) {
|
||||
// According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
|
||||
// an answer should also contain new ICE ufrag and password if an offer
|
||||
// has been received with new ufrag and password.
|
||||
request.options.transport_options[content.name].ice_restart =
|
||||
session_->IceRestartPending(content.name);
|
||||
options.transport_options.ice_restart =
|
||||
session_->IceRestartPending(options.mid);
|
||||
// We should pass the current SSL role to the transport description
|
||||
// factory, if there is already an existing ongoing session.
|
||||
rtc::SSLRole ssl_role;
|
||||
if (session_->GetSslRole(content.name, &ssl_role)) {
|
||||
request.options.transport_options[content.name].prefer_passive_role =
|
||||
if (session_->GetSslRole(options.mid, &ssl_role)) {
|
||||
options.transport_options.prefer_passive_role =
|
||||
(rtc::SSL_SERVER == ssl_role);
|
||||
}
|
||||
}
|
||||
@ -433,13 +435,13 @@ void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
|
||||
return;
|
||||
}
|
||||
if (session_->local_description()) {
|
||||
for (const cricket::ContentInfo& content :
|
||||
session_->local_description()->description()->contents()) {
|
||||
// Include all local ICE candidates in the SessionDescription unless
|
||||
// the remote peer has requested an ICE restart.
|
||||
if (!request.options.transport_options[content.name].ice_restart) {
|
||||
// Include all local ICE candidates in the SessionDescription unless
|
||||
// the remote peer has requested an ICE restart.
|
||||
for (const cricket::MediaDescriptionOptions& options :
|
||||
request.options.media_description_options) {
|
||||
if (!options.transport_options.ice_restart) {
|
||||
CopyCandidatesFromSessionDescription(session_->local_description(),
|
||||
content.name, answer);
|
||||
options.mid, answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user