Comparing version 0.18.1 to 0.19.0
@@ -48,22 +48,6 @@ import { IncomingInviteRequest, IncomingRequestMessage, Logger, NameAddrHeader } from "../core"; | ||
* will be sent automatically. This is false it the UAC required | ||
* reliable provisional responses (100rel in Require header), | ||
* otherwise it is true. The provisional is sent by calling | ||
* `progress()` without any options. | ||
* | ||
* FIXME: TODO: It seems reasonable that the ISC user should | ||
* be able to optionally disable this behavior. As the provisional | ||
* is sent prior to the "invite" event being emitted, it's a known | ||
* issue that the ISC user cannot register listeners or do any other | ||
* setup prior to the call to `progress()`. As an example why this is | ||
* an issue, setting `ua.configuration.rel100` to REQUIRED will result | ||
* in an attempt by `progress()` to send a 183 with SDP produced by | ||
* calling `getDescription()` on a session description handler, but | ||
* the ISC user cannot perform any potentially required session description | ||
* handler initialization (thus preventing the utilization of setting | ||
* `ua.configuration.rel100` to REQUIRED). That begs the question of | ||
* why this behavior is disabled when the UAC requires 100rel but not | ||
* when the UAS requires 100rel? But ignoring that, it's just one example | ||
* of a class of cases where the ISC user needs to do something prior | ||
* to the first call to `progress()` and is unable to do so. | ||
* @internal | ||
* reliable provisional responses (100rel in Require header) or | ||
* the user agent configuration has specified to not send an | ||
* initial response, otherwise it is true. The provisional is sent by | ||
* calling `progress()` without any options. | ||
*/ | ||
@@ -70,0 +54,0 @@ get autoSendAnInitialProvisionalResponse(): boolean; |
@@ -135,25 +135,9 @@ import { fromBodyLegacy, getBody, Grammar, SignalingState, Timers, TransactionStateError } from "../core"; | ||
* will be sent automatically. This is false it the UAC required | ||
* reliable provisional responses (100rel in Require header), | ||
* otherwise it is true. The provisional is sent by calling | ||
* `progress()` without any options. | ||
* | ||
* FIXME: TODO: It seems reasonable that the ISC user should | ||
* be able to optionally disable this behavior. As the provisional | ||
* is sent prior to the "invite" event being emitted, it's a known | ||
* issue that the ISC user cannot register listeners or do any other | ||
* setup prior to the call to `progress()`. As an example why this is | ||
* an issue, setting `ua.configuration.rel100` to REQUIRED will result | ||
* in an attempt by `progress()` to send a 183 with SDP produced by | ||
* calling `getDescription()` on a session description handler, but | ||
* the ISC user cannot perform any potentially required session description | ||
* handler initialization (thus preventing the utilization of setting | ||
* `ua.configuration.rel100` to REQUIRED). That begs the question of | ||
* why this behavior is disabled when the UAC requires 100rel but not | ||
* when the UAS requires 100rel? But ignoring that, it's just one example | ||
* of a class of cases where the ISC user needs to do something prior | ||
* to the first call to `progress()` and is unable to do so. | ||
* @internal | ||
* reliable provisional responses (100rel in Require header) or | ||
* the user agent configuration has specified to not send an | ||
* initial response, otherwise it is true. The provisional is sent by | ||
* calling `progress()` without any options. | ||
*/ | ||
get autoSendAnInitialProvisionalResponse() { | ||
return this.rel100 === "required" ? false : true; | ||
return this.rel100 !== "required" && this.userAgent.configuration.sendInitialProvisionalResponse; | ||
} | ||
@@ -160,0 +144,0 @@ /** |
@@ -23,3 +23,7 @@ import { NameAddrHeader } from "../core"; | ||
get replaces() { | ||
return this.referTo.uri.getHeader("replaces"); | ||
const value = this.referTo.uri.getHeader("replaces"); | ||
if (value instanceof Array) { | ||
return value[0]; | ||
} | ||
return value; | ||
} | ||
@@ -26,0 +30,0 @@ /** Incoming REFER request message. */ |
@@ -1,2 +0,2 @@ | ||
import { C, Grammar, URI } from "../core"; | ||
import { C, Grammar, URI, equivalentURI } from "../core"; | ||
import { EmitterImpl } from "./emitter"; | ||
@@ -294,13 +294,17 @@ import { RequestPendingError } from "./exceptions"; | ||
} | ||
/* Adding host and port checks may break people not using contactName, so only check those | ||
* if the parameter is set. The server mucking with host and port is entirely legal, | ||
* so in cases where that occurs usage of contactName is currently broken. | ||
*/ | ||
if (contact.uri.user === this.userAgent.contact.uri.user && | ||
(this.userAgent.configuration.contactName === "" || | ||
(contact.uri.host === this.userAgent.contact.uri.host && | ||
contact.uri.port === this.userAgent.contact.uri.port))) { | ||
expires = Number(contact.getParam("expires")); | ||
break; | ||
// If we are using a randomly generated user name (which is the default behavior) | ||
if (this.userAgent.configuration.contactName === "") { | ||
// compare the user portion of the URI under the assumption that it will be unique | ||
if (contact.uri.user === this.userAgent.contact.uri.user) { | ||
expires = Number(contact.getParam("expires")); | ||
break; | ||
} | ||
} | ||
else { | ||
// otherwise use comparision rules in Section 19.1.4 | ||
if (equivalentURI(contact.uri, this.userAgent.contact.uri)) { | ||
expires = Number(contact.getParam("expires")); | ||
break; | ||
} | ||
} | ||
contact = undefined; | ||
@@ -307,0 +311,0 @@ } |
@@ -67,5 +67,5 @@ import { Logger, URI } from "../core"; | ||
/** | ||
* The contact name associated with the user agent. | ||
* The user portion of user agent's contact URI. | ||
* @remarks | ||
* User specified contact name, if not specifed random string will be generated | ||
* If not specifed a random string will be generated and utilized as the user portion of the contact URI. | ||
* @defaultValue `""` | ||
@@ -75,5 +75,3 @@ */ | ||
/** | ||
* The URI parameters associated with the user agent. | ||
* @remarks | ||
* User specified contact parameters | ||
* The URI parameters of the user agent's contact URI. | ||
* @defaultValue `{ transport: "ws" }` | ||
@@ -162,2 +160,8 @@ */ | ||
/** | ||
* If true, a first provisional response after the 100 Trying will be sent automatically if UAC does not | ||
* require reliable provisional responses. | ||
* @defaultValue `true` | ||
*/ | ||
sendInitialProvisionalResponse?: boolean; | ||
/** | ||
* A factory for generating `SessionDescriptionHandler` instances. | ||
@@ -164,0 +168,0 @@ * @remarks |
@@ -189,2 +189,3 @@ import { DigestAuthentication, Grammar, IncomingRequestMessage, IncomingResponseMessage, Levels, LoggerFactory, Parser, URI, UserAgentCore } from "../core"; | ||
reconnectionDelay: 4, | ||
sendInitialProvisionalResponse: true, | ||
sessionDescriptionHandlerFactory: defaultSessionDescriptionHandlerFactory(), | ||
@@ -191,0 +192,0 @@ sessionDescriptionHandlerFactoryOptions: {}, |
@@ -7,3 +7,5 @@ import { Parameters } from "./parameters"; | ||
export declare class URI extends Parameters { | ||
private headers; | ||
headers: { | ||
[name: string]: Array<string>; | ||
}; | ||
private normal; | ||
@@ -22,3 +24,5 @@ private raw; | ||
[name: string]: string | number | null; | ||
}, headers?: any); | ||
}, headers?: { | ||
[name: string]: Array<string>; | ||
}); | ||
get scheme(): string; | ||
@@ -33,6 +37,6 @@ set scheme(value: string); | ||
set port(value: number | undefined); | ||
setHeader(name: string, value: any): void; | ||
getHeader(name: string): string | undefined; | ||
setHeader(name: string, value: Array<string> | string): void; | ||
getHeader(name: string): Array<string> | undefined; | ||
hasHeader(name: string): boolean; | ||
deleteHeader(header: string): any; | ||
deleteHeader(header: string): Array<string> | undefined; | ||
clearHeaders(): void; | ||
@@ -48,2 +52,16 @@ clone(): URI; | ||
} | ||
/** | ||
* Returns true if URIs are equivalent per RFC 3261 Section 19.1.4. | ||
* @param a URI to compare | ||
* @param b URI to compare | ||
* | ||
* @remarks | ||
* 19.1.4 URI Comparison | ||
* Some operations in this specification require determining whether two | ||
* SIP or SIPS URIs are equivalent. | ||
* | ||
* https://tools.ietf.org/html/rfc3261#section-19.1.4 | ||
* @internal | ||
*/ | ||
export declare function equivalentURI(a: URI, b: URI): boolean; | ||
//# sourceMappingURL=uri.d.ts.map |
@@ -125,2 +125,3 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
if (this.headers.hasOwnProperty(header)) { | ||
// eslint-disable-next-line @typescript-eslint/no-for-in-array | ||
for (const idx in this.headers[header]) { | ||
@@ -190,1 +191,114 @@ // eslint-disable-next-line no-prototype-builtins | ||
} | ||
/** | ||
* Returns true if URIs are equivalent per RFC 3261 Section 19.1.4. | ||
* @param a URI to compare | ||
* @param b URI to compare | ||
* | ||
* @remarks | ||
* 19.1.4 URI Comparison | ||
* Some operations in this specification require determining whether two | ||
* SIP or SIPS URIs are equivalent. | ||
* | ||
* https://tools.ietf.org/html/rfc3261#section-19.1.4 | ||
* @internal | ||
*/ | ||
export function equivalentURI(a, b) { | ||
// o A SIP and SIPS URI are never equivalent. | ||
if (a.scheme !== b.scheme) { | ||
return false; | ||
} | ||
// o Comparison of the userinfo of SIP and SIPS URIs is case- | ||
// sensitive. This includes userinfo containing passwords or | ||
// formatted as telephone-subscribers. Comparison of all other | ||
// components of the URI is case-insensitive unless explicitly | ||
// defined otherwise. | ||
// | ||
// o The ordering of parameters and header fields is not significant | ||
// in comparing SIP and SIPS URIs. | ||
// | ||
// o Characters other than those in the "reserved" set (see RFC 2396 | ||
// [5]) are equivalent to their ""%" HEX HEX" encoding. | ||
// | ||
// o An IP address that is the result of a DNS lookup of a host name | ||
// does not match that host name. | ||
// | ||
// o For two URIs to be equal, the user, password, host, and port | ||
// components must match. | ||
// | ||
// A URI omitting the user component will not match a URI that | ||
// includes one. A URI omitting the password component will not | ||
// match a URI that includes one. | ||
// | ||
// A URI omitting any component with a default value will not | ||
// match a URI explicitly containing that component with its | ||
// default value. For instance, a URI omitting the optional port | ||
// component will not match a URI explicitly declaring port 5060. | ||
// The same is true for the transport-parameter, ttl-parameter, | ||
// user-parameter, and method components. | ||
// | ||
// Defining sip:user@host to not be equivalent to | ||
// sip:user@host:5060 is a change from RFC 2543. When deriving | ||
// addresses from URIs, equivalent addresses are expected from | ||
// equivalent URIs. The URI sip:user@host:5060 will always | ||
// resolve to port 5060. The URI sip:user@host may resolve to | ||
// other ports through the DNS SRV mechanisms detailed in [4]. | ||
// FIXME: TODO: | ||
// - character compared to hex encoding is not handled | ||
// - password does not exist on URI currently | ||
if (a.user !== b.user || a.host !== b.host || a.port !== b.port) { | ||
return false; | ||
} | ||
// o URI uri-parameter components are compared as follows: | ||
function compareParameters(a, b) { | ||
// - Any uri-parameter appearing in both URIs must match. | ||
const parameterKeysA = Object.keys(a.parameters); | ||
const parameterKeysB = Object.keys(b.parameters); | ||
const intersection = parameterKeysA.filter(x => parameterKeysB.includes(x)); | ||
if (!intersection.every(key => a.parameters[key] === b.parameters[key])) { | ||
return false; | ||
} | ||
// - A user, ttl, or method uri-parameter appearing in only one | ||
// URI never matches, even if it contains the default value. | ||
if (!["user", "ttl", "method", "transport"].every(key => a.hasParam(key) && b.hasParam(key) || !a.hasParam(key) && !b.hasParam(key))) { | ||
return false; | ||
} | ||
// - A URI that includes an maddr parameter will not match a URI | ||
// that contains no maddr parameter. | ||
if (!["maddr"].every(key => a.hasParam(key) && b.hasParam(key) || !a.hasParam(key) && !b.hasParam(key))) { | ||
return false; | ||
} | ||
// - All other uri-parameters appearing in only one URI are | ||
// ignored when comparing the URIs. | ||
return true; | ||
} | ||
if (!compareParameters(a, b)) { | ||
return false; | ||
} | ||
// o URI header components are never ignored. Any present header | ||
// component MUST be present in both URIs and match for the URIs | ||
// to match. The matching rules are defined for each header field | ||
// in Section 20. | ||
const headerKeysA = Object.keys(a.headers); | ||
const headerKeysB = Object.keys(b.headers); | ||
// No need to check if no headers | ||
if (headerKeysA.length !== 0 || headerKeysB.length !== 0) { | ||
// Must have same number of headers | ||
if (headerKeysA.length !== headerKeysB.length) { | ||
return false; | ||
} | ||
// Must have same headers | ||
const intersection = headerKeysA.filter(x => headerKeysB.includes(x)); | ||
if (intersection.length !== headerKeysB.length) { | ||
return false; | ||
} | ||
// FIXME: Not to spec. But perhaps not worth fixing? | ||
// Must have same header values | ||
// It seems too much to consider multiple headers with same name. | ||
// It seems too much to compare two header params according to the rule of each header. | ||
// We'll assume a single header and compare them string to string... | ||
if (!intersection.every(key => a.headers[key].length && b.headers[key].length && a.headers[key][0] === b.headers[key][0])) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} |
@@ -1,2 +0,2 @@ | ||
declare const version = "0.18.1"; | ||
declare const version = "0.19.0"; | ||
declare const name = "sip.js"; | ||
@@ -3,0 +3,0 @@ export { name, version }; |
@@ -31,2 +31,6 @@ import { SessionDescriptionHandlerOptions as SessionDescriptionHandlerOptionsDefinition } from "../../../api"; | ||
/** | ||
* If true, offer and answer directions will be set to place peer on hold. | ||
*/ | ||
hold?: boolean; | ||
/** | ||
* The maximum duration to wait in ms for ICE gathering to complete. | ||
@@ -33,0 +37,0 @@ * No timeout if undefined or zero. |
@@ -194,2 +194,7 @@ import { BodyAndContentType, SessionDescriptionHandler as SessionDescriptionHandlerDefinition, SessionDescriptionHandlerModifier } from "../../../api"; | ||
/** | ||
* Depending on the current signaling state and the session hold state, update transceiver direction. | ||
* @param options - Session description handler options. | ||
*/ | ||
protected updateDirection(options?: SessionDescriptionHandlerOptions): Promise<void>; | ||
/** | ||
* Called when ICE gathering completes and resolves any waiting promise. | ||
@@ -196,0 +201,0 @@ */ |
@@ -143,2 +143,3 @@ /** | ||
return this.getLocalMediaStream(options) | ||
.then(() => this.updateDirection(options)) | ||
.then(() => this.createDataChannel(options)) | ||
@@ -514,2 +515,169 @@ .then(() => this.createLocalOfferOrAnswer(options)) | ||
/** | ||
* Depending on the current signaling state and the session hold state, update transceiver direction. | ||
* @param options - Session description handler options. | ||
*/ | ||
updateDirection(options) { | ||
if (this._peerConnection === undefined) { | ||
return Promise.reject(new Error("Peer connection closed.")); | ||
} | ||
// 4.2.3. setDirection | ||
// | ||
// The setDirection method sets the direction of a transceiver, which | ||
// affects the direction property of the associated "m=" section on | ||
// future calls to createOffer and createAnswer. The permitted values | ||
// for direction are "recvonly", "sendrecv", "sendonly", and "inactive", | ||
// mirroring the identically named direction attributes defined in | ||
// [RFC4566], Section 6. | ||
// | ||
// When creating offers, the transceiver direction is directly reflected | ||
// in the output, even for re-offers. When creating answers, the | ||
// transceiver direction is intersected with the offered direction, as | ||
// explained in Section 5.3 below. | ||
// | ||
// Note that while setDirection sets the direction property of the | ||
// transceiver immediately (Section 4.2.4), this property does not | ||
// immediately affect whether the transceiver's RtpSender will send or | ||
// its RtpReceiver will receive. The direction in effect is represented | ||
// by the currentDirection property, which is only updated when an | ||
// answer is applied. | ||
// | ||
// 4.2.4. direction | ||
// | ||
// The direction property indicates the last value passed into | ||
// setDirection. If setDirection has never been called, it is set to | ||
// the direction the transceiver was initialized with. | ||
// | ||
// 4.2.5. currentDirection | ||
// | ||
// The currentDirection property indicates the last negotiated direction | ||
// for the transceiver's associated "m=" section. More specifically, it | ||
// indicates the direction attribute [RFC3264] of the associated "m=" | ||
// section in the last applied answer (including provisional answers), | ||
// with "send" and "recv" directions reversed if it was a remote answer. | ||
// For example, if the direction attribute for the associated "m=" | ||
// section in a remote answer is "recvonly", currentDirection is set to | ||
// "sendonly". | ||
// | ||
// If an answer that references this transceiver has not yet been | ||
// applied or if the transceiver is stopped, currentDirection is set to | ||
// "null". | ||
// https://tools.ietf.org/html/rfc8829#section-4.2.3 | ||
// | ||
// * A direction attribute, determined by applying the rules regarding | ||
// the offered direction specified in [RFC3264], Section 6.1, and | ||
// then intersecting with the direction of the associated | ||
// RtpTransceiver. For example, in the case where an "m=" section is | ||
// offered as "sendonly" and the local transceiver is set to | ||
// "sendrecv", the result in the answer is a "recvonly" direction. | ||
// https://tools.ietf.org/html/rfc8829#section-5.3.1 | ||
// | ||
// If a stream is offered as sendonly, the corresponding stream MUST be | ||
// marked as recvonly or inactive in the answer. If a media stream is | ||
// listed as recvonly in the offer, the answer MUST be marked as | ||
// sendonly or inactive in the answer. If an offered media stream is | ||
// listed as sendrecv (or if there is no direction attribute at the | ||
// media or session level, in which case the stream is sendrecv by | ||
// default), the corresponding stream in the answer MAY be marked as | ||
// sendonly, recvonly, sendrecv, or inactive. If an offered media | ||
// stream is listed as inactive, it MUST be marked as inactive in the | ||
// answer. | ||
// https://tools.ietf.org/html/rfc3264#section-6.1 | ||
switch (this._peerConnection.signalingState) { | ||
case "stable": | ||
// if we are stable, assume we are creating a local offer | ||
this.logger.debug("SessionDescriptionHandler.updateDirection - setting offer direction"); | ||
{ | ||
// determine the direction to offer given the current direction and hold state | ||
const directionToOffer = (currentDirection) => { | ||
switch (currentDirection) { | ||
case "inactive": | ||
return (options === null || options === void 0 ? void 0 : options.hold) ? "inactive" : "recvonly"; | ||
case "recvonly": | ||
return (options === null || options === void 0 ? void 0 : options.hold) ? "inactive" : "recvonly"; | ||
case "sendonly": | ||
return (options === null || options === void 0 ? void 0 : options.hold) ? "sendonly" : "sendrecv"; | ||
case "sendrecv": | ||
return (options === null || options === void 0 ? void 0 : options.hold) ? "sendonly" : "sendrecv"; | ||
case "stopped": | ||
return "stopped"; | ||
default: | ||
throw new Error("Should never happen"); | ||
} | ||
}; | ||
// set the transceiver direction to the offer direction | ||
this._peerConnection.getTransceivers().forEach((transceiver) => { | ||
if (transceiver.direction /* guarding, but should always be true */) { | ||
const offerDirection = directionToOffer(transceiver.direction); | ||
if (transceiver.direction !== offerDirection) { | ||
transceiver.direction = offerDirection; | ||
} | ||
} | ||
}); | ||
} | ||
break; | ||
case "have-remote-offer": | ||
// if we have a remote offer, assume we are creating a local answer | ||
this.logger.debug("SessionDescriptionHandler.updateDirection - setting answer direction"); | ||
// FIXME: This is not the correct way to determine the answer direction as it is only | ||
// considering first match in the offered SDP and using that to determine the answer direction. | ||
// While that may be fine for our current use cases, it is not a generally correct approach. | ||
{ | ||
// determine the offered direction | ||
const offeredDirection = (() => { | ||
const description = this._peerConnection.remoteDescription; | ||
if (!description) { | ||
throw new Error("Failed to read remote offer"); | ||
} | ||
const searchResult = /a=sendrecv\r\n|a=sendonly\r\n|a=recvonly\r\n|a=inactive\r\n/.exec(description.sdp); | ||
if (searchResult) { | ||
switch (searchResult[0]) { | ||
case "a=inactive\r\n": | ||
return "inactive"; | ||
case "a=recvonly\r\n": | ||
return "recvonly"; | ||
case "a=sendonly\r\n": | ||
return "sendonly"; | ||
case "a=sendrecv\r\n": | ||
return "sendrecv"; | ||
default: | ||
throw new Error("Should never happen"); | ||
} | ||
} | ||
return "sendrecv"; | ||
})(); | ||
// determine the answer direction based on the offered direction and our hold state | ||
const answerDirection = (() => { | ||
switch (offeredDirection) { | ||
case "inactive": | ||
return "inactive"; | ||
case "recvonly": | ||
return "sendonly"; | ||
case "sendonly": | ||
return (options === null || options === void 0 ? void 0 : options.hold) ? "inactive" : "recvonly"; | ||
case "sendrecv": | ||
return (options === null || options === void 0 ? void 0 : options.hold) ? "sendonly" : "sendrecv"; | ||
default: | ||
throw new Error("Should never happen"); | ||
} | ||
})(); | ||
// set the transceiver direction to the answer direction | ||
this._peerConnection.getTransceivers().forEach((transceiver) => { | ||
if (transceiver.direction /* guarding, but should always be true */) { | ||
if (transceiver.direction !== "stopped" && transceiver.direction !== answerDirection) { | ||
transceiver.direction = answerDirection; | ||
} | ||
} | ||
}); | ||
} | ||
break; | ||
case "have-local-offer": | ||
case "have-local-pranswer": | ||
case "have-remote-pranswer": | ||
case "closed": | ||
default: | ||
return Promise.reject(new Error("Invalid signaling state " + this._peerConnection.signalingState)); | ||
} | ||
return Promise.resolve(); | ||
} | ||
/** | ||
* Called when ICE gathering completes and resolves any waiting promise. | ||
@@ -516,0 +684,0 @@ */ |
@@ -20,2 +20,3 @@ import { InvitationAcceptOptions, InviterInviteOptions, InviterOptions, RegistererOptions, RegistererRegisterOptions, RegistererUnregisterOptions } from "../../../api"; | ||
private held; | ||
private muted; | ||
private options; | ||
@@ -193,2 +194,4 @@ private registerer; | ||
/** Helper function to enable/disable media tracks. */ | ||
private enableReceiverTracks; | ||
/** Helper function to enable/disable media tracks. */ | ||
private enableSenderTracks; | ||
@@ -195,0 +198,0 @@ /** |
import { Invitation, Inviter, Messager, Registerer, RegistererState, RequestPendingError, SessionState, UserAgent, UserAgentState } from "../../../api"; | ||
import { holdModifier } from "../modifiers"; | ||
import { SessionDescriptionHandler } from "../session-description-handler"; | ||
@@ -24,2 +23,3 @@ import { Transport } from "../transport"; | ||
this.held = false; | ||
this.muted = false; | ||
this.registerer = undefined; | ||
@@ -453,4 +453,3 @@ this.registerRequested = false; | ||
isMuted() { | ||
const track = this.localAudioTrack || this.localVideoTrack; | ||
return track ? !track.enabled : false; | ||
return this.muted; | ||
} | ||
@@ -592,2 +591,21 @@ /** | ||
/** Helper function to enable/disable media tracks. */ | ||
enableReceiverTracks(enable) { | ||
if (!this.session) { | ||
throw new Error("Session does not exist."); | ||
} | ||
const sessionDescriptionHandler = this.session.sessionDescriptionHandler; | ||
if (!(sessionDescriptionHandler instanceof SessionDescriptionHandler)) { | ||
throw new Error("Session's session description handler not instance of SessionDescriptionHandler."); | ||
} | ||
const peerConnection = sessionDescriptionHandler.peerConnection; | ||
if (!peerConnection) { | ||
throw new Error("Peer connection closed."); | ||
} | ||
peerConnection.getReceivers().forEach((receiver) => { | ||
if (receiver.track) { | ||
receiver.track.enabled = enable; | ||
} | ||
}); | ||
} | ||
/** Helper function to enable/disable media tracks. */ | ||
enableSenderTracks(enable) { | ||
@@ -766,2 +784,4 @@ if (!this.session) { | ||
this.held = hold; | ||
this.enableReceiverTracks(!this.held); | ||
this.enableSenderTracks(!this.held && !this.muted); | ||
if (this.delegate && this.delegate.onCallHold) { | ||
@@ -773,2 +793,4 @@ this.delegate.onCallHold(this.held); | ||
this.logger.warn(`[${this.id}] re-invite request was rejected`); | ||
this.enableReceiverTracks(!this.held); | ||
this.enableSenderTracks(!this.held && !this.muted); | ||
if (this.delegate && this.delegate.onCallHold) { | ||
@@ -780,17 +802,20 @@ this.delegate.onCallHold(this.held); | ||
}; | ||
// Session properties used to pass modifiers to the SessionDescriptionHandler: | ||
// Session properties used to pass options to the SessionDescriptionHandler: | ||
// | ||
// 1) Session.sessionDescriptionHandlerModifiers | ||
// - used in all cases when handling the initial INVITE transaction as either UAC or UAS | ||
// - may be set directly at anytime | ||
// - may optionally be set via constructor option | ||
// - may optionally be set via options passed to Inviter.invite() or Invitation.accept() | ||
// 1) Session.sessionDescriptionHandlerOptions | ||
// SDH options for the initial INVITE transaction. | ||
// - Used in all cases when handling the initial INVITE transaction as either UAC or UAS. | ||
// - May be set directly at anytime. | ||
// - May optionally be set via constructor option. | ||
// - May optionally be set via options passed to Inviter.invite() or Invitation.accept(). | ||
// | ||
// 2) Session.sessionDescriptionHandlerModifiersReInvite | ||
// - used in all cases when handling a re-INVITE transaction as either UAC or UAS | ||
// - may be set directly at anytime | ||
// - may optionally be set via constructor option | ||
// - may optionally be set via options passed to Session.invite() | ||
// Set the session's SDH re-INVITE modifiers to produce the appropriate SDP offer to place call on hold | ||
session.sessionDescriptionHandlerModifiersReInvite = hold ? [holdModifier] : []; | ||
// 2) Session.sessionDescriptionHandlerOptionsReInvite | ||
// SDH options for re-INVITE transactions. | ||
// - Used in all cases when handling a re-INVITE transaction as either UAC or UAS. | ||
// - May be set directly at anytime. | ||
// - May optionally be set via constructor option. | ||
// - May optionally be set via options passed to Session.invite(). | ||
const sessionDescriptionHandlerOptions = session.sessionDescriptionHandlerOptionsReInvite; | ||
sessionDescriptionHandlerOptions.hold = hold; | ||
session.sessionDescriptionHandlerOptionsReInvite = sessionDescriptionHandlerOptions; | ||
// Send re-INVITE | ||
@@ -800,7 +825,5 @@ return this.session | ||
.then(() => { | ||
// Reset the session's SDH re-INVITE modifiers. | ||
// Note that if the modifiers are not reset, they will be applied | ||
// to the SDP answer as well (which we do not want in this case). | ||
session.sessionDescriptionHandlerModifiersReInvite = []; | ||
this.enableSenderTracks(!hold); // mute/unmute | ||
// preemptively enable/disable tracks | ||
this.enableReceiverTracks(!hold); | ||
this.enableSenderTracks(!hold && !this.muted); | ||
}) | ||
@@ -827,3 +850,4 @@ .catch((error) => { | ||
} | ||
this.enableSenderTracks(!mute); | ||
this.muted = mute; | ||
this.enableSenderTracks(!this.held && !this.muted); | ||
} | ||
@@ -830,0 +854,0 @@ /** Helper function to attach local media to html elements. */ |
@@ -1,2 +0,2 @@ | ||
export declare const LIBRARY_VERSION = "0.18.1"; | ||
export declare const LIBRARY_VERSION = "0.19.0"; | ||
//# sourceMappingURL=version.d.ts.map |
@@ -1,1 +0,1 @@ | ||
export const LIBRARY_VERSION = "0.18.1"; | ||
export const LIBRARY_VERSION = "0.19.0"; |
@@ -5,3 +5,3 @@ { | ||
"description": "A SIP library for JavaScript", | ||
"version": "0.18.1", | ||
"version": "0.19.0", | ||
"license": "MIT", | ||
@@ -37,12 +37,12 @@ "main": "./lib/index.js", | ||
"@microsoft/api-extractor": "7.7.11", | ||
"@types/jasmine": "^3.6.2", | ||
"@types/jasmine": "^3.6.3", | ||
"@typescript-eslint/eslint-plugin": "^2.34.0", | ||
"@typescript-eslint/parser": "^2.34.0", | ||
"circular-dependency-plugin": "^5.2.2", | ||
"eslint": "^7.16.0", | ||
"eslint-config-prettier": "^7.1.0", | ||
"eslint-plugin-prettier": "^3.3.0", | ||
"eslint": "^7.18.0", | ||
"eslint-config-prettier": "^7.2.0", | ||
"eslint-plugin-prettier": "^3.3.1", | ||
"eslint-plugin-tree-shaking": "^1.8.0", | ||
"jasmine-core": "^3.6.0", | ||
"karma": "^5.2.3", | ||
"karma": "^6.0.2", | ||
"karma-chrome-launcher": "^3.1.0", | ||
@@ -58,6 +58,6 @@ "karma-cli": "^2.0.0", | ||
"prettier": "2.2.1", | ||
"ts-loader": "^8.0.12", | ||
"ts-pegjs": "0.2.7", | ||
"ts-loader": "^8.0.14", | ||
"ts-pegjs": "0.3.0", | ||
"typescript": "4.1.3", | ||
"webpack": "^4.44.2", | ||
"webpack": "^4.46.0", | ||
"webpack-cli": "^3.3.12" | ||
@@ -64,0 +64,0 @@ }, |
@@ -7,4 +7,4 @@ # Security Policy | ||
| ------- | ------------------ | | ||
| 0.14.x | :white_check_mark: | | ||
| < 0.14.x | :x: | | ||
| 0.19.x | :white_check_mark: | | ||
| < 0.19.x | :x: | | ||
@@ -11,0 +11,0 @@ ## Reporting a Vulnerability |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1295154
26398
565