/* * Copyright 2017 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree.
*/
RTCError VerifyCodecPreferences( const std::vector<RtpCodecCapability>& unfiltered_codecs, const std::vector<cricket::Codec>& recv_codecs, const FieldTrialsView& field_trials) { // If the intersection between codecs and // RTCRtpReceiver.getCapabilities(kind).codecs only contains RTX, RED, FEC // codecs or Comfort Noise codecs or is an empty set, throw // InvalidModificationError. // This ensures that we always have something to offer, regardless of // transceiver.direction. // TODO(fippo): clean up the filtering killswitch
std::vector<RtpCodecCapability> codecs = unfiltered_codecs; if (!absl::c_any_of(codecs, [&recv_codecs](const RtpCodecCapability& codec) { return codec.IsMediaCodec() &&
absl::c_any_of(recv_codecs,
[&codec](const cricket::Codec& recv_codec) { return recv_codec.MatchesRtpCodec(codec);
});
})) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, "Invalid codec preferences: Missing codec from recv " "codec capabilities.");
}
// Let codecCapabilities RTCRtpReceiver.getCapabilities(kind).codecs. // For each codec in codecs, If // codec is not in codecCapabilities, throw InvalidModificationError. for (constauto& codec_preference : codecs) { bool is_recv_codec = absl::c_any_of(
recv_codecs, [&codec_preference](const cricket::Codec& codec) { return codec.MatchesRtpCodec(codec_preference);
}); if (!is_recv_codec) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
std::string( "Invalid codec preferences: invalid codec with name \"") +
codec_preference.name + "\".");
}
}
// Check we have a real codec (not just rtx, red, fec or CN) if (absl::c_all_of(codecs, [](const RtpCodecCapability& codec) { return !codec.IsMediaCodec();
})) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION, "Invalid codec preferences: codec list must have a non " "RTX, RED or FEC entry.");
}
return RTCError::OK();
}
TaskQueueBase* GetCurrentTaskQueueOrThread() {
TaskQueueBase* current = TaskQueueBase::Current(); if (!current)
current = rtc::ThreadManager::Instance()->CurrentThread(); return current;
}
// Set default header extensions depending on whether simulcast/SVC is used.
RtpParameters parameters = sender->internal()->GetParametersInternal(); bool uses_simulcast = parameters.encodings.size() > 1; bool uses_svc = !parameters.encodings.empty() &&
parameters.encodings[0].scalability_mode.has_value() &&
parameters.encodings[0].scalability_mode !=
ScalabilityModeToString(ScalabilityMode::kL1T1); if (uses_simulcast || uses_svc) { // Enable DD and VLA extensions, can be deactivated by the API. // Skip this if the GFD extension was enabled via field trial // for backward compability reasons. bool uses_gfd =
absl::c_find_if(
header_extensions_to_negotiate_,
[](const RtpHeaderExtensionCapability& ext) { return ext.uri == RtpExtension::kGenericFrameDescriptorUri00 &&
ext.direction != webrtc::RtpTransceiverDirection::kStopped;
}) != header_extensions_to_negotiate_.end(); if (!uses_gfd) { for (RtpHeaderExtensionCapability& ext :
header_extensions_to_negotiate_) { if (ext.uri == RtpExtension::kVideoLayersAllocationUri ||
ext.uri == RtpExtension::kDependencyDescriptorUri) {
ext.direction = RtpTransceiverDirection::kSendRecv;
}
}
}
}
}
RtpTransceiver::~RtpTransceiver() { // TODO(tommi): On Android, when running PeerConnectionClientTest (e.g. // PeerConnectionClientTest#testCameraSwitch), the instance doesn't get // deleted on `thread_`. See if we can fix that. if (!stopped_) {
RTC_DCHECK_RUN_ON(thread_);
StopInternal();
}
RTC_CHECK(!channel_) << "Missing call to ClearChannel?";
}
RTCError RtpTransceiver::CreateChannel(
absl::string_view mid,
Call* call_ptr, const cricket::MediaConfig& media_config, bool srtp_required,
CryptoOptions crypto_options, const cricket::AudioOptions& audio_options, const cricket::VideoOptions& video_options,
VideoBitrateAllocatorFactory* video_bitrate_allocator_factory,
std::function<RtpTransportInternal*(absl::string_view)> transport_lookup) {
RTC_DCHECK_RUN_ON(thread_); if (!media_engine()) { // TODO(hta): Must be a better way return RTCError(RTCErrorType::INTERNAL_ERROR, "No media engine for mid=" + std::string(mid));
}
std::unique_ptr<cricket::ChannelInterface> new_channel; if (media_type() == cricket::MEDIA_TYPE_AUDIO) { // TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to // the worker thread. We shouldn't be using the `call_ptr_` hack here but // simply be on the worker thread and use `call_` (update upstream code).
RTC_DCHECK(call_ptr);
RTC_DCHECK(media_engine()); // TODO(bugs.webrtc.org/11992): Remove this workaround after updates in // PeerConnection and add the expectation that we're already on the right // thread.
context()->worker_thread()->BlockingCall([&] {
RTC_DCHECK_RUN_ON(context()->worker_thread());
std::unique_ptr<cricket::VoiceMediaSendChannelInterface>
media_send_channel = media_engine()->voice().CreateSendChannel(
call_ptr, media_config, audio_options, crypto_options,
codec_pair_id); if (!media_send_channel) { // TODO(bugs.webrtc.org/14912): Consider CHECK or reporting failure return;
}
std::unique_ptr<cricket::VoiceMediaReceiveChannelInterface>
media_receive_channel = media_engine()->voice().CreateReceiveChannel(
call_ptr, media_config, audio_options, crypto_options,
codec_pair_id); if (!media_receive_channel) { return;
} // Note that this is safe because both sending and // receiving channels will be deleted at the same time.
media_send_channel->SetSsrcListChangedCallback(
[receive_channel =
media_receive_channel.get()](const std::set<uint32_t>& choices) {
receive_channel->ChooseReceiverReportSsrc(choices);
});
// TODO(bugs.webrtc.org/11992): CreateVideoChannel internally switches to // the worker thread. We shouldn't be using the `call_ptr_` hack here but // simply be on the worker thread and use `call_` (update upstream code).
context()->worker_thread()->BlockingCall([&] {
RTC_DCHECK_RUN_ON(context()->worker_thread());
std::unique_ptr<cricket::VideoMediaReceiveChannelInterface>
media_receive_channel = media_engine()->video().CreateReceiveChannel(
call_ptr, media_config, video_options, crypto_options); if (!media_receive_channel) { return;
} // Note that this is safe because both sending and // receiving channels will be deleted at the same time.
media_send_channel->SetSsrcListChangedCallback(
[receive_channel =
media_receive_channel.get()](const std::set<uint32_t>& choices) {
receive_channel->ChooseReceiverReportSsrc(choices);
});
new_channel = std::make_unique<cricket::VideoChannel>(
context()->worker_thread(), context()->network_thread(),
context()->signaling_thread(), std::move(media_send_channel),
std::move(media_receive_channel), mid, srtp_required, crypto_options,
context()->ssrc_generator());
});
} if (!new_channel) { // TODO(hta): Must be a better way return RTCError(RTCErrorType::INTERNAL_ERROR, "Failed to create channel for mid=" + std::string(mid));
}
SetChannel(std::move(new_channel), transport_lookup); return RTCError::OK();
}
void RtpTransceiver::SetChannel(
std::unique_ptr<cricket::ChannelInterface> channel,
std::function<RtpTransportInternal*(const std::string&)> transport_lookup) {
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(channel);
RTC_DCHECK(transport_lookup);
RTC_DCHECK(!channel_); // Cannot set a channel on a stopped transceiver. if (stopped_) { return;
}
// An alternative to this, could be to require SetChannel to be called // on the network thread. The channel object operates for the most part // on the network thread, as part of its initialization being on the network // thread is required, so setting a channel object as part of the construction // (without thread hopping) might be the more efficient thing to do than // how SetChannel works today. // Similarly, if the channel() accessor is limited to the network thread, that // helps with keeping the channel implementation requirements being met and // avoids synchronization for accessing the pointer or network related state.
context()->network_thread()->BlockingCall([&]() { if (channel_) {
channel_->SetFirstPacketReceivedCallback(nullptr);
channel_->SetFirstPacketSentCallback(nullptr);
channel_->SetRtpTransport(nullptr);
channel_to_delete = std::move(channel_);
}
void RtpTransceiver::PushNewMediaChannelAndDeleteChannel(
std::unique_ptr<cricket::ChannelInterface> channel_to_delete) { // The clumsy combination of pushing down media channel and deleting // the channel is due to the desire to do both things in one Invoke(). if (!channel_to_delete && senders_.empty() && receivers_.empty()) { return;
}
context()->worker_thread()->BlockingCall([&]() { // Push down the new media_channel, if any, otherwise clear it. auto* media_send_channel =
channel_ ? channel_->media_send_channel() : nullptr; for (constauto& sender : senders_) {
sender->internal()->SetMediaChannel(media_send_channel);
}
// Destroy the channel, if we had one, now _after_ updating the receivers // who might have had references to the previous channel. if (channel_to_delete) {
channel_to_delete.reset(nullptr);
}
});
}
bool RtpTransceiver::RemoveReceiver(RtpReceiverInterface* receiver) {
RTC_DCHECK_RUN_ON(thread_);
RTC_DCHECK(!unified_plan_); if (receiver) {
RTC_DCHECK_EQ(media_type(), receiver->media_type());
} auto it = absl::c_find(receivers_, receiver); if (it == receivers_.end()) { returnfalse;
}
(*it)->internal()->Stop();
context()->worker_thread()->BlockingCall([&]() { // `Stop()` will clear the receiver's pointer to the media channel.
(*it)->internal()->SetMediaChannel(nullptr);
});
RtpTransceiverDirection RtpTransceiver::direction() const { if (unified_plan_ && stopping()) return RtpTransceiverDirection::kStopped;
return direction_;
}
RTCError RtpTransceiver::SetDirectionWithError(
RtpTransceiverDirection new_direction) { if (unified_plan_ && stopping()) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, "Cannot set direction on a stopping transceiver.");
} if (new_direction == direction_) return RTCError::OK();
if (new_direction == RtpTransceiverDirection::kStopped) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER, "The set direction 'stopped' is invalid.");
}
void RtpTransceiver::StopSendingAndReceiving() { // 1. Let sender be transceiver.[[Sender]]. // 2. Let receiver be transceiver.[[Receiver]]. // // 3. Stop sending media with sender. //
RTC_DCHECK_RUN_ON(thread_);
// 4. Send an RTCP BYE for each RTP stream that was being sent by sender, as // specified in [RFC3550]. for (constauto& sender : senders_)
sender->internal()->Stop();
// Signal to receiver sources that we're stopping. for (constauto& receiver : receivers_)
receiver->internal()->Stop();
context()->worker_thread()->BlockingCall([&]() { // 5 Stop receiving media with receiver. for (constauto& receiver : receivers_)
receiver->internal()->SetMediaChannel(nullptr);
});
RTCError RtpTransceiver::StopStandard() {
RTC_DCHECK_RUN_ON(thread_); // If we're on Plan B, do what Stop() used to do there. if (!unified_plan_) {
StopInternal(); return RTCError::OK();
} // 1. Let transceiver be the RTCRtpTransceiver object on which the method is // invoked. // // 2. Let connection be the RTCPeerConnection object associated with // transceiver. // // 3. If connection.[[IsClosed]] is true, throw an InvalidStateError. if (is_pc_closed_) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE, "PeerConnection is closed.");
}
// 4. If transceiver.[[Stopping]] is true, abort these steps. if (stopping_) return RTCError::OK();
// 5. Stop sending and receiving given transceiver, and update the // negotiation-needed flag for connection.
StopSendingAndReceiving();
on_negotiation_needed_();
void RtpTransceiver::StopTransceiverProcedure() {
RTC_DCHECK_RUN_ON(thread_); // As specified in the "Stop the RTCRtpTransceiver" procedure // 1. If transceiver.[[Stopping]] is false, stop sending and receiving given // transceiver. if (!stopping_)
StopSendingAndReceiving();
// 2. Set transceiver.[[Stopped]] to true.
stopped_ = true;
// Signal the updated change to the senders. for (constauto& sender : senders_)
sender->internal()->SetTransceiverAsStopped();
// 3. Set transceiver.[[Receptive]] to false. // 4. Set transceiver.[[CurrentDirection]] to null.
current_direction_ = std::nullopt;
}
RTCError RtpTransceiver::SetCodecPreferences(
rtc::ArrayView<RtpCodecCapability> codec_capabilities) {
RTC_DCHECK(unified_plan_); // 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot // to codecs and abort these steps. if (codec_capabilities.empty()) {
codec_preferences_.clear(); return RTCError::OK();
}
// 4. Remove any duplicate values in codecs.
std::vector<RtpCodecCapability> codecs;
absl::c_remove_copy_if(codec_capabilities, std::back_inserter(codecs),
[&codecs](const RtpCodecCapability& codec) { return absl::c_linear_search(codecs, codec);
});
RTCError RtpTransceiver::SetHeaderExtensionsToNegotiate(
rtc::ArrayView<const RtpHeaderExtensionCapability> header_extensions) { // https://w3c.github.io/webrtc-extensions/#dom-rtcrtptransceiver-setheaderextensionstonegotiate if (header_extensions.size() != header_extensions_to_negotiate_.size()) { return RTCError(RTCErrorType::INVALID_MODIFICATION, "Size of extensions to negotiate does not match.");
} // For each index i of extensions, run the following steps: ... for (size_t i = 0; i < header_extensions.size(); i++) { constauto& extension = header_extensions[i]; if (extension.uri != header_extensions_to_negotiate_[i].uri) { return RTCError(RTCErrorType::INVALID_MODIFICATION, "Reordering extensions is not allowed.");
} if (IsMandatoryHeaderExtension(extension.uri) &&
extension.direction != RtpTransceiverDirection::kSendRecv) { return RTCError(RTCErrorType::INVALID_MODIFICATION, "Attempted to stop a mandatory extension.");
}
// TODO(bugs.webrtc.org/7477): Currently there are no recvonly extensions so // this can not be checked: "When there exists header extension capabilities // that have directions other than kSendRecv, restrict extension.direction // as to not exceed that capability."
}
// Apply mutation after error checking. for (size_t i = 0; i < header_extensions.size(); i++) {
header_extensions_to_negotiate_[i].direction =
header_extensions[i].direction;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.