@brandbrigade/ott-bb-player 1.0.16 → 1.0.18

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.
@@ -0,0 +1,4335 @@
1
+ // test v3
2
+ const META_WORKER = `try {
3
+
4
+ let UZIP = {};
5
+ if(typeof module == "object") module.exports = UZIP;
6
+ UZIP["parse"] = function(buf, onlyNames) // ArrayBuffer
7
+ {
8
+ let rUs = UZIP.bin.readUshort, rUi = UZIP.bin.readUint, o = 0, out = {};
9
+ let data = new Uint8Array(buf);
10
+ let eocd = data.length-4;
11
+
12
+ while(rUi(data, eocd)!=0x06054b50) eocd--;
13
+
14
+ o = eocd;
15
+ o+=4; // sign = 0x06054b50
16
+ o+=4; // disks = 0;
17
+ let cnu = rUs(data, o); o+=2;
18
+ let cnt = rUs(data, o); o+=2;
19
+
20
+ let csize = rUi(data, o); o+=4;
21
+ let coffs = rUi(data, o); o+=4;
22
+
23
+ o = coffs;
24
+ for(let i=0; i<cnu; i++)
25
+ {
26
+ let sign = rUi(data, o); o+=4;
27
+ o += 4; // versions;
28
+ o += 4; // flag + compr
29
+ o += 4; // time
30
+
31
+ let crc32 = rUi(data, o); o+=4;
32
+ let csize = rUi(data, o); o+=4;
33
+ let usize = rUi(data, o); o+=4;
34
+
35
+ let nl = rUs(data, o), el = rUs(data, o+2), cl = rUs(data, o+4); o += 6; // name, extra, comment
36
+ o += 8; // disk, attribs
37
+
38
+ let roff = rUi(data, o); o+=4;
39
+ o += nl + el + cl;
40
+
41
+ UZIP._readLocal(data, roff, out, csize, usize, onlyNames);
42
+ }
43
+ //console.log(out);
44
+ return out;
45
+ }
46
+
47
+ UZIP._readLocal = function(data, o, out, csize, usize, onlyNames)
48
+ {
49
+ let rUs = UZIP.bin.readUshort, rUi = UZIP.bin.readUint;
50
+ let sign = rUi(data, o); o+=4;
51
+ let ver = rUs(data, o); o+=2;
52
+ let gpflg = rUs(data, o); o+=2;
53
+ //if((gpflg&8)!=0) throw "unknown sizes";
54
+ let cmpr = rUs(data, o); o+=2;
55
+
56
+ let time = rUi(data, o); o+=4;
57
+
58
+ let crc32 = rUi(data, o); o+=4;
59
+ //let csize = rUi(data, o); o+=4;
60
+ //let usize = rUi(data, o); o+=4;
61
+ o+=8;
62
+
63
+ let nlen = rUs(data, o); o+=2;
64
+ let elen = rUs(data, o); o+=2;
65
+
66
+ let name = UZIP.bin.readUTF8(data, o, nlen); o+=nlen; //console.log(name);
67
+ o += elen;
68
+
69
+ //console.log(sign.toString(16), ver, gpflg, cmpr, crc32.toString(16), "csize, usize", csize, usize, nlen, elen, name, o);
70
+ if(onlyNames) { out[name]={size:usize, csize:csize}; return; }
71
+ let file = new Uint8Array(data.buffer, o);
72
+ if(false) {}
73
+ else if(cmpr==0) out[name] = new Uint8Array(file.buffer.slice(o, o+csize));
74
+ else if(cmpr==8) {
75
+ let buf = new Uint8Array(usize); UZIP.inflateRaw(file, buf);
76
+ /*let nbuf = pako["inflateRaw"](file);
77
+ if(usize>8514000) {
78
+ //console.log(PUtils.readASCII(buf , 8514500, 500));
79
+ //console.log(PUtils.readASCII(nbuf, 8514500, 500));
80
+ }
81
+ for(let i=0; i<buf.length; i++) if(buf[i]!=nbuf[i]) { console.log(buf.length, nbuf.length, usize, i); throw "e"; }
82
+ */
83
+ out[name] = buf;
84
+ }
85
+ else throw "unknown compression method: "+cmpr;
86
+ }
87
+
88
+ UZIP.inflateRaw = function(file, buf) { return UZIP.F.inflate(file, buf); }
89
+ UZIP.inflate = function(file, buf) {
90
+ let CMF = file[0], FLG = file[1];
91
+ let CM = (CMF&15), CINFO = (CMF>>>4);
92
+ //console.log(CM, CINFO,CMF,FLG);
93
+ return UZIP.inflateRaw(new Uint8Array(file.buffer, file.byteOffset+2, file.length-6), buf);
94
+ }
95
+ UZIP.deflate = function(data, opts/*, buf, off*/) {
96
+ if(opts==null) opts={level:6};
97
+ let off=0, buf=new Uint8Array(50+Math.floor(data.length*1.1));
98
+ buf[off]=120; buf[off+1]=156; off+=2;
99
+ off = UZIP.F.deflateRaw(data, buf, off, opts.level);
100
+ let crc = UZIP.adler(data, 0, data.length);
101
+ buf[off+0]=((crc>>>24)&255);
102
+ buf[off+1]=((crc>>>16)&255);
103
+ buf[off+2]=((crc>>> 8)&255);
104
+ buf[off+3]=((crc>>> 0)&255);
105
+ return new Uint8Array(buf.buffer, 0, off+4);
106
+ }
107
+ UZIP.deflateRaw = function(data, opts) {
108
+ if(opts==null) opts={level:6};
109
+ let buf=new Uint8Array(50+Math.floor(data.length*1.1));
110
+ let off = UZIP.F.deflateRaw(data, buf, off, opts.level);
111
+ return new Uint8Array(buf.buffer, 0, off);
112
+ }
113
+
114
+
115
+ UZIP.encode = function(obj, noCmpr) {
116
+ if(noCmpr==null) noCmpr=false;
117
+ let tot = 0, wUi = UZIP.bin.writeUint, wUs = UZIP.bin.writeUshort;
118
+ let zpd = {};
119
+ for(let p in obj) { let cpr = !UZIP._noNeed(p) && !noCmpr, buf = obj[p], crc = UZIP.crc.crc(buf,0,buf.length);
120
+ zpd[p] = { cpr:cpr, usize:buf.length, crc:crc, file: (cpr ? UZIP.deflateRaw(buf) : buf) }; }
121
+
122
+ for(let p in zpd) tot += zpd[p].file.length + 30 + 46 + 2*UZIP.bin.sizeUTF8(p);
123
+ tot += 22;
124
+
125
+ let data = new Uint8Array(tot), o = 0;
126
+ let fof = []
127
+
128
+ for(let p in zpd) {
129
+ let file = zpd[p]; fof.push(o);
130
+ o = UZIP._writeHeader(data, o, p, file, 0);
131
+ }
132
+ let i=0, ioff = o;
133
+ for(let p in zpd) {
134
+ let file = zpd[p]; fof.push(o);
135
+ o = UZIP._writeHeader(data, o, p, file, 1, fof[i++]);
136
+ }
137
+ let csize = o-ioff;
138
+
139
+ wUi(data, o, 0x06054b50); o+=4;
140
+ o += 4; // disks
141
+ wUs(data, o, i); o += 2;
142
+ wUs(data, o, i); o += 2; // number of c d records
143
+ wUi(data, o, csize); o += 4;
144
+ wUi(data, o, ioff ); o += 4;
145
+ o += 2;
146
+ return data.buffer;
147
+ }
148
+ // no need to compress .PNG, .ZIP, .JPEG ....
149
+ UZIP._noNeed = function(fn) { let ext = fn.split(".").pop().toLowerCase(); return "png,jpg,jpeg,zip".indexOf(ext)!=-1; }
150
+
151
+ UZIP._writeHeader = function(data, o, p, obj, t, roff)
152
+ {
153
+ let wUi = UZIP.bin.writeUint, wUs = UZIP.bin.writeUshort;
154
+ let file = obj.file;
155
+
156
+ wUi(data, o, t==0 ? 0x04034b50 : 0x02014b50); o+=4; // sign
157
+ if(t==1) o+=2; // ver made by
158
+ wUs(data, o, 20); o+=2; // ver
159
+ wUs(data, o, 0); o+=2; // gflip
160
+ wUs(data, o, obj.cpr?8:0); o+=2; // cmpr
161
+
162
+ wUi(data, o, 0); o+=4; // time
163
+ wUi(data, o, obj.crc); o+=4; // crc32
164
+ wUi(data, o, file.length); o+=4; // csize
165
+ wUi(data, o, obj.usize); o+=4; // usize
166
+
167
+ wUs(data, o, UZIP.bin.sizeUTF8(p)); o+=2; // nlen
168
+ wUs(data, o, 0); o+=2; // elen
169
+
170
+ if(t==1) {
171
+ o += 2; // comment length
172
+ o += 2; // disk number
173
+ o += 6; // attributes
174
+ wUi(data, o, roff); o+=4; // usize
175
+ }
176
+ let nlen = UZIP.bin.writeUTF8(data, o, p); o+= nlen;
177
+ if(t==0) { data.set(file, o); o += file.length; }
178
+ return o;
179
+ }
180
+ UZIP.crc = {
181
+ table : ( function() {
182
+ let tab = new Uint32Array(256);
183
+ for (let n=0; n<256; n++) {
184
+ let c = n;
185
+ for (let k=0; k<8; k++) {
186
+ if (c & 1) c = 0xedb88320 ^ (c >>> 1);
187
+ else c = c >>> 1;
188
+ }
189
+ tab[n] = c; }
190
+ return tab; })(),
191
+ update : function(c, buf, off, len) {
192
+ for (let i=0; i<len; i++) c = UZIP.crc.table[(c ^ buf[off+i]) & 0xff] ^ (c >>> 8);
193
+ return c;
194
+ },
195
+ crc : function(b,o,l) { return UZIP.crc.update(0xffffffff,b,o,l) ^ 0xffffffff; }
196
+ }
197
+ UZIP.adler = function(data,o,len) {
198
+ let a = 1, b = 0;
199
+ let off = o, end=o+len;
200
+ while(off<end) {
201
+ let eend = Math.min(off+5552, end);
202
+ while(off<eend) {
203
+ a += data[off++];
204
+ b += a;
205
+ }
206
+ a=a%65521;
207
+ b=b%65521;
208
+ }
209
+ return (b << 16) | a;
210
+ }
211
+
212
+ UZIP.bin = {
213
+ readUshort : function(buff,p) { return (buff[p]) | (buff[p+1]<<8); },
214
+ writeUshort: function(buff,p,n){ buff[p] = (n)&255; buff[p+1] = (n>>8)&255; },
215
+ readUint : function(buff,p) { return (buff[p+3]*(256*256*256)) + ((buff[p+2]<<16) | (buff[p+1]<< 8) | buff[p]); },
216
+ writeUint : function(buff,p,n){ buff[p]=n&255; buff[p+1]=(n>>8)&255; buff[p+2]=(n>>16)&255; buff[p+3]=(n>>24)&255; },
217
+ readASCII : function(buff,p,l){ let s = ""; for(let i=0; i<l; i++) s += String.fromCharCode(buff[p+i]); return s; },
218
+ writeASCII : function(data,p,s){ for(let i=0; i<s.length; i++) data[p+i] = s.charCodeAt(i); },
219
+ pad : function(n) { return n.length < 2 ? "0" + n : n; },
220
+ readUTF8 : function(buff, p, l) {
221
+ let s = "", ns;
222
+ for(let i=0; i<l; i++) s += "%" + UZIP.bin.pad(buff[p+i].toString(16));
223
+ try { ns = decodeURIComponent(s); }
224
+ catch(e) { return UZIP.bin.readASCII(buff, p, l); }
225
+ return ns;
226
+ },
227
+ writeUTF8 : function(buff, p, str) {
228
+ let strl = str.length, i=0;
229
+ for(let ci=0; ci<strl; ci++)
230
+ {
231
+ let code = str.charCodeAt(ci);
232
+ if ((code&(0xffffffff-(1<< 7)+1))==0) { buff[p+i] = ( code ); i++; }
233
+ else if((code&(0xffffffff-(1<<11)+1))==0) { buff[p+i] = (192|(code>> 6)); buff[p+i+1] = (128|((code>> 0)&63)); i+=2; }
234
+ else if((code&(0xffffffff-(1<<16)+1))==0) { buff[p+i] = (224|(code>>12)); buff[p+i+1] = (128|((code>> 6)&63)); buff[p+i+2] = (128|((code>>0)&63)); i+=3; }
235
+ else if((code&(0xffffffff-(1<<21)+1))==0) { buff[p+i] = (240|(code>>18)); buff[p+i+1] = (128|((code>>12)&63)); buff[p+i+2] = (128|((code>>6)&63)); buff[p+i+3] = (128|((code>>0)&63)); i+=4; }
236
+ else throw "e";
237
+ }
238
+ return i;
239
+ },
240
+ sizeUTF8 : function(str) {
241
+ let strl = str.length, i=0;
242
+ for(let ci=0; ci<strl; ci++)
243
+ {
244
+ let code = str.charCodeAt(ci);
245
+ if ((code&(0xffffffff-(1<< 7)+1))==0) { i++ ; }
246
+ else if((code&(0xffffffff-(1<<11)+1))==0) { i+=2; }
247
+ else if((code&(0xffffffff-(1<<16)+1))==0) { i+=3; }
248
+ else if((code&(0xffffffff-(1<<21)+1))==0) { i+=4; }
249
+ else throw "e";
250
+ }
251
+ return i;
252
+ }
253
+ }
254
+ UZIP.F = {};
255
+
256
+ UZIP.F.deflateRaw = function(data, out, opos, lvl) {
257
+ let opts = [
258
+ /*
259
+ ush good_length; /* reduce lazy search above this match length
260
+ ush max_lazy; /* do not perform lazy search above this match length
261
+ ush nice_length; /* quit search above this match length
262
+ */
263
+ /* good lazy nice chain */
264
+ /* 0 */ [ 0, 0, 0, 0,0], /* store only */
265
+ /* 1 */ [ 4, 4, 8, 4,0], /* max speed, no lazy matches */
266
+ /* 2 */ [ 4, 5, 16, 8,0],
267
+ /* 3 */ [ 4, 6, 16, 16,0],
268
+
269
+ /* 4 */ [ 4, 10, 16, 32,0], /* lazy matches */
270
+ /* 5 */ [ 8, 16, 32, 32,0],
271
+ /* 6 */ [ 8, 16, 128, 128,0],
272
+ /* 7 */ [ 8, 32, 128, 256,0],
273
+ /* 8 */ [32, 128, 258, 1024,1],
274
+ /* 9 */ [32, 258, 258, 4096,1]]; /* max compression */
275
+
276
+ let opt = opts[lvl];
277
+
278
+
279
+ let U = UZIP.F.U, goodIndex = UZIP.F._goodIndex, hash = UZIP.F._hash, putsE = UZIP.F._putsE;
280
+ let i = 0, pos = opos<<3, cvrd = 0, dlen = data.length;
281
+
282
+ if(lvl==0) {
283
+ while(i<dlen) { let len = Math.min(0xffff, dlen-i);
284
+ putsE(out, pos, (i+len==dlen ? 1 : 0)); pos = UZIP.F._copyExact(data, i, len, out, pos+8); i += len; }
285
+ return pos>>>3;
286
+ }
287
+
288
+ let lits = U.lits, strt=U.strt, prev=U.prev, li=0, lc=0, bs=0, ebits=0, c=0, nc=0; // last_item, literal_count, block_start
289
+ if(dlen>2) { nc=UZIP.F._hash(data,0); strt[nc]=0; }
290
+ let nmch=0,nmci=0;
291
+
292
+ for(i=0; i<dlen; i++) {
293
+ c = nc;
294
+ //*
295
+ if(i+1<dlen-2) {
296
+ nc = UZIP.F._hash(data, i+1);
297
+ let ii = ((i+1)&0x7fff);
298
+ prev[ii]=strt[nc];
299
+ strt[nc]=ii;
300
+ } //*/
301
+ if(cvrd<=i) {
302
+ if((li>14000 || lc>26697) && (dlen-i)>100) {
303
+ if(cvrd<i) { lits[li]=i-cvrd; li+=2; cvrd=i; }
304
+ pos = UZIP.F._writeBlock(((i==dlen-1) || (cvrd==dlen))?1:0, lits, li, ebits, data,bs,i-bs, out, pos); li=lc=ebits=0; bs=i;
305
+ }
306
+
307
+ let mch = 0;
308
+ //if(nmci==i) mch= nmch; else
309
+ if(i<dlen-2) mch = UZIP.F._bestMatch(data, i, prev, c, Math.min(opt[2],dlen-i), opt[3]);
310
+ /*
311
+ if(mch!=0 && opt[4]==1 && (mch>>>16)<opt[1] && i+1<dlen-2) {
312
+ nmch = UZIP.F._bestMatch(data, i+1, prev, nc, opt[2], opt[3]); nmci=i+1;
313
+ //let mch2 = UZIP.F._bestMatch(data, i+2, prev, nnc); //nmci=i+1;
314
+ if((nmch>>>16)>(mch>>>16)) mch=0;
315
+ }//*/
316
+ let len = mch>>>16, dst = mch&0xffff; //if(i-dst<0) throw "e";
317
+ if(mch!=0) {
318
+ let len = mch>>>16, dst = mch&0xffff; //if(i-dst<0) throw "e";
319
+ let lgi = goodIndex(len, U.of0); U.lhst[257+lgi]++;
320
+ let dgi = goodIndex(dst, U.df0); U.dhst[ dgi]++; ebits += U.exb[lgi] + U.dxb[dgi];
321
+ lits[li] = (len<<23)|(i-cvrd); lits[li+1] = (dst<<16)|(lgi<<8)|dgi; li+=2;
322
+ cvrd = i + len;
323
+ }
324
+ else { U.lhst[data[i]]++; }
325
+ lc++;
326
+ }
327
+ }
328
+ if(bs!=i || data.length==0) {
329
+ if(cvrd<i) { lits[li]=i-cvrd; li+=2; cvrd=i; }
330
+ pos = UZIP.F._writeBlock(1, lits, li, ebits, data,bs,i-bs, out, pos); li=0; lc=0; li=lc=ebits=0; bs=i;
331
+ }
332
+ while((pos&7)!=0) pos++;
333
+ return pos>>>3;
334
+ }
335
+ UZIP.F._bestMatch = function(data, i, prev, c, nice, chain) {
336
+ let ci = (i&0x7fff), pi=prev[ci];
337
+ //console.log("----", i);
338
+ let dif = ((ci-pi + (1<<15)) & 0x7fff); if(pi==ci || c!=UZIP.F._hash(data,i-dif)) return 0;
339
+ let tl=0, td=0; // top length, top distance
340
+ let dlim = Math.min(0x7fff, i);
341
+ while(dif<=dlim && --chain!=0 && pi!=ci /*&& c==UZIP.F._hash(data,i-dif)*/) {
342
+ if(tl==0 || (data[i+tl]==data[i+tl-dif])) {
343
+ let cl = UZIP.F._howLong(data, i, dif);
344
+ if(cl>tl) {
345
+ tl=cl; td=dif; if(tl>=nice) break; //*
346
+ if(dif+2<cl) cl = dif+2;
347
+ let maxd = 0; // pi does not point to the start of the word
348
+ for(let j=0; j<cl-2; j++) {
349
+ let ei = (i-dif+j+ (1<<15)) & 0x7fff;
350
+ let li = prev[ei];
351
+ let curd = (ei-li + (1<<15)) & 0x7fff;
352
+ if(curd>maxd) { maxd=curd; pi = ei; }
353
+ } //*/
354
+ }
355
+ }
356
+
357
+ ci=pi; pi = prev[ci];
358
+ dif += ((ci-pi + (1<<15)) & 0x7fff);
359
+ }
360
+ return (tl<<16)|td;
361
+ }
362
+ UZIP.F._howLong = function(data, i, dif) {
363
+ if(data[i]!=data[i-dif] || data[i+1]!=data[i+1-dif] || data[i+2]!=data[i+2-dif]) return 0;
364
+ let oi=i, l = Math.min(data.length, i+258); i+=3;
365
+ //while(i+4<l && data[i]==data[i-dif] && data[i+1]==data[i+1-dif] && data[i+2]==data[i+2-dif] && data[i+3]==data[i+3-dif]) i+=4;
366
+ while(i<l && data[i]==data[i-dif]) i++;
367
+ return i-oi;
368
+ }
369
+ UZIP.F._hash = function(data, i) {
370
+ return (((data[i]<<8) | data[i+1])+(data[i+2]<<4))&0xffff;
371
+ //let hash_shift = 0, hash_mask = 255;
372
+ //let h = data[i+1] % 251;
373
+ //h = (((h << 8) + data[i+2]) % 251);
374
+ //h = (((h << 8) + data[i+2]) % 251);
375
+ //h = ((h<<hash_shift) ^ (c) ) & hash_mask;
376
+ //return h | (data[i]<<8);
377
+ //return (data[i] | (data[i+1]<<8));
378
+ }
379
+ //UZIP.___toth = 0;
380
+ UZIP.saved = 0;
381
+ UZIP.F._writeBlock = function(BFINAL, lits, li, ebits, data,o0,l0, out, pos) {
382
+ let U = UZIP.F.U, putsF = UZIP.F._putsF, putsE = UZIP.F._putsE;
383
+
384
+ //*
385
+ let T, ML, MD, MH, numl, numd, numh, lset, dset; U.lhst[256]++;
386
+ T = UZIP.F.getTrees(); ML=T[0]; MD=T[1]; MH=T[2]; numl=T[3]; numd=T[4]; numh=T[5]; lset=T[6]; dset=T[7];
387
+
388
+ let cstSize = (((pos+3)&7)==0 ? 0 : 8-((pos+3)&7)) + 32 + (l0<<3);
389
+ let fxdSize = ebits + UZIP.F.contSize(U.fltree, U.lhst) + UZIP.F.contSize(U.fdtree, U.dhst);
390
+ let dynSize = ebits + UZIP.F.contSize(U.ltree , U.lhst) + UZIP.F.contSize(U.dtree , U.dhst);
391
+ dynSize += 14 + 3*numh + UZIP.F.contSize(U.itree, U.ihst) + (U.ihst[16]*2 + U.ihst[17]*3 + U.ihst[18]*7);
392
+
393
+ for(let j=0; j<286; j++) U.lhst[j]=0; for(let j=0; j<30; j++) U.dhst[j]=0; for(let j=0; j<19; j++) U.ihst[j]=0;
394
+ //*/
395
+ let BTYPE = (cstSize<fxdSize && cstSize<dynSize) ? 0 : ( fxdSize<dynSize ? 1 : 2 );
396
+ putsF(out, pos, BFINAL); putsF(out, pos+1, BTYPE); pos+=3;
397
+
398
+ let opos = pos;
399
+ if(BTYPE==0) {
400
+ while((pos&7)!=0) pos++;
401
+ pos = UZIP.F._copyExact(data, o0, l0, out, pos);
402
+ }
403
+ else {
404
+ let ltree, dtree;
405
+ if(BTYPE==1) { ltree=U.fltree; dtree=U.fdtree; }
406
+ if(BTYPE==2) {
407
+ UZIP.F.makeCodes(U.ltree, ML); UZIP.F.revCodes(U.ltree, ML);
408
+ UZIP.F.makeCodes(U.dtree, MD); UZIP.F.revCodes(U.dtree, MD);
409
+ UZIP.F.makeCodes(U.itree, MH); UZIP.F.revCodes(U.itree, MH);
410
+
411
+ ltree = U.ltree; dtree = U.dtree;
412
+
413
+ putsE(out, pos,numl-257); pos+=5; // 286
414
+ putsE(out, pos,numd- 1); pos+=5; // 30
415
+ putsE(out, pos,numh- 4); pos+=4; // 19
416
+
417
+ for(let i=0; i<numh; i++) putsE(out, pos+i*3, U.itree[(U.ordr[i]<<1)+1]); pos+=3* numh;
418
+ pos = UZIP.F._codeTiny(lset, U.itree, out, pos);
419
+ pos = UZIP.F._codeTiny(dset, U.itree, out, pos);
420
+ }
421
+
422
+ let off=o0;
423
+ for(let si=0; si<li; si+=2) {
424
+ let qb=lits[si], len=(qb>>>23), end = off+(qb&((1<<23)-1));
425
+ while(off<end) pos = UZIP.F._writeLit(data[off++], ltree, out, pos);
426
+
427
+ if(len!=0) {
428
+ let qc = lits[si+1], dst=(qc>>16), lgi=(qc>>8)&255, dgi=(qc&255);
429
+ pos = UZIP.F._writeLit(257+lgi, ltree, out, pos);
430
+ putsE(out, pos, len-U.of0[lgi]); pos+=U.exb[lgi];
431
+
432
+ pos = UZIP.F._writeLit(dgi, dtree, out, pos);
433
+ putsF(out, pos, dst-U.df0[dgi]); pos+=U.dxb[dgi]; off+=len;
434
+ }
435
+ }
436
+ pos = UZIP.F._writeLit(256, ltree, out, pos);
437
+ }
438
+ //console.log(pos-opos, fxdSize, dynSize, cstSize);
439
+ return pos;
440
+ }
441
+ UZIP.F._copyExact = function(data,off,len,out,pos) {
442
+ let p8 = (pos>>>3);
443
+ out[p8]=(len); out[p8+1]=(len>>>8); out[p8+2]=255-out[p8]; out[p8+3]=255-out[p8+1]; p8+=4;
444
+ out.set(new Uint8Array(data.buffer, off, len), p8);
445
+ //for(let i=0; i<len; i++) out[p8+i]=data[off+i];
446
+ return pos + ((len+4)<<3);
447
+ }
448
+ /*
449
+ Interesting facts:
450
+ - decompressed block can have bytes, which do not occur in a Huffman tree (copied from the previous block by reference)
451
+ */
452
+
453
+ UZIP.F.getTrees = function() {
454
+ let U = UZIP.F.U;
455
+ let ML = UZIP.F._hufTree(U.lhst, U.ltree, 15);
456
+ let MD = UZIP.F._hufTree(U.dhst, U.dtree, 15);
457
+ let lset = [], numl = UZIP.F._lenCodes(U.ltree, lset);
458
+ let dset = [], numd = UZIP.F._lenCodes(U.dtree, dset);
459
+ for(let i=0; i<lset.length; i+=2) U.ihst[lset[i]]++;
460
+ for(let i=0; i<dset.length; i+=2) U.ihst[dset[i]]++;
461
+ let MH = UZIP.F._hufTree(U.ihst, U.itree, 7);
462
+ let numh = 19; while(numh>4 && U.itree[(U.ordr[numh-1]<<1)+1]==0) numh--;
463
+ return [ML, MD, MH, numl, numd, numh, lset, dset];
464
+ }
465
+ UZIP.F.getSecond= function(a) { let b=[]; for(let i=0; i<a.length; i+=2) b.push (a[i+1]); return b; }
466
+ UZIP.F.nonZero = function(a) { let b= ""; for(let i=0; i<a.length; i+=2) if(a[i+1]!=0)b+=(i>>1)+","; return b; }
467
+ UZIP.F.contSize = function(tree, hst) { let s=0; for(let i=0; i<hst.length; i++) s+= hst[i]*tree[(i<<1)+1]; return s; }
468
+ UZIP.F._codeTiny = function(set, tree, out, pos) {
469
+ for(let i=0; i<set.length; i+=2) {
470
+ let l = set[i], rst = set[i+1]; //console.log(l, pos, tree[(l<<1)+1]);
471
+ pos = UZIP.F._writeLit(l, tree, out, pos);
472
+ let rsl = l==16 ? 2 : (l==17 ? 3 : 7);
473
+ if(l>15) { UZIP.F._putsE(out, pos, rst, rsl); pos+=rsl; }
474
+ }
475
+ return pos;
476
+ }
477
+ UZIP.F._lenCodes = function(tree, set) {
478
+ let len=tree.length; while(len!=2 && tree[len-1]==0) len-=2; // when no distances, keep one code with length 0
479
+ for(let i=0; i<len; i+=2) {
480
+ let l = tree[i+1], nxt = (i+3<len ? tree[i+3]:-1), nnxt = (i+5<len ? tree[i+5]:-1), prv = (i==0 ? -1 : tree[i-1]);
481
+ if(l==0 && nxt==l && nnxt==l) {
482
+ let lz = i+5;
483
+ while(lz+2<len && tree[lz+2]==l) lz+=2;
484
+ let zc = Math.min((lz+1-i)>>>1, 138);
485
+ if(zc<11) set.push(17, zc-3);
486
+ else set.push(18, zc-11);
487
+ i += zc*2-2;
488
+ }
489
+ else if(l==prv && nxt==l && nnxt==l) {
490
+ let lz = i+5;
491
+ while(lz+2<len && tree[lz+2]==l) lz+=2;
492
+ let zc = Math.min((lz+1-i)>>>1, 6);
493
+ set.push(16, zc-3);
494
+ i += zc*2-2;
495
+ }
496
+ else set.push(l, 0);
497
+ }
498
+ return len>>>1;
499
+ }
500
+ UZIP.F._hufTree = function(hst, tree, MAXL) {
501
+ let list=[], hl = hst.length, tl=tree.length, i=0;
502
+ for(i=0; i<tl; i+=2) { tree[i]=0; tree[i+1]=0; }
503
+ for(i=0; i<hl; i++) if(hst[i]!=0) list.push({lit:i, f:hst[i]});
504
+ let end = list.length, l2=list.slice(0);
505
+ if(end==0) return 0; // empty histogram (usually for dist)
506
+ if(end==1) { let lit=list[0].lit, l2=lit==0?1:0; tree[(lit<<1)+1]=1; tree[(l2<<1)+1]=1; return 1; }
507
+ list.sort(function(a,b){return a.f-b.f;});
508
+ let a=list[0], b=list[1], i0=0, i1=1, i2=2; list[0]={lit:-1,f:a.f+b.f,l:a,r:b,d:0};
509
+ while(i1!=end-1) {
510
+ if(i0!=i1 && (i2==end || list[i0].f<list[i2].f)) { a=list[i0++]; } else { a=list[i2++]; }
511
+ if(i0!=i1 && (i2==end || list[i0].f<list[i2].f)) { b=list[i0++]; } else { b=list[i2++]; }
512
+ list[i1++]={lit:-1,f:a.f+b.f, l:a,r:b};
513
+ }
514
+ let maxl = UZIP.F.setDepth(list[i1-1], 0);
515
+ if(maxl>MAXL) { UZIP.F.restrictDepth(l2, MAXL, maxl); maxl = MAXL; }
516
+ for(i=0; i<end; i++) tree[(l2[i].lit<<1)+1]=l2[i].d;
517
+ return maxl;
518
+ }
519
+
520
+ UZIP.F.setDepth = function(t, d) {
521
+ if(t.lit!=-1) { t.d=d; return d; }
522
+ return Math.max( UZIP.F.setDepth(t.l, d+1), UZIP.F.setDepth(t.r, d+1) );
523
+ }
524
+
525
+ UZIP.F.restrictDepth = function(dps, MD, maxl) {
526
+ let i=0, bCost=1<<(maxl-MD), dbt=0;
527
+ dps.sort(function(a,b){return b.d==a.d ? a.f-b.f : b.d-a.d;});
528
+
529
+ for(i=0; i<dps.length; i++) if(dps[i].d>MD) { let od=dps[i].d; dps[i].d=MD; dbt+=bCost-(1<<(maxl-od)); } else break;
530
+ dbt = dbt>>>(maxl-MD);
531
+ while(dbt>0) { let od=dps[i].d; if(od<MD) { dps[i].d++; dbt-=(1<<(MD-od-1)); } else i++; }
532
+ for(; i>=0; i--) if(dps[i].d==MD && dbt<0) { dps[i].d--; dbt++; } if(dbt!=0) console.log("debt left");
533
+ }
534
+
535
+ UZIP.F._goodIndex = function(v, arr) {
536
+ let i=0; if(arr[i|16]<=v) i|=16; if(arr[i|8]<=v) i|=8; if(arr[i|4]<=v) i|=4; if(arr[i|2]<=v) i|=2; if(arr[i|1]<=v) i|=1; return i;
537
+ }
538
+ UZIP.F._writeLit = function(ch, ltree, out, pos) {
539
+ UZIP.F._putsF(out, pos, ltree[ch<<1]);
540
+ return pos+ltree[(ch<<1)+1];
541
+ }
542
+ UZIP.F.inflate = function(data, buf) {
543
+ let u8=Uint8Array;
544
+ if(data[0]==3 && data[1]==0) return (buf ? buf : new u8(0));
545
+ let F=UZIP.F, bitsF = F._bitsF, bitsE = F._bitsE, decodeTiny = F._decodeTiny, makeCodes = F.makeCodes, codes2map=F.codes2map, get17 = F._get17;
546
+ let U = F.U;
547
+
548
+ let noBuf = (buf==null);
549
+ if(noBuf) buf = new u8((data.length>>>2)<<3);
550
+
551
+ let BFINAL=0, BTYPE=0, HLIT=0, HDIST=0, HCLEN=0, ML=0, MD=0;
552
+ let off = 0, pos = 0;
553
+ let lmap, dmap;
554
+
555
+ while(BFINAL==0) {
556
+ BFINAL = bitsF(data, pos , 1);
557
+ BTYPE = bitsF(data, pos+1, 2); pos+=3;
558
+ //console.log(BFINAL, BTYPE);
559
+
560
+ if(BTYPE==0) {
561
+ if((pos&7)!=0) pos+=8-(pos&7);
562
+ let p8 = (pos>>>3)+4, len = data[p8-4]|(data[p8-3]<<8); //console.log(len);//bitsF(data, pos, 16),
563
+ if(noBuf) buf=UZIP.F._check(buf, off+len);
564
+ buf.set(new u8(data.buffer, data.byteOffset+p8, len), off);
565
+ //for(let i=0; i<len; i++) buf[off+i] = data[p8+i];
566
+ //for(let i=0; i<len; i++) if(buf[off+i] != data[p8+i]) throw "e";
567
+ pos = ((p8+len)<<3); off+=len; continue;
568
+ }
569
+ if(noBuf) buf=UZIP.F._check(buf, off+(1<<17)); // really not enough in many cases (but PNG and ZIP provide buffer in advance)
570
+ if(BTYPE==1) { lmap = U.flmap; dmap = U.fdmap; ML = (1<<9)-1; MD = (1<<5)-1; }
571
+ if(BTYPE==2) {
572
+ HLIT = bitsE(data, pos , 5)+257;
573
+ HDIST = bitsE(data, pos+ 5, 5)+ 1;
574
+ HCLEN = bitsE(data, pos+10, 4)+ 4; pos+=14;
575
+
576
+ let ppos = pos;
577
+ for(let i=0; i<38; i+=2) { U.itree[i]=0; U.itree[i+1]=0; }
578
+ let tl = 1;
579
+ for(let i=0; i<HCLEN; i++) { let l=bitsE(data, pos+i*3, 3); U.itree[(U.ordr[i]<<1)+1] = l; if(l>tl)tl=l; } pos+=3*HCLEN; //console.log(itree);
580
+ makeCodes(U.itree, tl);
581
+ codes2map(U.itree, tl, U.imap);
582
+
583
+ lmap = U.lmap; dmap = U.dmap;
584
+
585
+ pos = decodeTiny(U.imap, (1<<tl)-1, HLIT+HDIST, data, pos, U.ttree);
586
+ let mx0 = F._copyOut(U.ttree, 0, HLIT , U.ltree); ML = (1<<mx0)-1;
587
+ let mx1 = F._copyOut(U.ttree, HLIT, HDIST, U.dtree); MD = (1<<mx1)-1;
588
+
589
+ //let ml = decodeTiny(U.imap, (1<<tl)-1, HLIT , data, pos, U.ltree); ML = (1<<(ml>>>24))-1; pos+=(ml&0xffffff);
590
+ makeCodes(U.ltree, mx0);
591
+ codes2map(U.ltree, mx0, lmap);
592
+
593
+ //let md = decodeTiny(U.imap, (1<<tl)-1, HDIST, data, pos, U.dtree); MD = (1<<(md>>>24))-1; pos+=(md&0xffffff);
594
+ makeCodes(U.dtree, mx1);
595
+ codes2map(U.dtree, mx1, dmap);
596
+ }
597
+ //let ooff=off, opos=pos;
598
+ while(true) {
599
+ let code = lmap[get17(data, pos) & ML]; pos += code&15;
600
+ let lit = code>>>4; //U.lhst[lit]++;
601
+ if((lit>>>8)==0) { buf[off++] = lit; }
602
+ else if(lit==256) { break; }
603
+ else {
604
+ let end = off+lit-254;
605
+ if(lit>264) { let ebs = U.ldef[lit-257]; end = off + (ebs>>>3) + bitsE(data, pos, ebs&7); pos += ebs&7; }
606
+ //UZIP.F.dst[end-off]++;
607
+
608
+ let dcode = dmap[get17(data, pos) & MD]; pos += dcode&15;
609
+ let dlit = dcode>>>4;
610
+ let dbs = U.ddef[dlit], dst = (dbs>>>4) + bitsF(data, pos, dbs&15); pos += dbs&15;
611
+
612
+ //let o0 = off-dst, stp = Math.min(end-off, dst);
613
+ //if(stp>20) while(off<end) { buf.copyWithin(off, o0, o0+stp); off+=stp; } else
614
+ //if(end-dst<=off) buf.copyWithin(off, off-dst, end-dst); else
615
+ //if(dst==1) buf.fill(buf[off-1], off, end); else
616
+ if(noBuf) buf=UZIP.F._check(buf, off+(1<<17));
617
+ while(off<end) { buf[off]=buf[off++-dst]; buf[off]=buf[off++-dst]; buf[off]=buf[off++-dst]; buf[off]=buf[off++-dst]; }
618
+ off=end;
619
+ //while(off!=end) { buf[off]=buf[off++-dst]; }
620
+ }
621
+ }
622
+ //console.log(off-ooff, (pos-opos)>>>3);
623
+ }
624
+ //console.log(UZIP.F.dst);
625
+ //console.log(tlen, dlen, off-tlen+tcnt);
626
+ return buf.length==off ? buf : buf.slice(0,off);
627
+ }
628
+ UZIP.F._check=function(buf, len) {
629
+ let bl=buf.length; if(len<=bl) return buf;
630
+ let nbuf = new Uint8Array(Math.max(bl<<1,len)); nbuf.set(buf,0);
631
+ //for(let i=0; i<bl; i+=4) { nbuf[i]=buf[i]; nbuf[i+1]=buf[i+1]; nbuf[i+2]=buf[i+2]; nbuf[i+3]=buf[i+3]; }
632
+ return nbuf;
633
+ }
634
+
635
+ UZIP.F._decodeTiny = function(lmap, LL, len, data, pos, tree) {
636
+ let bitsE = UZIP.F._bitsE, get17 = UZIP.F._get17;
637
+ let i = 0;
638
+ while(i<len) {
639
+ let code = lmap[get17(data, pos)&LL]; pos+=code&15;
640
+ let lit = code>>>4;
641
+ if(lit<=15) { tree[i]=lit; i++; }
642
+ else {
643
+ let ll = 0, n = 0;
644
+ if(lit==16) {
645
+ n = (3 + bitsE(data, pos, 2)); pos += 2; ll = tree[i-1];
646
+ }
647
+ else if(lit==17) {
648
+ n = (3 + bitsE(data, pos, 3)); pos += 3;
649
+ }
650
+ else if(lit==18) {
651
+ n = (11 + bitsE(data, pos, 7)); pos += 7;
652
+ }
653
+ let ni = i+n;
654
+ while(i<ni) { tree[i]=ll; i++; }
655
+ }
656
+ }
657
+ return pos;
658
+ }
659
+ UZIP.F._copyOut = function(src, off, len, tree) {
660
+ let mx=0, i=0, tl=tree.length>>>1;
661
+ while(i<len) { let v=src[i+off]; tree[(i<<1)]=0; tree[(i<<1)+1]=v; if(v>mx)mx=v; i++; }
662
+ while(i<tl ) { tree[(i<<1)]=0; tree[(i<<1)+1]=0; i++; }
663
+ return mx;
664
+ }
665
+
666
+ UZIP.F.makeCodes = function(tree, MAX_BITS) { // code, length
667
+ let U = UZIP.F.U;
668
+ let max_code = tree.length;
669
+ let code, bits, n, i, len;
670
+
671
+ let bl_count = U.bl_count; for(let i=0; i<=MAX_BITS; i++) bl_count[i]=0;
672
+ for(i=1; i<max_code; i+=2) bl_count[tree[i]]++;
673
+
674
+ let next_code = U.next_code; // smallest code for each length
675
+
676
+ code = 0;
677
+ bl_count[0] = 0;
678
+ for (bits = 1; bits <= MAX_BITS; bits++) {
679
+ code = (code + bl_count[bits-1]) << 1;
680
+ next_code[bits] = code;
681
+ }
682
+
683
+ for (n = 0; n < max_code; n+=2) {
684
+ len = tree[n+1];
685
+ if (len != 0) {
686
+ tree[n] = next_code[len];
687
+ next_code[len]++;
688
+ }
689
+ }
690
+ }
691
+ UZIP.F.codes2map = function(tree, MAX_BITS, map) {
692
+ let max_code = tree.length;
693
+ let U=UZIP.F.U, r15 = U.rev15;
694
+ for(let i=0; i<max_code; i+=2) if(tree[i+1]!=0) {
695
+ let lit = i>>1;
696
+ let cl = tree[i+1], val = (lit<<4)|cl; // : (0x8000 | (U.of0[lit-257]<<7) | (U.exb[lit-257]<<4) | cl);
697
+ let rest = (MAX_BITS-cl), i0 = tree[i]<<rest, i1 = i0 + (1<<rest);
698
+ //tree[i]=r15[i0]>>>(15-MAX_BITS);
699
+ while(i0!=i1) {
700
+ let p0 = r15[i0]>>>(15-MAX_BITS);
701
+ map[p0]=val; i0++;
702
+ }
703
+ }
704
+ }
705
+ UZIP.F.revCodes = function(tree, MAX_BITS) {
706
+ let r15 = UZIP.F.U.rev15, imb = 15-MAX_BITS;
707
+ for(let i=0; i<tree.length; i+=2) { let i0 = (tree[i]<<(MAX_BITS-tree[i+1])); tree[i] = r15[i0]>>>imb; }
708
+ }
709
+
710
+ // used only in deflate
711
+ UZIP.F._putsE= function(dt, pos, val ) { val = val<<(pos&7); let o=(pos>>>3); dt[o]|=val; dt[o+1]|=(val>>>8); }
712
+ UZIP.F._putsF= function(dt, pos, val ) { val = val<<(pos&7); let o=(pos>>>3); dt[o]|=val; dt[o+1]|=(val>>>8); dt[o+2]|=(val>>>16); }
713
+
714
+ UZIP.F._bitsE= function(dt, pos, length) { return ((dt[pos>>>3] | (dt[(pos>>>3)+1]<<8) )>>>(pos&7))&((1<<length)-1); }
715
+ UZIP.F._bitsF= function(dt, pos, length) { return ((dt[pos>>>3] | (dt[(pos>>>3)+1]<<8) | (dt[(pos>>>3)+2]<<16))>>>(pos&7))&((1<<length)-1); }
716
+ /*
717
+ UZIP.F._get9 = function(dt, pos) {
718
+ return ((dt[pos>>>3] | (dt[(pos>>>3)+1]<<8))>>>(pos&7))&511;
719
+ } */
720
+ UZIP.F._get17= function(dt, pos) { // return at least 17 meaningful bytes
721
+ return (dt[pos>>>3] | (dt[(pos>>>3)+1]<<8) | (dt[(pos>>>3)+2]<<16) )>>>(pos&7);
722
+ }
723
+ UZIP.F._get25= function(dt, pos) { // return at least 17 meaningful bytes
724
+ return (dt[pos>>>3] | (dt[(pos>>>3)+1]<<8) | (dt[(pos>>>3)+2]<<16) | (dt[(pos>>>3)+3]<<24) )>>>(pos&7);
725
+ }
726
+ UZIP.F.U = function(){
727
+ let u16=Uint16Array, u32=Uint32Array;
728
+ return {
729
+ next_code : new u16(16),
730
+ bl_count : new u16(16),
731
+ ordr : [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ],
732
+ of0 : [3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],
733
+ exb : [0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0],
734
+ ldef : new u16(32),
735
+ df0 : [1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 65535, 65535],
736
+ dxb : [0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0],
737
+ ddef : new u32(32),
738
+ flmap: new u16( 512), fltree: [],
739
+ fdmap: new u16( 32), fdtree: [],
740
+ lmap : new u16(32768), ltree : [], ttree:[],
741
+ dmap : new u16(32768), dtree : [],
742
+ imap : new u16( 512), itree : [],
743
+ //rev9 : new u16( 512)
744
+ rev15: new u16(1<<15),
745
+ lhst : new u32(286), dhst : new u32( 30), ihst : new u32(19),
746
+ lits : new u32(15000),
747
+ strt : new u16(1<<16),
748
+ prev : new u16(1<<15)
749
+ };
750
+ } ();
751
+
752
+ (function(){
753
+ let U = UZIP.F.U;
754
+ let len = 1<<15;
755
+ for(let i=0; i<len; i++) {
756
+ let x = i;
757
+ x = (((x & 0xaaaaaaaa) >>> 1) | ((x & 0x55555555) << 1));
758
+ x = (((x & 0xcccccccc) >>> 2) | ((x & 0x33333333) << 2));
759
+ x = (((x & 0xf0f0f0f0) >>> 4) | ((x & 0x0f0f0f0f) << 4));
760
+ x = (((x & 0xff00ff00) >>> 8) | ((x & 0x00ff00ff) << 8));
761
+ U.rev15[i] = (((x >>> 16) | (x << 16)))>>>17;
762
+ }
763
+
764
+ function pushV(tgt, n, sv) { while(n--!=0) tgt.push(0,sv); }
765
+
766
+ for(let i=0; i<32; i++) { U.ldef[i]=(U.of0[i]<<3)|U.exb[i]; U.ddef[i]=(U.df0[i]<<4)|U.dxb[i]; }
767
+
768
+ pushV(U.fltree, 144, 8); pushV(U.fltree, 255-143, 9); pushV(U.fltree, 279-255, 7); pushV(U.fltree,287-279,8);
769
+ /*
770
+ let i = 0;
771
+ for(; i<=143; i++) U.fltree.push(0,8);
772
+ for(; i<=255; i++) U.fltree.push(0,9);
773
+ for(; i<=279; i++) U.fltree.push(0,7);
774
+ for(; i<=287; i++) U.fltree.push(0,8);
775
+ */
776
+ UZIP.F.makeCodes(U.fltree, 9);
777
+ UZIP.F.codes2map(U.fltree, 9, U.flmap);
778
+ UZIP.F.revCodes (U.fltree, 9)
779
+
780
+ pushV(U.fdtree,32,5);
781
+ //for(i=0;i<32; i++) U.fdtree.push(0,5);
782
+ UZIP.F.makeCodes(U.fdtree, 5);
783
+ UZIP.F.codes2map(U.fdtree, 5, U.fdmap);
784
+ UZIP.F.revCodes (U.fdtree, 5)
785
+
786
+ pushV(U.itree,19,0); pushV(U.ltree,286,0); pushV(U.dtree,30,0); pushV(U.ttree,320,0);
787
+ /*
788
+ for(let i=0; i< 19; i++) U.itree.push(0,0);
789
+ for(let i=0; i<286; i++) U.ltree.push(0,0);
790
+ for(let i=0; i< 30; i++) U.dtree.push(0,0);
791
+ for(let i=0; i<320; i++) U.ttree.push(0,0);
792
+ */
793
+ })();
794
+
795
+
796
+
797
+ let started = false;
798
+ let cloudFrontURL = "";
799
+ let folderName = "";
800
+ let lastRequest = {};//{tc:"00_00_00_00", path:"..."}
801
+ let lastTimeCodeFound = null;//for statistics
802
+ let msecAvg = [];//for statistics
803
+ const delimiter = "_";
804
+ let maxTimeCode = {};//don't pass this timecode
805
+ let enabled = true;
806
+ let isWorking = false;
807
+
808
+ const HandleData = async (data) => {
809
+ const result = UZIP.parse(data);
810
+ let sortedKeys = Object.keys(result).sort();
811
+
812
+ for (const key of sortedKeys) {
813
+ if (key.endsWith(".json")) {
814
+ let transferableArray = [];
815
+ const metadata = JSON.parse(new TextDecoder().decode(result[key]));
816
+ const tc = metadata.timecode.replace(/:/g,"_");//replaceAll(":", "_");
817
+ if (metadata.placeholders) {
818
+ for (const groupId in metadata.placeholders) {
819
+ let alphaKey = \`\${tc}_\${groupId}.alpha\`;
820
+ if (result[alphaKey]) {
821
+ metadata.placeholders[groupId].alphaAsUint8Array = result[alphaKey];//new Uint8Array(result[alphaKey].buffer);
822
+ transferableArray.push(result[alphaKey].buffer);
823
+ }
824
+ }
825
+ }
826
+ postMessage(metadata,transferableArray);
827
+ }
828
+ }
829
+ }
830
+
831
+ const Str2TimeCode = (str) => {
832
+ const list = str.split(delimiter);
833
+ const timecode = {h: parseInt(list[0]), m: parseInt(list[1]), s: parseInt(list[2]), f: parseInt(list[3])};
834
+ return timecode;
835
+ }
836
+ const TimeCode2Str = (timecode) => {
837
+ return ("0" + timecode.h).slice(-2) + delimiter + ("0" + timecode.m).slice(-2) + delimiter + ("0" + timecode.s).slice(-2) + delimiter + ("0" + timecode.f).slice(-2);
838
+ }
839
+ const AddSecondsToTimeCode = (timecode, seconds) => {
840
+ let ret = {...timecode};//cloning araay
841
+ ret.s += seconds;
842
+ if (ret.s >= 60) {
843
+ ret.s = 0;
844
+ ret.m++;
845
+ if (ret.m >= 60) {
846
+ ret.m = 0;
847
+ ret.h++;
848
+ }
849
+ }
850
+ return ret;
851
+ }
852
+ const GetNextTimeCode_TC = (tc, seconds = 0) => {
853
+ let timecode = Str2TimeCode(tc);
854
+ if (seconds) timecode = AddSecondsToTimeCode(timecode, seconds);
855
+ return timecode;
856
+ }
857
+ const GetNextTimeCode = (tc, seconds = 0) => {
858
+ let timecode = GetNextTimeCode_TC(tc,seconds);
859
+ return TimeCode2Str(timecode);
860
+ }
861
+
862
+
863
+ const IsTCBigger = (tc1, tc2) => {
864
+ //return true iif tc1 > tc2
865
+ if (tc1.h > tc2.h) return true;
866
+ else if (tc1.h < tc2.h) return false;
867
+ else if (tc1.m > tc2.m) return true;
868
+ else if (tc1.m < tc2.m) return false;
869
+ else if (tc1.s > tc2.s) return true;
870
+ else if (tc1.s < tc2.s) return false;
871
+ else if (tc1.f > tc2.f) return true;
872
+ else return false;
873
+ }
874
+ const TryNextFrame = (seconds) => {
875
+ if(!enabled) return;
876
+ //if the required time code exceed the max time code, wait a bit
877
+ if (seconds > 0 && IsTCBigger(GetNextTimeCode_TC(lastRequest.tc, seconds), maxTimeCode)) {
878
+ //console.log("the current TC ", GetNextTimeCode_TC(lastRequest.tc, seconds), " is bigger than the max time code: ", maxTimeCode);//@oror
879
+ setTimeout(() => {
880
+ TryNextFrame(seconds);
881
+ }, 100);
882
+ }
883
+ else
884
+ {
885
+ //console.log("We're good to take the next timecode");//@oror
886
+ setTimeout(() => {
887
+ lastRequest.tc = GetNextTimeCode(lastRequest.tc, seconds);
888
+ //console.log("next time code: ", lastRequest.tc);//@oror
889
+ GetMetaData();
890
+ }, 0);
891
+ }
892
+ }
893
+
894
+ const AvgMetaDataMsec = () => {
895
+ let sum = 0;
896
+ for (let i = 0; i < msecAvg.length; i++) {
897
+ sum += msecAvg[i];
898
+ }
899
+ return sum / msecAvg.length;
900
+ };
901
+ const GetMetaData = async () => {
902
+ isWorking = true;
903
+ const item = lastRequest;
904
+ const url = \`\${item.path}/\${item.tc}.zip\`;
905
+ try {
906
+ const t1 = performance.now();
907
+ const res = await fetch(url);
908
+ if (res.status >= 400) {
909
+ //console.log(\`\${item.tc} time code not found, url is \${url}\`);
910
+ let now = performance.now();
911
+ let msecSinceLastTimeDate = now - lastTimeCodeFound;
912
+ let secSinceLastTimeDate = (msecSinceLastTimeDate/1000);
913
+ if (secSinceLastTimeDate > 4) {
914
+ console.error("Didn't find timecode ", item.tc, ", now for " + secSinceLastTimeDate.toFixed(2) + " seconds - give up !");
915
+ TryNextFrame(2);//try the same time code again
916
+ }
917
+ else {
918
+ console.error("Didn't find timecode ", item.tc, ", now for " + secSinceLastTimeDate.toFixed(2) + " seconds");
919
+ TryNextFrame(0);//try the same time code again
920
+ }
921
+ isWorking = false;
922
+ return;
923
+ }
924
+ let now = performance.now();
925
+ let msecSinceLastTimeDate = 0;
926
+ if (lastTimeCodeFound) {
927
+ msecSinceLastTimeDate = now - lastTimeCodeFound;
928
+ msecAvg.push(msecSinceLastTimeDate);
929
+ while (msecAvg.length > 1000) msecAvg.shift();
930
+ }
931
+ lastTimeCodeFound = now;
932
+
933
+ //console.log(\`\${item.tc} time code found, (\${msecSinceLastTimeDate} msec from last time, avg: \${AvgMetaDataMsec()})\`);
934
+
935
+ const data = await res.arrayBuffer();
936
+ await HandleData(data);
937
+ const t2 = performance.now();
938
+ //postMessage({time: \`Fetch and Decompress: \${t2 - t1} msec\`});
939
+ } catch (err) {
940
+ console.error(\`Error on url \${url}\`, err);
941
+ }
942
+ isWorking = false;
943
+ TryNextFrame(2);
944
+ }
945
+
946
+ addEventListener("message", (request) => {
947
+ if (!started && request.data.cloudFrontURL) {
948
+ started = true;
949
+ cloudFrontURL = request.data.cloudFrontURL;
950
+ folderName = request.data.folderName;
951
+ let tcReplaced = request.data.tc.replace(/:/g,"_");//replaceAll(":", "_")
952
+ lastRequest = {tc: tcReplaced, path: \`\${cloudFrontURL}/\${folderName}\`};//from the lastReqest object
953
+ let timeCode = Str2TimeCode(lastRequest.tc);//parse the time code string into a real time-code
954
+ maxTimeCode = AddSecondsToTimeCode(Object.assign({}, timeCode), 4);//set the max limit (on a clone)
955
+ //set the timecode needed
956
+ timeCode.f = 0;
957
+ timeCode.s = timeCode.s % 2 === 0 ? timeCode.s : timeCode.s - 1;
958
+ lastRequest.tc = TimeCode2Str(timeCode);
959
+ console.log("Meta-data worker starting from this time-code: ", lastRequest.tc);//@oror
960
+ GetMetaData();
961
+ }
962
+ else {
963
+ let tcReplaced = request.data.tc.replace(/:/g,"_");
964
+ //console.log("metaDataDownloader new timecode = " + tcReplaced);
965
+ let timeCode = Str2TimeCode(tcReplaced);
966
+ timeCode.f = 0;
967
+ timeCode.s = timeCode.s % 2 === 0 ? timeCode.s : timeCode.s - 1;
968
+ if (IsTCBigger(timeCode, maxTimeCode))
969
+ {
970
+ lastRequest.tc = TimeCode2Str(timeCode);
971
+ console.log("Meta-data worker jumps forward to this time-code: ", lastRequest.tc);
972
+ }
973
+ if (IsTCBigger(Str2TimeCode(lastRequest.tc), AddSecondsToTimeCode(timeCode,4)))
974
+ {
975
+ lastRequest.tc = TimeCode2Str(timeCode);
976
+ console.log("Meta-data worker jumps back to this time-code: ", lastRequest.tc);
977
+ }
978
+ maxTimeCode = AddSecondsToTimeCode(timeCode, 4);
979
+ if(!enabled && request.data.enabled)
980
+ {
981
+ enabled = request.data.enabled;
982
+ if(!isWorking) GetMetaData();
983
+ }
984
+ else {
985
+ enabled = request.data.enabled;
986
+ }
987
+ //console.log("max time code: ", maxTimeCode);
988
+ }
989
+ });
990
+
991
+ } catch (e){
992
+ console.error(e);
993
+ }`;
994
+
995
+ /*
996
+ * Copyright 2021, GFXFundamentals.
997
+ * All rights reserved.
998
+ *
999
+ * Redistribution and use in source and binary forms, with or without
1000
+ * modification, are permitted provided that the following conditions are
1001
+ * met:
1002
+ *
1003
+ * * Redistributions of source code must retain the above copyright
1004
+ * notice, this list of conditions and the following disclaimer.
1005
+ * * Redistributions in binary form must reproduce the above
1006
+ * copyright notice, this list of conditions and the following disclaimer
1007
+ * in the documentation and/or other materials provided with the
1008
+ * distribution.
1009
+ * * Neither the name of GFXFundamentals. nor the names of his
1010
+ * contributors may be used to endorse or promote products derived from
1011
+ * this software without specific prior written permission.
1012
+ *
1013
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1014
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1015
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1016
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1017
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1018
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1019
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1020
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1021
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1022
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1023
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1024
+ */
1025
+
1026
+
1027
+ let debug_lastFrameFromVideoCallback=-1;
1028
+ let debug_tc = "";
1029
+
1030
+ /* global define */
1031
+ (function(root, factory) { // eslint-disable-line
1032
+ if (typeof define === 'function' && define.amd) {
1033
+ // AMD. Register as an anonymous module.
1034
+ define([], function() {
1035
+ return factory.call(root);
1036
+ });
1037
+ } else {
1038
+ // Browser globals
1039
+ root.webglUtils = factory.call(root);
1040
+ }
1041
+ }(this, function() {
1042
+ "use strict";
1043
+
1044
+ const topWindow = this;
1045
+
1046
+ /** @module webgl-utils */
1047
+
1048
+ function isInIFrame(w) {
1049
+ w = w || topWindow;
1050
+ return w !== w.top;
1051
+ }
1052
+
1053
+ if (!isInIFrame()) {
1054
+ console.log("%c%s", 'color:blue;font-weight:bold;', 'for more about webgl-utils.js see:'); // eslint-disable-line
1055
+ console.log("%c%s", 'color:blue;font-weight:bold;', 'http://webgl2fundamentals.org/webgl/lessons/webgl-boilerplate.html'); // eslint-disable-line
1056
+ }
1057
+
1058
+ /**
1059
+ * Wrapped logging function.
1060
+ * @param {string} msg The message to log.
1061
+ */
1062
+ function error(msg) {
1063
+ if (topWindow.console) {
1064
+ if (topWindow.console.error) {
1065
+ topWindow.console.error(msg);
1066
+ } else if (topWindow.console.log) {
1067
+ topWindow.console.log(msg);
1068
+ }
1069
+ }
1070
+ }
1071
+
1072
+ const errorRE = /ERROR:\s*\d+:(\d+)/gi;
1073
+ function addLineNumbersWithError(src, log = '') {
1074
+ // Note: Error message formats are not defined by any spec so this may or may not work.
1075
+ const matches = [...log.matchAll(errorRE)];
1076
+ const lineNoToErrorMap = new Map(matches.map((m, ndx) => {
1077
+ const lineNo = parseInt(m[1]);
1078
+ const next = matches[ndx + 1];
1079
+ const end = next ? next.index : log.length;
1080
+ const msg = log.substring(m.index, end);
1081
+ return [lineNo - 1, msg];
1082
+ }));
1083
+ return src.split('\n').map((line, lineNo) => {
1084
+ const err = lineNoToErrorMap.get(lineNo);
1085
+ return `${lineNo + 1}: ${line}${err ? `\n\n^^^ ${err}` : ''}`;
1086
+ }).join('\n');
1087
+ }
1088
+
1089
+
1090
+ /**
1091
+ * Error Callback
1092
+ * @callback ErrorCallback
1093
+ * @param {string} msg error message.
1094
+ * @memberOf module:webgl-utils
1095
+ */
1096
+
1097
+ /**
1098
+ * Loads a shader.
1099
+ * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
1100
+ * @param {string} shaderSource The shader source.
1101
+ * @param {number} shaderType The type of shader.
1102
+ * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors.
1103
+ * @return {WebGLShader} The created shader.
1104
+ */
1105
+ function loadShader(gl, shaderSource, shaderType, opt_errorCallback) {
1106
+ const errFn = opt_errorCallback || error;
1107
+ // Create the shader object
1108
+ const shader = gl.createShader(shaderType);
1109
+
1110
+ // Load the shader source
1111
+ gl.shaderSource(shader, shaderSource);
1112
+
1113
+ // Compile the shader
1114
+ gl.compileShader(shader);
1115
+
1116
+ // Check the compile status
1117
+ const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
1118
+ if (!compiled) {
1119
+ // Something went wrong during compilation; get the error
1120
+ const lastError = gl.getShaderInfoLog(shader);
1121
+ errFn(`Error compiling shader: ${lastError}\n${addLineNumbersWithError(shaderSource, lastError)}`);
1122
+ gl.deleteShader(shader);
1123
+ return null;
1124
+ }
1125
+
1126
+ return shader;
1127
+ }
1128
+
1129
+ /**
1130
+ * Creates a program, attaches shaders, binds attrib locations, links the
1131
+ * program and calls useProgram.
1132
+ * @param {WebGLShader[]} shaders The shaders to attach
1133
+ * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
1134
+ * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
1135
+ * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
1136
+ * on error. If you want something else pass an callback. It's passed an error message.
1137
+ * @memberOf module:webgl-utils
1138
+ */
1139
+ function createProgram(
1140
+ gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
1141
+ const errFn = opt_errorCallback || error;
1142
+ const program = gl.createProgram();
1143
+ shaders.forEach(function(shader) {
1144
+ gl.attachShader(program, shader);
1145
+ });
1146
+ if (opt_attribs) {
1147
+ opt_attribs.forEach(function(attrib, ndx) {
1148
+ gl.bindAttribLocation(
1149
+ program,
1150
+ opt_locations ? opt_locations[ndx] : ndx,
1151
+ attrib);
1152
+ });
1153
+ }
1154
+ gl.linkProgram(program);
1155
+
1156
+ // Check the link status
1157
+ const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
1158
+ if (!linked) {
1159
+ // something went wrong with the link
1160
+ const lastError = gl.getProgramInfoLog(program);
1161
+ errFn(`Error in program linking: ${lastError}\n${
1162
+ shaders.map(shader => {
1163
+ const src = addLineNumbersWithError(gl.getShaderSource(shader));
1164
+ const type = gl.getShaderParameter(shader, gl.SHADER_TYPE);
1165
+ return `${glEnumToString(gl, type)}:\n${src}`;
1166
+ }).join('\n')
1167
+ }`);
1168
+
1169
+ gl.deleteProgram(program);
1170
+ return null;
1171
+ }
1172
+ return program;
1173
+ }
1174
+
1175
+ /**
1176
+ * Loads a shader from a script tag.
1177
+ * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
1178
+ * @param {string} scriptId The id of the script tag.
1179
+ * @param {number} opt_shaderType The type of shader. If not passed in it will
1180
+ * be derived from the type of the script tag.
1181
+ * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors.
1182
+ * @return {WebGLShader} The created shader.
1183
+ */
1184
+ function createShaderFromScript(
1185
+ gl, scriptId, opt_shaderType, opt_errorCallback) {
1186
+ let shaderSource = "";
1187
+ let shaderType;
1188
+ const shaderScript = document.getElementById(scriptId);
1189
+ if (!shaderScript) {
1190
+ throw ("*** Error: unknown script element" + scriptId);
1191
+ }
1192
+ shaderSource = shaderScript.text;
1193
+
1194
+ if (!opt_shaderType) {
1195
+ if (shaderScript.type === "x-shader/x-vertex") {
1196
+ shaderType = gl.VERTEX_SHADER;
1197
+ } else if (shaderScript.type === "x-shader/x-fragment") {
1198
+ shaderType = gl.FRAGMENT_SHADER;
1199
+ } else if (shaderType !== gl.VERTEX_SHADER && shaderType !== gl.FRAGMENT_SHADER) {
1200
+ throw ("*** Error: unknown shader type");
1201
+ }
1202
+ }
1203
+
1204
+ return loadShader(
1205
+ gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
1206
+ opt_errorCallback);
1207
+ }
1208
+
1209
+ const defaultShaderType = [
1210
+ "VERTEX_SHADER",
1211
+ "FRAGMENT_SHADER",
1212
+ ];
1213
+
1214
+ /**
1215
+ * Creates a program from 2 script tags.
1216
+ *
1217
+ * @param {WebGLRenderingContext} gl The WebGLRenderingContext
1218
+ * to use.
1219
+ * @param {string[]} shaderScriptIds Array of ids of the script
1220
+ * tags for the shaders. The first is assumed to be the
1221
+ * vertex shader, the second the fragment shader.
1222
+ * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
1223
+ * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
1224
+ * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
1225
+ * on error. If you want something else pass an callback. It's passed an error message.
1226
+ * @return {WebGLProgram} The created program.
1227
+ * @memberOf module:webgl-utils
1228
+ */
1229
+ function createProgramFromScripts(
1230
+ gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) {
1231
+ const shaders = [];
1232
+ for (let ii = 0; ii < shaderScriptIds.length; ++ii) {
1233
+ shaders.push(createShaderFromScript(
1234
+ gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], opt_errorCallback));
1235
+ }
1236
+ return createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
1237
+ }
1238
+
1239
+ /**
1240
+ * Creates a program from 2 sources.
1241
+ *
1242
+ * @param {WebGLRenderingContext} gl The WebGLRenderingContext
1243
+ * to use.
1244
+ * @param {string[]} shaderSourcess Array of sources for the
1245
+ * shaders. The first is assumed to be the vertex shader,
1246
+ * the second the fragment shader.
1247
+ * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
1248
+ * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
1249
+ * @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
1250
+ * on error. If you want something else pass an callback. It's passed an error message.
1251
+ * @return {WebGLProgram} The created program.
1252
+ * @memberOf module:webgl-utils
1253
+ */
1254
+ function createProgramFromSources(
1255
+ gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
1256
+ const shaders = [];
1257
+ for (let ii = 0; ii < shaderSources.length; ++ii) {
1258
+ shaders.push(loadShader(
1259
+ gl, shaderSources[ii], gl[defaultShaderType[ii]], opt_errorCallback));
1260
+ }
1261
+ return createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
1262
+ }
1263
+
1264
+ /**
1265
+ * Resize a canvas to match the size its displayed.
1266
+ * @param {HTMLCanvasElement} canvas The canvas to resize.
1267
+ * @param {number} [multiplier] amount to multiply by.
1268
+ * Pass in window.devicePixelRatio for native pixels.
1269
+ * @return {boolean} true if the canvas was resized.
1270
+ * @memberOf module:webgl-utils
1271
+ */
1272
+ function resizeCanvasToDisplaySize(canvas, multiplier) {
1273
+ multiplier = multiplier || 1;
1274
+ const width = canvas.clientWidth * multiplier | 0;
1275
+ const height = canvas.clientHeight * multiplier | 0;
1276
+ if (canvas.width !== width || canvas.height !== height) {
1277
+ canvas.width = width;
1278
+ canvas.height = height;
1279
+ return true;
1280
+ }
1281
+ return false;
1282
+ }
1283
+
1284
+ return {
1285
+ createProgram: createProgram,
1286
+ createProgramFromScripts: createProgramFromScripts,
1287
+ createProgramFromSources: createProgramFromSources,
1288
+ resizeCanvasToDisplaySize: resizeCanvasToDisplaySize,
1289
+ };
1290
+
1291
+ }));
1292
+ const BB = {MAX_LOGOS:4};
1293
+ BB.GL = {
1294
+
1295
+ vertexShaderSource : `#version 300 es
1296
+
1297
+ // an attribute is an input (in) to a vertex shader.
1298
+ // It will receive vertex data from the buffer
1299
+ in vec2 a_position;
1300
+
1301
+ //Identity matrix for now
1302
+ uniform mat3 u_matrix;
1303
+
1304
+ //The canvas resolution, to covert to ZeTor
1305
+ uniform vec2 u_resolution;
1306
+
1307
+ //a_texCoord->v_texCoord interpolation
1308
+ //in vec2 a_texCoord;
1309
+ //out vec2 v_texCoord;
1310
+
1311
+ // Main vertex shader
1312
+ void main() {
1313
+ //Translate, rotate, scale by u_matrix:
1314
+ vec2 position = (u_matrix * vec3(a_position, 1)).xy;
1315
+
1316
+ //Convert the position from pixels to ZeTor [0.0,1.0]
1317
+ vec2 zeroToOne = position / u_resolution;
1318
+
1319
+ //Convert from [0,1] to [0,2]
1320
+ vec2 zeroToTwo = zeroToOne * 2.0;
1321
+
1322
+ //Convert from [0,2] to [-1,+1] (clipspace)
1323
+ vec2 clipSpace = zeroToTwo - 1.0;
1324
+
1325
+ gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
1326
+
1327
+ //Pass the texCoord to the fragment shader
1328
+ //The GPU will interpolate this value between points.
1329
+ //v_texCoord = a_texCoord;
1330
+ }
1331
+ `,
1332
+
1333
+ fragmentShaderSource : `#version 300 es
1334
+
1335
+ //High precision mode
1336
+ precision highp float;
1337
+
1338
+ //The textures
1339
+ //uniform sampler2D u_image;//video frame's texture
1340
+ uniform sampler2D ad0;//the ad texture
1341
+ uniform sampler2D ad1;//the ad texture
1342
+ uniform sampler2D ad2;//the ad texture
1343
+ uniform sampler2D ad3;//the ad texture
1344
+
1345
+ uniform vec2 u_resolution2;//resolution of the canvas
1346
+
1347
+ //v_texCoord from the vertex shader
1348
+ //in vec2 v_texCoord;
1349
+
1350
+ //Logos' staff
1351
+ uniform int hasLogo0;//has logo ?
1352
+ uniform int adForLogo0;
1353
+ uniform sampler2D alpha0;//the alpha's mate
1354
+ uniform mat3 u_matrix0;//the reverse homography
1355
+ uniform vec2 u_alpha0TopLeft;//the alpha mate's top-left
1356
+ uniform vec2 u_alpha0Size;//the alpha mate's size
1357
+
1358
+ uniform int hasLogo1;//has logo ?
1359
+ uniform int adForLogo1;
1360
+ uniform sampler2D alpha1;//the alpha's mate
1361
+ uniform mat3 u_matrix1;//the reverse homography
1362
+ uniform vec2 u_alpha1TopLeft;//the alpha mate's top-left
1363
+ uniform vec2 u_alpha1Size;//the alpha mate's size
1364
+
1365
+ uniform int hasLogo2;//has logo ?
1366
+ uniform int adForLogo2;
1367
+ uniform sampler2D alpha2;//the alpha's mate
1368
+ uniform mat3 u_matrix2;//the reverse homography
1369
+ uniform vec2 u_alpha2TopLeft;//the alpha mate's top-left
1370
+ uniform vec2 u_alpha2Size;//the alpha mate's size
1371
+
1372
+ uniform int hasLogo3;//has logo ?
1373
+ uniform int adForLogo3;
1374
+ uniform sampler2D alpha3;//the alpha's mate
1375
+ uniform mat3 u_matrix3;//the reverse homography
1376
+ uniform vec2 u_alpha3TopLeft;//the alpha mate's top-left
1377
+ uniform vec2 u_alpha3Size;//the alpha mate's size
1378
+
1379
+ uniform int hasDigit;//has debug digit texture
1380
+ uniform sampler2D digitTex;//the digits's mate
1381
+ uniform vec2 u_digitTopLeft;//the digit mate's top-left
1382
+ uniform vec2 u_digitTopLeft2;//the digit mate's top-left
1383
+ uniform vec2 u_digitSize;//the digit mate's size
1384
+
1385
+ //Output
1386
+ out vec4 outColor;
1387
+ bool BlendDigit(in vec2 curCoord) {
1388
+ //vec2 texCoord = curCoord;
1389
+ //texCoord /= u_resolution2;
1390
+ vec2 digitCoord = (curCoord - u_digitTopLeft)/u_digitSize;//get the coordinate on the digit size
1391
+ if (digitCoord.x >= 0.0 && digitCoord.y >= 0.0 && digitCoord.x <= 1.0 && digitCoord.y <= 1.0)
1392
+ {
1393
+ outColor = texture(digitTex, digitCoord);
1394
+ return true;
1395
+ }
1396
+ return false;
1397
+ }
1398
+ bool BlendDigit2(in vec2 curCoord) {
1399
+ //vec2 texCoord = curCoord;
1400
+ //texCoord /= u_resolution2;
1401
+ vec2 digitCoord = (curCoord - u_digitTopLeft2)/u_digitSize;//get the coordinate on the digit size
1402
+ if (digitCoord.x >= 0.0 && digitCoord.y >= 0.0 && digitCoord.x <= 1.0 && digitCoord.y <= 1.0)
1403
+ {
1404
+ outColor = texture(digitTex, digitCoord);
1405
+ return true;
1406
+ }
1407
+ return false;
1408
+ }
1409
+ bool BlendWithLogo(
1410
+ in int adUnit,
1411
+ in sampler2D alphaTexture,
1412
+ in mat3 matrix,
1413
+ in vec2 curCoord,
1414
+ in vec2 alphaTopLeft,
1415
+ in vec2 alphaSize)
1416
+ {
1417
+
1418
+
1419
+
1420
+ vec2 texCoord;
1421
+ texCoord.x = (matrix[0][0]*curCoord.x + matrix[0][1]*curCoord.y + matrix[0][2]) / (matrix[2][0]*curCoord.x + matrix[2][1]*curCoord.y + 1.0);
1422
+ texCoord.y = (matrix[1][0]*curCoord.x + matrix[1][1]*curCoord.y + matrix[1][2]) / (matrix[2][0]*curCoord.x + matrix[2][1]*curCoord.y + 1.0);
1423
+ texCoord /= u_resolution2;
1424
+
1425
+ //if we're in the bounding rect of the logo
1426
+ if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x <= 1.0 && texCoord.y <= 1.0)
1427
+ {
1428
+
1429
+
1430
+ float alpha = 1.0;
1431
+ vec2 alphaCoord = (curCoord - alphaTopLeft)/alphaSize;//get the coordinate on the alpha mate
1432
+ if (
1433
+ alphaCoord.x >= 0.0 &&
1434
+ alphaCoord.y >= 0.0 &&
1435
+ alphaCoord.x <= 1.0 &&
1436
+ alphaCoord.y <= 1.0
1437
+ )
1438
+ {
1439
+ //get the alpha texel:
1440
+ alpha *= texture(alphaTexture, alphaCoord).a;//without blur
1441
+ }
1442
+ vec4 ad0Pix =
1443
+ adUnit == 0 ? texture(ad0, texCoord) :
1444
+ adUnit == 1 ? texture(ad1, texCoord) :
1445
+ adUnit == 2 ? texture(ad2, texCoord) :
1446
+ texture(ad3, texCoord);//get the logo's texel
1447
+ alpha *= ad0Pix.a;//consider the alpha of the logo
1448
+ vec3 resultRGB = ad0Pix.rgb;// + videoImagePix.rgb*(1.0-alpha);//blend
1449
+ if (alpha == 0.0) resultRGB = vec3(0,0,0);
1450
+ outColor = vec4(resultRGB, alpha);
1451
+ return true;
1452
+ }
1453
+ else return false;
1454
+
1455
+ }
1456
+ void main()
1457
+ {
1458
+ //calculate cur-coord (with pixels units)
1459
+ vec2 curCoord = gl_FragCoord.xy;
1460
+ curCoord.y = u_resolution2[1] - curCoord.y;
1461
+ //if (hasDigit == 1 && (BlendDigit(curCoord) || BlendDigit2(curCoord))) return;
1462
+ if (hasDigit == 1 && (BlendDigit(curCoord))) return;
1463
+ else if (hasLogo0 == 1 &&
1464
+ BlendWithLogo(adForLogo0,
1465
+ alpha0, u_matrix0, curCoord,u_alpha0TopLeft, u_alpha0Size))
1466
+ {
1467
+ return;
1468
+ }
1469
+ else if (hasLogo1 == 1 &&
1470
+ BlendWithLogo(adForLogo1,
1471
+ alpha1, u_matrix1, curCoord,u_alpha1TopLeft, u_alpha1Size))
1472
+ {
1473
+ return;
1474
+ }
1475
+ else if (hasLogo2 == 1 &&
1476
+ BlendWithLogo(adForLogo2,
1477
+ alpha2, u_matrix2, curCoord,u_alpha2TopLeft, u_alpha2Size))
1478
+ {
1479
+ return;
1480
+ }
1481
+ else if (hasLogo3 == 1 &&
1482
+ BlendWithLogo(adForLogo3,
1483
+ alpha3, u_matrix3, curCoord,u_alpha3TopLeft, u_alpha3Size))
1484
+ {
1485
+ return;
1486
+ }
1487
+ else {//no logo
1488
+ outColor = vec4(0,0,0,0);
1489
+ }
1490
+ }
1491
+ `,
1492
+
1493
+ vertexShaderSourceV1 : `
1494
+
1495
+ //High precision mode
1496
+ precision highp float;
1497
+
1498
+ // an attribute is an input (in) to a vertex shader.
1499
+ // It will receive vertex data from the buffer
1500
+ attribute vec2 a_position;
1501
+ attribute vec2 a_texCoord;
1502
+ uniform vec2 u_resolution;
1503
+ varying vec2 v_texCoord;
1504
+
1505
+
1506
+ // Main vertex shader
1507
+ void main() {
1508
+ // convert the rectangle from pixels to 0.0 to 1.0
1509
+ vec2 zeroToOne = a_position / u_resolution;
1510
+
1511
+ // convert from 0->1 to 0->2
1512
+ vec2 zeroToTwo = zeroToOne * 2.0;
1513
+
1514
+ // convert from 0->2 to -1->+1 (clipspace)
1515
+ vec2 clipSpace = zeroToTwo - 1.0;
1516
+
1517
+ gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
1518
+
1519
+ // pass the texCoord to the fragment shader
1520
+ // The GPU will interpolate this value between points.
1521
+ v_texCoord = a_texCoord;
1522
+ }
1523
+ `,
1524
+
1525
+ fragmentShaderSourceV1 : `
1526
+
1527
+ //High precision mode
1528
+ precision highp float;
1529
+
1530
+ //The textures
1531
+ uniform sampler2D u_image;//video frame's texture2D
1532
+ uniform sampler2D ad0;//the ad texture2D
1533
+ uniform sampler2D ad1;//the ad texture2D
1534
+ uniform sampler2D ad2;//the ad texture2D
1535
+ uniform sampler2D ad3;//the ad texture2D
1536
+
1537
+ uniform vec2 u_resolution2;//resolution of the canvas
1538
+
1539
+ //v_texCoord from the vertex shader
1540
+ varying vec2 v_texCoord;
1541
+
1542
+ //Logos' staff
1543
+ uniform int hasLogo0;//has logo ?
1544
+ uniform int adForLogo0;
1545
+ uniform sampler2D alpha0;//the alpha's mate
1546
+ uniform mat3 u_matrix0;//the reverse homography
1547
+ uniform vec2 u_alpha0TopLeft;//the alpha mate's top-left
1548
+ uniform vec2 u_alpha0Size;//the alpha mate's size
1549
+
1550
+ uniform int hasLogo1;//has logo ?
1551
+ uniform int adForLogo1;
1552
+ uniform sampler2D alpha1;//the alpha's mate
1553
+ uniform mat3 u_matrix1;//the reverse homography
1554
+ uniform vec2 u_alpha1TopLeft;//the alpha mate's top-left
1555
+ uniform vec2 u_alpha1Size;//the alpha mate's size
1556
+
1557
+ uniform int hasLogo2;//has logo ?
1558
+ uniform int adForLogo2;
1559
+ uniform sampler2D alpha2;//the alpha's mate
1560
+ uniform mat3 u_matrix2;//the reverse homography
1561
+ uniform vec2 u_alpha2TopLeft;//the alpha mate's top-left
1562
+ uniform vec2 u_alpha2Size;//the alpha mate's size
1563
+
1564
+ uniform int hasLogo3;//has logo ?
1565
+ uniform int adForLogo3;
1566
+ uniform sampler2D alpha3;//the alpha's mate
1567
+ uniform mat3 u_matrix3;//the reverse homography
1568
+ uniform vec2 u_alpha3TopLeft;//the alpha mate's top-left
1569
+ uniform vec2 u_alpha3Size;//the alpha mate's size
1570
+
1571
+ uniform sampler2D injectoLogo;//the injecto logo to cover the timecode watermark
1572
+
1573
+ const float Directions = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower)
1574
+ const float Quality = 3.0; // BLUR QUALITY (Default 3.0 - More is better but slower)
1575
+ const float Size = 12.0; // BLUR SIZE (Radius)
1576
+ const float Pi = 6.28318530718; // Pi*2
1577
+
1578
+ vec3 BlurBGR(vec4 videoImagePix)
1579
+ {
1580
+ vec3 res = videoImagePix.rgb;
1581
+ //get the kernel radius:
1582
+ vec2 Radius = Size/u_resolution2.xy;
1583
+
1584
+ // Blur around:
1585
+ for( float d=0.0; d<Pi; d+=Pi/Directions)
1586
+ {
1587
+ for(float i=1.0/Quality; i<=1.0; i+=1.0/Quality)
1588
+ {
1589
+ res += texture2D( u_image, v_texCoord+vec2(cos(d),sin(d))*Radius*i).rgb;
1590
+ }
1591
+ }
1592
+ res /= Quality * Directions - 15.0;
1593
+
1594
+ return res;
1595
+ }
1596
+
1597
+ bool BlendWithLogo(
1598
+ int adUnit,
1599
+ sampler2D alphaTexture,
1600
+ mat3 matrix,
1601
+ vec2 curCoord,
1602
+ vec2 alphaTopLeft,
1603
+ vec2 alphaSize,
1604
+ vec4 videoImagePix)
1605
+ {
1606
+ vec2 texCoord;
1607
+ texCoord.x = (matrix[0][0]*curCoord.x + matrix[0][1]*curCoord.y + matrix[0][2]) / (matrix[2][0]*curCoord.x + matrix[2][1]*curCoord.y + 1.0);
1608
+ texCoord.y = (matrix[1][0]*curCoord.x + matrix[1][1]*curCoord.y + matrix[1][2]) / (matrix[2][0]*curCoord.x + matrix[2][1]*curCoord.y + 1.0);
1609
+ texCoord /= u_resolution2;
1610
+
1611
+ //if we're in the bounding rect of the logo
1612
+ if (texCoord.x >= 0.0 && texCoord.y >= 0.0 && texCoord.x <= 1.0 && texCoord.y <= 1.0)
1613
+ {
1614
+
1615
+
1616
+ float alpha = 1.0;
1617
+ vec2 alphaCoord = (curCoord - alphaTopLeft)/alphaSize;//get the coordinate on the alpha mate
1618
+ if (alphaCoord.x >= 0.0 && alphaCoord.y >= 0.0 && alphaCoord.x <= 1.0 && alphaCoord.y <= 1.0)
1619
+ {
1620
+ //get the alpha texel:
1621
+ //alpha *= BlurAlpha(alphaCoord);//blurred
1622
+ alpha *= texture2D(alphaTexture, alphaCoord).a;//without blur
1623
+ }
1624
+ if (adUnit == 99)//blur
1625
+ {
1626
+ gl_FragColor = vec4(BlurBGR(videoImagePix)*alpha + videoImagePix.rgb*(1.0-alpha), 1.0);
1627
+ //outColor = vec4(BlurBGR(videoImagePix), 1.0);
1628
+ return true;
1629
+ }
1630
+ vec4 ad0Pix =
1631
+ adUnit == 0 ? texture2D(ad0, texCoord) :
1632
+ adUnit == 1 ? texture2D(ad1, texCoord) :
1633
+ adUnit == 2 ? texture2D(ad2, texCoord) :
1634
+ texture2D(ad3, texCoord);//get the logo's texel
1635
+ alpha *= ad0Pix.a;//consider the alpha of the logo
1636
+ vec3 resultRGB = ad0Pix.rgb*alpha + videoImagePix.rgb*(1.0-alpha);//blend
1637
+ gl_FragColor = vec4(resultRGB, 1.0);
1638
+ return true;
1639
+ }
1640
+ else return false;
1641
+
1642
+ }
1643
+ void main()
1644
+ {
1645
+ //calculate cur-coord (with pixels units)
1646
+ vec2 curCoord = gl_FragCoord.xy;
1647
+ curCoord.y = u_resolution2[1] - curCoord.y;
1648
+
1649
+ //cover the timecode with some watermark
1650
+ if (v_texCoord[0] <= 0.125 && (1.0-v_texCoord[1]) <= 0.125)
1651
+ {
1652
+ gl_FragColor = texture2D(injectoLogo, vec2(v_texCoord[0]*8.0, 1.0-(1.0-v_texCoord[1])*8.0));
1653
+ return;
1654
+ }
1655
+
1656
+ vec4 videoImagePix = texture2D(u_image, v_texCoord);
1657
+
1658
+ if (hasLogo0 == 1 &&
1659
+ BlendWithLogo(
1660
+ adForLogo0,
1661
+ alpha0, u_matrix0, curCoord,u_alpha0TopLeft, u_alpha0Size, videoImagePix))
1662
+ {
1663
+ return;
1664
+ }
1665
+ else if (hasLogo1 == 1 &&
1666
+ BlendWithLogo(
1667
+ adForLogo1,
1668
+ alpha1, u_matrix1, curCoord,u_alpha1TopLeft, u_alpha1Size, videoImagePix))
1669
+ {
1670
+ return;
1671
+ }
1672
+ else if (hasLogo2 == 1 &&
1673
+ BlendWithLogo(
1674
+ adForLogo2,
1675
+ alpha2, u_matrix2, curCoord,u_alpha2TopLeft, u_alpha2Size, videoImagePix))
1676
+ {
1677
+ return;
1678
+ }
1679
+ else if (hasLogo3 == 1 &&
1680
+ BlendWithLogo(
1681
+ adForLogo3,
1682
+ alpha3, u_matrix3, curCoord,u_alpha3TopLeft, u_alpha3Size, videoImagePix))
1683
+ {
1684
+ return;
1685
+ }
1686
+ else {//only video texel, no logo
1687
+ gl_FragColor = videoImagePix;
1688
+ }
1689
+ }
1690
+ `
1691
+ }
1692
+ BB.FrameRateMode = {
1693
+ Src60_MediaTime60: Symbol('Src60_MediaTime60'),//source is 60, player's fps is 60 as well
1694
+ Src59_MediaTime60: Symbol('Src59_MediaTime60'),//source is 59.94, player's fps is 60 (dropping every 1001 frame)
1695
+ Src59_MediaTime59: Symbol('Src59_MediaTime59'),//source is 59.94, player's fps is 59.94fps as well
1696
+ Src29_MediaTime29: Symbol('Src29_MediaTime29')//source is 29.97, player's fps is 29.97fps as well
1697
+ };
1698
+ BB.RenderingMode = {
1699
+ RenderDigits : Symbol('RenderDigits'),
1700
+ RenderTimecode : Symbol('RenderTimecode'),
1701
+ Augment : Symbol('Augment')
1702
+ };
1703
+ BB.SynchMethod = {
1704
+ OnNewSegment : Symbol('OnNewSegment'),
1705
+ OnMetadata : Symbol('OnMetadata'),
1706
+ None : Symbol('None'),
1707
+ };
1708
+ BB.Utils = (() => {
1709
+ const padZero = (num) => {
1710
+ return num.toString().padStart(2, '0');
1711
+ }
1712
+ const TimeCodesFramesTranslator = (() => {
1713
+ const printTimecode = (timecode, duplicated, dot2) => {
1714
+ let str = "";
1715
+ if (Array.isArray(timecode)) {
1716
+ str = [
1717
+ padZero(timecode[0]),
1718
+ padZero(timecode[1]),
1719
+ padZero(timecode[2]),
1720
+ padZero(timecode[3])
1721
+ ].join(':');
1722
+ if (dot2==true){
1723
+ if(timecode[4]==1 && (duplicated === undefined || duplicated))
1724
+ str += ".2";
1725
+ else
1726
+ str += ".1";
1727
+ }
1728
+ else{
1729
+ if(timecode[4]==1 && (duplicated === undefined || duplicated))
1730
+ str += ".1";
1731
+ }
1732
+ }
1733
+ else {
1734
+ const delimiter = ":";
1735
+ str = ("0" + timecode.h).slice(-2) + delimiter + ("0" + timecode.m).slice(-2) + delimiter + ("0" + timecode.s).slice(-2) + delimiter + ("0" + timecode.f).slice(-2);
1736
+ if (duplicated) str += "." + (timecode.field ? "1" : "2");
1737
+ }
1738
+
1739
+ return str;
1740
+ }
1741
+
1742
+ const timeCodeStr2TimeCodeArray = (timecodeStr) => {
1743
+ let tokens = timecodeStr.split(":");
1744
+ if (tokens.length != 4) {
1745
+ console.error("timeCodeStr2TimeCodeArray: Failed to parse " + timecodeStr);
1746
+ return [0,0,0,0,0];
1747
+ }
1748
+ let timeCodeArray = [
1749
+ parseInt(tokens[0]),//hours
1750
+ parseInt(tokens[1]),//minutes
1751
+ parseInt(tokens[2])];//seconds
1752
+ if (tokens[3].indexOf(".") >= 0) {
1753
+ let framesAndField = tokens[3].split(".");
1754
+ timeCodeArray.push(parseInt(framesAndField[0]));//frames
1755
+ timeCodeArray.push(parseInt(framesAndField[1]));//field
1756
+ }
1757
+ else {
1758
+ timeCodeArray.push(parseInt(tokens[3]));//frames
1759
+ timeCodeArray.push(0);//field
1760
+ }
1761
+ return timeCodeArray;
1762
+ }
1763
+
1764
+
1765
+ const Timecode2Frames = (timecode, fps, duplicated) => { //timecode [hh,mm,ss,ff,.1] //.1= 1|0, fps (60,59.94,30.29.96), duplicated (true|false)
1766
+ // finding counting fps and dropped frames
1767
+ let countingFps=60;
1768
+ let dropFrames=0;
1769
+ if (Math.floor(fps)==60)
1770
+ {
1771
+ countingFps = 60;
1772
+ dropFrames=0;
1773
+ }
1774
+ else if (Math.floor(fps)==59)
1775
+ {
1776
+ countingFps = 60;
1777
+ dropFrames=4;
1778
+ }
1779
+ else if (Math.floor(fps)==30)
1780
+ {
1781
+ countingFps = 30;
1782
+ dropFrames=0;
1783
+ }
1784
+ else if (Math.floor(fps)==29)
1785
+ {
1786
+ countingFps = 30;
1787
+ dropFrames=2;
1788
+ }
1789
+ let result=0;
1790
+ let framesPerMinute = Math.floor(fps*60); //3600|3596|1800|1798
1791
+ let framesPer10Minutes = Math.floor(fps*60*10); //36000|35964|18000|17982
1792
+ let framesPerHour = framesPer10Minutes*6;
1793
+ let hh=timecode[0];
1794
+ let mm=timecode[1];
1795
+ let ss=timecode[2];
1796
+ let ff=timecode[3];
1797
+ let dotOne = timecode[4];//(timecode[4]==0)?false:true;
1798
+ result += hh*framesPerHour;
1799
+ result += mm*(countingFps*60);
1800
+ result += ss*countingFps;
1801
+ if (duplicated)
1802
+ result += ff * 2 + (dotOne-1); //we are on .1|.2 mode
1803
+ else
1804
+ result += ff;
1805
+ result -= dropFrames*(mm);
1806
+ result += dropFrames*(Math.floor(mm/10));//drop Frames on every minute exept every 10th
1807
+ return result;
1808
+ }
1809
+
1810
+ const Frames2Timecode = (frames, fps, duplicated) => { //timecode [hh,mm,ss,ff,.1] //.1= 1|0, fps (60,59.94,30.29.96), duplicated (true|false)
1811
+
1812
+ // finding counting fps and dropped frames
1813
+ let countingFps=60;
1814
+ let dropFrames=0;
1815
+ if (Math.floor(fps)==60)
1816
+ {
1817
+ countingFps = 60;
1818
+ dropFrames=0;
1819
+ }
1820
+ else if (Math.floor(fps)==59)
1821
+ {
1822
+ countingFps = 60;
1823
+ dropFrames=4;
1824
+ }
1825
+ else if (Math.floor(fps)==30)
1826
+ {
1827
+ countingFps = 30;
1828
+ dropFrames=0;
1829
+ }
1830
+ else if (Math.floor(fps)==29)
1831
+ {
1832
+ countingFps = 30;
1833
+ dropFrames=2;
1834
+ }
1835
+ let result=0;
1836
+ let framesPerMinute = Math.floor(fps*60); //3600|3596|1800|1798
1837
+ let framesPer10Minutes = Math.floor(fps*60*10) //36000|35964|18000|17982
1838
+ let framesPerHour = framesPer10Minutes*6;
1839
+
1840
+ let framesLeft=frames;
1841
+ let hh = Math.floor(framesLeft / framesPerHour);
1842
+ framesLeft-=hh*framesPerHour;
1843
+ let mm10 = Math.floor(framesLeft / framesPer10Minutes);
1844
+
1845
+ framesLeft-=mm10*framesPer10Minutes;
1846
+
1847
+ if (framesLeft >= countingFps*60) //3600 (in 59.94fps)
1848
+ framesLeft+= dropFrames*(Math.floor((framesLeft-countingFps*60)/framesPerMinute)+1); //3600 /3596 (in 59.94fps)
1849
+
1850
+ //framesLeft-= Math.floor(framesLeft / framesPerMinute)*(dropFrames)
1851
+
1852
+ let mm = Math.floor(framesLeft / (countingFps*60)); //3600 (in 59.94fps)
1853
+
1854
+ framesLeft-=mm*(countingFps*60); //3600 (in 59.94fps)
1855
+
1856
+ let ss = Math.floor((framesLeft) / countingFps);
1857
+ framesLeft-=ss*countingFps;
1858
+ let ff = framesLeft;
1859
+ mm+=mm10*10;
1860
+
1861
+ dotOne=0;
1862
+ if (duplicated)
1863
+ {
1864
+ dotOne = ff % 2;
1865
+ ff=Math.floor(ff/2);
1866
+ }
1867
+ let returnTimecode = [hh,mm,ss,ff,dotOne];
1868
+ return returnTimecode;
1869
+
1870
+ }
1871
+
1872
+ const TimeCodeAddFrames = (tc, framesToAdd, fps) => {
1873
+ let duplicated = fps > 30;
1874
+ let frames = Timecode2Frames(tc, fps, duplicated);
1875
+ frames += framesToAdd;
1876
+ return Frames2Timecode(frames, fps, duplicated);
1877
+ }
1878
+
1879
+ const TimeCodeArray2TimeCodeJson = (timeCodeArray) => {
1880
+ return {h:timeCodeArray[0],m:timeCodeArray[1],s:timeCodeArray[2],f:timeCodeArray[3],field:timeCodeArray[4]};
1881
+ }
1882
+
1883
+ const timeDate2TCString = (timeDate, fps) => {
1884
+ let tc = BB.Utils.timeDate2TC(timeDate,fps);
1885
+ //didn't check new version
1886
+ if (fps>59){
1887
+ return BB.Utils.TimeCodesFramesTranslator.printTimecode(tcFromMessage,true);
1888
+ }
1889
+ else{
1890
+ return BB.Utils.TimeCodesFramesTranslator.printTimecode(tcFromMessage,false);
1891
+ }
1892
+ //--------------
1893
+ //old
1894
+ let platform = BB.Utils.Platform;
1895
+
1896
+ let timeStampStr = "";
1897
+ if (platform.isApple()){
1898
+ timeStampStr = timeDate;
1899
+ }else{
1900
+ timeStampStr = timeDate.toISOString();
1901
+ }
1902
+ let timeStr = timeStampStr.split("T")[1];
1903
+ let arr1 = timeStr.split(":");
1904
+ let hrs = parseInt(arr1[0]);
1905
+ let mins = parseInt(arr1[1]);
1906
+ let arr2 = arr1[2].split(".");
1907
+ let secs = parseInt(arr2[0]);
1908
+ let msec = parseInt(arr2[1].substring(0, 3));
1909
+ let frames = Math.round(((msec / 1000) * fps) /*+ 0.45*/);
1910
+ if (fps>59){
1911
+
1912
+ let field = (frames % 2 == 0 ? 1 : 2);
1913
+ frames = Math.floor(frames / 2);
1914
+ let str = hrs.toString().padStart(2, '0') + ":" +
1915
+ mins.toString().padStart(2, '0') + ":" +
1916
+ secs.toString().padStart(2, '0') + ":" +
1917
+ frames.toString().padStart(2, '0') + "." + field.toString();
1918
+ return str;
1919
+ }
1920
+ else {
1921
+ let str = hrs.toString().padStart(2, '0') + ":" +
1922
+ mins.toString().padStart(2, '0') + ":" +
1923
+ secs.toString().padStart(2, '0') + ":" +
1924
+ frames.toString().padStart(2, '0');
1925
+ return str;
1926
+ }
1927
+ return str;
1928
+
1929
+ }
1930
+
1931
+ const timeDate2TC = (timeDate, fps) => {
1932
+ let platform = BB.Utils.Platform;
1933
+ let timeStampStr = "";
1934
+ if (platform.isApple()){
1935
+ timeStampStr = timeDate;
1936
+ }else{
1937
+ timeStampStr = timeDate.toISOString();
1938
+ }
1939
+ let timeStr = timeStampStr.split("T")[1];
1940
+ let arr1 = timeStr.split(":");
1941
+ let hrs = parseInt(arr1[0]);
1942
+ let mins = parseInt(arr1[1]);
1943
+ let arr2 = arr1[2].split(".");
1944
+ let secs = parseInt(arr2[0]);
1945
+ let msec = parseInt(arr2[1].substring(0, 3));
1946
+ //let frames = Math.round(((msec / 1000) * fps)+0.02 /*+ 0.45*/);
1947
+ if (fps>59){
1948
+ //let field = (frames % 2 == 0 ? 1 : 2);
1949
+ //frames = Math.floor(frames / 2);
1950
+ //let returnTimecode = [hrs,mins,secs,frames,field-1];
1951
+ let allFrames = Math.round((msec/1000+secs+mins*60+hrs*60*60)*fps);
1952
+ let returnTimecode = BB.Utils.TimeCodesFramesTranslator.Frames2Timecode(allFrames,fps,true);
1953
+ return returnTimecode;
1954
+ }
1955
+ else
1956
+ {
1957
+ //let returnTimecode = [hrs,mins,secs,frames,0];
1958
+ //let returnTimecode = BB.Utils.TimeCodesFramesTranslator.Frames2Timecode(frames+secs*fps+mins*fps*60+hrs*fps*60*60,fps,false);
1959
+ let allFrames = Math.round((msec/1000+secs+mins*60+hrs*60*60)*fps);
1960
+ let returnTimecode = BB.Utils.TimeCodesFramesTranslator.Frames2Timecode(allFrames,fps,false);
1961
+ return returnTimecode;
1962
+ }
1963
+
1964
+ }
1965
+
1966
+
1967
+ return {
1968
+ printTimecode:printTimecode,
1969
+ timeCodeStr2TimeCodeArray:timeCodeStr2TimeCodeArray,
1970
+ Timecode2Frames:Timecode2Frames,
1971
+ Frames2Timecode:Frames2Timecode,
1972
+ TimeCodeAddFrames:TimeCodeAddFrames,
1973
+ TimeCodeArray2TimeCodeJson:TimeCodeArray2TimeCodeJson,
1974
+ timeDate2TCString:timeDate2TCString,
1975
+ timeDate2TC:timeDate2TC
1976
+ };
1977
+
1978
+ })();
1979
+
1980
+ const MedianCalculator = (maxSize_) => {
1981
+ let maxSize = maxSize_ || 9999999999;
1982
+ let values = [];
1983
+ let count = 0;
1984
+
1985
+ const add = (value) => {
1986
+ ++count;
1987
+ values.push(value);
1988
+ while (values.length > maxSize) values.shift();
1989
+ };
1990
+ const getMedian = () => {
1991
+ if (values.length == 0) return -1;
1992
+ let sortedValues = Array.from(values).sort((a,b) => { return a - b; });
1993
+ return sortedValues[parseInt(sortedValues.length/2)];
1994
+ };
1995
+ const howMany = () => {
1996
+ return count;
1997
+ };
1998
+
1999
+ return {
2000
+ add,
2001
+ getMedian,
2002
+ howMany
2003
+ };
2004
+ };
2005
+
2006
+ //todo: make it use one function. Now they are similar
2007
+ const Platform = (() => {
2008
+ const getCurrentPlatform = () => {
2009
+ const userAgent = navigator.userAgent;
2010
+ if (/windows/i.test(userAgent)) {
2011
+ return 'Windows';
2012
+ } else if (/android/i.test(userAgent)) {
2013
+ return 'Android';
2014
+ } else if (/iphone|ipad|ipod/i.test(userAgent)) {
2015
+ return 'iOS';
2016
+ } else if (/mac/i.test(userAgent)) {
2017
+ let isIPadAnyhow = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
2018
+ if (isIPadAnyhow) return 'iPad';
2019
+ else return 'Mac';
2020
+ } else if (/ipad/i.test(userAgent)) {
2021
+ return 'iPad';
2022
+ } else {
2023
+ return 'Something else';
2024
+ }
2025
+ }
2026
+ const isMac = () => {
2027
+ let platform = getCurrentPlatform();
2028
+ return platform =='Mac';
2029
+ };
2030
+ const isiPad = () => {
2031
+ let platform = getCurrentPlatform();
2032
+ return platform =='iPad';
2033
+ };
2034
+ const isiPhone = () => {
2035
+ let platform = getCurrentPlatform();
2036
+ return platform =='iOS';
2037
+ };
2038
+ const isApple = () => {
2039
+ let platform = getCurrentPlatform();
2040
+ return platform == 'iOS' || platform == 'Mac' || platform == 'iPad';
2041
+ }
2042
+ const isWindows = () => {
2043
+ //return true;
2044
+ let platform = getCurrentPlatform();
2045
+ return platform == 'Windows';
2046
+ }
2047
+ const isAndroid = () => {
2048
+ let platform = getCurrentPlatform();
2049
+ return platform == 'Android';
2050
+ }
2051
+ const getDeviceDescription = () => {
2052
+ let deviceType = getCurrentPlatform();
2053
+ if (deviceType == 'Windows') return "a Windows PC";
2054
+ else if (deviceType == 'Android') return "an Android phone";
2055
+ else if (deviceType == 'iOS') return "an iPhone";
2056
+ else if (deviceType == 'Mac') return "a Mac";
2057
+ else if (deviceType == 'iPad') return "an iPad";
2058
+ else return "an Unknown device";
2059
+ };
2060
+ const getDeviceName = () => {
2061
+ let module = {
2062
+ options: [],
2063
+ header: [navigator.platform, navigator.userAgent, navigator.appVersion, navigator.vendor, window.opera],
2064
+ dataos: [
2065
+ { name: 'Windows Phone', value: 'Windows Phone', version: 'OS' },
2066
+ { name: 'Windows', value: 'Win', version: 'NT' },
2067
+ { name: 'iPhone', value: 'iPhone', version: 'OS' },
2068
+ { name: 'iPad', value: 'iPad', version: 'OS' },
2069
+ { name: 'Kindle', value: 'Silk', version: 'Silk' },
2070
+ { name: 'Android', value: 'Android', version: 'Android' },
2071
+ { name: 'PlayBook', value: 'PlayBook', version: 'OS' },
2072
+ { name: 'BlackBerry', value: 'BlackBerry', version: '/' },
2073
+ { name: 'Macintosh', value: 'Mac', version: 'OS X' },
2074
+ { name: 'Linux', value: 'Linux', version: 'rv' },
2075
+ { name: 'Palm', value: 'Palm', version: 'PalmOS' }
2076
+ ],
2077
+ databrowser: [
2078
+ { name: 'Chrome', value: 'Chrome', version: 'Chrome' },
2079
+ { name: 'Firefox', value: 'Firefox', version: 'Firefox' },
2080
+ { name: 'Safari', value: 'Safari', version: 'Version' },
2081
+ { name: 'Internet Explorer', value: 'MSIE', version: 'MSIE' },
2082
+ { name: 'Opera', value: 'Opera', version: 'Opera' },
2083
+ { name: 'BlackBerry', value: 'CLDC', version: 'CLDC' },
2084
+ { name: 'Mozilla', value: 'Mozilla', version: 'Mozilla' }
2085
+ ],
2086
+ init: function () {
2087
+ let agent = this.header.join(' '),
2088
+ os = this.matchItem(agent, this.dataos),
2089
+ browser = this.matchItem(agent, this.databrowser);
2090
+
2091
+ return { os: os, browser: browser };
2092
+ },
2093
+ matchItem: function (string, data) {
2094
+ let i = 0,
2095
+ j = 0,
2096
+ html = '',
2097
+ regex,
2098
+ regexv,
2099
+ match,
2100
+ matches,
2101
+ version;
2102
+
2103
+ for (i = 0; i < data.length; i += 1) {
2104
+ regex = new RegExp(data[i].value, 'i');
2105
+ match = regex.test(string);
2106
+ if (match) {
2107
+ regexv = new RegExp(data[i].version + '[- /:;]([\\d._]+)', 'i');
2108
+ matches = string.match(regexv);
2109
+ version = '';
2110
+ if (matches) { if (matches[1]) { matches = matches[1]; } }
2111
+ if (matches) {
2112
+ matches = matches.split(/[._]+/);
2113
+ for (j = 0; j < matches.length; j += 1) {
2114
+ if (j === 0) {
2115
+ version += matches[j] + '.';
2116
+ } else {
2117
+ version += matches[j];
2118
+ }
2119
+ }
2120
+ } else {
2121
+ version = '0';
2122
+ }
2123
+ return {
2124
+ name: data[i].name,
2125
+ version: parseFloat(version)
2126
+ };
2127
+ }
2128
+ }
2129
+ return { name: 'unknown', version: 0 };
2130
+ }
2131
+ };
2132
+
2133
+ let e = module.init();
2134
+ return e.os.name;
2135
+ };
2136
+ const getBrowserName = () => {
2137
+ const userAgent = navigator.userAgent;
2138
+
2139
+ if (userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Edge") === -1) {
2140
+ return "Chrome";
2141
+ } else if (userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") === -1) {
2142
+ return "Safari";
2143
+ } else if (userAgent.indexOf("Firefox") > -1) {
2144
+ return "Firefox";
2145
+ } else if (userAgent.indexOf("MSIE") > -1 || userAgent.indexOf("Trident") > -1) {
2146
+ return "Internet Explorer";
2147
+ } else if (userAgent.indexOf("Edge") > -1) {
2148
+ return "Edge";
2149
+ } else {
2150
+ return "Unknown";
2151
+ }
2152
+ }
2153
+ const isSafariMac = () => {
2154
+ return (isMac()&&(getBrowserName() == "Safari"))
2155
+ }
2156
+ const isChromeMac = () => {
2157
+ return (isMac()&&(getBrowserName() == "Chrome"))
2158
+ }
2159
+ const isChromeWin = () => {
2160
+ return (isWindows()&&(getBrowserName() == "Chrome"))
2161
+ }
2162
+ return {
2163
+ getDeviceName,
2164
+ getCurrentPlatform,
2165
+ isApple,
2166
+ isWindows,
2167
+ isAndroid,
2168
+ isMac,
2169
+ isiPhone,
2170
+ isiPad,
2171
+ getDeviceDescription,
2172
+ getBrowserName,
2173
+ isSafariMac,
2174
+ isChromeMac,
2175
+ isChromeWin
2176
+ }
2177
+ })();
2178
+
2179
+ const getMediaTimeFPS = (frameRateMode) => {
2180
+ let fps = 60;
2181
+ switch(frameRateMode) {
2182
+ case BB.FrameRateMode.Src60_MediaTime60:
2183
+ fps = 60;
2184
+ break;
2185
+ case BB.FrameRateMode.Src59_MediaTime60:
2186
+ fps = 60;
2187
+ break;
2188
+ case BB.FrameRateMode.Src59_MediaTime59:
2189
+ fps = 60000/1001;
2190
+ break;
2191
+ case BB.FrameRateMode.Src29_MediaTime29:
2192
+ fps = 30000/1001;
2193
+ break;
2194
+ default:
2195
+ console.error("BB.Utils.getMediaTimeFPS: Unknown FrameRate mode");
2196
+ break;
2197
+ };
2198
+ return fps;
2199
+ };
2200
+ const getSourceFPS = (frameRateMode) => {
2201
+ let fps = 60;
2202
+ switch(frameRateMode) {
2203
+ case BB.FrameRateMode.Src60_MediaTime60:
2204
+ fps = 60;
2205
+ break;
2206
+ case BB.FrameRateMode.Src59_MediaTime60:
2207
+ fps = 60000/1001;
2208
+ break;
2209
+ case BB.FrameRateMode.Src59_MediaTime59:
2210
+ fps = 60000/1001;
2211
+ break;
2212
+ case BB.FrameRateMode.Src29_MediaTime29:
2213
+ fps = 30000/1001;
2214
+ break;
2215
+ default:
2216
+ console.error("BB.Utils.getSourceFPS: Unknown FrameRate mode");
2217
+ break;
2218
+ };
2219
+ return fps;
2220
+ };
2221
+ const setMediaFPS = (newFPS, options) => {
2222
+ const equil = (one, two) => {
2223
+ if (Math.abs(one-two)<0.01)
2224
+ return true;
2225
+ else
2226
+ return false;
2227
+ }
2228
+
2229
+ if (equil(newFPS,60)){
2230
+ if (equil(BB.Utils.getSourceFPS(options.frameRateMode),60))
2231
+ options.frameRateMode = BB.FrameRateMode.Src60_MediaTime60;
2232
+ else if(equil(BB.Utils.getSourceFPS(options.frameRateMode),60000/1001))
2233
+ options.frameRateMode = BB.FrameRateMode.Src59_MediaTime60;
2234
+ else
2235
+ console.error("setMediaFPS: Unknown Source FrameRate mode");
2236
+ }
2237
+ else if (equil(newFPS,60000/1001)){
2238
+ if (equil(BB.Utils.getSourceFPS(options.frameRateMode),60))
2239
+ options.frameRateMode = BB.FrameRateMode.Src60_MediaTime59;
2240
+ else if(equil(BB.Utils.getSourceFPS(options.frameRateMode),60000/1001))
2241
+ options.frameRateMode = BB.FrameRateMode.Src59_MediaTime59;
2242
+ else
2243
+ console.error("setMediaFPS: Unknown Source FrameRate mode");
2244
+ }
2245
+ else if (equil(newFPS,30000/1001)){
2246
+ if (equil(BB.Utils.getSourceFPS(options.frameRateMode),30000/1001))
2247
+ options.frameRateMode = BB.FrameRateMode.Src29_MediaTime29;
2248
+ else
2249
+ console.error("setMediaFPS: Unknown Source FrameRate mode, fps = " + newFPS);
2250
+ }
2251
+ else
2252
+ console.error("setMediaFPS: Unknown Media FrameRate mode");
2253
+ };
2254
+
2255
+
2256
+ const FPSGetter = () => {
2257
+
2258
+ const possibleFps29_30 = [
2259
+ 30000/1001,//29.97
2260
+ 30/1//30,0
2261
+
2262
+ ];
2263
+ const possibleFps59_60 = [
2264
+ 60000/1001,//59.94
2265
+ 60/1///60
2266
+ ];
2267
+ let bestFPSMatches = [];
2268
+ let deltaStorage = [];
2269
+ let deltaCount =0;
2270
+ let lastMediaTimeForFPSGetting = -1;
2271
+ //let fps = 60;
2272
+ let count = 0;
2273
+ const addTime = (mediaTime) => {
2274
+ count++;
2275
+ //let fraction2PossibleFPS = new Map();
2276
+ let minFraction=999;
2277
+ let minCandidateFps=0;
2278
+ if (lastMediaTimeForFPSGetting == -1){
2279
+ lastMediaTimeForFPSGetting = mediaTime;
2280
+ return;
2281
+ }
2282
+ else
2283
+ {
2284
+ let delta = mediaTime - lastMediaTimeForFPSGetting;
2285
+ if (deltaCount<100){
2286
+ deltaStorage.push(delta);
2287
+ deltaCount++;
2288
+ return;
2289
+ }
2290
+ else{
2291
+ deltaStorage.push(delta);
2292
+ deltaStorage.shift();
2293
+ }
2294
+ let total = 0;
2295
+ for(let i = 0; i < deltaStorage.length; i++) {
2296
+ total += deltaStorage[i];
2297
+ }
2298
+ delta = total / deltaStorage.length;
2299
+ //console.log("delta= " + delta.toFixed(5) + " fps= " + (1/delta).toFixed(2));
2300
+ if ((1/delta) < 40)
2301
+ possibleFps = possibleFps29_30;
2302
+ else
2303
+ possibleFps = possibleFps59_60;
2304
+ }
2305
+ //possibleFps = possibleFps59_60;
2306
+ lastMediaTimeForFPSGetting = mediaTime;
2307
+
2308
+ for (let i = 0; i < possibleFps.length; ++i) {
2309
+ let candidateFps = possibleFps[i];
2310
+ let dblFrame = mediaTime*candidateFps;
2311
+ let intFrame = Math.round(dblFrame);
2312
+ let fraction = Math.abs(dblFrame - intFrame);
2313
+ if (fraction < minFraction)
2314
+ {
2315
+ minFraction = fraction;
2316
+ minCandidateFps = candidateFps;
2317
+ }
2318
+
2319
+ //fraction2PossibleFPS.set(fraction, candidateFps);
2320
+ }
2321
+ let bestFPSMatch = minCandidateFps;//fraction2PossibleFPS.entries().next().value[1];
2322
+ bestFPSMatches.push(bestFPSMatch);
2323
+ //console.log("bestFPSMatch = " + bestFPSMatch);
2324
+ if(count>1000)
2325
+ bestFPSMatches.shift(); //deleting the first el
2326
+ }
2327
+ const getProbableFPS = () => {
2328
+ let count = {};
2329
+ let mostOccurringValue;
2330
+ let maxCount = 0;
2331
+
2332
+ // Count the occurrences of each value in the array
2333
+ for (let i = 0; i < bestFPSMatches.length; i++) {
2334
+ let value = bestFPSMatches[i];
2335
+ count[value] = (count[value] || 0) + 1;
2336
+
2337
+ // Update the most occurring value and its count if necessary
2338
+ if (count[value] > maxCount) {
2339
+ mostOccurringValue = value;
2340
+ maxCount = count[value];
2341
+ }
2342
+ }
2343
+
2344
+ return mostOccurringValue;
2345
+ }
2346
+
2347
+ const howMany = () => {
2348
+ return count;
2349
+ };
2350
+
2351
+ return {
2352
+ addTime:addTime,
2353
+ getProbableFPS:getProbableFPS,
2354
+ howMany:howMany
2355
+ };
2356
+
2357
+
2358
+ };
2359
+
2360
+ return {
2361
+ TimeCodesFramesTranslator,
2362
+ MedianCalculator,
2363
+ Platform,
2364
+ getMediaTimeFPS,
2365
+ getSourceFPS,
2366
+ setMediaFPS,
2367
+ FPSGetter
2368
+ }
2369
+ })();
2370
+ BB.Socket = class {
2371
+ constructor(callback) {
2372
+ this.isConnected = false;
2373
+ this.folderName = "";
2374
+ this.callback = callback;
2375
+ this.name = "";
2376
+ }
2377
+
2378
+ Connect (channel){
2379
+ if (typeof io == "undefined" || this.isConnected) return;
2380
+ this.isConnected = true;
2381
+ const socket = io("https://www.virtualott.com");
2382
+ socket.on("connect", () => {
2383
+ console.log("socket connected");
2384
+ let platform = BB.Utils.Platform;
2385
+ socket.emit('setinfo', {device:platform.getDeviceName()},(info)=>{
2386
+ this.name = info.name;
2387
+ socket.emit("join", channel, (data) => {
2388
+ console.log(`join ${channel} room`);
2389
+ this.callback("join", data);
2390
+ });
2391
+ });
2392
+ });
2393
+ socket.on("error", (err) => {
2394
+ console.error(`error::${err}`);
2395
+ });
2396
+ socket.on('disconnect', (reason) => {
2397
+ console.error(`disconnect from signaling::${reason}`);
2398
+ });
2399
+ socket.on("messageroom", (message) => {
2400
+ this.callback("messageroom", message);
2401
+ console.log("messageroom: ", message);
2402
+ });
2403
+ socket.on("ads", (message) => {
2404
+ this.callback("ads", message);
2405
+ console.log("ads:", message);
2406
+ });
2407
+ }
2408
+ }
2409
+ BB.Augmenter = (line, video, options) => {
2410
+ console.log("BB.Augmenter Start");
2411
+ // MK OnMetaData Sync Begin
2412
+ function processTimedMetadata(messageData, presentationTime) {
2413
+ // Timed metadata
2414
+ let finalTimeCodeData = "";
2415
+ if (messageData && messageData.includes("www.mediakind.com/NTC")) {
2416
+ const metadata = messageData.replace(/.*NTC./, "");
2417
+ let pos = 0;
2418
+ while (pos + 8 <= metadata.length) {
2419
+ const type = metadata.substring(pos, pos + 4);
2420
+ const length = ((metadata.charCodeAt(pos + 4) * 256 + metadata.charCodeAt(pos + 5)) * 256 + metadata.charCodeAt(pos + 6)) * 256 + metadata.charCodeAt(pos + 7);
2421
+ if (pos + 8 + length > metadata.length) {
2422
+ break; // oops!
2423
+ }
2424
+ const value = metadata.substring(pos + 8, pos + 8 + length);
2425
+ if (type == "utc_" ) {
2426
+ //previousTimeCode = currentTimeCode;
2427
+ timecodeDateTime = new Date(parseInt(value)).getTime()/1000;
2428
+ timecodeDelta = timecodeDateTime - presentationTime;
2429
+ //finalTimeCodeData += `${new Date(parseInt(value)).toISOString()} | timestamp: ${new Date(parseInt(value)).getTime()/1000} | delta: ${timecodeDelta}`;
2430
+
2431
+ }
2432
+ if (type == "tc__" ) {
2433
+ //previousTimeCode = currentTimeCode;
2434
+ //tcDateTime = new Date(parseInt(value)).getTime()/1000;
2435
+ //tcDelta = tcDateTime - presentationTime;
2436
+ tcTimeStamp = new Date(parseInt(value)).getTime();
2437
+ //finalTimeCodeData += `${new Date(parseInt(value)).toISOString()} | tcTimestamp: ${tcTimeStamp} | segmentPlaybackDateTime: ${hlsDateTime}`;
2438
+
2439
+ }
2440
+ if (type === "offs") {
2441
+ timecode_offset = (parseInt(value)/90);
2442
+ if (options.debug) console.log("We are HERE:TimeCode", timecode_offset);
2443
+
2444
+
2445
+ }
2446
+ pos += 8 + length;
2447
+ }
2448
+ }
2449
+ return finalTimeCodeData;
2450
+ }
2451
+
2452
+ let firstFrameTimecode = [0,0,0,0,0];
2453
+ function onNewMetadata(data) {
2454
+ // look for timed metadata and display in UI if available
2455
+ if ((data && data.metadata && data.metadata.id == 0) || (platform.isSafariMac())){ //mac check is temp. Need to get id from safari CallBack
2456
+ if (data.metadata.messageData) {
2457
+ // Typically seen on Chrome, Edge browsers
2458
+ data.metadata.messageData = processTimedMetadata(data.metadata.messageData, data.metadata.presentationTime);
2459
+ } else if (data.metadata.frames && data.metadata.frames.length && data.metadata.frames[0].key && data.metadata.frames[0].key.toLowerCase() === "priv") {
2460
+ // Typically seen on Safari browser
2461
+ const NTC_DATA = data.metadata.frames.filter((el) => {
2462
+ return ( el.info && el.info.toLowerCase().includes("ntc")) ;
2463
+ });
2464
+ if (NTC_DATA.length) {
2465
+ messageData = `${NTC_DATA[NTC_DATA.length - 1].info}\u0000${String.fromCharCode(...NTC_DATA[NTC_DATA.length - 1].data)}`;
2466
+ data.metadata.messageData = processTimedMetadata(messageData, data.metadata.presentationTime);
2467
+ }
2468
+ }
2469
+ if((data.metadata.id == 0) || ((platform.isSafariMac()&&(timecode_offset == 0)))){ //mac check is temp. Need to get id from safari CallBack
2470
+ presentationTime = data.metadata.presentationTime * 1000;
2471
+
2472
+ offsetTimeCode = new Date(tcTimeStamp - timecode_offset);
2473
+ //offsetTimeCode = new Date(1721866388183);
2474
+ offsetTimeCodeframeNumber = Math.round((offsetTimeCode.getUTCMilliseconds() * 30 * 2)/1001)/2;
2475
+ decimalPartOff = offsetTimeCodeframeNumber % 1;
2476
+ partoff=1;
2477
+ if(decimalPartOff == 0.5){
2478
+ offsetTimeCodeframeNumber = Math.floor(offsetTimeCodeframeNumber);
2479
+ partoff=2;
2480
+ }
2481
+
2482
+ firstFrameTimecode = [offsetTimeCode.getUTCHours(),offsetTimeCode.getUTCMinutes(),offsetTimeCode.getUTCSeconds(),parseInt(offsetTimeCodeframeNumber),partoff==1?0:1];
2483
+
2484
+ //timeCodeOff = `${offsetTimeCode.getUTCHours()}:${offsetTimeCode.getUTCMinutes()}:${offsetTimeCode.getUTCSeconds()}:${parseInt(offsetTimeCodeframeNumber)}.${partoff}`;
2485
+
2486
+ //console.log("timeCodeOff=", timeCodeOff);
2487
+ //data.metadata.messageData = ``;
2488
+ //data.metadata.messageData = `TimeCode: ${timeCodeOff} | offsetTimeCodeframeNumber: ${offsetTimeCodeframeNumber} | offsetTimeCodeMilliseconds: ${offsetTimeCode.getUTCMilliseconds()}`;
2489
+ //dataForCSV += `${timeCodeOff},`
2490
+ //timedMetadata.textContent = JSON.stringify(data.metadata, null, 4);
2491
+ //timedMetadata.textContent+=`{"counter" : ${counter++}}`;
2492
+ }
2493
+ }
2494
+
2495
+
2496
+ }
2497
+ function onNewSegmentForMetadata(eventObj) {
2498
+ if (typeof globalConfig !== 'undefined')
2499
+ {
2500
+ if (typeof globalConfig.MKsync !== 'undefined')
2501
+ if (globalConfig.MKsync=="no")
2502
+ {
2503
+ return; //Temp for testing without MK
2504
+ }
2505
+ }
2506
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001)
2507
+ {
2508
+ let syncPoint = {frame:0,playerTime:0}
2509
+ syncPoint.frame = BB.Utils.TimeCodesFramesTranslator.Timecode2Frames(firstFrameTimecode,30000/1001,false);//@alal
2510
+ syncPoint.stringTime = BB.Utils.TimeCodesFramesTranslator.printTimecode(firstFrameTimecode,false,false);// timeDate2TCString(eventObj.dateTime, 30000/1001);
2511
+ console.log("New segment (" + eventObj.url +"): " + syncPoint.stringTime + " (" + syncPoint.frame + ")" + " playing at " + eventObj.playbackTime);
2512
+ syncPoint.playerTime = eventObj.playbackTime;
2513
+ options.syncPoint = syncPoint;
2514
+ }
2515
+ else if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001)
2516
+ {
2517
+ let syncPoint = {frame:0,playerTime:0}
2518
+ syncPoint.frame = BB.Utils.TimeCodesFramesTranslator.Timecode2Frames(firstFrameTimecode,60000/1001,true);//@alal
2519
+ syncPoint.stringTime = BB.Utils.TimeCodesFramesTranslator.printTimecode(firstFrameTimecode,true,true);// timeDate2TCString(eventObj.dateTime, 30000/1001);
2520
+ console.log("New segment (" + eventObj.url +"): " + syncPoint.stringTime + " (" + syncPoint.frame + ")" + " playing at " + eventObj.playbackTime);
2521
+ syncPoint.playerTime = eventObj.playbackTime;
2522
+ options.syncPoint = syncPoint;
2523
+ }
2524
+ if(eventObj.mimeType.includes("video")){
2525
+
2526
+ console.log("url: " + eventObj.url);
2527
+
2528
+ }
2529
+ }
2530
+ // MK OnMetaData Sync End
2531
+ function onNewSegment(eventObj) {
2532
+ switch (eventObj.eventType) { //just for check
2533
+ case wmcEvents.AMC_EVENT_PLAYER_SEGMENT_PLAYBACK:
2534
+ let syncPoint = {frame:0,playerTime:0}
2535
+
2536
+ //if ((fps == 30000/1001) || (fps == 30))
2537
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001)
2538
+ {
2539
+ console.log("New segment (" + eventObj.url +"): " + BB.Utils.TimeCodesFramesTranslator.timeDate2TCString(eventObj.dateTime, 30000/1001) + " playing at " + eventObj.playbackTime);
2540
+ syncPoint.frame = BB.Utils.TimeCodesFramesTranslator.Timecode2Frames(BB.Utils.TimeCodesFramesTranslator.timeDate2TC(eventObj.dateTime, 30000/1001),30000/1001,false);//@alal
2541
+ syncPoint.stringTime = BB.Utils.TimeCodesFramesTranslator.timeDate2TCString(eventObj.dateTime, 30000/1001);
2542
+ }
2543
+ //else ((fps == 60000/1001) || (fps == 30))
2544
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001)
2545
+ {
2546
+ console.log("New segment (" + eventObj.url +"): " + BB.Utils.TimeCodesFramesTranslator.timeDate2TCString(eventObj.dateTime, 60000/1001) + " playing at " + eventObj.playbackTime);
2547
+ syncPoint.frame = BB.Utils.TimeCodesFramesTranslator.Timecode2Frames(BB.Utils.TimeCodesFramesTranslator.timeDate2TC(eventObj.dateTime),fps,true);//@alal
2548
+ syncPoint.stringTime = BB.Utils.TimeCodesFramesTranslator.timeDate2TCString(eventObj.dateTime, 60000/1001);
2549
+ }
2550
+ syncPoint.playerTime = eventObj.playbackTime;
2551
+ options.syncPoint = syncPoint;
2552
+ //bbPlayer.SetSyncPoint(syncPoint);
2553
+ break;
2554
+ default:
2555
+ }
2556
+ }
2557
+ //do not need this anymore
2558
+ // if (player!={}){
2559
+ // if (options.SynchMethod == BB.SynchMethod.OnNewSegment)
2560
+ // {
2561
+ // player.addEventListener(wmcEvents.AMC_EVENT_PLAYER_SEGMENT_PLAYBACK, onNewSegment);
2562
+ // }
2563
+ // else if (options.SynchMethod == BB.SynchMethod.OnMetadata)
2564
+ // {
2565
+ // player.addEventListener(wmcEvents.AMC_EVENT_PLAYER_METADATA, onNewMetadata);
2566
+ // player.addEventListener(wmcEvents.AMC_EVENT_PLAYER_SEGMENT_PLAYBACK, onNewSegmentForMetadata);
2567
+ // }
2568
+ // }
2569
+
2570
+
2571
+
2572
+
2573
+ let tcTranslator = BB.Utils.TimeCodesFramesTranslator;
2574
+ let sourceFPS = BB.Utils.getSourceFPS(options.frameRateMode);
2575
+ let mediaTimeFPS = BB.Utils.getMediaTimeFPS(options.frameRateMode);
2576
+ refreshRate = options.refreshRate;
2577
+ //augmentation
2578
+ let metaDataFetcher = null;
2579
+ let composer = null;
2580
+ let ctxGl = null;
2581
+ if (options.renderingMode == BB.RenderingMode.Augment) {
2582
+ //We load it all only when we know the game id
2583
+ /*
2584
+ let playerCont = video.parentElement;
2585
+ composer = new BB.Composer3d(playerCont,video,options);
2586
+ ads = BB.Ads(composer, options);
2587
+ metaDataFetcher = BB.MetaDataFetcher(options,composer);
2588
+ */
2589
+ }
2590
+ else
2591
+ {
2592
+ //add a gl canvas to draw digits/timecode
2593
+ let canvas = document.createElement("canvas");
2594
+ canvas.width = 500;//video.videoWidth;
2595
+ canvas.height = 500;//video.videoHeight;
2596
+ document.body.appendChild(canvas);
2597
+ ctxGl = canvas.getContext('webgl');
2598
+ }
2599
+ let platform = BB.Utils.Platform;
2600
+
2601
+ //GL rendering method:
2602
+ let lastVideoFrameRenderedInGL = -1;
2603
+ let lastCorrection = false;
2604
+ const render = (currentFrame, correction) => {
2605
+ const startTime = performance.now();
2606
+ //if (BB.Utils.getSourceFPS(options.frameRateMode) != 30000/1001){ //ignoring this check for 29.97 fps
2607
+ if ((lastVideoFrameRenderedInGL == currentFrame) && (correction == lastCorrection))
2608
+ {
2609
+ if (options.debug) console.BBlog("render(): lastVideoFrameRenderedInGL==currentFrame: " + currentFrame);
2610
+ return 0;
2611
+ }
2612
+ //}
2613
+ if ((!options.enabled)||(options.stopAugment)){
2614
+ composer.CleanGL();
2615
+ return 0;
2616
+ }
2617
+
2618
+ if (options.renderingMode == BB.RenderingMode.RenderDigits)
2619
+ renderDigit(ctxGl, currentFrame);
2620
+ else if (options.renderingMode == BB.RenderingMode.RenderTimecode) {
2621
+ if ((sourceFPS==60000/1001)||(sourceFPS==60))
2622
+ renderTimecode(ctxGl, tcTranslator.Frames2Timecode(currentFrame,sourceFPS,true),true);
2623
+ else if ((sourceFPS==30000/1001)||(sourceFPS==30))
2624
+ renderTimecode(ctxGl, tcTranslator.Frames2Timecode(currentFrame,sourceFPS,false),false);
2625
+ }
2626
+ else if (options.renderingMode == BB.RenderingMode.Augment)
2627
+ if ((sourceFPS==60000/1001)||(sourceFPS==60))
2628
+ composer.renderAugmentation(tcTranslator.printTimecode(tcTranslator.Frames2Timecode(currentFrame,sourceFPS,true)));
2629
+ else if ((sourceFPS==30000/1001)||(sourceFPS==30)){
2630
+ if(correction){
2631
+ composer.renderAugmentation(tcTranslator.printTimecode(tcTranslator.Frames2Timecode(currentFrame*2+1,sourceFPS*2,true),true)); //@alal
2632
+ } else {
2633
+ composer.renderAugmentation(tcTranslator.printTimecode(tcTranslator.Frames2Timecode(currentFrame,sourceFPS,false),false)); //@alal
2634
+ }
2635
+ }
2636
+ lastVideoFrameRenderedInGL = currentFrame;
2637
+ lastCorrection = correction;
2638
+ const endTime = performance.now();
2639
+ return endTime - startTime;
2640
+ };
2641
+
2642
+
2643
+
2644
+
2645
+
2646
+
2647
+ //Windows/Android://////////////
2648
+
2649
+
2650
+
2651
+
2652
+ //------------------
2653
+
2654
+ let countVid = 0;
2655
+ let lastVidDateTime=0;
2656
+ let prevDeltaVid=32;
2657
+ let prevprevDeltaVid=32;
2658
+ let lastCalled = "";
2659
+ let lastOffset=0;
2660
+ let lastOffsetShown;
2661
+ let onFrameVideoWindows2 = function(now,metadata) {
2662
+ //console.log("VID");
2663
+ if (lastCalled=="ANI2")
2664
+ {
2665
+ if (options.debug) console.BBlog("This videoCallback can be incorrect. This time doing stuff in requestAnimation frame");
2666
+ video.requestVideoFrameCallback(onFrameVideoWindows2);
2667
+ return;
2668
+ }
2669
+ lastCalled = "VID";
2670
+ lastNow=now;
2671
+ //console.log("mediaTime-currentTime = " + ((metadata.mediaTime - curTime)*mediaTimeFPS).toFixed(4) +
2672
+ // " expected-now = " + ((metadata.expectedDisplayTime - now)/1000*mediaTimeFPS).toFixed(4));
2673
+ let dblFullFrame = ((metadata.mediaTime - options.syncPoint.playerTime)%(60*60*24))*mediaTimeFPS; // take time excluding days
2674
+ let timeFromLastSync = metadata.mediaTime - options.syncPoint.playerTime;
2675
+ lastFrameFromVideoCallback = Math.floor(dblFullFrame+0.1) + options.syncPoint.frame;
2676
+ if (options.debug) {
2677
+ var numberInput = document.getElementById("numberInput").value;
2678
+ var number = parseInt(numberInput);
2679
+ lastFrameFromVideoCallback+=number;
2680
+ }
2681
+ else
2682
+ {
2683
+ lastFrameFromVideoCallback+=1;
2684
+ }
2685
+ lastMediaTime = metadata.mediaTime;
2686
+
2687
+ let playerTime = video.currentTime;
2688
+ let DebugLine2 = document.getElementById("debugText2");
2689
+ var timeStr = "";
2690
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2691
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(lastFrameFromVideoCallback, sourceFPS,true),true,true);
2692
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2693
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(lastFrameFromVideoCallback, sourceFPS,false),false,false);
2694
+ }else{
2695
+ timeStr="NOT SUPPORTED";
2696
+ }
2697
+ var offsetStr = "";
2698
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2699
+ offsetStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(options.augmentation.offset, sourceFPS,true),true,true);
2700
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2701
+ offsetStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(options.augmentation.offset, sourceFPS,false),false,false);
2702
+ }else{
2703
+ offsetStr="NOT SUPPORTED";
2704
+ }
2705
+ let resultFrames = lastFrameFromVideoCallback - options.augmentation.offset;
2706
+ if (metaDataFetcher) metaDataFetcher.setLastFrame(resultFrames);
2707
+ var resultTimecodeStr = "";
2708
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2709
+ resultTimecodeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(resultFrames, sourceFPS,true),true,true);
2710
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2711
+ resultTimecodeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(resultFrames, sourceFPS,false),false,false);
2712
+ }else{
2713
+ resultTimecodeStr="NOT SUPPORTED";
2714
+ }
2715
+ let deltaVid = (Date.now()-lastVidDateTime);
2716
+
2717
+ let nowTime = new Date();
2718
+ let hours = String(nowTime.getHours()).padStart(2, '0');
2719
+ let minutes = String(nowTime.getMinutes()).padStart(2, '0');
2720
+ let seconds = String(nowTime.getSeconds()).padStart(2, '0');
2721
+ let milliseconds = String(nowTime.getMilliseconds()).padStart(3, '0');
2722
+ let formattedTime = `${hours}:${minutes}:${seconds}.${milliseconds}`;
2723
+
2724
+ if (options.debug) {
2725
+ if ((options.augmentation.offset != lastOffset) || (lastOffsetShown< 10*60))
2726
+ {
2727
+ // set something to print offset different color
2728
+ var offsetColor = "red";
2729
+ }
2730
+ else
2731
+ {
2732
+ var offsetColor = "black";
2733
+ }
2734
+ DebugLine2.innerHTML = "VID. "
2735
+ + " Game: " + options.gameId
2736
+ + ", Time Fired: " + formattedTime
2737
+ + ", PlayerTime:" + metadata.mediaTime.toFixed(4)
2738
+
2739
+
2740
+ + ", Stream Time-Code: " + timeStr
2741
+ //+ ", offset: " + options.augmentation.offset + "(" + offsetStr + ")"
2742
+ + ", offset: <span style='color:" + offsetColor + "'>" + options.augmentation.offset + "(" + offsetStr + ")</span>"
2743
+ + ", Brain Time-Code: " + resultTimecodeStr
2744
+ //+ ", count:" + countVid
2745
+ + ", TimeFromLastSync:" + timeFromLastSync.toFixed(4)
2746
+ + ", LastSyncPointTime:" + options.syncPoint.playerTime.toFixed(4)
2747
+ + ", LastSyncPointFrame:" + options.syncPoint.frame
2748
+ + ", LastSyncPoint:" + options.syncPoint.stringTime
2749
+ //+ ", deltaVid: " + deltaVid + " ms"
2750
+ //+ ", prevDeltaVid: " + prevDeltaVid + " ms"
2751
+ //+ ", prevprevDeltaVid: " + prevprevDeltaVid + " ms"
2752
+ //+ ", expected-now = " + (metadata.expectedDisplayTime - now).toFixed(4)
2753
+ + " </br>";
2754
+ if (options.augmentation.offset != lastOffset)
2755
+ {
2756
+ lastOffsetShown=0;
2757
+ }
2758
+ lastOffset = options.augmentation.offset;
2759
+ lastOffsetShown++;
2760
+ }
2761
+
2762
+ countVid++;
2763
+ lastVidDateTime = Date.now();
2764
+ let plus05Correction = false;
2765
+ if (options.debug) {
2766
+ var checkbox_plus05Correction = document.getElementById('plus05CorrectionCheckbox');
2767
+ plus05Correction = checkbox_plus05Correction.checked;
2768
+ }
2769
+ let renderingDuration = 0;
2770
+ //if ((metadata.expectedDisplayTime - now)>5)
2771
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2772
+ let bottomBorder = 24;
2773
+ let topBorder = 33+(33-bottomBorder);
2774
+ if (((deltaVid<bottomBorder)&&(prevDeltaVid<topBorder)) || ((prevDeltaVid<bottomBorder)&&(deltaVid<topBorder)) || ((prevprevDeltaVid<bottomBorder)&&(prevDeltaVid<topBorder)&&(deltaVid<topBorder)) || (plus05Correction==true))
2775
+ {
2776
+ DebugLine2.innerHTML += " Setting (some) ms timer";
2777
+ setTimeout(function() {
2778
+ render(resultFrames,false);
2779
+ //video.requestVideoFrameCallback(onFrameVideoWindows2);
2780
+ }, 10);
2781
+ }
2782
+ else
2783
+ {
2784
+ render(resultFrames,false);
2785
+ }
2786
+ }
2787
+ else if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001)
2788
+ {
2789
+ renderingDuration = render(resultFrames,false);
2790
+ }
2791
+ else
2792
+ {
2793
+ console.BBlog("FPS not supported");
2794
+ }
2795
+ prevprevDeltaVid=prevDeltaVid;
2796
+ prevDeltaVid=deltaVid;
2797
+ if (options.debug)
2798
+ {
2799
+ console.BBlog("VID."
2800
+ + " TimeFired: " + formattedTime
2801
+ + ", rendering: " + timeStr
2802
+ + ", took: " + renderingDuration.toFixed(4) + " ms"
2803
+ );
2804
+ }
2805
+ video.requestVideoFrameCallback(onFrameVideoWindows2);
2806
+ return;
2807
+ }
2808
+
2809
+ const onAnimatedFrameWindows2 = (timeStamp) => {
2810
+ let nowTime = new Date();
2811
+ let hours = String(nowTime.getHours()).padStart(2, '0');
2812
+ let minutes = String(nowTime.getMinutes()).padStart(2, '0');
2813
+ let seconds = String(nowTime.getSeconds()).padStart(2, '0');
2814
+ let milliseconds = String(nowTime.getMilliseconds()).padStart(3, '0');
2815
+ let formattedTime = `${hours}:${minutes}:${seconds}.${milliseconds}`;
2816
+ //console.log("ANI");
2817
+ if (video.paused) //doing nothing
2818
+ {
2819
+ window.requestAnimationFrame(onAnimatedFrameWindows2);
2820
+ return;
2821
+ }
2822
+ let DebugLine = document.getElementById("debugText");
2823
+ if (options.debug) DebugLine.innerHTML = "ANI. "
2824
+ + " Time Fired: " + formattedTime;
2825
+
2826
+ if ((lastCalled=="ANI") || (lastCalled=="ANI2"))
2827
+ {
2828
+
2829
+ var timeStr = "";
2830
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2831
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(lastVideoFrameRenderedInGL+1, sourceFPS,true),true,true);
2832
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2833
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(lastVideoFrameRenderedInGL+1, sourceFPS,false),false,false);
2834
+ }else{
2835
+ timeStr="NOT SUPPORTED";
2836
+ }
2837
+ var offsetStr = "";
2838
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2839
+ offsetStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(options.augmentation.offset, sourceFPS,true),true,true);
2840
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2841
+ offsetStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(options.augmentation.offset, sourceFPS,false),false,false);
2842
+ }else{
2843
+ offsetStr="NOT SUPPORTED";
2844
+ }
2845
+ let resultFrames = lastVideoFrameRenderedInGL - options.augmentation.offset;
2846
+ var resultTimecodeStr = "";
2847
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2848
+ resultTimecodeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(resultFrames, sourceFPS,true),true,true);
2849
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2850
+ resultTimecodeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(resultFrames, sourceFPS,false),false,false);
2851
+ }else{
2852
+ resultTimecodeStr="NOT SUPPORTED";
2853
+ }
2854
+ if (options.debug)
2855
+ {
2856
+ console.BBlog("Probable videoFrameCallback was not fired. Apllying correction mechanism (" + timeStr + ")" + ", stage: " + lastCalled);
2857
+ DebugLine.innerHTML += " correction";
2858
+ }
2859
+
2860
+ let renderingDuration = render(lastVideoFrameRenderedInGL+1,false);
2861
+ if (options.debug) console.BBlog("ANI."
2862
+ + " Time Fired: " + formattedTime
2863
+ + ", correction"
2864
+ + ", time: " + timeStr
2865
+ + ", offset: " + options.augmentation.offset + "(" + offsetStr + ")"
2866
+ + ", resultTime: " + resultTimecodeStr
2867
+ + ", took: " + renderingDuration.toFixed(4) + " ms");
2868
+ if (lastCalled=="ANI")
2869
+ lastCalled = "ANI2";
2870
+ else if (lastCalled=="ANI2")
2871
+ lastCalled = "ANI";
2872
+ }
2873
+ else
2874
+ {
2875
+ //DebugLine.innerHTML = "ANI. ";
2876
+ if (options.debug) console.BBlog("ANI."
2877
+ + " Time Fired: " + formattedTime);
2878
+ lastCalled = "ANI";
2879
+ }
2880
+
2881
+
2882
+
2883
+ window.requestAnimationFrame(onAnimatedFrameWindows2);
2884
+ }
2885
+
2886
+
2887
+
2888
+ //Apple://///////////////////////
2889
+ //default rounding error:
2890
+ let roundingError = options.frameRateMode == BB.FrameRateMode.Src59_MediaTime60 ? -0.04 : +0.05;
2891
+ //debug - delete it after
2892
+ let deltaCur=0;
2893
+ let lastMetadata={};
2894
+ let lastExpectedDisplayTime=0;
2895
+ let lastTimeStamp=0;
2896
+ let nowFromVideoFrame=0;
2897
+ //--------------
2898
+ let countAppleVideo = 0;
2899
+ let frame = 0;
2900
+ let lastMediaTime = -1;
2901
+
2902
+ let lastVIDDate=0;
2903
+ let forceSync=false;
2904
+
2905
+ let lastCorr=0;
2906
+ let prevCorr=0;
2907
+
2908
+
2909
+
2910
+
2911
+
2912
+ // const onFrameVideoApple3 = (now,metadata) => {
2913
+ // lastVIDDate=performance.now();
2914
+ // var frameDbl = metadata.mediaTime*mediaTimeFPS;
2915
+ // var frame = Math.floor(frameDbl);
2916
+ // var timeStr = "";
2917
+ // if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2918
+ // timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(frame, sourceFPS,true),true,true);
2919
+ // }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2920
+ // timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(frame, sourceFPS,false),false,false);
2921
+ // }else{
2922
+ // timeStr="NOT SUPPORTED";
2923
+ // }
2924
+ // let DebugLine2 = document.getElementById("debugText2");
2925
+ // DebugLine2.textContent = " dblFrame: " + frameDbl.toFixed(4)
2926
+ // + " Frame: " + frame
2927
+ // + " countAppleVideo: " + countAppleVideo
2928
+ // + " Time: " + timeStr;
2929
+
2930
+
2931
+
2932
+ // countAppleVideo++;
2933
+ // video.requestVideoFrameCallback(onFrameVideoApple3);
2934
+ // return;
2935
+ // }
2936
+ let correction=false;
2937
+ const onAnimatedFrameApple3 = (timeStamp) => {
2938
+ var number = 2;
2939
+ if (options.debug)
2940
+ {
2941
+ let DebugLine = document.getElementById("debugText");
2942
+ var numberInput = document.getElementById("numberInput").value;
2943
+ number = parseInt(numberInput);
2944
+ }
2945
+ var realFrame = 0;
2946
+
2947
+ var dblFrame = ((video.currentTime - options.syncPoint.playerTime)%(60*60*24))*mediaTimeFPS; // take time excluding days
2948
+ realFrame=Math.floor(dblFrame) + options.syncPoint.frame;
2949
+
2950
+ realFrame+=number;
2951
+ var fraction = dblFrame - Math.floor(dblFrame);
2952
+ if ((correction)||(fraction<0.226)) correction=true;
2953
+ if (fraction>0.8) correction=false;
2954
+ if(correction) realFrame--;
2955
+ var timeStr = "";
2956
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2957
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(realFrame, sourceFPS,true),true,true);
2958
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2959
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(realFrame, sourceFPS,false),false,false);
2960
+ }else{
2961
+ timeStr="NOT SUPPORTED";
2962
+ }
2963
+ var offsetStr = "";
2964
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
2965
+ offsetStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(options.augmentation.offset, sourceFPS,true),true,true);
2966
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
2967
+ offsetStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(options.augmentation.offset, sourceFPS,false),false,false);
2968
+ }else{
2969
+ offsetStr="NOT SUPPORTED";
2970
+ }
2971
+ let resultFrames = realFrame - options.augmentation.offset;
2972
+ // if (options.debug) DebugLine.textContent = " countAppleANI: " + countAppleANI
2973
+ // + " dblFrame: " + dblFrame.toFixed(4)
2974
+ // + " realFrame: " + realFrame
2975
+ // + ", offset: " + options.augmentation.offset + "(" + offsetStr + ")"
2976
+ // + ", resultTime: " + resultTimecodeStr
2977
+ // + " Time: " + timeStr
2978
+ // + " correction: " + correction;
2979
+
2980
+
2981
+ if (metaDataFetcher) metaDataFetcher.setLastFrame(resultFrames);
2982
+ render(resultFrames);
2983
+ countAppleANI++;
2984
+ // lastFrame=realFrame;
2985
+ // prevCorr=lastCorr;
2986
+ // lastCorr=corr;
2987
+
2988
+ window.requestAnimationFrame(onAnimatedFrameApple3);
2989
+ return;
2990
+ }
2991
+ //-----------------------------------------------
2992
+ const onFrameVideoApple = (now,metadata) => {
2993
+ //if((!playing)&&(lastMediaTime!=-1)&&(lastMediaTime!=metadata.mediaTime)) playing=true;
2994
+
2995
+ let frameDbl = metadata.mediaTime*mediaTimeFPS;
2996
+ var number = 0;
2997
+ if (options.debug)
2998
+ {
2999
+ var numberInput = document.getElementById("numberInput").value;
3000
+ number = parseInt(numberInput);
3001
+ }
3002
+ frameDbl+=number;
3003
+ frame = Math.floor(frameDbl);
3004
+ var timeStr = "";
3005
+ if (BB.Utils.getSourceFPS(options.frameRateMode) == 60000/1001){
3006
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(frame, sourceFPS,true),true,true);
3007
+ }else if (BB.Utils.getSourceFPS(options.frameRateMode) == 30000/1001){
3008
+ timeStr=tcTranslator.printTimecode(tcTranslator.Frames2Timecode(frame, sourceFPS,false),false,false);
3009
+ }else{
3010
+ timeStr="NOT SUPPORTED";
3011
+ }
3012
+ let DebugLine2 = document.getElementById("debugText2");
3013
+ DebugLine2.textContent = " dblFrame: " + frameDbl.toFixed(4)
3014
+ + " Frame: " + frame
3015
+ + " countAppleVideo: " + countAppleVideo
3016
+ + " Time: " + timeStr;
3017
+
3018
+
3019
+
3020
+ countAppleVideo++;
3021
+ lastMediaTime=metadata.mediaTime;
3022
+ video.requestVideoFrameCallback(onFrameVideoApple);
3023
+ return;
3024
+ nowFromVideoFrame=now;
3025
+ lastMetadata = metadata;
3026
+ /*if(fpsGetter.howMany()<500)
3027
+ {
3028
+ fpsGetter.addTime(metadata.mediaTime);
3029
+ if((fpsGetter.howMany()>100)&& (fpsGetter.howMany() % 100 == 0)){
3030
+ let probableFPS = fpsGetter.getProbableFPS();
3031
+ console.log("Setting new media FPS " + probableFPS);
3032
+ BB.Utils.setMediaFPS(probableFPS, options);
3033
+ }
3034
+ }*/
3035
+
3036
+ //calculate delta between mediatime and current time
3037
+ let playerTime = video.currentTime;
3038
+ let delta = ((playerTime - metadata.mediaTime)*1000);
3039
+ //deltaCalc.add(delta);
3040
+ deltaCur=delta;
3041
+ video.requestVideoFrameCallback(onFrameVideoApple);
3042
+
3043
+ }
3044
+
3045
+ let renderOnNextFrame = -1;
3046
+ let lastDisplayTimeDelay = 9999;
3047
+ let countUnderCorrection = 0;
3048
+ let countAppleANI = 0;
3049
+ let lastFrame=0;
3050
+
3051
+ console.BBlog("Setting Callbacks");
3052
+ console.BBlog("Checking window and methods");
3053
+ if (typeof window !== 'undefined') {
3054
+ console.BBlog('The window object is defined. This script is running in a browser environment.');
3055
+ } else {
3056
+ console.BBlog('The window object is not defined. This script might not be running in a browser environment.');
3057
+ }
3058
+
3059
+ if ('requestAnimationFrame' in window) {
3060
+ console.BBlog('requestAnimationFrame is supported');
3061
+ } else {
3062
+ console.BBlog('requestAnimationFrame is not supported');
3063
+ }
3064
+
3065
+ if (typeof video !== 'undefined') {
3066
+ console.BBlog('Video object is defined. This script is running in a browser environment.');
3067
+ } else {
3068
+ console.BBlog('Video object is not defined. This script might not be running in a browser environment.');
3069
+ }
3070
+
3071
+ if ('requestVideoFrameCallback' in video) {
3072
+ console.BBlog('requestVideoFrameCallback is supported');
3073
+ } else {
3074
+ console.BBlog('requestVideoFrameCallback is not supported');
3075
+ }
3076
+
3077
+ if (platform.isWindows() || platform.isAndroid() || platform.isChromeMac()) {
3078
+ window.requestAnimationFrame(onAnimatedFrameWindows2);
3079
+ if (video.readyState >= 2) {
3080
+ console.BBlog("video element is already loaded. Setting up requestVideoFrameCallback");
3081
+ video.requestVideoFrameCallback(onFrameVideoWindows2);
3082
+ }
3083
+ else{
3084
+ console.BBlog("video element is not yet loaded. Setting up loadedmetadata event to wait until it's loaded");
3085
+ video.addEventListener('loadedmetadata', () => {
3086
+ video.requestVideoFrameCallback(onFrameVideoWindows2);
3087
+ });
3088
+ }
3089
+ } else if (platform.isSafariMac()) {
3090
+ window.requestAnimationFrame(onAnimatedFrameApple3);
3091
+ //video.requestVideoFrameCallback(onFrameVideoApple3);
3092
+
3093
+ }else {
3094
+ console.BBlog("Platform not supported");
3095
+ }
3096
+ console.BBlog("CallBacks are set");
3097
+ console.BBlog("Calculating ScreenFrameRate");
3098
+
3099
+ function getAverageRefreshRate() {
3100
+ return new Promise((resolve) => {
3101
+ let lastFrameTime = 0;
3102
+ const refreshRates = [];
3103
+ const numSamples = 20;
3104
+
3105
+ function calculateRefreshRate(timestamp) {
3106
+ if (lastFrameTime) {
3107
+ const delta = timestamp - lastFrameTime;
3108
+ const refreshRate = 1000 / delta;
3109
+ if (!(refreshRate === undefined))
3110
+ {
3111
+ if ((refreshRate>0)&&(refreshRate<200))
3112
+ refreshRates.push(refreshRate);
3113
+ }
3114
+ if (refreshRates.length === numSamples) {
3115
+ const averageRate = refreshRates.reduce((a, b) => a + b, 0) / numSamples;
3116
+
3117
+ resolve(Math.round(averageRate));
3118
+ return; // Stop after collecting the required number of samples
3119
+ }
3120
+ }
3121
+ lastFrameTime = timestamp;
3122
+ requestAnimationFrame(calculateRefreshRate);
3123
+ }
3124
+
3125
+ requestAnimationFrame(calculateRefreshRate);
3126
+ });
3127
+ }
3128
+
3129
+ // Example usage:
3130
+ getAverageRefreshRate().then((rate) => {
3131
+ options.ScreenFPS = rate;
3132
+
3133
+ if ((options.ScreenFPS > 70) || (options.ScreenFPS < 50))
3134
+ {
3135
+ console.BBerror(`Average refresh screen rate: ${rate} FPS, Aborting augmentation. Only 60FPS screens supported`);
3136
+ options.stopAugment = true;
3137
+ }
3138
+ else
3139
+ {
3140
+ console.BBlog(`Average refresh screen rate: ${rate} FPS, Continuing augmentation`);
3141
+ }
3142
+
3143
+ });
3144
+ const initializeComponents = () => {
3145
+ //We load it all only when we know the game id
3146
+ let playerCont = video.parentElement;
3147
+ composer = new BB.Composer3d(playerCont,video,options);
3148
+ ads = BB.Ads(composer, options);
3149
+ metaDataFetcher = BB.MetaDataFetcher(options,composer);
3150
+ }
3151
+ const getCanvas = () => {
3152
+ return composer.getCanvas();
3153
+ }
3154
+ const onNewSegmentInterface = (eventObj) =>
3155
+ {
3156
+ onNewSegmentForMetadata(eventObj);
3157
+ };
3158
+ const onNewMetadataInterface = (eventObj) =>
3159
+ {
3160
+ onNewMetadata(eventObj);
3161
+ };
3162
+ return {getCanvas, onNewSegmentInterface, onNewMetadataInterface, initializeComponents};
3163
+ };
3164
+ BB.Composer3d = class {
3165
+ constructor(videoContainer,video,options) {
3166
+ if (options.augmentation.renderTimecode) {
3167
+ this.digitImg = {};
3168
+ for (let i = 0; i < 10; ++i) {
3169
+ let str1 = i + ".1";
3170
+ let str2 = i + ".2";
3171
+ this.digitImg[str1] = this.CreateDigitImg(str1);
3172
+ this.digitImg[str2] = this.CreateDigitImg(str2);
3173
+ }
3174
+ }
3175
+ this.options = Object.assign({adLoopTime:5000,isAdLoop: false},options || {});
3176
+ this.sourceFPS = BB.Utils.getSourceFPS(this.options.frameRateMode);
3177
+ this.metadataArr = [];
3178
+ this.metadataMap = {};
3179
+ this.videoStarted = false;
3180
+ this.advertisementMap = {};
3181
+ this.ad2Url = {};//map an ad to its url
3182
+ this.videoCopier = null;
3183
+ //create a webgl canvas:
3184
+ this.canvas = document.createElement('canvas');
3185
+ this.ctx = this.canvas.getContext('webgl2', {premultipliedAlpha: false});
3186
+ if (this.ctx == null)
3187
+ {
3188
+ this.ctx = this.canvas.getContext('webgl', {premultipliedAlpha: false});
3189
+ this.webGLVersion = 1;
3190
+ }
3191
+ else
3192
+ {
3193
+ this.webGLVersion = 2;
3194
+ }
3195
+ console.log("Created webgl1 context: " + this.ctx + " version " + this.webGLVersion);//@oror
3196
+
3197
+ //place it on top of the video player:
3198
+ let oTop = videoContainer.offsetTop;
3199
+ let oLeft = videoContainer.offsetLeft;
3200
+ let videoTag = videoContainer.getElementsByTagName("video")[0];
3201
+ this.canvas.style.top = 0;
3202
+ this.canvas.style.left = 0;
3203
+
3204
+ // if (typeof wmcPlayer !='undefined')
3205
+ // {
3206
+ // this.canvas.style.top = 0;
3207
+ // this.canvas.style.left = 0;
3208
+ // }
3209
+ // else
3210
+ // {
3211
+ // this.canvas.style.top = oTop + "px";
3212
+ // this.canvas.style.left = oLeft + "px";
3213
+ // }
3214
+
3215
+ // if (videoTag.readyState >= 2)
3216
+ // {
3217
+ // this.canvas.style.top = videoTag.style.top;
3218
+ // this.canvas.style.left = videoTag.style.left;
3219
+ // }
3220
+ // else
3221
+ // {
3222
+ // videoTag.addEventListener('loadedmetadata', () => {
3223
+ // this.canvas.style.top = videoTag.offsetTop + "px";
3224
+ // this.canvas.style.left = videoTag.offsetLeft + "px";
3225
+ // });
3226
+ // }
3227
+ this.canvas.style.width = "100%";
3228
+ this.canvas.style.zIndex = 100000;
3229
+ this.canvas.width = videoContainer.clientWidth;
3230
+ this.canvas.height = videoContainer.clientHeight;
3231
+ this.canvas.style.position = "absolute";
3232
+
3233
+ let nextElement = videoTag.nextSibling;
3234
+
3235
+ if(nextElement)
3236
+ videoContainer.insertBefore( this.canvas,nextElement);
3237
+ else
3238
+ videoContainer.appendChild(this.canvas);
3239
+ this.canvas.style["pointer-events"] = "none";
3240
+ videoTag.addEventListener("click", (e) => {
3241
+ this.OnUserClickOnCanvas(e);
3242
+ });
3243
+ //this.canvas.addEventListener("dblclick", this.ToggleFullscreen);
3244
+ //let that = this;
3245
+ //this.canvas.addEventListener("mousedown", function(e) {
3246
+ //that.OnUserClickOnCanvas(e);
3247
+ //});
3248
+ this.InitGL();
3249
+ this.adTexture = null;//general ad texture
3250
+ this.place2AdTexture = {};//mapping specific places to their texture
3251
+ this.place2ad = {};//mapping which ads belong to whcih place
3252
+ this.ad2place = {};//other way around
3253
+ this.currentAds2BoundingRect = [];//array of current-frame ads to their bounding rect
3254
+ this.lastTimeReportedIncomingTC = 0;
3255
+ this.autoAddIndex = null;
3256
+ }
3257
+
3258
+ isPowerOf2(value) {
3259
+ return (value & (value - 1)) == 0;
3260
+ }
3261
+ CreateDigitImg(str) {
3262
+ // Create a canvas element
3263
+ let canvas = document.createElement('canvas');
3264
+ canvas.width = 50; // Set the width of the canvas
3265
+ canvas.height = 50; // Set the height of the canvas
3266
+
3267
+ // Get the 2D rendering context
3268
+ let ctx = canvas.getContext('2d');
3269
+ // Set the fill style to black
3270
+ ctx.fillStyle = 'black';
3271
+
3272
+ // Fill the entire canvas with black
3273
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
3274
+
3275
+ // Set the font properties
3276
+ ctx.font = '30px Arial';
3277
+ ctx.fillStyle = 'white';
3278
+
3279
+ // Draw the number on the canvas
3280
+ ctx.fillText(str, 10, 35); // Adjust the position as needed
3281
+
3282
+
3283
+ // Convert the canvas to a data URL
3284
+ let dataURL = canvas.toDataURL();
3285
+
3286
+ // Create an image element and set its source to the data URL
3287
+ let img = document.createElement('img');
3288
+ img.src = dataURL;
3289
+
3290
+ return img;
3291
+ }
3292
+ OnUserClickOnCanvas(e) {
3293
+ const rect = this.canvas.getBoundingClientRect();
3294
+ let x = event.clientX - rect.left;
3295
+ let y = event.clientY - rect.top;
3296
+ x = x/(rect.right - rect.left);
3297
+ y = y/(rect.bottom - rect.top);
3298
+ //console.log("CANVAS CLICKED ! x: " + x + " y: " + y);
3299
+ for (let currentAd of this.currentAds2BoundingRect) {
3300
+ if (currentAd.rect.l <= x && currentAd.rect.r >= x &&
3301
+ currentAd.rect.t <= y && currentAd.rect.b >= y)
3302
+ {
3303
+ let adNameWithoutThatStupidHashSign = currentAd.name.substring(1);
3304
+ if (this.ad2Url[adNameWithoutThatStupidHashSign]) {
3305
+ let urlToGo = this.ad2Url[adNameWithoutThatStupidHashSign];
3306
+ console.log("Clicked on ad: " + adNameWithoutThatStupidHashSign + " so should navigate to " + urlToGo + " !!!!!!!!!!!!!!!!!!!!!!");
3307
+ window.open(urlToGo, '_blank');
3308
+ }
3309
+ return;
3310
+ }
3311
+ }
3312
+
3313
+ }
3314
+ /*TexUnitForInjectoLogo(){
3315
+ return 0;
3316
+ }*/
3317
+ TexUnitForGenericAd(){
3318
+ return 0;
3319
+ }
3320
+ TexUnitForAdInPlace(place){
3321
+ let placesArr = Object.keys(this.place2ad);
3322
+ for (let i = 0; i < placesArr.length; ++i)
3323
+ {
3324
+ if (place == placesArr[i]) return i + 1;
3325
+ }
3326
+ return 0;
3327
+ }
3328
+ /*TexUnitForVideo() {
3329
+ let placesArr = Object.keys(this.place2ad);
3330
+ return 1 + placesArr.length;
3331
+ }*/
3332
+ TexUnitForPlaceHolder(placeHolder) {
3333
+ let placesArr = Object.keys(this.place2ad);
3334
+ for (let i = 0; i < placesArr.length; ++i)
3335
+ {
3336
+ let place = placesArr[i];//wall
3337
+ if (placeHolder.name.indexOf(place) >= 0) return i;// + 1;
3338
+ }
3339
+ return 0;
3340
+
3341
+ }
3342
+ AdNameForPlaceHolder(placeHolder) {
3343
+ let placesArr = Object.keys(this.place2ad);
3344
+ for (let i = 0; i < placesArr.length; ++i)
3345
+ {
3346
+ let place = placesArr[i];//wall
3347
+ if (placeHolder.name.indexOf(place) >= 0 && this.place2AdTexture && this.place2AdTexture[place] && this.place2AdTexture[place].name)
3348
+ {
3349
+ return this.place2AdTexture[place].name;
3350
+ }
3351
+ }
3352
+ return this.lastAdName;
3353
+ }
3354
+ TexUnitBaseForAlpha() {
3355
+ return 1 + Object.keys(this.place2ad).length;// + 1;
3356
+ }
3357
+ InitGL() {
3358
+ // setup GLSL this.program
3359
+ let newFragmentShaderSource = BB.GL.fragmentShaderSource;
3360
+ //FIX for MAC (it has bugs with transparensy with light and bright colors so didabling transparensy if alpha in shader is less then 0.8)
3361
+ if (BB.Utils.Platform.isSafariMac())
3362
+ newFragmentShaderSource = BB.GL.fragmentShaderSource.replace("if (alpha == 0.0)", "if (alpha < 0.8)");
3363
+ this.program = webglUtils.createProgramFromSources(this.ctx,
3364
+ this.webGLVersion == 2 ? [BB.GL.vertexShaderSource, newFragmentShaderSource] :
3365
+ [BB.GL.vertexShaderSourceV1, BB.GL.fragmentShaderSourceV1]);
3366
+ this.ctx.clearColor(0.0, 0.0, 0.0, 0.0);//Set clear color to black, fully opaque
3367
+ this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);// Clear the color buffer with specified clear color
3368
+ this.ctx.useProgram(this.program);// Tell it to use our this.program (pair of shaders)
3369
+ this.counter = 0;
3370
+ this.CleanGL();
3371
+ }
3372
+ CleanGL() {
3373
+ this.canvas.style.display="none";
3374
+ this.alphaFound = 0;
3375
+ }
3376
+ GenerateTexture(
3377
+ imageData,
3378
+ textureUnit,
3379
+ uniformName,
3380
+ internalFormat,
3381
+ width,
3382
+ height,
3383
+ oneByteAlignment) {
3384
+ //void this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, ImageData source);
3385
+ if (oneByteAlignment) {
3386
+ this.ctx.pixelStorei(this.ctx.UNPACK_ALIGNMENT, 1);
3387
+ } else {
3388
+ this.ctx.pixelStorei(this.ctx.UNPACK_ALIGNMENT, 4);
3389
+ }
3390
+ this.ctx.pixelStorei(this.ctx.UNPACK_FLIP_Y_WEBGL, false);
3391
+ const texture = this.ctx.createTexture();
3392
+ this.ctx.activeTexture(this.ctx.TEXTURE0 + textureUnit);
3393
+ this.ctx.bindTexture(this.ctx.TEXTURE_2D, texture);
3394
+
3395
+ try {
3396
+ if (width && height) {
3397
+ this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, (internalFormat == null ? this.ctx.RGBA : internalFormat), width, height, 0, (internalFormat == null ? this.ctx.RGBA : internalFormat), this.ctx.UNSIGNED_BYTE, imageData);
3398
+ } else {
3399
+ this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, (internalFormat == null ? this.ctx.RGBA : internalFormat), this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, imageData);
3400
+ }
3401
+ } catch (ex) {
3402
+ console.error(ex);
3403
+ return null;
3404
+ }
3405
+ if (this.webGLVersion == 1)
3406
+ {
3407
+ let widthToInspect = width || imageData.width;
3408
+ let heightToInspect = height || imageData.height;
3409
+ if (widthToInspect && this.isPowerOf2(widthToInspect) && heightToInspect && this.isPowerOf2(heightToInspect)) {
3410
+ // Yes, it's a power of 2. Generate mips.
3411
+ this.ctx.generateMipmap(this.ctx.TEXTURE_2D);
3412
+ } else {
3413
+ // No, it's not a power of 2. Turn off mips and set
3414
+ // wrapping to clamp to edge
3415
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE);
3416
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE);
3417
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.LINEAR);
3418
+ }
3419
+ }
3420
+ else
3421
+ {
3422
+ // Set the parameters so we can render any size image.
3423
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE);
3424
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE);
3425
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.LINEAR);
3426
+ this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.LINEAR);
3427
+ }
3428
+ let location = this.ctx.getUniformLocation(this.program, uniformName);
3429
+ if (location) {
3430
+ this.ctx.uniform1i(location, textureUnit);
3431
+ }
3432
+ return texture;
3433
+ }
3434
+ _nextAdvertisement() {
3435
+ this.autoAddIndex = setTimeout(()=>{
3436
+ const ads = Object.keys(this.advertisementMap);
3437
+ if(ads.length){
3438
+ this.SelectAd(ads[Math.floor(Math.random() * ads.length)]);
3439
+ this._nextAdvertisement();
3440
+ }
3441
+ },this.options.adLoopTime);
3442
+ }
3443
+ AddAdvertisement(name, path, cb = null) {
3444
+
3445
+ if(name.indexOf(".json") > -1) {
3446
+ fetch(path).then(async res => {
3447
+ this.place2ad = await res.json();
3448
+ this.ad2place = {}
3449
+ //create also the reverse mapping
3450
+ let placesArr = Object.keys(this.place2ad);
3451
+ for (let i = 0; i < placesArr.length; ++i) {
3452
+ let place = placesArr[i];
3453
+ if (place == "urls")
3454
+ {
3455
+ this.ad2Url = this.place2ad[place];
3456
+ delete this.place2ad[place];
3457
+ continue;
3458
+ }
3459
+ let adsForThisPlace = this.place2ad[place];
3460
+ for (let j = 0; j < adsForThisPlace.length; ++j) {
3461
+ let ad = adsForThisPlace[j];
3462
+ this.ad2place["#" + ad] = place;
3463
+ }
3464
+ }
3465
+ if (Object.keys(this.place2ad).length > 0) {
3466
+ console.log("composer::AddAdvertisement: place-to-ads mapping: ", this.place2ad);
3467
+ console.log("composer::AddAdvertisement: ads-to-place mapping: ", this.ad2place);
3468
+ setTimeout(()=>{this.MakeSureAdPerPlace();}, 3000);//make sure its ad is in the right place once all ads are loaded
3469
+ }
3470
+
3471
+ });
3472
+ if (cb) cb();
3473
+ return;
3474
+ }
3475
+ const adImage = new Image();
3476
+ adImage.crossOrigin = "Anonymous";
3477
+ adImage.onload = () => {
3478
+ let isFirst = Object.keys(this.advertisementMap).length === 0;
3479
+ this.advertisementMap[name] = adImage;
3480
+ if (isFirst) {
3481
+ this.SelectAd(name);
3482
+ if(this.options.isAdLoop) {
3483
+ clearTimeout(this.autoAddIndex);
3484
+ this._nextAdvertisement();
3485
+ }
3486
+ }
3487
+ if (cb) cb();
3488
+ }
3489
+ adImage.onerror = () => {
3490
+ alert(`( Security ) We cant reach ${path} .`);
3491
+ }
3492
+ adImage.src = path;
3493
+ return adImage;
3494
+ }
3495
+ MakeSureAdPerPlace() {
3496
+ //in case we have places, make sure each place has its initial ad
3497
+ if (this.place2ad) {
3498
+ let placesArr = Object.keys(this.place2ad);
3499
+ for (let i = 0; i < placesArr.length; ++i) {
3500
+ let place = placesArr[i];
3501
+ //do we have a texture for that place ?
3502
+ if (!this.place2AdTexture[place] && this.place2ad[place].length > 0) {//no we don't, add one
3503
+ console.log("MakeSureAdPerPlace: Set default image " + this.place2ad[place][0] + " for place " + place);
3504
+ this.SelectAd("#" + this.place2ad[place][0]);
3505
+ }
3506
+ }
3507
+ }
3508
+ }
3509
+ SelectAd(name) {
3510
+ console.log("composer::SelectAd: selected ad is " + name);
3511
+ let adImage = this.advertisementMap[name];
3512
+ if (!adImage) {
3513
+ console.error("SelectAd: Failed to find image '" + name + "'");
3514
+ } else {
3515
+ //is it for specific place ?
3516
+ if (this.ad2place && this.ad2place[name]) {//ad for a particular place
3517
+ let place = this.ad2place[name];
3518
+ console.log("composer::SelectAd: The selected ad should be in place " + place);
3519
+ let texUnitId = this.TexUnitForAdInPlace(place);
3520
+ let texName = "ad" + (texUnitId-1);
3521
+ if (this.place2AdTexture[place]) this.ctx.deleteTexture(this.place2AdTexture[place].tex);
3522
+ this.place2AdTexture[place] =
3523
+ {
3524
+ tex:this.GenerateTexture(adImage, texUnitId, texName),
3525
+ texUnitId:texUnitId,
3526
+ texName:texName,
3527
+ name:name
3528
+ };
3529
+
3530
+ }
3531
+ else {//general ad
3532
+ if (this.adTexture != null) {
3533
+ this.ctx.deleteTexture(this.adTexture)
3534
+ }
3535
+ this.adTexture = this.GenerateTexture(adImage, this.TexUnitForGenericAd(), "ad0");
3536
+ this.lastAd = adImage;
3537
+ this.lastAdName = name;
3538
+ }
3539
+ }
3540
+ };
3541
+
3542
+ OnMetaData(frameMetaData) {
3543
+ //console.BBlog("OnMetaData " + frameMetaData.timecode);
3544
+ this.metadataMap[frameMetaData.timecode] = frameMetaData;
3545
+ this.metadataArr.push(frameMetaData.timecode);
3546
+ }
3547
+
3548
+ ToggleFullscreen () {
3549
+ (this.requestFullscreen || this.webkitRequestFullscreen).call(this).catch((err) => {
3550
+ console.error(err)
3551
+ });
3552
+ }
3553
+
3554
+ RenderFrame() {
3555
+ // look up where the vertex data needs to go.
3556
+ //let texCoordAttributeLocation = this.ctx.getAttribLocation(this.program, "a_texCoord");
3557
+ let positionAttributeLocation = this.ctx.getAttribLocation(this.program, "a_position");
3558
+
3559
+
3560
+ // set the resolution for both vertex and fragment shaders
3561
+ let resolutionLocation = this.ctx.getUniformLocation(this.program, "u_resolution");
3562
+ let resolutionLocation2 = this.ctx.getUniformLocation(this.program, "u_resolution2");
3563
+ this.ctx.uniform2f(resolutionLocation, this.ctx.canvas.width, this.ctx.canvas.height);
3564
+ this.ctx.uniform2f(resolutionLocation2, this.ctx.canvas.width, this.ctx.canvas.height);
3565
+
3566
+ //shader matrix (unity):
3567
+ if (this.webGLVersion == 2)
3568
+ {
3569
+ let matrixLocation = this.ctx.getUniformLocation(this.program, "u_matrix");
3570
+ let matrix = [
3571
+ 1, 0, 0,
3572
+ 0, 1, 0,
3573
+ 0, 0, 1
3574
+ ];
3575
+ this.ctx.uniformMatrix3fv(matrixLocation, false, matrix);
3576
+ }
3577
+
3578
+ let vao = null;
3579
+ if (this.webGLVersion == 2)
3580
+ {
3581
+ // Create a vertex array object (attribute state)
3582
+ vao = this.ctx.createVertexArray();
3583
+
3584
+ // and make it the one we're currently working with
3585
+ this.ctx.bindVertexArray(vao);
3586
+ }
3587
+
3588
+ // Create a buffer and put a single pixel space rectangle in
3589
+ // it (2 triangles)
3590
+ let positionBuffer = this.ctx.createBuffer();
3591
+
3592
+ // Turn on the attribute
3593
+ this.ctx.enableVertexAttribArray(positionAttributeLocation);
3594
+
3595
+ // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
3596
+ this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, positionBuffer);
3597
+
3598
+ // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
3599
+ let size = 2; // 2 components per iteration
3600
+ let type = this.ctx.FLOAT; // the data is 32bit floats
3601
+ let normalize = false; // don't normalize the data
3602
+ let stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
3603
+ let offset = 0; // start at the beginning of the buffer
3604
+ this.ctx.vertexAttribPointer(
3605
+ positionAttributeLocation, size, type, normalize, stride, offset);
3606
+
3607
+ // provide texture coordinates for the rectangle.
3608
+ let texCoordBuffer = this.ctx.createBuffer();
3609
+ this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, texCoordBuffer);
3610
+ this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array([
3611
+ 0.0, 0.0,
3612
+ 1.0, 0.0,
3613
+ 0.0, 1.0,
3614
+ 0.0, 1.0,
3615
+ 1.0, 0.0,
3616
+ 1.0, 1.0,
3617
+ ]), this.ctx.STATIC_DRAW);
3618
+
3619
+
3620
+ /*
3621
+ // Turn on the attribute
3622
+ this.ctx.enableVertexAttribArray(texCoordAttributeLocation);
3623
+
3624
+ // Tell the attribute how to get data out of texCoordBuffer (ARRAY_BUFFER)
3625
+ let size = 2; // 2 components per iteration
3626
+ let type = this.ctx.FLOAT; // the data is 32bit floats
3627
+ let normalize = false; // don't normalize the data
3628
+ let stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
3629
+ let offset = 0; // start at the beginning of the buffer
3630
+ this.ctx.vertexAttribPointer(
3631
+ texCoordAttributeLocation, size, type, normalize, stride, offset);
3632
+ */
3633
+ //webglUtils.resizeCanvasToDisplaySize(this.ctx.canvas);
3634
+
3635
+ // Tell WebGL how to convert from clip space to pixels
3636
+ this.ctx.viewport(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
3637
+
3638
+ // Clear the canvas
3639
+ this.ctx.clearColor(0, 0, 0, 0);
3640
+ this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT);
3641
+
3642
+ // Bind the attribute/buffer set we want.
3643
+ //this.ctx.bindVertexArray(vao);
3644
+
3645
+
3646
+ // Bind the position buffer so this.ctx.bufferData that will be called
3647
+ // in setRectangle puts data in the position buffer
3648
+ this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, positionBuffer);
3649
+
3650
+ // Set a rectangle the same size as the image.
3651
+ this.SetRectangle(this.ctx, 0, 0, this.canvas.width, this.canvas.height);
3652
+
3653
+ // Draw the rectangle.
3654
+ let primitiveType = this.ctx.TRIANGLES;
3655
+ //let offset = 0;
3656
+ let count = 6;
3657
+ this.ctx.drawArrays(primitiveType, offset, count);
3658
+ }
3659
+
3660
+ SetRectangle(gl, x, y, width, height) {
3661
+ let x1 = x;
3662
+ let x2 = x + width;
3663
+ let y1 = y;
3664
+ let y2 = y + height;
3665
+ this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array([
3666
+ x1, y1,
3667
+ x2, y1,
3668
+ x1, y2,
3669
+ x1, y2,
3670
+ x2, y1,
3671
+ x2, y2,
3672
+ ]), this.ctx.STATIC_DRAW);
3673
+ }
3674
+
3675
+ renderAugmentation(nextTimeCode) {
3676
+ let tcTranslator = BB.Utils.TimeCodesFramesTranslator;
3677
+ //console.log("nextTimeCode: " + nextTimeCode);//@oror "00:00:14:02.1"
3678
+ let tc = tcTranslator.timeCodeStr2TimeCodeArray(nextTimeCode);
3679
+ let tcPlus1 = tcTranslator.TimeCodeAddFrames(tc, 1, this.sourceFPS);
3680
+ let tcPlus2 = tcTranslator.TimeCodeAddFrames(tc, 2, this.sourceFPS);
3681
+ let tcPlus1Str = tcTranslator.printTimecode(tcPlus1);
3682
+ let tcPlus2Str = tcTranslator.printTimecode(tcPlus2);
3683
+
3684
+
3685
+ this.videoStarted = true;
3686
+ const t1 = performance.now();
3687
+
3688
+
3689
+ //clean all the earliest meta:
3690
+ /*
3691
+ while (this.metadataArr.length > 0 &&
3692
+ this.metadataArr[0] < nextTimeCode) {
3693
+ let toDeleteFrameMetaData = this.metadataMap[this.metadataArr[0]];
3694
+ for (const key in toDeleteFrameMetaData.placeholders) {
3695
+ let holderToDelete = toDeleteFrameMetaData.placeholders[key];
3696
+ if (holderToDelete.alphaAsUint8Array) {
3697
+ holderToDelete.alphaAsUint8Array = null;
3698
+ }
3699
+ }
3700
+ delete this.metadataArr[this.metadataArr[0]];
3701
+ this.metadataArr.shift();
3702
+ }
3703
+ */
3704
+
3705
+ //get the next meta frame
3706
+ let frameMetaData = null;
3707
+ let next = [];
3708
+ if (this.metadataMap[nextTimeCode]) {
3709
+ frameMetaData = this.metadataMap[nextTimeCode];
3710
+ /*delete this.metadataMap[nextTimeCode];
3711
+ if (this.sourceFPS < 40) {
3712
+ delete this.metadataMap[nextTimeCode + ".1"];
3713
+ }*/
3714
+
3715
+ if (this.metadataMap[tcPlus1Str]) {
3716
+ next.push({nextVideo:this.metadataMap[tcPlus1Str],nextTimeCode:tcPlus1Str});
3717
+ }
3718
+ if (this.metadataMap[tcPlus2Str]) {
3719
+ next.push({nextVideo:this.metadataMap[tcPlus2Str],nextTimeCode:tcPlus2Str});
3720
+ }
3721
+ const metaArrIndex = this.metadataArr.indexOf(nextTimeCode);
3722
+
3723
+ //if (metaArrIndex > -1) this.metadataArr.splice(metaArrIndex, 1); //was
3724
+ if (metaArrIndex > -5) this.metadataArr.splice(0, metaArrIndex-4).forEach(item => {
3725
+ delete this.metadataMap[item];
3726
+ });
3727
+ this.alphaFound++;
3728
+ }
3729
+ else {
3730
+ this.counter++;
3731
+ if(this.counter > 60)
3732
+ {
3733
+ this.counter = 0;
3734
+ console.error("Missing meta data at time code " + nextTimeCode);
3735
+ }
3736
+ this.alphaFound++; //test for drawing number always
3737
+ //this.alphaFound = 0;
3738
+ ////nextImageData.buffer = null;
3739
+ //return;
3740
+ }
3741
+ if (this.alphaFound > 30 && this.canvas.style.display == "none") {
3742
+ this.canvas.style.display = "";
3743
+ }
3744
+
3745
+ let texturesForThisFrame = [];
3746
+ this.canvas.width = 1280;
3747
+ this.canvas.height = 720;
3748
+
3749
+ let placeHolderIndex = 0;
3750
+ this.currentAds2BoundingRect = [];//reset the coordinates
3751
+ if (frameMetaData) {
3752
+ //set the canvas size according the engine's canvas size
3753
+ // if (frameMetaData.canvasSize && (
3754
+ // frameMetaData.canvasSize.width != this.ctx.canvas.width ||
3755
+ // frameMetaData.canvasSize.height != this.ctx.canvas.height)) {
3756
+ // this.ctx.canvas.width = frameMetaData.canvasSize.width;
3757
+ // this.ctx.canvas.height = frameMetaData.canvasSize.height;
3758
+ // }
3759
+
3760
+ //for each place-holder, create an alpha texture and inform the shader of the alpha mate's position and size
3761
+ //if (false)
3762
+ let doNotRenderAugmentation = false;
3763
+ if (this.options.debug)
3764
+ {
3765
+ var checkbox_doNotRenderAugmentation = document.getElementById('doNotRenderAugmentationCheckbox');
3766
+ doNotRenderAugmentation = checkbox_doNotRenderAugmentation.checked;
3767
+ }
3768
+ if(!doNotRenderAugmentation)
3769
+ for (const key in frameMetaData.placeholders) {
3770
+ const holder = frameMetaData.placeholders[key];
3771
+ if (!holder.alphaAsUint8Array) continue;
3772
+ //create the alpha texture
3773
+ let alphaUnfiromName = "alpha" + placeHolderIndex;
3774
+ let alphaTextureNum = this.TexUnitBaseForAlpha() + placeHolderIndex;
3775
+ holder.alphaTex = this.GenerateTexture(holder.alphaAsUint8Array,
3776
+ alphaTextureNum,
3777
+ alphaUnfiromName,
3778
+ this.ctx.ALPHA,
3779
+ holder.width,
3780
+ holder.height,
3781
+ true);
3782
+ holder.alphaAsUint8Array = null;
3783
+ if (!holder.alphaTex) {
3784
+ console.error("Failed to create alpha texture");
3785
+ continue;
3786
+ }
3787
+ texturesForThisFrame.push(holder.alphaTex);
3788
+ //notify the shader that this place-holder exist
3789
+ let hasLogoUniformName = "hasLogo" + placeHolderIndex;
3790
+ let hasLogoLoc = this.ctx.getUniformLocation(this.program, hasLogoUniformName);
3791
+ this.ctx.uniform1i(hasLogoLoc, 1);
3792
+ //tell the shader whcih add to use for this placeholder
3793
+ let adForLogoUniformName = "adForLogo" + placeHolderIndex;
3794
+ let adForLogoUniform = this.ctx.getUniformLocation(this.program, adForLogoUniformName);
3795
+ let isBlur = false;
3796
+ if (holder.name.indexOf("blur") >= 0)
3797
+ {
3798
+ this.ctx.uniform1i(adForLogoUniform, 99);
3799
+ isBlur = true;
3800
+ }
3801
+ else
3802
+ {
3803
+ this.ctx.uniform1i(adForLogoUniform, this.TexUnitForPlaceHolder(holder));
3804
+ }
3805
+
3806
+ //set that alpha top-left and size
3807
+ let alphaTopLeftUniformName = "u_alpha" + placeHolderIndex + "TopLeft";
3808
+ let alphaTopLeftLoc = this.ctx.getUniformLocation(this.program, alphaTopLeftUniformName);
3809
+ this.ctx.uniform2fv(alphaTopLeftLoc, [holder.maskX, holder.maskY]);
3810
+ let alphaSizeUniformName = "u_alpha" + placeHolderIndex + "Size";
3811
+ let alphaSizeLoc = this.ctx.getUniformLocation(this.program, alphaSizeUniformName);
3812
+ this.ctx.uniform2fv(alphaSizeLoc, [holder.width, holder.height]);
3813
+ //homography:
3814
+ let homographyMatrixUniformName = "u_matrix" + placeHolderIndex;
3815
+ let placeHolderHomographyLocation = this.ctx.getUniformLocation(this.program, homographyMatrixUniformName);
3816
+ this.ctx.uniformMatrix3fv(placeHolderHomographyLocation, false, holder.tranform);
3817
+ //bounding rect:
3818
+ if (!isBlur)
3819
+ {
3820
+ let boundingRect = {l:-1,t:-1,r:-1,b:-1};
3821
+ for (let corner of holder.corners) {
3822
+ if (corner.x < boundingRect.l ||boundingRect.l == -1) boundingRect.l = corner.x;
3823
+ if (corner.x > boundingRect.r ||boundingRect.r == -1) boundingRect.r = corner.x;
3824
+ if (corner.y < boundingRect.t ||boundingRect.t == -1) boundingRect.t = corner.y;
3825
+ if (corner.y > boundingRect.b ||boundingRect.b == -1) boundingRect.b = corner.y;
3826
+ }
3827
+ boundingRect.l /= frameMetaData.canvasSize.width;
3828
+ boundingRect.r /= frameMetaData.canvasSize.width;
3829
+ boundingRect.t /= frameMetaData.canvasSize.height;
3830
+ boundingRect.b /= frameMetaData.canvasSize.height;
3831
+ this.currentAds2BoundingRect.push({
3832
+ name:this.AdNameForPlaceHolder(holder),
3833
+ rect:boundingRect
3834
+ });
3835
+ }
3836
+ /*
3837
+ //This code create the perspective transform in JS
3838
+ //Currently we use the transformation supplied with the frameMetaData instead
3839
+ let srcCorners = [
3840
+ 0, 0,
3841
+ this.canvas.width, 0,
3842
+ this.canvas.width, this.canvas.height,
3843
+ 0, this.canvas.height];
3844
+
3845
+ let dstCorners = [];
3846
+ let ratioX = this.canvas.width / frameMetaData.processingWidth;
3847
+ let ratioY = this.canvas.height / frameMetaData.processingHeight;
3848
+ for (let corner of holder.corners) {
3849
+ corner.x *= ratioX;
3850
+ corner.y *= ratioY;
3851
+ dstCorners[dstCorners.length] = corner.x;
3852
+ dstCorners[dstCorners.length] = corner.y;
3853
+ }
3854
+
3855
+ let perspT = PerspT(srcCorners, dstCorners);
3856
+ this.ctx.uniformMatrix3fv(placeHolderHomographyLocation, false, perspT.coeffsInv);
3857
+ */
3858
+ placeHolderIndex++;
3859
+ }
3860
+ } else {
3861
+ // console.log(`No data at ${tc}`);
3862
+ }
3863
+
3864
+ //mark the other logo as "not exist"
3865
+ for (; placeHolderIndex < BB.MAX_LOGOS; ++placeHolderIndex) {
3866
+ let hasLogoUniformName = "hasLogo" + placeHolderIndex;
3867
+ let hasLogoLoc = this.ctx.getUniformLocation(this.program, hasLogoUniformName);
3868
+ this.ctx.uniform1i(hasLogoLoc, 0);
3869
+ //unbind the textures that are not required
3870
+ let alphaUnfiromName = "alpha" + placeHolderIndex;
3871
+ let location = this.ctx.getUniformLocation(this.program, alphaUnfiromName);
3872
+ this.ctx.uniform1i(location, 0);
3873
+ }
3874
+ //render the timecode on the canvas (for debug)
3875
+ if (this.options.augmentation.renderTimecode)
3876
+ {{
3877
+ let framesAndField = nextTimeCode.split(":")[3];
3878
+ let field = (framesAndField.indexOf(".") >= 0) ? 2 : 1;
3879
+ let frameLastDigit = parseInt(framesAndField.split(".")[0])%10;
3880
+ let debugImgStr = frameLastDigit + "." + field;//"1.1";
3881
+ //console.log("render " + debugImgStr + " tc = " + nextTimeCode + " scedueled on frame " + debug_lastFrameFromVideoCallback + "(" + debug_tc + ")");
3882
+ let digitTextureNum = this.TexUnitBaseForAlpha() + 5;
3883
+ let digitTex = this.GenerateTexture(
3884
+ this.digitImg[debugImgStr],
3885
+ digitTextureNum,
3886
+ "digitTex");
3887
+ texturesForThisFrame.push(digitTex);
3888
+ let hasDigitUniformName = "hasDigit";
3889
+ let hasDigitLoc = this.ctx.getUniformLocation(this.program, hasDigitUniformName);
3890
+ this.ctx.uniform1i(hasDigitLoc, 1);
3891
+ //set that digit's top-left and size
3892
+ let digitTopLeftUniformName = "u_digitTopLeft";
3893
+ let digitTopLeftLoc = this.ctx.getUniformLocation(this.program, digitTopLeftUniformName);
3894
+ this.ctx.uniform2fv(digitTopLeftLoc, [0, 0]);
3895
+
3896
+ let digitSizeUniformName = "u_digitSize";
3897
+ let digitSizeLoc = this.ctx.getUniformLocation(this.program, digitSizeUniformName);
3898
+ this.ctx.uniform2fv(digitSizeLoc, [200, 200]);
3899
+
3900
+ //let digitTopLeftUniformName2 = "u_digitTopLeft2";
3901
+ //let digitTopLeftLoc2 = this.ctx.getUniformLocation(this.program, digitTopLeftUniformName2);
3902
+ //this.ctx.uniform2fv(digitTopLeftLoc2, [1000, 0]);
3903
+
3904
+
3905
+ }
3906
+ /*{
3907
+ let framesAndField = nextTimeCode.split(":")[3];
3908
+ let field = (framesAndField.indexOf(".") >= 0) ? 2 : 1;
3909
+ let frameLastDigit = parseInt(framesAndField.split(".")[0])%10;
3910
+ let debugImgStr = frameLastDigit + "." + field;//"1.1";
3911
+ //console.log("render " + debugImgStr + " tc = " + nextTimeCode + " scedueled on frame " + debug_lastFrameFromVideoCallback + "(" + debug_tc + ")");
3912
+ let digitTextureNum = this.TexUnitBaseForAlpha() + 5;
3913
+ let digitTex = this.GenerateTexture(
3914
+ this.digitImg[debugImgStr],
3915
+ digitTextureNum,
3916
+ "digitTex");
3917
+ texturesForThisFrame.push(digitTex);
3918
+ let hasDigitUniformName = "hasDigit";
3919
+ let hasDigitLoc = this.ctx.getUniformLocation(this.program, hasDigitUniformName);
3920
+ this.ctx.uniform1i(hasDigitLoc, 1);
3921
+ //set that digit's top-left and size
3922
+ let digitTopLeftUniformName = "u_digitTopLeft2";
3923
+ let digitTopLeftLoc2 = this.ctx.getUniformLocation(this.program, digitTopLeftUniformName);
3924
+ this.ctx.uniform2fv(digitTopLeftLoc2, [1000, 0]);
3925
+ let digitSizeUniformName = "u_digitSize";
3926
+ let digitSizeLoc = this.ctx.getUniformLocation(this.program, digitSizeUniformName);
3927
+ this.ctx.uniform2fv(digitSizeLoc, [200, 200]);
3928
+ }*/
3929
+ /*
3930
+ let adForLogoUniformName = "adForLogo" + placeHolderIndex;
3931
+ let adForLogoUniform = this.ctx.getUniformLocation(this.program, adForLogoUniformName);
3932
+ let isBlur = false;
3933
+ if (holder.name.indexOf("blur") >= 0)
3934
+ {
3935
+ this.ctx.uniform1i(adForLogoUniform, 99);
3936
+ isBlur = true;
3937
+ }
3938
+ else
3939
+ {
3940
+ this.ctx.uniform1i(adForLogoUniform, this.TexUnitForPlaceHolder(holder));
3941
+ }
3942
+ */
3943
+ }
3944
+ else //no digit
3945
+ {
3946
+ let hasDigitUniformName = "hasDigit";
3947
+ let hasDigitLoc = this.ctx.getUniformLocation(this.program, hasDigitUniformName);
3948
+ this.ctx.uniform1i(hasDigitLoc, 0);
3949
+ }
3950
+
3951
+ //render the frame
3952
+ this.RenderFrame();
3953
+
3954
+ //cleanup:
3955
+ //delete the texture created for this frame
3956
+ for (const texture of texturesForThisFrame) {
3957
+ this.ctx.deleteTexture(texture);
3958
+ }
3959
+
3960
+ }
3961
+ getCanvas() {
3962
+ return this.canvas;
3963
+ }
3964
+ }
3965
+ BB.Ads = (composer, options) => {
3966
+ let selectedAdsList = [];
3967
+ const socket = new BB.Socket((type, message) => {
3968
+ switch (type) {
3969
+ case "roommembers":
3970
+ console.log("roommembers message: ", message);
3971
+ break;
3972
+ case "join":
3973
+ const name = document.getElementById("name");
3974
+ if(name){
3975
+ name.textContent = socket.name;
3976
+ }
3977
+ break;
3978
+ case "messageroom":
3979
+ if (message.player === socket.name) {
3980
+ if (message.url) {
3981
+ const name = "#external";
3982
+ composer.AddAdvertisement(name, message.url, () => {
3983
+ composer.SelectAd(name);
3984
+ });
3985
+ } else {
3986
+ selectedAdsList = message.ads;
3987
+ if (selectedAdsList.length) {
3988
+ composer.SelectAd(selectedAdsList[0]);
3989
+ }
3990
+ }
3991
+ }
3992
+ break;
3993
+ case "ads":
3994
+ selectedAdsList = message;
3995
+ if (selectedAdsList.length) {
3996
+ composer.SelectAd(selectedAdsList[0]);
3997
+ }
3998
+ break;
3999
+ }
4000
+ });
4001
+
4002
+ var script = document.createElement('script');
4003
+ script.src = 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js';
4004
+ document.getElementsByTagName('head')[0].appendChild(script);
4005
+ script.onload = function() {
4006
+ socket.Connect(options.augmentation.channelName);
4007
+ }
4008
+
4009
+ //load the ads for this projects:
4010
+ if (options.augmentation.logoURL) {
4011
+ composer.AddAdvertisement(`#ad`, options.augmentation.logoURL);
4012
+ }
4013
+ else //get logo according to the channel's S3 bucket
4014
+ {
4015
+ fetch("https://www.virtualott.com/ads/" + options.augmentation.channelName).then(async (res) => {
4016
+ const list = await res.json();
4017
+ list.forEach(photo => {
4018
+ composer.AddAdvertisement(`#${photo.name}`, photo.url);
4019
+ });
4020
+ });
4021
+ }
4022
+
4023
+ };
4024
+ BB.MetaDataFetcher = (options, composer) => {
4025
+ let tcTranslator = BB.Utils.TimeCodesFramesTranslator;
4026
+ let sourceFPS = BB.Utils.getSourceFPS(options.frameRateMode);
4027
+ let folderName;
4028
+ let lastFrames;
4029
+ let metadataWorker = null;
4030
+ let overlayWorker = null;
4031
+ console.log("Project name: " + options.gameId);
4032
+ const setMetaDataFolderName = (_folderName) => {
4033
+ folderName = _folderName;
4034
+ console.log("Folder name for " + options.gameId + ": " + folderName);
4035
+ if (lastFrames) {
4036
+ loadMetaData(lastFrames);
4037
+ }
4038
+ };
4039
+ async function getMetaDataFolderName() {
4040
+ const res = await fetch(options.allGamesURL);//get all the games
4041
+ const list = await res.json();//extract the games' list
4042
+ //if (typeof list[options.gameId] != "undefined") {//see if we found the current project
4043
+ // setMetaDataFolderName(list[options.gameId].folder);//set the project's folder
4044
+ //} else {
4045
+ // setTimeout(async ()=>{
4046
+ // await getMetaDataFolderName();//Didn't find it ? Don't give up, try again after 2 secs
4047
+ // },2000);
4048
+ //}
4049
+ setMetaDataFolderName(options.gameId);//set the project's folder
4050
+ }
4051
+ getMetaDataFolderName();
4052
+ const setLastFrame = (framesNum) => {
4053
+ lastFrames = framesNum;
4054
+ if (folderName) {
4055
+ loadMetaData(lastFrames);
4056
+ }
4057
+ }
4058
+ const loadMetaData = (lastFrames) => {
4059
+ //console.BBlog("loadMetaData");
4060
+ if (metadataWorker == null) {
4061
+ console.BBlog("We have no MetaDataDownloaderThread yet. Creating it");
4062
+ let blob = new Blob([META_WORKER], { type: 'application/javascript' });
4063
+ metadataWorker = new Worker(URL.createObjectURL(blob));
4064
+ // Handle errors from the worker
4065
+ metadataWorker.onerror = function(error) {
4066
+ console.error('Error from worker:', error);
4067
+ };
4068
+
4069
+ // Optionally handle the onerror event using addEventListener
4070
+ metadataWorker.addEventListener('error', function(error) {
4071
+ console.error('Error from worker (via addEventListener):', error.message);
4072
+ });
4073
+ if (metadataWorker) console.BBlog("MetaDataDownloaderThread created");
4074
+ else console.BBlog("Error in creating MetaDataDownloaderThread");
4075
+ metadataWorker.addEventListener("message", (msg) => {
4076
+ if (msg.data.time) {
4077
+ //Debug("prof",msg.data.time);
4078
+ } else {
4079
+ //console.BBlog("loaded MetaData");
4080
+ composer.OnMetaData(msg.data);//send to composer
4081
+ }
4082
+ });
4083
+ //get the last timecode with 2-sec round-down
4084
+ let timeCode = tcTranslator.TimeCodeArray2TimeCodeJson(tcTranslator.Frames2Timecode(lastFrames,sourceFPS,true));
4085
+ metadataWorker.postMessage({cloudFrontURL:options.cloudFrontURL, folderName:folderName, tc: tcTranslator.printTimecode(timeCode, false)});
4086
+ }
4087
+ else //the metadata worker is already in action, let it know the current playing data so the max-timecode is updated accordingly
4088
+ {
4089
+ let timeCode = tcTranslator.TimeCodeArray2TimeCodeJson(tcTranslator.Frames2Timecode(lastFrames,sourceFPS,true));
4090
+ metadataWorker.postMessage({tc: tcTranslator.printTimecode(timeCode, false),enabled:options.enabled});
4091
+ }
4092
+ }
4093
+
4094
+ return {
4095
+ setLastFrame:setLastFrame
4096
+ };
4097
+ }
4098
+ BB.OTTPlayer = class {
4099
+ constructor(video, srcFrameRate, optionsFromMK){
4100
+ //first, generate the inital options:
4101
+ this.video = video;
4102
+ this.srcFrameRate = srcFrameRate;
4103
+ console.log("optionsFromMK: ", optionsFromMK);
4104
+ this.options = Object.assign({
4105
+ enabled:true,
4106
+ stopAugment:false, //setted to true when there is exception or smt went wrong
4107
+ gameId:"Unknown",
4108
+ medialook:false,
4109
+ allGamesURL:"https://www.virtualott.com/games/",
4110
+ cloudFrontURL:"https://db8igqxlbvayn.cloudfront.net/prod",
4111
+ mediaIdJsonURL: "https://games-prod.virtualott.com/map",
4112
+ offsetJsonURL: "https://games-prod.virtualott.com/offset",
4113
+ frameRateMode:BB.FrameRateMode.Src59_MediaTime60,
4114
+ renderingMode:BB.RenderingMode.Augment, //MUST, other will not work in this test
4115
+ SynchMethod:BB.SynchMethod.None,
4116
+ debug:false,
4117
+ augmentation: {
4118
+ renderTimecode:true,
4119
+ offset:0
4120
+ },
4121
+ syncPoint: {
4122
+ frame:0,
4123
+ playerTime:0,
4124
+ mediaTimeMS:0
4125
+ }
4126
+ }, optionsFromMK || {});
4127
+ console.log("options after merge with MK's options: ", this.options);
4128
+ this.options.augmentation.channelName = "nba";
4129
+ console.BBlog = function(message) {
4130
+ console.log("OTTPlayer: " + message);
4131
+ };
4132
+ console.BBerror = function(message) {
4133
+ console.error("OTTPlayer: " + message);
4134
+ };
4135
+ //this.options.debug = true; //alal//
4136
+ function compareVersions(version1, version2) {
4137
+
4138
+ const v1 = version1.split('.').map(Number);
4139
+ const v2 = version2.split('.').map(Number);
4140
+
4141
+ for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
4142
+ const num1 = v1[i] || 0;
4143
+ const num2 = v2[i] || 0;
4144
+
4145
+ if (num1 < num2) {
4146
+ return -1;
4147
+ } else if (num1 > num2) {
4148
+ return 1;
4149
+ }
4150
+ }
4151
+
4152
+ return 0;
4153
+ }
4154
+ this.getGlobalConfig();
4155
+
4156
+
4157
+ }
4158
+ //after we merged the options with the url-param global config
4159
+ init() {
4160
+ //override with global options:
4161
+ if (typeof globalConfig === 'undefined')
4162
+ this.options = Object.assign(this.options, {});
4163
+ else
4164
+ this.options = Object.assign(this.options, globalConfig);
4165
+ this.options.augmentation.offset = 0;
4166
+ console.log("options after merge with the global options: ", this.options);
4167
+ //this.options.debug = true; //alal
4168
+ //if the debug option is off - make the debug-div disappear
4169
+ if (!this.options.debug) {
4170
+ let elementsToHide = ["debugText","forplus","forDoNotRender","debugText2","toggleBtn","numberInput","plus05CorrectionCheckbox","doNotRenderAugmentationCheckbox", "plus05CorrectionLabel", "doNotRenderAugmentationLabel", "br1","br2"];
4171
+ for (let i = 0; i < elementsToHide.length; i++) {
4172
+ const elemToHide = document.getElementById(elementsToHide[i]);
4173
+ if (elemToHide) {
4174
+ elemToHide.style.display = "none";
4175
+ }
4176
+ }
4177
+ }
4178
+
4179
+ let platformDetector = BB.Utils.Platform;
4180
+ let tcTranslator = BB.Utils.TimeCodesFramesTranslator;
4181
+ if (this.srcFrameRate) this.SetFps(this.srcFrameRate);
4182
+ this.fetchAndFindGameIdAndOffsetFromMediaId(this.options.gameId);//this will fetch the right game id and the right
4183
+
4184
+ }
4185
+ weHaveTheGameID() {
4186
+ //we need to have this.option.offset updated at this stage so the augmentor will get the right offset
4187
+ let line = {};
4188
+ this.frameDrawer = BB.Augmenter(line, this.video, this.options);
4189
+ if (this.options.renderingMode == BB.RenderingMode.Augment) {
4190
+ //we need the game id to connect to the right bucket
4191
+ this.frameDrawer.initializeComponents();
4192
+ }
4193
+ }
4194
+ async getGlobalConfig() {
4195
+ //make sure we have global config
4196
+ const URLParams = new URL(document.location);
4197
+ const CONFIG_NAME = URLParams.searchParams.get("config") || "";
4198
+ if (CONFIG_NAME !== "")
4199
+ {
4200
+ try {
4201
+ const response = await fetch(CONFIG_NAME);
4202
+ if (!response.ok) {
4203
+ throw new Error('Network response was not ok');
4204
+ }
4205
+ const configJson = await response.json();
4206
+ console.log("Got the following config: ", configJson);
4207
+ if (typeof globalConfig== 'undefined') window.globalConfig = {};
4208
+ globalConfig = Object.assign({}, globalConfig, configJson);
4209
+
4210
+
4211
+ } catch (error) {
4212
+ console.error('Error fetching JSON:', error);
4213
+ }
4214
+ }
4215
+ this.init();
4216
+ }
4217
+
4218
+ // Function to fetch the JSON and find the value for mediaID
4219
+ async fetchAndFindGameIdAndOffsetFromMediaId(mediaID) {
4220
+ try {
4221
+ const response = await fetch(this.options.mediaIdJsonURL+ "/" + mediaID);
4222
+ if (!response.ok) {
4223
+ throw new Error(`HTTP error! Status: ${response.status}`);
4224
+ }
4225
+
4226
+ const jsonData = await response.json();
4227
+
4228
+ if (jsonData.hasOwnProperty("GAME_ID")) {
4229
+ const value = jsonData["GAME_ID"];
4230
+ console.BBlog(`GameId for mediaid= ${mediaID}: ${value}`);
4231
+ this.options.gameId = value;
4232
+ console.log("fetchAndFindGameIdAndOffsetFromMediaId: I Know what is the game id ! Its " + this.options.gameId + ", so I'm launching the meta-data fetcher web-worker");
4233
+ this.weHaveTheGameID();
4234
+
4235
+ //metaDataFetcher = BB.MetaDataFetcher(options,composer);
4236
+ this.fetchOffsetForGameId(this.options.gameId);
4237
+
4238
+ } else {
4239
+ console.BBerror(`MediaID ${mediaID} not found in JSON.`);
4240
+ this.options.stopAugment = true;
4241
+ }
4242
+ } catch (error) {
4243
+ console.BBerror('Error fetching and processing GameId JSON:', error);
4244
+ //this.options.stopAugment = true;
4245
+ }
4246
+ }
4247
+
4248
+ // Function to fetch the JSON and find the value for mediaID
4249
+ async fetchOffsetForGameId(gameId) {
4250
+ try {
4251
+ const response = await fetch(this.options.offsetJsonURL + "/" + this.options.gameId);
4252
+
4253
+ if (!response.ok) {
4254
+ console.BBlog(`HTTP error! Status: ${response.status}`);
4255
+ setTimeout(() => {
4256
+ this.fetchOffsetForGameId(gameId);
4257
+ }, 5000);
4258
+ }
4259
+
4260
+ const jsonData = await response.json();
4261
+
4262
+ if ((jsonData.hasOwnProperty("offset")) && (jsonData.hasOwnProperty("found"))) {
4263
+ const value = jsonData["offset"];
4264
+ const found =jsonData["found"];
4265
+
4266
+ if (found == true)
4267
+ {
4268
+ console.BBlog(`Offest for gameid= ${gameId}: ${value}`);
4269
+ this.options.augmentation.offset = value;
4270
+ //call get-offset again later on
4271
+ setTimeout(() => {
4272
+ this.fetchOffsetForGameId(gameId);
4273
+ }, 60000);
4274
+ }
4275
+ } else {
4276
+ console.BBlog(`offset for ${mediaID} not found in JSON.`);
4277
+ setTimeout(() => {
4278
+ this.fetchOffsetForGameId(gameId);
4279
+ }, 5000);
4280
+ }
4281
+ } catch (error) {
4282
+ console.BBerror('Error fetching and processing JSON:', error);
4283
+ setTimeout(() => {
4284
+ this.fetchOffsetForGameId(gameId);
4285
+ }, 5000);
4286
+
4287
+ }
4288
+ }
4289
+
4290
+ Toggle (flag) {
4291
+ this.options.enabled = flag;
4292
+ }
4293
+ GetCanvas(){
4294
+ return this.frameDrawer.getCanvas();
4295
+ }
4296
+ SetFps(fps){
4297
+ let platformDetector = BB.Utils.Platform;
4298
+ switch(fps)
4299
+ {
4300
+ case 60:
4301
+ this.options.frameRateMode = BB.FrameRateMode.Src60_MediaTime60;
4302
+ break;
4303
+ case 60000/1001:
4304
+ case 59.94:
4305
+ if (platformDetector.isWindows() || platformDetector.isAndroid() || platformDetector.isChromeMac()) {
4306
+ this.options.frameRateMode = BB.FrameRateMode.Src59_MediaTime59;
4307
+ }
4308
+ else { //we are playing on MAC Safary 59.94 source HLS(only) in 59.99 mediaTimeFPS //only for HLS
4309
+ //options.frameRateMode = BB.FrameRateMode.Src59_MediaTime60;
4310
+ this.options.frameRateMode = BB.FrameRateMode.Src59_MediaTime59;
4311
+ }
4312
+ break;
4313
+ case 30000/1001:
4314
+ case 29.97:
4315
+ this.options.frameRateMode = BB.FrameRateMode.Src29_MediaTime29;
4316
+ break;
4317
+ default:
4318
+ console.error("Unsupported src frame-rate: " + fps + "fps");
4319
+ break;
4320
+ }
4321
+ }
4322
+ SetSyncPoint(syncPoint)
4323
+ {
4324
+ this.options.syncPoint = syncPoint;
4325
+
4326
+ }
4327
+ onNewSegment(eventObj)
4328
+ {
4329
+ this.frameDrawer.onNewSegmentInterface(eventObj);
4330
+ }
4331
+ onNewMetadata(eventObj)
4332
+ {
4333
+ this.frameDrawer.onNewMetadataInterface(eventObj);
4334
+ }
4335
+ }