The SessionDescriptionHandler class provides an implementation of which adhears to the SessionDescriptionHandler interface required by the API. The class is intended to be suitable for extending to provide custom behaviour if needed.
Session has a sessionDescriptionHandler property. Invitation and Inviter extend Session, so...
See Session.sessionDescriptionHandler docs for more info.
If Session is an instance of Inviter and an offer was sent in the INVITE, Session.sessionDescriptionHandler will be defined when the session state changes to SessionState.Establishing. Otherwise it will be defined when the session state changes to SessionState.Established.
The session description handler and media tracks are availble once the Session state transitions to SessionState.Established...
import { Session, SessionState, Web } from "sip.js";
// A Session state change handler which assigns media streams to HTML media elements.
function handleStateChanges(
session: Session,
localHTMLMediaElement: HTMLVideoElement | undefined,
remoteHTMLMediaElement: HTMLAudioElement | HTMLVideoElement | undefined
): void {
// Track session state changes and set media tracks to HTML elements when they become available.
session.stateChange.addListener((state: SessionState) => {
switch (state) {
case SessionState.Initial:
break;
case SessionState.Establishing:
break;
case SessionState.Established:
const sessionDescriptionHandler = session.sessionDescriptionHandler;
if (!sessionDescriptionHandler || !(sessionDescriptionHandler instanceof Web.SessionDescriptionHandler)) {
throw new Error("Invalid session description handler.");
}
if (localHTMLMediaElement) {
assignStream(sessionDescriptionHandler.localMediaStream, localHTMLMediaElement);
}
if (remoteHTMLMediaElement) {
assignStream(sessionDescriptionHandler.remoteMediaStream, remoteHTMLMediaElement);
}
break;
case SessionState.Terminating:
break;
case SessionState.Terminated:
break;
default:
throw new Error("Unknown session state.");
}
});
}
// Assign a MediaStream to an HTMLMediaElement and update if tracks change.
function assignStream(stream: MediaStream, element: HTMLMediaElement): void {
// Set element source.
element.autoplay = true; // Safari does not allow calling .play() from a non user action
element.srcObject = stream;
// Load and start playback of media.
element.play().catch((error: Error) => {
console.error("Failed to play media");
console.error(error);
});
// If a track is added, load and restart playback of media.
stream.onaddtrack = (): void => {
element.load(); // Safari does not work otheriwse
element.play().catch((error: Error) => {
console.error("Failed to play remote media on add track");
console.error(error);
});
};
// If a track is removed, load and restart playback of media.
stream.onremovetrack = (): void => {
element.load(); // Safari does not work otheriwse
element.play().catch((error: Error) => {
console.error("Failed to play remote media on remove track");
console.error(error);
});
};
}Providing for alternative media acquisition can be done by providing a MediaStreamFactory...
import { UserAgent, UserAgentOptions, Web } from "sip.js";
// Create media stream factory
const myMediaStreamFactory: Web.MediaStreamFactory = (
constraints: MediaStreamConstraints,
sessionDescriptionHandler: Web.SessionDescriptionHandler
): Promise<MediaStream> => {
const mediaStream = new MediaStream(); // my custom media stream acquisition
return Promise.resolve(mediaStream);
};
// Create session description handler factory
const mySessionDescriptionHandlerFactory: Web.SessionDescriptionHandlerFactory = Web.defaultSessionDescriptionHandlerFactory(
myMediaStreamFactory
);
// Create user agent
const myUserAgent = new UserAgent({
sessionDescriptionHandlerFactory: mySessionDescriptionHandlerFactory
});The session description handler is availble once the Session state transitions to SessionState.Established, however there are cases where tracks are added or removed if the media changes - for example, on upgrade from audio only to a video session. Not also that when the SessionDescriptionHandler is constructed the media stream initially has no tracks, so the presence of tracks should not be assumed.
See SessionDescriptionHandler.remoteMediaStream docs for more info.
import { Web } from "sip.js";
function handleAddTrack(sessionDescriptionHandler: Web.SessionDescriptionHandler): void {
sessionDescriptionHandler.remoteMediaStream.onaddtrack = (event) => {
const track = event.track;
console.log("A track was added");
};
sessionDescriptionHandler.remoteMediaStream.onremovetrack = (event) => {
const track = event.track;
console.log("A track was removed");
};
}import { Web } from "sip.js";
// The Session.sessionDescriptionHandlerOptionsReInvite property
// may be used to pass options to the SessionDescriptionHandler.
const sessionDescriptionHandlerOptions: Web.SesionDescriptionHandlerOptions = {
hold: true; // set to false to "unhold" session
}
session.sessionDescriptionHandlerOptionsReInvite = sessionDescriptionHandlerOptions;
const options: SessionInviteOptions = {
requestDelegate: {
onAccept: (): void => {
// session is on hold
},
onReject: (): void => {
// re-invite request was rejected, call not on hold
}
}
};
// Send re-INVITE
session
.invite(options)
.catch((error: Error) => {
if (error instanceof RequestPendingError) {
// a hold request is already in progress
}
});See docs for more info.
If you must have access as soon as it is created and before it is utilized, use the session delegate.
See SessionDelegate.onSessionDescriptionHandler() docs for more info.
import { SessionDescriptionHandler } from "sip.js";
function handleSessionDescriptionHandlerCreated(session: Session): void {
session.delegate = {
onSessionDescriptionHandler: (sessionDescriptionHandler: SessionDescriptionHandler, provisional: boolean) => {
console.log("A session description handler was created");
}
};
}SessionDescriptionHandler has a peerConnection property.
See docs for more info.
SessionDescriptionHandler has a peerConnectionDelegate property.
See docs for more info.