Bug 1265827: Implement RTCPeerConnectionState. r=jib,webidl,smaug

Differential Revision: https://phabricator.services.mozilla.com/D168143
This commit is contained in:
Byron Campen 2023-03-14 13:27:10 +00:00
Родитель c61882a351
Коммит b01ce6d90d
9 изменённых файлов: 231 добавлений и 46 удалений

Просмотреть файл

@ -535,6 +535,7 @@ class RTCPeerConnection {
this.makeGetterSetterEH("ondatachannel");
this.makeGetterSetterEH("oniceconnectionstatechange");
this.makeGetterSetterEH("onicegatheringstatechange");
this.makeGetterSetterEH("onconnectionstatechange");
this.makeGetterSetterEH("onidentityresult");
this.makeGetterSetterEH("onpeeridentity");
this.makeGetterSetterEH("onidpassertionerror");
@ -1596,6 +1597,9 @@ class RTCPeerConnection {
get iceConnectionState() {
return this._iceConnectionState;
}
get connectionState() {
return this._pc.connectionState;
}
get signalingState() {
// checking for our local pc closed indication
@ -1913,6 +1917,11 @@ class PeerConnectionObserver {
this._dompc.handleIceGatheringStateChange();
break;
case "ConnectionState":
_globalPCList.notifyLifecycleObservers(this, "connectionstatechange");
this.dispatchEvent(new this._win.Event("connectionstatechange"));
break;
default:
this._dompc.logWarning("Unhandled state type: " + state);
break;

Просмотреть файл

@ -80,6 +80,7 @@
#include "js/RootingAPI.h" // JS::{{,Mutable}Handle,Rooted}
#include "mozilla/PeerIdentity.h"
#include "mozilla/dom/RTCCertificate.h"
#include "mozilla/dom/RTCDtlsTransportBinding.h" // RTCDtlsTransportState
#include "mozilla/dom/RTCRtpReceiverBinding.h"
#include "mozilla/dom/RTCRtpSenderBinding.h"
#include "mozilla/dom/RTCStatsReportBinding.h"
@ -329,6 +330,7 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
mSignalingState(RTCSignalingState::Stable),
mIceConnectionState(RTCIceConnectionState::New),
mIceGatheringState(RTCIceGatheringState::New),
mConnectionState(RTCPeerConnectionState::New),
mWindow(do_QueryInterface(aGlobal ? aGlobal->GetAsSupports() : nullptr)),
mCertificate(nullptr),
mSTSThread(nullptr),
@ -1973,6 +1975,136 @@ nsresult PeerConnectionImpl::OnAlpnNegotiated(bool aPrivacyRequested) {
return NS_OK;
}
void PeerConnectionImpl::OnDtlsStateChange(const std::string& aTransportId,
TransportLayer::State aState) {
auto it = mTransportIdToRTCDtlsTransport.find(aTransportId);
if (it != mTransportIdToRTCDtlsTransport.end()) {
it->second->UpdateState(aState);
}
UpdateConnectionState();
}
RTCPeerConnectionState PeerConnectionImpl::GetNewConnectionState() const {
// closed The RTCPeerConnection object's [[IsClosed]] slot is true.
if (IsClosed()) {
return RTCPeerConnectionState::Closed;
}
// Would use a bitset, but that requires lots of static_cast<size_t>
// Oh well.
std::set<RTCDtlsTransportState> statesFound;
for (const auto& [id, dtlsTransport] : mTransportIdToRTCDtlsTransport) {
Unused << id;
statesFound.insert(dtlsTransport->State());
}
// failed The previous state doesn't apply and any RTCIceTransports are
// in the "failed" state or any RTCDtlsTransports are in the "failed" state.
// NOTE: "any RTCIceTransports are in the failed state" is equivalent to
// mIceConnectionState == Failed
if (mIceConnectionState == RTCIceConnectionState::Failed ||
statesFound.count(RTCDtlsTransportState::Failed)) {
return RTCPeerConnectionState::Failed;
}
// disconnected None of the previous states apply and any
// RTCIceTransports are in the "disconnected" state.
// NOTE: "any RTCIceTransports are in the disconnected state" is equivalent to
// mIceConnectionState == Disconnected.
if (mIceConnectionState == RTCIceConnectionState::Disconnected) {
return RTCPeerConnectionState::Disconnected;
}
// new None of the previous states apply and all RTCIceTransports are
// in the "new" or "closed" state, and all RTCDtlsTransports are in the "new"
// or "closed" state, or there are no transports.
// NOTE: "all RTCIceTransports are in the new or closed state" is equivalent
// to mIceConnectionState == New.
if (mIceConnectionState == RTCIceConnectionState::New &&
!statesFound.count(RTCDtlsTransportState::Connecting) &&
!statesFound.count(RTCDtlsTransportState::Connected) &&
!statesFound.count(RTCDtlsTransportState::Failed)) {
return RTCPeerConnectionState::New;
}
// No transports
if (statesFound.empty()) {
return RTCPeerConnectionState::New;
}
// connecting None of the previous states apply and any
// RTCIceTransport is in the "new" or "checking" state or any
// RTCDtlsTransport is in the "new" or "connecting" state.
// NOTE: "None of the previous states apply and any RTCIceTransport is in the
// "new" or "checking" state" is equivalent to mIceConnectionState ==
// Checking.
if (mIceConnectionState == RTCIceConnectionState::Checking ||
statesFound.count(RTCDtlsTransportState::New) ||
statesFound.count(RTCDtlsTransportState::Connecting)) {
return RTCPeerConnectionState::Connecting;
}
// connected None of the previous states apply and all RTCIceTransports are
// in the "connected", "completed" or "closed" state, and all
// RTCDtlsTransports are in the "connected" or "closed" state.
// NOTE: "None of the previous states apply and all RTCIceTransports are in
// the "connected", "completed" or "closed" state" is equivalent to
// mIceConnectionState == Connected.
if (mIceConnectionState == RTCIceConnectionState::Connected &&
!statesFound.count(RTCDtlsTransportState::New) &&
!statesFound.count(RTCDtlsTransportState::Failed) &&
!statesFound.count(RTCDtlsTransportState::Connecting)) {
return RTCPeerConnectionState::Connected;
}
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// THERE IS NO CATCH-ALL NONE-OF-THE-ABOVE IN THE SPEC! THIS IS REALLY BAD! !!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Let's try to figure out how bad, precisely.
// Any one of these will cause us to bail above.
MOZ_ASSERT(mIceConnectionState != RTCIceConnectionState::Failed &&
mIceConnectionState != RTCIceConnectionState::Disconnected &&
mIceConnectionState != RTCIceConnectionState::Checking);
MOZ_ASSERT(!statesFound.count(RTCDtlsTransportState::New) &&
!statesFound.count(RTCDtlsTransportState::Connecting) &&
!statesFound.count(RTCDtlsTransportState::Failed));
// One of these must be set, or the empty() check would have failed above.
MOZ_ASSERT(statesFound.count(RTCDtlsTransportState::Connected) ||
statesFound.count(RTCDtlsTransportState::Closed));
// Here are our remaining possibilities:
// ICE connected, !statesFound.count(Connected), statesFound.count(Closed)
// ICE connected, statesFound.count(Connected), !statesFound.count(Closed)
// ICE connected, statesFound.count(Connected), statesFound.count(Closed)
// All three of these would result in returning Connected above.
// ICE new, !statesFound.count(Connected), statesFound.count(Closed)
// This results in returning New above. Whew.
// ICE new, statesFound.count(Connected), !statesFound.count(Closed)
// ICE new, statesFound.count(Connected), statesFound.count(Closed)
// These would make it all the way here! Very weird state though, for all
// ICE transports to be new/closed, but having a connected DTLS transport.
// Handle this as a non-transition, just in case.
return mConnectionState;
}
void PeerConnectionImpl::UpdateConnectionState() {
auto newState = GetNewConnectionState();
if (newState != mConnectionState) {
CSFLogDebug(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__,
static_cast<int>(mConnectionState), static_cast<int>(newState),
this);
mConnectionState = newState;
if (mConnectionState != RTCPeerConnectionState::Closed) {
JSErrorResult jrv;
mPCObserver->OnStateChange(PCObserverStateType::ConnectionState, jrv);
}
}
}
void PeerConnectionImpl::OnMediaError(const std::string& aError) {
CSFLogError(LOGTAG, "Encountered media error! %s", aError.c_str());
// TODO: Let content know about this somehow.
@ -2191,6 +2323,15 @@ PeerConnectionImpl::IceGatheringState(RTCIceGatheringState* aState) {
return NS_OK;
}
NS_IMETHODIMP
PeerConnectionImpl::ConnectionState(RTCPeerConnectionState* aState) {
PC_AUTO_ENTER_API_CALL_NO_CHECK();
MOZ_ASSERT(aState);
*aState = mConnectionState;
return NS_OK;
}
nsresult PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const {
PC_AUTO_ENTER_API_CALL_NO_CHECK();
MOZ_ASSERT(mTrickle || !assert_ice_ready ||
@ -2289,6 +2430,7 @@ PeerConnectionImpl::Close() {
}
mSignalingState = RTCSignalingState::Closed;
mConnectionState = RTCPeerConnectionState::Closed;
if (!mTransportHandler) {
// We were never initialized, apparently.
@ -2752,6 +2894,10 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing(
UpdateNegotiationNeeded();
}
// Spec does not actually tell us to do this, but that is probably a
// spec bug.
UpdateConnectionState();
JSErrorResult jrv;
if (newSignalingState != mSignalingState) {
mSignalingState = newSignalingState;
@ -2963,7 +3109,9 @@ void PeerConnectionImpl::IceConnectionStateChange(
dom::RTCIceConnectionState domState) {
PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
CSFLogDebug(LOGTAG, "%s: %d", __FUNCTION__, static_cast<int>(domState));
CSFLogDebug(LOGTAG, "%s: %d -> %d", __FUNCTION__,
static_cast<int>(mIceConnectionState),
static_cast<int>(domState));
if (domState == mIceConnectionState) {
// no work to be done since the states are the same.
@ -3006,6 +3154,7 @@ void PeerConnectionImpl::IceConnectionStateChange(
WrappableJSErrorResult rv;
mPCObserver->OnStateChange(PCObserverStateType::IceConnectionState, rv);
UpdateConnectionState();
}
void PeerConnectionImpl::OnCandidateFound(const std::string& aTransportId,
@ -3588,19 +3737,29 @@ void PeerConnectionImpl::EnsureTransports(const JsepSession& aSession) {
}
void PeerConnectionImpl::UpdateRTCDtlsTransports(bool aMarkAsStable) {
for (auto& transceiver : mTransceivers) {
std::string transportId = transceiver->GetTransportId();
for (const auto& jsepTransceiver : mJsepSession->GetTransceivers()) {
std::string transportId = jsepTransceiver->mTransport.mTransportId;
if (transportId.empty()) {
continue;
}
if (!mTransportIdToRTCDtlsTransport.count(transportId)) {
mTransportIdToRTCDtlsTransport.emplace(
transportId, new RTCDtlsTransport(transceiver->GetParentObject()));
transportId, new RTCDtlsTransport(GetParentObject()));
}
transceiver->SetDtlsTransport(mTransportIdToRTCDtlsTransport[transportId],
aMarkAsStable);
}
for (auto& transceiver : mTransceivers) {
std::string transportId = transceiver->GetTransportId();
if (transportId.empty()) {
continue;
}
if (mTransportIdToRTCDtlsTransport.count(transportId)) {
transceiver->SetDtlsTransport(mTransportIdToRTCDtlsTransport[transportId],
aMarkAsStable);
}
}
// TODO (bug 1278299): DataChannel transport
}
void PeerConnectionImpl::RollbackRTCDtlsTransports() {
@ -3640,6 +3799,8 @@ nsresult PeerConnectionImpl::UpdateTransports(const JsepSession& aSession,
transceiverImpl->UpdateTransport();
}
// TODO(bug 1278299): DataChannel transport object
return NS_OK;
}
@ -3807,6 +3968,10 @@ void PeerConnectionImpl::SignalHandler::ConnectSignals() {
this, &PeerConnectionImpl::SignalHandler::OnCandidateFound_s);
mSource->SignalAlpnNegotiated.connect(
this, &PeerConnectionImpl::SignalHandler::AlpnNegotiated_s);
mSource->SignalStateChange.connect(
this, &PeerConnectionImpl::SignalHandler::ConnectionStateChange_s);
mSource->SignalRtcpStateChange.connect(
this, &PeerConnectionImpl::SignalHandler::ConnectionStateChange_s);
}
void PeerConnectionImpl::AddIceCandidate(const std::string& aCandidate,
@ -4104,6 +4269,20 @@ void PeerConnectionImpl::SignalHandler::AlpnNegotiated_s(
NS_DISPATCH_NORMAL);
}
void PeerConnectionImpl::SignalHandler::ConnectionStateChange_s(
const std::string& aTransportId, TransportLayer::State aState) {
GetMainThreadSerialEventTarget()->Dispatch(
NS_NewRunnableFunction(__func__,
[handle = mHandle, aTransportId, aState] {
PeerConnectionWrapper wrapper(handle);
if (wrapper.impl()) {
wrapper.impl()->OnDtlsStateChange(aTransportId,
aState);
}
}),
NS_DISPATCH_NORMAL);
}
/**
* Tells you if any local track is isolated to a specific peer identity.
* Obviously, we want all the tracks to be isolated equally so that they can

Просмотреть файл

@ -368,6 +368,14 @@ class PeerConnectionImpl final
return mIceGatheringState;
}
NS_IMETHODIMP ConnectionState(mozilla::dom::RTCPeerConnectionState* aState);
mozilla::dom::RTCPeerConnectionState ConnectionState() {
mozilla::dom::RTCPeerConnectionState state;
ConnectionState(&state);
return state;
}
NS_IMETHODIMP Close();
void Close(ErrorResult& rv) { rv = Close(); }
@ -469,6 +477,11 @@ class PeerConnectionImpl final
// called when DTLS connects; we only need this once
nsresult OnAlpnNegotiated(bool aPrivacyRequested);
void OnDtlsStateChange(const std::string& aTransportId,
TransportLayer::State aState);
void UpdateConnectionState();
dom::RTCPeerConnectionState GetNewConnectionState() const;
// initialize telemetry for when calls start
void StartCallTelem();
@ -599,6 +612,8 @@ class PeerConnectionImpl final
mozilla::dom::RTCIceConnectionState mIceConnectionState;
mozilla::dom::RTCIceGatheringState mIceGatheringState;
mozilla::dom::RTCPeerConnectionState mConnectionState;
RefPtr<PeerConnectionObserver> mPCObserver;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
@ -869,6 +884,8 @@ class PeerConnectionImpl final
void OnCandidateFound_s(const std::string& aTransportId,
const CandidateInfo& aCandidateInfo);
void AlpnNegotiated_s(const std::string& aAlpn, bool aPrivacyRequested);
void ConnectionStateChange_s(const std::string& aTransportId,
TransportLayer::State aState);
private:
const std::string mHandle;

Просмотреть файл

@ -226,17 +226,6 @@ void RTCRtpTransceiver::Init(const RTCRtpTransceiverInit& aInit,
InitConduitControl();
}
auto self = nsMainThreadPtrHandle<RTCRtpTransceiver>(
new nsMainThreadPtrHolder<RTCRtpTransceiver>(
"RTCRtpTransceiver::RTCRtpTransceiver::self", this, false));
mStsThread->Dispatch(
NS_NewRunnableFunction("RTCRtpTransceiver::RTCRtpTransceiver", [self] {
self->mTransportHandler->SignalStateChange.connect(
self.get(), &RTCRtpTransceiver::UpdateDtlsTransportState);
self->mTransportHandler->SignalRtcpStateChange.connect(
self.get(), &RTCRtpTransceiver::UpdateDtlsTransportState);
}));
mSender->SetStreams(aInit.mStreams);
mDirection = aInit.mDirection;
}
@ -253,23 +242,6 @@ void RTCRtpTransceiver::RollbackToStableDtlsTransport() {
mDtlsTransport = mLastStableDtlsTransport;
}
void RTCRtpTransceiver::UpdateDtlsTransportState(
const std::string& aTransportId, TransportLayer::State aState) {
if (!GetMainThreadSerialEventTarget()->IsOnCurrentThread()) {
GetMainThreadSerialEventTarget()->Dispatch(
WrapRunnable(this, &RTCRtpTransceiver::UpdateDtlsTransportState,
aTransportId, aState),
NS_DISPATCH_NORMAL);
return;
}
if (!mDtlsTransport) {
return;
}
mDtlsTransport->UpdateState(aState);
}
void RTCRtpTransceiver::InitAudio() {
mConduit = AudioSessionConduit::Create(mCallWrapper, mStsThread);
@ -888,10 +860,8 @@ void RTCRtpTransceiver::StopImpl() {
auto self = nsMainThreadPtrHandle<RTCRtpTransceiver>(
new nsMainThreadPtrHolder<RTCRtpTransceiver>(
"RTCRtpTransceiver::StopImpl::self", this, false));
mStsThread->Dispatch(NS_NewRunnableFunction(__func__, [self] {
self->disconnect_all();
self->mTransportHandler = nullptr;
}));
mStsThread->Dispatch(NS_NewRunnableFunction(
__func__, [self] { self->mTransportHandler = nullptr; }));
}
bool RTCRtpTransceiver::IsVideo() const { return mIsVideo; }

Просмотреть файл

@ -51,9 +51,7 @@ class RTCRtpSender;
* Audio/VideoConduit for feeding RTP/RTCP into webrtc.org for decoding, and
* feeding audio/video frames into webrtc.org for encoding into RTP/RTCP.
*/
class RTCRtpTransceiver : public nsISupports,
public nsWrapperCache,
public sigslot::has_slots<> {
class RTCRtpTransceiver : public nsISupports, public nsWrapperCache {
public:
/**
* |aSendTrack| might or might not be set.
@ -112,8 +110,6 @@ class RTCRtpTransceiver : public nsISupports,
void SetJsepSession(JsepSession* aJsepSession);
std::string GetMidAscii() const;
void UpdateDtlsTransportState(const std::string& aTransportId,
TransportLayer::State aState);
void SetDtlsTransport(RTCDtlsTransport* aDtlsTransport, bool aStable);
void RollbackToStableDtlsTransport();

Просмотреть файл

@ -108,6 +108,7 @@ interface PeerConnectionImpl {
readonly attribute RTCIceConnectionState iceConnectionState;
readonly attribute RTCIceGatheringState iceGatheringState;
readonly attribute RTCPeerConnectionState connectionState;
readonly attribute RTCSignalingState signalingState;
attribute DOMString id;

Просмотреть файл

@ -10,7 +10,8 @@ enum PCObserverStateType {
"None",
"IceConnectionState",
"IceGatheringState",
"SignalingState"
"SignalingState",
"ConnectionState",
};
enum PCError {

Просмотреть файл

@ -36,6 +36,15 @@ enum RTCIceConnectionState {
"closed"
};
enum RTCPeerConnectionState {
"closed",
"failed",
"disconnected",
"new",
"connecting",
"connected"
};
enum mozPacketDumpType {
"rtp", // dump unencrypted rtp as the MediaPipeline sees it
"srtp", // dump encrypted rtp as the MediaPipeline sees it
@ -107,6 +116,7 @@ interface RTCPeerConnection : EventTarget {
readonly attribute boolean? canTrickleIceCandidates;
readonly attribute RTCIceGatheringState iceGatheringState;
readonly attribute RTCIceConnectionState iceConnectionState;
readonly attribute RTCPeerConnectionState connectionState;
undefined restartIce ();
[Pref="media.peerconnection.identity.enabled"]
readonly attribute Promise<RTCIdentityAssertion> peerIdentity;
@ -160,6 +170,7 @@ interface RTCPeerConnection : EventTarget {
attribute EventHandler ontrack; // replaces onaddtrack and onaddstream.
attribute EventHandler oniceconnectionstatechange;
attribute EventHandler onicegatheringstatechange;
attribute EventHandler onconnectionstatechange;
Promise<RTCStatsReport> getStats (optional MediaStreamTrack? selector = null);

Просмотреть файл

@ -16,7 +16,8 @@
enum RTCLifecycleEvent {
"initialized",
"icegatheringstatechange",
"iceconnectionstatechange"
"iceconnectionstatechange",
"connectionstatechange",
};
callback PeerConnectionLifecycleCallback = undefined (RTCPeerConnection pc,