From e22429af17460bfe9ada52ecb6208bb506a8d3b1 Mon Sep 17 00:00:00 2001 From: bbaldino Date: Thu, 9 Sep 2021 13:04:48 -0700 Subject: [PATCH] fix: support ice gathering already being complete in getLocalDescriptionWithIceCandidates (#40) --- src/peer-connection-utils.spec.ts | 25 ++++++++++++++++++------- src/peer-connection-utils.ts | 23 +++++++++++++++++------ src/peer-connection.ts | 9 +++++++++ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/peer-connection-utils.spec.ts b/src/peer-connection-utils.spec.ts index 40a6718..77671e9 100644 --- a/src/peer-connection-utils.spec.ts +++ b/src/peer-connection-utils.spec.ts @@ -6,15 +6,16 @@ import { mocked } from './mocks/mock'; jest.mock('./peer-connection'); +const dummyLocalDesc = { + type: 'offer', + sdp: 'sdp with candidates', + toJSON: () => undefined, +} as RTCSessionDescription; + describe('getLocalDescriptionWithIceCandidates', () => { const mockPc = mocked(new PeerConnection(), true); test('return the correct offer after ice gathering has finished', (done) => { - const localDesc = { - type: 'offer', - sdp: 'sdp with candidates', - toJSON: () => undefined, - } as RTCSessionDescription; - mockPc.getLocalDescription.mockReturnValueOnce(localDesc); + mockPc.getLocalDescription.mockReturnValueOnce(dummyLocalDesc); const promise = new PromiseHelper( getLocalDescriptionWithIceCandidates(mockPc as unknown as PeerConnection) @@ -46,11 +47,12 @@ describe('getLocalDescriptionWithIceCandidates', () => { Promise.resolve().then(() => { expect(mockPc.getLocalDescription.mock.calls).toHaveLength(1); expect(promise.isFinished()).toBe(true); - expect(promise.resolvedValue).toBe(localDesc); + expect(promise.resolvedValue).toBe(dummyLocalDesc); done(); }); }); }); + test('rejects if the local description is null', async () => { mockPc.getLocalDescription.mockReturnValueOnce(null); const promise = getLocalDescriptionWithIceCandidates(mockPc as unknown as PeerConnection); @@ -62,6 +64,15 @@ describe('getLocalDescriptionWithIceCandidates', () => { }); await expect(promise).rejects.toStrictEqual(expect.any(Error)); }); + + test('resolves immediately if the ICE candidates have already been gathered', () => { + mockPc.getLocalDescription.mockReturnValueOnce(dummyLocalDesc); + Object.defineProperty(mockPc, 'iceGatheringState', { + get: () => 'complete', + }); + + return getLocalDescriptionWithIceCandidates(mockPc as unknown as PeerConnection); + }); }); /** diff --git a/src/peer-connection-utils.ts b/src/peer-connection-utils.ts index 2a99ad4..1fecc0d 100644 --- a/src/peer-connection-utils.ts +++ b/src/peer-connection-utils.ts @@ -11,16 +11,27 @@ export function getLocalDescriptionWithIceCandidates( peerConnection: PeerConnection ): Promise { return new Promise((resolve, reject) => { + /** + * A helper method to retrieve the local description and resolve, if one is found, or reject + * with an error if it's not. + */ + const getLocalDescAndResolve = () => { + const localDesc = peerConnection.getLocalDescription(); + if (localDesc) { + resolve(localDesc); + } else { + reject(new Error('Local description was null')); + } + }; peerConnection.on(PeerConnection.Events.IceGatheringStateChange, (e) => { if (e.target.iceGatheringState === 'complete') { - const localDesc = peerConnection.getLocalDescription(); - if (localDesc) { - resolve(localDesc); - } else { - reject(new Error('Local description was null')); - } + getLocalDescAndResolve(); } // TODO(brian): throw an error if we see an error iceGatheringState }); + // It's possible ICE gathering is already done + if (peerConnection.iceGatheringState === 'complete') { + getLocalDescAndResolve(); + } }); } diff --git a/src/peer-connection.ts b/src/peer-connection.ts index 39dc6de..e5fed76 100644 --- a/src/peer-connection.ts +++ b/src/peer-connection.ts @@ -179,6 +179,15 @@ class PeerConnection extends EventEmitter { getTransceivers(): RTCRtpTransceiver[] { return this.pc.getTransceivers(); } + + /** + * Returns a string that describes the connections' ICE gathering state. + * + * @returns - The ICE gathering state. + */ + get iceGatheringState(): RTCIceGathererState { + return this.pc.iceGatheringState; + } } export { MediaStreamTrackKind, PeerConnection };