Plug-in media transport state listener
IceConnected state (transport state) now includes the state of the MediaTransport. This is a first change of two. Second change will add state change signals to the PeerConnectionInterface informing separately about ice+media transport vs ice+dtls. Bug: webrtc:9719 Change-Id: I5731530073e8f26dfc8b188778d268b815da7052 Reviewed-on: https://webrtc-review.googlesource.com/c/108901 Commit-Queue: Peter Slatala <psla@webrtc.org> Reviewed-by: Bjorn Mellem <mellem@webrtc.org> Reviewed-by: Anton Sukhanov <sukhanov@webrtc.org> Reviewed-by: Seth Hampson <shampson@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25473}
This commit is contained in:

committed by
Commit Bot

parent
189013bef9
commit
4eb4112508
@ -57,9 +57,6 @@ class FakeMediaTransport : public MediaTransportInterface {
|
|||||||
void SetTargetTransferRateObserver(
|
void SetTargetTransferRateObserver(
|
||||||
webrtc::TargetTransferRateObserver* observer) override {}
|
webrtc::TargetTransferRateObserver* observer) override {}
|
||||||
|
|
||||||
void SetMediaTransportStateCallback(
|
|
||||||
MediaTransportStateCallback* callback) override {}
|
|
||||||
|
|
||||||
RTCError SendData(int channel_id,
|
RTCError SendData(int channel_id,
|
||||||
const SendDataParams& params,
|
const SendDataParams& params,
|
||||||
const rtc::CopyOnWriteBuffer& buffer) override {
|
const rtc::CopyOnWriteBuffer& buffer) override {
|
||||||
@ -70,8 +67,20 @@ class FakeMediaTransport : public MediaTransportInterface {
|
|||||||
|
|
||||||
void SetDataSink(DataChannelSink* sink) override {}
|
void SetDataSink(DataChannelSink* sink) override {}
|
||||||
|
|
||||||
|
void SetMediaTransportStateCallback(
|
||||||
|
MediaTransportStateCallback* callback) override {
|
||||||
|
state_callback_ = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetState(webrtc::MediaTransportState state) {
|
||||||
|
if (state_callback_) {
|
||||||
|
state_callback_->OnStateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const MediaTransportSettings settings_;
|
const MediaTransportSettings settings_;
|
||||||
|
MediaTransportStateCallback* state_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fake media transport factory creates fake media transport.
|
// Fake media transport factory creates fake media transport.
|
||||||
|
@ -115,9 +115,17 @@ JsepTransport::JsepTransport(
|
|||||||
RTC_DCHECK(!sdes_transport);
|
RTC_DCHECK(!sdes_transport);
|
||||||
dtls_srtp_transport_ = std::move(dtls_srtp_transport);
|
dtls_srtp_transport_ = std::move(dtls_srtp_transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (media_transport_) {
|
||||||
|
media_transport_->SetMediaTransportStateCallback(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsepTransport::~JsepTransport() {}
|
JsepTransport::~JsepTransport() {
|
||||||
|
if (media_transport_) {
|
||||||
|
media_transport_->SetMediaTransportStateCallback(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription(
|
webrtc::RTCError JsepTransport::SetLocalJsepTransportDescription(
|
||||||
const JsepTransportDescription& jsep_description,
|
const JsepTransportDescription& jsep_description,
|
||||||
@ -636,4 +644,12 @@ bool JsepTransport::GetTransportStats(DtlsTransportInternal* dtls_transport,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsepTransport::OnStateChanged(webrtc::MediaTransportState state) {
|
||||||
|
// TODO(bugs.webrtc.org/9719) This method currently fires on the network
|
||||||
|
// thread, but media transport does not make such guarantees. We need to make
|
||||||
|
// sure this callback is guaranteed to be executed on the network thread.
|
||||||
|
media_transport_state_ = state;
|
||||||
|
SignalMediaTransportStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace cricket
|
} // namespace cricket
|
||||||
|
@ -71,7 +71,8 @@ struct JsepTransportDescription {
|
|||||||
//
|
//
|
||||||
// On Threading: JsepTransport performs work solely on the network thread, and
|
// On Threading: JsepTransport performs work solely on the network thread, and
|
||||||
// so its methods should only be called on the network thread.
|
// so its methods should only be called on the network thread.
|
||||||
class JsepTransport : public sigslot::has_slots<> {
|
class JsepTransport : public sigslot::has_slots<>,
|
||||||
|
public webrtc::MediaTransportStateCallback {
|
||||||
public:
|
public:
|
||||||
// |mid| is just used for log statements in order to identify the Transport.
|
// |mid| is just used for log statements in order to identify the Transport.
|
||||||
// Note that |local_certificate| is allowed to be null since a remote
|
// Note that |local_certificate| is allowed to be null since a remote
|
||||||
@ -171,11 +172,19 @@ class JsepTransport : public sigslot::has_slots<> {
|
|||||||
return media_transport_.get();
|
return media_transport_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the latest media transport state.
|
||||||
|
webrtc::MediaTransportState media_transport_state() const {
|
||||||
|
return media_transport_state_;
|
||||||
|
}
|
||||||
|
|
||||||
// This is signaled when RTCP-mux becomes active and
|
// This is signaled when RTCP-mux becomes active and
|
||||||
// |rtcp_dtls_transport_| is destroyed. The JsepTransportController will
|
// |rtcp_dtls_transport_| is destroyed. The JsepTransportController will
|
||||||
// handle the signal and update the aggregate transport states.
|
// handle the signal and update the aggregate transport states.
|
||||||
sigslot::signal<> SignalRtcpMuxActive;
|
sigslot::signal<> SignalRtcpMuxActive;
|
||||||
|
|
||||||
|
// This is signaled for changes in |media_transport_| state.
|
||||||
|
sigslot::signal<> SignalMediaTransportStateChanged;
|
||||||
|
|
||||||
// TODO(deadbeef): The methods below are only public for testing. Should make
|
// TODO(deadbeef): The methods below are only public for testing. Should make
|
||||||
// them utility functions or objects so they can be tested independently from
|
// them utility functions or objects so they can be tested independently from
|
||||||
// this class.
|
// this class.
|
||||||
@ -231,6 +240,9 @@ class JsepTransport : public sigslot::has_slots<> {
|
|||||||
bool GetTransportStats(DtlsTransportInternal* dtls_transport,
|
bool GetTransportStats(DtlsTransportInternal* dtls_transport,
|
||||||
TransportStats* stats);
|
TransportStats* stats);
|
||||||
|
|
||||||
|
// Invoked whenever the state of the media transport changes.
|
||||||
|
void OnStateChanged(webrtc::MediaTransportState state) override;
|
||||||
|
|
||||||
const std::string mid_;
|
const std::string mid_;
|
||||||
// needs-ice-restart bit as described in JSEP.
|
// needs-ice-restart bit as described in JSEP.
|
||||||
bool needs_ice_restart_ = false;
|
bool needs_ice_restart_ = false;
|
||||||
@ -257,6 +269,11 @@ class JsepTransport : public sigslot::has_slots<> {
|
|||||||
// Optional media transport (experimental).
|
// Optional media transport (experimental).
|
||||||
std::unique_ptr<webrtc::MediaTransportInterface> media_transport_;
|
std::unique_ptr<webrtc::MediaTransportInterface> media_transport_;
|
||||||
|
|
||||||
|
// If |media_transport_| is provided, this variable represents the state of
|
||||||
|
// media transport.
|
||||||
|
webrtc::MediaTransportState media_transport_state_ =
|
||||||
|
webrtc::MediaTransportState::kPending;
|
||||||
|
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(JsepTransport);
|
RTC_DISALLOW_COPY_AND_ASSIGN(JsepTransport);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1041,6 +1041,8 @@ RTCError JsepTransportController::MaybeCreateJsepTransport(
|
|||||||
std::move(media_transport));
|
std::move(media_transport));
|
||||||
jsep_transport->SignalRtcpMuxActive.connect(
|
jsep_transport->SignalRtcpMuxActive.connect(
|
||||||
this, &JsepTransportController::UpdateAggregateStates_n);
|
this, &JsepTransportController::UpdateAggregateStates_n);
|
||||||
|
jsep_transport->SignalMediaTransportStateChanged.connect(
|
||||||
|
this, &JsepTransportController::UpdateAggregateStates_n);
|
||||||
SetTransportForMid(content_info.name, jsep_transport.get());
|
SetTransportForMid(content_info.name, jsep_transport.get());
|
||||||
|
|
||||||
jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
|
jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
|
||||||
@ -1234,6 +1236,10 @@ void JsepTransportController::UpdateAggregateStates_n() {
|
|||||||
PeerConnectionInterface::PeerConnectionState::kNew;
|
PeerConnectionInterface::PeerConnectionState::kNew;
|
||||||
cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew;
|
cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew;
|
||||||
bool any_failed = false;
|
bool any_failed = false;
|
||||||
|
|
||||||
|
// TODO(http://bugs.webrtc.org/9719) If(when) media_transport disables
|
||||||
|
// dtls_transports entirely, the below line will have to be changed to account
|
||||||
|
// for the fact that dtls transports might be absent.
|
||||||
bool all_connected = !dtls_transports.empty();
|
bool all_connected = !dtls_transports.empty();
|
||||||
bool all_completed = !dtls_transports.empty();
|
bool all_completed = !dtls_transports.empty();
|
||||||
bool any_gathering = false;
|
bool any_gathering = false;
|
||||||
@ -1262,6 +1268,31 @@ void JsepTransportController::UpdateAggregateStates_n() {
|
|||||||
dtls_state_counts[dtls->dtls_state()]++;
|
dtls_state_counts[dtls->dtls_state()]++;
|
||||||
ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++;
|
ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto it = jsep_transports_by_name_.begin();
|
||||||
|
it != jsep_transports_by_name_.end(); ++it) {
|
||||||
|
auto jsep_transport = it->second.get();
|
||||||
|
if (!jsep_transport->media_transport()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no 'kIceConnectionDisconnected', so we only need to handle
|
||||||
|
// connected and completed.
|
||||||
|
// We treat kClosed as failed, because if it happens before shutting down
|
||||||
|
// media transports it means that there was a failure.
|
||||||
|
// MediaTransportInterface allows to flip back and forth between kWritable
|
||||||
|
// and kPending, but there does not exist an implementation that does that,
|
||||||
|
// and the contract of jsep transport controller doesn't quite expect that.
|
||||||
|
// When this happens, we would go from connected to connecting state, but
|
||||||
|
// this may change in future.
|
||||||
|
any_failed |= jsep_transport->media_transport_state() ==
|
||||||
|
webrtc::MediaTransportState::kClosed;
|
||||||
|
all_completed &= jsep_transport->media_transport_state() ==
|
||||||
|
webrtc::MediaTransportState::kWritable;
|
||||||
|
all_connected &= jsep_transport->media_transport_state() ==
|
||||||
|
webrtc::MediaTransportState::kWritable;
|
||||||
|
}
|
||||||
|
|
||||||
if (any_failed) {
|
if (any_failed) {
|
||||||
new_connection_state = cricket::kIceConnectionFailed;
|
new_connection_state = cricket::kIceConnectionFailed;
|
||||||
} else if (all_completed) {
|
} else if (all_completed) {
|
||||||
|
@ -730,7 +730,8 @@ TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
|
|||||||
EXPECT_EQ(1, combined_connection_state_signal_count_);
|
EXPECT_EQ(1, combined_connection_state_signal_count_);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
|
TEST_F(JsepTransportControllerTest,
|
||||||
|
SignalConnectionStateConnectedNoMediaTransport) {
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
CreateJsepTransportController(JsepTransportController::Config());
|
||||||
auto description = CreateSessionDescriptionWithoutBundle();
|
auto description = CreateSessionDescriptionWithoutBundle();
|
||||||
EXPECT_TRUE(transport_controller_
|
EXPECT_TRUE(transport_controller_
|
||||||
@ -778,6 +779,93 @@ TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
|
|||||||
EXPECT_EQ(2, combined_connection_state_signal_count_);
|
EXPECT_EQ(2, combined_connection_state_signal_count_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(JsepTransportControllerTest,
|
||||||
|
SignalConnectionStateConnectedWithMediaTransport) {
|
||||||
|
FakeMediaTransportFactory fake_media_transport_factory;
|
||||||
|
JsepTransportController::Config config;
|
||||||
|
config.media_transport_factory = &fake_media_transport_factory;
|
||||||
|
CreateJsepTransportController(config);
|
||||||
|
auto description = CreateSessionDescriptionWithoutBundle();
|
||||||
|
AddCryptoSettings(description.get());
|
||||||
|
EXPECT_TRUE(transport_controller_
|
||||||
|
->SetLocalDescription(SdpType::kOffer, description.get())
|
||||||
|
.ok());
|
||||||
|
|
||||||
|
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
|
||||||
|
transport_controller_->GetDtlsTransport(kAudioMid1));
|
||||||
|
auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
|
||||||
|
transport_controller_->GetDtlsTransport(kVideoMid1));
|
||||||
|
fake_audio_dtls->SetWritable(true);
|
||||||
|
fake_video_dtls->SetWritable(true);
|
||||||
|
// Decreasing connection count from 2 to 1 triggers connection state event.
|
||||||
|
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
|
||||||
|
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
|
||||||
|
fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
|
||||||
|
fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
|
||||||
|
fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
|
||||||
|
fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
|
||||||
|
|
||||||
|
// Still not connected, because we are waiting for media transport.
|
||||||
|
EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
|
||||||
|
kTimeout);
|
||||||
|
|
||||||
|
FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
|
||||||
|
transport_controller_->GetMediaTransport(kAudioMid1));
|
||||||
|
|
||||||
|
media_transport->SetState(webrtc::MediaTransportState::kWritable);
|
||||||
|
EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
|
||||||
|
kTimeout);
|
||||||
|
|
||||||
|
// Still waiting for the second media transport.
|
||||||
|
media_transport = static_cast<FakeMediaTransport*>(
|
||||||
|
transport_controller_->GetMediaTransport(kVideoMid1));
|
||||||
|
media_transport->SetState(webrtc::MediaTransportState::kWritable);
|
||||||
|
|
||||||
|
EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(JsepTransportControllerTest,
|
||||||
|
SignalConnectionStateFailedWhenMediaTransportClosed) {
|
||||||
|
FakeMediaTransportFactory fake_media_transport_factory;
|
||||||
|
JsepTransportController::Config config;
|
||||||
|
config.media_transport_factory = &fake_media_transport_factory;
|
||||||
|
CreateJsepTransportController(config);
|
||||||
|
auto description = CreateSessionDescriptionWithoutBundle();
|
||||||
|
AddCryptoSettings(description.get());
|
||||||
|
EXPECT_TRUE(transport_controller_
|
||||||
|
->SetLocalDescription(SdpType::kOffer, description.get())
|
||||||
|
.ok());
|
||||||
|
|
||||||
|
auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
|
||||||
|
transport_controller_->GetDtlsTransport(kAudioMid1));
|
||||||
|
auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
|
||||||
|
transport_controller_->GetDtlsTransport(kVideoMid1));
|
||||||
|
fake_audio_dtls->SetWritable(true);
|
||||||
|
fake_video_dtls->SetWritable(true);
|
||||||
|
// Decreasing connection count from 2 to 1 triggers connection state event.
|
||||||
|
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
|
||||||
|
fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
|
||||||
|
fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
|
||||||
|
fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
|
||||||
|
fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
|
||||||
|
fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
|
||||||
|
|
||||||
|
FakeMediaTransport* media_transport = static_cast<FakeMediaTransport*>(
|
||||||
|
transport_controller_->GetMediaTransport(kAudioMid1));
|
||||||
|
|
||||||
|
media_transport->SetState(webrtc::MediaTransportState::kWritable);
|
||||||
|
|
||||||
|
media_transport = static_cast<FakeMediaTransport*>(
|
||||||
|
transport_controller_->GetMediaTransport(kVideoMid1));
|
||||||
|
|
||||||
|
media_transport->SetState(webrtc::MediaTransportState::kWritable);
|
||||||
|
|
||||||
|
EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
|
||||||
|
|
||||||
|
media_transport->SetState(webrtc::MediaTransportState::kClosed);
|
||||||
|
EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
|
TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
|
||||||
CreateJsepTransportController(JsepTransportController::Config());
|
CreateJsepTransportController(JsepTransportController::Config());
|
||||||
auto description = CreateSessionDescriptionWithoutBundle();
|
auto description = CreateSessionDescriptionWithoutBundle();
|
||||||
|
Reference in New Issue
Block a user