diff --git a/README.md b/README.md index 9e95237..aa3093a 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Child components get access to this context: answerCall: PropTypes.func, startCall: PropTypes.func, stopCall: PropTypes.func, + sendDTMF: PropTypes.func, } ``` @@ -120,6 +121,15 @@ To make calls, simply use these functions: The value for `destination` argument equals to the target SIP user without the host part (e.g. `+441234567890` or `bob`). The omitted host part is equal to host you’ve defined in `SipProvider` props (e.g. `sip.example.com`). +To send DTMF tones while in-call, you can use this function: + +`sendDTMF(tones)` + +You can pass as many tones as you want in a `string` (e.g. `sendDTMF("1234")`). +You may also specify `duration` and `interToneGap` in milliseconds, as `sendDTMF("1234", 100, 70)`. See [the MDN docs for `RTCDTMFSender.insertDTMF()`](https://developer.mozilla.org/en-US/docs/Web/API/RTCDTMFSender/insertDTMF) for further details. + +The DTMF implementation is **not** SIP INFO, but [RFC-4733](https://tools.ietf.org/html/rfc4733). + --- The values for `sip.status`, `sip.errorType`, `call.status` and `call.direction` can be imported as constants to make typos easier to detect: diff --git a/src/components/SipProvider/index.ts b/src/components/SipProvider/index.ts index 6c99403..0b022b2 100644 --- a/src/components/SipProvider/index.ts +++ b/src/components/SipProvider/index.ts @@ -55,6 +55,7 @@ export default class SipProvider extends React.Component< callStatus: CallStatus; callDirection: CallDirection | null; callCounterpart: string | null; + dtmfSender: RTCDTMFSender | null; rtcSession; } > { @@ -67,6 +68,7 @@ export default class SipProvider extends React.Component< answerCall: PropTypes.func, startCall: PropTypes.func, stopCall: PropTypes.func, + sendDTMF: PropTypes.func, }; public static propTypes = { @@ -119,6 +121,7 @@ export default class SipProvider extends React.Component< callStatus: CALL_STATUS_IDLE, callDirection: null, callCounterpart: null, + dtmfSender: null, }; this.ua = null; @@ -137,6 +140,7 @@ export default class SipProvider extends React.Component< status: this.state.callStatus, direction: this.state.callDirection, counterpart: this.state.callCounterpart, + dtmfSender: this.state.dtmfSender, }, registerSip: this.registerSip, unregisterSip: this.unregisterSip, @@ -144,6 +148,7 @@ export default class SipProvider extends React.Component< answerCall: this.answerCall, startCall: this.startCall, stopCall: this.stopCall, + sendDTMF: this.sendDTMF, }; } @@ -291,6 +296,20 @@ export default class SipProvider extends React.Component< this.ua.terminateSessions(); }; + public sendDTMF = (tones, duration = 100, interToneGap = 70) => { + if ( + this.state.callStatus === "callStatus/ACTIVE" && + this.state.dtmfSender + ) { + this.state.dtmfSender.insertDTMF(tones, duration, interToneGap); + } else { + this.logger.debug( + "Warning:", + "You are attempting to send DTMF, but there is no active call.", + ); + } + }; + public reconfigureDebug() { const { debug } = this.props; @@ -471,6 +490,7 @@ export default class SipProvider extends React.Component< callStatus: CALL_STATUS_IDLE, callDirection: null, callCounterpart: null, + dtmfSender: null, }); }); @@ -479,11 +499,17 @@ export default class SipProvider extends React.Component< return; } + // Close senders, as these keep the microphone open according to browsers (and that keeps Bluetooth headphones from exiting headset mode) + this.state.rtcSession.connection.getSenders().forEach((sender) => { + sender.track.stop(); + }); + this.setState({ rtcSession: null, callStatus: CALL_STATUS_IDLE, callDirection: null, callCounterpart: null, + dtmfSender: null, }); }); @@ -495,6 +521,12 @@ export default class SipProvider extends React.Component< [ this.remoteAudio.srcObject, ] = rtcSession.connection.getRemoteStreams(); + + // Set up DTMF + this.setState({ + dtmfSender: rtcSession.connection.getSenders()[0].dtmf, + }); + // const played = this.remoteAudio.play(); const played = this.remoteAudio.play(); @@ -508,6 +540,7 @@ export default class SipProvider extends React.Component< this.remoteAudio.play(); }, 2000); }); + // this.setState({ dtmfSender: rtcSession.connection.createDTMFSender(rtcSession.getAudioTracks()[0])}); this.setState({ callStatus: CALL_STATUS_ACTIVE }); return; }