Skip to content

Commit

Permalink
Expose ICE gathering state and state change event (#276)
Browse files Browse the repository at this point in the history
Fixes #253

- Add `transport.iceGatheringState` getter.
- Add `transport.on('iceGatheringStateChangeEventNumTimesCalled', (iceGatheringState) => { })` event,
- Bonus track: Reset `transport.connectionState` to "closed" within `transport.close()` method.
  • Loading branch information
ibc committed Jul 17, 2023
1 parent 6d3e918 commit 99714af
Show file tree
Hide file tree
Showing 14 changed files with 138 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/Transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ export type DtlsFingerprint =

export type DtlsRole = 'auto' | 'client' | 'server';

export type IceGatheringState =
| 'new'
| 'gathering'
| 'complete';

export type ConnectionState =
| 'new'
| 'connecting'
Expand All @@ -130,6 +135,7 @@ export type PlainRtpParameters =
export type TransportEvents =
{
connect: [{ dtlsParameters: DtlsParameters }, () => void, (error: Error) => void];
icegatheringstatechange: [IceGatheringState];
connectionstatechange: [ConnectionState];
produce:
[
Expand Down Expand Up @@ -199,6 +205,8 @@ export class Transport<TransportAppData extends AppData = AppData>
private readonly _maxSctpMessageSize?: number | null;
// RTC handler isntance.
private readonly _handler: HandlerInterface;
// Transport ICE gathering state.
private _iceGatheringState: IceGatheringState = 'new';
// Transport connection state.
private _connectionState: ConnectionState = 'new';
// App custom data.
Expand Down Expand Up @@ -332,6 +340,14 @@ export class Transport<TransportAppData extends AppData = AppData>
return this._handler;
}

/**
* ICE gathering state.
*/
get iceGatheringState(): IceGatheringState
{
return this._iceGatheringState;
}

/**
* Connection state.
*/
Expand Down Expand Up @@ -381,6 +397,10 @@ export class Transport<TransportAppData extends AppData = AppData>
// Close the handler.
this._handler.close();

// Change connection state to 'closed' since the handler may not emit
// '@connectionstatechange' event.
this._connectionState = 'closed';

// Close all Producers.
for (const producer of this._producers.values())
{
Expand Down Expand Up @@ -1217,6 +1237,23 @@ export class Transport<TransportAppData extends AppData = AppData>
this.safeEmit('connect', { dtlsParameters }, callback, errback);
});

handler.on('@icegatheringstatechange', (iceGatheringState: IceGatheringState) =>
{
if (iceGatheringState === this._iceGatheringState)
{
return;
}

logger.debug('ICE gathering state changed to %s', iceGatheringState);

this._iceGatheringState = iceGatheringState;

if (!this._closed)
{
this.safeEmit('icegatheringstatechange', iceGatheringState);
}
});

handler.on('@connectionstatechange', (connectionState: ConnectionState) =>
{
if (connectionState === this._connectionState)
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Chrome111.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ export class Chrome111 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Chrome55.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ export class Chrome55 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Chrome67.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ export class Chrome67 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Chrome70.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ export class Chrome70 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Chrome74.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export class Chrome74 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
7 changes: 7 additions & 0 deletions src/handlers/FakeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
IceParameters,
DtlsParameters,
DtlsRole,
IceGatheringState,
ConnectionState
} from '../Transport';
import { RtpCapabilities, RtpParameters } from '../RtpParameters';
Expand Down Expand Up @@ -138,6 +139,12 @@ export class FakeHandler extends HandlerInterface
this._closed = true;
}

// NOTE: Custom method for simulation purposes.
setIceGatheringState(iceGatheringState: IceGatheringState): void
{
this.emit('@icegatheringstatechange', iceGatheringState);
}

// NOTE: Custom method for simulation purposes.
setConnectionState(connectionState: ConnectionState): void
{
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Firefox60.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ export class Firefox60 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
2 changes: 2 additions & 0 deletions src/handlers/HandlerInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
IceParameters,
IceCandidate,
DtlsParameters,
IceGatheringState,
ConnectionState
} from '../Transport';
import {
Expand Down Expand Up @@ -99,6 +100,7 @@ export type HandlerEvents =
() => void,
(error: Error) => void
];
'@icegatheringstatechange': [IceGatheringState];
'@connectionstatechange': [ConnectionState];
};

Expand Down
5 changes: 5 additions & 0 deletions src/handlers/ReactNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export class ReactNative extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/ReactNativeUnifiedPlan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ export class ReactNativeUnifiedPlan extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Safari11.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ export class Safari11 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
5 changes: 5 additions & 0 deletions src/handlers/Safari12.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ export class Safari12 extends HandlerInterface
},
proprietaryConstraints);

this._pc.addEventListener('icegatheringstatechange', () =>
{
this.emit('@icegatheringstatechange', this._pc.iceGatheringState);
});

if (this._pc.connectionState)
{
this._pc.addEventListener('connectionstatechange', () =>
Expand Down
42 changes: 42 additions & 0 deletions src/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,48 @@ test('transport.updateIceServers() without iceServers rejects with TypeError', a
.toThrow(TypeError);
}, 500);

test('ICE gathering state change fires "icegatheringstatechange" in live Transport', () =>
{
// NOTE: These tests are a bit flaky and we should isolate them. FakeHandler
// emits '@connectionstatechange' with value 'connecting' as soon as its
// private setupTransport() method is called (which has happens many times in
// tests above already). So here we have to reset it manually to test things.

// @ts-ignore
sendTransport.handler.setIceGatheringState('new');
// @ts-ignore
sendTransport.handler.setConnectionState('new');

let iceGatheringStateChangeEventNumTimesCalled = 0;
let connectionStateChangeEventNumTimesCalled = 0;

sendTransport.on('icegatheringstatechange', (iceGatheringState) =>
{
iceGatheringStateChangeEventNumTimesCalled++;

expect(iceGatheringState).toBe('complete');
expect(sendTransport.iceGatheringState).toBe('complete');
expect(sendTransport.connectionState).toBe('new');
});

// eslint-disable-next-line @typescript-eslint/no-unused-vars
sendTransport.on('connectionstatechange', (connectionState) =>
{
connectionStateChangeEventNumTimesCalled++;
});

// @ts-ignore
sendTransport.handler.setIceGatheringState('complete');

expect(iceGatheringStateChangeEventNumTimesCalled).toBe(1);
expect(connectionStateChangeEventNumTimesCalled).toBe(0);
expect(sendTransport.iceGatheringState).toBe('complete');
expect(sendTransport.connectionState).toBe('new');

sendTransport.removeAllListeners('icegatheringstatechange');
sendTransport.removeAllListeners('connectionstatechange');
});

test('connection state change fires "connectionstatechange" in live Transport', () =>
{
let connectionStateChangeEventNumTimesCalled = 0;
Expand Down

0 comments on commit 99714af

Please sign in to comment.