hap-nodejs
Version:
HAP-NodeJS is a Node.js implementation of HomeKit Accessory Server.
867 lines • 57.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RTPStreamManagement = exports.StreamRequestTypes = exports.AudioStreamingSamplerate = exports.AudioStreamingCodecType = exports.SRTPCryptoSuites = exports.AudioSamplerate = exports.AudioBitrate = exports.VideoCodecPacketizationMode = exports.H264Level = exports.H264Profile = exports.VideoCodecType = void 0;
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
const crypto_1 = tslib_1.__importDefault(require("crypto"));
const debug_1 = tslib_1.__importDefault(require("debug"));
const net_1 = tslib_1.__importDefault(require("net"));
const Characteristic_1 = require("../Characteristic");
const controller_1 = require("../controller");
const Service_1 = require("../Service");
const hapStatusError_1 = require("../util/hapStatusError");
const once_1 = require("../util/once");
const tlv = tslib_1.__importStar(require("../util/tlv"));
const uuid = tslib_1.__importStar(require("../util/uuid"));
const RTPProxy_1 = tslib_1.__importDefault(require("./RTPProxy"));
const debug = (0, debug_1.default)("HAP-NodeJS:Camera:RTPStreamManagement");
// ---------------------------------- TLV DEFINITIONS START ----------------------------------
var StreamingStatusTypes;
(function (StreamingStatusTypes) {
StreamingStatusTypes[StreamingStatusTypes["STATUS"] = 1] = "STATUS";
})(StreamingStatusTypes || (StreamingStatusTypes = {}));
var StreamingStatus;
(function (StreamingStatus) {
StreamingStatus[StreamingStatus["AVAILABLE"] = 0] = "AVAILABLE";
StreamingStatus[StreamingStatus["IN_USE"] = 1] = "IN_USE";
StreamingStatus[StreamingStatus["UNAVAILABLE"] = 2] = "UNAVAILABLE";
})(StreamingStatus || (StreamingStatus = {}));
// ----------
var SupportedVideoStreamConfigurationTypes;
(function (SupportedVideoStreamConfigurationTypes) {
SupportedVideoStreamConfigurationTypes[SupportedVideoStreamConfigurationTypes["VIDEO_CODEC_CONFIGURATION"] = 1] = "VIDEO_CODEC_CONFIGURATION";
})(SupportedVideoStreamConfigurationTypes || (SupportedVideoStreamConfigurationTypes = {}));
var VideoCodecConfigurationTypes;
(function (VideoCodecConfigurationTypes) {
VideoCodecConfigurationTypes[VideoCodecConfigurationTypes["CODEC_TYPE"] = 1] = "CODEC_TYPE";
VideoCodecConfigurationTypes[VideoCodecConfigurationTypes["CODEC_PARAMETERS"] = 2] = "CODEC_PARAMETERS";
VideoCodecConfigurationTypes[VideoCodecConfigurationTypes["ATTRIBUTES"] = 3] = "ATTRIBUTES";
})(VideoCodecConfigurationTypes || (VideoCodecConfigurationTypes = {}));
var VideoCodecParametersTypes;
(function (VideoCodecParametersTypes) {
VideoCodecParametersTypes[VideoCodecParametersTypes["PROFILE_ID"] = 1] = "PROFILE_ID";
VideoCodecParametersTypes[VideoCodecParametersTypes["LEVEL"] = 2] = "LEVEL";
VideoCodecParametersTypes[VideoCodecParametersTypes["PACKETIZATION_MODE"] = 3] = "PACKETIZATION_MODE";
VideoCodecParametersTypes[VideoCodecParametersTypes["CVO_ENABLED"] = 4] = "CVO_ENABLED";
VideoCodecParametersTypes[VideoCodecParametersTypes["CVO_ID"] = 5] = "CVO_ID";
})(VideoCodecParametersTypes || (VideoCodecParametersTypes = {}));
var VideoAttributesTypes;
(function (VideoAttributesTypes) {
VideoAttributesTypes[VideoAttributesTypes["IMAGE_WIDTH"] = 1] = "IMAGE_WIDTH";
VideoAttributesTypes[VideoAttributesTypes["IMAGE_HEIGHT"] = 2] = "IMAGE_HEIGHT";
VideoAttributesTypes[VideoAttributesTypes["FRAME_RATE"] = 3] = "FRAME_RATE";
})(VideoAttributesTypes || (VideoAttributesTypes = {}));
/**
* @group Camera
*/
var VideoCodecType;
(function (VideoCodecType) {
VideoCodecType[VideoCodecType["H264"] = 0] = "H264";
// while the namespace is already reserved for H265 it isn't currently supported.
// H265 = 0x01,
})(VideoCodecType || (exports.VideoCodecType = VideoCodecType = {}));
/**
* @group Camera
*/
var H264Profile;
(function (H264Profile) {
H264Profile[H264Profile["BASELINE"] = 0] = "BASELINE";
H264Profile[H264Profile["MAIN"] = 1] = "MAIN";
H264Profile[H264Profile["HIGH"] = 2] = "HIGH";
})(H264Profile || (exports.H264Profile = H264Profile = {}));
/**
* @group Camera
*/
var H264Level;
(function (H264Level) {
H264Level[H264Level["LEVEL3_1"] = 0] = "LEVEL3_1";
H264Level[H264Level["LEVEL3_2"] = 1] = "LEVEL3_2";
H264Level[H264Level["LEVEL4_0"] = 2] = "LEVEL4_0";
})(H264Level || (exports.H264Level = H264Level = {}));
/**
* @group Camera
*/
var VideoCodecPacketizationMode;
(function (VideoCodecPacketizationMode) {
VideoCodecPacketizationMode[VideoCodecPacketizationMode["NON_INTERLEAVED"] = 0] = "NON_INTERLEAVED";
})(VideoCodecPacketizationMode || (exports.VideoCodecPacketizationMode = VideoCodecPacketizationMode = {}));
var VideoCodecCVO;
(function (VideoCodecCVO) {
VideoCodecCVO[VideoCodecCVO["UNSUPPORTED"] = 0] = "UNSUPPORTED";
VideoCodecCVO[VideoCodecCVO["SUPPORTED"] = 1] = "SUPPORTED";
})(VideoCodecCVO || (VideoCodecCVO = {}));
// ----------
var SupportedAudioStreamConfigurationTypes;
(function (SupportedAudioStreamConfigurationTypes) {
SupportedAudioStreamConfigurationTypes[SupportedAudioStreamConfigurationTypes["AUDIO_CODEC_CONFIGURATION"] = 1] = "AUDIO_CODEC_CONFIGURATION";
SupportedAudioStreamConfigurationTypes[SupportedAudioStreamConfigurationTypes["COMFORT_NOISE_SUPPORT"] = 2] = "COMFORT_NOISE_SUPPORT";
})(SupportedAudioStreamConfigurationTypes || (SupportedAudioStreamConfigurationTypes = {}));
var AudioCodecConfigurationTypes;
(function (AudioCodecConfigurationTypes) {
AudioCodecConfigurationTypes[AudioCodecConfigurationTypes["CODEC_TYPE"] = 1] = "CODEC_TYPE";
AudioCodecConfigurationTypes[AudioCodecConfigurationTypes["CODEC_PARAMETERS"] = 2] = "CODEC_PARAMETERS";
})(AudioCodecConfigurationTypes || (AudioCodecConfigurationTypes = {}));
var AudioCodecTypes;
(function (AudioCodecTypes) {
AudioCodecTypes[AudioCodecTypes["PCMU"] = 0] = "PCMU";
AudioCodecTypes[AudioCodecTypes["PCMA"] = 1] = "PCMA";
AudioCodecTypes[AudioCodecTypes["AAC_ELD"] = 2] = "AAC_ELD";
AudioCodecTypes[AudioCodecTypes["OPUS"] = 3] = "OPUS";
AudioCodecTypes[AudioCodecTypes["MSBC"] = 4] = "MSBC";
AudioCodecTypes[AudioCodecTypes["AMR"] = 5] = "AMR";
AudioCodecTypes[AudioCodecTypes["AMR_WB"] = 6] = "AMR_WB";
})(AudioCodecTypes || (AudioCodecTypes = {}));
var AudioCodecParametersTypes;
(function (AudioCodecParametersTypes) {
AudioCodecParametersTypes[AudioCodecParametersTypes["CHANNEL"] = 1] = "CHANNEL";
AudioCodecParametersTypes[AudioCodecParametersTypes["BIT_RATE"] = 2] = "BIT_RATE";
AudioCodecParametersTypes[AudioCodecParametersTypes["SAMPLE_RATE"] = 3] = "SAMPLE_RATE";
AudioCodecParametersTypes[AudioCodecParametersTypes["PACKET_TIME"] = 4] = "PACKET_TIME"; // only present in selected audio codec parameters tlv
})(AudioCodecParametersTypes || (AudioCodecParametersTypes = {}));
/**
* @group Camera
*/
var AudioBitrate;
(function (AudioBitrate) {
AudioBitrate[AudioBitrate["VARIABLE"] = 0] = "VARIABLE";
AudioBitrate[AudioBitrate["CONSTANT"] = 1] = "CONSTANT";
})(AudioBitrate || (exports.AudioBitrate = AudioBitrate = {}));
/**
* @group Camera
*/
var AudioSamplerate;
(function (AudioSamplerate) {
AudioSamplerate[AudioSamplerate["KHZ_8"] = 0] = "KHZ_8";
AudioSamplerate[AudioSamplerate["KHZ_16"] = 1] = "KHZ_16";
AudioSamplerate[AudioSamplerate["KHZ_24"] = 2] = "KHZ_24";
// 3, 4, 5 are theoretically defined, but no idea to what kHz value they correspond to
// probably KHZ_32, KHZ_44_1, KHZ_48 (as supported by Secure Video recordings)
})(AudioSamplerate || (exports.AudioSamplerate = AudioSamplerate = {}));
// ----------
var SupportedRTPConfigurationTypes;
(function (SupportedRTPConfigurationTypes) {
SupportedRTPConfigurationTypes[SupportedRTPConfigurationTypes["SRTP_CRYPTO_SUITE"] = 2] = "SRTP_CRYPTO_SUITE";
})(SupportedRTPConfigurationTypes || (SupportedRTPConfigurationTypes = {}));
/**
* @group Camera
*/
var SRTPCryptoSuites;
(function (SRTPCryptoSuites) {
SRTPCryptoSuites[SRTPCryptoSuites["AES_CM_128_HMAC_SHA1_80"] = 0] = "AES_CM_128_HMAC_SHA1_80";
SRTPCryptoSuites[SRTPCryptoSuites["AES_CM_256_HMAC_SHA1_80"] = 1] = "AES_CM_256_HMAC_SHA1_80";
SRTPCryptoSuites[SRTPCryptoSuites["NONE"] = 2] = "NONE";
})(SRTPCryptoSuites || (exports.SRTPCryptoSuites = SRTPCryptoSuites = {}));
// ----------
var SetupEndpointsTypes;
(function (SetupEndpointsTypes) {
SetupEndpointsTypes[SetupEndpointsTypes["SESSION_ID"] = 1] = "SESSION_ID";
SetupEndpointsTypes[SetupEndpointsTypes["CONTROLLER_ADDRESS"] = 3] = "CONTROLLER_ADDRESS";
SetupEndpointsTypes[SetupEndpointsTypes["VIDEO_SRTP_PARAMETERS"] = 4] = "VIDEO_SRTP_PARAMETERS";
SetupEndpointsTypes[SetupEndpointsTypes["AUDIO_SRTP_PARAMETERS"] = 5] = "AUDIO_SRTP_PARAMETERS";
})(SetupEndpointsTypes || (SetupEndpointsTypes = {}));
var AddressTypes;
(function (AddressTypes) {
AddressTypes[AddressTypes["ADDRESS_VERSION"] = 1] = "ADDRESS_VERSION";
AddressTypes[AddressTypes["ADDRESS"] = 2] = "ADDRESS";
AddressTypes[AddressTypes["VIDEO_RTP_PORT"] = 3] = "VIDEO_RTP_PORT";
AddressTypes[AddressTypes["AUDIO_RTP_PORT"] = 4] = "AUDIO_RTP_PORT";
})(AddressTypes || (AddressTypes = {}));
var IPAddressVersion;
(function (IPAddressVersion) {
IPAddressVersion[IPAddressVersion["IPV4"] = 0] = "IPV4";
IPAddressVersion[IPAddressVersion["IPV6"] = 1] = "IPV6";
})(IPAddressVersion || (IPAddressVersion = {}));
var SRTPParametersTypes;
(function (SRTPParametersTypes) {
SRTPParametersTypes[SRTPParametersTypes["SRTP_CRYPTO_SUITE"] = 1] = "SRTP_CRYPTO_SUITE";
SRTPParametersTypes[SRTPParametersTypes["MASTER_KEY"] = 2] = "MASTER_KEY";
SRTPParametersTypes[SRTPParametersTypes["MASTER_SALT"] = 3] = "MASTER_SALT"; // 14 bytes
})(SRTPParametersTypes || (SRTPParametersTypes = {}));
var SetupEndpointsResponseTypes;
(function (SetupEndpointsResponseTypes) {
SetupEndpointsResponseTypes[SetupEndpointsResponseTypes["SESSION_ID"] = 1] = "SESSION_ID";
SetupEndpointsResponseTypes[SetupEndpointsResponseTypes["STATUS"] = 2] = "STATUS";
SetupEndpointsResponseTypes[SetupEndpointsResponseTypes["ACCESSORY_ADDRESS"] = 3] = "ACCESSORY_ADDRESS";
SetupEndpointsResponseTypes[SetupEndpointsResponseTypes["VIDEO_SRTP_PARAMETERS"] = 4] = "VIDEO_SRTP_PARAMETERS";
SetupEndpointsResponseTypes[SetupEndpointsResponseTypes["AUDIO_SRTP_PARAMETERS"] = 5] = "AUDIO_SRTP_PARAMETERS";
SetupEndpointsResponseTypes[SetupEndpointsResponseTypes["VIDEO_SSRC"] = 6] = "VIDEO_SSRC";
SetupEndpointsResponseTypes[SetupEndpointsResponseTypes["AUDIO_SSRC"] = 7] = "AUDIO_SSRC";
})(SetupEndpointsResponseTypes || (SetupEndpointsResponseTypes = {}));
var SetupEndpointsStatus;
(function (SetupEndpointsStatus) {
SetupEndpointsStatus[SetupEndpointsStatus["SUCCESS"] = 0] = "SUCCESS";
SetupEndpointsStatus[SetupEndpointsStatus["BUSY"] = 1] = "BUSY";
SetupEndpointsStatus[SetupEndpointsStatus["ERROR"] = 2] = "ERROR";
})(SetupEndpointsStatus || (SetupEndpointsStatus = {}));
// ----------
var SelectedRTPStreamConfigurationTypes;
(function (SelectedRTPStreamConfigurationTypes) {
SelectedRTPStreamConfigurationTypes[SelectedRTPStreamConfigurationTypes["SESSION_CONTROL"] = 1] = "SESSION_CONTROL";
SelectedRTPStreamConfigurationTypes[SelectedRTPStreamConfigurationTypes["SELECTED_VIDEO_PARAMETERS"] = 2] = "SELECTED_VIDEO_PARAMETERS";
SelectedRTPStreamConfigurationTypes[SelectedRTPStreamConfigurationTypes["SELECTED_AUDIO_PARAMETERS"] = 3] = "SELECTED_AUDIO_PARAMETERS";
})(SelectedRTPStreamConfigurationTypes || (SelectedRTPStreamConfigurationTypes = {}));
var SessionControlTypes;
(function (SessionControlTypes) {
SessionControlTypes[SessionControlTypes["SESSION_IDENTIFIER"] = 1] = "SESSION_IDENTIFIER";
SessionControlTypes[SessionControlTypes["COMMAND"] = 2] = "COMMAND";
})(SessionControlTypes || (SessionControlTypes = {}));
var SessionControlCommand;
(function (SessionControlCommand) {
SessionControlCommand[SessionControlCommand["END_SESSION"] = 0] = "END_SESSION";
SessionControlCommand[SessionControlCommand["START_SESSION"] = 1] = "START_SESSION";
SessionControlCommand[SessionControlCommand["SUSPEND_SESSION"] = 2] = "SUSPEND_SESSION";
SessionControlCommand[SessionControlCommand["RESUME_SESSION"] = 3] = "RESUME_SESSION";
SessionControlCommand[SessionControlCommand["RECONFIGURE_SESSION"] = 4] = "RECONFIGURE_SESSION";
})(SessionControlCommand || (SessionControlCommand = {}));
var SelectedVideoParametersTypes;
(function (SelectedVideoParametersTypes) {
SelectedVideoParametersTypes[SelectedVideoParametersTypes["CODEC_TYPE"] = 1] = "CODEC_TYPE";
SelectedVideoParametersTypes[SelectedVideoParametersTypes["CODEC_PARAMETERS"] = 2] = "CODEC_PARAMETERS";
SelectedVideoParametersTypes[SelectedVideoParametersTypes["ATTRIBUTES"] = 3] = "ATTRIBUTES";
SelectedVideoParametersTypes[SelectedVideoParametersTypes["RTP_PARAMETERS"] = 4] = "RTP_PARAMETERS";
})(SelectedVideoParametersTypes || (SelectedVideoParametersTypes = {}));
var VideoRTPParametersTypes;
(function (VideoRTPParametersTypes) {
VideoRTPParametersTypes[VideoRTPParametersTypes["PAYLOAD_TYPE"] = 1] = "PAYLOAD_TYPE";
VideoRTPParametersTypes[VideoRTPParametersTypes["SYNCHRONIZATION_SOURCE"] = 2] = "SYNCHRONIZATION_SOURCE";
VideoRTPParametersTypes[VideoRTPParametersTypes["MAX_BIT_RATE"] = 3] = "MAX_BIT_RATE";
VideoRTPParametersTypes[VideoRTPParametersTypes["MIN_RTCP_INTERVAL"] = 4] = "MIN_RTCP_INTERVAL";
VideoRTPParametersTypes[VideoRTPParametersTypes["MAX_MTU"] = 5] = "MAX_MTU";
})(VideoRTPParametersTypes || (VideoRTPParametersTypes = {}));
var SelectedAudioParametersTypes;
(function (SelectedAudioParametersTypes) {
SelectedAudioParametersTypes[SelectedAudioParametersTypes["CODEC_TYPE"] = 1] = "CODEC_TYPE";
SelectedAudioParametersTypes[SelectedAudioParametersTypes["CODEC_PARAMETERS"] = 2] = "CODEC_PARAMETERS";
SelectedAudioParametersTypes[SelectedAudioParametersTypes["RTP_PARAMETERS"] = 3] = "RTP_PARAMETERS";
SelectedAudioParametersTypes[SelectedAudioParametersTypes["COMFORT_NOISE"] = 4] = "COMFORT_NOISE";
})(SelectedAudioParametersTypes || (SelectedAudioParametersTypes = {}));
var AudioRTPParametersTypes;
(function (AudioRTPParametersTypes) {
AudioRTPParametersTypes[AudioRTPParametersTypes["PAYLOAD_TYPE"] = 1] = "PAYLOAD_TYPE";
AudioRTPParametersTypes[AudioRTPParametersTypes["SYNCHRONIZATION_SOURCE"] = 2] = "SYNCHRONIZATION_SOURCE";
AudioRTPParametersTypes[AudioRTPParametersTypes["MAX_BIT_RATE"] = 3] = "MAX_BIT_RATE";
AudioRTPParametersTypes[AudioRTPParametersTypes["MIN_RTCP_INTERVAL"] = 4] = "MIN_RTCP_INTERVAL";
AudioRTPParametersTypes[AudioRTPParametersTypes["COMFORT_NOISE_PAYLOAD_TYPE"] = 6] = "COMFORT_NOISE_PAYLOAD_TYPE";
})(AudioRTPParametersTypes || (AudioRTPParametersTypes = {}));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isLegacySRTPOptions(options) {
return "srtp" in options;
}
/**
* @group Camera
*/
var AudioStreamingCodecType;
(function (AudioStreamingCodecType) {
AudioStreamingCodecType["PCMU"] = "PCMU";
AudioStreamingCodecType["PCMA"] = "PCMA";
AudioStreamingCodecType["AAC_ELD"] = "AAC-eld";
AudioStreamingCodecType["OPUS"] = "OPUS";
AudioStreamingCodecType["MSBC"] = "mSBC";
AudioStreamingCodecType["AMR"] = "AMR";
AudioStreamingCodecType["AMR_WB"] = "AMR-WB";
})(AudioStreamingCodecType || (exports.AudioStreamingCodecType = AudioStreamingCodecType = {}));
/**
* @group Camera
*/
var AudioStreamingSamplerate;
(function (AudioStreamingSamplerate) {
AudioStreamingSamplerate[AudioStreamingSamplerate["KHZ_8"] = 8] = "KHZ_8";
AudioStreamingSamplerate[AudioStreamingSamplerate["KHZ_16"] = 16] = "KHZ_16";
AudioStreamingSamplerate[AudioStreamingSamplerate["KHZ_24"] = 24] = "KHZ_24";
})(AudioStreamingSamplerate || (exports.AudioStreamingSamplerate = AudioStreamingSamplerate = {}));
/**
* @group Camera
*/
var StreamRequestTypes;
(function (StreamRequestTypes) {
StreamRequestTypes["RECONFIGURE"] = "reconfigure";
StreamRequestTypes["START"] = "start";
StreamRequestTypes["STOP"] = "stop";
})(StreamRequestTypes || (exports.StreamRequestTypes = StreamRequestTypes = {}));
/**
* @group Camera
*/
class RTPStreamManagement {
id;
delegate;
service;
stateChangeDelegate;
requireProxy;
disableAudioProxy;
supportedCryptoSuites;
videoOnly = false;
supportedRTPConfiguration;
supportedVideoStreamConfiguration;
supportedAudioStreamConfiguration;
activeConnection;
activeConnectionClosedListener;
sessionIdentifier = undefined;
/**
* @private private API
*/
streamStatus = 0 /* StreamingStatus.AVAILABLE */; // use _updateStreamStatus to update this property
ipVersion; // ip version for the current session
selectedConfiguration = ""; // base64 representation of the currently selected configuration
setupEndpointsResponse = ""; // response of the SetupEndpoints Characteristic
/**
* @private deprecated API
*/
audioProxy;
/**
* @private deprecated API
*/
videoProxy;
/**
* A RTPStreamManagement is considered disabled if `HomeKitCameraActive` is set to false.
* We use a closure based approach to retrieve the value of this characteristic.
* The characteristic is managed by the RecordingManagement.
*/
disabledThroughOperatingMode;
constructor(id, options, delegate, service, disabledThroughOperatingMode) {
this.id = id;
this.delegate = delegate;
this.requireProxy = options.proxy || false;
this.disableAudioProxy = options.disable_audio_proxy || false;
if (isLegacySRTPOptions(options)) {
this.supportedCryptoSuites = [options.srtp ? 0 /* SRTPCryptoSuites.AES_CM_128_HMAC_SHA1_80 */ : 2 /* SRTPCryptoSuites.NONE */];
}
else {
this.supportedCryptoSuites = options.supportedCryptoSuites;
}
if (this.supportedCryptoSuites.length === 0) {
this.supportedCryptoSuites.push(2 /* SRTPCryptoSuites.NONE */);
}
if (!options.video) {
throw new Error("Video parameters cannot be undefined in options");
}
this.supportedRTPConfiguration = RTPStreamManagement._supportedRTPConfiguration(this.supportedCryptoSuites);
this.supportedVideoStreamConfiguration = RTPStreamManagement._supportedVideoStreamConfiguration(options.video);
this.supportedAudioStreamConfiguration = this._supportedAudioStreamConfiguration(options.audio);
this.activeConnectionClosedListener = this._handleStopStream.bind(this);
this.service = service || this.constructService(id);
this.setupServiceHandlers();
this.resetSetupEndpointsResponse();
this.resetSelectedStreamConfiguration();
this.disabledThroughOperatingMode = disabledThroughOperatingMode;
}
forceStop() {
this.handleSessionClosed();
}
getService() {
return this.service;
}
handleFactoryReset() {
this.resetSelectedStreamConfiguration();
this.resetSetupEndpointsResponse();
this.service.updateCharacteristic(Characteristic_1.Characteristic.Active, true);
// on a factory reset the assumption is that all connections were already terminated and thus "handleStopStream" was already called
}
destroy() {
if (this.activeConnection) {
this._handleStopStream();
}
}
constructService(id) {
const managementService = new Service_1.Service.CameraRTPStreamManagement("", id.toString());
// this service is required only when recording is enabled. We don't really have access to this info here,
// so we just add the characteristic. Doesn't really hurt.
managementService.setCharacteristic(Characteristic_1.Characteristic.Active, true);
return managementService;
}
setupServiceHandlers() {
if (!this.service.testCharacteristic(Characteristic_1.Characteristic.Active)) {
// the active characteristic might not be present on some older configurations.
this.service.setCharacteristic(Characteristic_1.Characteristic.Active, true);
}
this.service.getCharacteristic(Characteristic_1.Characteristic.Active)
.on("change" /* CharacteristicEventTypes.CHANGE */, () => this.stateChangeDelegate?.())
.setProps({ adminOnlyAccess: [1 /* Access.WRITE */] });
// ensure that configurations are up-to-date and reflected in the characteristic values
this.service.setCharacteristic(Characteristic_1.Characteristic.SupportedRTPConfiguration, this.supportedRTPConfiguration);
this.service.setCharacteristic(Characteristic_1.Characteristic.SupportedVideoStreamConfiguration, this.supportedVideoStreamConfiguration);
this.service.setCharacteristic(Characteristic_1.Characteristic.SupportedAudioStreamConfiguration, this.supportedAudioStreamConfiguration);
this._updateStreamStatus(0 /* StreamingStatus.AVAILABLE */); // reset streaming status to available
this.service.setCharacteristic(Characteristic_1.Characteristic.SetupEndpoints, this.setupEndpointsResponse); // reset SetupEndpoints to default
this.service.getCharacteristic(Characteristic_1.Characteristic.SelectedRTPStreamConfiguration)
.on("get" /* CharacteristicEventTypes.GET */, callback => {
if (this.streamingIsDisabled()) {
callback(null, tlv.encode(1 /* SelectedRTPStreamConfigurationTypes.SESSION_CONTROL */, tlv.encode(2 /* SessionControlTypes.COMMAND */, SessionControlCommand.SUSPEND_SESSION)).toString("base64"));
return;
}
callback(null, this.selectedConfiguration);
})
.on("set" /* CharacteristicEventTypes.SET */, this._handleSelectedStreamConfigurationWrite.bind(this));
this.service.getCharacteristic(Characteristic_1.Characteristic.SetupEndpoints)
.on("get" /* CharacteristicEventTypes.GET */, callback => {
if (this.streamingIsDisabled()) {
callback(null, tlv.encode(2 /* SetupEndpointsResponseTypes.STATUS */, 2 /* SetupEndpointsStatus.ERROR */).toString("base64"));
return;
}
callback(null, this.setupEndpointsResponse);
})
.on("set" /* CharacteristicEventTypes.SET */, (value, callback, context, connection) => {
if (!connection) {
debug("Set event handler for SetupEndpoints cannot be called from plugin. Connection undefined!");
callback(-70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */);
return;
}
this.handleSetupEndpoints(value, callback, connection);
});
}
handleSessionClosed() {
this.resetSelectedStreamConfiguration();
this.resetSetupEndpointsResponse();
if (this.activeConnection) {
this.activeConnection.removeListener("closed" /* HAPConnectionEvent.CLOSED */, this.activeConnectionClosedListener);
this.activeConnection.setMaxListeners(this.activeConnection.getMaxListeners() - 1);
this.activeConnection = undefined;
}
this._updateStreamStatus(0 /* StreamingStatus.AVAILABLE */);
this.sessionIdentifier = undefined;
this.ipVersion = undefined;
if (this.videoProxy) {
this.videoProxy.destroy();
this.videoProxy = undefined;
}
if (this.audioProxy) {
this.audioProxy.destroy();
this.audioProxy = undefined;
}
}
streamingIsDisabled(callback) {
if (!this.service.getCharacteristic(Characteristic_1.Characteristic.Active).value) {
callback && callback(new hapStatusError_1.HapStatusError(-70412 /* HAPStatus.NOT_ALLOWED_IN_CURRENT_STATE */));
return true;
}
if (this.disabledThroughOperatingMode?.()) {
callback && callback(new hapStatusError_1.HapStatusError(-70412 /* HAPStatus.NOT_ALLOWED_IN_CURRENT_STATE */));
return true;
}
return false;
}
_handleSelectedStreamConfigurationWrite(value, callback) {
if (this.streamingIsDisabled(callback)) {
return;
}
const data = Buffer.from(value, "base64");
const objects = tlv.decode(data);
const sessionControl = tlv.decode(objects[1 /* SelectedRTPStreamConfigurationTypes.SESSION_CONTROL */]);
const sessionIdentifier = uuid.unparse(sessionControl[1 /* SessionControlTypes.SESSION_IDENTIFIER */]);
const requestType = sessionControl[2 /* SessionControlTypes.COMMAND */][0];
if (sessionIdentifier !== this.sessionIdentifier) {
debug(`Received unknown session Identifier with request to ${SessionControlCommand[requestType]}`);
callback(-70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */);
return;
}
this.selectedConfiguration = value;
// intercept the callback chain to check if an error occurred.
const streamCallback = (error, writeResponse) => {
callback(error, writeResponse); // does not support writeResponse, but how knows what comes in the future.
if (error) {
this.handleSessionClosed();
}
};
switch (requestType) {
case SessionControlCommand.START_SESSION: {
const selectedVideoParameters = tlv.decode(objects[2 /* SelectedRTPStreamConfigurationTypes.SELECTED_VIDEO_PARAMETERS */]);
const selectedAudioParameters = tlv.decode(objects[3 /* SelectedRTPStreamConfigurationTypes.SELECTED_AUDIO_PARAMETERS */]);
this._handleStartStream(selectedVideoParameters, selectedAudioParameters, streamCallback);
break;
}
case SessionControlCommand.RECONFIGURE_SESSION: {
const reconfiguredVideoParameters = tlv.decode(objects[2 /* SelectedRTPStreamConfigurationTypes.SELECTED_VIDEO_PARAMETERS */]);
this.handleReconfigureStream(reconfiguredVideoParameters, streamCallback);
break;
}
case SessionControlCommand.END_SESSION:
this._handleStopStream(streamCallback);
break;
case SessionControlCommand.RESUME_SESSION:
case SessionControlCommand.SUSPEND_SESSION:
default:
debug(`Unhandled request type ${SessionControlCommand[requestType]}`);
callback(-70410 /* HAPStatus.INVALID_VALUE_IN_REQUEST */);
return;
}
}
_handleStartStream(videoConfiguration, audioConfiguration, callback) {
// selected video configuration
// noinspection JSUnusedLocalSymbols
const videoCodec = videoConfiguration[1 /* SelectedVideoParametersTypes.CODEC_TYPE */]; // always 0x00 for h264
const videoParametersTLV = videoConfiguration[2 /* SelectedVideoParametersTypes.CODEC_PARAMETERS */];
const videoAttributesTLV = videoConfiguration[3 /* SelectedVideoParametersTypes.ATTRIBUTES */];
const videoRTPParametersTLV = videoConfiguration[4 /* SelectedVideoParametersTypes.RTP_PARAMETERS */];
// video parameters
const videoParameters = tlv.decode(videoParametersTLV);
const h264Profile = videoParameters[1 /* VideoCodecParametersTypes.PROFILE_ID */][0];
const h264Level = videoParameters[2 /* VideoCodecParametersTypes.LEVEL */][0];
const packetizationMode = videoParameters[3 /* VideoCodecParametersTypes.PACKETIZATION_MODE */][0];
const cvoEnabled = videoParameters[4 /* VideoCodecParametersTypes.CVO_ENABLED */];
let cvoId = undefined;
if (cvoEnabled && cvoEnabled[0] === 1 /* VideoCodecCVO.SUPPORTED */) {
cvoId = videoParameters[5 /* VideoCodecParametersTypes.CVO_ID */].readUInt8(0);
}
// video attributes
const videoAttributes = tlv.decode(videoAttributesTLV);
const width = videoAttributes[1 /* VideoAttributesTypes.IMAGE_WIDTH */].readUInt16LE(0);
const height = videoAttributes[2 /* VideoAttributesTypes.IMAGE_HEIGHT */].readUInt16LE(0);
const frameRate = videoAttributes[3 /* VideoAttributesTypes.FRAME_RATE */].readUInt8(0);
// video rtp parameters
const videoRTPParameters = tlv.decode(videoRTPParametersTLV);
const videoPayloadType = videoRTPParameters[1 /* VideoRTPParametersTypes.PAYLOAD_TYPE */].readUInt8(0); // 99
const videoSSRC = videoRTPParameters[2 /* VideoRTPParametersTypes.SYNCHRONIZATION_SOURCE */].readUInt32LE(0);
const videoMaximumBitrate = videoRTPParameters[3 /* VideoRTPParametersTypes.MAX_BIT_RATE */].readUInt16LE(0);
const videoRTCPInterval = videoRTPParameters[4 /* VideoRTPParametersTypes.MIN_RTCP_INTERVAL */].readFloatLE(0);
let maxMTU = this.ipVersion === "ipv6" ? 1228 : 1378; // default values ipv4: 1378 bytes; ipv6: 1228 bytes
if (videoRTPParameters[5 /* VideoRTPParametersTypes.MAX_MTU */]) {
maxMTU = videoRTPParameters[5 /* VideoRTPParametersTypes.MAX_MTU */].readUInt16LE(0);
}
// selected audio configuration
const audioCodec = audioConfiguration[1 /* SelectedAudioParametersTypes.CODEC_TYPE */][0];
const audioParametersTLV = audioConfiguration[2 /* SelectedAudioParametersTypes.CODEC_PARAMETERS */];
const audioRTPParametersTLV = audioConfiguration[3 /* SelectedAudioParametersTypes.RTP_PARAMETERS */];
const comfortNoise = !!audioConfiguration[4 /* SelectedAudioParametersTypes.COMFORT_NOISE */].readUInt8(0);
// audio parameters
const audioParameters = tlv.decode(audioParametersTLV);
const channels = audioParameters[1 /* AudioCodecParametersTypes.CHANNEL */][0];
const audioBitrate = audioParameters[2 /* AudioCodecParametersTypes.BIT_RATE */][0];
const samplerate = audioParameters[3 /* AudioCodecParametersTypes.SAMPLE_RATE */][0];
const rtpPacketTime = audioParameters[4 /* AudioCodecParametersTypes.PACKET_TIME */].readUInt8(0);
// audio rtp parameters
const audioRTPParameters = tlv.decode(audioRTPParametersTLV);
const audioPayloadType = audioRTPParameters[1 /* AudioRTPParametersTypes.PAYLOAD_TYPE */].readUInt8(0); // 110
const audioSSRC = audioRTPParameters[2 /* AudioRTPParametersTypes.SYNCHRONIZATION_SOURCE */].readUInt32LE(0);
const audioMaximumBitrate = audioRTPParameters[3 /* AudioRTPParametersTypes.MAX_BIT_RATE */].readUInt16LE(0);
const audioRTCPInterval = audioRTPParameters[4 /* AudioRTPParametersTypes.MIN_RTCP_INTERVAL */].readFloatLE(0);
const comfortNoisePayloadType = audioRTPParameters[6 /* AudioRTPParametersTypes.COMFORT_NOISE_PAYLOAD_TYPE */].readUInt8(0); // 13
if (this.requireProxy) {
this.videoProxy.setOutgoingPayloadType(videoPayloadType);
if (!this.disableAudioProxy) {
this.audioProxy.setOutgoingPayloadType(audioPayloadType);
}
}
const videoInfo = {
codec: videoCodec.readUInt8(0),
profile: h264Profile,
level: h264Level,
packetizationMode: packetizationMode,
cvoId: cvoId,
width: width,
height: height,
fps: frameRate,
pt: videoPayloadType,
ssrc: videoSSRC,
max_bit_rate: videoMaximumBitrate,
rtcp_interval: videoRTCPInterval,
mtu: maxMTU,
};
let audioCodecName;
let samplerateNum;
switch (audioCodec) {
case 0 /* AudioCodecTypes.PCMU */:
audioCodecName = "PCMU" /* AudioStreamingCodecType.PCMU */;
break;
case 1 /* AudioCodecTypes.PCMA */:
audioCodecName = "PCMA" /* AudioStreamingCodecType.PCMA */;
break;
case 2 /* AudioCodecTypes.AAC_ELD */:
audioCodecName = "AAC-eld" /* AudioStreamingCodecType.AAC_ELD */;
break;
case 3 /* AudioCodecTypes.OPUS */:
audioCodecName = "OPUS" /* AudioStreamingCodecType.OPUS */;
break;
case 4 /* AudioCodecTypes.MSBC */:
audioCodecName = "mSBC" /* AudioStreamingCodecType.MSBC */;
break;
case 5 /* AudioCodecTypes.AMR */:
audioCodecName = "AMR" /* AudioStreamingCodecType.AMR */;
break;
case 6 /* AudioCodecTypes.AMR_WB */:
audioCodecName = "AMR-WB" /* AudioStreamingCodecType.AMR_WB */;
break;
default:
throw new Error(`Encountered unknown selected audio codec ${audioCodec}`);
}
switch (samplerate) {
case 0 /* AudioSamplerate.KHZ_8 */:
samplerateNum = 8;
break;
case 1 /* AudioSamplerate.KHZ_16 */:
samplerateNum = 16;
break;
case 2 /* AudioSamplerate.KHZ_24 */:
samplerateNum = 24;
break;
default:
throw new Error(`Encountered unknown selected audio samplerate ${samplerate}`);
}
const audioInfo = {
codec: audioCodecName,
channel: channels,
bit_rate: audioBitrate,
sample_rate: samplerateNum,
packet_time: rtpPacketTime,
pt: audioPayloadType,
ssrc: audioSSRC,
max_bit_rate: audioMaximumBitrate,
rtcp_interval: audioRTCPInterval,
comfort_pt: comfortNoisePayloadType,
comfortNoiseEnabled: comfortNoise,
};
const request = {
sessionID: this.sessionIdentifier,
type: "start" /* StreamRequestTypes.START */,
video: videoInfo,
audio: audioInfo,
};
this.delegate.handleStreamRequest(request, error => callback(error));
}
handleReconfigureStream(videoConfiguration, callback) {
// selected video configuration
const videoAttributesTLV = videoConfiguration[3 /* SelectedVideoParametersTypes.ATTRIBUTES */];
const videoRTPParametersTLV = videoConfiguration[4 /* SelectedVideoParametersTypes.RTP_PARAMETERS */];
// video attributes
const videoAttributes = tlv.decode(videoAttributesTLV);
const width = videoAttributes[1 /* VideoAttributesTypes.IMAGE_WIDTH */].readUInt16LE(0);
const height = videoAttributes[2 /* VideoAttributesTypes.IMAGE_HEIGHT */].readUInt16LE(0);
const frameRate = videoAttributes[3 /* VideoAttributesTypes.FRAME_RATE */].readUInt8(0);
// video rtp parameters
const videoRTPParameters = tlv.decode(videoRTPParametersTLV);
const videoMaximumBitrate = videoRTPParameters[3 /* VideoRTPParametersTypes.MAX_BIT_RATE */].readUInt16LE(0);
// seems to be always zero, use default of 0.5
const videoRTCPInterval = videoRTPParameters[4 /* VideoRTPParametersTypes.MIN_RTCP_INTERVAL */].readFloatLE(0) || 0.5;
const reconfiguredVideoInfo = {
width: width,
height: height,
fps: frameRate,
max_bit_rate: videoMaximumBitrate,
rtcp_interval: videoRTCPInterval,
};
const request = {
sessionID: this.sessionIdentifier,
type: "reconfigure" /* StreamRequestTypes.RECONFIGURE */,
video: reconfiguredVideoInfo,
};
this.delegate.handleStreamRequest(request, error => callback(error));
}
_handleStopStream(callback) {
const request = {
sessionID: this.sessionIdentifier, // save sessionIdentifier before handleSessionClosed is called
type: "stop" /* StreamRequestTypes.STOP */,
};
this.handleSessionClosed();
this.delegate.handleStreamRequest(request, error => callback ? callback(error) : undefined);
}
handleSetupEndpoints(value, callback, connection) {
if (this.streamingIsDisabled(callback)) {
return;
}
const data = Buffer.from(value, "base64");
const objects = tlv.decode(data);
const sessionIdentifier = uuid.unparse(objects[1 /* SetupEndpointsTypes.SESSION_ID */]);
if (this.streamStatus !== 0 /* StreamingStatus.AVAILABLE */) {
this.setupEndpointsResponse = tlv.encode(1 /* SetupEndpointsResponseTypes.SESSION_ID */, uuid.write(sessionIdentifier), 2 /* SetupEndpointsResponseTypes.STATUS */, 1 /* SetupEndpointsStatus.BUSY */).toString("base64");
callback();
return;
}
(0, assert_1.default)(this.activeConnection == null, "Found non-nil `activeConnection` when trying to setup streaming endpoints, even though streamStatus is reported to be AVAILABLE!");
this.activeConnection = connection;
this.activeConnection.setMaxListeners(this.activeConnection.getMaxListeners() + 1);
this.activeConnection.on("closed" /* HAPConnectionEvent.CLOSED */, this.activeConnectionClosedListener);
this.sessionIdentifier = sessionIdentifier;
this._updateStreamStatus(1 /* StreamingStatus.IN_USE */);
// Address
const targetAddressPayload = objects[3 /* SetupEndpointsTypes.CONTROLLER_ADDRESS */];
const processedAddressInfo = tlv.decode(targetAddressPayload);
const addressVersion = processedAddressInfo[1 /* AddressTypes.ADDRESS_VERSION */][0];
const controllerAddress = processedAddressInfo[2 /* AddressTypes.ADDRESS */].toString("utf8");
const targetVideoPort = processedAddressInfo[3 /* AddressTypes.VIDEO_RTP_PORT */].readUInt16LE(0);
const targetAudioPort = processedAddressInfo[4 /* AddressTypes.AUDIO_RTP_PORT */].readUInt16LE(0);
// Video SRTP Params
const videoSRTPPayload = objects[4 /* SetupEndpointsTypes.VIDEO_SRTP_PARAMETERS */];
const processedVideoInfo = tlv.decode(videoSRTPPayload);
const videoCryptoSuite = processedVideoInfo[1 /* SRTPParametersTypes.SRTP_CRYPTO_SUITE */][0];
const videoMasterKey = processedVideoInfo[2 /* SRTPParametersTypes.MASTER_KEY */];
const videoMasterSalt = processedVideoInfo[3 /* SRTPParametersTypes.MASTER_SALT */];
// Audio SRTP Params
const audioSRTPPayload = objects[5 /* SetupEndpointsTypes.AUDIO_SRTP_PARAMETERS */];
const processedAudioInfo = tlv.decode(audioSRTPPayload);
const audioCryptoSuite = processedAudioInfo[1 /* SRTPParametersTypes.SRTP_CRYPTO_SUITE */][0];
const audioMasterKey = processedAudioInfo[2 /* SRTPParametersTypes.MASTER_KEY */];
const audioMasterSalt = processedAudioInfo[3 /* SRTPParametersTypes.MASTER_SALT */];
debug("Session: ", sessionIdentifier, "\nControllerAddress: ", controllerAddress, "\nVideoPort: ", targetVideoPort, "\nAudioPort: ", targetAudioPort, "\nVideo Crypto: ", videoCryptoSuite, "\nVideo Master Key: ", videoMasterKey, "\nVideo Master Salt: ", videoMasterSalt, "\nAudio Crypto: ", audioCryptoSuite, "\nAudio Master Key: ", audioMasterKey, "\nAudio Master Salt: ", audioMasterSalt);
const prepareRequest = {
sessionID: sessionIdentifier,
sourceAddress: connection.localAddress,
targetAddress: controllerAddress,
addressVersion: addressVersion === 1 /* IPAddressVersion.IPV6 */ ? "ipv6" : "ipv4",
video: {
port: targetVideoPort,
srtpCryptoSuite: videoCryptoSuite,
srtp_key: videoMasterKey,
srtp_salt: videoMasterSalt,
},
audio: {
port: targetAudioPort,
srtpCryptoSuite: audioCryptoSuite,
srtp_key: audioMasterKey,
srtp_salt: audioMasterSalt,
},
};
const promises = [];
if (this.requireProxy) {
prepareRequest.targetAddress = connection.getLocalAddress(addressVersion === 1 /* IPAddressVersion.IPV6 */ ? "ipv6" : "ipv4"); // ip versions must be the same
this.videoProxy = new RTPProxy_1.default({
outgoingAddress: controllerAddress,
outgoingPort: targetVideoPort,
outgoingSSRC: crypto_1.default.randomBytes(4).readUInt32LE(0), // videoSSRC
disabled: false,
});
promises.push(this.videoProxy.setup().then(() => {
prepareRequest.video.proxy_rtp = this.videoProxy.incomingRTPPort();
prepareRequest.video.proxy_rtcp = this.videoProxy.incomingRTCPPort();
}));
if (!this.disableAudioProxy) {
this.audioProxy = new RTPProxy_1.default({
outgoingAddress: controllerAddress,
outgoingPort: targetAudioPort,
outgoingSSRC: crypto_1.default.randomBytes(4).readUInt32LE(0), // audioSSRC
disabled: this.videoOnly,
});
promises.push(this.audioProxy.setup().then(() => {
prepareRequest.audio.proxy_rtp = this.audioProxy.incomingRTPPort();
prepareRequest.audio.proxy_rtcp = this.audioProxy.incomingRTCPPort();
}));
}
}
Promise.all(promises).then(() => {
this.delegate.prepareStream(prepareRequest, (0, once_1.once)((error, response) => {
if (error || !response) {
debug(`PrepareStream request encountered an error: ${error ? error.message : undefined}`);
this.setupEndpointsResponse = tlv.encode(1 /* SetupEndpointsResponseTypes.SESSION_ID */, uuid.write(sessionIdentifier), 2 /* SetupEndpointsResponseTypes.STATUS */, 2 /* SetupEndpointsStatus.ERROR */).toString("base64");
this.handleSessionClosed();
callback(error);
}
else {
this.generateSetupEndpointResponse(connection, sessionIdentifier, prepareRequest, response, callback);
}
}));
});
}
generateSetupEndpointResponse(connection, identifier, request, response, callback) {
let address;
let addressVersion = request.addressVersion;
let videoPort;
let audioPort;
let videoCryptoSuite;
let videoSRTPKey;
let videoSRTPSalt;
let audioCryptoSuite;
let audioSRTPKey;
let audioSRTPSalt;
let videoSSRC;
let audioSSRC;
if (!this.videoOnly && !response.audio) {
throw new Error("Audio was enabled but not supplied in PrepareStreamResponse!");
}
// Provide default values if audio was not supplied
const audio = response.audio || {
port: request.audio.port,
ssrc: controller_1.CameraController.generateSynchronisationSource(),
srtp_key: request.audio.srtp_key,
srtp_salt: request.audio.srtp_salt,
};
if (!this.requireProxy) {
const videoInfo = response.video;
const audioInfo = audio;
if (response.addressOverride) {
addressVersion = net_1.default.isIPv4(response.addressOverride) ? "ipv4" : "ipv6";
address = response.addressOverride;
}
else {
address = connection.getLocalAddress(addressVersion);
}
if (request.addressVersion !== addressVersion) {
throw new Error(`Incoming and outgoing ip address versions must match! Expected ${request.addressVersion} but got ${addressVersion}`);
}
videoPort = videoInfo.port;
audioPort = audioInfo.port;
if (request.video.srtpCryptoSuite !== 2 /* SRTPCryptoSuites.NONE */
&& (videoInfo.srtp_key === undefined || videoInfo.srtp_salt === undefined)) {
throw new Error("SRTP was selected for the prepared video stream, but no 'srtp_key' or 'srtp_salt' was specified!");
}
if (request.audio.srtpCryptoSuite !== 2 /* SRTPCryptoSuites.NONE */
&& (audioInfo.srtp_key === undefined || audioInfo.srtp_salt === undefined)) {
throw new Error("SRTP was selected for the prepared audio stream, but no 'srtp_key' or 'srtp_salt' was specified!");
}
videoCryptoSuite = request.video.srtpCryptoSuite;
videoSRTPKey = videoInfo.srtp_key || Buffer.alloc(0); // key and salt are zero-length for cryptoSuite = NONE
videoSRTPSalt = videoInfo.srtp_salt || Buffer.alloc(0);
audioCryptoSuite = request.audio.srtpCryptoSuite;
audioSRTPKey = audioInfo.srtp_key || Buffer.alloc(0); // key and salt are zero-length for cryptoSuite = NONE
audioSRTPSalt = audioInfo.srtp_salt || Buffer.alloc(0);
videoSSRC = videoInfo.ssrc;
audioSSRC = audioInfo.ssrc;
}
else {
const videoInfo = response.video;
address = connection.getLocalAddress(request.addressVersion);
videoCryptoSuite = 2 /* SRTPCryptoSuites.NONE */;
videoSRTPKey = Buffer.alloc(0);
videoSRTPSalt = Buffer.alloc(0);
audioCryptoSuite = 2 /* SRTPCryptoSuites.NONE */;
audioSRTPKey = Buffer.alloc(0);
audioSRTPSalt = Buffer.alloc(0);
this.videoProxy.setIncomingPayloadType(videoInfo.proxy_pt);
this.videoProxy.setServerAddress(videoInfo.proxy_server_address);
this.videoProxy.setServerRTPPort(videoInfo.proxy_server_rtp);
this.videoProxy.setServerRTCPPort(videoInfo.proxy_server_rtcp);
videoPort = this.videoProxy.outgoingLocalPort();
videoSSRC = this.videoProxy.outgoingSSRC;
if (!this.disableAudioProxy) {
const audioInfo = response.audio;
this.audioProxy.setIncomingPayloadType(audioInfo.proxy_pt);
this.audioProxy.setServerAddress(audioInfo.proxy_server_address);
this.audioProxy.setServerRTPPort(audioInfo.proxy_server_rtp);
this.audioProxy.setServerRTCPPort(audioInfo.proxy_server_rtcp);
audioPort = this.audioProxy.outgoingLocalPort();
audioSSRC = this.audioProxy.outgoingSSRC;
}
else {
const audioInfo = response.audio;
audioPort = audioInfo.port;
audioSSRC = audioInfo.ssrc;
}
}
this.ipVersion = addressVersion; // we need to save this in order to calculate some default mtu values later
const accessoryAddress = tlv.encode(1 /* AddressTypes.ADDRESS_VERSION */, addressVersion === "ipv4" ? 0 /* IPAddressVersion.IPV4 */ : 1 /* IPAddressVersion.IPV6 */, 2 /* AddressTypes.ADDRESS */, address, 3 /* AddressTypes.VIDEO_RTP_PORT */, tlv.writeUInt16(videoPort), 4 /* AddressTypes.AUDIO_RTP_PORT */, tlv.writeUInt16(audioPort));
const videoSRTPParameters = tlv.encode(1 /* SRTPParametersTypes.SRTP_CRYPTO_SUITE */, videoCryptoSuite, 2 /* SRTPParametersTypes.MASTER_KEY */, videoSRTPKey, 3 /* SRTPParametersTypes.MASTER_SALT */, videoSRTPSalt);
const audioSRTPParameters = tlv.encode(1 /* SRTPParametersTypes.SRTP_CRYPTO_SUITE */, audioCryptoSuite, 2 /* SRTPParametersTypes.MASTER_KEY */, audioSRTPKey, 3 /* SRTPParametersTypes.MASTER_SALT */, audioSRTPSalt);
this.setupEndpointsResponse = tlv.encode(1 /* SetupEndpointsResponseTypes.SESSION_ID */, uuid.write(identifier), 2 /* SetupEndpointsResponseTypes.STATUS */, 0 /* SetupEndpointsStatus.SUCCESS */, 3 /* SetupEndpointsResponseTypes.ACCESSORY_ADDRESS */, accessoryAddress, 4 /* SetupEndpointsResponseTypes.VIDEO_SRTP_PARAMETERS */, videoSRTPParameters, 5 /* SetupEndpointsResponseTypes.AUDIO_SRTP_PARAMETERS */, audioSRTPParameters, 6 /* SetupEndpointsResponseTypes.VIDEO_SSRC */, tlv.writeUInt32(videoSSRC), 7 /* SetupEndpointsResponseTypes.AUDIO_SSRC */, tlv.writeUInt32(audioSSRC)).toString("base64");
callback();
}
_updateStreamStatus(status) {
this.streamStatus = status;
this.service.updateCharacteristic(Characteristic_1.Characteristic.StreamingStatus, tlv.encode(1 /* StreamingStatusTypes.STATUS */, this.streamStatus).toString("base64"));
}
static _supportedRTPConfiguration(supportedCryptoSuites) {
if (supportedCryptoSuites.length === 1 && supportedCryptoSuites[0] === 2 /* SRTPCryptoSuites.NONE */) {
debug("Client claims it doesn't support SRTP. The stream may stops working with future iOS releases.");
}
return tlv.encode(2 /* SupportedRTPConfigurationTypes.SRTP_CRYPTO_SUITE */, supportedCryptoSuites).toString("base64");
}
static _supportedVideoStreamConfiguration(videoOptions) {
if (!videoOptions.codec) {
throw new Error("Video codec cannot be undefined");
}
if (!videoOptions.resolutions) {
throw new Error("Video resolutions cannot be undefined");
}
let codecParameters = tlv.encode(1 /* VideoCodecParametersTypes.PROFILE_ID */, videoOptions.codec.profiles, 2 /* VideoCodecParametersTypes.LEVEL */, videoOptions.codec.levels, 3 /* VideoCodecParametersTypes.PACKETIZATION_MODE */, 0 /* VideoCodecPacketizationMode.NON_INTERLEAVED */);
if (videoOptions.cvoId != null) {
codecParameters = Buffer.concat([
codecParameters,
tlv.encode(4 /* VideoCodecParametersTypes.CVO_ENABLED */, 1 /* VideoCodecCVO.SUPPORTED */, 5 /* VideoCodecParametersTypes.CVO_ID */, videoOptions.cvoId),
]);
}
const videoStreamConfiguration = tlv.encode(1 /* VideoCodecConfigurationTypes.CODEC_TYPE */, 0 /* VideoCodecType.H264 */, 2 /*