@wireapp/avs 10.0.46 → 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_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
- "number",
1483
- "number", "number",
1484
- "number", "number",
1485
- "number", "number"], [pc.self,
1486
- self_audio_level,
1487
- pc.stats.recv_apkts, pc.stats.recv_vpkts,
1488
- pc.stats.sent_apkts, pc.stats.sent_vpkts,
1489
- pc.stats.ploss, rtt]);
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,
@@ -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
@@ -21,5 +21,5 @@
21
21
  "prepack": "prettier -w src/avs_wcall.ts && tsc && cp src/*.js src/*.d.ts dist/",
22
22
  "test": "karma start"
23
23
  },
24
- "version": "10.0.46"
24
+ "version": "10.0.48"
25
25
  }