@wireapp/avs 9.4.18 → 9.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avs_core.js +1 -1
- package/dist/avs_pc.d.ts +2 -0
- package/dist/avs_pc.js +70 -27
- package/dist/avs_wcall.d.ts +1 -0
- package/dist/avs_wcall.js +3 -0
- package/package.json +1 -1
package/dist/avs_pc.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type AudioStreamHandler = (convid: string, stream_id: string, streams: re
|
|
|
4
4
|
export type VideoStreamHandler = (convid: string, remote_userid: string, remote_clientid: string, streams: readonly MediaStream[] | null) => void;
|
|
5
5
|
declare function pc_InitModule(module: any, logh: WcallLogHandler): void;
|
|
6
6
|
declare function pc_SetUserMediaHandler(umh: UserMediaHandler): void;
|
|
7
|
+
declare function pc_IsConferenceCallingSupported(): boolean;
|
|
7
8
|
declare function pc_SetAudioStreamHandler(ash: AudioStreamHandler): void;
|
|
8
9
|
declare function pc_SetVideoStreamHandler(vsh: VideoStreamHandler): void;
|
|
9
10
|
declare function pc_ReplaceTrack(convid: string, newTrack: MediaStreamTrack): void;
|
|
@@ -16,6 +17,7 @@ declare const _default: {
|
|
|
16
17
|
setUserMediaHandler: typeof pc_SetUserMediaHandler;
|
|
17
18
|
setAudioStreamHandler: typeof pc_SetAudioStreamHandler;
|
|
18
19
|
setVideoStreamHandler: typeof pc_SetVideoStreamHandler;
|
|
20
|
+
isConferenceCallingSupported: typeof pc_IsConferenceCallingSupported;
|
|
19
21
|
replaceTrack: typeof pc_ReplaceTrack;
|
|
20
22
|
getStats: typeof pc_GetStats;
|
|
21
23
|
};
|
package/dist/avs_pc.js
CHANGED
|
@@ -6,14 +6,17 @@ var logFn = null;
|
|
|
6
6
|
var userMediaHandler = null;
|
|
7
7
|
var audioStreamHandler = null;
|
|
8
8
|
var videoStreamHandler = null;
|
|
9
|
+
var insertableLegacy = false;
|
|
10
|
+
var insertableStreams = false;
|
|
9
11
|
var pc_env = 0;
|
|
10
12
|
var pc_envver = 0;
|
|
11
13
|
var worker;
|
|
12
14
|
// Use inlined worker instead....
|
|
13
15
|
//let worker = new Worker('/worker/avs-worker.js', {name: 'AVS worker'});
|
|
14
16
|
// This is the content of the avs-worker.js file, copied here.
|
|
15
|
-
var workerContent = "\n\nvar coders = {};\n\nconst HDR_VERSION = 0;\n\n/*\nHeader\n\n 1 1 1 1 1 1\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| V=0 |E| RES |S|LEN |1|KLEN |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| KID... (length=KLEN) |\n+-------------------------------+\n| CTR... (length=LEN) |\n+-------------------------------+\n\nExtensions\n\n 0 1 2 3 4 5 6 7\n+-+-+-+-+-+-+-+-+---------------------------+\n|M| ELN | EID | VAL... (length=ELEN) |\n+-+-+-+-+-+-+-+-+---------------------------+\n*/\n\nfunction calcLen(val) {\n let l = 0;\n\n if (val == 0)\n\treturn 1;\n\n while (val != 0) {\n\tl++;\n\tval = val >>> 8;\t\n }\n\n return l;\n}\n\nfunction writeBytes(buf, p, len, val)\n{\n switch (len) {\n case 8:\n\tbuf[p] = (val >>> 56) & 0xff;\n\tp++;\n\n case 7:\n\tbuf[p] = (val >>> 48) & 0xff;\n\tp++;\n\n case 6:\n\tbuf[p] = (val >>> 40) & 0xff;\n\tp++;\n\t\n case 5:\n\tbuf[p] = (val >>> 32) & 0xff;\n\tp++;\n\n case 4:\n\tbuf[p] = (val >>> 24) & 0xff;\n\tp++;\n\n case 3:\n\tbuf[p] = (val >>> 16) & 0xff;\n\tp++;\n\n case 2:\n\tbuf[p] = (val >>> 8) & 0xff;\n\tp++;\n\n case 1:\n\tbuf[p] = val & 0xff;\n\tbreak;\n\n default:\n\treturn 0;\n }\n \n return len;\n}\n\nfunction readBytes(buf, p, len) {\n let l = len;\n let v = 0;\n\n while (l > 0) {\n\tv = v * 256;\n\tv = v + buf[p];\n\tp++;\n\tl--;\n }\n\n const lv = {\n\tlen: len,\n\tval: v\n };\n \n return lv;\n}\n\nfunction frameHeaderDecode(buf) {\n\n const MAX_EXTS = 16;\n let ext = buf[0] & 0x10;\n const flen = ((buf[1] >>> 4) & 0x7) + 1;\n const x = (buf[1] >>> 3) & 0x1;\n const klen = buf[1] & 0x7;\n let p = 2;\n let kid = -1;\n let fid = -1;\n let csrc = \"0\";\n let i = 0;\n\n if (x) {\n\tconst lv = readBytes(buf, p, klen + 1);\n\n\tp += lv.len;\n\tkid = lv.val;\n }\n else {\n\tkid = klen;\n }\n\n const lv = readBytes(buf, p, flen);\n p += lv.len;\n fid = lv.val;\n\n while(ext && p < buf.length && i < MAX_EXTS) {\n\tconst b = buf[p];\n\text = b & 0x80;\n\tconst elen = ((b >>> 4) & 0x7) + 1;\n\tconst eid = (b & 0xf);\n\n if (eid == 1 && elen == 4) {\n const csrco = readBytes(buf, p + 1, elen);\n csrc = csrco.val.toString();\n }\n p += elen + 1;\n i++;\n }\n\n if (kid < 0 || fid < 0) {\n\treturn null;\n }\n else {\n\tconst frameHdr = {\n\t len: p,\n\t frameId: fid,\n\t keyId: kid,\n\t csrc: csrc\n\t}\n\treturn frameHdr;\n }\n}\n\nfunction frameHeaderEncode(fid, kid, csrc)\n{\n const ab = new ArrayBuffer(32);\n const buf = new Uint8Array(ab);\n let sig = 0;\n let x = 0;\n let klen = 0;\n let flen = 0;\n let p = 2;\n let ext = (csrc > 0) ? 1 : 0;\n\n if (kid > 7) {\n\tx = 1;\n\tklen = calcLen(kid) - 1;\n }\n else {\n\tklen = kid;\n }\n\n flen = calcLen(fid);\n\n buf[0] = (HDR_VERSION << 5) + (ext << 4);\n buf[1] = (sig << 7) + ((flen - 1) << 4) + (x << 3) + klen;\n if (x) {\n\tp += writeBytes(buf, p, klen + 1, kid);\n }\n p += writeBytes(buf, p, flen, fid);\n\n if (csrc > 0) {\n buf[p] = 0x31;\n p++;\n p+= writeBytes(buf, p, 4, csrc);\n }\n return buf.subarray(0, p);\n}\n\nfunction xor_iv(iv, fid, kid) {\n const oiv = new Uint8Array(iv);\n\n oiv[0] = oiv[0] ^ ((fid >> 24) & 0xFF);\n oiv[1] = oiv[1] ^ ((fid >> 16) & 0xFF);\n oiv[2] = oiv[2] ^ ((fid >> 8) & 0xFF);\n oiv[3] = oiv[3] ^ ((fid ) & 0xFF);\n oiv[4] = oiv[4] ^ ((kid >> 24) & 0xFF);\n oiv[5] = oiv[5] ^ ((kid >> 16) & 0xFF);\n oiv[6] = oiv[6] ^ ((kid >> 8) & 0xFF);\n oiv[7] = oiv[7] ^ ((kid ) & 0xFF);\n\n return oiv;\n}\n\nfunction getMediaKey(self, index) {\n postMessage({\n\top: \"getMediaKey\",\n\tself: self,\n\tindex: index\n });\n}\n\nfunction getCurrentMediaKey(self, index) {\n postMessage({\n\top: \"getCurrentMediaKey\",\n\tself: self\n });\n}\n\nfunction doLog(str) {\n postMessage({\n\top: \"log\",\n\tlevel: 1,\n\tlogString: str\n });\n}\n\nfunction encryptFrame(coder, rtcFrame, controller) {\n const dataBuf = rtcFrame.data;\n const data = new Uint8Array(dataBuf);\n const t = Object.prototype.toString.call(rtcFrame);\n const isVideo = t === '[object RTCEncodedVideoFrame]';\n const userid = \"self\";\n\n const meta = rtcFrame.getMetadata();\n const ssrc = meta.synchronizationSource;\n\n if (isVideo && !coder.video.first_recv) {\n doLog(\"frame_enc: encrypt: first frame received type: video uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" ssrc: \" + ssrc);\n coder.video.first_recv = true;\n coder.video.first_succ = false;\n }\n if (!isVideo && !coder.audio.first_recv) {\n doLog(\"frame_enc: encrypt: first frame received type: audio uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" ssrc: \" + ssrc);\n coder.audio.first_recv = true;\n coder.audio.first_succ = false;\n }\n\n if (coder.currentKey == null) {\n getCurrentMediaKey(coder.self);\n return;\n }\n\n const baseiv = isVideo ? coder.video.iv : coder.audio.iv;\n const iv = xor_iv(baseiv, coder.frameId, coder.currentKey.id);\n \n const hdr = frameHeaderEncode(coder.frameId, coder.currentKey.id, ssrc);\n crypto.subtle.encrypt(\n {\n name: \"AES-GCM\",\n iv: iv,\n tagLength: 128,\n additionalData: hdr\n },\n coder.currentKey.key,\n data\n )\n .then(function(encdata){\n const enc8 = new Uint8Array(encdata); \t \n const enc = new Uint8Array(hdr.length + enc8.length);\n \n enc.set(hdr);\n enc.set(enc8, hdr.length);\n\n rtcFrame.data = enc.buffer;\n controller.enqueue(rtcFrame);\n\n if (isVideo && !coder.video.first_succ) {\n doLog(\"frame_enc: encrypt: first frame encrypted type: video uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" csrc: \" + ssrc);\n coder.video.first_succ = true;\n }\n if (!isVideo && !coder.audio.first_succ) {\n doLog(\"frame_enc: encrypt: first frame encrypted type: audio uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" csrc: \" + ssrc);\n coder.audio.first_succ = true;\n }\n\n coder.frameId++;\n });\n}\n\nfunction decryptFrame(coder, rtcFrame, controller) {\n const dataBuf = rtcFrame.data;\n const data = new Uint8Array(dataBuf);\n\n const t = Object.prototype.toString.call(rtcFrame);\n const isVideo = t === '[object RTCEncodedVideoFrame]'\n\n const frameHdr = frameHeaderDecode(data);\n\n if (frameHdr == null) {\n doLog('decryptFrame: failed to decode frame header');\n return;\n }\n\n const meta = rtcFrame.getMetadata();\n let ssrc = meta.synchronizationSource.toString();\n let csrc = null;\n if (frameHdr.csrc != \"0\") {\n csrc = frameHdr.csrc;\n }\n else if (meta.contributingSources && meta.contributingSources.length > 0) {\n csrc = meta.contributingSources[0].toString();\n }\n else {\n csrc = ssrc;\n }\n\n if (!csrc) {\n doLog('decryptFrame: no csrc for ssrc ' + ssrc);\n return;\n }\n\n let uinfo = null;\n if (isVideo) {\n uinfo = coder.video.users[csrc];\n }\n else {\n uinfo = coder.audio.users[csrc];\n }\n\n if (!uinfo) {\n doLog('decryptFrame: no userinfo for csrc: ' + csrc);\n return;\n }\n \n //console.log('frame_dec: video ' + isVideo + ' ssrc ' + ssrc + ' csrc ' + csrc + ' user ' + uinfo.userid.substring(0,8));\n\n if (isVideo && uinfo.transp_ssrcv != ssrc) {\n doLog(\"frame_dec: decrypt: first frame received type: video uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc);\n uinfo.first_succ_video = false;\n\n /* Only call videoStreamHandler for selective video */\n if (uinfo.ssrcv != ssrc) {\n for (const [key, u] of Object.entries(coder.video.users)) {\n if (u.transp_ssrcv == ssrc) {\n postMessage({\n op: \"setvstream\",\n self: coder.self,\n userid: u.userid,\n clientid: u.clientid,\n ssrc: \"0\",\n });\n u.transp_ssrcv = null;\n }\n }\n\n postMessage({\n op: \"setvstream\",\n self: coder.self,\n userid: uinfo.userid,\n clientid: uinfo.clientid,\n ssrc: ssrc,\n });\n }\n uinfo.transp_ssrcv = ssrc;\n }\n\n if (!isVideo && !uinfo.first_recv_audio) {\n doLog(\"frame_dec: decrypt: first frame received type: audio uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc);\n uinfo.first_recv_audio = true;\n uinfo.first_succ_audio = false;\n }\n /*\n console.log(\"coder[\" + coder.self + \"]\"\n\t+ \" len=\" + frameHdr.len\n\t+ \" kid=\"+ frameHdr.keyId\n\t+ \" fid=\" + frameHdr.frameId);\n */\n\n let kinfo = null;\n for (i = 0; i < coder.keys.length; i++) {\n if (coder.keys[i].id == frameHdr.keyId) {\n kinfo = coder.keys[i];\n break;\n }\n }\n\n if (!kinfo) {\n getMediaKey(coder.self, frameHdr.keyId);\n return;\n }\n\n let iv = null;\n if (isVideo) {\n iv = xor_iv(uinfo.ivv, frameHdr.frameId, frameHdr.keyId);\n }\n else {\n iv = xor_iv(uinfo.iva, frameHdr.frameId, frameHdr.keyId);\n }\n\n const hdr = data.subarray(0, frameHdr.len);\n const dec = data.subarray(frameHdr.len);\n crypto.subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: iv,\n tagLength: 128,\n additionalData: hdr\n },\n kinfo.key,\n dec\n )\n .then(function(decdata){\n rtcFrame.data = decdata;\n controller.enqueue(rtcFrame);\n if (isVideo && !uinfo.first_succ_video) {\n doLog(\"frame_dec: decrypt: first frame decrypted type: video uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc);\n uinfo.first_succ_video = true;\n }\n if (!isVideo && !uinfo.first_succ_audio) {\n doLog(\"frame_dec: decrypt: first frame decrypted type: audio uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc);\n uinfo.first_succ_audio = true;\n }\n })\n .catch(err => {\n console.log(err);\n });\n}\n\nonmessage = async (event) => {\n const {op, self} = event.data;\n doLog('AVS worker op=' + op + ' self=' + self);\n\n let coder = coders[self];\n if (!coder) {\n\tdoLog('AVS worker: no coder for self=' + self);\n } \n /*\n const {opinfo, readableStream, writableStream} = event.data;\n const mval = opinfo.mtype === 'video' ? 1 : 0; // corresponds to enum frame_media_type\n */\n if (op === 'create') {\n\tconst { iva, ivv } = event.data;\n\tif (!coder) {\n\t coder = {\n\t\tself: self,\n\t\tframeId: Math.floor(Math.random() * 4294967295),\n\t\tcurrentKey: null,\n\t\tkeys: [],\n\t\taudio: {\n\t\t first_recv: false,\n\t\t first_succ: false,\n\t\t iv: iva,\n\t\t users: {},\n\t\t},\n\t\tvideo: {\n\t\t first_recv: false,\n\t\t first_succ: false,\n\t\t iv: ivv,\n\t\t users: {},\n\t\t},\n\t }\n\t coders[self] = coder;\n\n\t doLog('AVS worker: adding coder for self=' + self);\n\t}\n }\n else if (op === 'destroy') {\n\tcoders[self] = null;\n }\n else if (op === 'addUser') {\n\tif (!coder) {\n\t doLog('addUser: no coder');\n\t return;\n\t}\n\tconst {userInfo} = event.data;\n\n\tdoLog('addUser: adding audio ssrc: '+userInfo.ssrca);\n\tcoder.audio.users[userInfo.ssrca] = userInfo;\n\tif (userInfo.ssrcv != 0) {\n\t coder.video.users[userInfo.ssrcv] = userInfo;\n\t}\n }\n else if (op === 'setupSender') {\n\tconst {readableStream, writableStream} = event.data;\n\n\tconst transformStream = new TransformStream({\n\t transform: (frame, controller) => {encryptFrame(coder, frame, controller);},\n\t});\n\treadableStream\n .pipeThrough(transformStream)\n .pipeTo(writableStream);\n }\n else if (op === 'setupReceiver') {\n const {readableStream, writableStream} = event.data;\n\n if (!coder) {\n\t doLog('setupSender: no coder for self=' + self);\n return;\n }\n\n const transformStream = new TransformStream({\n\t transform: (frame, controller) => {decryptFrame(coder, frame, controller);},\n });\n readableStream\n .pipeThrough(transformStream)\n .pipeTo(writableStream); \n }\n else if (op === 'setMediaKey') {\n const {index, current, key} = event.data;\n\n doLog(\"setMediaKey: got key for: \" + self\n\t\t + \" index=\" + index + \" current=\" + current\n\t\t + \" keyLen=\" + key.length);\n\n crypto.subtle.importKey(\n \"raw\",\n key,\n \"AES-GCM\",\n false,\n [\"encrypt\", \"decrypt\"]\n )\n .then(function(k){\n let kinfo = null;\n for (i = 0; i < coder.keys.length; i++) {\n if (coder.keys[i].id == index) {\n coder.keys[i].key = k;\n kinfo = coder.keys[i];\n break;\n }\n }\n if (!kinfo) {\n kinfo = {id:index, key:k};\n coder.keys.push(kinfo);\n }\n if (index == current)\n coder.currentKey = kinfo;\n\n while (coder.keys.length > 4) {\n coder.keys.shift();\n }\n });\n } \n};\n";
|
|
16
|
-
function callStreamHandler(pc, userid, clientid, ssrc) {
|
|
17
|
+
var workerContent = "\n\nvar coders = {};\n\nconst HDR_VERSION = 0;\n\n/*\nHeader\n\n 1 1 1 1 1 1\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| V=0 |E| RES |S|LEN |1|KLEN |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| KID... (length=KLEN) |\n+-------------------------------+\n| CTR... (length=LEN) |\n+-------------------------------+\n\nExtensions\n\n 0 1 2 3 4 5 6 7\n+-+-+-+-+-+-+-+-+---------------------------+\n|M| ELN | EID | VAL... (length=ELEN) |\n+-+-+-+-+-+-+-+-+---------------------------+\n*/\n\nfunction calcLen(val) {\n let l = 0;\n\n if (val == 0)\n\treturn 1;\n\n while (val != 0) {\n\tl++;\n\tval = val >>> 8;\t\n }\n\n return l;\n}\n\nfunction writeBytes(buf, p, len, val)\n{\n switch (len) {\n case 8:\n\tbuf[p] = (val >>> 56) & 0xff;\n\tp++;\n\n case 7:\n\tbuf[p] = (val >>> 48) & 0xff;\n\tp++;\n\n case 6:\n\tbuf[p] = (val >>> 40) & 0xff;\n\tp++;\n\t\n case 5:\n\tbuf[p] = (val >>> 32) & 0xff;\n\tp++;\n\n case 4:\n\tbuf[p] = (val >>> 24) & 0xff;\n\tp++;\n\n case 3:\n\tbuf[p] = (val >>> 16) & 0xff;\n\tp++;\n\n case 2:\n\tbuf[p] = (val >>> 8) & 0xff;\n\tp++;\n\n case 1:\n\tbuf[p] = val & 0xff;\n\tbreak;\n\n default:\n\treturn 0;\n }\n \n return len;\n}\n\nfunction readBytes(buf, p, len) {\n let l = len;\n let v = 0;\n\n while (l > 0) {\n\tv = v * 256;\n\tv = v + buf[p];\n\tp++;\n\tl--;\n }\n\n const lv = {\n\tlen: len,\n\tval: v\n };\n \n return lv;\n}\n\nfunction frameHeaderDecode(buf) {\n\n const MAX_EXTS = 16;\n let ext = buf[0] & 0x10;\n const flen = ((buf[1] >>> 4) & 0x7) + 1;\n const x = (buf[1] >>> 3) & 0x1;\n const klen = buf[1] & 0x7;\n let p = 2;\n let kid = -1;\n let fid = -1;\n let csrc = \"0\";\n let i = 0;\n\n if (x) {\n\tconst lv = readBytes(buf, p, klen + 1);\n\n\tp += lv.len;\n\tkid = lv.val;\n }\n else {\n\tkid = klen;\n }\n\n const lv = readBytes(buf, p, flen);\n p += lv.len;\n fid = lv.val;\n\n while(ext && p < buf.length && i < MAX_EXTS) {\n\tconst b = buf[p];\n\text = b & 0x80;\n\tconst elen = ((b >>> 4) & 0x7) + 1;\n\tconst eid = (b & 0xf);\n\n if (eid == 1 && elen == 4) {\n const csrco = readBytes(buf, p + 1, elen);\n csrc = csrco.val.toString();\n }\n p += elen + 1;\n i++;\n }\n\n if (kid < 0 || fid < 0) {\n\treturn null;\n }\n else {\n\tconst frameHdr = {\n\t len: p,\n\t frameId: fid,\n\t keyId: kid,\n\t csrc: csrc\n\t}\n\treturn frameHdr;\n }\n}\n\nfunction frameHeaderEncode(fid, kid, csrc)\n{\n const ab = new ArrayBuffer(32);\n const buf = new Uint8Array(ab);\n let sig = 0;\n let x = 0;\n let klen = 0;\n let flen = 0;\n let p = 2;\n let ext = (csrc > 0) ? 1 : 0;\n\n if (kid > 7) {\n\tx = 1;\n\tklen = calcLen(kid) - 1;\n }\n else {\n\tklen = kid;\n }\n\n flen = calcLen(fid);\n\n buf[0] = (HDR_VERSION << 5) + (ext << 4);\n buf[1] = (sig << 7) + ((flen - 1) << 4) + (x << 3) + klen;\n if (x) {\n\tp += writeBytes(buf, p, klen + 1, kid);\n }\n p += writeBytes(buf, p, flen, fid);\n\n if (csrc > 0) {\n buf[p] = 0x31;\n p++;\n p+= writeBytes(buf, p, 4, csrc);\n }\n return buf.subarray(0, p);\n}\n\nfunction xor_iv(iv, fid, kid) {\n const oiv = new Uint8Array(iv);\n\n oiv[0] = oiv[0] ^ ((fid >> 24) & 0xFF);\n oiv[1] = oiv[1] ^ ((fid >> 16) & 0xFF);\n oiv[2] = oiv[2] ^ ((fid >> 8) & 0xFF);\n oiv[3] = oiv[3] ^ ((fid ) & 0xFF);\n oiv[4] = oiv[4] ^ ((kid >> 24) & 0xFF);\n oiv[5] = oiv[5] ^ ((kid >> 16) & 0xFF);\n oiv[6] = oiv[6] ^ ((kid >> 8) & 0xFF);\n oiv[7] = oiv[7] ^ ((kid ) & 0xFF);\n\n return oiv;\n}\n\nfunction getMediaKey(self, index) {\n postMessage({\n\top: \"getMediaKey\",\n\tself: self,\n\tindex: index\n });\n}\n\nfunction getCurrentMediaKey(self, index) {\n postMessage({\n\top: \"getCurrentMediaKey\",\n\tself: self\n });\n}\n\nfunction doLog(str) {\n postMessage({\n\top: \"log\",\n\tlevel: 1,\n\tlogString: str\n });\n}\n\nfunction encryptFrame(coder, rtcFrame, controller) {\n const dataBuf = rtcFrame.data;\n const data = new Uint8Array(dataBuf);\n const t = Object.prototype.toString.call(rtcFrame);\n const isVideo = t === '[object RTCEncodedVideoFrame]';\n const userid = \"self\";\n\n const meta = rtcFrame.getMetadata();\n const ssrc = meta.synchronizationSource;\n\n if (isVideo && !coder.video.first_recv) {\n doLog(\"frame_enc: encrypt: first frame received type: video uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" ssrc: \" + ssrc);\n coder.video.first_recv = true;\n coder.video.first_succ = false;\n }\n if (!isVideo && !coder.audio.first_recv) {\n doLog(\"frame_enc: encrypt: first frame received type: audio uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" ssrc: \" + ssrc);\n coder.audio.first_recv = true;\n coder.audio.first_succ = false;\n }\n\n if (coder.currentKey == null) {\n getCurrentMediaKey(coder.self);\n return;\n }\n\n const baseiv = isVideo ? coder.video.iv : coder.audio.iv;\n const iv = xor_iv(baseiv, coder.frameId, coder.currentKey.id);\n \n const hdr = frameHeaderEncode(coder.frameId, coder.currentKey.id, ssrc);\n crypto.subtle.encrypt(\n {\n name: \"AES-GCM\",\n iv: iv,\n tagLength: 128,\n additionalData: hdr\n },\n coder.currentKey.key,\n data\n )\n .then(function(encdata){\n const enc8 = new Uint8Array(encdata); \t \n const enc = new Uint8Array(hdr.length + enc8.length);\n \n enc.set(hdr);\n enc.set(enc8, hdr.length);\n\n rtcFrame.data = enc.buffer;\n controller.enqueue(rtcFrame);\n\n if (isVideo && !coder.video.first_succ) {\n doLog(\"frame_enc: encrypt: first frame encrypted type: video uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" csrc: \" + ssrc);\n coder.video.first_succ = true;\n }\n if (!isVideo && !coder.audio.first_succ) {\n doLog(\"frame_enc: encrypt: first frame encrypted type: audio uid: \" +\n userid.substring(0,8) + \" fid: \" + coder.frameId + \" csrc: \" + ssrc);\n coder.audio.first_succ = true;\n }\n\n coder.frameId++;\n });\n}\n\nfunction decryptFrame(track_id, coder, rtcFrame, controller) {\n const dataBuf = rtcFrame.data;\n const data = new Uint8Array(dataBuf);\n\n const t = Object.prototype.toString.call(rtcFrame);\n const isVideo = t === '[object RTCEncodedVideoFrame]'\n\n const frameHdr = frameHeaderDecode(data);\n\n if (frameHdr == null) {\n doLog('decryptFrame: failed to decode frame header');\n return;\n }\n\n const meta = rtcFrame.getMetadata();\n let ssrc = meta.synchronizationSource.toString();\n let csrc = null;\n if (frameHdr.csrc != \"0\") {\n csrc = frameHdr.csrc;\n }\n else if (meta.contributingSources && meta.contributingSources.length > 0) {\n csrc = meta.contributingSources[0].toString();\n }\n else {\n csrc = ssrc;\n }\n\n if (!csrc) {\n doLog('decryptFrame: no csrc for ssrc ' + ssrc);\n return;\n }\n\n let uinfo = null;\n if (isVideo) {\n uinfo = coder.video.users[csrc];\n }\n else {\n uinfo = coder.audio.users[csrc];\n }\n\n if (!uinfo) {\n doLog('decryptFrame: no userinfo for csrc: ' + csrc);\n return;\n }\n \n //console.log('frame_dec: video ' + isVideo + ' ssrc ' + ssrc + ' csrc ' + csrc + ' user ' + uinfo.userid.substring(0,8));\n\n if (isVideo && uinfo.transp_ssrcv != ssrc) {\n doLog(\"frame_dec: decrypt: first frame received type: video uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc + \" ssrc: \" + ssrc + \" uinfo.ssrcv: \" + uinfo.ssrcv);\n uinfo.first_succ_video = false;\n\n /* Only call videoStreamHandler for selective video */\n if (uinfo.ssrcv != ssrc) {\n for (const [key, u] of Object.entries(coder.video.users)) {\n if (u.transp_ssrcv == ssrc) {\n postMessage({\n op: \"setvstream\",\n self: coder.self,\n userid: u.userid,\n clientid: u.clientid,\n ssrc: \"0\",\n\t\t track_id: \"0\",\n });\n u.transp_ssrcv = null;\n }\n }\n\n postMessage({\n op: \"setvstream\",\n self: coder.self,\n userid: uinfo.userid,\n clientid: uinfo.clientid,\n ssrc: ssrc,\n\t track_id: track_id,\n });\n }\n uinfo.transp_ssrcv = ssrc;\n }\n\n if (!isVideo && !uinfo.first_recv_audio) {\n doLog(\"frame_dec: decrypt: first frame received type: audio uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc);\n uinfo.first_recv_audio = true;\n uinfo.first_succ_audio = false;\n }\n /*\n console.log(\"coder[\" + coder.self + \"]\"\n\t+ \" len=\" + frameHdr.len\n\t+ \" kid=\"+ frameHdr.keyId\n\t+ \" fid=\" + frameHdr.frameId);\n */\n\n let kinfo = null;\n for (i = 0; i < coder.keys.length; i++) {\n if (coder.keys[i].id == frameHdr.keyId) {\n kinfo = coder.keys[i];\n break;\n }\n }\n\n if (!kinfo) {\n getMediaKey(coder.self, frameHdr.keyId);\n return;\n }\n\n let iv = null;\n if (isVideo) {\n iv = xor_iv(uinfo.ivv, frameHdr.frameId, frameHdr.keyId);\n }\n else {\n iv = xor_iv(uinfo.iva, frameHdr.frameId, frameHdr.keyId);\n }\n\n const hdr = data.subarray(0, frameHdr.len);\n const dec = data.subarray(frameHdr.len);\n crypto.subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: iv,\n tagLength: 128,\n additionalData: hdr\n },\n kinfo.key,\n dec\n )\n .then(function(decdata){\n rtcFrame.data = decdata;\n controller.enqueue(rtcFrame);\n if (isVideo && !uinfo.first_succ_video) {\n doLog(\"frame_dec: decrypt: first frame decrypted type: video uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc);\n uinfo.first_succ_video = true;\n }\n if (!isVideo && !uinfo.first_succ_audio) {\n doLog(\"frame_dec: decrypt: first frame decrypted type: audio uid: \" +\n uinfo.userid.substring(0,8) + \" fid: \" + frameHdr.frameId + \" csrc: \" + csrc);\n uinfo.first_succ_audio = true;\n }\n })\n .catch(err => {\n console.log(err);\n });\n}\n\nfunction setupSender(readableStream, writableStream, coder) {\n\tconst transformStream = new TransformStream({\n\t transform: (frame, controller) => {encryptFrame(coder, frame, controller);},\n\t});\n\treadableStream\n .pipeThrough(transformStream)\n .pipeTo(writableStream);\n}\n\nfunction setupReceiver(readableStream, writableStream, coder, track_id) {\n const transformStream = new TransformStream({\n\t transform: (frame, controller) => {decryptFrame(track_id, coder, frame, controller);},\n });\n readableStream\n .pipeThrough(transformStream)\n .pipeTo(writableStream); \n}\n\n\nonrtctransform = (event) => {\n let transform;\n const writableStream = event.transformer.writable;\n const readableStream = event.transformer.readable;\n const coder = coders[event.transformer.options.self];\n\n if (!writableStream) {\n pc_log(LOG_LEVEL_WARN, \"onrtctransform: no writable stream\");\n return;\n }\n if (event.transformer.options.name === \"senderTransform\")\n setupSender(readableStream, writableStream, coder);\n else if (event.transformer.options.name === 'receiverTransform') {\n const track_id = event.transformer.options.track_id;\n doLog(\"onrtctransform: receiver trackid=\" + track_id);\n setupReceiver(readableStream, writableStream, coder, track_id);\n }\n};\n\n\nonmessage = async (event) => {\n const {op, self} = event.data;\n doLog('AVS worker op=' + op + ' self=' + self);\n\n let coder = coders[self];\n if (!coder) {\n\tdoLog('AVS worker: no coder for self=' + self);\n } \n /*\n const {opinfo, readableStream, writableStream} = event.data;\n const mval = opinfo.mtype === 'video' ? 1 : 0; // corresponds to enum frame_media_type\n */\n if (op === 'create') {\n\tconst { iva, ivv } = event.data;\n\tif (!coder) {\n\t coder = {\n\t\tself: self,\n\t\tframeId: Math.floor(Math.random() * 4294967295),\n\t\tcurrentKey: null,\n\t\tkeys: [],\n\t\taudio: {\n\t\t first_recv: false,\n\t\t first_succ: false,\n\t\t iv: iva,\n\t\t users: {},\n\t\t},\n\t\tvideo: {\n\t\t first_recv: false,\n\t\t first_succ: false,\n\t\t iv: ivv,\n\t\t users: {},\n\t\t},\n\t }\n\t coders[self] = coder;\n\n\t doLog('AVS worker: adding coder for self=' + self);\n\t}\n }\n else if (op === 'destroy') {\n\tcoders[self] = null;\n }\n else if (op === 'addUser') {\n\tif (!coder) {\n\t doLog('addUser: no coder');\n\t return;\n\t}\n\tconst {userInfo} = event.data;\n\n\tdoLog('addUser: adding audio ssrc: '+userInfo.ssrca);\n\tcoder.audio.users[userInfo.ssrca] = userInfo;\n\tif (userInfo.ssrcv != 0) {\n\t coder.video.users[userInfo.ssrcv] = userInfo;\n\t}\n }\n else if (op === 'setupSender') {\n\tconst {readableStream, writableStream} = event.data;\n\n\tsetupSender(readableStream, writableStream, coder);\n }\n else if (op === 'setupReceiver') {\n const {readableStream, writableStream} = event.data;\n\n if (!coder) {\n\t doLog('setupSender: no coder for self=' + self);\n return;\n }\n\n setupReceiver(readableStream, writableStream, coder, null);\n }\n else if (op === 'setMediaKey') {\n const {index, current, key} = event.data;\n\n doLog(\"setMediaKey: got key for: \" + self\n\t\t + \" index=\" + index + \" current=\" + current\n\t\t + \" keyLen=\" + key.length);\n\n crypto.subtle.importKey(\n \"raw\",\n key,\n \"AES-GCM\",\n false,\n [\"encrypt\", \"decrypt\"]\n )\n .then(function(k){\n let kinfo = null;\n for (i = 0; i < coder.keys.length; i++) {\n if (coder.keys[i].id == index) {\n coder.keys[i].key = k;\n kinfo = coder.keys[i];\n break;\n }\n }\n if (!kinfo) {\n kinfo = {id:index, key:k};\n coder.keys.push(kinfo);\n }\n if (index == current)\n coder.currentKey = kinfo;\n\n while (coder.keys.length > 4) {\n coder.keys.shift();\n }\n });\n } \n};\n";
|
|
18
|
+
function callStreamHandler(pc, userid, clientid, ssrc, track_id) {
|
|
19
|
+
pc_log(LOG_LEVEL_INFO, "vsh: ".concat(videoStreamHandler, " rtc: ").concat(pc.rtc, " ssrc: ").concat(ssrc));
|
|
17
20
|
if (ssrc == "0") {
|
|
18
21
|
if (videoStreamHandler) {
|
|
19
22
|
pc_log(LOG_LEVEL_INFO, "calling vsh(".concat(pc.convid.substring(0, 8), ", ").concat(userid.substring(0, 8), ", ").concat(clientid.substring(0, 4), ") to remove renderer"));
|
|
@@ -21,16 +24,35 @@ function callStreamHandler(pc, userid, clientid, ssrc) {
|
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
else if (pc.rtc) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
if (pc_env === ENV_FIREFOX) {
|
|
28
|
+
pc.rtc.getTransceivers().forEach(function (trans) {
|
|
29
|
+
var track = trans.receiver.track;
|
|
30
|
+
if (track.kind != 'video')
|
|
31
|
+
return;
|
|
32
|
+
pc_log(LOG_LEVEL_INFO, "vsh: id:".concat(track.id, " looking for: ").concat(track_id));
|
|
33
|
+
if (track.id === track_id) {
|
|
34
|
+
var stream = new MediaStream([trans.receiver.track]);
|
|
35
|
+
if (videoStreamHandler) {
|
|
36
|
+
pc_log(LOG_LEVEL_INFO, "calling vsh(".concat(pc.convid.substring(0, 8), ", ").concat(userid.substring(0, 8), ", ").concat(clientid.substring(0, 4), ") with 1 stream"));
|
|
37
|
+
videoStreamHandler(pc.convid, userid, clientid, [stream]);
|
|
38
|
+
}
|
|
31
39
|
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
var label_1 = pc.streams[ssrc];
|
|
44
|
+
pc.rtc.getTransceivers().forEach(function (trans) {
|
|
45
|
+
var track = trans.receiver.track;
|
|
46
|
+
pc_log(LOG_LEVEL_INFO, "vsh: label:".concat(track.label, " id:").concat(track.id, " looking for: ").concat(label_1));
|
|
47
|
+
if (trans.receiver.track.label === label_1) {
|
|
48
|
+
var stream = new MediaStream([trans.receiver.track]);
|
|
49
|
+
if (videoStreamHandler) {
|
|
50
|
+
pc_log(LOG_LEVEL_INFO, "calling vsh(".concat(pc.convid.substring(0, 8), ", ").concat(userid.substring(0, 8), ", ").concat(clientid.substring(0, 4), ") with 1 stream"));
|
|
51
|
+
videoStreamHandler(pc.convid, userid, clientid, [stream]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
34
56
|
}
|
|
35
57
|
}
|
|
36
58
|
function createWorker() {
|
|
@@ -51,10 +73,12 @@ function createWorker() {
|
|
|
51
73
|
pc_log(level, logString);
|
|
52
74
|
}
|
|
53
75
|
else if (op === 'setvstream') {
|
|
54
|
-
var _c = event.data, userid = _c.userid, clientid = _c.clientid, ssrc = _c.ssrc;
|
|
76
|
+
var _c = event.data, userid = _c.userid, clientid = _c.clientid, ssrc = _c.ssrc, track_id = _c.track_id;
|
|
77
|
+
pc_log(LOG_LEVEL_INFO, "setvstream: called");
|
|
55
78
|
var pcs = connectionsStore.getPeerConnectionBySelf(self);
|
|
56
79
|
if (pcs.length == 1) {
|
|
57
|
-
|
|
80
|
+
pc_log(LOG_LEVEL_INFO, "setvstream: calling videoStreamHandler: pc=".concat(pcs[0], ", ").concat(userid, "/").concat(clientid, " ssrc=").concat(ssrc));
|
|
81
|
+
callStreamHandler(pcs[0], userid, clientid, ssrc, track_id);
|
|
58
82
|
}
|
|
59
83
|
}
|
|
60
84
|
};
|
|
@@ -421,6 +445,7 @@ function dataChannelHandler(pc, event) {
|
|
|
421
445
|
ccallDcEstabHandler(pc, dcHnd);
|
|
422
446
|
}
|
|
423
447
|
function pc_SetEnv(env) {
|
|
448
|
+
pc_log(LOG_LEVEL_INFO, "setEnv=".concat(env));
|
|
424
449
|
pc_env = env;
|
|
425
450
|
}
|
|
426
451
|
function pc_New(self, convidPtr, audioIvPtr, videoIvPtr, ivlen) {
|
|
@@ -449,8 +474,6 @@ function pc_New(self, convidPtr, audioIvPtr, videoIvPtr, ivlen) {
|
|
|
449
474
|
users: {},
|
|
450
475
|
iva: iva8,
|
|
451
476
|
ivv: ivv8,
|
|
452
|
-
insertable_legacy: false,
|
|
453
|
-
insertable_streams: false,
|
|
454
477
|
stats: {
|
|
455
478
|
ploss: 0,
|
|
456
479
|
lastploss: 0,
|
|
@@ -474,10 +497,6 @@ function pc_Create(hnd, privacy, conv_type) {
|
|
|
474
497
|
return;
|
|
475
498
|
}
|
|
476
499
|
pc.conv_type = conv_type;
|
|
477
|
-
var pt = RTCRtpSender.prototype;
|
|
478
|
-
pc.insertable_legacy = !!pt.createEncodedVideoStreams;
|
|
479
|
-
pc.insertable_streams = !!pt.createEncodedStreams;
|
|
480
|
-
pc_log(LOG_LEVEL_INFO, "insertable: ".concat(pc.insertable_legacy, "/").concat(pc.insertable_streams));
|
|
481
500
|
var transportPolicy = privacy !== 0 ? "relay" : "all";
|
|
482
501
|
var useEncoding = pc.conv_type === CONV_TYPE_CONFERENCE;
|
|
483
502
|
var config = {
|
|
@@ -508,8 +527,11 @@ function pc_Create(hnd, privacy, conv_type) {
|
|
|
508
527
|
for (var _b = 0, _c = stream.getTracks(); _b < _c.length; _b++) {
|
|
509
528
|
var track = _c[_b];
|
|
510
529
|
if (track) {
|
|
511
|
-
|
|
512
|
-
|
|
530
|
+
if (pc_env === ENV_FIREFOX)
|
|
531
|
+
label = track.id;
|
|
532
|
+
else
|
|
533
|
+
label = track.label;
|
|
534
|
+
pc_log(LOG_LEVEL_INFO, "onTrack: convid=".concat(pc.convid.substring(0, 8), " track=").concat(track.id, "/").concat(track.label, "=>").concat(label, " kind=").concat(track.kind, " enabled=").concat(track.enabled, "/").concat(track.muted, "/undefined/").concat(track.readyState, " remote=undefined"));
|
|
513
535
|
if (!track.enabled)
|
|
514
536
|
track.enabled = true;
|
|
515
537
|
if (pc.conv_type === CONV_TYPE_CONFERENCE) {
|
|
@@ -553,7 +575,11 @@ function pc_Close(hnd) {
|
|
|
553
575
|
pc.rtc.getTransceivers().forEach(function (trans) {
|
|
554
576
|
var track = trans.receiver.track;
|
|
555
577
|
if (track && track.kind === "audio") {
|
|
556
|
-
var label =
|
|
578
|
+
var label = '';
|
|
579
|
+
if (pc_env == ENV_FIREFOX)
|
|
580
|
+
label = track.id;
|
|
581
|
+
else
|
|
582
|
+
label = track.label;
|
|
557
583
|
if (audioStreamHandler && pc.rtc) {
|
|
558
584
|
pc_log(LOG_LEVEL_INFO, "pc_Close: calling ash(".concat(pc.convid.substring(0, 8), ", ").concat(label, ") with 0 streams"));
|
|
559
585
|
audioStreamHandler(pc.convid, hnd.toString() + label, null);
|
|
@@ -721,13 +747,17 @@ function setupSenderTransform(pc, sender) {
|
|
|
721
747
|
if (!sender || !sender.track) {
|
|
722
748
|
return;
|
|
723
749
|
}
|
|
724
|
-
if (!
|
|
750
|
+
if (!insertableLegacy && !insertableStreams) {
|
|
725
751
|
pc_log(LOG_LEVEL_WARN, "setupSenderTransform: insertable streams not supported");
|
|
726
752
|
return;
|
|
727
753
|
}
|
|
754
|
+
if (pc_env === ENV_FIREFOX) {
|
|
755
|
+
sender.transform = new RTCRtpScriptTransform(worker, { name: "senderTransform", self: pc.self });
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
728
758
|
var mtype = sender.track.kind === 'video' ? 1 : 0; // corresponds to enum frame_media_type
|
|
729
759
|
var senderStreams = null;
|
|
730
|
-
if (
|
|
760
|
+
if (insertableStreams)
|
|
731
761
|
senderStreams = sender.createEncodedStreams();
|
|
732
762
|
else
|
|
733
763
|
senderStreams = mtype === 1 ? sender.createEncodedVideoStreams() : sender.createEncodedAudioStreams();
|
|
@@ -745,13 +775,17 @@ function setupReceiverTransform(pc, receiver) {
|
|
|
745
775
|
if (!receiver || !receiver.track) {
|
|
746
776
|
return;
|
|
747
777
|
}
|
|
748
|
-
if (!
|
|
778
|
+
if (!insertableLegacy && !insertableStreams) {
|
|
749
779
|
pc_log(LOG_LEVEL_WARN, "setupReceiverTransform: insertable streams not supported");
|
|
750
780
|
return;
|
|
751
781
|
}
|
|
782
|
+
if (pc_env == ENV_FIREFOX) {
|
|
783
|
+
receiver.transform = new RTCRtpScriptTransform(worker, { name: "receiverTransform", self: pc.self, track_id: receiver.track.id });
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
752
786
|
var mtype = receiver.track.kind === 'video' ? 1 : 0; // corresponds to enum frame_media_type
|
|
753
787
|
var receiverStreams = null;
|
|
754
|
-
if (
|
|
788
|
+
if (insertableStreams)
|
|
755
789
|
receiverStreams = receiver.createEncodedStreams();
|
|
756
790
|
else
|
|
757
791
|
receiverStreams = mtype === 1 ? receiver.createEncodedVideoStreams() : receiver.createEncodedAudioStreams();
|
|
@@ -938,7 +972,8 @@ function pc_SetRemoteDescription(hnd, typePtr, sdpPtr) {
|
|
|
938
972
|
.catch(function (err) {
|
|
939
973
|
pc_log(LOG_LEVEL_WARN, "setRemoteDescription failed: " + err, err);
|
|
940
974
|
});
|
|
941
|
-
|
|
975
|
+
if (pc_env != ENV_FIREFOX)
|
|
976
|
+
extractSSRCs(pc, sdp);
|
|
942
977
|
}
|
|
943
978
|
function pc_SetLocalDescription(hnd, typePtr, sdpPtr) {
|
|
944
979
|
pc_log(LOG_LEVEL_INFO, "pc_SetLocalDescription: hnd=".concat(hnd));
|
|
@@ -1164,6 +1199,10 @@ function pc_InitModule(module, logh) {
|
|
|
1164
1199
|
createWorker();
|
|
1165
1200
|
em_module = module;
|
|
1166
1201
|
logFn = logh;
|
|
1202
|
+
var pt = RTCRtpSender.prototype;
|
|
1203
|
+
insertableStreams = !!pt.createEncodedStreams || "transform" in pt;
|
|
1204
|
+
insertableLegacy = !!pt.createEncodedVideoStreams;
|
|
1205
|
+
pc_log(LOG_LEVEL_INFO, "insertable: ".concat(insertableLegacy, "/").concat(insertableStreams));
|
|
1167
1206
|
var callbacks = [
|
|
1168
1207
|
[pc_SetEnv, "vi"],
|
|
1169
1208
|
[pc_New, "iiiiii"],
|
|
@@ -1203,6 +1242,9 @@ function pc_InitModule(module, logh) {
|
|
|
1203
1242
|
function pc_SetUserMediaHandler(umh) {
|
|
1204
1243
|
userMediaHandler = umh;
|
|
1205
1244
|
}
|
|
1245
|
+
function pc_IsConferenceCallingSupported() {
|
|
1246
|
+
return insertableStreams || insertableLegacy;
|
|
1247
|
+
}
|
|
1206
1248
|
function pc_SetAudioStreamHandler(ash) {
|
|
1207
1249
|
audioStreamHandler = ash;
|
|
1208
1250
|
}
|
|
@@ -1334,6 +1376,7 @@ exports.default = {
|
|
|
1334
1376
|
setUserMediaHandler: pc_SetUserMediaHandler,
|
|
1335
1377
|
setAudioStreamHandler: pc_SetAudioStreamHandler,
|
|
1336
1378
|
setVideoStreamHandler: pc_SetVideoStreamHandler,
|
|
1379
|
+
isConferenceCallingSupported: pc_IsConferenceCallingSupported,
|
|
1337
1380
|
replaceTrack: pc_ReplaceTrack,
|
|
1338
1381
|
getStats: pc_GetStats
|
|
1339
1382
|
};
|
package/dist/avs_wcall.d.ts
CHANGED
|
@@ -165,6 +165,7 @@ export declare class Wcall {
|
|
|
165
165
|
setUserMediaHandler(userMediaHandler: UserMediaHandler): void;
|
|
166
166
|
setAudioStreamHandler(audioStreamHandler: AudioStreamHandler): void;
|
|
167
167
|
setVideoStreamHandler(videoStreamHandler: VideoStreamHandler): void;
|
|
168
|
+
isConferenceCallingSupported(): boolean;
|
|
168
169
|
replaceTrack(convid: string, newTrack: MediaStreamTrack): void;
|
|
169
170
|
getStats(convid: string): Promise<Array<{
|
|
170
171
|
userid: string;
|
package/dist/avs_wcall.js
CHANGED
|
@@ -594,6 +594,9 @@ var Wcall = /** @class */ (function () {
|
|
|
594
594
|
Wcall.prototype.setVideoStreamHandler = function (videoStreamHandler) {
|
|
595
595
|
avs_pc_1.default.setVideoStreamHandler(videoStreamHandler);
|
|
596
596
|
};
|
|
597
|
+
Wcall.prototype.isConferenceCallingSupported = function () {
|
|
598
|
+
return avs_pc_1.default.isConferenceCallingSupported();
|
|
599
|
+
};
|
|
597
600
|
Wcall.prototype.replaceTrack = function (convid, newTrack) {
|
|
598
601
|
avs_pc_1.default.replaceTrack(convid, newTrack);
|
|
599
602
|
};
|
package/package.json
CHANGED