@wireapp/avs 10.0.45 → 10.0.48
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 +3 -3
- package/dist/avs_pc.js +63 -12
- package/dist/avs_wcall.d.ts +1 -0
- package/dist/avs_wcall.js +3 -0
- package/package.json +1 -1
package/dist/avs_pc.js
CHANGED
|
@@ -15,10 +15,14 @@ var worker;
|
|
|
15
15
|
// Use inlined worker instead....
|
|
16
16
|
//let worker = new Worker('/worker/avs-worker.js', {name: 'AVS worker'});
|
|
17
17
|
// This is the content of the avs-worker.js file, copied here.
|
|
18
|
-
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 let ssrc = meta.synchronizationSource;\n\n if (isVideo)\n ssrc = coder.video.ssrc;\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 ssrc: \"0\",\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 ssrc: \"0\",\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 === 'updateSsrc') {\n\tconst { ssrca, ssrcv } = event.data;\n\tcoder.audio.ssrc = ssrca;\n\tcoder.video.ssrc = ssrcv;\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
|
+
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 let ssrc = meta.synchronizationSource;\n\n if (isVideo)\n ssrc = coder.video.ssrc;\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 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 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 ssrc: \"0\",\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 ssrc: \"0\",\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 === 'updateSsrc') {\n\tconst { ssrca, ssrcv } = event.data;\n\tcoder.audio.ssrc = ssrca;\n\tcoder.video.ssrc = ssrcv;\n }\n else if (op === 'addUser') {\n if (!coder) {\n doLog('addUser: no coder');\n return;\n }\n const {userInfo} = event.data;\n \n doLog('addUser: adding audio ssrc: '+userInfo.ssrca);\n coder.audio.users[userInfo.ssrca] = userInfo;\n if (userInfo.ssrcv != 0) {\n coder.video.users[userInfo.ssrcv] = userInfo;\n }\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";
|
|
19
19
|
function callStreamHandler(pc, userid, clientid, ssrc, track_id) {
|
|
20
20
|
pc_log(LOG_LEVEL_INFO, "vsh: ".concat(videoStreamHandler, " rtc: ").concat(pc.rtc, " ssrc: ").concat(ssrc));
|
|
21
|
+
var uinfo = uinfo_from_device(pc, userid, clientid);
|
|
21
22
|
if (ssrc == "0") {
|
|
23
|
+
if (uinfo) {
|
|
24
|
+
uinfo.video_track_id = "0";
|
|
25
|
+
}
|
|
22
26
|
if (videoStreamHandler) {
|
|
23
27
|
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"));
|
|
24
28
|
videoStreamHandler(pc.convid, userid, clientid, null);
|
|
@@ -32,6 +36,9 @@ function callStreamHandler(pc, userid, clientid, ssrc, track_id) {
|
|
|
32
36
|
return;
|
|
33
37
|
pc_log(LOG_LEVEL_INFO, "vsh: id:".concat(track.id, " looking for: ").concat(track_id));
|
|
34
38
|
if (track.id === track_id) {
|
|
39
|
+
if (uinfo) {
|
|
40
|
+
uinfo.video_track_id = track_id;
|
|
41
|
+
}
|
|
35
42
|
var stream = new MediaStream([trans.receiver.track]);
|
|
36
43
|
if (videoStreamHandler) {
|
|
37
44
|
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"));
|
|
@@ -46,6 +53,9 @@ function callStreamHandler(pc, userid, clientid, ssrc, track_id) {
|
|
|
46
53
|
var track = trans.receiver.track;
|
|
47
54
|
pc_log(LOG_LEVEL_INFO, "vsh: label:".concat(track.label, " id:").concat(track.id, " looking for: ").concat(label_1));
|
|
48
55
|
if (trans.receiver.track.label === label_1) {
|
|
56
|
+
if (uinfo) {
|
|
57
|
+
uinfo.video_track_id = track.id;
|
|
58
|
+
}
|
|
49
59
|
var stream = new MediaStream([trans.receiver.track]);
|
|
50
60
|
if (videoStreamHandler) {
|
|
51
61
|
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"));
|
|
@@ -175,6 +185,26 @@ function uinfo_from_ssrca(pc, ssrc) {
|
|
|
175
185
|
}
|
|
176
186
|
return null;
|
|
177
187
|
}
|
|
188
|
+
function uinfo_from_device(pc, user_id, client_id) {
|
|
189
|
+
for (var _i = 0, _a = Object.keys(pc.users); _i < _a.length; _i++) {
|
|
190
|
+
var key = _a[_i];
|
|
191
|
+
var uinfo = pc.users[key];
|
|
192
|
+
if (uinfo.userid === user_id && uinfo.clientid === client_id) {
|
|
193
|
+
return uinfo;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
function uinfo_from_video_track_id(pc, track_id) {
|
|
199
|
+
for (var _i = 0, _a = Object.keys(pc.users); _i < _a.length; _i++) {
|
|
200
|
+
var key = _a[_i];
|
|
201
|
+
var uinfo = pc.users[key];
|
|
202
|
+
if (uinfo.video_track_id === track_id) {
|
|
203
|
+
return uinfo;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
178
208
|
function replace_track(pc, newTrack) {
|
|
179
209
|
var rtc = pc.rtc;
|
|
180
210
|
if (!rtc)
|
|
@@ -613,6 +643,7 @@ function pc_Create(hnd, privacy, conv_type) {
|
|
|
613
643
|
if (event.track.kind == "video" && videoStreamHandler) {
|
|
614
644
|
var userid = pc.remote_userid;
|
|
615
645
|
var clientid = pc.remote_clientid;
|
|
646
|
+
// @TODO: Remove, because the `keys` from `pc.users[]` is are random strings and not track.id or track.label, so means `uinfo` will be `undefined` everytime!
|
|
616
647
|
var uinfo = pc.users[label];
|
|
617
648
|
if (uinfo) {
|
|
618
649
|
userid = uinfo.userid;
|
|
@@ -993,6 +1024,11 @@ function pc_AddDecoderAnswer(hnd) {
|
|
|
993
1024
|
ccallLocalSdpHandler(pc, 1, "sdp-error", err.toString());
|
|
994
1025
|
});
|
|
995
1026
|
}
|
|
1027
|
+
/**
|
|
1028
|
+
* This methode is called by the jsflow
|
|
1029
|
+
*
|
|
1030
|
+
* @see: jsflow.c::jsflow_add_decoders_for_user
|
|
1031
|
+
*/
|
|
996
1032
|
function pc_AddUserInfo(hnd, labelPtr, useridPtr, clientidPtr, ssrcaPtr, ssrcvPtr, audioIvPtr, videoIvPtr, ivlen) {
|
|
997
1033
|
pc_log(LOG_LEVEL_INFO, "pc_AddUserInfo2: hnd=".concat(hnd));
|
|
998
1034
|
var pc = connectionsStore.getPeerConnection(hnd);
|
|
@@ -1014,12 +1050,15 @@ function pc_AddUserInfo(hnd, labelPtr, useridPtr, clientidPtr, ssrcaPtr, ssrcvPt
|
|
|
1014
1050
|
ivv8.set(vptr);
|
|
1015
1051
|
var uinfo = {
|
|
1016
1052
|
label: label,
|
|
1053
|
+
video_track_id: "0",
|
|
1017
1054
|
userid: userId,
|
|
1018
1055
|
clientid: clientId,
|
|
1019
1056
|
ssrca: ssrca,
|
|
1020
1057
|
ssrcv: ssrcv,
|
|
1021
1058
|
iva: iva8,
|
|
1022
1059
|
ivv: ivv8,
|
|
1060
|
+
frame_width: 0,
|
|
1061
|
+
frame_height: 0,
|
|
1023
1062
|
audio_level: 0,
|
|
1024
1063
|
first_recv_audio: false,
|
|
1025
1064
|
first_succ_audio: false,
|
|
@@ -1454,6 +1493,19 @@ function pc_GetLocalStats(hnd) {
|
|
|
1454
1493
|
apkts = apkts + p;
|
|
1455
1494
|
}
|
|
1456
1495
|
else if (stat.kind === 'video') {
|
|
1496
|
+
var user_info = null;
|
|
1497
|
+
if (!!stat.trackIdentifier) {
|
|
1498
|
+
user_info = uinfo_from_video_track_id(pc, stat.trackIdentifier);
|
|
1499
|
+
}
|
|
1500
|
+
if (user_info !== null) {
|
|
1501
|
+
var frame_height = !!stat.frameHeight ? stat === null || stat === void 0 ? void 0 : stat.frameHeight : 0;
|
|
1502
|
+
var frame_width = !!stat.frameWidth ? stat === null || stat === void 0 ? void 0 : stat.frameWidth : 0;
|
|
1503
|
+
if (user_info.frame_width !== frame_width || user_info.frame_height !== frame_height) {
|
|
1504
|
+
user_info.frame_width = frame_width;
|
|
1505
|
+
user_info.frame_height = frame_height;
|
|
1506
|
+
pc_log(LOG_LEVEL_INFO, "pc_user_resolution: label=".concat(user_info.label, " ").concat(user_info.userid.substring(0, 8), "/").concat(user_info.clientid.substring(0, 4), " resolution:").concat(user_info.frame_width, "x").concat(user_info.frame_height));
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1457
1509
|
vpkts = vpkts + p;
|
|
1458
1510
|
}
|
|
1459
1511
|
}
|
|
@@ -1478,17 +1530,16 @@ function pc_GetLocalStats(hnd) {
|
|
|
1478
1530
|
pc.stats.recv_vpkts = vpkts;
|
|
1479
1531
|
pc.stats.ploss = ploss - pc.stats.lastploss;
|
|
1480
1532
|
pc.stats.lastploss = ploss;
|
|
1481
|
-
em_module.ccall("pc_set_stats", null, ["number",
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
pc.stats.
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
})
|
|
1491
|
-
.catch(function (err) { return pc_log(LOG_LEVEL_INFO, "pc_GetLocalStats: failed hnd=".concat(hnd, " err=").concat(err), err); });
|
|
1533
|
+
em_module.ccall("pc_set_stats", null, ["number", "number", "number", "number", "number", "number", "number", "number"], [
|
|
1534
|
+
pc.self, self_audio_level,
|
|
1535
|
+
pc.stats.recv_apkts,
|
|
1536
|
+
pc.stats.recv_vpkts,
|
|
1537
|
+
pc.stats.sent_apkts,
|
|
1538
|
+
pc.stats.sent_vpkts,
|
|
1539
|
+
pc.stats.ploss,
|
|
1540
|
+
rtt
|
|
1541
|
+
]);
|
|
1542
|
+
}).catch(function (err) { return pc_log(LOG_LEVEL_INFO, "pc_GetLocalStats: failed hnd=".concat(hnd, " err=").concat(err), err); });
|
|
1492
1543
|
}
|
|
1493
1544
|
exports.default = {
|
|
1494
1545
|
init: pc_InitModule,
|
package/dist/avs_wcall.d.ts
CHANGED
|
@@ -135,6 +135,7 @@ export declare class Wcall {
|
|
|
135
135
|
setShutdownHandler(wuser: number, shuth: WcallShutdownHandler, arg?: number): void;
|
|
136
136
|
destroy(wuser: number): void;
|
|
137
137
|
setTrace(wuser: number, trace: number): void;
|
|
138
|
+
setBackground(wuser: number, background: number): number;
|
|
138
139
|
start(wuser: number, convid: string, call_type: number, conv_type: number, audio_cbr: number): number;
|
|
139
140
|
answer(wuser: number, convid: string, call_type: number, audio_cbr: number): number;
|
|
140
141
|
resp(wuser: number, status: number, reason: string, ctx: number): void;
|
package/dist/avs_wcall.js
CHANGED
|
@@ -375,6 +375,9 @@ var Wcall = /** @class */ (function () {
|
|
|
375
375
|
Wcall.prototype.setTrace = function (wuser, trace) {
|
|
376
376
|
this.em_module.ccall("wcall_set_trace", "null", ["number", "number"], [wuser, trace]);
|
|
377
377
|
};
|
|
378
|
+
Wcall.prototype.setBackground = function (wuser, background) {
|
|
379
|
+
return this.em_module.ccall("wcall_set_background", "number", ["number", "number"], [wuser, background]);
|
|
380
|
+
};
|
|
378
381
|
Wcall.prototype.start = function (wuser, convid, call_type, conv_type, audio_cbr) {
|
|
379
382
|
return this.em_module.ccall("wcall_start", "number", ["number", "string", "number", "number", "number"], [wuser, convid, call_type, conv_type, audio_cbr]);
|
|
380
383
|
};
|
package/package.json
CHANGED