livekit-client
Advanced tools
Comparing version 1.15.6 to 1.15.7
@@ -1,2 +0,2 @@ | ||
!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";function e(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{c(r.next(e))}catch(e){o(e)}}function a(e){try{c(r.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;var t,n,r,i="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},o={exports:{}};n=i,r=function(){var e=function(){},t="undefined",n=typeof window!==t&&typeof window.navigator!==t&&/Trident\/|MSIE /.test(window.navigator.userAgent),r=["trace","debug","info","warn","error"];function i(e,t){var n=e[t];if("function"==typeof n.bind)return n.bind(e);try{return Function.prototype.bind.call(n,e)}catch(t){return function(){return Function.prototype.apply.apply(n,[e,arguments])}}}function o(){console.log&&(console.log.apply?console.log.apply(console,arguments):Function.prototype.apply.apply(console.log,[console,arguments])),console.trace&&console.trace()}function s(t,n){for(var i=0;i<r.length;i++){var o=r[i];this[o]=i<t?e:this.methodFactory(o,t,n)}this.log=this.debug}function a(e,n,r){return function(){typeof console!==t&&(s.call(this,n,r),this[e].apply(this,arguments))}}function c(r,s,c){return function(r){return"debug"===r&&(r="log"),typeof console!==t&&("trace"===r&&n?o:void 0!==console[r]?i(console,r):void 0!==console.log?i(console,"log"):e)}(r)||a.apply(this,arguments)}function d(e,n,i){var o,a=this;n=null==n?"WARN":n;var d="loglevel";function u(){var e;if(typeof window!==t&&d){try{e=window.localStorage[d]}catch(e){}if(typeof e===t)try{var n=window.document.cookie,r=n.indexOf(encodeURIComponent(d)+"=");-1!==r&&(e=/^([^;]+)/.exec(n.slice(r))[1])}catch(e){}return void 0===a.levels[e]&&(e=void 0),e}}"string"==typeof e?d+=":"+e:"symbol"==typeof e&&(d=void 0),a.name=e,a.levels={TRACE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4,SILENT:5},a.methodFactory=i||c,a.getLevel=function(){return o},a.setLevel=function(n,i){if("string"==typeof n&&void 0!==a.levels[n.toUpperCase()]&&(n=a.levels[n.toUpperCase()]),!("number"==typeof n&&n>=0&&n<=a.levels.SILENT))throw"log.setLevel() called with invalid level: "+n;if(o=n,!1!==i&&function(e){var n=(r[e]||"silent").toUpperCase();if(typeof window!==t&&d){try{return void(window.localStorage[d]=n)}catch(e){}try{window.document.cookie=encodeURIComponent(d)+"="+n+";"}catch(e){}}}(n),s.call(a,n,e),typeof console===t&&n<a.levels.SILENT)return"No console available for logging"},a.setDefaultLevel=function(e){n=e,u()||a.setLevel(e,!1)},a.resetLevel=function(){a.setLevel(n,!1),function(){if(typeof window!==t&&d){try{return void window.localStorage.removeItem(d)}catch(e){}try{window.document.cookie=encodeURIComponent(d)+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC"}catch(e){}}}()},a.enableAll=function(e){a.setLevel(a.levels.TRACE,e)},a.disableAll=function(e){a.setLevel(a.levels.SILENT,e)};var y=u();null==y&&(y=n),a.setLevel(y,!1)}var u=new d,y={};u.getLogger=function(e){if("symbol"!=typeof e&&"string"!=typeof e||""===e)throw new TypeError("You must supply a name when creating a logger.");var t=y[e];return t||(t=y[e]=new d(e,u.getLevel(),u.methodFactory)),t};var h=typeof window!==t?window.log:void 0;return u.noConflict=function(){return typeof window!==t&&window.log===u&&(window.log=h),u},u.getLoggers=function(){return y},u.default=u,u},(t=o).exports?t.exports=r():n.log=r();var s,a=o.exports;!function(e){e[e.trace=0]="trace",e[e.debug=1]="debug",e[e.info=2]="info",e[e.warn=3]="warn",e[e.error=4]="error",e[e.silent=5]="silent"}(s||(s={}));a.getLogger("livekit").setDefaultLevel(s.info);const c=a.getLogger("lk-e2ee"),d="AES-GCM",u={key:10,delta:3,audio:1,empty:0},y={sharedKey:!1,ratchetSalt:"LKFrameEncryptionKey",ratchetWindowSize:8,failureTolerance:10};class h extends Error{constructor(e,t){super(t||"an error has occured"),this.code=e}}var l,p,f,v,g,m;!function(e){e.PermissionDenied="PermissionDenied",e.NotFound="NotFound",e.DeviceInUse="DeviceInUse",e.Other="Other"}(l||(l={})),function(e){e.getFailure=function(t){if(t&&"name"in t)return"NotFoundError"===t.name||"DevicesNotFoundError"===t.name?e.NotFound:"NotAllowedError"===t.name||"PermissionDeniedError"===t.name?e.PermissionDenied:"NotReadableError"===t.name||"TrackStartError"===t.name?e.DeviceInUse:e.Other}}(l||(l={})),function(e){e[e.InvalidKey=0]="InvalidKey",e[e.MissingKey=1]="MissingKey",e[e.InternalError=2]="InternalError"}(p||(p={}));class w extends h{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:p.InternalError;super(40,e),this.reason=t}}!function(e){e.SetKey="setKey",e.RatchetRequest="ratchetRequest",e.KeyRatcheted="keyRatcheted"}(f||(f={})),function(e){e.KeyRatcheted="keyRatcheted"}(v||(v={})),function(e){e.ParticipantEncryptionStatusChanged="participantEncryptionStatusChanged",e.EncryptionError="encryptionError"}(g||(g={})),function(e){e.Error="cryptorError"}(m||(m={}));var I,S={exports:{}},L="object"==typeof Reflect?Reflect:null,b=L&&"function"==typeof L.apply?L.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};I=L&&"function"==typeof L.ownKeys?L.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var E=Number.isNaN||function(e){return e!=e};function k(){k.init.call(this)}S.exports=k,S.exports.once=function(e,t){return new Promise((function(n,r){function i(n){e.removeListener(t,o),r(n)}function o(){"function"==typeof e.removeListener&&e.removeListener("error",i),n([].slice.call(arguments))}F(e,t,o,{once:!0}),"error"!==t&&function(e,t,n){"function"==typeof e.on&&F(e,"error",t,n)}(e,i,{once:!0})}))},k.EventEmitter=k,k.prototype._events=void 0,k.prototype._eventsCount=0,k.prototype._maxListeners=void 0;var K=10;function C(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function _(e){return void 0===e._maxListeners?k.defaultMaxListeners:e._maxListeners}function A(e,t,n,r){var i,o,s,a;if(C(n),void 0===(o=e._events)?(o=e._events=Object.create(null),e._eventsCount=0):(void 0!==o.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),o=e._events),s=o[t]),void 0===s)s=o[t]=n,++e._eventsCount;else if("function"==typeof s?s=o[t]=r?[n,s]:[s,n]:r?s.unshift(n):s.push(n),(i=_(e))>0&&s.length>i&&!s.warned){s.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=s.length,a=c,console&&console.warn&&console.warn(a)}return e}function T(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function R(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},i=T.bind(r);return i.listener=n,r.wrapFn=i,i}function P(e,t,n){var r=e._events;if(void 0===r)return[];var i=r[t];return void 0===i?[]:"function"==typeof i?n?[i.listener||i]:[i]:n?function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(i):U(i,i.length)}function x(e){var t=this._events;if(void 0!==t){var n=t[e];if("function"==typeof n)return 1;if(void 0!==n)return n.length}return 0}function U(e,t){for(var n=new Array(t),r=0;r<t;++r)n[r]=e[r];return n}function F(e,t,n,r){if("function"==typeof e.on)r.once?e.once(t,n):e.on(t,n);else{if("function"!=typeof e.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof e);e.addEventListener(t,(function i(o){r.once&&e.removeEventListener(t,i),n(o)}))}}Object.defineProperty(k,"defaultMaxListeners",{enumerable:!0,get:function(){return K},set:function(e){if("number"!=typeof e||e<0||E(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");K=e}}),k.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},k.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||E(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},k.prototype.getMaxListeners=function(){return _(this)},k.prototype.emit=function(e){for(var t=[],n=1;n<arguments.length;n++)t.push(arguments[n]);var r="error"===e,i=this._events;if(void 0!==i)r=r&&void 0===i.error;else if(!r)return!1;if(r){var o;if(t.length>0&&(o=t[0]),o instanceof Error)throw o;var s=new Error("Unhandled error."+(o?" ("+o.message+")":""));throw s.context=o,s}var a=i[e];if(void 0===a)return!1;if("function"==typeof a)b(a,this,t);else{var c=a.length,d=U(a,c);for(n=0;n<c;++n)b(d[n],this,t)}return!0},k.prototype.addListener=function(e,t){return A(this,e,t,!1)},k.prototype.on=k.prototype.addListener,k.prototype.prependListener=function(e,t){return A(this,e,t,!0)},k.prototype.once=function(e,t){return C(t),this.on(e,R(this,e,t)),this},k.prototype.prependOnceListener=function(e,t){return C(t),this.prependListener(e,R(this,e,t)),this},k.prototype.removeListener=function(e,t){var n,r,i,o,s;if(C(t),void 0===(r=this._events))return this;if(void 0===(n=r[e]))return this;if(n===t||n.listener===t)0==--this._eventsCount?this._events=Object.create(null):(delete r[e],r.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(i=-1,o=n.length-1;o>=0;o--)if(n[o]===t||n[o].listener===t){s=n[o].listener,i=o;break}if(i<0)return this;0===i?n.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(n,i),1===n.length&&(r[e]=n[0]),void 0!==r.removeListener&&this.emit("removeListener",e,s||t)}return this},k.prototype.off=k.prototype.removeListener,k.prototype.removeAllListeners=function(e){var t,n,r;if(void 0===(n=this._events))return this;if(void 0===n.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==n[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete n[e]),this;if(0===arguments.length){var i,o=Object.keys(n);for(r=0;r<o.length;++r)"removeListener"!==(i=o[r])&&this.removeAllListeners(i);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(void 0!==t)for(r=t.length-1;r>=0;r--)this.removeListener(e,t[r]);return this},k.prototype.listeners=function(e){return P(this,e,!0)},k.prototype.rawListeners=function(e){return P(this,e,!1)},k.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):x.call(e,t)},k.prototype.listenerCount=x,k.prototype.eventNames=function(){return this._eventsCount>0?I(this._events):[]};var O=S.exports;function M(e,t){const n=(new TextEncoder).encode(t);switch(e){case"HKDF":return{name:"HKDF",salt:n,hash:"SHA-256",info:new ArrayBuffer(128)};case"PBKDF2":return{name:"PBKDF2",salt:n,hash:"SHA-256",iterations:1e5};default:throw new Error("algorithm ".concat(e," is currently unsupported"))}}function D(t,n){return e(this,void 0,void 0,(function*(){const e=M(t.algorithm.name,n),r=yield crypto.subtle.deriveKey(e,t,{name:d,length:128},!1,["encrypt","decrypt"]);return{material:t,encryptionKey:r}}))}class N{constructor(){this.consecutiveSifCount=0,this.lastSifReceivedAt=0,this.userFramesSinceSif=0}recordSif(){var e;this.consecutiveSifCount+=1,null!==(e=this.sifSequenceStartedAt)&&void 0!==e||(this.sifSequenceStartedAt=Date.now()),this.lastSifReceivedAt=Date.now()}recordUserFrame(){void 0!==this.sifSequenceStartedAt&&(this.userFramesSinceSif+=1,(this.userFramesSinceSif>this.consecutiveSifCount||Date.now()-this.lastSifReceivedAt>2e3)&&this.reset())}isSifAllowed(){return this.consecutiveSifCount<100&&(void 0===this.sifSequenceStartedAt||Date.now()-this.sifSequenceStartedAt<2e3)}reset(){this.userFramesSinceSif=0,this.consecutiveSifCount=0,this.sifSequenceStartedAt=void 0}}const B=new Map;class q extends O.EventEmitter{encodeFunction(e,t){throw Error("not implemented for subclass")}decodeFunction(e,t){throw Error("not implemented for subclass")}}class V extends q{constructor(e){var t;super(),this.sendCounts=new Map,this.keys=e.keys,this.participantIdentity=e.participantIdentity,this.rtpMap=new Map,this.keyProviderOptions=e.keyProviderOptions,this.sifTrailer=null!==(t=e.sifTrailer)&&void 0!==t?t:Uint8Array.from([]),this.sifGuard=new N}setParticipant(e,t){this.participantIdentity=e,this.keys=t,this.sifGuard.reset()}unsetParticipant(){this.participantIdentity=void 0}isEnabled(){return this.participantIdentity?B.get(this.participantIdentity):void 0}getParticipantIdentity(){return this.participantIdentity}getTrackId(){return this.trackId}setVideoCodec(e){this.videoCodec=e}setRtpMap(e){this.rtpMap=e}setupTransform(e,t,n,r,i){i&&(c.info("setting codec on cryptor to",{codec:i}),this.videoCodec=i);const o="encode"===e?this.encodeFunction:this.decodeFunction,s=new TransformStream({transform:o.bind(this)});t.pipeThrough(s).pipeTo(n).catch((e=>{c.warn(e),this.emit(m.Error,e instanceof w?e:new w(e.message))})),this.trackId=r}setSifTrailer(e){this.sifTrailer=e}encodeFunction(t,n){var r;return e(this,void 0,void 0,(function*(){if(!this.isEnabled()||0===t.data.byteLength)return n.enqueue(t);const e=this.keys.getKeySet();if(!e)throw new TypeError("key set not found for ".concat(this.participantIdentity," at index ").concat(this.keys.getCurrentKeyIndex()));const{encryptionKey:i}=e,o=this.keys.getCurrentKeyIndex();if(i){const e=this.makeIV(null!==(r=t.getMetadata().synchronizationSource)&&void 0!==r?r:-1,t.timestamp);let a=this.getUnencryptedBytes(t);const u=new Uint8Array(t.data,0,a.unencryptedBytes),y=new Uint8Array(2);y[0]=12,y[1]=o;try{const r=yield crypto.subtle.encrypt({name:d,iv:e,additionalData:new Uint8Array(t.data,0,u.byteLength)},i,new Uint8Array(t.data,a.unencryptedBytes));let o=new Uint8Array(r.byteLength+e.byteLength+y.byteLength);o.set(new Uint8Array(r)),o.set(new Uint8Array(e),r.byteLength),o.set(y,r.byteLength+e.byteLength),a.isH264&&(o=function(e){const t=[];for(var n=0,r=0;r<e.length;++r){var i=e[r];i<=3&&n>=2&&(t.push(3),n=0),t.push(i),0==i?++n:n=0}return new Uint8Array(t)}(o));var s=new Uint8Array(u.byteLength+o.byteLength);return s.set(u),s.set(o,u.byteLength),t.data=s.buffer,n.enqueue(t)}catch(e){c.error(e)}}else this.emit(m.Error,new w("encryption key missing for encoding",p.MissingKey))}))}decodeFunction(t,n){return e(this,void 0,void 0,(function*(){if(!this.isEnabled()||0===t.data.byteLength)return this.sifGuard.recordUserFrame(),n.enqueue(t);if(function(e,t){if(0===t.byteLength)return!1;const n=new Uint8Array(e.slice(e.byteLength-t.byteLength));return t.every(((e,t)=>e===n[t]))}(t.data,this.sifTrailer))return this.sifGuard.recordSif(),this.sifGuard.isSifAllowed()?(t.data=t.data.slice(0,t.data.byteLength-this.sifTrailer.byteLength),n.enqueue(t)):void c.warn("SIF limit reached, dropping frame");this.sifGuard.recordUserFrame();const e=new Uint8Array(t.data)[t.data.byteLength-1];if(this.keys.getKeySet(e)&&this.keys.hasValidKey)try{const r=yield this.decryptFrame(t,e);if(this.keys.decryptionSuccess(),r)return n.enqueue(r)}catch(e){e instanceof w&&e.reason===p.InvalidKey?this.keys.hasValidKey&&(this.emit(m.Error,e),this.keys.decryptionFailure()):c.warn("decoding frame failed",{error:e})}else!this.keys.getKeySet(e)&&this.keys.hasValidKey&&(c.warn("skipping decryption due to missing key at index ".concat(e)),this.emit(m.Error,new w("missing key at index ".concat(e," for participant ").concat(this.participantIdentity),p.MissingKey)))}))}decryptFrame(t,n){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{ratchetCount:0};var o;return e(this,void 0,void 0,(function*(){const e=this.keys.getKeySet(n);if(!i.encryptionKey&&!e)throw new TypeError("no encryption key found for decryption of ".concat(this.participantIdentity));let s=this.getUnencryptedBytes(t);try{const n=new Uint8Array(t.data,0,s.unencryptedBytes);var a=new Uint8Array(t.data,n.length,t.data.byteLength-n.length);if(s.isH264&&function(e){for(var t=0;t<e.length-3;t++)if(0==e[t]&&0==e[t+1]&&3==e[t+2])return!0;return!1}(a)){a=function(e){const t=[];for(var n=e.length,r=0;r<e.length;)n-r>=3&&!e[r]&&!e[r+1]&&3==e[r+2]?(t.push(e[r++]),t.push(e[r++]),r++):t.push(e[r++]);return new Uint8Array(t)}(a);const e=new Uint8Array(n.byteLength+a.byteLength);e.set(n),e.set(a,n.byteLength),t.data=e.buffer}const r=new Uint8Array(t.data,t.data.byteLength-2,2),c=r[0],u=new Uint8Array(t.data,t.data.byteLength-c-r.byteLength,c),y=n.byteLength,h=t.data.byteLength-(n.byteLength+c+r.byteLength),l=yield crypto.subtle.decrypt({name:d,iv:u,additionalData:new Uint8Array(t.data,0,n.byteLength)},null!==(o=i.encryptionKey)&&void 0!==o?o:e.encryptionKey,new Uint8Array(t.data,y,h)),p=new ArrayBuffer(n.byteLength+l.byteLength),f=new Uint8Array(p);return f.set(new Uint8Array(t.data,0,n.byteLength)),f.set(new Uint8Array(l),n.byteLength),t.data=p,t}catch(o){if(this.keyProviderOptions.ratchetWindowSize>0){if(i.ratchetCount<this.keyProviderOptions.ratchetWindowSize){let o;if(c.debug("ratcheting key attempt ".concat(i.ratchetCount," of ").concat(this.keyProviderOptions.ratchetWindowSize,", for kind ").concat(t instanceof RTCEncodedAudioFrame?"audio":"video")),(null!=r?r:e)===this.keys.getKeySet(n)){const e=yield this.keys.ratchetKey(n,!1);o=yield D(e,this.keyProviderOptions.ratchetSalt)}const s=yield this.decryptFrame(t,n,r||e,{ratchetCount:i.ratchetCount+1,encryptionKey:null==o?void 0:o.encryptionKey});return s&&o&&(null!=r?r:e)===this.keys.getKeySet(n)&&(this.keys.setKeySet(o,n,!0),this.keys.setCurrentKeyIndex(n)),s}throw c.warn("maximum ratchet attempts exceeded"),new w("valid key missing for participant ".concat(this.participantIdentity),p.InvalidKey)}throw new w("Decryption failed: ".concat(o.message),p.InvalidKey)}}))}makeIV(e,t){var n;const r=new ArrayBuffer(12),i=new DataView(r);this.sendCounts.has(e)||this.sendCounts.set(e,Math.floor(65535*Math.random()));const o=null!==(n=this.sendCounts.get(e))&&void 0!==n?n:0;return i.setUint32(0,e),i.setUint32(4,t),i.setUint32(8,t-o%65535),this.sendCounts.set(e,o+1),r}getUnencryptedBytes(e){var t,n={unencryptedBytes:0,isH264:!1};if(function(e){return"type"in e}(e)){let r=null!==(t=this.getVideoCodec(e))&&void 0!==t?t:this.videoCodec;if("av1"===r||"vp9"===r)throw new Error("".concat(r," is not yet supported for end to end encryption"));if("vp8"===r)return n.unencryptedBytes=u[e.type],n;const i=new Uint8Array(e.data);try{const e=function(e){const t=[];let n=0,r=0,i=e.length-2;for(;r<i;){for(;r<i&&(0!==e[r]||0!==e[r+1]||1!==e[r+2]);)r++;r>=i&&(r=e.length);let o=r;for(;o>n&&0===e[o-1];)o--;if(0===n){if(o!==n)throw TypeError("byte stream contains leading data")}else t.push(n);n=r+=3}return t}(i);if(n.isH264="h264"===r||e.some((e=>[H.SLICE_IDR,H.SLICE_NON_IDR].includes(j(i[e])))),n.isH264){for(const t of e){switch(j(i[t])){case H.SLICE_IDR:case H.SLICE_NON_IDR:return n.unencryptedBytes=t+2,n}}throw new TypeError("Could not find NALU")}}catch(e){}return n.unencryptedBytes=u[e.type],n}return n.unencryptedBytes=u.audio,n}getVideoCodec(e){if(0===this.rtpMap.size)return;const t=e.getMetadata().payloadType;return t?this.rtpMap.get(t):void 0}}function j(e){return e&X}const X=31;var H;!function(e){e[e.SLICE_NON_IDR=1]="SLICE_NON_IDR",e[e.SLICE_PARTITION_A=2]="SLICE_PARTITION_A",e[e.SLICE_PARTITION_B=3]="SLICE_PARTITION_B",e[e.SLICE_PARTITION_C=4]="SLICE_PARTITION_C",e[e.SLICE_IDR=5]="SLICE_IDR",e[e.SEI=6]="SEI",e[e.SPS=7]="SPS",e[e.PPS=8]="PPS",e[e.AUD=9]="AUD",e[e.END_SEQ=10]="END_SEQ",e[e.END_STREAM=11]="END_STREAM",e[e.FILLER_DATA=12]="FILLER_DATA",e[e.SPS_EXT=13]="SPS_EXT",e[e.PREFIX_NALU=14]="PREFIX_NALU",e[e.SUBSET_SPS=15]="SUBSET_SPS",e[e.DPS=16]="DPS",e[e.SLICE_AUX=19]="SLICE_AUX",e[e.SLICE_EXT=20]="SLICE_EXT",e[e.SLICE_LAYER_EXT=21]="SLICE_LAYER_EXT"}(H||(H={}));class G extends O.EventEmitter{get hasValidKey(){return this._hasValidKey}constructor(e,t){super(),this.decryptionFailureCount=0,this._hasValidKey=!0,this.currentKeyIndex=0,this.cryptoKeyRing=new Array(16).fill(void 0),this.keyProviderOptions=t,this.ratchetPromiseMap=new Map,this.participantIdentity=e,this.resetKeyStatus()}decryptionFailure(){this.keyProviderOptions.failureTolerance<0||(this.decryptionFailureCount+=1,this.decryptionFailureCount>this.keyProviderOptions.failureTolerance&&(c.warn("key for ".concat(this.participantIdentity," is being marked as invalid")),this._hasValidKey=!1))}decryptionSuccess(){this.resetKeyStatus()}resetKeyStatus(){this.decryptionFailureCount=0,this._hasValidKey=!0}ratchetKey(t){let n=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const r=null!=t?t:this.getCurrentKeyIndex(),i=this.ratchetPromiseMap.get(r);if(void 0!==i)return i;const o=new Promise(((t,i)=>e(this,void 0,void 0,(function*(){try{const i=this.getKeySet(r);if(!i)throw new TypeError("Cannot ratchet key without a valid keyset of participant ".concat(this.participantIdentity));const o=i.material,s=yield function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{name:d},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"encrypt";return e(this,void 0,void 0,(function*(){return crypto.subtle.importKey("raw",t,n,!1,"derive"===r?["deriveBits","deriveKey"]:["encrypt","decrypt"])}))}(yield function(t,n){return e(this,void 0,void 0,(function*(){const e=M(t.algorithm.name,n);return crypto.subtle.deriveBits(e,t,256)}))}(o,this.keyProviderOptions.ratchetSalt),o.algorithm.name,"derive");n&&(this.setKeyFromMaterial(s,r,!0),this.emit(v.KeyRatcheted,s,this.participantIdentity,r)),t(s)}catch(e){i(e)}finally{this.ratchetPromiseMap.delete(r)}}))));return this.ratchetPromiseMap.set(r,o),o}setKey(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return e(this,void 0,void 0,(function*(){yield this.setKeyFromMaterial(t,n),this.resetKeyStatus()}))}setKeyFromMaterial(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return e(this,void 0,void 0,(function*(){const e=n>=0?n%this.cryptoKeyRing.length:-1;c.debug("setting new key with index ".concat(e));const i=yield D(t,this.keyProviderOptions.ratchetSalt);this.setKeySet(i,e>=0?e:this.currentKeyIndex,r),e>=0&&(this.currentKeyIndex=e)}))}setKeySet(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.cryptoKeyRing[t%this.cryptoKeyRing.length]=e,n&&this.emit(v.KeyRatcheted,e.material,this.participantIdentity,t)}setCurrentKeyIndex(t){return e(this,void 0,void 0,(function*(){this.currentKeyIndex=t%this.cryptoKeyRing.length,this.resetKeyStatus()}))}getCurrentKeyIndex(){return this.currentKeyIndex}getKeySet(e){return this.cryptoKeyRing[null!=e?e:this.currentKeyIndex]}}const z=[],W=new Map;let Y,Q,J,Z=!1,$=y;function ee(e,t){let n=z.find((e=>e.getTrackId()===t));if(n)e!==n.getParticipantIdentity()&&n.setParticipant(e,te(e));else{if(c.info("creating new cryptor for",{participantIdentity:e}),!$)throw Error("Missing keyProvider options");n=new V({participantIdentity:e,keys:te(e),keyProviderOptions:$,sifTrailer:J}),function(e){e.on(m.Error,(e=>{const t={kind:"error",data:{error:new Error("".concat(p[e.reason],": ").concat(e.message))}};postMessage(t)}))}(n),z.push(n)}return n}function te(e){if(Z)return ne();let t=W.get(e);return t||(t=new G(e,$),Q&&t.setKey(Q),t.on(v.KeyRatcheted,re),W.set(e,t)),t}function ne(){return Y||(Y=new G("shared-key",$)),Y}function re(e,t,n){postMessage({kind:"ratchetKey",data:{participantIdentity:t,keyIndex:n,material:e}})}c.setDefaultLevel("info"),onmessage=t=>{const{kind:n,data:r}=t.data;switch(n){case"init":c.info("worker initialized"),$=r.keyProviderOptions,Z=!!r.keyProviderOptions.sharedKey;postMessage({kind:"initAck",data:{enabled:false}});break;case"enable":u=r.enabled,y=r.participantIdentity,B.set(y,u),c.info("updated e2ee enabled status"),postMessage(t.data);break;case"decode":ee(r.participantIdentity,r.trackId).setupTransform(n,r.readableStream,r.writableStream,r.trackId,r.codec);break;case"encode":ee(r.participantIdentity,r.trackId).setupTransform(n,r.readableStream,r.writableStream,r.trackId,r.codec);break;case"setKey":Z?(c.warn("set shared key"),a=r.key,d=r.keyIndex,c.debug("setting shared key"),Q=a,ne().setKey(a,d)):r.participantIdentity?(c.warn("set participant sender key ".concat(r.participantIdentity," index ").concat(r.keyIndex)),te(r.participantIdentity).setKey(r.key,r.keyIndex)):c.error("no participant Id was provided and shared key usage is disabled");break;case"removeTransform":o=r.trackId,null===(s=z.find((e=>e.getTrackId()===o)))||void 0===s||s.unsetParticipant();break;case"updateCodec":ee(r.participantIdentity,r.trackId).setVideoCodec(r.codec);break;case"setRTPMap":z.forEach((e=>{e.getParticipantIdentity()===r.participantIdentity&&e.setRtpMap(r.map)}));break;case"ratchetRequest":!function(t){e(this,void 0,void 0,(function*(){if(Z){const e=ne();yield e.ratchetKey(t.keyIndex),e.resetKeyStatus()}else if(t.participantIdentity){const e=te(t.participantIdentity);yield e.ratchetKey(t.keyIndex),e.resetKeyStatus()}else c.error("no participant Id was provided for ratchet request and shared key usage is disabled")}))}(r);break;case"setSifTrailer":i=r.trailer,J=i,z.forEach((e=>{e.setSifTrailer(i)}))}var i,o,s,a,d,u,y},self.RTCTransformEvent&&(c.debug("setup transform event"),self.onrtctransform=e=>{const t=e.transformer;c.debug("transformer",t),t.handled=!0;const{kind:n,participantIdentity:r,trackId:i,codec:o}=t.options,s=ee(r,i);c.debug("transform",{codec:o}),s.setupTransform(n,t.readable,t.writable,i,o)})})); | ||
!function(e){"function"==typeof define&&define.amd?define(e):e()}((function(){"use strict";function e(e,t,n,r){return new(n||(n=Promise))((function(i,o){function s(e){try{c(r.next(e))}catch(e){o(e)}}function a(e){try{c(r.throw(e))}catch(e){o(e)}}function c(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;var t,n,r,i="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},o={exports:{}};n=i,r=function(){var e=function(){},t="undefined",n=typeof window!==t&&typeof window.navigator!==t&&/Trident\/|MSIE /.test(window.navigator.userAgent),r=["trace","debug","info","warn","error"];function i(e,t){var n=e[t];if("function"==typeof n.bind)return n.bind(e);try{return Function.prototype.bind.call(n,e)}catch(t){return function(){return Function.prototype.apply.apply(n,[e,arguments])}}}function o(){console.log&&(console.log.apply?console.log.apply(console,arguments):Function.prototype.apply.apply(console.log,[console,arguments])),console.trace&&console.trace()}function s(t,n){for(var i=0;i<r.length;i++){var o=r[i];this[o]=i<t?e:this.methodFactory(o,t,n)}this.log=this.debug}function a(e,n,r){return function(){typeof console!==t&&(s.call(this,n,r),this[e].apply(this,arguments))}}function c(r,s,c){return function(r){return"debug"===r&&(r="log"),typeof console!==t&&("trace"===r&&n?o:void 0!==console[r]?i(console,r):void 0!==console.log?i(console,"log"):e)}(r)||a.apply(this,arguments)}function d(e,n,i){var o,a=this;n=null==n?"WARN":n;var d="loglevel";function u(){var e;if(typeof window!==t&&d){try{e=window.localStorage[d]}catch(e){}if(typeof e===t)try{var n=window.document.cookie,r=n.indexOf(encodeURIComponent(d)+"=");-1!==r&&(e=/^([^;]+)/.exec(n.slice(r))[1])}catch(e){}return void 0===a.levels[e]&&(e=void 0),e}}"string"==typeof e?d+=":"+e:"symbol"==typeof e&&(d=void 0),a.name=e,a.levels={TRACE:0,DEBUG:1,INFO:2,WARN:3,ERROR:4,SILENT:5},a.methodFactory=i||c,a.getLevel=function(){return o},a.setLevel=function(n,i){if("string"==typeof n&&void 0!==a.levels[n.toUpperCase()]&&(n=a.levels[n.toUpperCase()]),!("number"==typeof n&&n>=0&&n<=a.levels.SILENT))throw"log.setLevel() called with invalid level: "+n;if(o=n,!1!==i&&function(e){var n=(r[e]||"silent").toUpperCase();if(typeof window!==t&&d){try{return void(window.localStorage[d]=n)}catch(e){}try{window.document.cookie=encodeURIComponent(d)+"="+n+";"}catch(e){}}}(n),s.call(a,n,e),typeof console===t&&n<a.levels.SILENT)return"No console available for logging"},a.setDefaultLevel=function(e){n=e,u()||a.setLevel(e,!1)},a.resetLevel=function(){a.setLevel(n,!1),function(){if(typeof window!==t&&d){try{return void window.localStorage.removeItem(d)}catch(e){}try{window.document.cookie=encodeURIComponent(d)+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC"}catch(e){}}}()},a.enableAll=function(e){a.setLevel(a.levels.TRACE,e)},a.disableAll=function(e){a.setLevel(a.levels.SILENT,e)};var y=u();null==y&&(y=n),a.setLevel(y,!1)}var u=new d,y={};u.getLogger=function(e){if("symbol"!=typeof e&&"string"!=typeof e||""===e)throw new TypeError("You must supply a name when creating a logger.");var t=y[e];return t||(t=y[e]=new d(e,u.getLevel(),u.methodFactory)),t};var l=typeof window!==t?window.log:void 0;return u.noConflict=function(){return typeof window!==t&&window.log===u&&(window.log=l),u},u.getLoggers=function(){return y},u.default=u,u},(t=o).exports?t.exports=r():n.log=r();var s,a,c=o.exports;!function(e){e[e.trace=0]="trace",e[e.debug=1]="debug",e[e.info=2]="info",e[e.warn=3]="warn",e[e.error=4]="error",e[e.silent=5]="silent"}(s||(s={})),function(e){e.Default="livekit",e.Room="livekit-room",e.Participant="livekit-participant",e.Track="livekit-track",e.Publication="livekit-track-publication",e.Engine="livekit-engine",e.Signal="livekit-signal",e.PCManager="livekit-pc-manager",e.PCTransport="livekit-pc-transport",e.E2EE="lk-e2ee"}(a||(a={})),c.getLogger("livekit").setDefaultLevel(s.info);const d=c.getLogger("lk-e2ee"),u="AES-GCM",y={key:10,delta:3,audio:1,empty:0},l={sharedKey:!1,ratchetSalt:"LKFrameEncryptionKey",ratchetWindowSize:8,failureTolerance:10};class h extends Error{constructor(e,t){super(t||"an error has occured"),this.code=e}}var p,f,v,g,m,w;!function(e){e.PermissionDenied="PermissionDenied",e.NotFound="NotFound",e.DeviceInUse="DeviceInUse",e.Other="Other"}(p||(p={})),function(e){e.getFailure=function(t){if(t&&"name"in t)return"NotFoundError"===t.name||"DevicesNotFoundError"===t.name?e.NotFound:"NotAllowedError"===t.name||"PermissionDeniedError"===t.name?e.PermissionDenied:"NotReadableError"===t.name||"TrackStartError"===t.name?e.DeviceInUse:e.Other}}(p||(p={})),function(e){e[e.InvalidKey=0]="InvalidKey",e[e.MissingKey=1]="MissingKey",e[e.InternalError=2]="InternalError"}(f||(f={}));class I extends h{constructor(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:f.InternalError;super(40,e),this.reason=t}}!function(e){e.SetKey="setKey",e.RatchetRequest="ratchetRequest",e.KeyRatcheted="keyRatcheted"}(v||(v={})),function(e){e.KeyRatcheted="keyRatcheted"}(g||(g={})),function(e){e.ParticipantEncryptionStatusChanged="participantEncryptionStatusChanged",e.EncryptionError="encryptionError"}(m||(m={})),function(e){e.Error="cryptorError"}(w||(w={}));var S,L={exports:{}},b="object"==typeof Reflect?Reflect:null,E=b&&"function"==typeof b.apply?b.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};S=b&&"function"==typeof b.ownKeys?b.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var k=Number.isNaN||function(e){return e!=e};function K(){K.init.call(this)}L.exports=K,L.exports.once=function(e,t){return new Promise((function(n,r){function i(n){e.removeListener(t,o),r(n)}function o(){"function"==typeof e.removeListener&&e.removeListener("error",i),n([].slice.call(arguments))}O(e,t,o,{once:!0}),"error"!==t&&function(e,t,n){"function"==typeof e.on&&O(e,"error",t,n)}(e,i,{once:!0})}))},K.EventEmitter=K,K.prototype._events=void 0,K.prototype._eventsCount=0,K.prototype._maxListeners=void 0;var C=10;function _(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function A(e){return void 0===e._maxListeners?K.defaultMaxListeners:e._maxListeners}function T(e,t,n,r){var i,o,s,a;if(_(n),void 0===(o=e._events)?(o=e._events=Object.create(null),e._eventsCount=0):(void 0!==o.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),o=e._events),s=o[t]),void 0===s)s=o[t]=n,++e._eventsCount;else if("function"==typeof s?s=o[t]=r?[n,s]:[s,n]:r?s.unshift(n):s.push(n),(i=A(e))>0&&s.length>i&&!s.warned){s.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=s.length,a=c,console&&console.warn&&console.warn(a)}return e}function P(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function R(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},i=P.bind(r);return i.listener=n,r.wrapFn=i,i}function x(e,t,n){var r=e._events;if(void 0===r)return[];var i=r[t];return void 0===i?[]:"function"==typeof i?n?[i.listener||i]:[i]:n?function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(i):F(i,i.length)}function U(e){var t=this._events;if(void 0!==t){var n=t[e];if("function"==typeof n)return 1;if(void 0!==n)return n.length}return 0}function F(e,t){for(var n=new Array(t),r=0;r<t;++r)n[r]=e[r];return n}function O(e,t,n,r){if("function"==typeof e.on)r.once?e.once(t,n):e.on(t,n);else{if("function"!=typeof e.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof e);e.addEventListener(t,(function i(o){r.once&&e.removeEventListener(t,i),n(o)}))}}Object.defineProperty(K,"defaultMaxListeners",{enumerable:!0,get:function(){return C},set:function(e){if("number"!=typeof e||e<0||k(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");C=e}}),K.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},K.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||k(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},K.prototype.getMaxListeners=function(){return A(this)},K.prototype.emit=function(e){for(var t=[],n=1;n<arguments.length;n++)t.push(arguments[n]);var r="error"===e,i=this._events;if(void 0!==i)r=r&&void 0===i.error;else if(!r)return!1;if(r){var o;if(t.length>0&&(o=t[0]),o instanceof Error)throw o;var s=new Error("Unhandled error."+(o?" ("+o.message+")":""));throw s.context=o,s}var a=i[e];if(void 0===a)return!1;if("function"==typeof a)E(a,this,t);else{var c=a.length,d=F(a,c);for(n=0;n<c;++n)E(d[n],this,t)}return!0},K.prototype.addListener=function(e,t){return T(this,e,t,!1)},K.prototype.on=K.prototype.addListener,K.prototype.prependListener=function(e,t){return T(this,e,t,!0)},K.prototype.once=function(e,t){return _(t),this.on(e,R(this,e,t)),this},K.prototype.prependOnceListener=function(e,t){return _(t),this.prependListener(e,R(this,e,t)),this},K.prototype.removeListener=function(e,t){var n,r,i,o,s;if(_(t),void 0===(r=this._events))return this;if(void 0===(n=r[e]))return this;if(n===t||n.listener===t)0==--this._eventsCount?this._events=Object.create(null):(delete r[e],r.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(i=-1,o=n.length-1;o>=0;o--)if(n[o]===t||n[o].listener===t){s=n[o].listener,i=o;break}if(i<0)return this;0===i?n.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(n,i),1===n.length&&(r[e]=n[0]),void 0!==r.removeListener&&this.emit("removeListener",e,s||t)}return this},K.prototype.off=K.prototype.removeListener,K.prototype.removeAllListeners=function(e){var t,n,r;if(void 0===(n=this._events))return this;if(void 0===n.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==n[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete n[e]),this;if(0===arguments.length){var i,o=Object.keys(n);for(r=0;r<o.length;++r)"removeListener"!==(i=o[r])&&this.removeAllListeners(i);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(void 0!==t)for(r=t.length-1;r>=0;r--)this.removeListener(e,t[r]);return this},K.prototype.listeners=function(e){return x(this,e,!0)},K.prototype.rawListeners=function(e){return x(this,e,!1)},K.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):U.call(e,t)},K.prototype.listenerCount=U,K.prototype.eventNames=function(){return this._eventsCount>0?S(this._events):[]};var M=L.exports;function D(e,t){const n=(new TextEncoder).encode(t);switch(e){case"HKDF":return{name:"HKDF",salt:n,hash:"SHA-256",info:new ArrayBuffer(128)};case"PBKDF2":return{name:"PBKDF2",salt:n,hash:"SHA-256",iterations:1e5};default:throw new Error("algorithm ".concat(e," is currently unsupported"))}}function N(t,n){return e(this,void 0,void 0,(function*(){const e=D(t.algorithm.name,n),r=yield crypto.subtle.deriveKey(e,t,{name:u,length:128},!1,["encrypt","decrypt"]);return{material:t,encryptionKey:r}}))}class B{constructor(){this.consecutiveSifCount=0,this.lastSifReceivedAt=0,this.userFramesSinceSif=0}recordSif(){var e;this.consecutiveSifCount+=1,null!==(e=this.sifSequenceStartedAt)&&void 0!==e||(this.sifSequenceStartedAt=Date.now()),this.lastSifReceivedAt=Date.now()}recordUserFrame(){void 0!==this.sifSequenceStartedAt&&(this.userFramesSinceSif+=1,(this.userFramesSinceSif>this.consecutiveSifCount||Date.now()-this.lastSifReceivedAt>2e3)&&this.reset())}isSifAllowed(){return this.consecutiveSifCount<100&&(void 0===this.sifSequenceStartedAt||Date.now()-this.sifSequenceStartedAt<2e3)}reset(){this.userFramesSinceSif=0,this.consecutiveSifCount=0,this.sifSequenceStartedAt=void 0}}const q=new Map;class V extends M.EventEmitter{encodeFunction(e,t){throw Error("not implemented for subclass")}decodeFunction(e,t){throw Error("not implemented for subclass")}}class j extends V{constructor(e){var t;super(),this.sendCounts=new Map,this.keys=e.keys,this.participantIdentity=e.participantIdentity,this.rtpMap=new Map,this.keyProviderOptions=e.keyProviderOptions,this.sifTrailer=null!==(t=e.sifTrailer)&&void 0!==t?t:Uint8Array.from([]),this.sifGuard=new B}setParticipant(e,t){this.participantIdentity=e,this.keys=t,this.sifGuard.reset()}unsetParticipant(){this.participantIdentity=void 0}isEnabled(){return this.participantIdentity?q.get(this.participantIdentity):void 0}getParticipantIdentity(){return this.participantIdentity}getTrackId(){return this.trackId}setVideoCodec(e){this.videoCodec=e}setRtpMap(e){this.rtpMap=e}setupTransform(e,t,n,r,i){i&&(d.info("setting codec on cryptor to",{codec:i}),this.videoCodec=i);const o="encode"===e?this.encodeFunction:this.decodeFunction,s=new TransformStream({transform:o.bind(this)});t.pipeThrough(s).pipeTo(n).catch((e=>{d.warn(e),this.emit(w.Error,e instanceof I?e:new I(e.message))})),this.trackId=r}setSifTrailer(e){this.sifTrailer=e}encodeFunction(t,n){var r;return e(this,void 0,void 0,(function*(){if(!this.isEnabled()||0===t.data.byteLength)return n.enqueue(t);const e=this.keys.getKeySet();if(!e)throw new TypeError("key set not found for ".concat(this.participantIdentity," at index ").concat(this.keys.getCurrentKeyIndex()));const{encryptionKey:i}=e,o=this.keys.getCurrentKeyIndex();if(i){const e=this.makeIV(null!==(r=t.getMetadata().synchronizationSource)&&void 0!==r?r:-1,t.timestamp);let a=this.getUnencryptedBytes(t);const c=new Uint8Array(t.data,0,a.unencryptedBytes),y=new Uint8Array(2);y[0]=12,y[1]=o;try{const r=yield crypto.subtle.encrypt({name:u,iv:e,additionalData:new Uint8Array(t.data,0,c.byteLength)},i,new Uint8Array(t.data,a.unencryptedBytes));let o=new Uint8Array(r.byteLength+e.byteLength+y.byteLength);o.set(new Uint8Array(r)),o.set(new Uint8Array(e),r.byteLength),o.set(y,r.byteLength+e.byteLength),a.isH264&&(o=function(e){const t=[];for(var n=0,r=0;r<e.length;++r){var i=e[r];i<=3&&n>=2&&(t.push(3),n=0),t.push(i),0==i?++n:n=0}return new Uint8Array(t)}(o));var s=new Uint8Array(c.byteLength+o.byteLength);return s.set(c),s.set(o,c.byteLength),t.data=s.buffer,n.enqueue(t)}catch(e){d.error(e)}}else this.emit(w.Error,new I("encryption key missing for encoding",f.MissingKey))}))}decodeFunction(t,n){return e(this,void 0,void 0,(function*(){if(!this.isEnabled()||0===t.data.byteLength)return this.sifGuard.recordUserFrame(),n.enqueue(t);if(function(e,t){if(0===t.byteLength)return!1;const n=new Uint8Array(e.slice(e.byteLength-t.byteLength));return t.every(((e,t)=>e===n[t]))}(t.data,this.sifTrailer))return this.sifGuard.recordSif(),this.sifGuard.isSifAllowed()?(t.data=t.data.slice(0,t.data.byteLength-this.sifTrailer.byteLength),n.enqueue(t)):void d.warn("SIF limit reached, dropping frame");this.sifGuard.recordUserFrame();const e=new Uint8Array(t.data)[t.data.byteLength-1];if(this.keys.getKeySet(e)&&this.keys.hasValidKey)try{const r=yield this.decryptFrame(t,e);if(this.keys.decryptionSuccess(),r)return n.enqueue(r)}catch(e){e instanceof I&&e.reason===f.InvalidKey?this.keys.hasValidKey&&(this.emit(w.Error,e),this.keys.decryptionFailure()):d.warn("decoding frame failed",{error:e})}else!this.keys.getKeySet(e)&&this.keys.hasValidKey&&(d.warn("skipping decryption due to missing key at index ".concat(e)),this.emit(w.Error,new I("missing key at index ".concat(e," for participant ").concat(this.participantIdentity),f.MissingKey)))}))}decryptFrame(t,n){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{ratchetCount:0};var o;return e(this,void 0,void 0,(function*(){const e=this.keys.getKeySet(n);if(!i.encryptionKey&&!e)throw new TypeError("no encryption key found for decryption of ".concat(this.participantIdentity));let s=this.getUnencryptedBytes(t);try{const n=new Uint8Array(t.data,0,s.unencryptedBytes);var a=new Uint8Array(t.data,n.length,t.data.byteLength-n.length);if(s.isH264&&function(e){for(var t=0;t<e.length-3;t++)if(0==e[t]&&0==e[t+1]&&3==e[t+2])return!0;return!1}(a)){a=function(e){const t=[];for(var n=e.length,r=0;r<e.length;)n-r>=3&&!e[r]&&!e[r+1]&&3==e[r+2]?(t.push(e[r++]),t.push(e[r++]),r++):t.push(e[r++]);return new Uint8Array(t)}(a);const e=new Uint8Array(n.byteLength+a.byteLength);e.set(n),e.set(a,n.byteLength),t.data=e.buffer}const r=new Uint8Array(t.data,t.data.byteLength-2,2),c=r[0],d=new Uint8Array(t.data,t.data.byteLength-c-r.byteLength,c),y=n.byteLength,l=t.data.byteLength-(n.byteLength+c+r.byteLength),h=yield crypto.subtle.decrypt({name:u,iv:d,additionalData:new Uint8Array(t.data,0,n.byteLength)},null!==(o=i.encryptionKey)&&void 0!==o?o:e.encryptionKey,new Uint8Array(t.data,y,l)),p=new ArrayBuffer(n.byteLength+h.byteLength),f=new Uint8Array(p);return f.set(new Uint8Array(t.data,0,n.byteLength)),f.set(new Uint8Array(h),n.byteLength),t.data=p,t}catch(o){if(this.keyProviderOptions.ratchetWindowSize>0){if(i.ratchetCount<this.keyProviderOptions.ratchetWindowSize){let o;if(d.debug("ratcheting key attempt ".concat(i.ratchetCount," of ").concat(this.keyProviderOptions.ratchetWindowSize,", for kind ").concat(t instanceof RTCEncodedAudioFrame?"audio":"video")),(null!=r?r:e)===this.keys.getKeySet(n)){const e=yield this.keys.ratchetKey(n,!1);o=yield N(e,this.keyProviderOptions.ratchetSalt)}const s=yield this.decryptFrame(t,n,r||e,{ratchetCount:i.ratchetCount+1,encryptionKey:null==o?void 0:o.encryptionKey});return s&&o&&(null!=r?r:e)===this.keys.getKeySet(n)&&(this.keys.setKeySet(o,n,!0),this.keys.setCurrentKeyIndex(n)),s}throw d.warn("maximum ratchet attempts exceeded"),new I("valid key missing for participant ".concat(this.participantIdentity),f.InvalidKey)}throw new I("Decryption failed: ".concat(o.message),f.InvalidKey)}}))}makeIV(e,t){var n;const r=new ArrayBuffer(12),i=new DataView(r);this.sendCounts.has(e)||this.sendCounts.set(e,Math.floor(65535*Math.random()));const o=null!==(n=this.sendCounts.get(e))&&void 0!==n?n:0;return i.setUint32(0,e),i.setUint32(4,t),i.setUint32(8,t-o%65535),this.sendCounts.set(e,o+1),r}getUnencryptedBytes(e){var t,n={unencryptedBytes:0,isH264:!1};if(function(e){return"type"in e}(e)){let r=null!==(t=this.getVideoCodec(e))&&void 0!==t?t:this.videoCodec;if("av1"===r||"vp9"===r)throw new Error("".concat(r," is not yet supported for end to end encryption"));if("vp8"===r)return n.unencryptedBytes=y[e.type],n;const i=new Uint8Array(e.data);try{const e=function(e){const t=[];let n=0,r=0,i=e.length-2;for(;r<i;){for(;r<i&&(0!==e[r]||0!==e[r+1]||1!==e[r+2]);)r++;r>=i&&(r=e.length);let o=r;for(;o>n&&0===e[o-1];)o--;if(0===n){if(o!==n)throw TypeError("byte stream contains leading data")}else t.push(n);n=r+=3}return t}(i);if(n.isH264="h264"===r||e.some((e=>[G.SLICE_IDR,G.SLICE_NON_IDR].includes(X(i[e])))),n.isH264){for(const t of e){switch(X(i[t])){case G.SLICE_IDR:case G.SLICE_NON_IDR:return n.unencryptedBytes=t+2,n}}throw new TypeError("Could not find NALU")}}catch(e){}return n.unencryptedBytes=y[e.type],n}return n.unencryptedBytes=y.audio,n}getVideoCodec(e){if(0===this.rtpMap.size)return;const t=e.getMetadata().payloadType;return t?this.rtpMap.get(t):void 0}}function X(e){return e&H}const H=31;var G;!function(e){e[e.SLICE_NON_IDR=1]="SLICE_NON_IDR",e[e.SLICE_PARTITION_A=2]="SLICE_PARTITION_A",e[e.SLICE_PARTITION_B=3]="SLICE_PARTITION_B",e[e.SLICE_PARTITION_C=4]="SLICE_PARTITION_C",e[e.SLICE_IDR=5]="SLICE_IDR",e[e.SEI=6]="SEI",e[e.SPS=7]="SPS",e[e.PPS=8]="PPS",e[e.AUD=9]="AUD",e[e.END_SEQ=10]="END_SEQ",e[e.END_STREAM=11]="END_STREAM",e[e.FILLER_DATA=12]="FILLER_DATA",e[e.SPS_EXT=13]="SPS_EXT",e[e.PREFIX_NALU=14]="PREFIX_NALU",e[e.SUBSET_SPS=15]="SUBSET_SPS",e[e.DPS=16]="DPS",e[e.SLICE_AUX=19]="SLICE_AUX",e[e.SLICE_EXT=20]="SLICE_EXT",e[e.SLICE_LAYER_EXT=21]="SLICE_LAYER_EXT"}(G||(G={}));class z extends M.EventEmitter{get hasValidKey(){return this._hasValidKey}constructor(e,t){super(),this.decryptionFailureCount=0,this._hasValidKey=!0,this.currentKeyIndex=0,this.cryptoKeyRing=new Array(16).fill(void 0),this.keyProviderOptions=t,this.ratchetPromiseMap=new Map,this.participantIdentity=e,this.resetKeyStatus()}decryptionFailure(){this.keyProviderOptions.failureTolerance<0||(this.decryptionFailureCount+=1,this.decryptionFailureCount>this.keyProviderOptions.failureTolerance&&(d.warn("key for ".concat(this.participantIdentity," is being marked as invalid")),this._hasValidKey=!1))}decryptionSuccess(){this.resetKeyStatus()}resetKeyStatus(){this.decryptionFailureCount=0,this._hasValidKey=!0}ratchetKey(t){let n=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const r=null!=t?t:this.getCurrentKeyIndex(),i=this.ratchetPromiseMap.get(r);if(void 0!==i)return i;const o=new Promise(((t,i)=>e(this,void 0,void 0,(function*(){try{const i=this.getKeySet(r);if(!i)throw new TypeError("Cannot ratchet key without a valid keyset of participant ".concat(this.participantIdentity));const o=i.material,s=yield function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{name:u},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"encrypt";return e(this,void 0,void 0,(function*(){return crypto.subtle.importKey("raw",t,n,!1,"derive"===r?["deriveBits","deriveKey"]:["encrypt","decrypt"])}))}(yield function(t,n){return e(this,void 0,void 0,(function*(){const e=D(t.algorithm.name,n);return crypto.subtle.deriveBits(e,t,256)}))}(o,this.keyProviderOptions.ratchetSalt),o.algorithm.name,"derive");n&&(this.setKeyFromMaterial(s,r,!0),this.emit(g.KeyRatcheted,s,this.participantIdentity,r)),t(s)}catch(e){i(e)}finally{this.ratchetPromiseMap.delete(r)}}))));return this.ratchetPromiseMap.set(r,o),o}setKey(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return e(this,void 0,void 0,(function*(){yield this.setKeyFromMaterial(t,n),this.resetKeyStatus()}))}setKeyFromMaterial(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return e(this,void 0,void 0,(function*(){const e=n>=0?n%this.cryptoKeyRing.length:-1;d.debug("setting new key with index ".concat(e));const i=yield N(t,this.keyProviderOptions.ratchetSalt);this.setKeySet(i,e>=0?e:this.currentKeyIndex,r),e>=0&&(this.currentKeyIndex=e)}))}setKeySet(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.cryptoKeyRing[t%this.cryptoKeyRing.length]=e,n&&this.emit(g.KeyRatcheted,e.material,this.participantIdentity,t)}setCurrentKeyIndex(t){return e(this,void 0,void 0,(function*(){this.currentKeyIndex=t%this.cryptoKeyRing.length,this.resetKeyStatus()}))}getCurrentKeyIndex(){return this.currentKeyIndex}getKeySet(e){return this.cryptoKeyRing[null!=e?e:this.currentKeyIndex]}}const W=[],Y=new Map;let Q,J,Z,$=!1,ee=l;function te(e,t){let n=W.find((e=>e.getTrackId()===t));if(n)e!==n.getParticipantIdentity()&&n.setParticipant(e,ne(e));else{if(d.info("creating new cryptor for",{participantIdentity:e}),!ee)throw Error("Missing keyProvider options");n=new j({participantIdentity:e,keys:ne(e),keyProviderOptions:ee,sifTrailer:Z}),function(e){e.on(w.Error,(e=>{const t={kind:"error",data:{error:new Error("".concat(f[e.reason],": ").concat(e.message))}};postMessage(t)}))}(n),W.push(n)}return n}function ne(e){if($)return re();let t=Y.get(e);return t||(t=new z(e,ee),J&&t.setKey(J),t.on(g.KeyRatcheted,ie),Y.set(e,t)),t}function re(){return Q||(Q=new z("shared-key",ee)),Q}function ie(e,t,n){postMessage({kind:"ratchetKey",data:{participantIdentity:t,keyIndex:n,material:e}})}d.setDefaultLevel("info"),onmessage=t=>{const{kind:n,data:r}=t.data;switch(n){case"init":d.info("worker initialized"),ee=r.keyProviderOptions,$=!!r.keyProviderOptions.sharedKey;postMessage({kind:"initAck",data:{enabled:false}});break;case"enable":u=r.enabled,y=r.participantIdentity,q.set(y,u),d.info("updated e2ee enabled status"),postMessage(t.data);break;case"decode":te(r.participantIdentity,r.trackId).setupTransform(n,r.readableStream,r.writableStream,r.trackId,r.codec);break;case"encode":te(r.participantIdentity,r.trackId).setupTransform(n,r.readableStream,r.writableStream,r.trackId,r.codec);break;case"setKey":$?(d.warn("set shared key"),a=r.key,c=r.keyIndex,d.debug("setting shared key"),J=a,re().setKey(a,c)):r.participantIdentity?(d.warn("set participant sender key ".concat(r.participantIdentity," index ").concat(r.keyIndex)),ne(r.participantIdentity).setKey(r.key,r.keyIndex)):d.error("no participant Id was provided and shared key usage is disabled");break;case"removeTransform":o=r.trackId,null===(s=W.find((e=>e.getTrackId()===o)))||void 0===s||s.unsetParticipant();break;case"updateCodec":te(r.participantIdentity,r.trackId).setVideoCodec(r.codec);break;case"setRTPMap":W.forEach((e=>{e.getParticipantIdentity()===r.participantIdentity&&e.setRtpMap(r.map)}));break;case"ratchetRequest":!function(t){e(this,void 0,void 0,(function*(){if($){const e=re();yield e.ratchetKey(t.keyIndex),e.resetKeyStatus()}else if(t.participantIdentity){const e=ne(t.participantIdentity);yield e.ratchetKey(t.keyIndex),e.resetKeyStatus()}else d.error("no participant Id was provided for ratchet request and shared key usage is disabled")}))}(r);break;case"setSifTrailer":i=r.trailer,Z=i,W.forEach((e=>{e.setSifTrailer(i)}))}var i,o,s,a,c,u,y},self.RTCTransformEvent&&(d.debug("setup transform event"),self.onrtctransform=e=>{const t=e.transformer;d.debug("transformer",t),t.handled=!0;const{kind:n,participantIdentity:r,trackId:i,codec:o}=t.options,s=te(r,i);d.debug("transform",{codec:o}),s.setupTransform(n,t.readable,t.writable,i,o)})})); | ||
//# sourceMappingURL=livekit-client.e2ee.worker.js.map |
import { ParticipantInfo, ReconnectReason, Room, SpeakerInfo, VideoLayer } from '../proto/livekit_models_pb'; | ||
import { AddTrackRequest, ConnectionQualityUpdate, JoinResponse, LeaveRequest, ReconnectResponse, SessionDescription, SignalRequest, SignalTarget, SimulateScenario, StreamStateUpdate, SubscribedQualityUpdate, SubscriptionPermissionUpdate, SubscriptionResponse, SyncState, TrackPermission, TrackPublishedResponse, TrackUnpublishedResponse, UpdateSubscription, UpdateTrackSettings } from '../proto/livekit_rtc_pb'; | ||
import type { LoggerOptions } from '../room/types'; | ||
import { AsyncQueue } from '../utils/AsyncQueue'; | ||
@@ -68,3 +69,6 @@ interface ConnectOpts extends SignalOptions { | ||
private connectionLock; | ||
constructor(useJSON?: boolean); | ||
private log; | ||
private loggerContextCb?; | ||
constructor(useJSON?: boolean, loggerOptions?: LoggerOptions); | ||
private get logContext(); | ||
join(url: string, token: string, opts: SignalOptions, abortSignal?: AbortSignal): Promise<JoinResponse>; | ||
@@ -71,0 +75,0 @@ reconnect(url: string, token: string, sid?: string, reason?: ReconnectReason): Promise<ReconnectResponse | void>; |
@@ -1,2 +0,2 @@ | ||
import { LogLevel, setLogExtension, setLogLevel } from './logger'; | ||
import { LogLevel, getLogger, setLogExtension, setLogLevel } from './logger'; | ||
import { DataPacket_Kind, DisconnectReason, VideoQuality } from './proto/livekit_models_pb'; | ||
@@ -36,4 +36,4 @@ import DefaultReconnectPolicy from './room/DefaultReconnectPolicy'; | ||
export * from './room/track/processor/types'; | ||
export { setLogLevel, setLogExtension, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isBrowserSupported, supportsAdaptiveStream, supportsDynacast, supportsAV1, supportsVP9, createAudioAnalyser, LogLevel, Room, ConnectionState, RoomState, DataPacket_Kind, DisconnectReason, Participant, RemoteParticipant, LocalParticipant, LocalAudioTrack, LocalVideoTrack, LocalTrack, LocalTrackPublication, RemoteTrack, RemoteAudioTrack, RemoteVideoTrack, RemoteTrackPublication, TrackPublication, VideoQuality, ConnectionQuality, DefaultReconnectPolicy, CriticalTimers, }; | ||
export { setLogLevel, setLogExtension, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isBrowserSupported, supportsAdaptiveStream, supportsDynacast, supportsAV1, supportsVP9, createAudioAnalyser, LogLevel, getLogger, Room, ConnectionState, RoomState, DataPacket_Kind, DisconnectReason, Participant, RemoteParticipant, LocalParticipant, LocalAudioTrack, LocalVideoTrack, LocalTrack, LocalTrackPublication, RemoteTrack, RemoteAudioTrack, RemoteVideoTrack, RemoteTrackPublication, TrackPublication, VideoQuality, ConnectionQuality, DefaultReconnectPolicy, CriticalTimers, }; | ||
export type { ElementInfo, ParticipantTrackPermission, AudioAnalyserOptions, LiveKitReactNativeInfo, }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -10,4 +10,16 @@ import * as log from 'loglevel'; | ||
} | ||
export declare enum LoggerNames { | ||
Default = "livekit", | ||
Room = "livekit-room", | ||
Participant = "livekit-participant", | ||
Track = "livekit-track", | ||
Publication = "livekit-track-publication", | ||
Engine = "livekit-engine", | ||
Signal = "livekit-signal", | ||
PCManager = "livekit-pc-manager", | ||
PCTransport = "livekit-pc-transport", | ||
E2EE = "lk-e2ee" | ||
} | ||
type LogLevelString = keyof typeof LogLevel; | ||
type StructuredLogger = { | ||
export type StructuredLogger = { | ||
trace: (msg: string, context?: object) => void; | ||
@@ -22,3 +34,7 @@ debug: (msg: string, context?: object) => void; | ||
export default _default; | ||
export declare function setLogLevel(level: LogLevel | LogLevelString, loggerName?: 'livekit' | 'lk-e2ee'): void; | ||
/** | ||
* @internal | ||
*/ | ||
export declare function getLogger(name: string): StructuredLogger; | ||
export declare function setLogLevel(level: LogLevel | LogLevelString, loggerName?: LoggerNames): void; | ||
export type LogExtension = (level: LogLevel, msg: string, context?: object) => void; | ||
@@ -29,4 +45,4 @@ /** | ||
*/ | ||
export declare function setLogExtension(extension: LogExtension): void; | ||
export declare function setLogExtension(extension: LogExtension, logger?: log.Logger): void; | ||
export declare const workerLogger: StructuredLogger; | ||
//# sourceMappingURL=logger.d.ts.map |
@@ -76,2 +76,3 @@ import type { E2EEOptions } from './e2ee/types'; | ||
e2ee?: E2EEOptions; | ||
loggerName?: string; | ||
} | ||
@@ -78,0 +79,0 @@ /** |
import type TypedEmitter from 'typed-emitter'; | ||
import { StructuredLogger } from '../../logger'; | ||
import { DataPacket_Kind, ParticipantInfo, ParticipantPermission, ConnectionQuality as ProtoQuality, SubscriptionError } from '../../proto/livekit_models_pb'; | ||
@@ -8,2 +9,3 @@ import type LocalTrackPublication from '../track/LocalTrackPublication'; | ||
import type { TrackPublication } from '../track/TrackPublication'; | ||
import type { LoggerOptions } from '../types'; | ||
export declare enum ConnectionQuality { | ||
@@ -43,6 +45,12 @@ Excellent = "excellent", | ||
protected audioContext?: AudioContext; | ||
protected log: StructuredLogger; | ||
protected loggerOptions?: LoggerOptions; | ||
protected get logContext(): { | ||
participantSid: string; | ||
participantId: string; | ||
}; | ||
get isEncrypted(): boolean; | ||
get isAgent(): boolean; | ||
/** @internal */ | ||
constructor(sid: string, identity: string, name?: string, metadata?: string); | ||
constructor(sid: string, identity: string, name?: string, metadata?: string, loggerOptions?: LoggerOptions); | ||
getTracks(): TrackPublication[]; | ||
@@ -49,0 +57,0 @@ /** |
@@ -5,4 +5,5 @@ import LocalAudioTrack from '../track/LocalAudioTrack'; | ||
import { VideoPreset } from '../track/options'; | ||
import type { LoggerOptions } from '../types'; | ||
/** @internal */ | ||
export declare function mediaTrackToLocalTrack(mediaStreamTrack: MediaStreamTrack, constraints?: MediaTrackConstraints): LocalVideoTrack | LocalAudioTrack; | ||
export declare function mediaTrackToLocalTrack(mediaStreamTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, loggerOptions?: LoggerOptions): LocalVideoTrack | LocalAudioTrack; | ||
export declare const presets169: VideoPreset[]; | ||
@@ -9,0 +10,0 @@ export declare const presets43: VideoPreset[]; |
@@ -7,2 +7,3 @@ import type { SignalClient } from '../../api/SignalClient'; | ||
import type { AdaptiveStreamSettings } from '../track/types'; | ||
import type { LoggerOptions } from '../types'; | ||
import Participant from './Participant'; | ||
@@ -20,3 +21,3 @@ import type { ParticipantEventCallbacks } from './Participant'; | ||
/** @internal */ | ||
constructor(signalClient: SignalClient, sid: string, identity?: string, name?: string, metadata?: string); | ||
constructor(signalClient: SignalClient, sid: string, identity?: string, name?: string, metadata?: string, loggerOptions?: LoggerOptions); | ||
protected addTrackPublication(publication: RemoteTrackPublication): void; | ||
@@ -23,0 +24,0 @@ getTrack(source: Track.Source): RemoteTrackPublication | undefined; |
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import type { LoggerOptions } from './types'; | ||
/** @internal */ | ||
@@ -21,2 +22,4 @@ interface TrackBitrateInfo { | ||
private mediaConstraints; | ||
private log; | ||
private loggerOptions; | ||
pendingCandidates: RTCIceCandidateInit[]; | ||
@@ -36,4 +39,5 @@ restartingIce: boolean; | ||
onTrack?: (ev: RTCTrackEvent) => void; | ||
constructor(config?: RTCConfiguration, mediaConstraints?: Record<string, unknown>); | ||
constructor(config?: RTCConfiguration, mediaConstraints?: Record<string, unknown>, loggerOptions?: LoggerOptions); | ||
private createPC; | ||
private get logContext(); | ||
get isICEConnected(): boolean; | ||
@@ -40,0 +44,0 @@ addIceCandidate(candidate: RTCIceCandidateInit): Promise<void>; |
import { SignalTarget } from '../proto/livekit_rtc_pb'; | ||
import PCTransport from './PCTransport'; | ||
import type { LoggerOptions } from './types'; | ||
export declare enum PCTransportState { | ||
@@ -27,3 +28,6 @@ NEW = 0, | ||
private connectionLock; | ||
constructor(rtcConfig: RTCConfiguration, subscriberPrimary: boolean); | ||
private log; | ||
private loggerOptions; | ||
constructor(rtcConfig: RTCConfiguration, subscriberPrimary: boolean, loggerOptions: LoggerOptions); | ||
private get logContext(); | ||
requirePublisher(require?: boolean): void; | ||
@@ -30,0 +34,0 @@ requireSubscriber(require?: boolean): void; |
@@ -67,2 +67,3 @@ import type TypedEmitter from 'typed-emitter'; | ||
private isVideoPlaybackBlocked; | ||
private log; | ||
/** | ||
@@ -78,2 +79,3 @@ * Creates a new Room, the primary construct for a LiveKit session. | ||
private setupE2EE; | ||
private get logContext(); | ||
/** | ||
@@ -80,0 +82,0 @@ * if the current room has a participant with `recorder: true` in its JWT grant |
@@ -58,3 +58,11 @@ import type TypedEventEmitter from 'typed-emitter'; | ||
private regionUrlProvider?; | ||
private log; | ||
private loggerOptions; | ||
constructor(options: InternalRoomOptions); | ||
/** @internal */ | ||
get logContext(): { | ||
room: string | undefined; | ||
roomSid: string | undefined; | ||
identity: string | undefined; | ||
}; | ||
join(url: string, token: string, opts: SignalOptions, abortSignal?: AbortSignal): Promise<JoinResponse>; | ||
@@ -61,0 +69,0 @@ close(): Promise<void>; |
import type { AudioSenderStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import LocalTrack from './LocalTrack'; | ||
@@ -16,3 +17,3 @@ import type { AudioCaptureOptions } from './options'; | ||
*/ | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, audioContext?: AudioContext); | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, audioContext?: AudioContext, loggerOptions?: LoggerOptions); | ||
setDeviceId(deviceId: ConstrainDOMString): Promise<boolean>; | ||
@@ -19,0 +20,0 @@ mute(): Promise<LocalAudioTrack>; |
@@ -0,1 +1,2 @@ | ||
import type { LoggerOptions } from '../types'; | ||
import { Mutex } from '../utils'; | ||
@@ -26,3 +27,3 @@ import { Track } from './Track'; | ||
*/ | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean); | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, loggerOptions?: LoggerOptions); | ||
get id(): string; | ||
@@ -29,0 +30,0 @@ get dimensions(): Track.Dimensions | undefined; |
import type { TrackInfo } from '../../proto/livekit_models_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
import type LocalAudioTrack from './LocalAudioTrack'; | ||
@@ -12,3 +13,3 @@ import type LocalTrack from './LocalTrack'; | ||
get isUpstreamPaused(): boolean | undefined; | ||
constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack); | ||
constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack, loggerOptions?: LoggerOptions); | ||
setTrack(track?: Track): void; | ||
@@ -15,0 +16,0 @@ get isMuted(): boolean; |
@@ -5,2 +5,3 @@ import type { SignalClient } from '../../api/SignalClient'; | ||
import type { VideoSenderStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import LocalTrack from './LocalTrack'; | ||
@@ -30,3 +31,3 @@ import { Track } from './Track'; | ||
*/ | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean); | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, loggerOptions?: LoggerOptions); | ||
get isSimulcast(): boolean; | ||
@@ -33,0 +34,0 @@ startMonitor(signalClient: SignalClient): void; |
@@ -152,5 +152,6 @@ import type { Track } from './Track'; | ||
/** | ||
* capture resolution, defaults to screen resolution | ||
* NOTE: In Safari 17, specifying any resolution at all would lead to a low-resolution | ||
* capture. https://bugs.webkit.org/show_bug.cgi?id=263015 | ||
* capture resolution, defaults to 1080 for all browsers other than Safari | ||
* On Safari 17, default resolution is not capped, due to a bug, specifying | ||
* any resolution at all would lead to a low-resolution capture. | ||
* https://bugs.webkit.org/show_bug.cgi?id=263015 | ||
*/ | ||
@@ -166,2 +167,4 @@ resolution?: VideoResolution; | ||
systemAudio?: 'include' | 'exclude'; | ||
/** specify the type of content, see: https://www.w3.org/TR/mst-content-hint/#video-content-hints */ | ||
contentHint?: 'detail' | 'text' | 'motion'; | ||
/** | ||
@@ -285,2 +288,3 @@ * Experimental option to control whether the audio playing in a tab will continue to be played out of a user's | ||
readonly h360fps3: VideoPreset; | ||
readonly h360fps15: VideoPreset; | ||
readonly h720fps5: VideoPreset; | ||
@@ -291,4 +295,5 @@ readonly h720fps15: VideoPreset; | ||
readonly h1080fps30: VideoPreset; | ||
readonly original: VideoPreset; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=options.d.ts.map |
import type { AudioReceiverStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import RemoteTrack from './RemoteTrack'; | ||
@@ -12,3 +13,3 @@ import type { AudioOutputOptions } from './options'; | ||
private sinkId?; | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, audioContext?: AudioContext, audioOutput?: AudioOutputOptions); | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, audioContext?: AudioContext, audioOutput?: AudioOutputOptions, loggerOptions?: LoggerOptions); | ||
/** | ||
@@ -15,0 +16,0 @@ * sets the volume for all attached audio elements |
@@ -0,1 +1,2 @@ | ||
import type { LoggerOptions } from '../types'; | ||
import { Track } from './Track'; | ||
@@ -5,3 +6,3 @@ export default abstract class RemoteTrack extends Track { | ||
receiver?: RTCRtpReceiver; | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, kind: Track.Kind, receiver?: RTCRtpReceiver); | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, kind: Track.Kind, receiver?: RTCRtpReceiver, loggerOptions?: LoggerOptions); | ||
/** @internal */ | ||
@@ -8,0 +9,0 @@ setMuted(muted: boolean): void; |
import { SubscriptionError, TrackInfo, VideoQuality } from '../../proto/livekit_models_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
import type RemoteTrack from './RemoteTrack'; | ||
@@ -15,3 +16,3 @@ import { Track } from './Track'; | ||
protected subscriptionError?: SubscriptionError; | ||
constructor(kind: Track.Kind, ti: TrackInfo, autoSubscribe: boolean | undefined); | ||
constructor(kind: Track.Kind, ti: TrackInfo, autoSubscribe: boolean | undefined, loggerOptions?: LoggerOptions); | ||
/** | ||
@@ -18,0 +19,0 @@ * Subscribe or unsubscribe to this remote track |
@@ -0,1 +1,2 @@ | ||
import type { LoggerOptions } from '../types'; | ||
import RemoteTrack from './RemoteTrack'; | ||
@@ -9,3 +10,3 @@ import type { AdaptiveStreamSettings } from './types'; | ||
private lastDimensions?; | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, adaptiveStreamSettings?: AdaptiveStreamSettings); | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, adaptiveStreamSettings?: AdaptiveStreamSettings, loggerOptions?: LoggerOptions); | ||
get isAdaptiveStream(): boolean; | ||
@@ -12,0 +13,0 @@ /** |
import type TypedEventEmitter from 'typed-emitter'; | ||
import type { SignalClient } from '../../api/SignalClient'; | ||
import { StructuredLogger } from '../../logger'; | ||
import { TrackSource, TrackType } from '../../proto/livekit_models_pb'; | ||
import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
declare const Track_base: new () => TypedEventEmitter<TrackEventCallbacks>; | ||
@@ -28,5 +30,10 @@ export declare abstract class Track extends Track_base { | ||
private backgroundTimeout; | ||
private loggerContextCb; | ||
protected _currentBitrate: number; | ||
protected monitorInterval?: ReturnType<typeof setInterval>; | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind); | ||
protected log: StructuredLogger; | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, loggerOptions?: LoggerOptions); | ||
protected get logContext(): { | ||
[x: string]: unknown; | ||
}; | ||
/** current receive bits per second */ | ||
@@ -63,2 +70,4 @@ get currentBitrate(): number; | ||
stopMonitor(): void; | ||
/** @internal */ | ||
updateLoggerOptions(loggerOptions: LoggerOptions): void; | ||
private recycleElement; | ||
@@ -65,0 +74,0 @@ protected appVisibilityChangedListener: () => void; |
@@ -5,2 +5,3 @@ import type TypedEventEmitter from 'typed-emitter'; | ||
import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
import LocalAudioTrack from './LocalAudioTrack'; | ||
@@ -29,5 +30,10 @@ import LocalVideoTrack from './LocalVideoTrack'; | ||
protected encryption: Encryption_Type; | ||
constructor(kind: Track.Kind, id: string, name: string); | ||
protected log: import("../../logger").StructuredLogger; | ||
private loggerContextCb?; | ||
constructor(kind: Track.Kind, id: string, name: string, loggerOptions?: LoggerOptions); | ||
/** @internal */ | ||
setTrack(track?: Track): void; | ||
protected get logContext(): { | ||
[x: string]: unknown; | ||
}; | ||
get isMuted(): boolean; | ||
@@ -34,0 +40,0 @@ get isEnabled(): boolean; |
@@ -31,2 +31,3 @@ import { TrackPublishedResponse } from '../../proto/livekit_rtc_pb'; | ||
export declare function getTrackPublicationInfo<T extends TrackPublication>(tracks: T[]): TrackPublishedResponse[]; | ||
export declare function getLogContextFromTrack(track: Track | TrackPublication): Record<string, unknown>; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -26,2 +26,6 @@ import type RemoteParticipant from './participant/RemoteParticipant'; | ||
export type SimulationScenario = 'signal-reconnect' | 'speaker' | 'node-failure' | 'server-leave' | 'migration' | 'resume-reconnect' | 'force-tcp' | 'force-tls' | 'full-reconnect' | 'subscriber-bandwidth'; | ||
export type LoggerOptions = { | ||
loggerName?: string; | ||
loggerContextCb?: () => Record<string, unknown>; | ||
}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -23,2 +23,3 @@ import { ClientInfo } from '../proto/livekit_models_pb'; | ||
export declare function isSafari(): boolean; | ||
export declare function isSafari17(): boolean; | ||
export declare function isMobile(): boolean; | ||
@@ -25,0 +26,0 @@ export declare function isWeb(): boolean; |
import { ParticipantInfo, ReconnectReason, Room, SpeakerInfo, VideoLayer } from '../proto/livekit_models_pb'; | ||
import { AddTrackRequest, ConnectionQualityUpdate, JoinResponse, LeaveRequest, ReconnectResponse, SessionDescription, SignalRequest, SignalTarget, SimulateScenario, StreamStateUpdate, SubscribedQualityUpdate, SubscriptionPermissionUpdate, SubscriptionResponse, SyncState, TrackPermission, TrackPublishedResponse, TrackUnpublishedResponse, UpdateSubscription, UpdateTrackSettings } from '../proto/livekit_rtc_pb'; | ||
import type { LoggerOptions } from '../room/types'; | ||
import { AsyncQueue } from '../utils/AsyncQueue'; | ||
@@ -68,3 +69,6 @@ interface ConnectOpts extends SignalOptions { | ||
private connectionLock; | ||
constructor(useJSON?: boolean); | ||
private log; | ||
private loggerContextCb?; | ||
constructor(useJSON?: boolean, loggerOptions?: LoggerOptions); | ||
private get logContext(); | ||
join(url: string, token: string, opts: SignalOptions, abortSignal?: AbortSignal): Promise<JoinResponse>; | ||
@@ -71,0 +75,0 @@ reconnect(url: string, token: string, sid?: string, reason?: ReconnectReason): Promise<ReconnectResponse | void>; |
@@ -1,2 +0,2 @@ | ||
import { LogLevel, setLogExtension, setLogLevel } from './logger'; | ||
import { LogLevel, getLogger, setLogExtension, setLogLevel } from './logger'; | ||
import { DataPacket_Kind, DisconnectReason, VideoQuality } from './proto/livekit_models_pb'; | ||
@@ -36,4 +36,4 @@ import DefaultReconnectPolicy from './room/DefaultReconnectPolicy'; | ||
export * from './room/track/processor/types'; | ||
export { setLogLevel, setLogExtension, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isBrowserSupported, supportsAdaptiveStream, supportsDynacast, supportsAV1, supportsVP9, createAudioAnalyser, LogLevel, Room, ConnectionState, RoomState, DataPacket_Kind, DisconnectReason, Participant, RemoteParticipant, LocalParticipant, LocalAudioTrack, LocalVideoTrack, LocalTrack, LocalTrackPublication, RemoteTrack, RemoteAudioTrack, RemoteVideoTrack, RemoteTrackPublication, TrackPublication, VideoQuality, ConnectionQuality, DefaultReconnectPolicy, CriticalTimers, }; | ||
export { setLogLevel, setLogExtension, getEmptyAudioStreamTrack, getEmptyVideoStreamTrack, isBrowserSupported, supportsAdaptiveStream, supportsDynacast, supportsAV1, supportsVP9, createAudioAnalyser, LogLevel, getLogger, Room, ConnectionState, RoomState, DataPacket_Kind, DisconnectReason, Participant, RemoteParticipant, LocalParticipant, LocalAudioTrack, LocalVideoTrack, LocalTrack, LocalTrackPublication, RemoteTrack, RemoteAudioTrack, RemoteVideoTrack, RemoteTrackPublication, TrackPublication, VideoQuality, ConnectionQuality, DefaultReconnectPolicy, CriticalTimers, }; | ||
export type { ElementInfo, ParticipantTrackPermission, AudioAnalyserOptions, LiveKitReactNativeInfo, }; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -10,4 +10,16 @@ import * as log from 'loglevel'; | ||
} | ||
export declare enum LoggerNames { | ||
Default = "livekit", | ||
Room = "livekit-room", | ||
Participant = "livekit-participant", | ||
Track = "livekit-track", | ||
Publication = "livekit-track-publication", | ||
Engine = "livekit-engine", | ||
Signal = "livekit-signal", | ||
PCManager = "livekit-pc-manager", | ||
PCTransport = "livekit-pc-transport", | ||
E2EE = "lk-e2ee" | ||
} | ||
type LogLevelString = keyof typeof LogLevel; | ||
type StructuredLogger = { | ||
export type StructuredLogger = { | ||
trace: (msg: string, context?: object) => void; | ||
@@ -22,3 +34,7 @@ debug: (msg: string, context?: object) => void; | ||
export default _default; | ||
export declare function setLogLevel(level: LogLevel | LogLevelString, loggerName?: 'livekit' | 'lk-e2ee'): void; | ||
/** | ||
* @internal | ||
*/ | ||
export declare function getLogger(name: string): StructuredLogger; | ||
export declare function setLogLevel(level: LogLevel | LogLevelString, loggerName?: LoggerNames): void; | ||
export type LogExtension = (level: LogLevel, msg: string, context?: object) => void; | ||
@@ -29,4 +45,4 @@ /** | ||
*/ | ||
export declare function setLogExtension(extension: LogExtension): void; | ||
export declare function setLogExtension(extension: LogExtension, logger?: log.Logger): void; | ||
export declare const workerLogger: StructuredLogger; | ||
//# sourceMappingURL=logger.d.ts.map |
@@ -76,2 +76,3 @@ import type { E2EEOptions } from './e2ee/types'; | ||
e2ee?: E2EEOptions; | ||
loggerName?: string; | ||
} | ||
@@ -78,0 +79,0 @@ /** |
import type TypedEmitter from 'typed-emitter'; | ||
import { StructuredLogger } from '../../logger'; | ||
import { DataPacket_Kind, ParticipantInfo, ParticipantPermission, ConnectionQuality as ProtoQuality, SubscriptionError } from '../../proto/livekit_models_pb'; | ||
@@ -8,2 +9,3 @@ import type LocalTrackPublication from '../track/LocalTrackPublication'; | ||
import type { TrackPublication } from '../track/TrackPublication'; | ||
import type { LoggerOptions } from '../types'; | ||
export declare enum ConnectionQuality { | ||
@@ -43,6 +45,12 @@ Excellent = "excellent", | ||
protected audioContext?: AudioContext; | ||
protected log: StructuredLogger; | ||
protected loggerOptions?: LoggerOptions; | ||
protected get logContext(): { | ||
participantSid: string; | ||
participantId: string; | ||
}; | ||
get isEncrypted(): boolean; | ||
get isAgent(): boolean; | ||
/** @internal */ | ||
constructor(sid: string, identity: string, name?: string, metadata?: string); | ||
constructor(sid: string, identity: string, name?: string, metadata?: string, loggerOptions?: LoggerOptions); | ||
getTracks(): TrackPublication[]; | ||
@@ -49,0 +57,0 @@ /** |
@@ -5,4 +5,5 @@ import LocalAudioTrack from '../track/LocalAudioTrack'; | ||
import { VideoPreset } from '../track/options'; | ||
import type { LoggerOptions } from '../types'; | ||
/** @internal */ | ||
export declare function mediaTrackToLocalTrack(mediaStreamTrack: MediaStreamTrack, constraints?: MediaTrackConstraints): LocalVideoTrack | LocalAudioTrack; | ||
export declare function mediaTrackToLocalTrack(mediaStreamTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, loggerOptions?: LoggerOptions): LocalVideoTrack | LocalAudioTrack; | ||
export declare const presets169: VideoPreset[]; | ||
@@ -9,0 +10,0 @@ export declare const presets43: VideoPreset[]; |
@@ -7,2 +7,3 @@ import type { SignalClient } from '../../api/SignalClient'; | ||
import type { AdaptiveStreamSettings } from '../track/types'; | ||
import type { LoggerOptions } from '../types'; | ||
import Participant from './Participant'; | ||
@@ -20,3 +21,3 @@ import type { ParticipantEventCallbacks } from './Participant'; | ||
/** @internal */ | ||
constructor(signalClient: SignalClient, sid: string, identity?: string, name?: string, metadata?: string); | ||
constructor(signalClient: SignalClient, sid: string, identity?: string, name?: string, metadata?: string, loggerOptions?: LoggerOptions); | ||
protected addTrackPublication(publication: RemoteTrackPublication): void; | ||
@@ -23,0 +24,0 @@ getTrack(source: Track.Source): RemoteTrackPublication | undefined; |
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import type { LoggerOptions } from './types'; | ||
/** @internal */ | ||
@@ -21,2 +22,4 @@ interface TrackBitrateInfo { | ||
private mediaConstraints; | ||
private log; | ||
private loggerOptions; | ||
pendingCandidates: RTCIceCandidateInit[]; | ||
@@ -36,4 +39,5 @@ restartingIce: boolean; | ||
onTrack?: (ev: RTCTrackEvent) => void; | ||
constructor(config?: RTCConfiguration, mediaConstraints?: Record<string, unknown>); | ||
constructor(config?: RTCConfiguration, mediaConstraints?: Record<string, unknown>, loggerOptions?: LoggerOptions); | ||
private createPC; | ||
private get logContext(); | ||
get isICEConnected(): boolean; | ||
@@ -40,0 +44,0 @@ addIceCandidate(candidate: RTCIceCandidateInit): Promise<void>; |
import { SignalTarget } from '../proto/livekit_rtc_pb'; | ||
import PCTransport from './PCTransport'; | ||
import type { LoggerOptions } from './types'; | ||
export declare enum PCTransportState { | ||
@@ -27,3 +28,6 @@ NEW = 0, | ||
private connectionLock; | ||
constructor(rtcConfig: RTCConfiguration, subscriberPrimary: boolean); | ||
private log; | ||
private loggerOptions; | ||
constructor(rtcConfig: RTCConfiguration, subscriberPrimary: boolean, loggerOptions: LoggerOptions); | ||
private get logContext(); | ||
requirePublisher(require?: boolean): void; | ||
@@ -30,0 +34,0 @@ requireSubscriber(require?: boolean): void; |
@@ -67,2 +67,3 @@ import type TypedEmitter from 'typed-emitter'; | ||
private isVideoPlaybackBlocked; | ||
private log; | ||
/** | ||
@@ -78,2 +79,3 @@ * Creates a new Room, the primary construct for a LiveKit session. | ||
private setupE2EE; | ||
private get logContext(); | ||
/** | ||
@@ -80,0 +82,0 @@ * if the current room has a participant with `recorder: true` in its JWT grant |
@@ -59,3 +59,11 @@ import type TypedEventEmitter from 'typed-emitter'; | ||
private regionUrlProvider?; | ||
private log; | ||
private loggerOptions; | ||
constructor(options: InternalRoomOptions); | ||
/** @internal */ | ||
get logContext(): { | ||
room: string | undefined; | ||
roomSid: string | undefined; | ||
identity: string | undefined; | ||
}; | ||
join(url: string, token: string, opts: SignalOptions, abortSignal?: AbortSignal): Promise<JoinResponse>; | ||
@@ -62,0 +70,0 @@ close(): Promise<void>; |
import type { AudioSenderStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import LocalTrack from './LocalTrack'; | ||
@@ -16,3 +17,3 @@ import type { AudioCaptureOptions } from './options'; | ||
*/ | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, audioContext?: AudioContext); | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, audioContext?: AudioContext, loggerOptions?: LoggerOptions); | ||
setDeviceId(deviceId: ConstrainDOMString): Promise<boolean>; | ||
@@ -19,0 +20,0 @@ mute(): Promise<LocalAudioTrack>; |
@@ -0,1 +1,2 @@ | ||
import type { LoggerOptions } from '../types'; | ||
import { Mutex } from '../utils'; | ||
@@ -26,3 +27,3 @@ import { Track } from './Track'; | ||
*/ | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean); | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, loggerOptions?: LoggerOptions); | ||
get id(): string; | ||
@@ -29,0 +30,0 @@ get dimensions(): Track.Dimensions | undefined; |
import type { TrackInfo } from '../../proto/livekit_models_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
import type LocalAudioTrack from './LocalAudioTrack'; | ||
@@ -12,3 +13,3 @@ import type LocalTrack from './LocalTrack'; | ||
get isUpstreamPaused(): boolean | undefined; | ||
constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack); | ||
constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack, loggerOptions?: LoggerOptions); | ||
setTrack(track?: Track): void; | ||
@@ -15,0 +16,0 @@ get isMuted(): boolean; |
@@ -5,2 +5,3 @@ import type { SignalClient } from '../../api/SignalClient'; | ||
import type { VideoSenderStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import LocalTrack from './LocalTrack'; | ||
@@ -30,3 +31,3 @@ import { Track } from './Track'; | ||
*/ | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean); | ||
constructor(mediaTrack: MediaStreamTrack, constraints?: MediaTrackConstraints, userProvidedTrack?: boolean, loggerOptions?: LoggerOptions); | ||
get isSimulcast(): boolean; | ||
@@ -33,0 +34,0 @@ startMonitor(signalClient: SignalClient): void; |
@@ -152,5 +152,6 @@ import type { Track } from './Track'; | ||
/** | ||
* capture resolution, defaults to screen resolution | ||
* NOTE: In Safari 17, specifying any resolution at all would lead to a low-resolution | ||
* capture. https://bugs.webkit.org/show_bug.cgi?id=263015 | ||
* capture resolution, defaults to 1080 for all browsers other than Safari | ||
* On Safari 17, default resolution is not capped, due to a bug, specifying | ||
* any resolution at all would lead to a low-resolution capture. | ||
* https://bugs.webkit.org/show_bug.cgi?id=263015 | ||
*/ | ||
@@ -166,2 +167,4 @@ resolution?: VideoResolution; | ||
systemAudio?: 'include' | 'exclude'; | ||
/** specify the type of content, see: https://www.w3.org/TR/mst-content-hint/#video-content-hints */ | ||
contentHint?: 'detail' | 'text' | 'motion'; | ||
/** | ||
@@ -293,2 +296,3 @@ * Experimental option to control whether the audio playing in a tab will continue to be played out of a user's | ||
readonly h360fps3: VideoPreset; | ||
readonly h360fps15: VideoPreset; | ||
readonly h720fps5: VideoPreset; | ||
@@ -299,4 +303,5 @@ readonly h720fps15: VideoPreset; | ||
readonly h1080fps30: VideoPreset; | ||
readonly original: VideoPreset; | ||
}; | ||
export {}; | ||
//# sourceMappingURL=options.d.ts.map |
import type { AudioReceiverStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import RemoteTrack from './RemoteTrack'; | ||
@@ -12,3 +13,3 @@ import type { AudioOutputOptions } from './options'; | ||
private sinkId?; | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, audioContext?: AudioContext, audioOutput?: AudioOutputOptions); | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, audioContext?: AudioContext, audioOutput?: AudioOutputOptions, loggerOptions?: LoggerOptions); | ||
/** | ||
@@ -15,0 +16,0 @@ * sets the volume for all attached audio elements |
@@ -0,1 +1,2 @@ | ||
import type { LoggerOptions } from '../types'; | ||
import { Track } from './Track'; | ||
@@ -5,3 +6,3 @@ export default abstract class RemoteTrack extends Track { | ||
receiver?: RTCRtpReceiver; | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, kind: Track.Kind, receiver?: RTCRtpReceiver); | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, kind: Track.Kind, receiver?: RTCRtpReceiver, loggerOptions?: LoggerOptions); | ||
/** @internal */ | ||
@@ -8,0 +9,0 @@ setMuted(muted: boolean): void; |
import { SubscriptionError, TrackInfo, VideoQuality } from '../../proto/livekit_models_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
import type RemoteTrack from './RemoteTrack'; | ||
@@ -15,3 +16,3 @@ import { Track } from './Track'; | ||
protected subscriptionError?: SubscriptionError; | ||
constructor(kind: Track.Kind, ti: TrackInfo, autoSubscribe: boolean | undefined); | ||
constructor(kind: Track.Kind, ti: TrackInfo, autoSubscribe: boolean | undefined, loggerOptions?: LoggerOptions); | ||
/** | ||
@@ -18,0 +19,0 @@ * Subscribe or unsubscribe to this remote track |
@@ -0,1 +1,2 @@ | ||
import type { LoggerOptions } from '../types'; | ||
import RemoteTrack from './RemoteTrack'; | ||
@@ -9,3 +10,3 @@ import type { AdaptiveStreamSettings } from './types'; | ||
private lastDimensions?; | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, adaptiveStreamSettings?: AdaptiveStreamSettings); | ||
constructor(mediaTrack: MediaStreamTrack, sid: string, receiver?: RTCRtpReceiver, adaptiveStreamSettings?: AdaptiveStreamSettings, loggerOptions?: LoggerOptions); | ||
get isAdaptiveStream(): boolean; | ||
@@ -12,0 +13,0 @@ /** |
import type TypedEventEmitter from 'typed-emitter'; | ||
import type { SignalClient } from '../../api/SignalClient'; | ||
import { StructuredLogger } from '../../logger'; | ||
import { TrackSource, TrackType } from '../../proto/livekit_models_pb'; | ||
import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
declare const Track_base: new () => TypedEventEmitter<TrackEventCallbacks>; | ||
@@ -28,5 +30,10 @@ export declare abstract class Track extends Track_base { | ||
private backgroundTimeout; | ||
private loggerContextCb; | ||
protected _currentBitrate: number; | ||
protected monitorInterval?: ReturnType<typeof setInterval>; | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind); | ||
protected log: StructuredLogger; | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind, loggerOptions?: LoggerOptions); | ||
protected get logContext(): { | ||
[x: string]: unknown; | ||
}; | ||
/** current receive bits per second */ | ||
@@ -63,2 +70,4 @@ get currentBitrate(): number; | ||
stopMonitor(): void; | ||
/** @internal */ | ||
updateLoggerOptions(loggerOptions: LoggerOptions): void; | ||
private recycleElement; | ||
@@ -65,0 +74,0 @@ protected appVisibilityChangedListener: () => void; |
@@ -5,2 +5,3 @@ import type TypedEventEmitter from 'typed-emitter'; | ||
import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc_pb'; | ||
import type { LoggerOptions } from '../types'; | ||
import LocalAudioTrack from './LocalAudioTrack'; | ||
@@ -29,5 +30,10 @@ import LocalVideoTrack from './LocalVideoTrack'; | ||
protected encryption: Encryption_Type; | ||
constructor(kind: Track.Kind, id: string, name: string); | ||
protected log: import("../../logger").StructuredLogger; | ||
private loggerContextCb?; | ||
constructor(kind: Track.Kind, id: string, name: string, loggerOptions?: LoggerOptions); | ||
/** @internal */ | ||
setTrack(track?: Track): void; | ||
protected get logContext(): { | ||
[x: string]: unknown; | ||
}; | ||
get isMuted(): boolean; | ||
@@ -34,0 +40,0 @@ get isEnabled(): boolean; |
@@ -31,2 +31,3 @@ import { TrackPublishedResponse } from '../../proto/livekit_rtc_pb'; | ||
export declare function getTrackPublicationInfo<T extends TrackPublication>(tracks: T[]): TrackPublishedResponse[]; | ||
export declare function getLogContextFromTrack(track: Track | TrackPublication): Record<string, unknown>; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -26,2 +26,6 @@ import type RemoteParticipant from './participant/RemoteParticipant'; | ||
export type SimulationScenario = 'signal-reconnect' | 'speaker' | 'node-failure' | 'server-leave' | 'migration' | 'resume-reconnect' | 'force-tcp' | 'force-tls' | 'full-reconnect' | 'subscriber-bandwidth'; | ||
export type LoggerOptions = { | ||
loggerName?: string; | ||
loggerContextCb?: () => Record<string, unknown>; | ||
}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -23,2 +23,3 @@ import { ClientInfo } from '../proto/livekit_models_pb'; | ||
export declare function isSafari(): boolean; | ||
export declare function isSafari17(): boolean; | ||
export declare function isMobile(): boolean; | ||
@@ -25,0 +26,0 @@ export declare function isWeb(): boolean; |
{ | ||
"name": "livekit-client", | ||
"version": "1.15.6", | ||
"version": "1.15.7", | ||
"description": "JavaScript/TypeScript client SDK for LiveKit", | ||
@@ -92,3 +92,3 @@ "main": "./dist/livekit-client.umd.js", | ||
"proto": "protoc --es_out src/proto --es_opt target=ts -I./protocol ./protocol/livekit_rtc.proto ./protocol/livekit_models.proto", | ||
"sample": "vite example -c vite.config.js", | ||
"sample": "vite example -c vite.config.mjs", | ||
"lint": "eslint src", | ||
@@ -95,0 +95,0 @@ "test": "vitest run src", |
import { protoInt64 } from '@bufbuild/protobuf'; | ||
import log from '../logger'; | ||
import log, { LoggerNames, getLogger } from '../logger'; | ||
import { | ||
@@ -42,2 +42,3 @@ ClientInfo, | ||
import CriticalTimers from '../room/timers'; | ||
import type { LoggerOptions } from '../room/types'; | ||
import { Mutex, getClientInfo, isReactNative, sleep, toWebsocketUrl } from '../room/utils'; | ||
@@ -176,3 +177,9 @@ import { AsyncQueue } from '../utils/AsyncQueue'; | ||
constructor(useJSON: boolean = false) { | ||
private log = log; | ||
private loggerContextCb?: LoggerOptions['loggerContextCb']; | ||
constructor(useJSON: boolean = false, loggerOptions: LoggerOptions = {}) { | ||
this.log = getLogger(loggerOptions.loggerName ?? LoggerNames.Signal); | ||
this.loggerContextCb = loggerOptions.loggerContextCb; | ||
this.useJSON = useJSON; | ||
@@ -186,2 +193,6 @@ this.requestQueue = new AsyncQueue(); | ||
private get logContext() { | ||
return this.loggerContextCb?.() ?? {}; | ||
} | ||
async join( | ||
@@ -208,3 +219,6 @@ url: string, | ||
if (!this.options) { | ||
log.warn('attempted to reconnect without signal options being set, ignoring'); | ||
this.log.warn( | ||
'attempted to reconnect without signal options being set, ignoring', | ||
this.logContext, | ||
); | ||
return; | ||
@@ -258,3 +272,3 @@ } | ||
abortSignal?.addEventListener('abort', abortHandler); | ||
log.debug(`connecting to ${url + params}`); | ||
this.log.debug(`connecting to ${url + params}`, this.logContext); | ||
if (this.ws) { | ||
@@ -310,3 +324,6 @@ await this.close(); | ||
} else { | ||
log.error(`could not decode websocket message: ${typeof ev.data}`); | ||
this.log.error( | ||
`could not decode websocket message: ${typeof ev.data}`, | ||
this.logContext, | ||
); | ||
return; | ||
@@ -325,3 +342,4 @@ } | ||
if (this.pingTimeoutDuration && this.pingTimeoutDuration > 0) { | ||
log.debug('ping config', { | ||
this.log.debug('ping config', { | ||
...this.logContext, | ||
timeout: this.pingTimeoutDuration, | ||
@@ -364,3 +382,3 @@ interval: this.pingIntervalDuration, | ||
this.ws.onclose = (ev: CloseEvent) => { | ||
log.warn(`websocket closed`, { ev }); | ||
this.log.warn(`websocket closed`, { ...this.logContext, reason: ev.reason }); | ||
this.handleOnClose(ev.reason); | ||
@@ -425,3 +443,3 @@ }; | ||
sendOffer(offer: RTCSessionDescriptionInit) { | ||
log.debug('sending offer', offer); | ||
this.log.debug('sending offer', { ...this.logContext, offerSdp: offer.sdp }); | ||
this.sendRequest({ | ||
@@ -435,3 +453,3 @@ case: 'offer', | ||
sendAnswer(answer: RTCSessionDescriptionInit) { | ||
log.debug('sending answer'); | ||
this.log.debug('sending answer', { ...this.logContext, answerSdp: answer.sdp }); | ||
return this.sendRequest({ | ||
@@ -444,3 +462,3 @@ case: 'answer', | ||
sendIceCandidate(candidate: RTCIceCandidateInit, target: SignalTarget) { | ||
log.trace('sending ice candidate', candidate); | ||
this.log.trace('sending ice candidate', { ...this.logContext, candidate }); | ||
return this.sendRequest({ | ||
@@ -575,3 +593,6 @@ case: 'trickle', | ||
if (!this.ws || this.ws.readyState !== this.ws.OPEN) { | ||
log.error(`cannot send signal request before connected, type: ${message?.case}`); | ||
this.log.error( | ||
`cannot send signal request before connected, type: ${message?.case}`, | ||
this.logContext, | ||
); | ||
return; | ||
@@ -588,3 +609,3 @@ } | ||
} catch (e) { | ||
log.error('error sending signal message', { error: e }); | ||
this.log.error('error sending signal message', { ...this.logContext, error: e }); | ||
} | ||
@@ -596,3 +617,3 @@ } | ||
if (msg == undefined) { | ||
log.debug('received unsupported message'); | ||
this.log.debug('received unsupported message', this.logContext); | ||
return; | ||
@@ -675,3 +696,3 @@ } | ||
} else { | ||
log.debug('unsupported message', msg); | ||
this.log.debug('unsupported message', { ...this.logContext, msgCase: msg.case }); | ||
} | ||
@@ -697,3 +718,3 @@ | ||
await this.close(); | ||
log.debug(`websocket connection closed: ${reason}`); | ||
this.log.debug(`websocket connection closed: ${reason}`, { ...this.logContext, reason }); | ||
if (onCloseCallback) { | ||
@@ -705,3 +726,3 @@ onCloseCallback(reason); | ||
private handleWSError(ev: Event) { | ||
log.error('websocket error', ev); | ||
this.log.error('websocket error', { ...this.logContext, error: ev }); | ||
} | ||
@@ -716,10 +737,11 @@ | ||
if (!this.pingTimeoutDuration) { | ||
log.warn('ping timeout duration not set'); | ||
this.log.warn('ping timeout duration not set', this.logContext); | ||
return; | ||
} | ||
this.pingTimeout = CriticalTimers.setTimeout(() => { | ||
log.warn( | ||
this.log.warn( | ||
`ping timeout triggered. last pong received at: ${new Date( | ||
Date.now() - this.pingTimeoutDuration! * 1000, | ||
).toUTCString()}`, | ||
this.logContext, | ||
); | ||
@@ -743,6 +765,6 @@ this.handleOnClose('ping timeout'); | ||
if (!this.pingIntervalDuration) { | ||
log.warn('ping interval duration not set'); | ||
this.log.warn('ping interval duration not set', this.logContext); | ||
return; | ||
} | ||
log.debug('start ping interval'); | ||
this.log.debug('start ping interval', this.logContext); | ||
this.pingInterval = CriticalTimers.setInterval(() => { | ||
@@ -754,3 +776,3 @@ this.sendPing(); | ||
private clearPingInterval() { | ||
log.debug('clearing ping interval'); | ||
this.log.debug('clearing ping interval', this.logContext); | ||
this.clearPingTimeout(); | ||
@@ -757,0 +779,0 @@ if (this.pingInterval) { |
@@ -1,2 +0,2 @@ | ||
import { LogLevel, setLogExtension, setLogLevel } from './logger'; | ||
import { LogLevel, getLogger, setLogExtension, setLogLevel } from './logger'; | ||
import { DataPacket_Kind, DisconnectReason, VideoQuality } from './proto/livekit_models_pb'; | ||
@@ -58,2 +58,3 @@ import DefaultReconnectPolicy from './room/DefaultReconnectPolicy'; | ||
LogLevel, | ||
getLogger, | ||
Room, | ||
@@ -60,0 +61,0 @@ ConnectionState, |
@@ -12,5 +12,18 @@ import * as log from 'loglevel'; | ||
export enum LoggerNames { | ||
Default = 'livekit', | ||
Room = 'livekit-room', | ||
Participant = 'livekit-participant', | ||
Track = 'livekit-track', | ||
Publication = 'livekit-track-publication', | ||
Engine = 'livekit-engine', | ||
Signal = 'livekit-signal', | ||
PCManager = 'livekit-pc-manager', | ||
PCTransport = 'livekit-pc-transport', | ||
E2EE = 'lk-e2ee', | ||
} | ||
type LogLevelString = keyof typeof LogLevel; | ||
type StructuredLogger = { | ||
export type StructuredLogger = { | ||
trace: (msg: string, context?: object) => void; | ||
@@ -24,3 +37,3 @@ debug: (msg: string, context?: object) => void; | ||
const livekitLogger = log.getLogger('livekit'); | ||
let livekitLogger = log.getLogger('livekit'); | ||
@@ -31,7 +44,18 @@ livekitLogger.setDefaultLevel(LogLevel.info); | ||
export function setLogLevel(level: LogLevel | LogLevelString, loggerName?: 'livekit' | 'lk-e2ee') { | ||
/** | ||
* @internal | ||
*/ | ||
export function getLogger(name: string) { | ||
const logger = log.getLogger(name); | ||
logger.setDefaultLevel(livekitLogger.getLevel()); | ||
return logger as StructuredLogger; | ||
} | ||
export function setLogLevel(level: LogLevel | LogLevelString, loggerName?: LoggerNames) { | ||
if (loggerName) { | ||
log.getLogger(loggerName).setLevel(level); | ||
} | ||
for (const logger of Object.values(log.getLoggers())) { | ||
for (const logger of Object.entries(log.getLoggers()) | ||
.filter(([logrName]) => logrName.startsWith('livekit')) | ||
.map(([, logr]) => logr)) { | ||
logger.setLevel(level); | ||
@@ -47,6 +71,6 @@ } | ||
*/ | ||
export function setLogExtension(extension: LogExtension) { | ||
const originalFactory = livekitLogger.methodFactory; | ||
export function setLogExtension(extension: LogExtension, logger = livekitLogger) { | ||
const originalFactory = logger.methodFactory; | ||
livekitLogger.methodFactory = (methodName, configLevel, loggerName) => { | ||
logger.methodFactory = (methodName, configLevel, loggerName) => { | ||
const rawMethod = originalFactory(methodName, configLevel, loggerName); | ||
@@ -65,5 +89,5 @@ | ||
}; | ||
livekitLogger.setLevel(livekitLogger.getLevel()); // Be sure to call setLevel method in order to apply plugin | ||
logger.setLevel(logger.getLevel()); // Be sure to call setLevel method in order to apply plugin | ||
} | ||
export const workerLogger = log.getLogger('lk-e2ee') as StructuredLogger; |
@@ -95,2 +95,4 @@ import type { E2EEOptions } from './e2ee/types'; | ||
e2ee?: E2EEOptions; | ||
loggerName?: string; | ||
} | ||
@@ -97,0 +99,0 @@ |
@@ -1,2 +0,1 @@ | ||
import log from '../../logger'; | ||
import type { InternalRoomOptions } from '../../options'; | ||
@@ -35,5 +34,6 @@ import { | ||
} from '../track/options'; | ||
import { VideoPresets, isBackupCodec } from '../track/options'; | ||
import { ScreenSharePresets, VideoPresets, isBackupCodec } from '../track/options'; | ||
import { | ||
constraintsForOptions, | ||
getLogContextFromTrack, | ||
mergeDefaultOptions, | ||
@@ -44,3 +44,12 @@ mimeTypeToVideoCodecString, | ||
import type { DataPublishOptions } from '../types'; | ||
import { Future, isFireFox, isSVCCodec, isSafari, isWeb, supportsAV1, supportsVP9 } from '../utils'; | ||
import { | ||
Future, | ||
isFireFox, | ||
isSVCCodec, | ||
isSafari, | ||
isSafari17, | ||
isWeb, | ||
supportsAV1, | ||
supportsVP9, | ||
} from '../utils'; | ||
import Participant from './Participant'; | ||
@@ -91,3 +100,6 @@ import type { ParticipantTrackPermission } from './ParticipantTrackPermission'; | ||
constructor(sid: string, identity: string, engine: RTCEngine, options: InternalRoomOptions) { | ||
super(sid, identity); | ||
super(sid, identity, undefined, undefined, { | ||
loggerName: options.loggerName, | ||
loggerContextCb: () => this.engine.logContext, | ||
}); | ||
this.audioTracks = new Map(); | ||
@@ -170,3 +182,3 @@ this.videoTracks = new Map(); | ||
if (this.reconnectFuture) { | ||
this.reconnectFuture.promise.catch((e) => log.warn(e)); | ||
this.reconnectFuture.promise.catch((e) => this.log.warn(e.message, this.logContext)); | ||
this.reconnectFuture?.reject?.('Got disconnected during reconnection attempt'); | ||
@@ -284,3 +296,3 @@ this.reconnectFuture = undefined; | ||
) { | ||
log.debug('setTrackEnabled', { source, enabled }); | ||
this.log.debug('setTrackEnabled', { ...this.logContext, source, enabled }); | ||
let track = this.getTrack(source); | ||
@@ -293,3 +305,3 @@ if (enabled) { | ||
if (this.pendingPublishing.has(source)) { | ||
log.info('skipping duplicate published source', { source }); | ||
this.log.info('skipping duplicate published source', { ...this.logContext, source }); | ||
// no-op it's already been requested | ||
@@ -322,3 +334,6 @@ return; | ||
for (const localTrack of localTracks) { | ||
log.info('publishing track', { localTrack }); | ||
this.log.info('publishing track', { | ||
...this.logContext, | ||
...getLogContextFromTrack(localTrack), | ||
}); | ||
publishPromises.push(this.publishTrack(localTrack, publishOptions)); | ||
@@ -433,3 +448,6 @@ } | ||
} | ||
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints); | ||
const track = mediaTrackToLocalTrack(mediaStreamTrack, trackConstraints, { | ||
loggerName: this.roomOptions.loggerName, | ||
loggerContextCb: () => this.logContext, | ||
}); | ||
if (track.kind === Track.Kind.Video) { | ||
@@ -459,2 +477,9 @@ track.source = Track.Source.Camera; | ||
if (options.resolution === undefined && !isSafari17()) { | ||
// we need to constrain the dimensions, otherwise it could lead to low bitrate | ||
// due to encoding a huge video. Encoding such large surfaces is really expensive | ||
// unfortunately Safari 17 has a but and cannot be constrained by default | ||
options.resolution = ScreenSharePresets.h1080fps30.resolution; | ||
} | ||
const constraints = screenCaptureToDisplayMediaStreamOptions(options); | ||
@@ -467,4 +492,11 @@ const stream: MediaStream = await navigator.mediaDevices.getDisplayMedia(constraints); | ||
} | ||
const screenVideo = new LocalVideoTrack(tracks[0], undefined, false); | ||
const screenVideo = new LocalVideoTrack(tracks[0], undefined, false, { | ||
loggerName: this.roomOptions.loggerName, | ||
loggerContextCb: () => this.logContext, | ||
}); | ||
screenVideo.source = Track.Source.ScreenShare; | ||
if (options.contentHint) { | ||
screenVideo.mediaStreamTrack.contentHint = options.contentHint; | ||
} | ||
const localTracks: Array<LocalTrack> = [screenVideo]; | ||
@@ -478,2 +510,3 @@ if (stream.getAudioTracks().length > 0) { | ||
this.audioContext, | ||
{ loggerName: this.roomOptions.loggerName, loggerContextCb: () => this.logContext }, | ||
); | ||
@@ -527,6 +560,12 @@ screenAudio.source = Track.Source.ScreenShareAudio; | ||
case 'audio': | ||
track = new LocalAudioTrack(track, defaultConstraints, true, this.audioContext); | ||
track = new LocalAudioTrack(track, defaultConstraints, true, this.audioContext, { | ||
loggerName: this.roomOptions.loggerName, | ||
loggerContextCb: () => this.logContext, | ||
}); | ||
break; | ||
case 'video': | ||
track = new LocalVideoTrack(track, defaultConstraints, true); | ||
track = new LocalVideoTrack(track, defaultConstraints, true, { | ||
loggerName: this.roomOptions.loggerName, | ||
loggerContextCb: () => this.logContext, | ||
}); | ||
break; | ||
@@ -536,2 +575,7 @@ default: | ||
} | ||
} else { | ||
track.updateLoggerOptions({ | ||
loggerName: this.roomOptions.loggerName, | ||
loggerContextCb: () => this.logContext, | ||
}); | ||
} | ||
@@ -555,3 +599,6 @@ | ||
if (existingPublication) { | ||
log.warn('track has already been published, skipping'); | ||
this.log.warn('track has already been published, skipping', { | ||
...this.logContext, | ||
...getLogContextFromTrack(existingPublication), | ||
}); | ||
return existingPublication; | ||
@@ -573,8 +620,12 @@ } | ||
if (options.dtx === undefined) { | ||
log.info( | ||
this.log.info( | ||
`Opus DTX will be disabled for stereo tracks by default. Enable them explicitly to make it work.`, | ||
{ | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}, | ||
); | ||
} | ||
if (options.red === undefined) { | ||
log.info( | ||
this.log.info( | ||
`Opus RED will be disabled for stereo tracks by default. Enable them explicitly to make it work.`, | ||
@@ -593,3 +644,8 @@ ); | ||
if (isSafari() && this.roomOptions.e2ee) { | ||
log.info(`End-to-end encryption is set up, simulcast publishing will be disabled on Safari`); | ||
this.log.info( | ||
`End-to-end encryption is set up, simulcast publishing will be disabled on Safari`, | ||
{ | ||
...this.logContext, | ||
}, | ||
); | ||
opts.simulcast = false; | ||
@@ -618,3 +674,6 @@ } | ||
if (existingTrackOfSource && track.source !== Track.Source.Unknown) { | ||
log.info(`publishing a second track with the same source: ${track.source}`); | ||
this.log.info(`publishing a second track with the same source: ${track.source}`, { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
} | ||
@@ -684,3 +743,7 @@ if (opts.stopMicTrackOnMute && track instanceof LocalAudioTrack) { | ||
// log failure | ||
log.error('could not determine track dimensions, using defaults', dims); | ||
this.log.error('could not determine track dimensions, using defaults', { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
dims, | ||
}); | ||
} | ||
@@ -769,3 +832,7 @@ // width and height should be defined for video | ||
if (updatedCodec !== videoCodec) { | ||
log.debug('falling back to server selected codec', { codec: updatedCodec }); | ||
this.log.debug('falling back to server selected codec', { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
codec: updatedCodec, | ||
}); | ||
/* @ts-ignore */ | ||
@@ -784,3 +851,6 @@ opts.videoCodec = updatedCodec; | ||
const publication = new LocalTrackPublication(track.kind, ti, track); | ||
const publication = new LocalTrackPublication(track.kind, ti, track, { | ||
loggerName: this.roomOptions.loggerName, | ||
loggerContextCb: () => this.logContext, | ||
}); | ||
// save options for when it needs to be republished again | ||
@@ -793,3 +863,7 @@ publication.options = opts; | ||
} | ||
log.debug(`publishing ${track.kind} with encodings`, { encodings, trackInfo: ti }); | ||
this.log.debug(`publishing ${track.kind} with encodings`, { | ||
...this.logContext, | ||
encodings, | ||
trackInfo: ti, | ||
}); | ||
@@ -887,4 +961,8 @@ track.sender = await this.engine.createSender(track, opts, encodings); | ||
if (!encodings) { | ||
log.info( | ||
this.log.info( | ||
`backup codec has been disabled, ignoring request to add additional codec for track`, | ||
{ | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}, | ||
); | ||
@@ -922,3 +1000,7 @@ return; | ||
await this.engine.negotiate(); | ||
log.debug(`published ${videoCodec} for track ${track.sid}`, { encodings, trackInfo: ti }); | ||
this.log.debug(`published ${videoCodec} for track ${track.sid}`, { | ||
...this.logContext, | ||
encodings, | ||
trackInfo: ti, | ||
}); | ||
} | ||
@@ -933,8 +1015,13 @@ | ||
log.debug('unpublishing track', { track, method: 'unpublishTrack' }); | ||
const pubLogContext = publication ? getLogContextFromTrack(publication) : undefined; | ||
this.log.debug('unpublishing track', { | ||
...this.logContext, | ||
...pubLogContext, | ||
}); | ||
if (!publication || !publication.track) { | ||
log.warn('track was not unpublished because no publication was found', { | ||
track, | ||
method: 'unpublishTrack', | ||
this.log.warn('track was not unpublished because no publication was found', { | ||
...this.logContext, | ||
...pubLogContext, | ||
}); | ||
@@ -992,3 +1079,7 @@ return undefined; | ||
} catch (e) { | ||
log.warn('failed to unpublish track', { error: e, method: 'unpublishTrack' }); | ||
this.log.warn('failed to unpublish track', { | ||
...this.logContext, | ||
...pubLogContext, | ||
error: e, | ||
}); | ||
} | ||
@@ -1051,3 +1142,4 @@ } | ||
// is necessary because computer had gone to sleep. | ||
log.debug('restarting existing track', { | ||
this.log.debug('restarting existing track', { | ||
...this.logContext, | ||
track: pub.trackSid, | ||
@@ -1181,5 +1273,6 @@ }); | ||
if (mutedOnServer !== ti.muted) { | ||
log.debug('updating server mute state after reconcile', { | ||
sid: ti.sid, | ||
muted: mutedOnServer, | ||
this.log.debug('updating server mute state after reconcile', { | ||
...this.logContext, | ||
...getLogContextFromTrack(pub), | ||
mutedOnServer, | ||
}); | ||
@@ -1194,3 +1287,4 @@ this.engine.client.sendMuteTrack(ti.sid, mutedOnServer); | ||
private updateTrackSubscriptionPermissions = () => { | ||
log.debug('updating track subscription permissions', { | ||
this.log.debug('updating track subscription permissions', { | ||
...this.logContext, | ||
allParticipantsAllowed: this.allParticipantsAllowedToSubscribe, | ||
@@ -1218,3 +1312,6 @@ participantTrackPermissions: this.participantTrackPermissions, | ||
if (!track.sid) { | ||
log.error('could not update mute status for unpublished track', track); | ||
this.log.error('could not update mute status for unpublished track', { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
return; | ||
@@ -1227,3 +1324,6 @@ } | ||
private onTrackUpstreamPaused = (track: LocalTrack) => { | ||
log.debug('upstream paused'); | ||
this.log.debug('upstream paused', { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
this.onTrackMuted(track, true); | ||
@@ -1233,3 +1333,6 @@ }; | ||
private onTrackUpstreamResumed = (track: LocalTrack) => { | ||
log.debug('upstream resumed'); | ||
this.log.debug('upstream resumed', { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
this.onTrackMuted(track, track.isMuted); | ||
@@ -1244,5 +1347,5 @@ }; | ||
if (!pub) { | ||
log.warn('received subscribed quality update for unknown track', { | ||
method: 'handleSubscribedQualityUpdate', | ||
sid: update.trackSid, | ||
this.log.warn('received subscribed quality update for unknown track', { | ||
...this.logContext, | ||
trackSid: update.trackSid, | ||
}); | ||
@@ -1258,3 +1361,6 @@ return; | ||
if (isBackupCodec(codec)) { | ||
log.debug(`publish ${codec} for ${pub.videoTrack.sid}`); | ||
this.log.debug(`publish ${codec} for ${pub.videoTrack.sid}`, { | ||
...this.logContext, | ||
...getLogContextFromTrack(pub), | ||
}); | ||
await this.publishAdditionalCodecForTrack(pub.videoTrack, codec, pub.options); | ||
@@ -1271,4 +1377,4 @@ } | ||
if (!track) { | ||
log.warn('received unpublished event for unknown track', { | ||
method: 'handleLocalTrackUnpublished', | ||
this.log.warn('received unpublished event for unknown track', { | ||
...this.logContext, | ||
trackSid: unpublished.trackSid, | ||
@@ -1286,4 +1392,5 @@ }); | ||
) { | ||
log.debug('unpublishing local track due to TrackEnded', { | ||
track: track.sid, | ||
this.log.debug('unpublishing local track due to TrackEnded', { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
@@ -1303,3 +1410,6 @@ this.unpublishTrack(track); | ||
if (currentPermissions && currentPermissions.state === 'denied') { | ||
log.warn(`user has revoked access to ${track.source}`); | ||
this.log.warn(`user has revoked access to ${track.source}`, { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
@@ -1322,7 +1432,13 @@ // detect granted change after permissions were denied to try and resume then | ||
if (!track.isMuted) { | ||
log.debug('track ended, attempting to use a different device'); | ||
this.log.debug('track ended, attempting to use a different device', { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
await track.restartTrack(); | ||
} | ||
} catch (e) { | ||
log.warn(`could not restart track, muting instead`); | ||
this.log.warn(`could not restart track, muting instead`, { | ||
...this.logContext, | ||
...getLogContextFromTrack(track), | ||
}); | ||
await track.mute(); | ||
@@ -1329,0 +1445,0 @@ } |
import { EventEmitter } from 'events'; | ||
import type TypedEmitter from 'typed-emitter'; | ||
import log from '../../logger'; | ||
import log, { LoggerNames, StructuredLogger, getLogger } from '../../logger'; | ||
import { | ||
@@ -19,2 +19,3 @@ DataPacket_Kind, | ||
import type { TrackPublication } from '../track/TrackPublication'; | ||
import type { LoggerOptions } from '../types'; | ||
@@ -84,2 +85,14 @@ export enum ConnectionQuality { | ||
protected log: StructuredLogger = log; | ||
protected loggerOptions?: LoggerOptions; | ||
protected get logContext() { | ||
return { | ||
...this.loggerOptions?.loggerContextCb?.(), | ||
participantSid: this.sid, | ||
participantId: this.identity, | ||
}; | ||
} | ||
get isEncrypted() { | ||
@@ -94,4 +107,14 @@ return this.tracks.size > 0 && Array.from(this.tracks.values()).every((tr) => tr.isEncrypted); | ||
/** @internal */ | ||
constructor(sid: string, identity: string, name?: string, metadata?: string) { | ||
constructor( | ||
sid: string, | ||
identity: string, | ||
name?: string, | ||
metadata?: string, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(); | ||
this.log = getLogger(loggerOptions?.loggerName ?? LoggerNames.Participant); | ||
this.loggerOptions = loggerOptions; | ||
this.setMaxListeners(100); | ||
@@ -193,3 +216,3 @@ this.sid = sid; | ||
this.participantInfo = info; | ||
log.trace('update participant info', { info }); | ||
this.log.trace('update participant info', { ...this.logContext, info }); | ||
return true; | ||
@@ -196,0 +219,0 @@ } |
@@ -186,5 +186,5 @@ import { describe, expect, it } from 'vitest'; | ||
expect(defaultSimulcastLayers[0].height).toBe(360); | ||
expect(defaultSimulcastLayers[0].encoding.maxFramerate).toBe(3); | ||
expect(defaultSimulcastLayers[0].encoding.maxBitrate).toBe(150_000); | ||
expect(defaultSimulcastLayers[0].encoding.maxFramerate).toBe(15); | ||
expect(defaultSimulcastLayers[0].encoding.maxBitrate).toBe(375000); | ||
}); | ||
}); |
@@ -13,2 +13,3 @@ import log from '../../logger'; | ||
import { ScreenSharePresets, VideoPreset, VideoPresets, VideoPresets43 } from '../track/options'; | ||
import type { LoggerOptions } from '../types'; | ||
import { getReactNativeOs, isFireFox, isReactNative, isSVCCodec } from '../utils'; | ||
@@ -20,8 +21,9 @@ | ||
constraints?: MediaTrackConstraints, | ||
loggerOptions?: LoggerOptions, | ||
): LocalVideoTrack | LocalAudioTrack { | ||
switch (mediaStreamTrack.kind) { | ||
case 'audio': | ||
return new LocalAudioTrack(mediaStreamTrack, constraints, false); | ||
return new LocalAudioTrack(mediaStreamTrack, constraints, false, undefined, loggerOptions); | ||
case 'video': | ||
return new LocalVideoTrack(mediaStreamTrack, constraints, false); | ||
return new LocalVideoTrack(mediaStreamTrack, constraints, false, loggerOptions); | ||
default: | ||
@@ -49,3 +51,3 @@ throw new TrackInvalidError(`unsupported track type: ${mediaStreamTrack.kind}`); | ||
export const computeDefaultScreenShareSimulcastPresets = (fromPreset: VideoPreset) => { | ||
const layers = [{ scaleResolutionDownBy: 2, fps: 3 }]; | ||
const layers = [{ scaleResolutionDownBy: 2, fps: fromPreset.encoding.maxFramerate }]; | ||
return layers.map( | ||
@@ -60,3 +62,4 @@ (t) => | ||
fromPreset.encoding.maxBitrate / | ||
(t.scaleResolutionDownBy ** 2 * ((fromPreset.encoding.maxFramerate ?? 30) / t.fps)), | ||
(t.scaleResolutionDownBy ** 2 * | ||
((fromPreset.encoding.maxFramerate ?? 30) / (t.fps ?? 30))), | ||
), | ||
@@ -63,0 +66,0 @@ ), |
import type { SignalClient } from '../../api/SignalClient'; | ||
import log from '../../logger'; | ||
import type { ParticipantInfo, SubscriptionError } from '../../proto/livekit_models_pb'; | ||
@@ -14,2 +13,4 @@ import type { UpdateSubscription, UpdateTrackSettings } from '../../proto/livekit_rtc_pb'; | ||
import type { AdaptiveStreamSettings } from '../track/types'; | ||
import { getLogContextFromTrack } from '../track/utils'; | ||
import type { LoggerOptions } from '../types'; | ||
import Participant from './Participant'; | ||
@@ -43,4 +44,5 @@ import type { ParticipantEventCallbacks } from './Participant'; | ||
metadata?: string, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(sid, identity || '', name, metadata); | ||
super(sid, identity || '', name, metadata, loggerOptions); | ||
this.signalClient = signalClient; | ||
@@ -58,3 +60,6 @@ this.tracks = new Map(); | ||
publication.on(TrackEvent.UpdateSettings, (settings: UpdateTrackSettings) => { | ||
log.debug('send update settings', settings); | ||
this.log.debug('send update settings', { | ||
...this.logContext, | ||
...getLogContextFromTrack(publication), | ||
}); | ||
this.signalClient.sendUpdateTrackSettings(settings); | ||
@@ -165,3 +170,6 @@ }); | ||
if (triesLeft === 0) { | ||
log.error('could not find published track', { participant: this.sid, trackSid: sid }); | ||
this.log.error('could not find published track', { | ||
...this.logContext, | ||
trackSid: sid, | ||
}); | ||
this.emit(ParticipantEvent.TrackSubscriptionFailed, sid); | ||
@@ -186,5 +194,5 @@ return; | ||
if (mediaTrack.readyState === 'ended') { | ||
log.error( | ||
this.log.error( | ||
'unable to subscribe because MediaStreamTrack is ended. Do not call MediaStreamTrack.stop()', | ||
{ participant: this.sid, trackSid: sid }, | ||
{ ...this.logContext, ...getLogContextFromTrack(publication) }, | ||
); | ||
@@ -254,2 +262,3 @@ this.emit(ParticipantEvent.TrackSubscriptionFailed, sid); | ||
this.signalClient.connectOptions?.autoSubscribe, | ||
{ loggerContextCb: () => this.logContext, loggerName: this.loggerOptions?.loggerName }, | ||
); | ||
@@ -262,9 +271,8 @@ publication.updateInfo(ti); | ||
if (existingTrackOfSource && publication.source !== Track.Source.Unknown) { | ||
log.debug( | ||
this.log.debug( | ||
`received a second track publication for ${this.identity} with the same source: ${publication.source}`, | ||
{ | ||
oldTrack: existingTrackOfSource, | ||
newTrack: publication, | ||
participant: this, | ||
participantInfo: info, | ||
...this.logContext, | ||
oldTrack: getLogContextFromTrack(existingTrackOfSource), | ||
newTrack: getLogContextFromTrack(publication), | ||
}, | ||
@@ -283,5 +291,5 @@ ); | ||
if (!validTracks.has(publication.trackSid)) { | ||
log.trace('detected removed track on remote participant, unpublishing', { | ||
publication, | ||
participantSid: this.sid, | ||
this.log.trace('detected removed track on remote participant, unpublishing', { | ||
...this.logContext, | ||
...getLogContextFromTrack(publication), | ||
}); | ||
@@ -352,5 +360,5 @@ this.unpublishTrack(publication.trackSid, true); | ||
): boolean { | ||
log.trace('participant event', { participant: this.sid, event, args }); | ||
this.log.trace('participant event', { ...this.logContext, event, args }); | ||
return super.emit(event, ...args); | ||
} | ||
} |
@@ -5,4 +5,5 @@ import { EventEmitter } from 'events'; | ||
import { debounce } from 'ts-debounce'; | ||
import log from '../logger'; | ||
import log, { LoggerNames, getLogger } from '../logger'; | ||
import { NegotiationError, UnexpectedConnectionState } from './errors'; | ||
import type { LoggerOptions } from './types'; | ||
import { ddExtensionURI, isChromiumBased, isSVCCodec } from './utils'; | ||
@@ -47,2 +48,6 @@ | ||
private log = log; | ||
private loggerOptions: LoggerOptions; | ||
pendingCandidates: RTCIceCandidateInit[] = []; | ||
@@ -76,4 +81,10 @@ | ||
constructor(config?: RTCConfiguration, mediaConstraints: Record<string, unknown> = {}) { | ||
constructor( | ||
config?: RTCConfiguration, | ||
mediaConstraints: Record<string, unknown> = {}, | ||
loggerOptions: LoggerOptions = {}, | ||
) { | ||
super(); | ||
this.log = getLogger(loggerOptions.loggerName ?? LoggerNames.PCTransport); | ||
this.loggerOptions = loggerOptions; | ||
this.config = config; | ||
@@ -118,2 +129,8 @@ this.mediaConstraints = mediaConstraints; | ||
private get logContext() { | ||
return { | ||
...this.loggerOptions.loggerContextCb?.(), | ||
}; | ||
} | ||
get isICEConnected(): boolean { | ||
@@ -236,3 +253,3 @@ return ( | ||
if (options?.iceRestart) { | ||
log.debug('restarting ICE'); | ||
this.log.debug('restarting ICE', this.logContext); | ||
this.restartingIce = true; | ||
@@ -254,3 +271,3 @@ } | ||
} else if (!this._pc || this._pc.signalingState === 'closed') { | ||
log.warn('could not createOffer with closed peer connection'); | ||
this.log.warn('could not createOffer with closed peer connection', this.logContext); | ||
return; | ||
@@ -260,3 +277,3 @@ } | ||
// actually negotiate | ||
log.debug('starting to negotiate'); | ||
this.log.debug('starting to negotiate', this.logContext); | ||
const offer = await this.pc.createOffer(options); | ||
@@ -462,3 +479,6 @@ | ||
try { | ||
log.debug(`setting munged ${remote ? 'remote' : 'local'} description`); | ||
this.log.debug( | ||
`setting munged ${remote ? 'remote' : 'local'} description`, | ||
this.logContext, | ||
); | ||
if (remote) { | ||
@@ -471,3 +491,4 @@ await this.pc.setRemoteDescription(sd); | ||
} catch (e) { | ||
log.warn(`not able to set ${sd.type}, falling back to unmodified sdp`, { | ||
this.log.warn(`not able to set ${sd.type}, falling back to unmodified sdp`, { | ||
...this.logContext, | ||
error: e, | ||
@@ -503,3 +524,3 @@ sdp: munged, | ||
} | ||
log.error(`unable to set ${sd.type}`, fields); | ||
this.log.error(`unable to set ${sd.type}`, { ...this.logContext, fields }); | ||
throw new NegotiationError(msg); | ||
@@ -506,0 +527,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import log from '../logger'; | ||
import log, { LoggerNames, getLogger } from '../logger'; | ||
import { SignalTarget } from '../proto/livekit_rtc_pb'; | ||
@@ -7,2 +7,3 @@ import PCTransport, { PCEvents } from './PCTransport'; | ||
import CriticalTimers from './timers'; | ||
import type { LoggerOptions } from './types'; | ||
import { Mutex, sleep } from './utils'; | ||
@@ -60,8 +61,19 @@ | ||
constructor(rtcConfig: RTCConfiguration, subscriberPrimary: boolean) { | ||
private log = log; | ||
private loggerOptions: LoggerOptions; | ||
constructor( | ||
rtcConfig: RTCConfiguration, | ||
subscriberPrimary: boolean, | ||
loggerOptions: LoggerOptions, | ||
) { | ||
this.log = getLogger(loggerOptions.loggerName ?? LoggerNames.PCManager); | ||
this.loggerOptions = loggerOptions; | ||
this.isPublisherConnectionRequired = !subscriberPrimary; | ||
this.isSubscriberConnectionRequired = subscriberPrimary; | ||
const googConstraints = { optional: [{ googDscp: true }] }; | ||
this.publisher = new PCTransport(rtcConfig, googConstraints); | ||
this.subscriber = new PCTransport(rtcConfig); | ||
this.publisher = new PCTransport(rtcConfig, googConstraints, loggerOptions); | ||
this.subscriber = new PCTransport(rtcConfig, loggerOptions); | ||
@@ -96,2 +108,8 @@ this.publisher.onConnectionStateChange = this.updateState; | ||
private get logContext() { | ||
return { | ||
...this.loggerOptions.loggerContextCb?.(), | ||
}; | ||
} | ||
requirePublisher(require = true) { | ||
@@ -129,3 +147,3 @@ this.isPublisherConnectionRequired = require; | ||
} catch (e) { | ||
log.warn('could not removeTrack', { error: e }); | ||
this.log.warn('could not removeTrack', { ...this.logContext, error: e }); | ||
} | ||
@@ -155,3 +173,4 @@ } | ||
async createSubscriberAnswerFromOffer(sd: RTCSessionDescriptionInit) { | ||
log.debug('received server offer', { | ||
this.log.debug('received server offer', { | ||
...this.logContext, | ||
RTCSdpType: sd.type, | ||
@@ -183,3 +202,3 @@ signalingState: this.subscriber.getSignallingState().toString(), | ||
) { | ||
log.debug('negotiation required, start negotiating'); | ||
this.log.debug('negotiation required, start negotiating', this.logContext); | ||
this.publisher.negotiate(); | ||
@@ -280,6 +299,7 @@ } | ||
if (previousState !== this.state) { | ||
log.debug( | ||
this.log.debug( | ||
`pc state change: from ${PCTransportState[previousState]} to ${ | ||
PCTransportState[this.state] | ||
}`, | ||
this.logContext, | ||
); | ||
@@ -306,3 +326,3 @@ this.onStateChange?.( | ||
const abortHandler = () => { | ||
log.warn('abort transport connection'); | ||
this.log.warn('abort transport connection', this.logContext); | ||
CriticalTimers.clearTimeout(connectTimeout); | ||
@@ -309,0 +329,0 @@ |
@@ -10,3 +10,3 @@ import { EventEmitter } from 'events'; | ||
} from '../api/SignalClient'; | ||
import log from '../logger'; | ||
import log, { LoggerNames, getLogger } from '../logger'; | ||
import type { InternalRoomOptions } from '../options'; | ||
@@ -65,2 +65,3 @@ import { | ||
import { getTrackPublicationInfo } from './track/utils'; | ||
import type { LoggerOptions } from './types'; | ||
import { | ||
@@ -167,5 +168,14 @@ Mutex, | ||
private log = log; | ||
private loggerOptions: LoggerOptions; | ||
constructor(private options: InternalRoomOptions) { | ||
super(); | ||
this.client = new SignalClient(); | ||
this.log = getLogger(options.loggerName ?? LoggerNames.Engine); | ||
this.loggerOptions = { | ||
loggerName: options.loggerName, | ||
loggerContextCb: () => this.logContext, | ||
}; | ||
this.client = new SignalClient(undefined, this.loggerOptions); | ||
this.client.signalLatency = this.options.expSignalLatency; | ||
@@ -193,2 +203,11 @@ this.reconnectPolicy = this.options.reconnectPolicy; | ||
/** @internal */ | ||
get logContext() { | ||
return { | ||
room: this.latestJoinResponse?.room?.name, | ||
roomSid: this.latestJoinResponse?.room?.sid, | ||
identity: this.latestJoinResponse?.participant?.identity, | ||
}; | ||
} | ||
async join( | ||
@@ -227,4 +246,5 @@ url: string, | ||
if (e.reason === ConnectionErrorReason.ServerUnreachable) { | ||
log.warn( | ||
this.log.warn( | ||
`Couldn't connect to server, attempt ${this.joinAttempts} of ${this.maxJoinAttempts}`, | ||
this.logContext, | ||
); | ||
@@ -332,3 +352,3 @@ if (this.joinAttempts < this.maxJoinAttempts) { | ||
} catch (e: unknown) { | ||
log.warn('failed to remove track', { error: e, method: 'removeTrack' }); | ||
this.log.warn('failed to remove track', { ...this.logContext, error: e }); | ||
} | ||
@@ -365,3 +385,7 @@ return false; | ||
this.pcManager = new PCTransportManager(rtcConfig, joinResponse.subscriberPrimary); | ||
this.pcManager = new PCTransportManager( | ||
rtcConfig, | ||
joinResponse.subscriberPrimary, | ||
this.loggerOptions, | ||
); | ||
@@ -380,3 +404,3 @@ this.emit(EngineEvent.TransportsCreated, this.pcManager.publisher, this.pcManager.subscriber); | ||
this.pcManager.onStateChange = async (connectionState, publisherState, subscriberState) => { | ||
log.debug(`primary PC state changed ${connectionState}`); | ||
this.log.debug(`primary PC state changed ${connectionState}`, this.logContext); | ||
if (connectionState === PCTransportState.CONNECTED) { | ||
@@ -415,5 +439,3 @@ const shouldEmit = this.pcState === PCState.New; | ||
} | ||
log.debug('received server answer', { | ||
RTCSdpType: sd.type, | ||
}); | ||
this.log.debug('received server answer', { ...this.logContext, RTCSdpType: sd.type }); | ||
await this.pcManager.setPublisherAnswer(sd); | ||
@@ -427,3 +449,3 @@ }; | ||
} | ||
log.trace('got ICE candidate from peer', { candidate, target }); | ||
this.log.trace('got ICE candidate from peer', { ...this.logContext, candidate, target }); | ||
this.pcManager.addIceCandidate(candidate, target); | ||
@@ -442,5 +464,12 @@ }; | ||
this.client.onLocalTrackPublished = (res: TrackPublishedResponse) => { | ||
log.debug('received trackPublishedResponse', res); | ||
this.log.debug('received trackPublishedResponse', { | ||
...this.logContext, | ||
cid: res.cid, | ||
track: res.track?.sid, | ||
}); | ||
if (!this.pendingTrackResolvers[res.cid]) { | ||
log.error(`missing track resolver for ${res.cid}`); | ||
this.log.error(`missing track resolver for ${res.cid}`, { | ||
...this.logContext, | ||
cid: res.cid, | ||
}); | ||
return; | ||
@@ -478,3 +507,3 @@ } | ||
} | ||
log.trace('leave request', { leave }); | ||
this.log.debug('client leave request', { ...this.logContext, reason: leave?.reason }); | ||
}; | ||
@@ -487,3 +516,3 @@ } | ||
if (this.signalOpts?.e2eeEnabled) { | ||
log.debug('E2EE - setting up transports with insertable streams'); | ||
this.log.debug('E2EE - setting up transports with insertable streams', this.logContext); | ||
// this makes sure that no data is sent before the transforms are ready | ||
@@ -578,3 +607,3 @@ // @ts-ignore | ||
} | ||
log.debug(`on data channel ${channel.id}, ${channel.label}`); | ||
this.log.debug(`on data channel ${channel.id}, ${channel.label}`, this.logContext); | ||
channel.onmessage = this.handleDataMessage; | ||
@@ -594,3 +623,3 @@ }; | ||
} else { | ||
log.error('unsupported data type', message.data); | ||
this.log.error('unsupported data type', { ...this.logContext, data: message.data }); | ||
return; | ||
@@ -616,5 +645,8 @@ } | ||
const { error } = event.error; | ||
log.error(`DataChannel error on ${channelKind}: ${event.message}`, error); | ||
this.log.error(`DataChannel error on ${channelKind}: ${event.message}`, { | ||
...this.logContext, | ||
error, | ||
}); | ||
} else { | ||
log.error(`Unknown DataChannel error on ${channelKind}`, event); | ||
this.log.error(`Unknown DataChannel error on ${channelKind}`, { ...this.logContext, event }); | ||
} | ||
@@ -641,3 +673,3 @@ }; | ||
if (!cap) return; | ||
log.debug('get capabilities', cap); | ||
this.log.debug('get sender capabilities', { ...this.logContext, cap }); | ||
const matched: RTCRtpCodecCapability[] = []; | ||
@@ -686,3 +718,3 @@ const partialMatched: RTCRtpCodecCapability[] = []; | ||
if (supportsAddTrack()) { | ||
log.warn('using add-track fallback'); | ||
this.log.warn('using add-track fallback', this.logContext); | ||
const sender = await this.createRTCRtpSender(track.mediaStreamTrack); | ||
@@ -705,3 +737,3 @@ return sender; | ||
if (supportsAddTrack()) { | ||
log.debug('using add-track fallback'); | ||
this.log.debug('using add-track fallback', this.logContext); | ||
return this.createRTCRtpSender(track.mediaStreamTrack); | ||
@@ -786,3 +818,3 @@ } | ||
log.warn(`${connection} disconnected`); | ||
this.log.warn(`${connection} disconnected`, this.logContext); | ||
if (this.reconnectAttempts === 0) { | ||
@@ -794,4 +826,5 @@ // only reset start time on the first try | ||
const disconnect = (duration: number) => { | ||
log.warn( | ||
this.log.warn( | ||
`could not recover connection after ${this.reconnectAttempts} attempts, ${duration}ms. giving up`, | ||
this.logContext, | ||
); | ||
@@ -816,3 +849,3 @@ this.emit(EngineEvent.Disconnected); | ||
log.debug(`reconnecting in ${delay}ms`); | ||
this.log.debug(`reconnecting in ${delay}ms`, this.logContext); | ||
@@ -861,3 +894,3 @@ this.clearReconnectTimeout(); | ||
if (e instanceof UnexpectedConnectionState) { | ||
log.debug('received unrecoverable error', { error: e }); | ||
this.log.debug('received unrecoverable error', { ...this.logContext, error: e }); | ||
// unrecoverable | ||
@@ -873,6 +906,7 @@ recoverable = false; | ||
} else { | ||
log.info( | ||
this.log.info( | ||
`could not recover connection after ${this.reconnectAttempts} attempts, ${ | ||
Date.now() - this.reconnectStart | ||
}ms. giving up`, | ||
this.logContext, | ||
); | ||
@@ -891,3 +925,3 @@ this.emit(EngineEvent.Disconnected); | ||
} catch (e) { | ||
log.warn('encountered error in reconnect policy', { error: e }); | ||
this.log.warn('encountered error in reconnect policy', { ...this.logContext, error: e }); | ||
} | ||
@@ -906,3 +940,3 @@ | ||
log.info(`reconnecting, attempt: ${this.reconnectAttempts}`); | ||
this.log.info(`reconnecting, attempt: ${this.reconnectAttempts}`, this.logContext); | ||
this.emit(EngineEvent.Restarting); | ||
@@ -919,3 +953,6 @@ | ||
if (!this.signalOpts) { | ||
log.warn('attempted connection restart, without signal options present'); | ||
this.log.warn( | ||
'attempted connection restart, without signal options present', | ||
this.logContext, | ||
); | ||
throw new SignalReconnectError(); | ||
@@ -967,3 +1004,3 @@ } | ||
log.info(`resuming signal connection, attempt ${this.reconnectAttempts}`); | ||
this.log.info(`resuming signal connection, attempt ${this.reconnectAttempts}`, this.logContext); | ||
this.emit(EngineEvent.Resuming); | ||
@@ -982,3 +1019,3 @@ | ||
message = e.message; | ||
log.error(e.message); | ||
this.log.error(e.message, this.logContext); | ||
} | ||
@@ -1022,3 +1059,3 @@ if (e instanceof ConnectionError && e.reason === ConnectionErrorReason.NotAllowed) { | ||
log.debug('waiting for peer connection to reconnect'); | ||
this.log.debug('waiting for peer connection to reconnect', this.logContext); | ||
try { | ||
@@ -1169,3 +1206,3 @@ await sleep(minReconnectWait); // FIXME setTimeout again not ideal for a connection critical path | ||
abortController.abort(); | ||
log.debug('engine disconnected while negotiation was ongoing'); | ||
this.log.debug('engine disconnected while negotiation was ongoing', this.logContext); | ||
resolve(); | ||
@@ -1230,3 +1267,3 @@ return; | ||
if (!this.pcManager) { | ||
log.warn('sync state cannot be sent without peer connection setup'); | ||
this.log.warn('sync state cannot be sent without peer connection setup', this.logContext); | ||
return; | ||
@@ -1233,0 +1270,0 @@ } |
@@ -5,2 +5,3 @@ import DeviceManager from '../DeviceManager'; | ||
import { mediaTrackToLocalTrack } from '../participant/publishUtils'; | ||
import { isSafari17 } from '../utils'; | ||
import LocalAudioTrack from './LocalAudioTrack'; | ||
@@ -10,3 +11,2 @@ import type LocalTrack from './LocalTrack'; | ||
import { Track } from './Track'; | ||
import { ScreenSharePresets } from './options'; | ||
import type { | ||
@@ -18,2 +18,3 @@ AudioCaptureOptions, | ||
} from './options'; | ||
import { ScreenSharePresets } from './options'; | ||
import { | ||
@@ -122,4 +123,4 @@ constraintsForOptions, | ||
} | ||
if (options.resolution === undefined) { | ||
options.resolution = ScreenSharePresets.h1080fps15.resolution; | ||
if (options.resolution === undefined && !isSafari17()) { | ||
options.resolution = ScreenSharePresets.h1080fps30.resolution; | ||
} | ||
@@ -126,0 +127,0 @@ |
@@ -1,5 +0,5 @@ | ||
import log from '../../logger'; | ||
import { TrackEvent } from '../events'; | ||
import { computeBitrate, monitorFrequency } from '../stats'; | ||
import type { AudioSenderStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import { isWeb, unwrapConstraint } from '../utils'; | ||
@@ -31,4 +31,5 @@ import LocalTrack from './LocalTrack'; | ||
audioContext?: AudioContext, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(mediaTrack, Track.Kind.Audio, constraints, userProvidedTrack); | ||
super(mediaTrack, Track.Kind.Audio, constraints, userProvidedTrack, loggerOptions); | ||
this.audioContext = audioContext; | ||
@@ -56,3 +57,3 @@ this.checkForSilence(); | ||
if (this.source === Track.Source.Microphone && this.stopOnMute && !this.isUserProvided) { | ||
log.debug('stopping mic track'); | ||
this.log.debug('stopping mic track', this.logContext); | ||
// also stop the track, so that microphone indicator is turned off | ||
@@ -81,3 +82,3 @@ this._mediaStreamTrack.stop(); | ||
) { | ||
log.debug('reacquiring mic track'); | ||
this.log.debug('reacquiring mic track', this.logContext); | ||
await this.restartTrack(); | ||
@@ -133,3 +134,3 @@ } | ||
} catch (e) { | ||
log.error('could not get audio sender stats', { error: e }); | ||
this.log.error('could not get audio sender stats', { ...this.logContext, error: e }); | ||
return; | ||
@@ -165,3 +166,3 @@ } | ||
}; | ||
log.debug(`setting up audio processor ${processor.name}`); | ||
this.log.debug(`setting up audio processor ${processor.name}`, this.logContext); | ||
@@ -215,3 +216,3 @@ await processor.init(processorOptions); | ||
if (!this.isMuted) { | ||
log.warn('silence detected on local audio track'); | ||
this.log.warn('silence detected on local audio track', this.logContext); | ||
} | ||
@@ -218,0 +219,0 @@ this.emit(TrackEvent.AudioSilenceDetected); |
import { debounce } from 'ts-debounce'; | ||
import log from '../../logger'; | ||
import { getBrowser } from '../../utils/browserParser'; | ||
@@ -7,2 +6,3 @@ import DeviceManager from '../DeviceManager'; | ||
import { TrackEvent } from '../events'; | ||
import type { LoggerOptions } from '../types'; | ||
import { Mutex, compareVersions, isMobile, sleep } from '../utils'; | ||
@@ -54,4 +54,5 @@ import { Track, attachToElement, detachTrack } from './Track'; | ||
userProvidedTrack = false, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(mediaTrack, kind); | ||
super(mediaTrack, kind, loggerOptions); | ||
this.reacquireTrack = false; | ||
@@ -117,6 +118,2 @@ this.providedByUser = userProvidedTrack; | ||
this._mediaStreamTrack.removeEventListener('unmute', this.handleTrackUnmuteEvent); | ||
if (!this.providedByUser && this._mediaStreamTrack !== newTrack) { | ||
this._mediaStreamTrack.stop(); | ||
} | ||
} | ||
@@ -138,3 +135,3 @@ | ||
if (this.processor && newTrack && this.processorElement) { | ||
log.debug('restarting processor'); | ||
this.log.debug('restarting processor', this.logContext); | ||
if (this.kind === 'unknown') { | ||
@@ -157,2 +154,7 @@ throw TypeError('cannot set processor on track of unknown kind'); | ||
} | ||
// if `newTrack` is different from the existing track, stop the | ||
// older track just before replacing it | ||
if (!this.providedByUser && this._mediaStreamTrack !== newTrack) { | ||
this._mediaStreamTrack.stop(); | ||
} | ||
this._mediaStreamTrack = newTrack; | ||
@@ -221,3 +223,3 @@ if (newTrack) { | ||
log.debug('replace MediaStreamTrack'); | ||
this.log.debug('replace MediaStreamTrack', this.logContext); | ||
await this.setMediaStreamTrack(track); | ||
@@ -238,3 +240,3 @@ // this must be synced *after* setting mediaStreamTrack above, since it relies | ||
} | ||
log.debug('restarting track with constraints', constraints); | ||
this.log.debug('restarting track with constraints', { ...this.logContext, constraints }); | ||
@@ -267,3 +269,3 @@ const streamConstraints: MediaStreamConstraints = { | ||
newTrack.addEventListener('ended', this.handleEnded); | ||
log.debug('re-acquired MediaStreamTrack'); | ||
this.log.debug('re-acquired MediaStreamTrack', this.logContext); | ||
@@ -278,3 +280,3 @@ await this.setMediaStreamTrack(newTrack); | ||
protected setTrackMuted(muted: boolean) { | ||
log.debug(`setting ${this.kind} track ${muted ? 'muted' : 'unmuted'}`); | ||
this.log.debug(`setting ${this.kind} track ${muted ? 'muted' : 'unmuted'}`, this.logContext); | ||
@@ -302,6 +304,6 @@ if (this.isMuted === muted && this._mediaStreamTrack.enabled !== muted) { | ||
if (!isMobile()) return; | ||
log.debug(`visibility changed, is in Background: ${this.isInBackground}`); | ||
this.log.debug(`visibility changed, is in Background: ${this.isInBackground}`, this.logContext); | ||
if (!this.isInBackground && this.needsReAcquisition && !this.isUserProvided && !this.isMuted) { | ||
log.debug(`track needs to be reacquired, restarting ${this.source}`); | ||
this.log.debug(`track needs to be reacquired, restarting ${this.source}`, this.logContext); | ||
await this.restart(); | ||
@@ -314,3 +316,3 @@ this.reacquireTrack = false; | ||
this.debouncedTrackMuteHandler().catch(() => | ||
log.debug('track mute bounce got cancelled by an unmute event'), | ||
this.log.debug('track mute bounce got cancelled by an unmute event', this.logContext), | ||
); | ||
@@ -359,3 +361,3 @@ | ||
if (!this.sender) { | ||
log.warn('unable to pause upstream for an unpublished track'); | ||
this.log.warn('unable to pause upstream for an unpublished track', this.logContext); | ||
return; | ||
@@ -384,3 +386,3 @@ } | ||
if (!this.sender) { | ||
log.warn('unable to resume upstream for an unpublished track'); | ||
this.log.warn('unable to resume upstream for an unpublished track', this.logContext); | ||
return; | ||
@@ -425,3 +427,3 @@ } | ||
try { | ||
log.debug('setting up processor'); | ||
this.log.debug('setting up processor', this.logContext); | ||
if (this.processor) { | ||
@@ -440,3 +442,5 @@ await this.stopProcessor(); | ||
.play() | ||
.catch((error) => log.error('failed to play processor element', { error })); | ||
.catch((error) => | ||
this.log.error('failed to play processor element', { ...this.logContext, error }), | ||
); | ||
@@ -479,3 +483,3 @@ const processorOptions = { | ||
log.debug('stopping processor'); | ||
this.log.debug('stopping processor', this.logContext); | ||
this.processor.processedTrack?.stop(); | ||
@@ -482,0 +486,0 @@ await this.processor.destroy(); |
import type { TrackInfo } from '../../proto/livekit_models_pb'; | ||
import { TrackEvent } from '../events'; | ||
import type { LoggerOptions } from '../types'; | ||
import type LocalAudioTrack from './LocalAudioTrack'; | ||
@@ -19,4 +20,4 @@ import type LocalTrack from './LocalTrack'; | ||
constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack) { | ||
super(kind, ti.sid, ti.name); | ||
constructor(kind: Track.Kind, ti: TrackInfo, track?: LocalTrack, loggerOptions?: LoggerOptions) { | ||
super(kind, ti.sid, ti.name, loggerOptions); | ||
@@ -23,0 +24,0 @@ this.updateInfo(ti); |
import type { SignalClient } from '../../api/SignalClient'; | ||
import log from '../../logger'; | ||
import type { StructuredLogger } from '../../logger'; | ||
import { VideoLayer, VideoQuality } from '../../proto/livekit_models_pb'; | ||
@@ -8,2 +8,3 @@ import { SubscribedCodec, SubscribedQuality } from '../../proto/livekit_rtc_pb'; | ||
import { computeBitrate, monitorFrequency } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import { Mutex, isFireFox, isMobile, isWeb, unwrapConstraint } from '../utils'; | ||
@@ -62,4 +63,5 @@ import LocalTrack from './LocalTrack'; | ||
userProvidedTrack = true, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(mediaTrack, Track.Kind.Video, constraints, userProvidedTrack); | ||
super(mediaTrack, Track.Kind.Video, constraints, userProvidedTrack, loggerOptions); | ||
this.senderLock = new Mutex(); | ||
@@ -122,3 +124,3 @@ } | ||
if (this.source === Track.Source.Camera && !this.isUserProvided) { | ||
log.debug('stopping camera track'); | ||
this.log.debug('stopping camera track', this.logContext); | ||
// also stop the track, so that camera indicator is turned off | ||
@@ -138,3 +140,3 @@ this._mediaStreamTrack.stop(); | ||
if (this.source === Track.Source.Camera && !this.isUserProvided) { | ||
log.debug('reacquiring camera track'); | ||
this.log.debug('reacquiring camera track', this.logContext); | ||
await this.restartTrack(); | ||
@@ -209,3 +211,3 @@ } | ||
} | ||
log.debug(`setting publishing quality. max quality ${maxQuality}`); | ||
this.log.debug(`setting publishing quality. max quality ${maxQuality}`, this.logContext); | ||
this.setPublishingLayers(qualities); | ||
@@ -296,3 +298,4 @@ } | ||
async setPublishingCodecs(codecs: SubscribedCodec[]): Promise<VideoCodec[]> { | ||
log.debug('setting publishing codecs', { | ||
this.log.debug('setting publishing codecs', { | ||
...this.logContext, | ||
codecs, | ||
@@ -315,3 +318,6 @@ currentCodec: this.codec, | ||
const simulcastCodecInfo = this.simulcastCodecs.get(codec.codec as VideoCodec); | ||
log.debug(`try setPublishingCodec for ${codec.codec}`, simulcastCodecInfo); | ||
this.log.debug(`try setPublishingCodec for ${codec.codec}`, { | ||
...this.logContext, | ||
simulcastCodecInfo, | ||
}); | ||
if (!simulcastCodecInfo || !simulcastCodecInfo.sender) { | ||
@@ -325,3 +331,3 @@ for (const q of codec.qualities) { | ||
} else if (simulcastCodecInfo.encodings) { | ||
log.debug(`try setPublishingLayersForSender ${codec.codec}`); | ||
this.log.debug(`try setPublishingLayersForSender ${codec.codec}`, this.logContext); | ||
await setPublishingLayersForSender( | ||
@@ -332,2 +338,4 @@ simulcastCodecInfo.sender, | ||
this.senderLock, | ||
this.log, | ||
this.logContext, | ||
); | ||
@@ -345,3 +353,3 @@ } | ||
async setPublishingLayers(qualities: SubscribedQuality[]) { | ||
log.debug('setting publishing layers', qualities); | ||
this.log.debug('setting publishing layers', { ...this.logContext, qualities }); | ||
if (!this.sender || !this.encodings) { | ||
@@ -351,3 +359,10 @@ return; | ||
await setPublishingLayersForSender(this.sender, this.encodings, qualities, this.senderLock); | ||
await setPublishingLayersForSender( | ||
this.sender, | ||
this.encodings, | ||
qualities, | ||
this.senderLock, | ||
this.log, | ||
this.logContext, | ||
); | ||
} | ||
@@ -365,3 +380,3 @@ | ||
} catch (e) { | ||
log.error('could not get audio sender stats', { error: e }); | ||
this.log.error('could not get audio sender stats', { ...this.logContext, error: e }); | ||
return; | ||
@@ -397,5 +412,7 @@ } | ||
senderLock: Mutex, | ||
log: StructuredLogger, | ||
logContext: Record<string, unknown>, | ||
) { | ||
const unlock = await senderLock.lock(); | ||
log.debug('setPublishingLayersForSender', { sender, qualities, senderEncodings }); | ||
log.debug('setPublishingLayersForSender', { ...logContext, sender, qualities, senderEncodings }); | ||
try { | ||
@@ -474,2 +491,3 @@ const params = sender.getParameters(); | ||
}`, | ||
logContext, | ||
); | ||
@@ -498,3 +516,3 @@ | ||
params.encodings = encodings; | ||
log.debug(`setting encodings`, params.encodings); | ||
log.debug(`setting encodings`, { ...logContext, encodings: params.encodings }); | ||
await sender.setParameters(params); | ||
@@ -501,0 +519,0 @@ } |
@@ -172,5 +172,6 @@ import type { Track } from './Track'; | ||
/** | ||
* capture resolution, defaults to screen resolution | ||
* NOTE: In Safari 17, specifying any resolution at all would lead to a low-resolution | ||
* capture. https://bugs.webkit.org/show_bug.cgi?id=263015 | ||
* capture resolution, defaults to 1080 for all browsers other than Safari | ||
* On Safari 17, default resolution is not capped, due to a bug, specifying | ||
* any resolution at all would lead to a low-resolution capture. | ||
* https://bugs.webkit.org/show_bug.cgi?id=263015 | ||
*/ | ||
@@ -191,2 +192,5 @@ resolution?: VideoResolution; | ||
/** specify the type of content, see: https://www.w3.org/TR/mst-content-hint/#video-content-hints */ | ||
contentHint?: 'detail' | 'text' | 'motion'; | ||
/** | ||
@@ -372,7 +376,10 @@ * Experimental option to control whether the audio playing in a tab will continue to be played out of a user's | ||
h360fps3: new VideoPreset(640, 360, 200_000, 3, 'medium'), | ||
h720fps5: new VideoPreset(1280, 720, 400_000, 5, 'medium'), | ||
h360fps15: new VideoPreset(640, 360, 400_000, 15, 'medium'), | ||
h720fps5: new VideoPreset(1280, 720, 800_000, 5, 'medium'), | ||
h720fps15: new VideoPreset(1280, 720, 1_500_000, 15, 'medium'), | ||
h720fps30: new VideoPreset(1280, 720, 2_000_000, 30, 'medium'), | ||
h1080fps15: new VideoPreset(1920, 1080, 2_500_000, 15, 'medium'), | ||
h1080fps30: new VideoPreset(1920, 1080, 4_000_000, 30, 'medium'), | ||
h1080fps30: new VideoPreset(1920, 1080, 5_000_000, 30, 'medium'), | ||
// original resolution, without resizing | ||
original: new VideoPreset(0, 0, 7_000_000, 30, 'medium'), | ||
} as const; |
@@ -1,5 +0,5 @@ | ||
import log from '../../logger'; | ||
import { TrackEvent } from '../events'; | ||
import { computeBitrate } from '../stats'; | ||
import type { AudioReceiverStats } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import { isReactNative, supportsSetSinkId } from '../utils'; | ||
@@ -31,4 +31,5 @@ import RemoteTrack from './RemoteTrack'; | ||
audioOutput?: AudioOutputOptions, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(mediaTrack, sid, Track.Kind.Audio, receiver); | ||
super(mediaTrack, sid, Track.Kind.Audio, receiver, loggerOptions); | ||
this.audioContext = audioContext; | ||
@@ -111,3 +112,3 @@ this.webAudioPluginNodes = []; | ||
if (this.audioContext && needsNewWebAudioConnection) { | ||
log.debug('using audio context mapping'); | ||
this.log.debug('using audio context mapping', this.logContext); | ||
this.connectWebAudio(this.audioContext, element); | ||
@@ -114,0 +115,0 @@ element.volume = 0; |
import { TrackEvent } from '../events'; | ||
import { monitorFrequency } from '../stats'; | ||
import type { LoggerOptions } from '../types'; | ||
import { Track } from './Track'; | ||
@@ -14,4 +15,6 @@ | ||
receiver?: RTCRtpReceiver, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(mediaTrack, kind); | ||
super(mediaTrack, kind, loggerOptions); | ||
this.sid = sid; | ||
@@ -18,0 +21,0 @@ this.receiver = receiver; |
@@ -1,2 +0,1 @@ | ||
import log from '../../logger'; | ||
import { | ||
@@ -10,2 +9,3 @@ ParticipantTracks, | ||
import { TrackEvent } from '../events'; | ||
import type { LoggerOptions } from '../types'; | ||
import type RemoteTrack from './RemoteTrack'; | ||
@@ -35,4 +35,9 @@ import RemoteVideoTrack from './RemoteVideoTrack'; | ||
constructor(kind: Track.Kind, ti: TrackInfo, autoSubscribe: boolean | undefined) { | ||
super(kind, ti.sid, ti.name); | ||
constructor( | ||
kind: Track.Kind, | ||
ti: TrackInfo, | ||
autoSubscribe: boolean | undefined, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(kind, ti.sid, ti.name, loggerOptions); | ||
this.subscribed = autoSubscribe; | ||
@@ -257,9 +262,10 @@ this.updateInfo(ti); | ||
if (this.kind === Track.Kind.Video && this.isAdaptiveStream) { | ||
log.warn('adaptive stream is enabled, cannot change video track settings', { | ||
trackSid: this.trackSid, | ||
}); | ||
this.log.warn( | ||
'adaptive stream is enabled, cannot change video track settings', | ||
this.logContext, | ||
); | ||
return false; | ||
} | ||
if (!this.isDesired) { | ||
log.warn('cannot update track settings when not subscribed', { trackSid: this.trackSid }); | ||
this.log.warn('cannot update track settings when not subscribed', this.logContext); | ||
return false; | ||
@@ -280,5 +286,6 @@ } | ||
protected handleVisibilityChange = (visible: boolean) => { | ||
log.debug(`adaptivestream video visibility ${this.trackSid}, visible=${visible}`, { | ||
trackSid: this.trackSid, | ||
}); | ||
this.log.debug( | ||
`adaptivestream video visibility ${this.trackSid}, visible=${visible}`, | ||
this.logContext, | ||
); | ||
this.disabled = !visible; | ||
@@ -289,5 +296,6 @@ this.emitTrackUpdate(); | ||
protected handleVideoDimensionsChange = (dimensions: Track.Dimensions) => { | ||
log.debug(`adaptivestream video dimensions ${dimensions.width}x${dimensions.height}`, { | ||
trackSid: this.trackSid, | ||
}); | ||
this.log.debug( | ||
`adaptivestream video dimensions ${dimensions.width}x${dimensions.height}`, | ||
this.logContext, | ||
); | ||
this.videoDimensions = dimensions; | ||
@@ -294,0 +302,0 @@ this.emitTrackUpdate(); |
import { debounce } from 'ts-debounce'; | ||
import log from '../../logger'; | ||
import { TrackEvent } from '../events'; | ||
@@ -7,2 +6,3 @@ import type { VideoReceiverStats } from '../stats'; | ||
import CriticalTimers from '../timers'; | ||
import type { LoggerOptions } from '../types'; | ||
import type { ObservableMediaElement } from '../utils'; | ||
@@ -32,4 +32,5 @@ import { getDevicePixelRatio, getIntersectionObserver, getResizeObserver, isWeb } from '../utils'; | ||
adaptiveStreamSettings?: AdaptiveStreamSettings, | ||
loggerOptions?: LoggerOptions, | ||
) { | ||
super(mediaTrack, sid, Track.Kind.Video, receiver); | ||
super(mediaTrack, sid, Track.Kind.Video, receiver, loggerOptions); | ||
this.adaptiveStreamSettings = adaptiveStreamSettings; | ||
@@ -108,3 +109,3 @@ } | ||
} else { | ||
log.warn('visibility resize observer not triggered'); | ||
this.log.warn('visibility resize observer not triggered', this.logContext); | ||
} | ||
@@ -120,3 +121,3 @@ } | ||
if (!this.isAdaptiveStream) { | ||
log.warn('stopObservingElementInfo ignored'); | ||
this.log.warn('stopObservingElementInfo ignored', this.logContext); | ||
return; | ||
@@ -123,0 +124,0 @@ } |
import { EventEmitter } from 'events'; | ||
import type TypedEventEmitter from 'typed-emitter'; | ||
import type { SignalClient } from '../../api/SignalClient'; | ||
import log from '../../logger'; | ||
import log, { LoggerNames, StructuredLogger, getLogger } from '../../logger'; | ||
import { TrackSource, TrackType } from '../../proto/livekit_models_pb'; | ||
import { StreamState as ProtoStreamState } from '../../proto/livekit_rtc_pb'; | ||
import { TrackEvent } from '../events'; | ||
import type { LoggerOptions } from '../types'; | ||
import { isFireFox, isSafari, isWeb } from '../utils'; | ||
import { getLogContextFromTrack } from './utils'; | ||
@@ -49,2 +51,4 @@ const BACKGROUND_REACTION_DELAY = 5000; | ||
private loggerContextCb: LoggerOptions['loggerContextCb']; | ||
protected _currentBitrate: number = 0; | ||
@@ -54,4 +58,13 @@ | ||
protected constructor(mediaTrack: MediaStreamTrack, kind: Track.Kind) { | ||
protected log: StructuredLogger = log; | ||
protected constructor( | ||
mediaTrack: MediaStreamTrack, | ||
kind: Track.Kind, | ||
loggerOptions: LoggerOptions = {}, | ||
) { | ||
super(); | ||
this.log = getLogger(loggerOptions.loggerName ?? LoggerNames.Track); | ||
this.loggerContextCb = loggerOptions.loggerContextCb; | ||
this.setMaxListeners(100); | ||
@@ -64,2 +77,9 @@ this.kind = kind; | ||
protected get logContext() { | ||
return { | ||
...this.loggerContextCb?.(), | ||
...getLogContextFromTrack(this), | ||
}; | ||
} | ||
/** current receive bits per second */ | ||
@@ -230,2 +250,12 @@ get currentBitrate(): number { | ||
/** @internal */ | ||
updateLoggerOptions(loggerOptions: LoggerOptions) { | ||
if (loggerOptions.loggerName) { | ||
this.log = getLogger(loggerOptions.loggerName); | ||
} | ||
if (loggerOptions.loggerContextCb) { | ||
this.loggerContextCb = loggerOptions.loggerContextCb; | ||
} | ||
} | ||
private recycleElement(element: HTMLMediaElement) { | ||
@@ -232,0 +262,0 @@ if (element instanceof HTMLAudioElement) { |
import { EventEmitter } from 'events'; | ||
import type TypedEventEmitter from 'typed-emitter'; | ||
import log from '../../logger'; | ||
import log, { LoggerNames, getLogger } from '../../logger'; | ||
import { Encryption_Type } from '../../proto/livekit_models_pb'; | ||
@@ -8,2 +8,3 @@ import type { SubscriptionError, TrackInfo } from '../../proto/livekit_models_pb'; | ||
import { TrackEvent } from '../events'; | ||
import type { LoggerOptions } from '../types'; | ||
import LocalAudioTrack from './LocalAudioTrack'; | ||
@@ -15,2 +16,3 @@ import LocalVideoTrack from './LocalVideoTrack'; | ||
import { Track } from './Track'; | ||
import { getLogContextFromTrack } from './utils'; | ||
@@ -44,4 +46,10 @@ export class TrackPublication extends (EventEmitter as new () => TypedEventEmitter<PublicationEventCallbacks>) { | ||
constructor(kind: Track.Kind, id: string, name: string) { | ||
protected log = log; | ||
private loggerContextCb?: LoggerOptions['loggerContextCb']; | ||
constructor(kind: Track.Kind, id: string, name: string, loggerOptions?: LoggerOptions) { | ||
super(); | ||
this.log = getLogger(loggerOptions?.loggerName ?? LoggerNames.Publication); | ||
this.loggerContextCb = this.loggerContextCb; | ||
this.setMaxListeners(100); | ||
@@ -70,2 +78,9 @@ this.kind = kind; | ||
protected get logContext() { | ||
return { | ||
...this.loggerContextCb?.(), | ||
...getLogContextFromTrack(this), | ||
}; | ||
} | ||
get isMuted(): boolean { | ||
@@ -128,3 +143,3 @@ return this.metadataMuted; | ||
this.trackInfo = info; | ||
log.debug('update publication info', { info }); | ||
this.log.debug('update publication info', { ...this.logContext, info }); | ||
} | ||
@@ -131,0 +146,0 @@ } |
@@ -158,3 +158,4 @@ import { TrackPublishedResponse } from '../../proto/livekit_rtc_pb'; | ||
let videoConstraints: MediaTrackConstraints | boolean = options.video ?? true; | ||
if (options.resolution) { | ||
// treat 0 as uncapped | ||
if (options.resolution && options.resolution.width > 0 && options.resolution.height > 0) { | ||
videoConstraints = typeof videoConstraints === 'boolean' ? {} : videoConstraints; | ||
@@ -213,1 +214,22 @@ if (isSafari()) { | ||
} | ||
export function getLogContextFromTrack(track: Track | TrackPublication): Record<string, unknown> { | ||
if (track instanceof Track) { | ||
return { | ||
trackSid: track.sid, | ||
trackSource: track.source, | ||
trackMuted: track.isMuted, | ||
trackEnabled: track.mediaStreamTrack.enabled, | ||
trackKind: track.kind, | ||
}; | ||
} else { | ||
return { | ||
trackSid: track.trackSid, | ||
trackName: track.trackName, | ||
track: track.track ? getLogContextFromTrack(track.track) : undefined, | ||
trackEnabled: track.isEnabled, | ||
trackEncrypted: track.isEncrypted, | ||
trackMimeType: track.mimeType, | ||
}; | ||
} | ||
} |
@@ -44,1 +44,6 @@ import type RemoteParticipant from './participant/RemoteParticipant'; | ||
| 'subscriber-bandwidth'; | ||
export type LoggerOptions = { | ||
loggerName?: string; | ||
loggerContextCb?: () => Record<string, unknown>; | ||
}; |
@@ -151,2 +151,7 @@ import { ClientInfo, ClientInfo_SDK } from '../proto/livekit_models_pb'; | ||
export function isSafari17(): boolean { | ||
const b = getBrowser(); | ||
return b?.name === 'Safari' && b.version.startsWith('17.'); | ||
} | ||
export function isMobile(): boolean { | ||
@@ -153,0 +158,0 @@ if (!isWeb()) return false; |
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 too big to display
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
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
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 too big to display
6076934
59344