Default streams: don't block media even if on different transceiver.
This fixes some edge cases where early media could cause default stream that block the actual signaled media from beind delivered. Bug: webrtc:11477 Change-Id: I8b26df63a690861bd19f083102d1395e882f8733 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/183120 Commit-Queue: Taylor <deadbeef@webrtc.org> Reviewed-by: Erik Språng <sprang@webrtc.org> Reviewed-by: Rasmus Brandt <brandtr@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32030}
This commit is contained in:
committed by
Commit Bot
parent
0ade98316c
commit
c03a187391
@ -115,6 +115,7 @@ rtc_library("rtc_pc_base") {
|
||||
"../rtc_base:rtc_task_queue",
|
||||
"../rtc_base:stringutils",
|
||||
"../rtc_base/synchronization:mutex",
|
||||
"../rtc_base/synchronization:sequence_checker",
|
||||
"../rtc_base/system:file_wrapper",
|
||||
"../rtc_base/system:rtc_export",
|
||||
"../rtc_base/third_party/base64",
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/network_route.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "rtc_base/synchronization/sequence_checker.h"
|
||||
#include "rtc_base/trace_event.h"
|
||||
|
||||
namespace cricket {
|
||||
@ -206,7 +207,7 @@ void BaseChannel::Init_w(webrtc::RtpTransportInternal* rtp_transport) {
|
||||
}
|
||||
|
||||
void BaseChannel::Deinit() {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
media_channel_->SetInterface(/*iface=*/nullptr);
|
||||
// Packets arrive on the network thread, processing packets calls virtual
|
||||
// functions, so need to stop this process in Deinit that is called in
|
||||
@ -289,6 +290,13 @@ bool BaseChannel::SetRemoteContent(const MediaContentDescription* content,
|
||||
Bind(&BaseChannel::SetRemoteContent_w, this, content, type, error_desc));
|
||||
}
|
||||
|
||||
void BaseChannel::SetPayloadTypeDemuxingEnabled(bool enabled) {
|
||||
TRACE_EVENT0("webrtc", "BaseChannel::SetPayloadTypeDemuxingEnabled");
|
||||
InvokeOnWorker<void>(
|
||||
RTC_FROM_HERE,
|
||||
Bind(&BaseChannel::SetPayloadTypeDemuxingEnabled_w, this, enabled));
|
||||
}
|
||||
|
||||
bool BaseChannel::IsReadyToReceiveMedia_w() const {
|
||||
// Receive data if we are enabled and have local content,
|
||||
return enabled() &&
|
||||
@ -330,7 +338,7 @@ int BaseChannel::SetOption(SocketType type,
|
||||
int BaseChannel::SetOption_n(SocketType type,
|
||||
rtc::Socket::Option opt,
|
||||
int value) {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
RTC_DCHECK(rtp_transport_);
|
||||
switch (type) {
|
||||
case ST_RTP:
|
||||
@ -346,7 +354,7 @@ int BaseChannel::SetOption_n(SocketType type,
|
||||
}
|
||||
|
||||
void BaseChannel::OnWritableState(bool writable) {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (writable) {
|
||||
ChannelWritable_n();
|
||||
} else {
|
||||
@ -358,7 +366,7 @@ void BaseChannel::OnNetworkRouteChanged(
|
||||
absl::optional<rtc::NetworkRoute> network_route) {
|
||||
RTC_LOG(LS_INFO) << "Network route for " << ToString() << " was changed.";
|
||||
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
rtc::NetworkRoute new_route;
|
||||
if (network_route) {
|
||||
new_route = *(network_route);
|
||||
@ -479,7 +487,7 @@ void BaseChannel::OnRtpPacket(const webrtc::RtpPacketReceived& parsed_packet) {
|
||||
|
||||
invoker_.AsyncInvoke<void>(
|
||||
RTC_FROM_HERE, worker_thread_, [this, packet_buffer, packet_time_us] {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
media_channel_->OnPacketReceived(packet_buffer, packet_time_us);
|
||||
});
|
||||
}
|
||||
@ -537,7 +545,7 @@ void BaseChannel::UpdateWritableState_n() {
|
||||
}
|
||||
|
||||
void BaseChannel::ChannelWritable_n() {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (writable_) {
|
||||
return;
|
||||
}
|
||||
@ -551,7 +559,7 @@ void BaseChannel::ChannelWritable_n() {
|
||||
}
|
||||
|
||||
void BaseChannel::ChannelNotWritable_n() {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
if (!writable_)
|
||||
return;
|
||||
|
||||
@ -575,6 +583,24 @@ void BaseChannel::ResetUnsignaledRecvStream_w() {
|
||||
media_channel()->ResetUnsignaledRecvStream();
|
||||
}
|
||||
|
||||
void BaseChannel::SetPayloadTypeDemuxingEnabled_w(bool enabled) {
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
if (enabled == payload_type_demuxing_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (!enabled) {
|
||||
// TODO(crbug.com/11477): This will remove *all* unsignaled streams (those
|
||||
// without an explicitly signaled SSRC), which may include streams that
|
||||
// were matched to this channel by MID or RID. Ideally we'd remove only the
|
||||
// streams that were matched based on payload type alone, but currently
|
||||
// there is no straightforward way to identify those streams.
|
||||
media_channel()->ResetUnsignaledRecvStream();
|
||||
ClearHandledPayloadTypes();
|
||||
RegisterRtpDemuxerSink();
|
||||
}
|
||||
payload_type_demuxing_enabled_ = enabled;
|
||||
}
|
||||
|
||||
bool BaseChannel::UpdateLocalStreams_w(const std::vector<StreamParams>& streams,
|
||||
SdpType type,
|
||||
std::string* error_desc) {
|
||||
@ -741,7 +767,7 @@ void BaseChannel::OnMessage(rtc::Message* pmsg) {
|
||||
switch (pmsg->message_id) {
|
||||
case MSG_SEND_RTP_PACKET:
|
||||
case MSG_SEND_RTCP_PACKET: {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
SendPacketMessageData* data =
|
||||
static_cast<SendPacketMessageData*>(pmsg->pdata);
|
||||
bool rtcp = pmsg->message_id == MSG_SEND_RTCP_PACKET;
|
||||
@ -756,8 +782,10 @@ void BaseChannel::OnMessage(rtc::Message* pmsg) {
|
||||
}
|
||||
}
|
||||
|
||||
void BaseChannel::AddHandledPayloadType(int payload_type) {
|
||||
demuxer_criteria_.payload_types.insert(static_cast<uint8_t>(payload_type));
|
||||
void BaseChannel::MaybeAddHandledPayloadType(int payload_type) {
|
||||
if (payload_type_demuxing_enabled_) {
|
||||
demuxer_criteria_.payload_types.insert(static_cast<uint8_t>(payload_type));
|
||||
}
|
||||
}
|
||||
|
||||
void BaseChannel::ClearHandledPayloadTypes() {
|
||||
@ -767,7 +795,7 @@ void BaseChannel::ClearHandledPayloadTypes() {
|
||||
void BaseChannel::FlushRtcpMessages_n() {
|
||||
// Flush all remaining RTCP messages. This should only be called in
|
||||
// destructor.
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
rtc::MessageList rtcp_messages;
|
||||
network_thread_->Clear(this, MSG_SEND_RTCP_PACKET, &rtcp_messages);
|
||||
for (const auto& message : rtcp_messages) {
|
||||
@ -777,10 +805,10 @@ void BaseChannel::FlushRtcpMessages_n() {
|
||||
}
|
||||
|
||||
void BaseChannel::SignalSentPacket_n(const rtc::SentPacket& sent_packet) {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, worker_thread_,
|
||||
[this, sent_packet] {
|
||||
RTC_DCHECK(worker_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(worker_thread());
|
||||
SignalSentPacket(sent_packet);
|
||||
});
|
||||
}
|
||||
@ -810,7 +838,7 @@ VoiceChannel::~VoiceChannel() {
|
||||
}
|
||||
|
||||
void BaseChannel::UpdateMediaSendRecvState() {
|
||||
RTC_DCHECK(network_thread_->IsCurrent());
|
||||
RTC_DCHECK_RUN_ON(network_thread());
|
||||
invoker_.AsyncInvoke<void>(RTC_FROM_HERE, worker_thread_,
|
||||
[this] { UpdateMediaSendRecvState_w(); });
|
||||
}
|
||||
@ -869,7 +897,7 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content,
|
||||
|
||||
if (webrtc::RtpTransceiverDirectionHasRecv(audio->direction())) {
|
||||
for (const AudioCodec& codec : audio->codecs()) {
|
||||
AddHandledPayloadType(codec.id);
|
||||
MaybeAddHandledPayloadType(codec.id);
|
||||
}
|
||||
// Need to re-register the sink to update the handled payload.
|
||||
if (!RegisterRtpDemuxerSink()) {
|
||||
@ -1062,7 +1090,7 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content,
|
||||
|
||||
if (webrtc::RtpTransceiverDirectionHasRecv(video->direction())) {
|
||||
for (const VideoCodec& codec : video->codecs()) {
|
||||
AddHandledPayloadType(codec.id);
|
||||
MaybeAddHandledPayloadType(codec.id);
|
||||
}
|
||||
// Need to re-register the sink to update the handled payload.
|
||||
if (!RegisterRtpDemuxerSink()) {
|
||||
@ -1287,7 +1315,7 @@ bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content,
|
||||
return false;
|
||||
}
|
||||
for (const DataCodec& codec : data->codecs()) {
|
||||
AddHandledPayloadType(codec.id);
|
||||
MaybeAddHandledPayloadType(codec.id);
|
||||
}
|
||||
// Need to re-register the sink to update the handled payload.
|
||||
if (!RegisterRtpDemuxerSink()) {
|
||||
|
||||
19
pc/channel.h
19
pc/channel.h
@ -39,7 +39,9 @@
|
||||
#include "rtc_base/async_invoker.h"
|
||||
#include "rtc_base/async_udp_socket.h"
|
||||
#include "rtc_base/network.h"
|
||||
#include "rtc_base/synchronization/sequence_checker.h"
|
||||
#include "rtc_base/third_party/sigslot/sigslot.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rtc_base/unique_id_generator.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -124,6 +126,15 @@ class BaseChannel : public ChannelInterface,
|
||||
bool SetRemoteContent(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string* error_desc) override;
|
||||
// Controls whether this channel will receive packets on the basis of
|
||||
// matching payload type alone. This is needed for legacy endpoints that
|
||||
// don't signal SSRCs or use MID/RID, but doesn't make sense if there is
|
||||
// more than channel of specific media type, As that creates an ambiguity.
|
||||
//
|
||||
// This method will also remove any existing streams that were bound to this
|
||||
// channel on the basis of payload type, since one of these streams might
|
||||
// actually belong to a new channel. See: crbug.com/webrtc/11477
|
||||
void SetPayloadTypeDemuxingEnabled(bool enabled) override;
|
||||
|
||||
bool Enable(bool enable) override;
|
||||
|
||||
@ -224,6 +235,7 @@ class BaseChannel : public ChannelInterface,
|
||||
bool AddRecvStream_w(const StreamParams& sp);
|
||||
bool RemoveRecvStream_w(uint32_t ssrc);
|
||||
void ResetUnsignaledRecvStream_w();
|
||||
void SetPayloadTypeDemuxingEnabled_w(bool enabled);
|
||||
bool AddSendStream_w(const StreamParams& sp);
|
||||
bool RemoveSendStream_w(uint32_t ssrc);
|
||||
|
||||
@ -261,9 +273,11 @@ class BaseChannel : public ChannelInterface,
|
||||
return worker_thread_->Invoke<T>(posted_from, functor);
|
||||
}
|
||||
|
||||
void AddHandledPayloadType(int payload_type);
|
||||
// Add |payload_type| to |demuxer_criteria_| if payload type demuxing is
|
||||
// enabled.
|
||||
void MaybeAddHandledPayloadType(int payload_type) RTC_RUN_ON(worker_thread());
|
||||
|
||||
void ClearHandledPayloadTypes();
|
||||
void ClearHandledPayloadTypes() RTC_RUN_ON(worker_thread());
|
||||
|
||||
void UpdateRtpHeaderExtensionMap(
|
||||
const RtpHeaderExtensions& header_extensions);
|
||||
@ -308,6 +322,7 @@ class BaseChannel : public ChannelInterface,
|
||||
// well, but it can be changed only when signaling thread does a synchronous
|
||||
// call to the worker thread, so it should be safe.
|
||||
bool enabled_ = false;
|
||||
bool payload_type_demuxing_enabled_ RTC_GUARDED_BY(worker_thread()) = true;
|
||||
std::vector<StreamParams> local_streams_;
|
||||
std::vector<StreamParams> remote_streams_;
|
||||
webrtc::RtpTransceiverDirection local_content_direction_ =
|
||||
|
||||
@ -52,6 +52,7 @@ class ChannelInterface {
|
||||
virtual bool SetRemoteContent(const MediaContentDescription* content,
|
||||
webrtc::SdpType type,
|
||||
std::string* error_desc) = 0;
|
||||
virtual void SetPayloadTypeDemuxingEnabled(bool enabled) = 0;
|
||||
|
||||
// Access to the local and remote streams that were set on the channel.
|
||||
virtual const std::vector<StreamParams>& local_streams() const = 0;
|
||||
|
||||
@ -96,16 +96,15 @@ class ChannelManager final {
|
||||
// call the appropriate Destroy*Channel method when done.
|
||||
|
||||
// Creates a voice channel, to be associated with the specified session.
|
||||
VoiceChannel* CreateVoiceChannel(
|
||||
webrtc::Call* call,
|
||||
const cricket::MediaConfig& media_config,
|
||||
webrtc::RtpTransportInternal* rtp_transport,
|
||||
rtc::Thread* signaling_thread,
|
||||
const std::string& content_name,
|
||||
bool srtp_required,
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator,
|
||||
const AudioOptions& options);
|
||||
VoiceChannel* CreateVoiceChannel(webrtc::Call* call,
|
||||
const cricket::MediaConfig& media_config,
|
||||
webrtc::RtpTransportInternal* rtp_transport,
|
||||
rtc::Thread* signaling_thread,
|
||||
const std::string& content_name,
|
||||
bool srtp_required,
|
||||
const webrtc::CryptoOptions& crypto_options,
|
||||
rtc::UniqueRandomIdGenerator* ssrc_generator,
|
||||
const AudioOptions& options);
|
||||
// Destroys a voice channel created by CreateVoiceChannel.
|
||||
void DestroyVoiceChannel(VoiceChannel* voice_channel);
|
||||
|
||||
|
||||
@ -5982,6 +5982,87 @@ RTCError PeerConnection::UpdateSessionState(
|
||||
return RTCError::OK();
|
||||
}
|
||||
|
||||
void PeerConnection::UpdatePayloadTypeDemuxingState(
|
||||
cricket::ContentSource source) {
|
||||
// We may need to delete any created default streams and disable creation of
|
||||
// new ones on the basis of payload type. This is needed to avoid SSRC
|
||||
// collisions in Call's RtpDemuxer, in the case that a transceiver has
|
||||
// created a default stream, and then some other channel gets the SSRC
|
||||
// signaled in the corresponding Unified Plan "m=" section. For more context
|
||||
// see https://bugs.chromium.org/p/webrtc/issues/detail?id=11477
|
||||
const SessionDescriptionInterface* sdesc =
|
||||
(source == cricket::CS_LOCAL ? local_description()
|
||||
: remote_description());
|
||||
size_t num_receiving_video_transceivers = 0;
|
||||
size_t num_receiving_audio_transceivers = 0;
|
||||
for (auto& content_info : sdesc->description()->contents()) {
|
||||
if (content_info.rejected ||
|
||||
(source == cricket::ContentSource::CS_LOCAL &&
|
||||
!RtpTransceiverDirectionHasRecv(
|
||||
content_info.media_description()->direction())) ||
|
||||
(source == cricket::ContentSource::CS_REMOTE &&
|
||||
!RtpTransceiverDirectionHasSend(
|
||||
content_info.media_description()->direction()))) {
|
||||
// Ignore transceivers that are not receiving.
|
||||
continue;
|
||||
}
|
||||
switch (content_info.media_description()->type()) {
|
||||
case cricket::MediaType::MEDIA_TYPE_AUDIO:
|
||||
++num_receiving_audio_transceivers;
|
||||
break;
|
||||
case cricket::MediaType::MEDIA_TYPE_VIDEO:
|
||||
++num_receiving_video_transceivers;
|
||||
break;
|
||||
default:
|
||||
// Ignore data channels.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bool pt_demuxing_enabled_video = num_receiving_video_transceivers <= 1;
|
||||
bool pt_demuxing_enabled_audio = num_receiving_audio_transceivers <= 1;
|
||||
|
||||
// Gather all updates ahead of time so that all channels can be updated in a
|
||||
// single Invoke; necessary due to thread guards.
|
||||
std::vector<std::pair<RtpTransceiverDirection, cricket::ChannelInterface*>>
|
||||
channels_to_update;
|
||||
for (const auto& transceiver : transceivers_) {
|
||||
cricket::ChannelInterface* channel = transceiver->internal()->channel();
|
||||
const ContentInfo* content =
|
||||
FindMediaSectionForTransceiver(transceiver, sdesc);
|
||||
if (!channel || !content) {
|
||||
continue;
|
||||
}
|
||||
RtpTransceiverDirection local_direction =
|
||||
content->media_description()->direction();
|
||||
if (source == cricket::CS_REMOTE) {
|
||||
local_direction = RtpTransceiverDirectionReversed(local_direction);
|
||||
}
|
||||
channels_to_update.emplace_back(local_direction,
|
||||
transceiver->internal()->channel());
|
||||
}
|
||||
|
||||
if (!channels_to_update.empty()) {
|
||||
worker_thread()->Invoke<void>(
|
||||
RTC_FROM_HERE, [&channels_to_update, pt_demuxing_enabled_audio,
|
||||
pt_demuxing_enabled_video]() {
|
||||
for (const auto& it : channels_to_update) {
|
||||
RtpTransceiverDirection local_direction = it.first;
|
||||
cricket::ChannelInterface* channel = it.second;
|
||||
cricket::MediaType media_type = channel->media_type();
|
||||
if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO) {
|
||||
channel->SetPayloadTypeDemuxingEnabled(
|
||||
pt_demuxing_enabled_audio &&
|
||||
RtpTransceiverDirectionHasRecv(local_direction));
|
||||
} else if (media_type == cricket::MediaType::MEDIA_TYPE_VIDEO) {
|
||||
channel->SetPayloadTypeDemuxingEnabled(
|
||||
pt_demuxing_enabled_video &&
|
||||
RtpTransceiverDirectionHasRecv(local_direction));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
RTCError PeerConnection::PushdownMediaDescription(
|
||||
SdpType type,
|
||||
cricket::ContentSource source) {
|
||||
@ -5990,6 +6071,8 @@ RTCError PeerConnection::PushdownMediaDescription(
|
||||
: remote_description());
|
||||
RTC_DCHECK(sdesc);
|
||||
|
||||
UpdatePayloadTypeDemuxingState(source);
|
||||
|
||||
// Push down the new SDP media section for each audio/video transceiver.
|
||||
for (const auto& transceiver : transceivers_) {
|
||||
const ContentInfo* content_info =
|
||||
|
||||
@ -936,6 +936,10 @@ class PeerConnection : public PeerConnectionInternal,
|
||||
RTCError UpdateSessionState(SdpType type,
|
||||
cricket::ContentSource source,
|
||||
const cricket::SessionDescription* description);
|
||||
// Based on number of transceivers per media type, enabled or disable
|
||||
// payload type based demuxing in the affected channels.
|
||||
void UpdatePayloadTypeDemuxingState(cricket::ContentSource source)
|
||||
RTC_RUN_ON(signaling_thread());
|
||||
// Push the media parts of the local or remote session description
|
||||
// down to all of the channels.
|
||||
RTCError PushdownMediaDescription(SdpType type, cricket::ContentSource source)
|
||||
|
||||
@ -46,6 +46,7 @@ class MockChannelInterface : public cricket::ChannelInterface {
|
||||
webrtc::SdpType,
|
||||
std::string*),
|
||||
(override));
|
||||
MOCK_METHOD(void, SetPayloadTypeDemuxingEnabled, (bool), (override));
|
||||
MOCK_METHOD(const std::vector<StreamParams>&,
|
||||
local_streams,
|
||||
(),
|
||||
|
||||
Reference in New Issue
Block a user