mirror of https://github.com/OpenVidu/openvidu.git
Compare commits
2274 Commits
v2.17.0-be
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
f92ee9b886 | |
|
|
68d855a245 | |
|
|
72e7469012 | |
|
|
622a2f6707 | |
|
|
72697bafa5 | |
|
|
4fb4878342 | |
|
|
57e76fe69b | |
|
|
e1f16a6179 | |
|
|
9e9684c4db | |
|
|
efada4c166 | |
|
|
3fc0193260 | |
|
|
61a3589dd7 | |
|
|
8d1c2468f5 | |
|
|
020413257f | |
|
|
6c78abdcc0 | |
|
|
e31a78d153 | |
|
|
b1d0269211 | |
|
|
00fcb0b115 | |
|
|
4bf351b2df | |
|
|
e9ecceeb77 | |
|
|
413dec3e0f | |
|
|
414c26c31b | |
|
|
fce026766b | |
|
|
fe3f90d266 | |
|
|
5f6b404576 | |
|
|
9b8348bc04 | |
|
|
1403d062e9 | |
|
|
7bf0e0036c | |
|
|
76c957903f | |
|
|
68ea8001f1 | |
|
|
5a249fc3e1 | |
|
|
9bac0f6490 | |
|
|
fa664c97f1 | |
|
|
8e218ade3c | |
|
|
22af5c7df6 | |
|
|
04b8b741e2 | |
|
|
8af9f75a10 | |
|
|
fabeaf1471 | |
|
|
1ffd7ea9d6 | |
|
|
3af490522e | |
|
|
0280b64084 | |
|
|
83aad06574 | |
|
|
892c6efed2 | |
|
|
5b888aafc0 | |
|
|
d918e8059a | |
|
|
82ddca6b50 | |
|
|
6fb7d9583c | |
|
|
181c5f0789 | |
|
|
e486665efd | |
|
|
b659400c88 | |
|
|
98c7e3f751 | |
|
|
5e1df8b511 | |
|
|
16ec1f3920 | |
|
|
b01e8f4d23 | |
|
|
2413a0bb6d | |
|
|
bf6091e997 | |
|
|
9e0034dfac | |
|
|
637142cec6 | |
|
|
c304c9c761 | |
|
|
4dd007395f | |
|
|
7573656060 | |
|
|
8407363aaf | |
|
|
55fd64c254 | |
|
|
d151834048 | |
|
|
ce47224400 | |
|
|
61fbf9850b | |
|
|
ba1df4660c | |
|
|
d44e24592d | |
|
|
91aa127dad | |
|
|
1be876678c | |
|
|
388981be31 | |
|
|
6497751375 | |
|
|
5f0639c157 | |
|
|
e435a1a937 | |
|
|
932eda8115 | |
|
|
5d91f4d343 | |
|
|
703698b25f | |
|
|
b884e924b6 | |
|
|
ee8847c5cb | |
|
|
99c787c4f5 | |
|
|
2083c078fd | |
|
|
bc42a72836 | |
|
|
c3b7c6f4bb | |
|
|
b913dbb3b8 | |
|
|
f16eefa9df | |
|
|
96132553ae | |
|
|
709779b7fd | |
|
|
11ac3d32eb | |
|
|
c8bbcfed56 | |
|
|
76a6f6c301 | |
|
|
2de2920acf | |
|
|
fd8be9f23f | |
|
|
e66e5a23e1 | |
|
|
335fd8e3c3 | |
|
|
237ebe1d59 | |
|
|
5e5e404d7d | |
|
|
16e869c5da | |
|
|
c628b2ab68 | |
|
|
b7d9f822de | |
|
|
fe606cbf15 | |
|
|
7c71f41d95 | |
|
|
9728d966ad | |
|
|
ce610cab03 | |
|
|
d8f14c6905 | |
|
|
bdf4f07a28 | |
|
|
206a51baf7 | |
|
|
94b51b9971 | |
|
|
8984cfdcad | |
|
|
022d18578e | |
|
|
9beb8b435a | |
|
|
26b790bfd8 | |
|
|
308315dee4 | |
|
|
5fac396487 | |
|
|
99e36178a5 | |
|
|
8999076291 | |
|
|
220c6d7d4a | |
|
|
f989dc26d2 | |
|
|
3107d504ce | |
|
|
b8c0bb3283 | |
|
|
4aa8739ee8 | |
|
|
bcfcfdd704 | |
|
|
a59872c9e9 | |
|
|
cbf4bafa36 | |
|
|
95584d7437 | |
|
|
20e348ed60 | |
|
|
fbcc460f10 | |
|
|
e7e1a39e05 | |
|
|
18fd65350c | |
|
|
1551bf8244 | |
|
|
882f9405db | |
|
|
af3608c81c | |
|
|
395d0c3ade | |
|
|
f0bcb3ac03 | |
|
|
3579b9dc6a | |
|
|
dbb45eaffd | |
|
|
fc7d7fef5a | |
|
|
3f234276ee | |
|
|
b10bc5f8c6 | |
|
|
a9e727fd51 | |
|
|
c9b350eddc | |
|
|
21c297c73b | |
|
|
7df7b31d97 | |
|
|
d8d4870eb7 | |
|
|
aa15e8b713 | |
|
|
621df40a7f | |
|
|
147f7078a0 | |
|
|
55a67f191a | |
|
|
31587d7181 | |
|
|
d0f714a5e2 | |
|
|
e4ed447009 | |
|
|
57ea002953 | |
|
|
1b5a230132 | |
|
|
7348402364 | |
|
|
915a4598b1 | |
|
|
0b017b2abb | |
|
|
c12dbf247c | |
|
|
f087c172fc | |
|
|
fe3e99e4e1 | |
|
|
442b99771a | |
|
|
5f9fb06c2a | |
|
|
dfae82d4cf | |
|
|
549a822491 | |
|
|
b6f242107e | |
|
|
9023f03cee | |
|
|
500b19d1b9 | |
|
|
421c5ea29c | |
|
|
cea8ce0def | |
|
|
daf1349e75 | |
|
|
ffbc1f5e5d | |
|
|
a02e23763a | |
|
|
c275381cfd | |
|
|
34eb27dfe3 | |
|
|
d3702ab6ac | |
|
|
dafba8cdee | |
|
|
995ce8c00b | |
|
|
1e15fea5e4 | |
|
|
3139437bfe | |
|
|
59112b79fa | |
|
|
fd486270b9 | |
|
|
042f9b5a89 | |
|
|
28e96647fa | |
|
|
35eacb45a1 | |
|
|
c42108bd22 | |
|
|
63588148a7 | |
|
|
52d253a1db | |
|
|
bf7d83134a | |
|
|
77bba7e587 | |
|
|
fb999df526 | |
|
|
314a3d1898 | |
|
|
35f1085870 | |
|
|
cfdaabfc0b | |
|
|
146aeed893 | |
|
|
02b594e405 | |
|
|
bd3504b818 | |
|
|
743b05911c | |
|
|
1b2af77196 | |
|
|
1f13b11f88 | |
|
|
1cba6761bb | |
|
|
920bba1bf3 | |
|
|
1d3cba9517 | |
|
|
331a102a19 | |
|
|
6fe461dc0c | |
|
|
baf3da51c2 | |
|
|
48084544ba | |
|
|
6a02f96af2 | |
|
|
3c72bc6c4d | |
|
|
3027ab6c5b | |
|
|
ffc6bb4c41 | |
|
|
5df42d5344 | |
|
|
4ad8c52c38 | |
|
|
741954ac2b | |
|
|
75d6576603 | |
|
|
8a9e9e0d27 | |
|
|
1065f32a3a | |
|
|
223c7473e5 | |
|
|
f951eddfe5 | |
|
|
8dfb513bb0 | |
|
|
ab7a453507 | |
|
|
5b112286d9 | |
|
|
c4c5fb9866 | |
|
|
bd711b57f3 | |
|
|
94f2e3e573 | |
|
|
b7a94f4b03 | |
|
|
21b94bc4fd | |
|
|
745e04baa6 | |
|
|
4b611893e9 | |
|
|
b8fc003a4c | |
|
|
ed579b4e72 | |
|
|
d8ce736cc0 | |
|
|
cb8f11449f | |
|
|
efb691f481 | |
|
|
a8c2459d5f | |
|
|
0074b28d3a | |
|
|
760cf604eb | |
|
|
8ce155df6a | |
|
|
cff617b0c3 | |
|
|
127fbbd4e1 | |
|
|
3adfa91c54 | |
|
|
7f00318cbb | |
|
|
5433f516a9 | |
|
|
81fcae2d4e | |
|
|
0c1e1a7134 | |
|
|
518e1511d2 | |
|
|
b92630ecd8 | |
|
|
11137a2a8f | |
|
|
9310ff3ea1 | |
|
|
37428c91b6 | |
|
|
9e04d59b61 | |
|
|
c1f2971881 | |
|
|
8c28228357 | |
|
|
90fd0ef44e | |
|
|
6137bdbbbc | |
|
|
2acf636842 | |
|
|
0ad51d6c58 | |
|
|
6f97b9d8c2 | |
|
|
a64cf1d577 | |
|
|
e373d23cc9 | |
|
|
72888e4ea9 | |
|
|
2bf212ccfe | |
|
|
5fba250b1d | |
|
|
7315360fbc | |
|
|
d965e72822 | |
|
|
288a585809 | |
|
|
272eb9002c | |
|
|
dee470609c | |
|
|
ad8f368a91 | |
|
|
5d855a1338 | |
|
|
3defad20cc | |
|
|
9b4f330c4a | |
|
|
7d10ec6097 | |
|
|
8d443ac506 | |
|
|
1521a766e5 | |
|
|
a1edded5df | |
|
|
fa507d492f | |
|
|
0ae67e8a87 | |
|
|
051b14c5b2 | |
|
|
f610f37ac3 | |
|
|
501881c602 | |
|
|
ea8f0e2101 | |
|
|
d3520e9098 | |
|
|
b29b9102a9 | |
|
|
64fbaa2a42 | |
|
|
4c4380a87f | |
|
|
d8452c42ad | |
|
|
09ba39642e | |
|
|
11c6f2f965 | |
|
|
9a01c0e179 | |
|
|
06fc88f2b0 | |
|
|
f19a9129ba | |
|
|
f785dfd7ad | |
|
|
8185324a0a | |
|
|
88214676a8 | |
|
|
9f9eda5ce4 | |
|
|
75bb35d00a | |
|
|
d4006421e9 | |
|
|
0fe47c4b13 | |
|
|
5c97301c6d | |
|
|
de5f67dd3c | |
|
|
da108a4031 | |
|
|
5fb3be503c | |
|
|
6ea42e82c0 | |
|
|
6e1d9c7ee7 | |
|
|
4cdb761737 | |
|
|
51e3abd483 | |
|
|
77f23512a6 | |
|
|
f2cb2fb252 | |
|
|
d3b8214dcb | |
|
|
98a4b1b2cb | |
|
|
9114f4fcc6 | |
|
|
2ea61d05a1 | |
|
|
c82d7315ef | |
|
|
6f3475fd94 | |
|
|
7de722714e | |
|
|
d2a59f1193 | |
|
|
36df6348d6 | |
|
|
fa0392a0a3 | |
|
|
b829142caf | |
|
|
dead4853aa | |
|
|
d320cb2bdd | |
|
|
07ba255beb | |
|
|
5e4cbadff1 | |
|
|
ee876987c1 | |
|
|
0fd0b6c11c | |
|
|
01d01f3d00 | |
|
|
363d352c79 | |
|
|
40f6bd790d | |
|
|
a01b1e2a2f | |
|
|
c2d7f01310 | |
|
|
5e0af77913 | |
|
|
40ffd97aca | |
|
|
09132835fe | |
|
|
8c18243c88 | |
|
|
01530fef32 | |
|
|
d1cca5efac | |
|
|
d7f1d859a0 | |
|
|
235d586d0f | |
|
|
57be57d892 | |
|
|
a4b010faba | |
|
|
18343fa47d | |
|
|
738431b6bf | |
|
|
d156db6051 | |
|
|
e1634efe39 | |
|
|
ee79e39d4b | |
|
|
47ccaf58c7 | |
|
|
0a3129e818 | |
|
|
baa753ac21 | |
|
|
6a7ab9b026 | |
|
|
b59c56c3e8 | |
|
|
02e3fb0d4e | |
|
|
ada5984a32 | |
|
|
1d91d6fa9d | |
|
|
ed2fa45d59 | |
|
|
57d92873da | |
|
|
897404b54b | |
|
|
c53b1b5b81 | |
|
|
083adb1775 | |
|
|
b74f340042 | |
|
|
23ac1c3b00 | |
|
|
cbc8a6b859 | |
|
|
9f4bcc827e | |
|
|
333436a90e | |
|
|
2132d5b984 | |
|
|
7ff73ac225 | |
|
|
ee57064095 | |
|
|
28f2405cc6 | |
|
|
7f2660abd9 | |
|
|
5a0aea9ecb | |
|
|
5a40ce9056 | |
|
|
f309527c26 | |
|
|
52422fb58a | |
|
|
9a7a2e3adb | |
|
|
c273ae0109 | |
|
|
f19432af8e | |
|
|
9238becfe3 | |
|
|
8e12739c97 | |
|
|
d19f0f7bb5 | |
|
|
6bb5937b23 | |
|
|
3d60920447 | |
|
|
0ce0e2acde | |
|
|
4354561d5a | |
|
|
88baf99368 | |
|
|
fdc3b82122 | |
|
|
e307af24e3 | |
|
|
71598c2d7d | |
|
|
28a0574d99 | |
|
|
7336f4f609 | |
|
|
3d164c6422 | |
|
|
72c5aa97ad | |
|
|
c3b2b4663f | |
|
|
f7d6e81261 | |
|
|
8d99c376f7 | |
|
|
d37f094773 | |
|
|
5bacf37523 | |
|
|
888f0bdb7e | |
|
|
7e7b6d2915 | |
|
|
bd6ba55504 | |
|
|
724fbd9186 | |
|
|
95b8d055ed | |
|
|
0a6dd252af | |
|
|
a82b2189bc | |
|
|
9f0246db2f | |
|
|
64f62dd716 | |
|
|
33d71f24c3 | |
|
|
7c1066d126 | |
|
|
5bf5c68da4 | |
|
|
33d11926a7 | |
|
|
6604d6aa1f | |
|
|
1b93b1a551 | |
|
|
822ef1ad09 | |
|
|
2d6ab64d00 | |
|
|
4c9e949133 | |
|
|
31dd06c7e8 | |
|
|
17a6edbe91 | |
|
|
9fd1c5b746 | |
|
|
5072804e67 | |
|
|
7b560b447c | |
|
|
d52dc9b6cf | |
|
|
b2e50aa53b | |
|
|
5fa86f4f4c | |
|
|
c61bed5a75 | |
|
|
27644e8fa5 | |
|
|
9f7efd8ad5 | |
|
|
a7e5f27eb0 | |
|
|
52564b5f5d | |
|
|
3f1334b65d | |
|
|
110cbc130f | |
|
|
5097dc925e | |
|
|
551bc76ba1 | |
|
|
25ef634757 | |
|
|
aa437be8cf | |
|
|
d02b12141b | |
|
|
f28652b425 | |
|
|
fc17b3bf9e | |
|
|
d8033dcde4 | |
|
|
d6d828d3ea | |
|
|
db28c79ee8 | |
|
|
7a7a9d6582 | |
|
|
eccc37156a | |
|
|
d9bc9b3700 | |
|
|
db2748367c | |
|
|
9fcc593fb2 | |
|
|
027ad510dc | |
|
|
5f51f7446f | |
|
|
649a2efdab | |
|
|
07e4ede64d | |
|
|
5dd689adc6 | |
|
|
40c19cedca | |
|
|
6ec78e5522 | |
|
|
0b89603e1b | |
|
|
82264d436e | |
|
|
81fa53b5bf | |
|
|
30c705e8fc | |
|
|
6698ae2314 | |
|
|
dc759dffd4 | |
|
|
2493d3c1c8 | |
|
|
fdbd1bdc12 | |
|
|
dfd614a634 | |
|
|
86c4abacb1 | |
|
|
e709e63655 | |
|
|
86f0d18417 | |
|
|
e965315ab6 | |
|
|
08a5950c22 | |
|
|
01298af7a6 | |
|
|
f66da6f5b4 | |
|
|
d65d2cf00e | |
|
|
8ff9c7c635 | |
|
|
d293d69571 | |
|
|
6221c92bae | |
|
|
f2ab607c82 | |
|
|
c2ac2384d9 | |
|
|
f756e48d4c | |
|
|
cc44e743a8 | |
|
|
86320a024e | |
|
|
b970463325 | |
|
|
c0bcb95282 | |
|
|
52c6126b8d | |
|
|
46ba88cf9a | |
|
|
c1688c0d2a | |
|
|
d1630e9b82 | |
|
|
0db1eda057 | |
|
|
2adbdd9160 | |
|
|
218a9b5c9f | |
|
|
f89fd1e2cd | |
|
|
795046b827 | |
|
|
cfd9417ee1 | |
|
|
004ab046be | |
|
|
ce74823006 | |
|
|
762f5edf31 | |
|
|
d81e116e0d | |
|
|
2a44f20294 | |
|
|
49e2e87f69 | |
|
|
b92548c504 | |
|
|
ce869751f1 | |
|
|
f40dfa5466 | |
|
|
78d17defa9 | |
|
|
aac79ef80f | |
|
|
4face2e556 | |
|
|
69b41a1406 | |
|
|
8c18f4b366 | |
|
|
44eb9e9fe0 | |
|
|
a830033388 | |
|
|
d9d524b23c | |
|
|
48d60eab40 | |
|
|
7802b068be | |
|
|
875c8536cc | |
|
|
809ba26b35 | |
|
|
96af7d3225 | |
|
|
98b783e968 | |
|
|
8d1a4720bb | |
|
|
c57d2b2753 | |
|
|
01d602633b | |
|
|
8edae4e81d | |
|
|
626e2183c5 | |
|
|
bb2c63cb5b | |
|
|
c8ff947a4f | |
|
|
1fb201375e | |
|
|
2e07e25df7 | |
|
|
1e416325b6 | |
|
|
55728b7235 | |
|
|
50a07ed203 | |
|
|
ff8605ef85 | |
|
|
ea846ac415 | |
|
|
a14f8d997d | |
|
|
da5f55e34b | |
|
|
39323c73a3 | |
|
|
775a7bd436 | |
|
|
d2423b012d | |
|
|
ad96776571 | |
|
|
c8fd2090f2 | |
|
|
beecb101e2 | |
|
|
4384a74896 | |
|
|
d29254e56a | |
|
|
796a478010 | |
|
|
ac64aeb2be | |
|
|
ec2396d904 | |
|
|
6ae3186c1e | |
|
|
96907f2c7f | |
|
|
276722176b | |
|
|
8a4cc402fd | |
|
|
a30d3c5c9e | |
|
|
a87086fb6c | |
|
|
c24ad580de | |
|
|
169df4373b | |
|
|
41658de781 | |
|
|
4fccf86109 | |
|
|
a8acfae4a8 | |
|
|
0497ccf439 | |
|
|
f57c1de443 | |
|
|
287ec58bab | |
|
|
93e48ce139 | |
|
|
087d5384c8 | |
|
|
cb07252181 | |
|
|
bbfc579161 | |
|
|
2f2001490a | |
|
|
566e52f1dd | |
|
|
918bf0ae6d | |
|
|
c683d989a6 | |
|
|
2d320df71a | |
|
|
5bc4d20ae1 | |
|
|
9a6a4cbf25 | |
|
|
6bf48078a5 | |
|
|
a44da5f043 | |
|
|
0b57843e84 | |
|
|
cc402f34d3 | |
|
|
efcc15a109 | |
|
|
554fa142fc | |
|
|
16c417e8e4 | |
|
|
ca19a1235d | |
|
|
9e7443ae7f | |
|
|
4ab66e30a1 | |
|
|
8dcf0096ee | |
|
|
2f797ca54f | |
|
|
1235b3ef40 | |
|
|
9bbfc9855a | |
|
|
7bd56abd9c | |
|
|
601f4bcfed | |
|
|
ca0c9abeb9 | |
|
|
70513750da | |
|
|
a8dcd8f3cc | |
|
|
e59b055ec6 | |
|
|
ae694cc8e8 | |
|
|
cd7f8ae282 | |
|
|
759b7b2a70 | |
|
|
a1420849b4 | |
|
|
63e273129b | |
|
|
734bc2710d | |
|
|
0cff58edb7 | |
|
|
7c6ba2b584 | |
|
|
ddf2d936bb | |
|
|
41920219c3 | |
|
|
bb72abe9c4 | |
|
|
946be88928 | |
|
|
fb6df7773a | |
|
|
936287ae83 | |
|
|
d212f52f04 | |
|
|
22ab5641f7 | |
|
|
e70fe4c3ab | |
|
|
130f49d16b | |
|
|
6bfe05f306 | |
|
|
12a34ca7cb | |
|
|
3f2e7c711e | |
|
|
f8812e615b | |
|
|
34d514118a | |
|
|
fb057a5823 | |
|
|
197b46f212 | |
|
|
c52f632d7c | |
|
|
55db788f50 | |
|
|
d78774b218 | |
|
|
749948a6d9 | |
|
|
eb5984befe | |
|
|
504a9a915e | |
|
|
3175b5d328 | |
|
|
c6520133db | |
|
|
b74ae7a6ab | |
|
|
d83604b92a | |
|
|
b013d851da | |
|
|
fd0d23563e | |
|
|
1f6f151ce2 | |
|
|
f20a2a8001 | |
|
|
7348ce6535 | |
|
|
9bcb55d4d8 | |
|
|
87f43537d2 | |
|
|
02b1af2093 | |
|
|
0bd8a6f38d | |
|
|
6f691e8bb1 | |
|
|
4f13e5181b | |
|
|
c1f3b22bc9 | |
|
|
b81c0dfccc | |
|
|
de0ea61eb4 | |
|
|
c1cec2c07d | |
|
|
76ec592865 | |
|
|
0a952839c7 | |
|
|
912cdee47f | |
|
|
7b1f411db4 | |
|
|
58c15b0e3c | |
|
|
ba0e736da9 | |
|
|
423e0d302f | |
|
|
b4b808e3f1 | |
|
|
be7634c821 | |
|
|
c0fa49af8d | |
|
|
72b95741ca | |
|
|
90d13cedc4 | |
|
|
90acd8682c | |
|
|
4e50796547 | |
|
|
2726fa6a67 | |
|
|
4dc98e3272 | |
|
|
75c9576f4d | |
|
|
9f8cc36a9d | |
|
|
61273ad642 | |
|
|
c891a26e4b | |
|
|
6aa7a4106c | |
|
|
83b14c39d6 | |
|
|
f922c5cd53 | |
|
|
fb101bc2c7 | |
|
|
9be838308c | |
|
|
c7f1bbcd93 | |
|
|
6479382f58 | |
|
|
026352133a | |
|
|
1152a79354 | |
|
|
b40f120efa | |
|
|
f5e18fc11d | |
|
|
fffbdd3cd5 | |
|
|
5f8fa08fe0 | |
|
|
d7e7cfe6f7 | |
|
|
f7b0f8e803 | |
|
|
35a6e823b6 | |
|
|
c646f66aab | |
|
|
7529de5897 | |
|
|
19f9bee16e | |
|
|
82fe3dc986 | |
|
|
0677268516 | |
|
|
4a11b3678b | |
|
|
1c675aa664 | |
|
|
2ca5e2362e | |
|
|
979fd14ccc | |
|
|
af31380bbd | |
|
|
805c1bdb47 | |
|
|
1d68cbeaf8 | |
|
|
de9f195fd5 | |
|
|
1edc3c055b | |
|
|
898d12e695 | |
|
|
91fd7c09ef | |
|
|
4620645ae2 | |
|
|
0c6f798692 | |
|
|
5298a085cb | |
|
|
82371ba499 | |
|
|
a0e6b137cc | |
|
|
16ec1c9480 | |
|
|
1fe005c6af | |
|
|
5d2e78238f | |
|
|
83e9ab7021 | |
|
|
7060a0b06f | |
|
|
0296dcbd8e | |
|
|
ec807d3944 | |
|
|
dade445ce7 | |
|
|
3ae582700c | |
|
|
60bddcebda | |
|
|
7fa65661f9 | |
|
|
5eb47d7401 | |
|
|
6f6a6ad7e8 | |
|
|
bb2a1f6c39 | |
|
|
11e5888747 | |
|
|
868835bb91 | |
|
|
4ed42cee1d | |
|
|
1943d2f2e6 | |
|
|
4ad79c7bcb | |
|
|
ca0df78bc4 | |
|
|
f52673a8c4 | |
|
|
6e826fcbc3 | |
|
|
09d89b2cdb | |
|
|
92048dcf7a | |
|
|
1a6a998fea | |
|
|
da32a74ff9 | |
|
|
d996e41258 | |
|
|
9080159736 | |
|
|
e482181644 | |
|
|
8d522bb136 | |
|
|
fdcf14c9ae | |
|
|
09c53bb86f | |
|
|
855eceab23 | |
|
|
47e6ed01a5 | |
|
|
94bb69c418 | |
|
|
c4dfbdb419 | |
|
|
9fabff5355 | |
|
|
e8b75cd44d | |
|
|
81cc7450f5 | |
|
|
ab55e4d046 | |
|
|
11129e25b8 | |
|
|
9af84c2d54 | |
|
|
eae721a45b | |
|
|
3c363878a1 | |
|
|
5aa91f7217 | |
|
|
1c29c3c76c | |
|
|
4add44a528 | |
|
|
38ee33db27 | |
|
|
81d561d1a0 | |
|
|
38ef3681e7 | |
|
|
66687592de | |
|
|
ab428b3ecc | |
|
|
fc55b27e3e | |
|
|
59957d5f01 | |
|
|
29461299e3 | |
|
|
ff189941bd | |
|
|
0eb91d8948 | |
|
|
280ba3b0c2 | |
|
|
3dcde7db53 | |
|
|
cd76c3a74b | |
|
|
8e2f096217 | |
|
|
24f9a79aff | |
|
|
c9d172c0af | |
|
|
9c2bb6a5c6 | |
|
|
38eb4bd2c6 | |
|
|
35113f6837 | |
|
|
585767ec0c | |
|
|
2b78349b85 | |
|
|
f5b4abce26 | |
|
|
c1a11f8e00 | |
|
|
f86d71c0bf | |
|
|
0694281504 | |
|
|
8764b7a23b | |
|
|
d539504890 | |
|
|
7ecf7483bd | |
|
|
77d562c7de | |
|
|
c4599b7529 | |
|
|
1ac5ffbe32 | |
|
|
2daf28518b | |
|
|
8d150d27ae | |
|
|
378f6ea2ca | |
|
|
a36e87acc4 | |
|
|
d2380dbfda | |
|
|
bdb78db9d5 | |
|
|
f0acefe685 | |
|
|
d3d6e8d3be | |
|
|
1892a90f88 | |
|
|
80b08eeea5 | |
|
|
c8546abd40 | |
|
|
249b3812ba | |
|
|
ea35420526 | |
|
|
3f659457d6 | |
|
|
b5779e18bf | |
|
|
bc694f43d1 | |
|
|
ac8af98404 | |
|
|
572cc46c52 | |
|
|
2ec56608aa | |
|
|
40c6bbfa6d | |
|
|
794c16ae6d | |
|
|
fa1d33163b | |
|
|
2f885c7309 | |
|
|
951be72be9 | |
|
|
7d56ebfc77 | |
|
|
4e3f241c13 | |
|
|
7a8f7fb5d1 | |
|
|
cb8d87aecc | |
|
|
53e75e5f83 | |
|
|
60e802f017 | |
|
|
b495e94c9e | |
|
|
d395b807b3 | |
|
|
a5a3cbd1e8 | |
|
|
e6ea4ee1d8 | |
|
|
9fa6898db2 | |
|
|
6d62d091df | |
|
|
0334147690 | |
|
|
4614ba80e7 | |
|
|
69cd6f9c1a | |
|
|
d3c98050ce | |
|
|
b32ec088d8 | |
|
|
8db7c53a58 | |
|
|
ffb2a1590c | |
|
|
f48e1f805f | |
|
|
9a3b7d5ae7 | |
|
|
64d3c09bd4 | |
|
|
d99fb47161 | |
|
|
49bdfa7930 | |
|
|
72cac3b49e | |
|
|
0cab779c8e | |
|
|
90dfd60a06 | |
|
|
f3550c4f6a | |
|
|
027e85052a | |
|
|
e10198f501 | |
|
|
4af0e85341 | |
|
|
af8c4ec9d4 | |
|
|
8d4584dd79 | |
|
|
421869d48e | |
|
|
e45aec750e | |
|
|
98d7c4415f | |
|
|
1363a19117 | |
|
|
eba30772a3 | |
|
|
b4e7523277 | |
|
|
0fc9b05f93 | |
|
|
1111c62bfe | |
|
|
f7f2708b1f | |
|
|
13651e8a76 | |
|
|
a1b7081f9d | |
|
|
175585b2f1 | |
|
|
2b9b532656 | |
|
|
51728ec2d1 | |
|
|
54b7f82881 | |
|
|
9d392c82cb | |
|
|
931b9d63bd | |
|
|
10bcc87ac5 | |
|
|
d4be677613 | |
|
|
6aab00900e | |
|
|
4118ba2e92 | |
|
|
832b40fd83 | |
|
|
cfb6f60aab | |
|
|
8b83bb1c05 | |
|
|
5a1bc4f7c3 | |
|
|
bd23cfcd71 | |
|
|
ca5887ac06 | |
|
|
5347849c6d | |
|
|
8e04c2e38c | |
|
|
2e940b3af7 | |
|
|
4d2fa546cf | |
|
|
8083e3b5cb | |
|
|
8cdd4dd751 | |
|
|
68ccf49f29 | |
|
|
f841886b52 | |
|
|
fa91a529bc | |
|
|
c670bee1aa | |
|
|
9cdca285a2 | |
|
|
a6b9e802c7 | |
|
|
cae40f5493 | |
|
|
99e2b1fe97 | |
|
|
de5d712967 | |
|
|
20db08a7e4 | |
|
|
11386c3176 | |
|
|
0fbf4d9297 | |
|
|
8570782dfe | |
|
|
66bbe9f41f | |
|
|
047a23137a | |
|
|
d9a5ce30af | |
|
|
b78b127447 | |
|
|
836fa84cd1 | |
|
|
9fceedd91f | |
|
|
7bd2ad5868 | |
|
|
2043c33761 | |
|
|
a41749602f | |
|
|
2b2c2c8d01 | |
|
|
83057362bd | |
|
|
3091070179 | |
|
|
ecadb91b3e | |
|
|
9681e10d45 | |
|
|
1c299f958b | |
|
|
6ecc72800b | |
|
|
a65a22e204 | |
|
|
705ac24e3b | |
|
|
445f516c5b | |
|
|
3f6316026c | |
|
|
61f87bfe16 | |
|
|
179138eb50 | |
|
|
c3b7c5b147 | |
|
|
b9d3f16bac | |
|
|
48da31f452 | |
|
|
5755412566 | |
|
|
bdb544ac4e | |
|
|
0664da5e5b | |
|
|
5abd19deca | |
|
|
863f633c64 | |
|
|
0d03131217 | |
|
|
6069095804 | |
|
|
e15b6115c8 | |
|
|
ed418a205f | |
|
|
f962160c91 | |
|
|
5dc5528f90 | |
|
|
34f6d6935c | |
|
|
84864202aa | |
|
|
8f8441949c | |
|
|
ab0cf2a343 | |
|
|
216817b675 | |
|
|
d19d5d6dcb | |
|
|
799e875dd7 | |
|
|
4d7e33ea5e | |
|
|
29b6b17787 | |
|
|
fdeb2ba1a2 | |
|
|
73e8434585 | |
|
|
276fe225b0 | |
|
|
7cdf095e01 | |
|
|
be202a18b9 | |
|
|
fb934a56ba | |
|
|
2cb4211555 | |
|
|
34b9908ae3 | |
|
|
5349177f14 | |
|
|
316b14561f | |
|
|
038d6ccd29 | |
|
|
d6acd0d321 | |
|
|
273520cb2d | |
|
|
852b03267b | |
|
|
41ca11f9df | |
|
|
5d38aaeddf | |
|
|
682a51d11a | |
|
|
8aba9d0610 | |
|
|
a48a6ffa32 | |
|
|
c924cb9ab5 | |
|
|
ca1dd07f08 | |
|
|
ea724a8db0 | |
|
|
f66d642c2a | |
|
|
c2478d806b | |
|
|
d5d037be53 | |
|
|
36c24fab7c | |
|
|
763c7cba26 | |
|
|
e993cc7eb3 | |
|
|
adc102712e | |
|
|
e1c4a44cae | |
|
|
759127204f | |
|
|
f83e4fa7b6 | |
|
|
dc227a212c | |
|
|
62794257e3 | |
|
|
e1aaf91053 | |
|
|
72b8e78dff | |
|
|
2f41d27b62 | |
|
|
ef2189bada | |
|
|
9ee5a75e74 | |
|
|
9522e1ff63 | |
|
|
dadcfc6f77 | |
|
|
6ef6faca86 | |
|
|
af23890f20 | |
|
|
b2158ce5e8 | |
|
|
8347e521cd | |
|
|
1706491538 | |
|
|
b5afa436f6 | |
|
|
3ed03167ca | |
|
|
fe2f668d58 | |
|
|
13f404d072 | |
|
|
86d1260395 | |
|
|
c6cf3b91cd | |
|
|
370b3af37e | |
|
|
c0d8f24533 | |
|
|
c8884bc6e7 | |
|
|
7105fe448d | |
|
|
595ad9564e | |
|
|
b0f1db5770 | |
|
|
8caeb23c53 | |
|
|
e8218b3fd8 | |
|
|
d49ec43236 | |
|
|
bdb34e7221 | |
|
|
3cc28fa9f5 | |
|
|
f709eb8da9 | |
|
|
3af985f571 | |
|
|
bc9f3b6a6a | |
|
|
2985593391 | |
|
|
13a0616619 | |
|
|
c1a8940270 | |
|
|
dbd2e4a506 | |
|
|
4c0b829a08 | |
|
|
0b7fefd906 | |
|
|
b39f509562 | |
|
|
01e5a492c4 | |
|
|
f728dd848a | |
|
|
a1d040e61a | |
|
|
2389ce929f | |
|
|
54cbe84812 | |
|
|
453c9576ea | |
|
|
170c835e3b | |
|
|
d19a8406bf | |
|
|
1673a1b38c | |
|
|
deb10d2aa2 | |
|
|
92225253fa | |
|
|
6b82d6371d | |
|
|
b346f031c1 | |
|
|
58737cc887 | |
|
|
887fdf6ea9 | |
|
|
6947fe87cb | |
|
|
ab1f7c4688 | |
|
|
94940dc247 | |
|
|
f812b4d1be | |
|
|
0c4355f9c7 | |
|
|
1b5979e8f7 | |
|
|
078c13ac80 | |
|
|
4ea501031e | |
|
|
444f898125 | |
|
|
1652311448 | |
|
|
6ab6c22158 | |
|
|
41e9cc95eb | |
|
|
14debd515c | |
|
|
75eeda4aee | |
|
|
b9fb8942c5 | |
|
|
495572035f | |
|
|
b51a502cc9 | |
|
|
6576f40db8 | |
|
|
fc93566e04 | |
|
|
a61fb64707 | |
|
|
4aa1813383 | |
|
|
b2426149c6 | |
|
|
55c97c3d78 | |
|
|
8aad8948bf | |
|
|
286891c6fc | |
|
|
fc275452ec | |
|
|
e34ad74b8f | |
|
|
a689f0009f | |
|
|
3276b9d26e | |
|
|
0919f04fa4 | |
|
|
b93b8dd85e | |
|
|
8846c14157 | |
|
|
c8b445b7d2 | |
|
|
7820e02574 | |
|
|
3ad506e01a | |
|
|
78cdd2442d | |
|
|
1ac3660263 | |
|
|
4cd7369ff6 | |
|
|
2fa5d05c2b | |
|
|
4be4b9695b | |
|
|
80a0f16778 | |
|
|
d4cfd3893a | |
|
|
a8e0fcfb79 | |
|
|
c769ddd02d | |
|
|
0621f205c5 | |
|
|
6bc8c31af1 | |
|
|
0d73bdc462 | |
|
|
ce4b783ef0 | |
|
|
fb5c56cd87 | |
|
|
dd62c033df | |
|
|
4d80f823b4 | |
|
|
0341470fc0 | |
|
|
79f1580560 | |
|
|
ec7e915ee1 | |
|
|
5f73380afa | |
|
|
610f328b41 | |
|
|
d360bb99e2 | |
|
|
a61f843083 | |
|
|
9173918ce4 | |
|
|
42279d557d | |
|
|
f5fc739ea1 | |
|
|
0e9ae092e5 | |
|
|
48f20ec64f | |
|
|
d68326a374 | |
|
|
8925d09bd6 | |
|
|
103ae1fd54 | |
|
|
f1c65213df | |
|
|
a49c35ac7d | |
|
|
0e55ac35bc | |
|
|
bae861e2ce | |
|
|
36dad0c86d | |
|
|
46ab59f4b2 | |
|
|
98ecdc278e | |
|
|
1898057e2a | |
|
|
fffc100623 | |
|
|
7ba8d67db6 | |
|
|
2aec4b9fab | |
|
|
370f7b015f | |
|
|
7f9b879d45 | |
|
|
ce869588ec | |
|
|
5a9954c614 | |
|
|
c865b70c40 | |
|
|
9a5cf628d0 | |
|
|
38a8681d17 | |
|
|
8a8cce27f5 | |
|
|
fe749d40c0 | |
|
|
ed82ab1241 | |
|
|
cc8ac8d99d | |
|
|
1ece085349 | |
|
|
df128e0591 | |
|
|
fd6a076f55 | |
|
|
5d8f4033be | |
|
|
9defcf4e87 | |
|
|
d4cbc67836 | |
|
|
626043c9e9 | |
|
|
6395e74f85 | |
|
|
a4c2955dd4 | |
|
|
92fd9cd634 | |
|
|
fd99fb27f9 | |
|
|
3d0370017a | |
|
|
12e5a8b9d9 | |
|
|
cf15f9f7b6 | |
|
|
b31b612954 | |
|
|
37073bd0ec | |
|
|
d700dd3e97 | |
|
|
9fe3a6856c | |
|
|
98a1ea0cd2 | |
|
|
356784d473 | |
|
|
98e0f20dc0 | |
|
|
d0a9ad0e67 | |
|
|
875763f357 | |
|
|
637fe69b1d | |
|
|
56b22841ac | |
|
|
b489485c42 | |
|
|
600c3d778d | |
|
|
3b72c8f52d | |
|
|
74e68ea8b1 | |
|
|
e2d8a510e6 | |
|
|
5f0fe95d69 | |
|
|
7d88c1fafb | |
|
|
0a2a89e375 | |
|
|
f8bb589caa | |
|
|
a652754fc5 | |
|
|
da6388cc3c | |
|
|
4bdb6f303c | |
|
|
70916677de | |
|
|
e26fdf723c | |
|
|
4e299f1ae6 | |
|
|
f886eb217c | |
|
|
64e603e047 | |
|
|
2595afd722 | |
|
|
6916f1525d | |
|
|
0ec4d828b8 | |
|
|
837c1d5af7 | |
|
|
49ca194c07 | |
|
|
d1e6aeab36 | |
|
|
f8d76f647c | |
|
|
5acd93ecd0 | |
|
|
bf427ba4df | |
|
|
48192b3f50 | |
|
|
ef9ad28d56 | |
|
|
154cdc2db6 | |
|
|
7e5a8afb48 | |
|
|
ec6106b079 | |
|
|
57401acf2b | |
|
|
420da79ea4 | |
|
|
c4244cf397 | |
|
|
4a08a569b5 | |
|
|
3f0e96ac35 | |
|
|
cc5110dec3 | |
|
|
ace1a1f5a4 | |
|
|
7f1684fb78 | |
|
|
f124f0a661 | |
|
|
ca08e16ac4 | |
|
|
0865aaa97f | |
|
|
74cfa1e567 | |
|
|
b20da507c3 | |
|
|
bd3ad19cab | |
|
|
a2c6c8b4eb | |
|
|
09eeaf4b88 | |
|
|
27e686271e | |
|
|
7296057184 | |
|
|
2ce54f577b | |
|
|
128dd3cfed | |
|
|
c0e45e8df7 | |
|
|
1704bb2c8c | |
|
|
6905142a4a | |
|
|
95180b669f | |
|
|
30aa871075 | |
|
|
d878c3f5eb | |
|
|
064887f7d8 | |
|
|
a729cea252 | |
|
|
5a5fcc4d5e | |
|
|
dbf8a84981 | |
|
|
6b27a44542 | |
|
|
755de51c2a | |
|
|
4a846c5f56 | |
|
|
2b70c12be6 | |
|
|
71b0e6ca43 | |
|
|
b5d7371eed | |
|
|
80ed795c65 | |
|
|
ee2c3d009b | |
|
|
fd83b6693a | |
|
|
d071529e12 | |
|
|
4102d5c8c3 | |
|
|
65ff948354 | |
|
|
fda25021f2 | |
|
|
bfb350d3df | |
|
|
370a426fa9 | |
|
|
215f2828dd | |
|
|
353dd2b545 | |
|
|
1d70791fd9 | |
|
|
51c8eb27e6 | |
|
|
1122fb8649 | |
|
|
65025c4e33 | |
|
|
f93b55818d | |
|
|
1f348d2986 | |
|
|
e8f110f6b5 | |
|
|
091f53c596 | |
|
|
f4b9636d5a | |
|
|
53d54a682e | |
|
|
ce230f059a | |
|
|
b99f387b4e | |
|
|
a04f43e98d | |
|
|
4078852503 | |
|
|
873b52f19c | |
|
|
d96edcfec6 | |
|
|
b4080fedea | |
|
|
085d659363 | |
|
|
95e5e977da | |
|
|
25972260ac | |
|
|
4fd2fc8595 | |
|
|
0b74590252 | |
|
|
fc7b961010 | |
|
|
8f2cfec201 | |
|
|
84d3ea1de7 | |
|
|
6ace61fe75 | |
|
|
a46502ceca | |
|
|
eaed1b3692 | |
|
|
03a15d1077 | |
|
|
ea3f16778e | |
|
|
556b3a0a2b | |
|
|
17ff9105aa | |
|
|
6269db8978 | |
|
|
fd45988bfc | |
|
|
26f96761cd | |
|
|
c71848ec3c | |
|
|
b2dc5da33c | |
|
|
c37e619dac | |
|
|
b283dcd3a2 | |
|
|
a1d55575fd | |
|
|
2c200d4d86 | |
|
|
12ad515e5b | |
|
|
c8264fdb1b | |
|
|
3908dfc146 | |
|
|
e5fc3927b5 | |
|
|
84f23f6beb | |
|
|
1dd234a928 | |
|
|
57869cb358 | |
|
|
d18d37de82 | |
|
|
bb23e65897 | |
|
|
735d4a96bd | |
|
|
7e8b6adaad | |
|
|
76e27fb5d9 | |
|
|
cf77883982 | |
|
|
b133313e44 | |
|
|
e83025f522 | |
|
|
15a7037b04 | |
|
|
d37809ac5b | |
|
|
c6e3875ceb | |
|
|
b102a44d50 | |
|
|
a49e1818c0 | |
|
|
caa9accf02 | |
|
|
bba03ec8a0 | |
|
|
ac5700cd95 | |
|
|
a18b15146f | |
|
|
91c328b58f | |
|
|
84bf80c24a | |
|
|
3c323233a5 | |
|
|
e3aaf2a25a | |
|
|
83397b07dc | |
|
|
4e54cd2472 | |
|
|
5d61cb1afa | |
|
|
5152f38d23 | |
|
|
c4ba0b66b4 | |
|
|
c326c206c4 | |
|
|
757923bbc4 | |
|
|
57c17b50ef | |
|
|
a9854649a4 | |
|
|
6e1783fb87 | |
|
|
5d574fb3d2 | |
|
|
2fd764ce7c | |
|
|
e8fae9bf0f | |
|
|
b80ca191e2 | |
|
|
4a3865f419 | |
|
|
adf362f4e8 | |
|
|
86fdab922a | |
|
|
e65a6b7afe | |
|
|
5754dcfd8f | |
|
|
44110a6246 | |
|
|
206a44d881 | |
|
|
8b27191378 | |
|
|
d6c430cd66 | |
|
|
f0bba6ee43 | |
|
|
49dc9e7df0 | |
|
|
bbb16189ef | |
|
|
4af8be5a72 | |
|
|
f2f37ab42d | |
|
|
a8fd0f8cf7 | |
|
|
5fa3f749f5 | |
|
|
2785d3c036 | |
|
|
f0f5f217bb | |
|
|
958eaee41d | |
|
|
789ff26cfc | |
|
|
e505cd6c8d | |
|
|
8bb96d6912 | |
|
|
8d3fe2b681 | |
|
|
df7bd5f202 | |
|
|
75a484e831 | |
|
|
75f4c6497f | |
|
|
bc1d1b7925 | |
|
|
ad3ad30e86 | |
|
|
cccf0474d1 | |
|
|
1766ae15c8 | |
|
|
a64ccb03cf | |
|
|
a4e10199bd | |
|
|
ea6ea80f18 | |
|
|
7ccb53ab51 | |
|
|
d04a16a07b | |
|
|
d85bd79da9 | |
|
|
44b7474a38 | |
|
|
4c028e564e | |
|
|
4e2b71bb0c | |
|
|
51c03383cf | |
|
|
140449948a | |
|
|
d3a755b48e | |
|
|
185ea7a1fd | |
|
|
a38d6b4855 | |
|
|
46ba13410b | |
|
|
1fd6308ec1 | |
|
|
ce70a4b4db | |
|
|
a5ba5032bf | |
|
|
ac427c0f57 | |
|
|
e817c2304d | |
|
|
3922bb456c | |
|
|
e3340171a0 | |
|
|
56ce32729d | |
|
|
d652201ce8 | |
|
|
f7d57e1c52 | |
|
|
d7aa64fe91 | |
|
|
279da4eeaf | |
|
|
7832f5a75f | |
|
|
f735819ae2 | |
|
|
99cd4cdfd7 | |
|
|
3425e601bf | |
|
|
4ee20ed3ec | |
|
|
066810be17 | |
|
|
117e6647a9 | |
|
|
bad018a10e | |
|
|
b52f8922cb | |
|
|
af02ffe92a | |
|
|
8550b77d17 | |
|
|
354a26c6d0 | |
|
|
2dff8faf4b | |
|
|
8382aaf179 | |
|
|
a209e57fd5 | |
|
|
e819bf7b6c | |
|
|
1517be875a | |
|
|
2e39243070 | |
|
|
1f1b54f111 | |
|
|
5d354f1f0e | |
|
|
df799bfb2e | |
|
|
0c863e0940 | |
|
|
5a6b0db6f8 | |
|
|
6c1f77dd3a | |
|
|
5226b6d464 | |
|
|
570709adc2 | |
|
|
1655335aa7 | |
|
|
18ff97da7c | |
|
|
4ab629562d | |
|
|
42ad7bce5d | |
|
|
1ee4a9be10 | |
|
|
707ad3d673 | |
|
|
f91c7b1928 | |
|
|
78d6320cad | |
|
|
4eccd15494 | |
|
|
edfbd9cf34 | |
|
|
b1a44b0f3a | |
|
|
e01904b8bd | |
|
|
02969d1551 | |
|
|
8edd8827ca | |
|
|
4ab7286978 | |
|
|
16c1003584 | |
|
|
28a6fd4064 | |
|
|
5826032020 | |
|
|
34be4d8c13 | |
|
|
d0aedc70cf | |
|
|
4f04f3b33c | |
|
|
b29ecafea5 | |
|
|
2deadbbe02 | |
|
|
3b986dcfaa | |
|
|
d0f1e9a121 | |
|
|
56984fd465 | |
|
|
c6e08ac287 | |
|
|
ad54a3005d | |
|
|
80ab17ff92 | |
|
|
a30f4dcd85 | |
|
|
c0a525aac6 | |
|
|
493f1e3ba0 | |
|
|
628b62c0aa | |
|
|
b2d5a6e3b6 | |
|
|
71d94db06e | |
|
|
b7efcf86c0 | |
|
|
9f34c01d6e | |
|
|
f5dada6080 | |
|
|
28606b1ff7 | |
|
|
70e93df54d | |
|
|
027508acd3 | |
|
|
bfb91a332c | |
|
|
1ec723edd4 | |
|
|
d7b81739e7 | |
|
|
150657763c | |
|
|
8457592c0b | |
|
|
41f53b9895 | |
|
|
19f4f607b1 | |
|
|
ac5485ddc0 | |
|
|
e9cdb7b131 | |
|
|
841db74c75 | |
|
|
4c2ab10e07 | |
|
|
4fc878f796 | |
|
|
aa0c78b882 | |
|
|
4cef8c3fac | |
|
|
b1c47d9506 | |
|
|
b0afa580a7 | |
|
|
1ee8bd1f6e | |
|
|
1cdb938ad4 | |
|
|
9888154a02 | |
|
|
e252b84740 | |
|
|
09248b2b45 | |
|
|
8d7b2abe4f | |
|
|
98fbe060a3 | |
|
|
fccde40641 | |
|
|
664abf86dc | |
|
|
e133b1f92d | |
|
|
47c4c91f7a | |
|
|
a39249eaaf | |
|
|
af3c21742f | |
|
|
06f1fcbee1 | |
|
|
1db373caba | |
|
|
3deda3849e | |
|
|
9ac32da633 | |
|
|
5b61d8d0e9 | |
|
|
ce37fa6747 | |
|
|
bba002c046 | |
|
|
7ac3139e44 | |
|
|
c2a393cc51 | |
|
|
f70053c2f3 | |
|
|
239b7e28bf | |
|
|
07aa22ed1d | |
|
|
b6b2663cf3 | |
|
|
6ee258966d | |
|
|
44363dbde0 | |
|
|
2a3e05ef80 | |
|
|
888e45fe4d | |
|
|
9f59559575 | |
|
|
03859f5d06 | |
|
|
b8079ce519 | |
|
|
ab8fdec4f4 | |
|
|
ad152fab8e | |
|
|
80b097e379 | |
|
|
ff2dc6d86b | |
|
|
d81124150a | |
|
|
5cc6579bf0 | |
|
|
05b8da962e | |
|
|
fa5cd75e3c | |
|
|
4657b97f35 | |
|
|
23ad2dbd02 | |
|
|
4e7483c235 | |
|
|
594d9f92f9 | |
|
|
fbf04bc6a2 | |
|
|
2130d93c45 | |
|
|
6abc49a3a6 | |
|
|
9b7d0ea459 | |
|
|
05289aef93 | |
|
|
0d9b629b18 | |
|
|
19a64c993a | |
|
|
f0ec449f25 | |
|
|
5c209a55ba | |
|
|
96a0bbc155 | |
|
|
6f417a001f | |
|
|
9d975d3a17 | |
|
|
592cec9d10 | |
|
|
3d3f7c1d74 | |
|
|
9e68dd6d10 | |
|
|
a91fc4f870 | |
|
|
a940708f8d | |
|
|
a74decb540 | |
|
|
022a692735 | |
|
|
701e35356b | |
|
|
b28bbc7c41 | |
|
|
5c5d609fc8 | |
|
|
664e608cba | |
|
|
85c0cc0584 | |
|
|
73d275a42e | |
|
|
57f2816d05 | |
|
|
f02bacf372 | |
|
|
dc6f38c368 | |
|
|
c285d516ef | |
|
|
1e4571675d | |
|
|
acea8428c6 | |
|
|
f6d014bef6 | |
|
|
55d9abe2b8 | |
|
|
16f211751b | |
|
|
6a01a33dba | |
|
|
facc06bebc | |
|
|
87f2613860 | |
|
|
a34f67639a | |
|
|
fd1cc1a59d | |
|
|
c2873b5020 | |
|
|
918ef299ae | |
|
|
defba84160 | |
|
|
993dc831c8 | |
|
|
442119d0e4 | |
|
|
9d94ffa417 | |
|
|
af6a1a3693 | |
|
|
88416b3981 | |
|
|
85b6496ba0 | |
|
|
905b7fe281 | |
|
|
6fc2ca0bc6 | |
|
|
ad9e4db4e8 | |
|
|
48b8bd5c5a | |
|
|
bb16418385 | |
|
|
7eedba795b | |
|
|
ab74818579 | |
|
|
27d70de3cd | |
|
|
41f83bc340 | |
|
|
412a95ea43 | |
|
|
115e9131d0 | |
|
|
e11af2ebd7 | |
|
|
569a8a470c | |
|
|
a3d5a9c98f | |
|
|
e0de51921c | |
|
|
1ee9486143 | |
|
|
4d659581b7 | |
|
|
a8de57addd | |
|
|
25bcc2cefc | |
|
|
b5a0ab434a | |
|
|
c6e84cb5da | |
|
|
b0fc2f986a | |
|
|
90a665230c | |
|
|
2ad640f35b | |
|
|
fa93ba9ae5 | |
|
|
ec9088eb4a | |
|
|
0f29c49774 | |
|
|
dd811cdbf6 | |
|
|
c0efc392c6 | |
|
|
44b0c02a0e | |
|
|
2dbbcfbe33 | |
|
|
8a598a1702 | |
|
|
bccd4dd9e8 | |
|
|
08b7c19830 | |
|
|
2f6eb9f846 | |
|
|
0e71cbc561 | |
|
|
7ef258ffcc | |
|
|
4cb959ac83 | |
|
|
3786d5bef2 | |
|
|
5a41202935 | |
|
|
7af7b96a2d | |
|
|
4d37cbf4fe | |
|
|
85db0393dc | |
|
|
f503d5006b | |
|
|
b4decda1f5 | |
|
|
1279962555 | |
|
|
773bb11e1b | |
|
|
4c23213428 | |
|
|
0b0f260ec9 | |
|
|
7763f331bc | |
|
|
d29f650b02 | |
|
|
e512dcc735 | |
|
|
b529f6edc6 | |
|
|
4d004e4a9e | |
|
|
1173f04abc | |
|
|
8f2d598236 | |
|
|
5f22072f71 | |
|
|
3e21207f8c | |
|
|
6d018de09d | |
|
|
d708d7f757 | |
|
|
d32aefb900 | |
|
|
f23f39dd6f | |
|
|
99a5c945b3 | |
|
|
6057d1c11e | |
|
|
3318dfd207 | |
|
|
ead3252ce7 | |
|
|
c15d6170da | |
|
|
af5efc4de4 | |
|
|
de4812754b | |
|
|
1bf3ce1bc9 | |
|
|
b52ca52e1d | |
|
|
000f21b9dd | |
|
|
e02f72a0f1 | |
|
|
ad06e2239b | |
|
|
948a64c551 | |
|
|
8ee9d74d3d | |
|
|
8ab769161b | |
|
|
e63ef15d18 | |
|
|
963191fe93 | |
|
|
8220d9e6ed | |
|
|
f40093746f | |
|
|
c972751e42 | |
|
|
b437547501 | |
|
|
a6df44699c | |
|
|
7cc5e0c7d0 | |
|
|
2719540d32 | |
|
|
285ff7b8f6 | |
|
|
c24a6d4cd0 | |
|
|
602ae5b66b | |
|
|
97656aa9d6 | |
|
|
e426d223af | |
|
|
2a5174a7b5 | |
|
|
85f0e3ecd5 | |
|
|
b5e645f13d | |
|
|
f10649b40c | |
|
|
301e284d36 | |
|
|
cfef092457 | |
|
|
8f4ff06723 | |
|
|
5a3430fab5 | |
|
|
bfbca2862d | |
|
|
5d16528f82 | |
|
|
3b46b7333b | |
|
|
ead068fbee | |
|
|
6b8643d6c8 | |
|
|
5e19622198 | |
|
|
4d579cf8b3 | |
|
|
40ed2c5efc | |
|
|
0437cc9199 | |
|
|
af818b66f6 | |
|
|
956baad89f | |
|
|
46cc1db560 | |
|
|
2d293a689e | |
|
|
784db2c830 | |
|
|
f88ea86bed | |
|
|
fca9c7b2ab | |
|
|
3274db8a61 | |
|
|
bb8f0b752f | |
|
|
28fe810f06 | |
|
|
51c63fa4c5 | |
|
|
76c51af4ef | |
|
|
055e370669 | |
|
|
3143557951 | |
|
|
00087a7dc7 | |
|
|
8f69c0d1a4 | |
|
|
14b89da6f3 | |
|
|
6fec7f7cd3 | |
|
|
ad778ff0d3 | |
|
|
d7eae78372 | |
|
|
bfd21ecd1e | |
|
|
e4a6d9be1d | |
|
|
55246abc44 | |
|
|
93d6796fb3 | |
|
|
5f0ffb4b3a | |
|
|
e56ac82749 | |
|
|
a61b6bb587 | |
|
|
6e8e05553e | |
|
|
10f0961757 | |
|
|
eddf86f430 | |
|
|
425fe0983c | |
|
|
202e782c9d | |
|
|
f05dc3db33 | |
|
|
a11210a7ee | |
|
|
786f1009fb | |
|
|
4fe0a4fda2 | |
|
|
f158119d68 | |
|
|
925a51482d | |
|
|
8976cba6ce | |
|
|
3aae9a0ab1 | |
|
|
0010ac1157 | |
|
|
4ca3839821 | |
|
|
18f3f76b22 | |
|
|
4ebc22d9d0 | |
|
|
e16816422c | |
|
|
9f9f09c8c5 | |
|
|
6d37e4ce1f | |
|
|
dc248cae71 | |
|
|
ad8e8938d4 | |
|
|
e9e3bbaf09 | |
|
|
39c9cec7d1 | |
|
|
75b88175cb | |
|
|
3c1876769b | |
|
|
a73230ac79 | |
|
|
46c7516764 | |
|
|
31f5206f01 | |
|
|
bfa2ad8c65 | |
|
|
727a872d9f | |
|
|
0cb9180ec5 | |
|
|
0262c85ac0 | |
|
|
32fd093cf3 | |
|
|
3c97ceac37 | |
|
|
673058381f | |
|
|
3fa674efd6 | |
|
|
0b26735b7b | |
|
|
1965a6b44f | |
|
|
10e11072ab | |
|
|
c30091ee06 | |
|
|
58edee6b97 | |
|
|
e46a93f42c | |
|
|
9f20cc2485 | |
|
|
200c1db526 | |
|
|
3763904f73 | |
|
|
695c13769c | |
|
|
4e304b18d4 | |
|
|
16682c5456 | |
|
|
cb48033e72 | |
|
|
21e81baa03 | |
|
|
a976ab8390 | |
|
|
4dd688efa8 | |
|
|
e8ee49c396 | |
|
|
82bd984bbe | |
|
|
777b329e9f | |
|
|
e09d0f171e | |
|
|
358fb1af3a | |
|
|
8e5f5d4cf4 | |
|
|
2d93abbd02 | |
|
|
250e5293e9 | |
|
|
4c6e388cab | |
|
|
162871a7dd | |
|
|
3a59b7b921 | |
|
|
e6b4f23fcf | |
|
|
24ba648015 | |
|
|
18c7e5d524 | |
|
|
c8597b450e | |
|
|
8748eea15f | |
|
|
ada2b259b7 | |
|
|
334a87acf8 | |
|
|
3886cf1b45 | |
|
|
581aad38d5 | |
|
|
1e085e5990 | |
|
|
46a6176432 | |
|
|
edfbe3104e | |
|
|
ea59ad0519 | |
|
|
e620eab7d9 | |
|
|
9e7a073653 | |
|
|
51e1348825 | |
|
|
c6dac1ad4a | |
|
|
0b6fb08022 | |
|
|
54e04d7e06 | |
|
|
c7f247fbe6 | |
|
|
da2758a93e | |
|
|
2fbbc5e891 | |
|
|
055d99206b | |
|
|
31464df41b | |
|
|
e6a495f2a4 | |
|
|
0dd32610ae | |
|
|
7d8221de73 | |
|
|
2aecdfad1f | |
|
|
024230a87c | |
|
|
e73ebfee78 | |
|
|
7470261345 | |
|
|
5ea339c3cd | |
|
|
ecd05262be | |
|
|
2b6752fd73 | |
|
|
7f24241bc4 | |
|
|
75da9a8b20 | |
|
|
7d0ea25ec5 | |
|
|
00f67fa56c | |
|
|
48ebf2d36b | |
|
|
b8e4e48f2e | |
|
|
0fdfc819a6 | |
|
|
03b57c00cf | |
|
|
e63f44d856 | |
|
|
28b1763b31 | |
|
|
13317a888d | |
|
|
e60ac50d26 | |
|
|
22c7e08448 | |
|
|
23cccce323 | |
|
|
5cfdd5d039 | |
|
|
90b075cc7f | |
|
|
c9aed9da7b | |
|
|
c6687efe0e | |
|
|
a281c5b2f9 | |
|
|
73df3cc969 | |
|
|
10ad16464c | |
|
|
8d40507ccd | |
|
|
a604865f65 | |
|
|
f81735d7f9 | |
|
|
9685ce4f03 | |
|
|
fe0869e9fe | |
|
|
e5f87f58e8 | |
|
|
166ab0ba83 | |
|
|
09b3bbda62 | |
|
|
930676dc88 | |
|
|
6b60d00eae | |
|
|
fd7412462d | |
|
|
cd847c1daf | |
|
|
00d3e429af | |
|
|
22faa51ad7 | |
|
|
47058c6292 | |
|
|
d380d0359e | |
|
|
319a5a1e59 | |
|
|
6ac5d98211 | |
|
|
fd287c7d2c | |
|
|
da39561ca3 | |
|
|
594cd24706 | |
|
|
cff5e634f5 | |
|
|
142348fcef | |
|
|
aba3311076 | |
|
|
ddaf34a475 | |
|
|
6c122cd645 | |
|
|
1eba0f3602 | |
|
|
e619fd06f8 | |
|
|
4c87b03ad5 | |
|
|
906a30ed0b | |
|
|
2c4596a73f | |
|
|
e8c39c9242 | |
|
|
de46b5ce4d | |
|
|
61bd4cf2fc | |
|
|
47792608ce | |
|
|
3baa72396f | |
|
|
dcdcbd1055 | |
|
|
e32330ad95 | |
|
|
00d2e3e5fa | |
|
|
83dade9e9e | |
|
|
4cdae4601d | |
|
|
da3239d245 | |
|
|
5f7a88c68f | |
|
|
abce613468 | |
|
|
e4d0123682 | |
|
|
e242255da4 | |
|
|
e9b216f84e | |
|
|
788eb61ef9 | |
|
|
ef88b86e01 | |
|
|
ada73560d6 | |
|
|
a7332aac70 | |
|
|
4882bb89a2 | |
|
|
83a71e41ab | |
|
|
a30fd89d85 | |
|
|
1516fa7387 | |
|
|
9b599ebb6a | |
|
|
82115a7ce8 | |
|
|
fa422bfc02 | |
|
|
f90c4f5e6e | |
|
|
24607dabb5 | |
|
|
a04db053d3 | |
|
|
f7ffea35a4 | |
|
|
0b8df4c4dd | |
|
|
73be3e808b | |
|
|
247ce5ad4b | |
|
|
8e31d80fde | |
|
|
89f60fa0f5 | |
|
|
81e4421a0b | |
|
|
c1d5beb754 | |
|
|
2f0607bdf3 | |
|
|
658c1d5775 | |
|
|
cacb7d8685 | |
|
|
ff50fa45f0 | |
|
|
edb5dd36ff | |
|
|
8e418bfd16 | |
|
|
1a244f68cd | |
|
|
6a165ddf16 | |
|
|
a25ad7e4e8 | |
|
|
0ba234f794 | |
|
|
e867e7963d | |
|
|
e80c17cc04 | |
|
|
d51e0ca4a5 | |
|
|
2b00cf8d7a | |
|
|
7afbb8345c | |
|
|
2e8536064b | |
|
|
a55ad8cd22 | |
|
|
49d09f0a2b | |
|
|
88d5fd97a7 | |
|
|
59af2871d8 | |
|
|
6485cb3232 | |
|
|
1400355aaf | |
|
|
8fb6dfe2f3 | |
|
|
e45d585b2d | |
|
|
483238f69e | |
|
|
fe34c687b9 | |
|
|
ad8b7e6053 | |
|
|
1be02a6b45 | |
|
|
230e17743d | |
|
|
e826d4939a | |
|
|
2ec9c6927a | |
|
|
08aa82afc4 | |
|
|
65f492d5da | |
|
|
09ac981df0 | |
|
|
0b4f967e40 | |
|
|
68c356f7af | |
|
|
49206ee03d | |
|
|
9e2a64ee55 | |
|
|
5ad2e4d975 | |
|
|
0dbeb14d7c | |
|
|
4450406aa7 | |
|
|
a674c78049 | |
|
|
c2c41f41bd | |
|
|
39679f0a0a | |
|
|
f60bad0467 | |
|
|
9a7f54e8cd | |
|
|
126183a9e8 | |
|
|
68b1f5e2ad | |
|
|
d234639423 | |
|
|
cffa50ddab | |
|
|
f693db6f15 | |
|
|
fa3d4f6097 | |
|
|
1d53c87500 | |
|
|
7b8afcc21a | |
|
|
1a2dd5b203 | |
|
|
50418dac6a | |
|
|
377f338f09 | |
|
|
43a045bec6 | |
|
|
79ceed2c45 | |
|
|
bd74b9b030 | |
|
|
c2ec01d38c | |
|
|
be7609b0c6 | |
|
|
2da34db57e | |
|
|
5aed7fa9f0 | |
|
|
e785519b33 | |
|
|
d260bbceaa | |
|
|
6bf0797df6 | |
|
|
fdc6449443 | |
|
|
7d214f5664 | |
|
|
59abc1829a | |
|
|
76f4a5e0b0 | |
|
|
fc61692b09 | |
|
|
0678d86eeb | |
|
|
4e6e37fe39 | |
|
|
2daf8a2ede | |
|
|
93626e5907 | |
|
|
345ad44bdb | |
|
|
afe6eb1e8a | |
|
|
c32622dc28 | |
|
|
bced9ecfa2 | |
|
|
3dab60b7df | |
|
|
20fdbe0fe1 | |
|
|
153d138efd | |
|
|
a9a4be6c7c | |
|
|
44d3158d1b | |
|
|
cf2cc34525 | |
|
|
45c04e0156 | |
|
|
d143515470 | |
|
|
60ec0b4020 | |
|
|
b7caa1c73e | |
|
|
9e535a15cf | |
|
|
fa2e949b99 | |
|
|
615a8370e3 | |
|
|
d530ce3f28 | |
|
|
43f6636363 | |
|
|
57faf14b21 | |
|
|
110878f4c9 | |
|
|
197ddc8ce8 | |
|
|
2e5f605225 | |
|
|
396081055f | |
|
|
0079231967 | |
|
|
5ee08a2d62 | |
|
|
8539954b4e | |
|
|
fcdb35b97c | |
|
|
7b84207e0e | |
|
|
6242edfaf9 | |
|
|
34a16cf98c | |
|
|
88fdea590d | |
|
|
056538217c | |
|
|
1fd4e50506 | |
|
|
9bf5093ff3 | |
|
|
2b84d42bf3 | |
|
|
8f9512c3bd | |
|
|
ff3d4db8db | |
|
|
1f3f439e22 | |
|
|
c23f6910f0 | |
|
|
6242b7ed77 | |
|
|
08e3656c42 | |
|
|
3179dd6d58 | |
|
|
b510f115dd | |
|
|
0c69572ec8 | |
|
|
67fad6a3ce | |
|
|
8eae2e0eab | |
|
|
91d24ccd42 | |
|
|
9dd62009fb | |
|
|
3e7aa986b4 | |
|
|
bd1ad2f948 | |
|
|
fb7ba4b701 | |
|
|
5b79f9a0a1 | |
|
|
1b01ffab81 | |
|
|
3b3fcaa1f0 | |
|
|
132653cde7 | |
|
|
8ce647c35b | |
|
|
04e8581149 | |
|
|
7ebf56f1aa | |
|
|
3e42a6ef23 | |
|
|
097030c846 | |
|
|
4f21203618 | |
|
|
f2502a9e31 | |
|
|
eccf116343 | |
|
|
a9a514dbd1 | |
|
|
288e6492e1 | |
|
|
69c65faa4c | |
|
|
813d21938a | |
|
|
4d3602dc30 | |
|
|
b9fa01eccb | |
|
|
23eed95937 | |
|
|
0b7c993551 | |
|
|
8559ca96d4 | |
|
|
58c4d565f6 | |
|
|
2cba879541 | |
|
|
7d17c05409 | |
|
|
3d46a2c473 | |
|
|
c4d2c354e9 | |
|
|
5a5e99c50c | |
|
|
935b488745 | |
|
|
f0d9373e17 | |
|
|
0b965db1be | |
|
|
c55060deeb | |
|
|
0a577422f1 | |
|
|
4c9e9ee6b3 | |
|
|
ad1dca04cb | |
|
|
a22ceb2d84 | |
|
|
f7871d944b | |
|
|
a3a5a6c929 | |
|
|
5ab11ee63f | |
|
|
5a32f41b51 | |
|
|
d62b6e4e14 | |
|
|
ee00f45d6a | |
|
|
f4bde4b45a | |
|
|
968f1b1391 | |
|
|
6ac6adbeea | |
|
|
654a92de8c | |
|
|
d47a1c9ad7 | |
|
|
8b1aaa710e | |
|
|
ccbf9af14d | |
|
|
fc5a53ce91 | |
|
|
4b3b3780be | |
|
|
3265bf401d | |
|
|
9fd690559e | |
|
|
432301e565 | |
|
|
0d739982a8 | |
|
|
aa050b140d | |
|
|
e1b6fb6179 | |
|
|
87e9fb308c | |
|
|
879a88299d | |
|
|
e66c5d1070 | |
|
|
08b0742163 | |
|
|
b7ff9d66e1 | |
|
|
23c67db989 | |
|
|
cf613aedfc | |
|
|
6d93fc3405 | |
|
|
00d64ded9b | |
|
|
ffcb56cc0d | |
|
|
3d978e1c46 | |
|
|
eeb6d1b903 | |
|
|
6d624209a0 | |
|
|
322ac2e843 | |
|
|
ea56c09199 | |
|
|
42c833faf4 | |
|
|
1b22fd5c3a | |
|
|
5b5696d0ac | |
|
|
66b1ce790d | |
|
|
3f0756984a | |
|
|
7e739bc2d4 | |
|
|
a54bcd773d | |
|
|
1e4472f89d | |
|
|
35238a3390 | |
|
|
deb01720f3 | |
|
|
3569d49df6 | |
|
|
e0f79e815e | |
|
|
04f4991a92 | |
|
|
f2a84366d1 | |
|
|
05fa7e9a90 | |
|
|
1b5b876ba0 | |
|
|
131d883299 | |
|
|
2148b37974 | |
|
|
ae1c760af0 | |
|
|
85b651cc02 | |
|
|
2fcf136fa3 | |
|
|
8e07cfd3d5 | |
|
|
9b1663f1c8 | |
|
|
103f122c18 | |
|
|
f1bb937c60 | |
|
|
1b89d2751b | |
|
|
7c4b1c94ef | |
|
|
1f3bef9e01 | |
|
|
1aff994c8e | |
|
|
fd646bc2a2 | |
|
|
516d3d76dc | |
|
|
5767e24eb1 | |
|
|
e8f2a3e7d9 | |
|
|
841f6c917d | |
|
|
7aee20a0bf | |
|
|
861f3510e4 | |
|
|
d2f020415e | |
|
|
0b84cfdc58 | |
|
|
31002b2ffc | |
|
|
da527b4816 | |
|
|
21615755f9 | |
|
|
845a088e69 | |
|
|
04dfaabb97 | |
|
|
98d409c5c3 | |
|
|
8e5836a21b | |
|
|
b4c6c2b4a7 | |
|
|
d320da622c | |
|
|
7895ac0562 | |
|
|
ac0e93ea27 | |
|
|
9fff788546 | |
|
|
f671c9cb65 | |
|
|
3a03409617 | |
|
|
36906e84e0 | |
|
|
21020c1ca9 | |
|
|
fccd21eefd | |
|
|
05718efc2e | |
|
|
2cdf58f523 | |
|
|
6a049918d6 | |
|
|
8d39c2006e | |
|
|
a135ea0aec | |
|
|
f36da68598 | |
|
|
e2b6963a6b | |
|
|
0117e7ece4 | |
|
|
52fee08e45 | |
|
|
75945bb8fe | |
|
|
5de36ddb15 | |
|
|
e32385fc85 | |
|
|
f0705c9cce | |
|
|
429955e561 | |
|
|
d27e5c9658 | |
|
|
43339f7df8 | |
|
|
eb3c28d0be | |
|
|
3f1f685b83 | |
|
|
19b010a49c | |
|
|
fc51f2f272 | |
|
|
7a4435dd51 | |
|
|
390aca598b | |
|
|
c32548b8a2 | |
|
|
7e0e460078 | |
|
|
888cc1bfc5 | |
|
|
f1da724533 | |
|
|
da003448ff | |
|
|
cec967acc7 | |
|
|
56d9b66f83 | |
|
|
a8174b45a9 | |
|
|
e5b4c725be | |
|
|
36c707bfb8 | |
|
|
68eac029c1 | |
|
|
b7da22180c | |
|
|
6fa2487ee0 | |
|
|
b1be6e0bd5 | |
|
|
69fa73c9c0 | |
|
|
a8917c17ac | |
|
|
7c494b0476 | |
|
|
e08057c6d2 | |
|
|
1c7ac5c7fb | |
|
|
c5dca9c560 | |
|
|
9b9af1dae5 | |
|
|
59e90106b6 | |
|
|
acec341364 | |
|
|
47970a1cd3 | |
|
|
635579ad75 | |
|
|
4891dca38c | |
|
|
fc7c127a4e | |
|
|
f6422c7a40 | |
|
|
63b312227f | |
|
|
e6808f987c | |
|
|
09342806f2 | |
|
|
4cd1feea18 | |
|
|
6c90d6426b | |
|
|
8e3f1e1eaf | |
|
|
050057253a | |
|
|
83a341767e | |
|
|
114e91322c | |
|
|
2ee87c05ff | |
|
|
92717803ba | |
|
|
5b1a5fe4e9 | |
|
|
3e9f8b5669 | |
|
|
f89f1abffd | |
|
|
c1354b5bb8 | |
|
|
fb1a5c1d51 | |
|
|
bc555f8ba6 | |
|
|
fa9268b124 | |
|
|
880d5ced54 | |
|
|
bcf66b9e9e | |
|
|
aff437bd13 | |
|
|
36f0b1ff07 | |
|
|
3440d20977 | |
|
|
570056426c | |
|
|
b0d19484a7 | |
|
|
3d2c04c7de | |
|
|
73835701a6 | |
|
|
3cc11fbffb | |
|
|
e939245603 | |
|
|
69d7912e58 | |
|
|
665f3db149 | |
|
|
c7de78b9f3 | |
|
|
c2c3c1d193 | |
|
|
6eca1f1e71 | |
|
|
1b23996479 | |
|
|
2cafb64f4b | |
|
|
2faeb965ca | |
|
|
08f878b8a9 | |
|
|
a1b5ddc398 | |
|
|
95a2c4c58d | |
|
|
e325f9956d | |
|
|
c138e3eb21 | |
|
|
e38c7e4a6c | |
|
|
1d026e4a7d | |
|
|
e3f66a75ec | |
|
|
74ad11dd5c | |
|
|
f4f70bbf82 | |
|
|
9e57a9db7a | |
|
|
5287ed631f | |
|
|
29a8b864cb | |
|
|
69334e3f98 | |
|
|
c5ceade2a5 | |
|
|
51a7c56fb5 | |
|
|
5af01fec24 | |
|
|
e15fcebb02 | |
|
|
12b8087042 | |
|
|
8f9ad60d2b | |
|
|
8cbaedb403 | |
|
|
6cf14dfce2 | |
|
|
859ecfbcdd | |
|
|
4cc1bacc20 | |
|
|
cdb518ea97 | |
|
|
6786b67c10 | |
|
|
b774d2cefc | |
|
|
89e73262aa | |
|
|
cd8f89614a | |
|
|
5841d15a86 | |
|
|
da3fb64073 | |
|
|
87117f31e4 | |
|
|
0a3553c704 | |
|
|
59c3a84718 | |
|
|
464ef90c79 | |
|
|
a3d226a4cb | |
|
|
087ef665c2 | |
|
|
f92869cff5 | |
|
|
eae032405d | |
|
|
eb914b5418 | |
|
|
e5a19c78a6 | |
|
|
ef013ca5e0 | |
|
|
928514d57c | |
|
|
bd83a1993b | |
|
|
faf7f6820b | |
|
|
e59e9033f8 | |
|
|
1dc8b38c0c | |
|
|
be983f2a71 | |
|
|
cd99eba442 | |
|
|
bd3d831355 | |
|
|
ff8b51dbf1 | |
|
|
4219c6dbc7 | |
|
|
2cef860128 | |
|
|
317b388088 | |
|
|
ae686ffbb6 | |
|
|
e878b4bbf2 | |
|
|
d4e30455f1 | |
|
|
6b9f2be9d6 | |
|
|
076fb233e2 | |
|
|
084cfc49f7 | |
|
|
2e40d14432 | |
|
|
17ea55ad07 | |
|
|
6d5bca0762 | |
|
|
5b3dca2907 | |
|
|
7184bd4e9a | |
|
|
026e231507 | |
|
|
3afbe8a80e | |
|
|
471cbeb208 | |
|
|
c4ddcc952d | |
|
|
5b6b8aa9f4 | |
|
|
df2d90ba3b | |
|
|
a47e5ab704 | |
|
|
23b101ad8c | |
|
|
2d9247fc15 | |
|
|
5c10b41547 | |
|
|
2162636512 | |
|
|
6ff7f7fd89 | |
|
|
5f733acfe4 | |
|
|
c767e330a3 | |
|
|
482ecf8b7f | |
|
|
b4efa38e36 | |
|
|
eca5021f66 | |
|
|
c36ad3e282 | |
|
|
a7918ba174 | |
|
|
b9e54e032a | |
|
|
7f71f7734a | |
|
|
3c06fcb305 | |
|
|
4f1f50a2d5 | |
|
|
c1984b5071 | |
|
|
cc02fa4f7a | |
|
|
811409d250 | |
|
|
b6c1e40e28 | |
|
|
9db132b8b7 | |
|
|
e545fd1630 | |
|
|
a5e022e440 | |
|
|
a9ee101337 | |
|
|
7bd0774790 | |
|
|
b618dc45e9 | |
|
|
2d74acb39d | |
|
|
4d66a5b2a5 | |
|
|
ed69f431f8 | |
|
|
93fcbe5012 |
|
|
@ -0,0 +1,445 @@
|
|||
name: openvidu-components-angular Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'openvidu-components-angular/**'
|
||||
- '.github/workflows/openvidu-components-angular-tests.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
commit_sha:
|
||||
description: 'Commit SHA'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
test_setup:
|
||||
name: Test setup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Commit URL
|
||||
run: echo https://github.com/OpenVidu/openvidu/commit/${{ inputs.commit_sha || github.sha }}
|
||||
- name: Send Dispatch Event
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.OPENVIDU_DISPATCH_EVENT_GA }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message || 'Manually' }}
|
||||
COMMIT_URL: ${{ github.event.commits[0].url || 'Manually' }}
|
||||
BRANCH_NAME: ${{ github.ref_name }}
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
||||
https://api.github.com/repos/OpenVidu/openvidu-call/dispatches \
|
||||
-d '{"event_type":"openvidu-components-angular","client_payload":{"commit-message":"'"$COMMIT_MESSAGE"'","commit-ref":"'"$COMMIT_URL"'", "branch-name":"'"$BRANCH_NAME"'"}}'
|
||||
|
||||
nested_events:
|
||||
needs: test_setup
|
||||
name: Nested events
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd openvidu-components-angular
|
||||
npm install
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run nested components E2E event tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:nested-events --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
nested_structural_directives:
|
||||
needs: test_setup
|
||||
name: Nested Structural Directives
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run nested structural directives tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:nested-structural-directives --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
nested_attribute_directives:
|
||||
needs: test_setup
|
||||
name: Nested Attribute Directives
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run nested attribute directives tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:nested-attribute-directives --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_directives:
|
||||
needs: test_setup
|
||||
name: API Directives Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-directives --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
e2e_internal_directives:
|
||||
needs: test_setup
|
||||
name: Internal Directives Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-internal-directives --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_chat:
|
||||
needs: test_setup
|
||||
name: Chat E2E
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-chat --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_events:
|
||||
needs: test_setup
|
||||
name: Events E2E
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-events --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_media_devices:
|
||||
needs: test_setup
|
||||
name: Media devices E2E
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-media-devices --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_panels:
|
||||
needs: test_setup
|
||||
name: Panels E2E
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-panels --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_screen_sharing:
|
||||
needs: test_setup
|
||||
name: Screen sharing E2E
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-screensharing --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_stream:
|
||||
needs: test_setup
|
||||
name: Stream E2E
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -v $(pwd)/openvidu-components-angular/e2e/assets:/e2e-assets selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Tests
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-stream --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
e2e_toolbar:
|
||||
needs: test_setup
|
||||
name: Toolbar E2E
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.commit_sha || github.sha }}
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
- name: Install wait-on package
|
||||
run: npm install -g wait-on
|
||||
# - name: Run Browserless Chrome
|
||||
# run: docker run -d -p 3000:3000 --network host browserless/chrome:1.57-chrome-stable
|
||||
- name: Run Chrome
|
||||
run: docker run --network=host -d -p 4444:4444 selenium/standalone-chrome:138.0
|
||||
- name: Run openvidu-local-deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
- name: Start OpenVidu Call backend
|
||||
uses: OpenVidu/actions/start-openvidu-call@main
|
||||
- name: Build and Serve openvidu-components-angular Testapp
|
||||
uses: OpenVidu/actions/start-openvidu-components-testapp@main
|
||||
- name: Run Webcomponent E2E
|
||||
env:
|
||||
LAUNCH_MODE: CI
|
||||
run: npm run e2e:lib-toolbar --prefix openvidu-components-angular
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
name: OpenVidu integration tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "openvidu-test-integration/**"
|
||||
- ".github/workflows/openvidu-integration-tests.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
name: Integration tests
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Configure OpenVidu Local Deployment
|
||||
uses: OpenVidu/actions/start-openvidu-local-deployment@main
|
||||
with:
|
||||
ref-openvidu-local-deployment: development
|
||||
pre_startup_commands: |
|
||||
sed -i 's/interval: 10s/interval: 1s/' livekit.yaml
|
||||
sed -i '/interval: 1s/a \ fixer_interval: 10s' livekit.yaml
|
||||
- name: Install LiveKit CLI
|
||||
run: |
|
||||
curl -sSL https://get.livekit.io/cli | bash
|
||||
|
||||
- name: Checkout current repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: openvidu
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: ./openvidu/openvidu-test-integration
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
working-directory: ./openvidu/openvidu-test-integration
|
||||
run: npm run test:ci
|
||||
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: openvidu-integration-tests-report
|
||||
path: ./openvidu/openvidu-test-integration/test-results.json
|
||||
retention-days: 7
|
||||
- name: Cleanup
|
||||
if: always()
|
||||
uses: OpenVidu/actions/cleanup@main
|
||||
|
||||
|
|
@ -25,3 +25,5 @@ nbactions.xml
|
|||
*/.classpath
|
||||
*/.settings/*
|
||||
*/.tscache/*
|
||||
|
||||
.factorypath
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "openvidu-livekit"]
|
||||
path = openvidu-livekit
|
||||
url = https://github.com/OpenVidu/openvidu-livekit.git
|
||||
2
NOTICE
2
NOTICE
|
|
@ -1,4 +1,4 @@
|
|||
(C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
(C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
|||
17
README.md
17
README.md
|
|
@ -1,11 +1,13 @@
|
|||
[](#backers) [](#sponsors) [](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](#backers) [](#sponsors)
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
|
||||
[](https://npmjs.org/package/openvidu-browser)
|
||||
[](https://npmjs.org/package/openvidu-browser)
|
||||
|
||||
|
||||
[](https://docs.openvidu.io/en/stable/?badge=stable)
|
||||
[](https://docs.openvidu.io/en/stable/?badge=stable)
|
||||
[](https://hub.docker.com/r/openvidu/openvidu-server-kms)
|
||||
[](https://groups.google.com/forum/#!forum/openvidu)
|
||||
[](https://openvidu.discourse.group/)
|
||||
[](https://twitter.com/openvidu)
|
||||
|
||||
[![][OpenViduLogo]](https://openvidu.io)
|
||||
|
|
@ -15,6 +17,10 @@ openvidu
|
|||
|
||||
Visit [openvidu.io](https://openvidu.io)
|
||||
|
||||
## Community Forum
|
||||
|
||||
Visit [OpenVidu Community Forum](https://openvidu.discourse.group/)
|
||||
|
||||
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120
|
||||
|
||||
## Contributors
|
||||
|
|
@ -29,6 +35,11 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
|
|||
|
||||
<a href="https://opencollective.com/openvidu#backers" target="_blank"><img src="https://opencollective.com/openvidu/backers.svg?width=890"></a>
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
OpenVidu has been supported under project "CPP2021-008720 NewGenVidu: An elastic, user-friendly and privacy-friendly videoconferencing platform", funded by MCIN/AEI/10.13039/501100011033 and by the European Union-NextGenerationEU/PRTR.
|
||||
|
||||
<img height="75px" src="https://docs.openvidu.io/en/stable/img/logos/support.jpg">
|
||||
|
||||
## Sponsors
|
||||
|
||||
|
|
|
|||
25
ROADMAP.md
25
ROADMAP.md
|
|
@ -1,25 +0,0 @@
|
|||
# Next release
|
||||
|
||||
* IP Cameras support
|
||||
* Angular version update in library and tutorials
|
||||
* Ionic version update in tutorials
|
||||
* Dynamic change of cluster size (PRO)
|
||||
* On premises installation license management (PRO)
|
||||
|
||||
# Medium term
|
||||
* Multiple streams per participant
|
||||
* OpenVidu Call features for One to Many sessions
|
||||
* Revamped documentation and tutorials
|
||||
* Out of the box deployment in Azure, GCP and DO cloud providers
|
||||
* Elasticity (auto-scaling) (PRO)
|
||||
* Kubernetes support (PRO)
|
||||
* Simulcast and SVC (PRO)
|
||||
* Improved resource consumption (PRO)
|
||||
|
||||
# Long term
|
||||
* Dynamic change of participant roles
|
||||
* Granular permissions for participants
|
||||
* P2P sessions with 2 participants (PRO)
|
||||
* KurentoClient for advanced management of media sessions (PRO)
|
||||
* RTMP, HLS and DASH support (PRO)
|
||||
* Sessions migration between cluster nodes (for reduced resource consumption) (PRO)
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
docs/
|
||||
lib/
|
||||
static/*.js
|
||||
|
|
@ -1 +0,0 @@
|
|||
docs/
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"array-type": [
|
||||
true,
|
||||
"array"
|
||||
],
|
||||
"ban-types": {
|
||||
"options": [
|
||||
[
|
||||
"Object",
|
||||
"Avoid using the `Object` type. Did you mean `object`?"
|
||||
],
|
||||
[
|
||||
"Function",
|
||||
"Avoid using the `Function` type. Prefer a specific function type, like `() => void`, or use `ts.AnyFunction`."
|
||||
],
|
||||
[
|
||||
"Boolean",
|
||||
"Avoid using the `Boolean` type. Did you mean `boolean`?"
|
||||
],
|
||||
[
|
||||
"Number",
|
||||
"Avoid using the `Number` type. Did you mean `number`?"
|
||||
],
|
||||
[
|
||||
"String",
|
||||
"Avoid using the `String` type. Did you mean `string`?"
|
||||
]
|
||||
]
|
||||
},
|
||||
"class-name": true,
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"curly": [
|
||||
true,
|
||||
"ignore-same-line"
|
||||
],
|
||||
"indent": [
|
||||
true,
|
||||
"spaces",
|
||||
2
|
||||
],
|
||||
"interface-name": [
|
||||
true,
|
||||
"never-prefix"
|
||||
],
|
||||
"interface-over-type-literal": true,
|
||||
"jsdoc-format": true,
|
||||
"no-inferrable-types": true,
|
||||
"no-internal-module": true,
|
||||
"no-null-keyword": false,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-trailing-whitespace": [
|
||||
true,
|
||||
"ignore-template-strings"
|
||||
],
|
||||
"no-var-keyword": true,
|
||||
"object-literal-shorthand": true,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-whitespace"
|
||||
],
|
||||
"prefer-const": true,
|
||||
"quotemark": [
|
||||
true,
|
||||
"single",
|
||||
"avoid-escape",
|
||||
"avoid-template"
|
||||
],
|
||||
"semicolon": [
|
||||
true,
|
||||
"always",
|
||||
"ignore-bound-class-methods"
|
||||
],
|
||||
"space-within-parens": true,
|
||||
"triple-equals": true,
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-module",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"no-implicit-dependencies": [
|
||||
true,
|
||||
"dev"
|
||||
],
|
||||
"object-literal-key-quotes": [
|
||||
true,
|
||||
"consistent-as-needed"
|
||||
],
|
||||
"variable-name": [
|
||||
true,
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-leading-underscore"
|
||||
],
|
||||
"arrow-parens": false,
|
||||
"arrow-return-shorthand": false,
|
||||
"forin": false,
|
||||
"member-access": false,
|
||||
"no-conditional-assignment": false,
|
||||
"no-console": false,
|
||||
"no-debugger": false,
|
||||
"no-empty-interface": false,
|
||||
"no-eval": false,
|
||||
"no-object-literal-type-assertion": false,
|
||||
"no-shadowed-variable": false,
|
||||
"no-submodule-imports": false,
|
||||
"no-var-requires": false,
|
||||
"ordered-imports": false,
|
||||
"prefer-conditional-expression": false,
|
||||
"radix": false,
|
||||
"trailing-comma": false,
|
||||
"align": false,
|
||||
"eofline": false,
|
||||
"max-line-length": false,
|
||||
"no-consecutive-blank-lines": false,
|
||||
"space-before-function-paren": false,
|
||||
"ban-comma-operator": false,
|
||||
"max-classes-per-file": false,
|
||||
"member-ordering": false,
|
||||
"no-angle-bracket-type-assertion": false,
|
||||
"no-bitwise": false,
|
||||
"no-namespace": false,
|
||||
"no-reference": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"one-variable-per-declaration": false,
|
||||
"unified-signatures": false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
module.exports = {
|
||||
lib: [
|
||||
"lib.dom.d.ts",
|
||||
"lib.es5.d.ts",
|
||||
"lib.es2015.promise.d.ts",
|
||||
"lib.scripthost.d.ts"
|
||||
],
|
||||
mode: "file",
|
||||
module: "commonjs",
|
||||
name: "OpenVidu Browser",
|
||||
target: "es5",
|
||||
externalPattern: "node_modules",
|
||||
exclude: [
|
||||
"**/OpenViduInternal/Interfaces/Private/**",
|
||||
"**/OpenViduInternal/WebRtcStats/WebRtcStats.ts",
|
||||
"**/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts",
|
||||
"**/OpenViduInternal/ScreenSharing/**",
|
||||
"**/OpenViduInternal/KurentoUtils/**"
|
||||
],
|
||||
excludeExternals: true,
|
||||
excludePrivate: true,
|
||||
excludeProtected: true,
|
||||
excludeNotExported: true,
|
||||
theme: "default",
|
||||
readme: "none",
|
||||
includeVersion: true,
|
||||
listInvalidSymbolLinks: true,
|
||||
out: "./docs"
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ -z "$BASEHREF_VERSION" ]]; then
|
||||
echo "Example of use: \"BASEHREF_VERSION=2.12.0 ${0}\"" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Replace version from "stable" to the specified one in all TypeDoc links
|
||||
grep -rl '/en/stable/' src | xargs sed -i -e 's|/en/stable/|/en/'${BASEHREF_VERSION}'/|g'
|
||||
|
||||
# Generate TypeDoc
|
||||
./node_modules/typedoc/bin/typedoc --options ./config/typedoc.js ./src
|
||||
|
||||
# Return links to "stable" version
|
||||
grep -rl '/en/'${BASEHREF_VERSION}'/' src | xargs sed -i -e 's|/en/'${BASEHREF_VERSION}'/|/en/stable/|g'
|
||||
|
||||
# Clean previous docs from openvidu.io-docs repo and copy new ones
|
||||
rm -rf ../../openvidu.io-docs/docs/api/openvidu-browser/*
|
||||
cp -R ./docs/. ../../openvidu.io-docs/docs/api/openvidu-browser
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
{
|
||||
"author": "OpenVidu",
|
||||
"dependencies": {
|
||||
"freeice": "2.2.2",
|
||||
"hark": "1.2.3",
|
||||
"platform": "1.3.6",
|
||||
"uuid": "8.3.1",
|
||||
"wolfy87-eventemitter": "5.2.9"
|
||||
},
|
||||
"description": "OpenVidu Browser",
|
||||
"devDependencies": {
|
||||
"@types/node": "14.14.7",
|
||||
"@types/platform": "1.3.3",
|
||||
"browserify": "17.0.0",
|
||||
"grunt": "1.3.0",
|
||||
"grunt-cli": "1.3.2",
|
||||
"grunt-contrib-copy": "1.0.0",
|
||||
"grunt-contrib-sass": "2.0.0",
|
||||
"grunt-contrib-uglify": "5.0.0",
|
||||
"grunt-contrib-watch": "1.1.0",
|
||||
"grunt-postcss": "0.9.0",
|
||||
"grunt-string-replace": "1.3.1",
|
||||
"grunt-ts": "6.0.0-beta.22",
|
||||
"terser": "5.3.8",
|
||||
"tsify": "5.0.2",
|
||||
"tslint": "6.1.3",
|
||||
"typedoc": "0.19.2",
|
||||
"typescript": "4.0.5"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"main": "lib/index.js",
|
||||
"name": "openvidu-browser",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/OpenVidu/openvidu"
|
||||
},
|
||||
"scripts": {
|
||||
"browserify": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js Main.ts -p [ tsify ] --exclude kurento-browser-extensions --debug -o ../static/js/openvidu-browser-$VERSION.js -v",
|
||||
"browserify-prod": "VERSION=${VERSION:-dev}; mkdir -p static/js/ && cd src && ../node_modules/browserify/bin/cmd.js --debug Main.ts -p [ tsify ] --exclude kurento-browser-extensions | ../node_modules/terser/bin/terser --source-map content=inline --output ../static/js/openvidu-browser-$VERSION.min.js",
|
||||
"build": "cd src/OpenVidu && ./../../node_modules/typescript/bin/tsc && cd ../.. && ./node_modules/typescript/bin/tsc --declaration src/index.ts --outDir ./lib --sourceMap --lib dom,es5,es2015.promise,scripthost",
|
||||
"docs": "./generate-docs.sh"
|
||||
},
|
||||
"types": "lib/index.d.ts",
|
||||
"version": "2.16.0"
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import { OpenVidu } from './OpenVidu/OpenVidu';
|
||||
|
||||
if (window) {
|
||||
window['OpenVidu'] = OpenVidu;
|
||||
}
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Session } from './Session';
|
||||
import { Stream } from './Stream';
|
||||
import { LocalConnectionOptions } from '../OpenViduInternal/Interfaces/Private/LocalConnectionOptions';
|
||||
import { RemoteConnectionOptions } from '../OpenViduInternal/Interfaces/Private/RemoteConnectionOptions';
|
||||
import { InboundStreamOptions } from '../OpenViduInternal/Interfaces/Private/InboundStreamOptions';
|
||||
import { StreamOptionsServer } from '../OpenViduInternal/Interfaces/Private/StreamOptionsServer';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
|
||||
/**
|
||||
* Represents each one of the user's connection to the session (the local one and other user's connections).
|
||||
* Therefore each [[Session]] and [[Stream]] object has an attribute of type Connection
|
||||
*/
|
||||
export class Connection {
|
||||
|
||||
/**
|
||||
* Unique identifier of the connection
|
||||
*/
|
||||
connectionId: string;
|
||||
|
||||
/**
|
||||
* Time when this connection was created in OpenVidu Server (UTC milliseconds)
|
||||
*/
|
||||
creationTime: number;
|
||||
|
||||
/**
|
||||
* Data associated to this connection (and therefore to certain user). This is an important field:
|
||||
* it allows you to broadcast all the information you want for each user (a username, for example)
|
||||
*/
|
||||
data: string;
|
||||
|
||||
/**
|
||||
* Role of the connection.
|
||||
* - `SUBSCRIBER`: can subscribe to published Streams of other users by calling [[Session.subscribe]]
|
||||
* - `PUBLISHER`: SUBSCRIBER permissions + can publish their own Streams by calling [[Session.publish]]
|
||||
* - `MODERATOR`: SUBSCRIBER + PUBLISHER permissions + can force the unpublishing or disconnection over a third-party Stream or Connection by call [[Session.forceUnpublish]] and [[Session.forceDisconnect]]
|
||||
*
|
||||
* **Only defined for the local connection. In remote connections will be `undefined`**
|
||||
*/
|
||||
role: string;
|
||||
|
||||
/**
|
||||
* Whether the streams published by this connection will be recorded or not. This only affects [INDIVIDUAL recording](/en/stable/advanced-features/recording#selecting-streams-to-be-recorded) <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
|
||||
*
|
||||
* **Only defined for the local connection. In remote connections will be `undefined`**
|
||||
*/
|
||||
record: boolean;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
stream?: Stream;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
localOptions: LocalConnectionOptions | undefined;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
remoteOptions: RemoteConnectionOptions | undefined;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
disposed = false;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
rpcSessionId: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(private session: Session, connectionOptions: LocalConnectionOptions | RemoteConnectionOptions) {
|
||||
let msg = "'Connection' created ";
|
||||
if (!!(<LocalConnectionOptions>connectionOptions).role) {
|
||||
// Connection is local
|
||||
this.localOptions = <LocalConnectionOptions>connectionOptions;
|
||||
this.connectionId = this.localOptions.id;
|
||||
this.creationTime = this.localOptions.createdAt;
|
||||
this.data = this.localOptions.metadata;
|
||||
this.rpcSessionId = this.localOptions.sessionId;
|
||||
this.role = this.localOptions.role;
|
||||
this.record = this.localOptions.record;
|
||||
msg += '(local)';
|
||||
} else {
|
||||
// Connection is remote
|
||||
this.remoteOptions = <RemoteConnectionOptions>connectionOptions;
|
||||
this.connectionId = this.remoteOptions.id;
|
||||
this.creationTime = this.remoteOptions.createdAt;
|
||||
if (this.remoteOptions.metadata) {
|
||||
this.data = this.remoteOptions.metadata;
|
||||
}
|
||||
if (this.remoteOptions.streams) {
|
||||
this.initRemoteStreams(this.remoteOptions.streams);
|
||||
}
|
||||
msg += "(remote) with 'connectionId' [" + this.remoteOptions.id + ']';
|
||||
}
|
||||
logger.info(msg);
|
||||
}
|
||||
|
||||
|
||||
/* Hidden methods */
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
sendIceCandidate(candidate: RTCIceCandidate): void {
|
||||
|
||||
logger.debug((!!this.stream!.outboundStreamOpts ? 'Local' : 'Remote') + 'candidate for' +
|
||||
this.connectionId, candidate);
|
||||
|
||||
this.session.openvidu.sendRequest('onIceCandidate', {
|
||||
endpointName: this.connectionId,
|
||||
candidate: candidate.candidate,
|
||||
sdpMid: candidate.sdpMid,
|
||||
sdpMLineIndex: candidate.sdpMLineIndex
|
||||
}, (error, response) => {
|
||||
if (error) {
|
||||
logger.error('Error sending ICE candidate: '
|
||||
+ JSON.stringify(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initRemoteStreams(options: StreamOptionsServer[]): void {
|
||||
|
||||
// This is ready for supporting multiple streams per Connection object. Right now the loop will always run just once
|
||||
// this.stream should also be replaced by a collection of streams to support multiple streams per Connection
|
||||
options.forEach(opts => {
|
||||
const streamOptions: InboundStreamOptions = {
|
||||
id: opts.id,
|
||||
createdAt: opts.createdAt,
|
||||
connection: this,
|
||||
hasAudio: opts.hasAudio,
|
||||
hasVideo: opts.hasVideo,
|
||||
audioActive: opts.audioActive,
|
||||
videoActive: opts.videoActive,
|
||||
typeOfVideo: opts.typeOfVideo,
|
||||
frameRate: opts.frameRate,
|
||||
videoDimensions: !!opts.videoDimensions ? JSON.parse(opts.videoDimensions) : undefined,
|
||||
filter: !!opts.filter ? opts.filter : undefined
|
||||
};
|
||||
const stream = new Stream(this.session, streamOptions);
|
||||
|
||||
this.addStream(stream);
|
||||
});
|
||||
|
||||
logger.info("Remote 'Connection' with 'connectionId' [" + this.connectionId + '] is now configured for receiving Streams with options: ', this.stream!.inboundStreamOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
addStream(stream: Stream): void {
|
||||
stream.connection = this;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
removeStream(streamId: string): void {
|
||||
delete this.stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
dispose(): void {
|
||||
if (!!this.stream) {
|
||||
delete this.stream;
|
||||
}
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event as Event } from '../OpenViduInternal/Events/Event';
|
||||
import EventEmitter = require('wolfy87-eventemitter');
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
export abstract class EventDispatcher {
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
userHandlerArrowHandler: WeakMap<(event: Event) => void, (event: Event) => void> = new WeakMap();
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
ee = new EventEmitter();
|
||||
|
||||
/**
|
||||
* Adds function `handler` to handle event `type`
|
||||
*
|
||||
* @returns The EventDispatcher object
|
||||
*/
|
||||
abstract on(type: string, handler: (event: Event) => void): EventDispatcher;
|
||||
|
||||
/**
|
||||
* Adds function `handler` to handle event `type` just once. The handler will be automatically removed after first execution
|
||||
*
|
||||
* @returns The object that dispatched the event
|
||||
*/
|
||||
abstract once(type: string, handler: (event: Event) => void): EventDispatcher;
|
||||
|
||||
/**
|
||||
* Removes a `handler` from event `type`. If no handler is provided, all handlers will be removed from the event
|
||||
*
|
||||
* @returns The object that dispatched the event
|
||||
*/
|
||||
off(type: string, handler?: (event: Event) => void): EventDispatcher {
|
||||
if (!handler) {
|
||||
this.ee.removeAllListeners(type);
|
||||
} else {
|
||||
// Must remove internal arrow function handler paired with user handler
|
||||
const arrowHandler = this.userHandlerArrowHandler.get(handler);
|
||||
if (!!arrowHandler) {
|
||||
this.ee.off(type, arrowHandler);
|
||||
}
|
||||
this.userHandlerArrowHandler.delete(handler);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
onAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher {
|
||||
const arrowHandler = event => {
|
||||
if (event) {
|
||||
logger.info(message, event);
|
||||
} else {
|
||||
logger.info(message);
|
||||
}
|
||||
handler(event);
|
||||
};
|
||||
this.userHandlerArrowHandler.set(handler, arrowHandler);
|
||||
this.ee.on(type, arrowHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
onceAux(type: string, message: string, handler: (event: Event) => void): EventDispatcher {
|
||||
const arrowHandler = event => {
|
||||
if (event) {
|
||||
logger.info(message, event);
|
||||
} else {
|
||||
logger.info(message);
|
||||
}
|
||||
handler(event);
|
||||
// Remove handler from map after first and only execution
|
||||
this.userHandlerArrowHandler.delete(handler);
|
||||
};
|
||||
this.userHandlerArrowHandler.set(handler, arrowHandler);
|
||||
this.ee.once(type, arrowHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { FilterEvent } from '../OpenViduInternal/Events/FilterEvent';
|
||||
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
|
||||
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* **WARNING**: experimental option. This interface may change in the near future
|
||||
*
|
||||
* Video/audio filter applied to a Stream. See [[Stream.applyFilter]]
|
||||
*/
|
||||
export class Filter {
|
||||
|
||||
/**
|
||||
* Type of filter applied. This is the name of the remote class identifying the filter to apply in Kurento Media Server.
|
||||
* For example: `"FaceOverlayFilter"`, `"GStreamerFilter"`.
|
||||
*
|
||||
* You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's
|
||||
* [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L4)
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Parameters used to initialize the filter.
|
||||
* These correspond to the constructor parameters used in the filter in Kurento Media Server (except for `mediaPipeline` parameter, which is never needed).
|
||||
*
|
||||
* For example: for `filter.type = "GStreamerFilter"` could be `filter.options = {"command": "videobalance saturation=0.0"}`
|
||||
*
|
||||
* You can get this property in `*.kmd.json` files defining the Kurento filters. For example, for GStreamerFilter that's
|
||||
* [here](https://github.com/Kurento/kms-filters/blob/53a452fac71d61795952e3d2202156c6b00f6d65/src/server/interface/filters.GStreamerFilter.kmd.json#L13-L31)
|
||||
*/
|
||||
options: Object;
|
||||
|
||||
/**
|
||||
* Value passed the last time [[Filter.execMethod]] was called. If `undefined` this method has not been called yet.
|
||||
*
|
||||
* You can use this value to know the current status of any applied filter
|
||||
*/
|
||||
lastExecMethod?: {
|
||||
method: string, params: Object
|
||||
};
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
handlers: Map<string, (event: FilterEvent) => void> = new Map();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
stream: Stream;
|
||||
private logger: OpenViduLogger;
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(type: string, options: Object) {
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes a filter method. Available methods are specific for each filter
|
||||
*
|
||||
* @param method Name of the method
|
||||
* @param params Parameters of the method
|
||||
*/
|
||||
execMethod(method: string, params: Object): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Executing filter method to stream ' + this.stream.streamId);
|
||||
let stringParams;
|
||||
if (typeof params !== 'string') {
|
||||
try {
|
||||
stringParams = JSON.stringify(params);
|
||||
} catch (error) {
|
||||
const errorMsg = "'params' property must be a JSON formatted object";
|
||||
logger.error(errorMsg);
|
||||
reject(errorMsg);
|
||||
}
|
||||
} else {
|
||||
stringParams = <string>params;
|
||||
}
|
||||
this.stream.session.openvidu.sendRequest(
|
||||
'execFilterMethod',
|
||||
{ streamId: this.stream.streamId, method, params: stringParams },
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error('Error executing filter method for Stream ' + this.stream.streamId, error);
|
||||
if (error.code === 401) {
|
||||
reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to execute a filter method"));
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
} else {
|
||||
logger.info('Filter method successfully executed on Stream ' + this.stream.streamId);
|
||||
const oldValue = (<any>Object).assign({}, this.stream.filter);
|
||||
this.stream.filter!.lastExecMethod = { method, params: JSON.parse(stringParams) };
|
||||
this.stream.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.session, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
|
||||
this.stream.streamManager.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.stream.streamManager, this.stream, 'filter', this.stream.filter!, oldValue, 'execFilterMethod')]);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subscribe to certain filter event. Available events are specific for each filter
|
||||
*
|
||||
* @param eventType Event to which subscribe to.
|
||||
* @param handler Function to execute upon event dispatched. It receives as parameter a [[FilterEvent]] object
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully attached to the filter and rejected with an Error object if not
|
||||
*/
|
||||
addEventListener(eventType: string, handler: (event: FilterEvent) => void): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Adding filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId);
|
||||
this.stream.session.openvidu.sendRequest(
|
||||
'addFilterEventListener',
|
||||
{ streamId: this.stream.streamId, eventType },
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error('Error adding filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId, error);
|
||||
if (error.code === 401) {
|
||||
reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to add a filter event listener"));
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
} else {
|
||||
this.handlers.set(eventType, handler);
|
||||
logger.info('Filter event listener to event ' + eventType + ' successfully applied on Stream ' + this.stream.streamId);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes certain filter event listener previously set.
|
||||
*
|
||||
* @param eventType Event to unsubscribe from.
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the event listener was successfully removed from the filter and rejected with an Error object in other case
|
||||
*/
|
||||
removeEventListener(eventType: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.info('Removing filter event listener to event ' + eventType + ' to stream ' + this.stream.streamId);
|
||||
this.stream.session.openvidu.sendRequest(
|
||||
'removeFilterEventListener',
|
||||
{ streamId: this.stream.streamId, eventType },
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error('Error removing filter event listener to event ' + eventType + 'for Stream ' + this.stream.streamId, error);
|
||||
if (error.code === 401) {
|
||||
reject(new OpenViduError(OpenViduErrorName.OPENVIDU_PERMISSION_DENIED, "You don't have permissions to add a filter event listener"));
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
} else {
|
||||
this.handlers.delete(eventType);
|
||||
logger.info('Filter event listener to event ' + eventType + ' successfully removed on Stream ' + this.stream.streamId);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,387 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { LocalRecorderState } from '../OpenViduInternal/Enums/LocalRecorderState';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
declare var MediaRecorder: any;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Easy recording of [[Stream]] objects straightaway from the browser. Initialized with [[OpenVidu.initLocalRecorder]] method
|
||||
*
|
||||
* > WARNINGS:
|
||||
* - Performing browser local recording of **remote streams** may cause some troubles. A long waiting time may be required after calling _LocalRecorder.stop()_ in this case
|
||||
* - Only Chrome and Firefox support local stream recording
|
||||
*/
|
||||
export class LocalRecorder {
|
||||
|
||||
state: LocalRecorderState;
|
||||
|
||||
private connectionId: string;
|
||||
private mediaRecorder: any;
|
||||
private chunks: any[] = [];
|
||||
private blob?: Blob;
|
||||
private id: string;
|
||||
private videoPreviewSrc: string;
|
||||
private videoPreview: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(private stream: Stream) {
|
||||
platform = PlatformUtils.getInstance();
|
||||
this.connectionId = (!!this.stream.connection) ? this.stream.connection.connectionId : 'default-connection';
|
||||
this.id = this.stream.streamId + '_' + this.connectionId + '_localrecord';
|
||||
this.state = LocalRecorderState.READY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the recording of the Stream. [[state]] property must be `READY`. After method succeeds is set to `RECORDING`
|
||||
*
|
||||
* @param mimeType The [MediaRecorder.mimeType](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/mimeType) to be used to record this Stream.
|
||||
* Make sure the platform supports it or the promise will return an error. If this parameter is not provided, the MediaRecorder will use the default codecs available in the platform
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully started and rejected with an Error object if not
|
||||
*/
|
||||
record(mimeType?: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (typeof MediaRecorder === 'undefined') {
|
||||
logger.error('MediaRecorder not supported on your browser. See compatibility in https://caniuse.com/#search=MediaRecorder');
|
||||
throw (Error('MediaRecorder not supported on your browser. See compatibility in https://caniuse.com/#search=MediaRecorder'));
|
||||
}
|
||||
if (this.state !== LocalRecorderState.READY) {
|
||||
throw (Error('\'LocalRecord.record()\' needs \'LocalRecord.state\' to be \'READY\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.clean()\' or init a new LocalRecorder before'));
|
||||
}
|
||||
logger.log("Starting local recording of stream '" + this.stream.streamId + "' of connection '" + this.connectionId + "'");
|
||||
|
||||
let options = {};
|
||||
if (typeof MediaRecorder.isTypeSupported === 'function') {
|
||||
if (!!mimeType) {
|
||||
if (!MediaRecorder.isTypeSupported(mimeType)) {
|
||||
reject(new Error('mimeType "' + mimeType + '" is not supported'));
|
||||
}
|
||||
options = { mimeType };
|
||||
} else {
|
||||
logger.log('No mimeType parameter provided. Using default codecs');
|
||||
}
|
||||
} else {
|
||||
logger.warn('MediaRecorder#isTypeSupported is not supported. Using default codecs');
|
||||
}
|
||||
|
||||
this.mediaRecorder = new MediaRecorder(this.stream.getMediaStream(), options);
|
||||
this.mediaRecorder.start(10);
|
||||
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
this.mediaRecorder.ondataavailable = (e) => {
|
||||
this.chunks.push(e.data);
|
||||
};
|
||||
|
||||
this.mediaRecorder.onerror = (e) => {
|
||||
logger.error('MediaRecorder error: ', e);
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstart = () => {
|
||||
logger.log('MediaRecorder started (state=' + this.mediaRecorder.state + ')');
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstop = () => {
|
||||
this.onStopDefault();
|
||||
};
|
||||
|
||||
this.mediaRecorder.onpause = () => {
|
||||
logger.log('MediaRecorder paused (state=' + this.mediaRecorder.state + ')');
|
||||
};
|
||||
|
||||
this.mediaRecorder.onresume = () => {
|
||||
logger.log('MediaRecorder resumed (state=' + this.mediaRecorder.state + ')');
|
||||
};
|
||||
|
||||
this.mediaRecorder.onwarning = (e) => {
|
||||
logger.log('MediaRecorder warning: ' + e);
|
||||
};
|
||||
|
||||
this.state = LocalRecorderState.RECORDING;
|
||||
resolve();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ends the recording of the Stream. [[state]] property must be `RECORDING` or `PAUSED`. After method succeeds is set to `FINISHED`
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording successfully stopped and rejected with an Error object if not
|
||||
*/
|
||||
stop(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (this.state === LocalRecorderState.READY || this.state === LocalRecorderState.FINISHED) {
|
||||
throw (Error('\'LocalRecord.stop()\' needs \'LocalRecord.state\' to be \'RECORDING\' or \'PAUSED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.start()\' before'));
|
||||
}
|
||||
this.mediaRecorder.onstop = () => {
|
||||
this.onStopDefault();
|
||||
resolve();
|
||||
};
|
||||
this.mediaRecorder.stop();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pauses the recording of the Stream. [[state]] property must be `RECORDING`. After method succeeds is set to `PAUSED`
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully paused and rejected with an Error object if not
|
||||
*/
|
||||
pause(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (this.state !== LocalRecorderState.RECORDING) {
|
||||
reject(Error('\'LocalRecord.pause()\' needs \'LocalRecord.state\' to be \'RECORDING\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.start()\' or \'LocalRecorder.resume()\' before'));
|
||||
}
|
||||
this.mediaRecorder.pause();
|
||||
this.state = LocalRecorderState.PAUSED;
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the recording of the Stream. [[state]] property must be `PAUSED`. After method succeeds is set to `RECORDING`
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the recording was successfully resumed and rejected with an Error object if not
|
||||
*/
|
||||
resume(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
if (this.state !== LocalRecorderState.PAUSED) {
|
||||
throw (Error('\'LocalRecord.resume()\' needs \'LocalRecord.state\' to be \'PAUSED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.pause()\' before'));
|
||||
}
|
||||
this.mediaRecorder.resume();
|
||||
this.state = LocalRecorderState.RECORDING;
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Previews the recording, appending a new HTMLVideoElement to element with id `parentId`. [[state]] property must be `FINISHED`
|
||||
*/
|
||||
preview(parentElement): HTMLVideoElement {
|
||||
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
throw (Error('\'LocalRecord.preview()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
}
|
||||
|
||||
this.videoPreview = document.createElement('video');
|
||||
|
||||
this.videoPreview.id = this.id;
|
||||
this.videoPreview.autoplay = true;
|
||||
|
||||
if (platform.isSafariBrowser()) {
|
||||
this.videoPreview.setAttribute('playsinline', 'true');
|
||||
}
|
||||
|
||||
if (typeof parentElement === 'string') {
|
||||
const parentElementDom = document.getElementById(parentElement);
|
||||
if (parentElementDom) {
|
||||
this.videoPreview = parentElementDom.appendChild(this.videoPreview);
|
||||
}
|
||||
} else {
|
||||
this.videoPreview = parentElement.appendChild(this.videoPreview);
|
||||
}
|
||||
|
||||
this.videoPreview.src = this.videoPreviewSrc;
|
||||
|
||||
return this.videoPreview;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gracefully stops and cleans the current recording (WARNING: it is completely dismissed). Sets [[state]] to `READY` so the recording can start again
|
||||
*/
|
||||
clean(): void {
|
||||
const f = () => {
|
||||
delete this.blob;
|
||||
this.chunks = [];
|
||||
delete this.mediaRecorder;
|
||||
this.state = LocalRecorderState.READY;
|
||||
};
|
||||
if (this.state === LocalRecorderState.RECORDING || this.state === LocalRecorderState.PAUSED) {
|
||||
this.stop().then(() => f()).catch(() => f());
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Downloads the recorded video through the browser. [[state]] property must be `FINISHED`
|
||||
*/
|
||||
download(): void {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
throw (Error('\'LocalRecord.download()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
} else {
|
||||
const a: HTMLAnchorElement = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
document.body.appendChild(a);
|
||||
|
||||
const url = window.URL.createObjectURL(this.blob);
|
||||
a.href = url;
|
||||
a.download = this.id + '.webm';
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw Blob file. Methods preview, download, uploadAsBinary and uploadAsMultipartfile use this same file to perform their specific actions. [[state]] property must be `FINISHED`
|
||||
*/
|
||||
getBlob(): Blob {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
throw (Error('Call \'LocalRecord.stop()\' before getting Blob file'));
|
||||
} else {
|
||||
return this.blob!;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uploads the recorded video as a binary file performing an HTTP/POST operation to URL `endpoint`. [[state]] property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example:
|
||||
* ```
|
||||
* var headers = {
|
||||
* "Cookie": "$Version=1; Skin=new;",
|
||||
* "Authorization":"Basic QWxhZGpbjpuIHNlctZQ=="
|
||||
* }
|
||||
* ```
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not
|
||||
*/
|
||||
uploadAsBinary(endpoint: string, headers?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
reject(Error('\'LocalRecord.uploadAsBinary()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
} else {
|
||||
const http = new XMLHttpRequest();
|
||||
http.open('POST', endpoint, true);
|
||||
|
||||
if (typeof headers === 'object') {
|
||||
for (const key of Object.keys(headers)) {
|
||||
http.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
}
|
||||
|
||||
http.onreadystatechange = () => {
|
||||
if (http.readyState === 4) {
|
||||
if (http.status.toString().charAt(0) === '2') {
|
||||
// Success response from server (HTTP status standard: 2XX is success)
|
||||
resolve(http.responseText);
|
||||
} else {
|
||||
reject(http.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
http.send(this.blob);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uploads the recorded video as a multipart file performing an HTTP/POST operation to URL `endpoint`. [[state]] property must be `FINISHED`. Optional HTTP headers can be passed as second parameter. For example:
|
||||
* ```
|
||||
* var headers = {
|
||||
* "Cookie": "$Version=1; Skin=new;",
|
||||
* "Authorization":"Basic QWxhZGpbjpuIHNlctZQ=="
|
||||
* }
|
||||
* ```
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved with the `http.responseText` from server if the operation was successful and rejected with the failed `http.status` if not:
|
||||
*/
|
||||
uploadAsMultipartfile(endpoint: string, headers?: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.state !== LocalRecorderState.FINISHED) {
|
||||
reject(Error('\'LocalRecord.uploadAsMultipartfile()\' needs \'LocalRecord.state\' to be \'FINISHED\' (current value: \'' + this.state + '\'). Call \'LocalRecorder.stop()\' before'));
|
||||
} else {
|
||||
const http = new XMLHttpRequest();
|
||||
http.open('POST', endpoint, true);
|
||||
|
||||
if (typeof headers === 'object') {
|
||||
for (const key of Object.keys(headers)) {
|
||||
http.setRequestHeader(key, headers[key]);
|
||||
}
|
||||
}
|
||||
|
||||
const sendable = new FormData();
|
||||
sendable.append('file', this.blob!, this.id + '.webm');
|
||||
|
||||
http.onreadystatechange = () => {
|
||||
if (http.readyState === 4) {
|
||||
if (http.status.toString().charAt(0) === '2') {
|
||||
// Success response from server (HTTP status standard: 2XX is success)
|
||||
resolve(http.responseText);
|
||||
} else {
|
||||
reject(http.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
http.send(sendable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Private methods */
|
||||
|
||||
private onStopDefault(): void {
|
||||
logger.log('MediaRecorder stopped (state=' + this.mediaRecorder.state + ')');
|
||||
|
||||
this.blob = new Blob(this.chunks, { type: 'video/webm' });
|
||||
this.chunks = [];
|
||||
|
||||
this.videoPreviewSrc = window.URL.createObjectURL(this.blob);
|
||||
|
||||
this.state = LocalRecorderState.FINISHED;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,718 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { OpenVidu } from './OpenVidu';
|
||||
import { Session } from './Session';
|
||||
import { Stream } from './Stream';
|
||||
import { StreamManager } from './StreamManager';
|
||||
import { EventDispatcher } from './EventDispatcher';
|
||||
import { PublisherProperties } from '../OpenViduInternal/Interfaces/Public/PublisherProperties';
|
||||
import { Event } from '../OpenViduInternal/Events/Event';
|
||||
import { StreamEvent } from '../OpenViduInternal/Events/StreamEvent';
|
||||
import { StreamPropertyChangedEvent } from '../OpenViduInternal/Events/StreamPropertyChangedEvent';
|
||||
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent';
|
||||
import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/OpenViduError';
|
||||
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
/**
|
||||
* Packs local media streams. Participants can publish it to a session. Initialized with [[OpenVidu.initPublisher]] method
|
||||
*
|
||||
* ### Available event listeners (and events dispatched)
|
||||
*
|
||||
* - accessAllowed
|
||||
* - accessDenied
|
||||
* - accessDialogOpened
|
||||
* - accessDialogClosed
|
||||
* - streamCreated ([[StreamEvent]])
|
||||
* - streamDestroyed ([[StreamEvent]])
|
||||
* - streamPropertyChanged ([[StreamPropertyChangedEvent]])
|
||||
*/
|
||||
export class Publisher extends StreamManager {
|
||||
|
||||
/**
|
||||
* Whether the Publisher has been granted access to the requested input devices or not
|
||||
*/
|
||||
accessAllowed = false;
|
||||
|
||||
/**
|
||||
* Whether you have called [[Publisher.subscribeToRemote]] with value `true` or `false` (*false* by default)
|
||||
*/
|
||||
isSubscribedToRemote = false;
|
||||
|
||||
/**
|
||||
* The [[Session]] to which the Publisher belongs
|
||||
*/
|
||||
session: Session; // Initialized by Session.publish(Publisher)
|
||||
|
||||
private accessDenied = false;
|
||||
protected properties: PublisherProperties;
|
||||
private permissionDialogTimeout: NodeJS.Timer;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
openvidu: OpenVidu;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
videoReference: HTMLVideoElement;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
screenShareResizeInterval: NodeJS.Timer;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
IEAdapter: any;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(targEl: string | HTMLElement, properties: PublisherProperties, openvidu: OpenVidu) {
|
||||
super(new Stream((!!openvidu.session) ? openvidu.session : new Session(openvidu), { publisherProperties: properties, mediaConstraints: {} }), targEl);
|
||||
platform = PlatformUtils.getInstance();
|
||||
this.properties = properties;
|
||||
this.openvidu = openvidu;
|
||||
|
||||
this.stream.ee.on('local-stream-destroyed', (reason: string) => {
|
||||
this.stream.isLocalStreamPublished = false;
|
||||
const streamEvent = new StreamEvent(true, this, 'streamDestroyed', this.stream, reason);
|
||||
this.emitEvent('streamDestroyed', [streamEvent]);
|
||||
streamEvent.callDefaultBehavior();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publish or unpublish the audio stream (if available). Calling this method twice in a row passing same value will have no effect
|
||||
*
|
||||
* #### Events dispatched
|
||||
*
|
||||
* > _Only if `Session.publish(Publisher)` has been called for this Publisher_
|
||||
*
|
||||
* The [[Session]] object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"`
|
||||
* The [[Publisher]] object of the local participant will also dispatch the exact same event
|
||||
*
|
||||
* The [[Session]] object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"audioActive"` and `reason` set to `"publishAudio"`
|
||||
* The respective [[Subscriber]] object of every other participant receiving this Publisher's stream will also dispatch the exact same event
|
||||
*
|
||||
* See [[StreamPropertyChangedEvent]] to learn more.
|
||||
*
|
||||
* @param value `true` to publish the audio stream, `false` to unpublish it
|
||||
*/
|
||||
publishAudio(value: boolean): void {
|
||||
if (this.stream.audioActive !== value) {
|
||||
const affectedMediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote! : this.stream.getMediaStream();
|
||||
affectedMediaStream.getAudioTracks().forEach((track) => {
|
||||
track.enabled = value;
|
||||
});
|
||||
if (!!this.session && !!this.stream.streamId) {
|
||||
this.session.openvidu.sendRequest(
|
||||
'streamPropertyChanged',
|
||||
{
|
||||
streamId: this.stream.streamId,
|
||||
property: 'audioActive',
|
||||
newValue: value,
|
||||
reason: 'publishAudio'
|
||||
},
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error("Error sending 'streamPropertyChanged' event", error);
|
||||
} else {
|
||||
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this.stream, 'audioActive', value, !value, 'publishAudio')]);
|
||||
this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, this.stream, 'audioActive', value, !value, 'publishAudio')]);
|
||||
this.session.sendVideoData(this.stream.streamManager);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.stream.audioActive = value;
|
||||
logger.info("'Publisher' has " + (value ? 'published' : 'unpublished') + ' its audio stream');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Publish or unpublish the video stream (if available). Calling this method twice in a row passing same value will have no effect
|
||||
*
|
||||
* #### Events dispatched
|
||||
*
|
||||
* > _Only if `Session.publish(Publisher)` has been called for this Publisher_
|
||||
*
|
||||
* The [[Session]] object of the local participant will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"`
|
||||
* The [[Publisher]] object of the local participant will also dispatch the exact same event
|
||||
*
|
||||
* The [[Session]] object of every other participant connected to the session will dispatch a `streamPropertyChanged` event with `changedProperty` set to `"videoActive"` and `reason` set to `"publishVideo"`
|
||||
* The respective [[Subscriber]] object of every other participant receiving this Publisher's stream will also dispatch the exact same event
|
||||
*
|
||||
* See [[StreamPropertyChangedEvent]] to learn more.
|
||||
*
|
||||
* @param value `true` to publish the video stream, `false` to unpublish it
|
||||
*/
|
||||
publishVideo(value: boolean): void {
|
||||
if (this.stream.videoActive !== value) {
|
||||
const affectedMediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote! : this.stream.getMediaStream();
|
||||
affectedMediaStream.getVideoTracks().forEach((track) => {
|
||||
track.enabled = value;
|
||||
});
|
||||
if (!!this.session && !!this.stream.streamId) {
|
||||
this.session.openvidu.sendRequest(
|
||||
'streamPropertyChanged',
|
||||
{
|
||||
streamId: this.stream.streamId,
|
||||
property: 'videoActive',
|
||||
newValue: value,
|
||||
reason: 'publishVideo'
|
||||
},
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error("Error sending 'streamPropertyChanged' event", error);
|
||||
} else {
|
||||
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this.stream, 'videoActive', value, !value, 'publishVideo')]);
|
||||
this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, this.stream, 'videoActive', value, !value, 'publishVideo')]);
|
||||
this.session.sendVideoData(this.stream.streamManager);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.stream.videoActive = value;
|
||||
logger.info("'Publisher' has " + (value ? 'published' : 'unpublished') + ' its video stream');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call this method before [[Session.publish]] if you prefer to subscribe to your Publisher's remote stream instead of using the local stream, as any other user would do.
|
||||
*/
|
||||
subscribeToRemote(value?: boolean): void {
|
||||
value = (value !== undefined) ? value : true;
|
||||
this.isSubscribedToRemote = value;
|
||||
this.stream.subscribeToMyRemote(value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See [[EventDispatcher.on]]
|
||||
*/
|
||||
on(type: string, handler: (event: Event) => void): EventDispatcher {
|
||||
super.on(type, handler);
|
||||
if (type === 'streamCreated') {
|
||||
if (!!this.stream && this.stream.isLocalStreamPublished) {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
} else {
|
||||
this.stream.ee.on('stream-created-by-publisher', () => {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (type === 'remoteVideoPlaying') {
|
||||
if (this.stream.displayMyRemote() && this.videos[0] && this.videos[0].video &&
|
||||
this.videos[0].video.currentTime > 0 &&
|
||||
this.videos[0].video.paused === false &&
|
||||
this.videos[0].video.ended === false &&
|
||||
this.videos[0].video.readyState === 4) {
|
||||
this.emitEvent('remoteVideoPlaying', [new VideoElementEvent(this.videos[0].video, this, 'remoteVideoPlaying')]);
|
||||
}
|
||||
}
|
||||
if (type === 'accessAllowed') {
|
||||
if (this.accessAllowed) {
|
||||
this.emitEvent('accessAllowed', []);
|
||||
}
|
||||
}
|
||||
if (type === 'accessDenied') {
|
||||
if (this.accessDenied) {
|
||||
this.emitEvent('accessDenied', []);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See [[EventDispatcher.once]]
|
||||
*/
|
||||
once(type: string, handler: (event: Event) => void): Publisher {
|
||||
super.once(type, handler);
|
||||
if (type === 'streamCreated') {
|
||||
if (!!this.stream && this.stream.isLocalStreamPublished) {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
} else {
|
||||
this.stream.ee.once('stream-created-by-publisher', () => {
|
||||
this.emitEvent('streamCreated', [new StreamEvent(false, this, 'streamCreated', this.stream, '')]);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (type === 'remoteVideoPlaying') {
|
||||
if (this.stream.displayMyRemote() && this.videos[0] && this.videos[0].video &&
|
||||
this.videos[0].video.currentTime > 0 &&
|
||||
this.videos[0].video.paused === false &&
|
||||
this.videos[0].video.ended === false &&
|
||||
this.videos[0].video.readyState === 4) {
|
||||
this.emitEvent('remoteVideoPlaying', [new VideoElementEvent(this.videos[0].video, this, 'remoteVideoPlaying')]);
|
||||
}
|
||||
}
|
||||
if (type === 'accessAllowed') {
|
||||
if (this.accessAllowed) {
|
||||
this.emitEvent('accessAllowed', []);
|
||||
}
|
||||
}
|
||||
if (type === 'accessDenied') {
|
||||
if (this.accessDenied) {
|
||||
this.emitEvent('accessDenied', []);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current video or audio track with a different one. This allows you to replace an ongoing track with a different one
|
||||
* without having to renegotiate the whole WebRTC connection (that is, initializing a new Publisher, unpublishing the previous one
|
||||
* and publishing the new one).
|
||||
*
|
||||
* You can get this new MediaStreamTrack by using the native Web API or simply with [[OpenVidu.getUserMedia]] method.
|
||||
*
|
||||
* **WARNING: this method has been proven to work, but there may be some combinations of published/replaced tracks that may be incompatible between them and break the connection in OpenVidu Server. A complete renegotiation may be the only solution in this case**
|
||||
*
|
||||
* @param track The [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) object to replace the current one. If it is an audio track, the current audio track will be the replaced one. If it
|
||||
* is a video track, the current video track will be the replaced one.
|
||||
*
|
||||
* @returns A Promise (to which you can optionally subscribe to) that is resolved if the track was successfully replaced and rejected with an Error object in other case
|
||||
*/
|
||||
replaceTrack(track: MediaStreamTrack): Promise<any> {
|
||||
|
||||
const replaceMediaStreamTrack = () => {
|
||||
const mediaStream: MediaStream = this.stream.displayMyRemote() ? this.stream.localMediaStreamWhenSubscribedToRemote! : this.stream.getMediaStream();
|
||||
let removedTrack: MediaStreamTrack;
|
||||
if (track.kind === 'video') {
|
||||
removedTrack = mediaStream.getVideoTracks()[0];
|
||||
} else {
|
||||
removedTrack = mediaStream.getAudioTracks()[0];
|
||||
}
|
||||
mediaStream.removeTrack(removedTrack);
|
||||
removedTrack.stop();
|
||||
mediaStream.addTrack(track);
|
||||
this.session.sendVideoData(this.stream.streamManager, 5, true, 5);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.stream.isLocalStreamPublished) {
|
||||
// Only if the Publisher has been published is necessary to call native Web API RTCRtpSender.replaceTrack
|
||||
const senders: RTCRtpSender[] = this.stream.getRTCPeerConnection().getSenders();
|
||||
let sender: RTCRtpSender | undefined;
|
||||
if (track.kind === 'video') {
|
||||
sender = senders.find(s => !!s.track && s.track.kind === 'video');
|
||||
if (!sender) {
|
||||
reject(new Error('There\'s no replaceable track for that kind of MediaStreamTrack in this Publisher object'))
|
||||
}
|
||||
} else if (track.kind === 'audio') {
|
||||
sender = senders.find(s => !!s.track && s.track.kind === 'audio');
|
||||
if (!sender) {
|
||||
reject(new Error('There\'s no replaceable track for that kind of MediaStreamTrack in this Publisher object'))
|
||||
}
|
||||
} else {
|
||||
reject(new Error('Unknown track kind ' + track.kind));
|
||||
}
|
||||
(<any>sender).replaceTrack(track).then(() => {
|
||||
replaceMediaStreamTrack();
|
||||
resolve();
|
||||
}).catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
// Publisher not published. Simply modify local MediaStream tracks
|
||||
replaceMediaStreamTrack();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Hidden methods */
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initialize(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
let constraints: MediaStreamConstraints = {};
|
||||
let constraintsAux: MediaStreamConstraints = {};
|
||||
const timeForDialogEvent = 1250;
|
||||
let startTime;
|
||||
|
||||
const errorCallback = (openViduError: OpenViduError) => {
|
||||
this.accessDenied = true;
|
||||
this.accessAllowed = false;
|
||||
reject(openViduError);
|
||||
};
|
||||
|
||||
const successCallback = (mediaStream: MediaStream) => {
|
||||
this.accessAllowed = true;
|
||||
this.accessDenied = false;
|
||||
|
||||
if (typeof MediaStreamTrack !== 'undefined' && this.properties.audioSource instanceof MediaStreamTrack) {
|
||||
mediaStream.removeTrack(mediaStream.getAudioTracks()[0]);
|
||||
mediaStream.addTrack((<MediaStreamTrack>this.properties.audioSource));
|
||||
}
|
||||
|
||||
if (typeof MediaStreamTrack !== 'undefined' && this.properties.videoSource instanceof MediaStreamTrack) {
|
||||
mediaStream.removeTrack(mediaStream.getVideoTracks()[0]);
|
||||
mediaStream.addTrack((<MediaStreamTrack>this.properties.videoSource));
|
||||
}
|
||||
|
||||
// Apply PublisherProperties.publishAudio and PublisherProperties.publishVideo
|
||||
if (!!mediaStream.getAudioTracks()[0]) {
|
||||
const enabled = (this.stream.audioActive !== undefined && this.stream.audioActive !== null) ? this.stream.audioActive : !!this.stream.outboundStreamOpts.publisherProperties.publishAudio;
|
||||
mediaStream.getAudioTracks()[0].enabled = enabled;
|
||||
}
|
||||
if (!!mediaStream.getVideoTracks()[0]) {
|
||||
const enabled = (this.stream.videoActive !== undefined && this.stream.videoActive !== null) ? this.stream.videoActive : !!this.stream.outboundStreamOpts.publisherProperties.publishVideo;
|
||||
mediaStream.getVideoTracks()[0].enabled = enabled;
|
||||
}
|
||||
|
||||
this.initializeVideoReference(mediaStream);
|
||||
|
||||
if (!this.stream.displayMyRemote()) {
|
||||
// When we are subscribed to our remote we don't still set the MediaStream object in the video elements to
|
||||
// avoid early 'streamPlaying' event
|
||||
this.stream.updateMediaStreamInVideos();
|
||||
}
|
||||
delete this.firstVideoElement;
|
||||
|
||||
if (this.stream.isSendVideo()) {
|
||||
if (!this.stream.isSendScreen()) {
|
||||
|
||||
if (platform.isIonicIos() || platform.isSafariBrowser()) {
|
||||
// iOS Ionic or Safari. Limitation: cannot set videoDimensions directly, as the videoReference is not loaded
|
||||
// if not added to DOM. Must add it to DOM and wait for videoWidth and videoHeight properties to be defined
|
||||
|
||||
this.videoReference.style.display = 'none';
|
||||
document.body.appendChild(this.videoReference);
|
||||
|
||||
const videoDimensionsSet = () => {
|
||||
this.stream.videoDimensions = {
|
||||
width: this.videoReference.videoWidth,
|
||||
height: this.videoReference.videoHeight
|
||||
};
|
||||
this.stream.isLocalStreamReadyToPublish = true;
|
||||
this.stream.ee.emitEvent('stream-ready-to-publish', []);
|
||||
document.body.removeChild(this.videoReference);
|
||||
};
|
||||
|
||||
let interval;
|
||||
this.videoReference.addEventListener('loadedmetadata', () => {
|
||||
if (this.videoReference.videoWidth === 0) {
|
||||
interval = setInterval(() => {
|
||||
if (this.videoReference.videoWidth !== 0) {
|
||||
clearInterval(interval);
|
||||
videoDimensionsSet();
|
||||
}
|
||||
}, 40);
|
||||
} else {
|
||||
videoDimensionsSet();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Rest of platforms
|
||||
// With no screen share, video dimension can be set directly from MediaStream (getSettings)
|
||||
// Orientation must be checked for mobile devices (width and height are reversed)
|
||||
const { width, height } = this.getVideoDimensions(mediaStream);
|
||||
|
||||
if (platform.isMobileDevice() && (window.innerHeight > window.innerWidth)) {
|
||||
// Mobile portrait mode
|
||||
this.stream.videoDimensions = {
|
||||
width: height || 0,
|
||||
height: width || 0
|
||||
};
|
||||
} else {
|
||||
this.stream.videoDimensions = {
|
||||
width: width || 0,
|
||||
height: height || 0
|
||||
};
|
||||
}
|
||||
this.stream.isLocalStreamReadyToPublish = true;
|
||||
this.stream.ee.emitEvent('stream-ready-to-publish', []);
|
||||
}
|
||||
} else {
|
||||
// With screen share, video dimension must be got from a video element (onloadedmetadata event)
|
||||
this.videoReference.addEventListener('loadedmetadata', () => {
|
||||
this.stream.videoDimensions = {
|
||||
width: this.videoReference.videoWidth,
|
||||
height: this.videoReference.videoHeight
|
||||
};
|
||||
this.screenShareResizeInterval = setInterval(() => {
|
||||
const firefoxSettings = mediaStream.getVideoTracks()[0].getSettings();
|
||||
const newWidth = (platform.isChromeBrowser() || platform.isOperaBrowser()) ? this.videoReference.videoWidth : firefoxSettings.width;
|
||||
const newHeight = (platform.isChromeBrowser() || platform.isOperaBrowser()) ? this.videoReference.videoHeight : firefoxSettings.height;
|
||||
if (this.stream.isLocalStreamPublished &&
|
||||
(newWidth !== this.stream.videoDimensions.width ||
|
||||
newHeight !== this.stream.videoDimensions.height)) {
|
||||
const oldValue = { width: this.stream.videoDimensions.width, height: this.stream.videoDimensions.height };
|
||||
this.stream.videoDimensions = {
|
||||
width: newWidth || 0,
|
||||
height: newHeight || 0
|
||||
};
|
||||
this.session.openvidu.sendRequest(
|
||||
'streamPropertyChanged',
|
||||
{
|
||||
streamId: this.stream.streamId,
|
||||
property: 'videoDimensions',
|
||||
newValue: JSON.stringify(this.stream.videoDimensions),
|
||||
reason: 'screenResized'
|
||||
},
|
||||
(error, response) => {
|
||||
if (error) {
|
||||
logger.error("Error sending 'streamPropertyChanged' event", error);
|
||||
} else {
|
||||
this.session.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this.session, this.stream, 'videoDimensions', this.stream.videoDimensions, oldValue, 'screenResized')]);
|
||||
this.emitEvent('streamPropertyChanged', [new StreamPropertyChangedEvent(this, this.stream, 'videoDimensions', this.stream.videoDimensions, oldValue, 'screenResized')]);
|
||||
this.session.sendVideoData(this.stream.streamManager);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
this.stream.isLocalStreamReadyToPublish = true;
|
||||
this.stream.ee.emitEvent('stream-ready-to-publish', []);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.stream.isLocalStreamReadyToPublish = true;
|
||||
this.stream.ee.emitEvent('stream-ready-to-publish', []);
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
|
||||
const getMediaSuccess = (mediaStream: MediaStream, definedAudioConstraint) => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
if (this.stream.isSendScreen() && this.stream.isSendAudio()) {
|
||||
// When getting desktop as user media audio constraint must be false. Now we can ask for it if required
|
||||
constraintsAux.audio = definedAudioConstraint;
|
||||
constraintsAux.video = false;
|
||||
startTime = Date.now();
|
||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||
|
||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
||||
.then(audioOnlyStream => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
mediaStream.addTrack(audioOnlyStream.getAudioTracks()[0]);
|
||||
successCallback(mediaStream);
|
||||
})
|
||||
.catch(error => {
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
mediaStream.getAudioTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
errorCallback(this.openvidu.generateAudioDeviceError(error, constraints));
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
successCallback(mediaStream);
|
||||
}
|
||||
};
|
||||
|
||||
const getMediaError = error => {
|
||||
logger.error(error);
|
||||
this.clearPermissionDialogTimer(startTime, timeForDialogEvent);
|
||||
if (error.name === 'Error') {
|
||||
// Safari OverConstrainedError has as name property 'Error' instead of 'OverConstrainedError'
|
||||
error.name = error.constructor.name;
|
||||
}
|
||||
let errorName, errorMessage;
|
||||
switch (error.name.toLowerCase()) {
|
||||
case 'notfounderror':
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: constraints.video
|
||||
})
|
||||
.then(mediaStream => {
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
}).catch(e => {
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
});
|
||||
break;
|
||||
case 'notallowederror':
|
||||
errorName = this.stream.isSendScreen() ? OpenViduErrorName.SCREEN_CAPTURE_DENIED : OpenViduErrorName.DEVICE_ACCESS_DENIED;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
case 'overconstrainederror':
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: constraints.video
|
||||
})
|
||||
.then(mediaStream => {
|
||||
mediaStream.getVideoTracks().forEach((track) => {
|
||||
track.stop();
|
||||
});
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_AUDIO_DEVICE_NOT_FOUND;
|
||||
errorMessage = "Audio input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.audio).deviceId!!).exact + "' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage = "Audio input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
}).catch(e => {
|
||||
if (error.constraint.toLowerCase() === 'deviceid') {
|
||||
errorName = OpenViduErrorName.INPUT_VIDEO_DEVICE_NOT_FOUND;
|
||||
errorMessage = "Video input device with deviceId '" + (<ConstrainDOMStringParameters>(<MediaTrackConstraints>constraints.video).deviceId!!).exact + "' not found";
|
||||
} else {
|
||||
errorName = OpenViduErrorName.PUBLISHER_PROPERTIES_ERROR;
|
||||
errorMessage = "Video input device doesn't support the value passed for constraint '" + error.constraint + "'";
|
||||
}
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
});
|
||||
break;
|
||||
case 'aborterror':
|
||||
case 'notreadableerror':
|
||||
errorName = OpenViduErrorName.DEVICE_ALREADY_IN_USE;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
default:
|
||||
errorName = OpenViduErrorName.GENERIC_ERROR;
|
||||
errorMessage = error.toString();
|
||||
errorCallback(new OpenViduError(errorName, errorMessage));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.openvidu.generateMediaConstraints(this.properties)
|
||||
.then(myConstraints => {
|
||||
|
||||
if (!!myConstraints.videoTrack && !!myConstraints.audioTrack ||
|
||||
!!myConstraints.audioTrack && myConstraints.constraints?.video === false ||
|
||||
!!myConstraints.videoTrack && myConstraints.constraints?.audio === false) {
|
||||
// No need to call getUserMedia at all. MediaStreamTracks already provided
|
||||
successCallback(this.openvidu.addAlreadyProvidedTracks(myConstraints, new MediaStream()));
|
||||
// Return as we do not need to process further
|
||||
return;
|
||||
}
|
||||
|
||||
constraints = myConstraints.constraints;
|
||||
|
||||
const outboundStreamOptions = {
|
||||
mediaConstraints: constraints,
|
||||
publisherProperties: this.properties
|
||||
};
|
||||
this.stream.setOutboundStreamOptions(outboundStreamOptions);
|
||||
|
||||
const definedAudioConstraint = ((constraints.audio === undefined) ? true : constraints.audio);
|
||||
constraintsAux.audio = this.stream.isSendScreen() ? false : definedAudioConstraint;
|
||||
constraintsAux.video = constraints.video;
|
||||
startTime = Date.now();
|
||||
this.setPermissionDialogTimer(timeForDialogEvent);
|
||||
|
||||
if (this.stream.isSendScreen() && navigator.mediaDevices['getDisplayMedia'] && !platform.isElectron()) {
|
||||
navigator.mediaDevices['getDisplayMedia']({ video: true })
|
||||
.then(mediaStream => {
|
||||
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream);
|
||||
getMediaSuccess(mediaStream, definedAudioConstraint);
|
||||
})
|
||||
.catch(error => {
|
||||
getMediaError(error);
|
||||
});
|
||||
} else {
|
||||
navigator.mediaDevices.getUserMedia(constraintsAux)
|
||||
.then(mediaStream => {
|
||||
this.openvidu.addAlreadyProvidedTracks(myConstraints, mediaStream);
|
||||
getMediaSuccess(mediaStream, definedAudioConstraint);
|
||||
})
|
||||
.catch(error => {
|
||||
getMediaError(error);
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
.catch((error: OpenViduError) => {
|
||||
errorCallback(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
getVideoDimensions(mediaStream: MediaStream): MediaTrackSettings {
|
||||
return mediaStream.getVideoTracks()[0].getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
reestablishStreamPlayingEvent() {
|
||||
if (this.ee.getListeners('streamPlaying').length > 0) {
|
||||
this.addPlayEventToFirstVideo();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initializeVideoReference(mediaStream: MediaStream) {
|
||||
this.videoReference = document.createElement('video');
|
||||
|
||||
if (platform.isSafariBrowser()) {
|
||||
this.videoReference.setAttribute('playsinline', 'true');
|
||||
}
|
||||
|
||||
this.stream.setMediaStream(mediaStream);
|
||||
|
||||
if (!!this.firstVideoElement) {
|
||||
this.createVideoElement(this.firstVideoElement.targetElement, <VideoInsertMode>this.properties.insertMode);
|
||||
}
|
||||
|
||||
this.videoReference.srcObject = mediaStream;
|
||||
}
|
||||
|
||||
|
||||
/* Private methods */
|
||||
|
||||
private setPermissionDialogTimer(waitTime: number): void {
|
||||
this.permissionDialogTimeout = setTimeout(() => {
|
||||
this.emitEvent('accessDialogOpened', []);
|
||||
}, waitTime);
|
||||
}
|
||||
|
||||
private clearPermissionDialogTimer(startTime: number, waitTime: number): void {
|
||||
clearTimeout(this.permissionDialogTimeout);
|
||||
if ((Date.now() - startTime) > waitTime) {
|
||||
// Permission dialog was shown and now is closed
|
||||
this.emitEvent('accessDialogClosed', []);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,525 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { EventDispatcher } from './EventDispatcher';
|
||||
import { StreamManagerVideo } from '../OpenViduInternal/Interfaces/Public/StreamManagerVideo';
|
||||
import { Event } from '../OpenViduInternal/Events/Event';
|
||||
import { StreamManagerEvent } from '../OpenViduInternal/Events/StreamManagerEvent';
|
||||
import { VideoElementEvent } from '../OpenViduInternal/Events/VideoElementEvent';
|
||||
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
/**
|
||||
* Interface in charge of displaying the media streams in the HTML DOM. This wraps any [[Publisher]] and [[Subscriber]] object.
|
||||
* You can insert as many video players fo the same Stream as you want by calling [[StreamManager.addVideoElement]] or
|
||||
* [[StreamManager.createVideoElement]].
|
||||
* The use of StreamManager wrapper is particularly useful when you don't need to differentiate between Publisher or Subscriber streams or just
|
||||
* want to directly manage your own video elements (even more than one video element per Stream). This scenario is pretty common in
|
||||
* declarative, MVC frontend frameworks such as Angular, React or Vue.js
|
||||
*
|
||||
* ### Available event listeners (and events dispatched)
|
||||
*
|
||||
* - videoElementCreated ([[VideoElementEvent]])
|
||||
* - videoElementDestroyed ([[VideoElementEvent]])
|
||||
* - streamPlaying ([[StreamManagerEvent]])
|
||||
* - streamAudioVolumeChange ([[StreamManagerEvent]])
|
||||
*
|
||||
*/
|
||||
export class StreamManager extends EventDispatcher {
|
||||
|
||||
/**
|
||||
* The Stream represented in the DOM by the Publisher/Subscriber
|
||||
*/
|
||||
stream: Stream;
|
||||
|
||||
/**
|
||||
* All the videos displaying the Stream of this Publisher/Subscriber
|
||||
*/
|
||||
videos: StreamManagerVideo[] = [];
|
||||
|
||||
/**
|
||||
* Whether the Stream represented in the DOM is local or remote
|
||||
* - `false` for [[Publisher]]
|
||||
* - `true` for [[Subscriber]]
|
||||
*/
|
||||
remote: boolean;
|
||||
|
||||
/**
|
||||
* The DOM HTMLElement assigned as target element when creating the video for the Publisher/Subscriber. This property is only defined if:
|
||||
* - [[Publisher]] has been initialized by calling method [[OpenVidu.initPublisher]] with a valid `targetElement` parameter
|
||||
* - [[Subscriber]] has been initialized by calling method [[Session.subscribe]] with a valid `targetElement` parameter
|
||||
*/
|
||||
targetElement: HTMLElement;
|
||||
|
||||
/**
|
||||
* `id` attribute of the DOM video element displaying the Publisher/Subscriber's stream. This property is only defined if:
|
||||
* - [[Publisher]] has been initialized by calling method [[OpenVidu.initPublisher]] with a valid `targetElement` parameter
|
||||
* - [[Subscriber]] has been initialized by calling method [[Session.subscribe]] with a valid `targetElement` parameter
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
firstVideoElement?: StreamManagerVideo;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
lazyLaunchVideoElementCreatedEvent = false;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
element: HTMLElement;
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
protected canPlayListener: EventListener;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(stream: Stream, targetElement?: HTMLElement | string) {
|
||||
super();
|
||||
platform = PlatformUtils.getInstance();
|
||||
this.stream = stream;
|
||||
this.stream.streamManager = this;
|
||||
this.remote = !this.stream.isLocal();
|
||||
|
||||
if (!!targetElement) {
|
||||
let targEl;
|
||||
if (typeof targetElement === 'string') {
|
||||
targEl = document.getElementById(targetElement);
|
||||
} else if (targetElement instanceof HTMLElement) {
|
||||
targEl = targetElement;
|
||||
}
|
||||
|
||||
if (!!targEl) {
|
||||
this.firstVideoElement = {
|
||||
targetElement: targEl,
|
||||
video: document.createElement('video'),
|
||||
id: '',
|
||||
canplayListenerAdded: false
|
||||
};
|
||||
if (platform.isSafariBrowser()) {
|
||||
this.firstVideoElement.video.setAttribute('playsinline', 'true');
|
||||
}
|
||||
this.targetElement = targEl;
|
||||
this.element = targEl;
|
||||
}
|
||||
}
|
||||
|
||||
this.canPlayListener = () => {
|
||||
if (this.stream.isLocal()) {
|
||||
if (!this.stream.displayMyRemote()) {
|
||||
logger.info("Your local 'Stream' with id [" + this.stream.streamId + '] video is now playing');
|
||||
this.ee.emitEvent('videoPlaying', [new VideoElementEvent(this.videos[0].video, this, 'videoPlaying')]);
|
||||
} else {
|
||||
logger.info("Your own remote 'Stream' with id [" + this.stream.streamId + '] video is now playing');
|
||||
this.ee.emitEvent('remoteVideoPlaying', [new VideoElementEvent(this.videos[0].video, this, 'remoteVideoPlaying')]);
|
||||
}
|
||||
} else {
|
||||
logger.info("Remote 'Stream' with id [" + this.stream.streamId + '] video is now playing');
|
||||
this.ee.emitEvent('videoPlaying', [new VideoElementEvent(this.videos[0].video, this, 'videoPlaying')]);
|
||||
}
|
||||
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* See [[EventDispatcher.on]]
|
||||
*/
|
||||
on(type: string, handler: (event: Event) => void): EventDispatcher {
|
||||
|
||||
super.onAux(type, "Event '" + type + "' triggered by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler)
|
||||
|
||||
if (type === 'videoElementCreated') {
|
||||
if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) {
|
||||
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]);
|
||||
this.lazyLaunchVideoElementCreatedEvent = false;
|
||||
}
|
||||
}
|
||||
if (type === 'streamPlaying' || type === 'videoPlaying') {
|
||||
if (this.videos[0] && this.videos[0].video &&
|
||||
this.videos[0].video.currentTime > 0 &&
|
||||
this.videos[0].video.paused === false &&
|
||||
this.videos[0].video.ended === false &&
|
||||
this.videos[0].video.readyState === 4) {
|
||||
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
|
||||
this.ee.emitEvent('videoPlaying', [new VideoElementEvent(this.videos[0].video, this, 'videoPlaying')]);
|
||||
}
|
||||
}
|
||||
if (type === 'streamAudioVolumeChange' && this.stream.hasAudio) {
|
||||
this.stream.enableVolumeChangeEvent(false);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See [[EventDispatcher.once]]
|
||||
*/
|
||||
once(type: string, handler: (event: Event) => void): StreamManager {
|
||||
|
||||
super.onceAux(type, "Event '" + type + "' triggered once by '" + (this.remote ? 'Subscriber' : 'Publisher') + "'", handler);
|
||||
|
||||
if (type === 'videoElementCreated') {
|
||||
if (!!this.stream && this.lazyLaunchVideoElementCreatedEvent) {
|
||||
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(this.videos[0].video, this, 'videoElementCreated')]);
|
||||
}
|
||||
}
|
||||
if (type === 'streamPlaying' || type === 'videoPlaying') {
|
||||
if (this.videos[0] && this.videos[0].video &&
|
||||
this.videos[0].video.currentTime > 0 &&
|
||||
this.videos[0].video.paused === false &&
|
||||
this.videos[0].video.ended === false &&
|
||||
this.videos[0].video.readyState === 4) {
|
||||
this.ee.emitEvent('streamPlaying', [new StreamManagerEvent(this, 'streamPlaying', undefined)]);
|
||||
this.ee.emitEvent('videoPlaying', [new VideoElementEvent(this.videos[0].video, this, 'videoPlaying')]);
|
||||
}
|
||||
}
|
||||
if (type === 'streamAudioVolumeChange' && this.stream.hasAudio) {
|
||||
this.stream.enableOnceVolumeChangeEvent(false);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* See [[EventDispatcher.off]]
|
||||
*/
|
||||
off(type: string, handler?: (event: Event) => void): StreamManager {
|
||||
|
||||
super.off(type, handler);
|
||||
|
||||
if (type === 'streamAudioVolumeChange') {
|
||||
let remainingVolumeEventListeners = this.ee.getListeners(type).length;
|
||||
if (remainingVolumeEventListeners === 0) {
|
||||
this.stream.disableVolumeChangeEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes `video` element parameter display this [[stream]]. This is useful when you are
|
||||
* [managing the video elements on your own](/en/stable/cheatsheet/manage-videos/#you-take-care-of-the-video-players)
|
||||
*
|
||||
* Calling this method with a video already added to other Publisher/Subscriber will cause the video element to be
|
||||
* disassociated from that previous Publisher/Subscriber and to be associated to this one.
|
||||
*
|
||||
* @returns 1 if the video wasn't associated to any other Publisher/Subscriber and has been successfully added to this one.
|
||||
* 0 if the video was already added to this Publisher/Subscriber. -1 if the video was previously associated to any other
|
||||
* Publisher/Subscriber and has been successfully disassociated from that one and properly added to this one.
|
||||
*/
|
||||
addVideoElement(video: HTMLVideoElement): number {
|
||||
|
||||
this.initializeVideoProperties(video);
|
||||
|
||||
if (this.stream.isLocal() && this.stream.displayMyRemote()) {
|
||||
if (video.srcObject !== this.stream.getMediaStream()) {
|
||||
video.srcObject = this.stream.getMediaStream();
|
||||
}
|
||||
}
|
||||
|
||||
// If the video element is already part of this StreamManager do nothing
|
||||
for (const v of this.videos) {
|
||||
if (v.video === video) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
let returnNumber = 1;
|
||||
|
||||
for (const streamManager of this.stream.session.streamManagers) {
|
||||
if (streamManager.disassociateVideo(video)) {
|
||||
returnNumber = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.stream.session.streamManagers.forEach(streamManager => {
|
||||
streamManager.disassociateVideo(video);
|
||||
});
|
||||
|
||||
this.pushNewStreamManagerVideo({
|
||||
video,
|
||||
id: video.id,
|
||||
canplayListenerAdded: false
|
||||
});
|
||||
|
||||
logger.info('New video element associated to ', this);
|
||||
|
||||
return returnNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new video element displaying this [[stream]]. This allows you to have multiple video elements displaying the same media stream.
|
||||
*
|
||||
* #### Events dispatched
|
||||
*
|
||||
* The Publisher/Subscriber object will dispatch a `videoElementCreated` event once the HTML video element has been added to DOM. See [[VideoElementEvent]]
|
||||
*
|
||||
* @param targetElement HTML DOM element (or its `id` attribute) in which the video element of the Publisher/Subscriber will be inserted
|
||||
* @param insertMode How the video element will be inserted accordingly to `targetElemet`
|
||||
*
|
||||
* @returns The created HTMLVideoElement
|
||||
*/
|
||||
createVideoElement(targetElement?: string | HTMLElement, insertMode?: VideoInsertMode): HTMLVideoElement {
|
||||
let targEl;
|
||||
if (typeof targetElement === 'string') {
|
||||
targEl = document.getElementById(targetElement);
|
||||
if (!targEl) {
|
||||
throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement);
|
||||
}
|
||||
} else if (targetElement instanceof HTMLElement) {
|
||||
targEl = targetElement;
|
||||
} else {
|
||||
throw new Error("The provided 'targetElement' couldn't be resolved to any HTML element: " + targetElement);
|
||||
}
|
||||
|
||||
const video = this.createVideo();
|
||||
this.initializeVideoProperties(video);
|
||||
|
||||
let insMode = !!insertMode ? insertMode : VideoInsertMode.APPEND;
|
||||
switch (insMode) {
|
||||
case VideoInsertMode.AFTER:
|
||||
targEl.parentNode!!.insertBefore(video, targEl.nextSibling);
|
||||
break;
|
||||
case VideoInsertMode.APPEND:
|
||||
targEl.appendChild(video);
|
||||
break;
|
||||
case VideoInsertMode.BEFORE:
|
||||
targEl.parentNode!!.insertBefore(video, targEl);
|
||||
break;
|
||||
case VideoInsertMode.PREPEND:
|
||||
targEl.insertBefore(video, targEl.childNodes[0]);
|
||||
break;
|
||||
case VideoInsertMode.REPLACE:
|
||||
targEl.parentNode!!.replaceChild(video, targEl);
|
||||
break;
|
||||
default:
|
||||
insMode = VideoInsertMode.APPEND;
|
||||
targEl.appendChild(video);
|
||||
break;
|
||||
}
|
||||
|
||||
const v: StreamManagerVideo = {
|
||||
targetElement: targEl,
|
||||
video,
|
||||
insertMode: insMode,
|
||||
id: video.id,
|
||||
canplayListenerAdded: false
|
||||
};
|
||||
this.pushNewStreamManagerVideo(v);
|
||||
|
||||
this.ee.emitEvent('videoElementCreated', [new VideoElementEvent(v.video, this, 'videoElementCreated')]);
|
||||
this.lazyLaunchVideoElementCreatedEvent = !!this.firstVideoElement;
|
||||
|
||||
return video;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current configuration for the [[PublisherSpeakingEvent]] feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/streammanagerevent.html) feature for this specific
|
||||
* StreamManager audio stream, overriding the global options set with [[OpenVidu.setAdvancedConfiguration]]. This way you can customize the audio events options
|
||||
* for each specific StreamManager and change them dynamically.
|
||||
*
|
||||
* @param publisherSpeakingEventsOptions New options to be applied to this StreamManager's audio stream. It is an object which includes the following optional properties:
|
||||
* - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms)
|
||||
* - `threshold`: (number) the volume at which _publisherStartSpeaking_, _publisherStopSpeaking_ events will be fired. Default **-50** (dB)
|
||||
*/
|
||||
updatePublisherSpeakingEventsOptions(publisherSpeakingEventsOptions): void {
|
||||
const currentHarkOptions = !!this.stream.harkOptions ? this.stream.harkOptions : (this.stream.session.openvidu.advancedConfiguration.publisherSpeakingEventsOptions || {});
|
||||
const newInterval = (typeof publisherSpeakingEventsOptions.interval === 'number') ?
|
||||
publisherSpeakingEventsOptions.interval : ((typeof currentHarkOptions.interval === 'number') ? currentHarkOptions.interval : 100);
|
||||
const newThreshold = (typeof publisherSpeakingEventsOptions.threshold === 'number') ?
|
||||
publisherSpeakingEventsOptions.threshold : ((typeof currentHarkOptions.threshold === 'number') ? currentHarkOptions.threshold : -50);
|
||||
this.stream.harkOptions = {
|
||||
interval: newInterval,
|
||||
threshold: newThreshold
|
||||
};
|
||||
if (!!this.stream.speechEvent) {
|
||||
this.stream.speechEvent.setInterval(newInterval);
|
||||
this.stream.speechEvent.setThreshold(newThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hidden methods */
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
initializeVideoProperties(video: HTMLVideoElement): void {
|
||||
if (!(this.stream.isLocal() && this.stream.displayMyRemote())) {
|
||||
// Avoid setting the MediaStream into the srcObject if remote subscription before publishing
|
||||
if (video.srcObject !== this.stream.getMediaStream()) {
|
||||
// If srcObject already set don't do it again
|
||||
video.srcObject = this.stream.getMediaStream();
|
||||
}
|
||||
}
|
||||
video.autoplay = true;
|
||||
video.controls = false;
|
||||
|
||||
if (platform.isSafariBrowser()) {
|
||||
video.setAttribute('playsinline', 'true');
|
||||
}
|
||||
|
||||
if (!video.id) {
|
||||
video.id = (this.remote ? 'remote-' : 'local-') + 'video-' + this.stream.streamId;
|
||||
// DEPRECATED property: assign once the property id if the user provided a valid targetElement
|
||||
if (!this.id && !!this.targetElement) {
|
||||
this.id = video.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.remote && !this.stream.displayMyRemote()) {
|
||||
video.muted = true;
|
||||
if (video.style.transform === 'rotateY(180deg)' && !this.stream.outboundStreamOpts.publisherProperties.mirror) {
|
||||
// If the video was already rotated and now is set to not mirror
|
||||
this.removeMirrorVideo(video);
|
||||
} else if (this.stream.outboundStreamOpts.publisherProperties.mirror && !this.stream.isSendScreen()) {
|
||||
this.mirrorVideo(video);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
removeAllVideos(): void {
|
||||
for (let i = this.stream.session.streamManagers.length - 1; i >= 0; --i) {
|
||||
if (this.stream.session.streamManagers[i] === this) {
|
||||
this.stream.session.streamManagers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.videos.forEach(streamManagerVideo => {
|
||||
// Remove oncanplay event listener (only OpenVidu browser listener, not the user ones)
|
||||
if(!!streamManagerVideo.video && !!streamManagerVideo.video.removeEventListener){
|
||||
streamManagerVideo.video.removeEventListener('canplay', this.canPlayListener);
|
||||
} streamManagerVideo.canplayListenerAdded = false;
|
||||
if (!!streamManagerVideo.targetElement) {
|
||||
// Only remove from DOM videos created by OpenVidu Browser (those generated by passing a valid targetElement in OpenVidu.initPublisher
|
||||
// and Session.subscribe or those created by StreamManager.createVideoElement). All this videos triggered a videoElementCreated event
|
||||
streamManagerVideo.video.parentNode!.removeChild(streamManagerVideo.video);
|
||||
this.ee.emitEvent('videoElementDestroyed', [new VideoElementEvent(streamManagerVideo.video, this, 'videoElementDestroyed')]);
|
||||
}
|
||||
// Remove srcObject from the video
|
||||
this.removeSrcObject(streamManagerVideo);
|
||||
// Remove from collection of videos every video managed by OpenVidu Browser
|
||||
this.videos.filter(v => !v.targetElement);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
disassociateVideo(video: HTMLVideoElement): boolean {
|
||||
let disassociated = false;
|
||||
for (let i = 0; i < this.videos.length; i++) {
|
||||
if (this.videos[i].video === video) {
|
||||
this.videos[i].video.removeEventListener('canplay', this.canPlayListener);
|
||||
this.videos.splice(i, 1);
|
||||
disassociated = true;
|
||||
logger.info('Video element disassociated from ', this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return disassociated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
addPlayEventToFirstVideo() {
|
||||
if ((!!this.videos[0]) && (!!this.videos[0].video) && (!this.videos[0].canplayListenerAdded)) {
|
||||
this.videos[0].video.addEventListener('canplay', this.canPlayListener);
|
||||
this.videos[0].canplayListenerAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
updateMediaStream(mediaStream: MediaStream) {
|
||||
this.videos.forEach(streamManagerVideo => {
|
||||
streamManagerVideo.video.srcObject = mediaStream;
|
||||
if (platform.isIonicIos()) {
|
||||
// iOS Ionic. LIMITATION: must reinsert the video in the DOM for
|
||||
// the media stream to be updated
|
||||
const vParent = streamManagerVideo.video.parentElement;
|
||||
const newVideo = streamManagerVideo.video;
|
||||
vParent!!.replaceChild(newVideo, streamManagerVideo.video);
|
||||
streamManagerVideo.video = newVideo;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
emitEvent(type: string, eventArray: any[]): void {
|
||||
this.ee.emitEvent(type, eventArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
createVideo(): HTMLVideoElement {
|
||||
return document.createElement('video');
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
removeSrcObject(streamManagerVideo: StreamManagerVideo) {
|
||||
streamManagerVideo.video.srcObject = null;
|
||||
}
|
||||
|
||||
/* Private methods */
|
||||
|
||||
protected pushNewStreamManagerVideo(streamManagerVideo: StreamManagerVideo) {
|
||||
this.videos.push(streamManagerVideo);
|
||||
this.addPlayEventToFirstVideo();
|
||||
if (this.stream.session.streamManagers.indexOf(this) === -1) {
|
||||
this.stream.session.streamManagers.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
private mirrorVideo(video): void {
|
||||
if (!platform.isIonicIos()) {
|
||||
video.style.transform = 'rotateY(180deg)';
|
||||
video.style.webkitTransform = 'rotateY(180deg)';
|
||||
}
|
||||
}
|
||||
|
||||
private removeMirrorVideo(video): void {
|
||||
video.style.transform = 'unset';
|
||||
video.style.webkitTransform = 'unset';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Stream } from './Stream';
|
||||
import { StreamManager } from './StreamManager';
|
||||
import { SubscriberProperties } from '../OpenViduInternal/Interfaces/Public/SubscriberProperties';
|
||||
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* Packs remote media streams. Participants automatically receive them when others publish their streams. Initialized with [[Session.subscribe]] method
|
||||
*/
|
||||
export class Subscriber extends StreamManager {
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
properties: SubscriberProperties;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(stream: Stream, targEl: string | HTMLElement, properties: SubscriberProperties) {
|
||||
super(stream, targEl);
|
||||
this.element = this.targetElement;
|
||||
this.stream = stream;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe or unsubscribe from the audio stream (if available). Calling this method twice in a row passing same value will have no effect
|
||||
* @param value `true` to subscribe to the audio stream, `false` to unsubscribe from it
|
||||
*/
|
||||
subscribeToAudio(value: boolean): Subscriber {
|
||||
this.stream.getMediaStream().getAudioTracks().forEach((track) => {
|
||||
track.enabled = value;
|
||||
});
|
||||
this.stream.audioActive = value;
|
||||
logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its audio stream');
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe or unsubscribe from the video stream (if available). Calling this method twice in a row passing same value will have no effect
|
||||
* @param value `true` to subscribe to the video stream, `false` to unsubscribe from it
|
||||
*/
|
||||
subscribeToVideo(value: boolean): Subscriber {
|
||||
this.stream.getMediaStream().getVideoTracks().forEach((track) => {
|
||||
track.enabled = value;
|
||||
});
|
||||
this.stream.videoActive = value;
|
||||
logger.info("'Subscriber' has " + (value ? 'subscribed to' : 'unsubscribed from') + ' its video stream');
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
{
|
||||
//"allowUnusedLabels": true,
|
||||
"allowUnreachableCode": false,
|
||||
"buildOnSave": false,
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitBOM": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015.promise",
|
||||
"es5",
|
||||
"scripthost"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
//"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
//"noUnusedLocals": true,
|
||||
//"noUnusedParameters": true,
|
||||
"outDir": "../../lib",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressExcessPropertyErrors": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"target": "es5"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
export enum LocalRecorderState {
|
||||
READY = 'READY',
|
||||
RECORDING = 'RECORDING',
|
||||
PAUSED = 'PAUSED',
|
||||
FINISHED = 'FINISHED'
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines property [[OpenViduError.name]]
|
||||
*/
|
||||
export enum OpenViduErrorName {
|
||||
|
||||
/**
|
||||
* Browser is not supported by OpenVidu.
|
||||
* Returned upon unsuccessful [[Session.connect]]
|
||||
*/
|
||||
BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED',
|
||||
|
||||
/**
|
||||
* The user hasn't granted permissions to the required input device when the browser asked for them.
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
DEVICE_ACCESS_DENIED = 'DEVICE_ACCESS_DENIED',
|
||||
|
||||
/**
|
||||
* The required input device is probably being used by other process when the browser asked for it.
|
||||
* This error can also be triggered when the user granted permission to use the devices but a hardware
|
||||
* error occurred at the OS, browser or web page level, which prevented access to the device.
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
DEVICE_ALREADY_IN_USE = "DEVICE_ALREADY_IN_USE",
|
||||
|
||||
/**
|
||||
* The user hasn't granted permissions to capture some desktop screen when the browser asked for them.
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
SCREEN_CAPTURE_DENIED = 'SCREEN_CAPTURE_DENIED',
|
||||
|
||||
/**
|
||||
* Browser does not support screen sharing.
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
SCREEN_SHARING_NOT_SUPPORTED = 'SCREEN_SHARING_NOT_SUPPORTED',
|
||||
|
||||
/**
|
||||
* Only for Chrome, there's no screen sharing extension installed
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
SCREEN_EXTENSION_NOT_INSTALLED = 'SCREEN_EXTENSION_NOT_INSTALLED',
|
||||
|
||||
/**
|
||||
* Only for Chrome, the screen sharing extension is installed but is disabled
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
SCREEN_EXTENSION_DISABLED = 'SCREEN_EXTENSION_DISABLED',
|
||||
|
||||
/**
|
||||
* No video input device found with the provided deviceId (property [[PublisherProperties.videoSource]])
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
INPUT_VIDEO_DEVICE_NOT_FOUND = 'INPUT_VIDEO_DEVICE_NOT_FOUND',
|
||||
|
||||
/**
|
||||
* No audio input device found with the provided deviceId (property [[PublisherProperties.audioSource]])
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
INPUT_AUDIO_DEVICE_NOT_FOUND = 'INPUT_AUDIO_DEVICE_NOT_FOUND',
|
||||
|
||||
/**
|
||||
* There was an unknown error when trying to access the specified audio device
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
INPUT_AUDIO_DEVICE_GENERIC_ERROR = 'INPUT_AUDIO_DEVICE_GENERIC_ERROR',
|
||||
|
||||
/**
|
||||
* Method [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]] has been called with properties `videoSource` and `audioSource` of
|
||||
* [[PublisherProperties]] parameter both set to *false* or *null*
|
||||
*/
|
||||
NO_INPUT_SOURCE_SET = 'NO_INPUT_SOURCE_SET',
|
||||
|
||||
/**
|
||||
* Some media property of [[PublisherProperties]] such as `frameRate` or `resolution` is not supported
|
||||
* by the input devices (whenever it is possible they are automatically adjusted to the most similar value).
|
||||
* Returned upon unsuccessful [[OpenVidu.initPublisher]] or [[OpenVidu.getUserMedia]]
|
||||
*/
|
||||
PUBLISHER_PROPERTIES_ERROR = 'PUBLISHER_PROPERTIES_ERROR',
|
||||
|
||||
/**
|
||||
* The client tried to call a method without the required permissions. This can occur for methods [[Session.publish]],
|
||||
* [[Session.forceUnpublish]], [[Session.forceDisconnect]], [[Stream.applyFilter]], [[Stream.removeFilter]]
|
||||
*/
|
||||
OPENVIDU_PERMISSION_DENIED = 'OPENVIDU_PERMISSION_DENIED',
|
||||
|
||||
/**
|
||||
* _Not in use yet_
|
||||
*/
|
||||
OPENVIDU_NOT_CONNECTED = 'OPENVIDU_NOT_CONNECTED',
|
||||
|
||||
/**
|
||||
* _Not in use yet_
|
||||
*/
|
||||
GENERIC_ERROR = 'GENERIC_ERROR'
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple object to identify runtime errors on the client side
|
||||
*/
|
||||
export class OpenViduError {
|
||||
|
||||
name: OpenViduErrorName;
|
||||
message: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(name: OpenViduErrorName, message: string) {
|
||||
this.name = name;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* How the video will be inserted in the DOM for Publishers and Subscribers. See [[PublisherProperties.insertMode]] and [[SubscriberProperties.insertMode]]
|
||||
*/
|
||||
export enum VideoInsertMode {
|
||||
|
||||
/**
|
||||
* Video inserted after the target element (as next sibling)
|
||||
*/
|
||||
AFTER = 'AFTER',
|
||||
/**
|
||||
* Video inserted as last child of the target element
|
||||
*/
|
||||
APPEND = 'APPEND',
|
||||
/**
|
||||
* Video inserted before the target element (as previous sibling)
|
||||
*/
|
||||
BEFORE = 'BEFORE',
|
||||
/**
|
||||
* Video inserted as first child of the target element
|
||||
*/
|
||||
PREPEND = 'PREPEND',
|
||||
/**
|
||||
* Video replaces target element
|
||||
*/
|
||||
REPLACE = 'REPLACE'
|
||||
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
|
||||
|
||||
/**
|
||||
* Defines the following events:
|
||||
* - `connectionCreated`: dispatched by [[Session]] after a new user has connected to the session
|
||||
* - `connectionDestroyed`: dispatched by [[Session]] after a new user has left the session
|
||||
*/
|
||||
export class ConnectionEvent extends Event {
|
||||
|
||||
/**
|
||||
* Connection object that was created or destroyed
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* For `connectionDestroyed` event:
|
||||
* - "disconnect": the remote user has called `Session.disconnect()`
|
||||
* - "forceDisconnectByUser": the remote user has been evicted from the Session by other user calling `Session.forceDisconnect()`
|
||||
* - "forceDisconnectByServer": the remote user has been evicted from the Session by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "networkDisconnect": the remote user network connection has dropped
|
||||
*
|
||||
* For `connectionCreated` event an empty string
|
||||
*/
|
||||
reason: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(cancelable: boolean, target: Session, type: string, connection: Connection, reason: string) {
|
||||
super(cancelable, target, type);
|
||||
this.connection = connection;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Event } from './Event';
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
|
||||
*
|
||||
* Defines event `connectionPropertyChanged` dispatched by [[Session]] object.
|
||||
* This event is fired when any property of the local [[Connection]] object changes.
|
||||
* The properties that may change are [[Connection.role]] and [[Connection.record]].
|
||||
*
|
||||
* The only way the Connection properties may change is by updating them through:
|
||||
*
|
||||
* - [API REST](/en/stable/reference-docs/REST-API/#patch-openviduapisessionsltsession_idgtconnectionltconnection_idgt)
|
||||
* - [openvidu-java-client](/en/stable/reference-docs/openvidu-java-client/#update-a-connection)
|
||||
* - [openvidu-node-client](/en/stable/reference-docs/openvidu-node-client/#update-a-connection)<br><br>
|
||||
*/
|
||||
export class ConnectionPropertyChangedEvent extends Event {
|
||||
|
||||
/**
|
||||
* The Connection whose property has changed
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* The property of the stream that changed. This value is either `"role"` or `"record"`
|
||||
*/
|
||||
changedProperty: string;
|
||||
|
||||
/**
|
||||
* New value of the property (after change, current value)
|
||||
*/
|
||||
newValue: Object;
|
||||
|
||||
/**
|
||||
* Previous value of the property (before change)
|
||||
*/
|
||||
oldValue: Object;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, connection: Connection, changedProperty: string, newValue: Object, oldValue: Object) {
|
||||
super(false, target, 'connectionPropertyChanged');
|
||||
this.connection = connection;
|
||||
this.changedProperty = changedProperty;
|
||||
this.newValue = newValue;
|
||||
this.oldValue = oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Filter } from '../../OpenVidu/Filter';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
|
||||
export abstract class Event {
|
||||
|
||||
/**
|
||||
* Whether the event has a default behavior that may be prevented by calling [[Event.preventDefault]]
|
||||
*/
|
||||
cancelable: boolean;
|
||||
|
||||
/**
|
||||
* The object that dispatched the event
|
||||
*/
|
||||
target: Session | StreamManager | Filter;
|
||||
|
||||
/**
|
||||
* The type of event. This is the same string you pass as first parameter when calling method `on()` of any object implementing [[EventDispatcher]] interface
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
hasBeenPrevented = false;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(cancelable: boolean, target: Session | StreamManager | Filter, type: string) {
|
||||
this.cancelable = cancelable;
|
||||
this.target = target;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the default beahivour of the event has been prevented or not. Call [[Event.preventDefault]] to prevent it
|
||||
*/
|
||||
isDefaultPrevented(): boolean {
|
||||
return this.hasBeenPrevented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents the default behavior of the event. The following events have a default behavior:
|
||||
*
|
||||
* - `sessionDisconnected`: dispatched by [[Session]] object, automatically unsubscribes the leaving participant from every Subscriber object of the session (this includes closing the WebRTCPeer connection and disposing all MediaStreamTracks)
|
||||
* and also deletes any HTML video element associated to each Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method [[Session.subscribe]] or
|
||||
* by calling [[Subscriber.createVideoElement]]). For every video removed, each Subscriber object will also dispatch a `videoElementDestroyed` event.
|
||||
*
|
||||
* - `streamDestroyed`:
|
||||
* - If dispatched by a [[Publisher]] (*you* have unpublished): automatically stops all media tracks and deletes any HTML video element associated to it (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement`
|
||||
* in method [[OpenVidu.initPublisher]] or by calling [[Publisher.createVideoElement]]). For every video removed, the Publisher object will also dispatch a `videoElementDestroyed` event.
|
||||
* - If dispatched by [[Session]] (*other user* has unpublished): automatically unsubscribes the proper Subscriber object from the session (this includes closing the WebRTCPeer connection and disposing all MediaStreamTracks)
|
||||
* and also deletes any HTML video element associated to that Subscriber (only those created by OpenVidu Browser, either by passing a valid parameter as `targetElement` in method [[Session.subscribe]] or
|
||||
* by calling [[Subscriber.createVideoElement]]). For every video removed, the Subscriber object will also dispatch a `videoElementDestroyed` event.
|
||||
*/
|
||||
preventDefault() {
|
||||
// tslint:disable-next-line:no-empty
|
||||
this.callDefaultBehavior = () => { };
|
||||
this.hasBeenPrevented = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
abstract callDefaultBehavior();
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Filter } from '../../OpenVidu/Filter';
|
||||
|
||||
|
||||
/**
|
||||
* Defines every event dispatched by audio/video stream filters. You can subscribe to filter events by calling [[Filter.addEventListener]]
|
||||
*/
|
||||
export class FilterEvent extends Event {
|
||||
|
||||
/**
|
||||
* Data of the event
|
||||
*/
|
||||
data: Object;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Filter, eventType: string, data: Object) {
|
||||
super(false, target, eventType);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
|
||||
/**
|
||||
* **This feature is part of OpenVidu Pro tier** <a href="https://docs.openvidu.io/en/stable/openvidu-pro/" target="_blank" style="display: inline-block; background-color: rgb(0, 136, 170); color: white; font-weight: bold; padding: 0px 5px; margin-right: 5px; border-radius: 3px; font-size: 13px; line-height:21px; font-family: Montserrat, sans-serif">PRO</a>
|
||||
*
|
||||
* Defines event `networkQualityLevelChanged` dispatched by [[Session]].
|
||||
* This event is fired when the network quality level of a [[Connection]] changes. See [network quality](/en/stable/advanced-features/network-quality/)
|
||||
*/
|
||||
export class NetworkQualityLevelChangedEvent extends Event {
|
||||
|
||||
/**
|
||||
* New value of the network quality level
|
||||
*/
|
||||
newValue: number;
|
||||
|
||||
/**
|
||||
* Old value of the network quality level
|
||||
*/
|
||||
oldValue: number;
|
||||
|
||||
/**
|
||||
* Connection for whom the network quality level changed
|
||||
*/
|
||||
connection: Connection
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, newValue: number, oldValue: number, connection: Connection) {
|
||||
super(false, target, 'networkQualityLevelChanged');
|
||||
this.newValue = newValue;
|
||||
this.oldValue = oldValue;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../..';
|
||||
|
||||
|
||||
/**
|
||||
* Defines the following events:
|
||||
* - `publisherStartSpeaking`: dispatched by [[Session]] when a remote user has started speaking
|
||||
* - `publisherStopSpeaking`: dispatched by [[Session]] when a remote user has stopped speaking
|
||||
*
|
||||
* More information:
|
||||
* - This events will only be triggered for **remote streams that have audio tracks** ([[Stream.hasAudio]] must be true)
|
||||
* - You can further configure how the events are dispatched by setting property `publisherSpeakingEventsOptions` in the call of [[OpenVidu.setAdvancedConfiguration]]
|
||||
*/
|
||||
export class PublisherSpeakingEvent extends Event {
|
||||
|
||||
/**
|
||||
* The client that started or stopped speaking
|
||||
*/
|
||||
connection: Connection;
|
||||
|
||||
/**
|
||||
* The streamId of the Stream affected by the speaking event
|
||||
*/
|
||||
streamId: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, type: string, connection: Connection, streamId: string) {
|
||||
super(false, target, type);
|
||||
this.type = type;
|
||||
this.connection = connection;
|
||||
this.streamId = streamId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
|
||||
|
||||
/**
|
||||
* Defines the following events:
|
||||
* - `recordingStarted`: dispatched by [[Session]] after the session has started being recorded
|
||||
* - `recordingStopped`: dispatched by [[Session]] after the session has stopped being recorded
|
||||
*/
|
||||
export class RecordingEvent extends Event {
|
||||
|
||||
/**
|
||||
* The recording ID generated in openvidu-server
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The recording name you supplied to openvidu-server. For example, to name your recording file MY_RECORDING:
|
||||
* - With **API REST**: POST to `/api/recordings/start` passing JSON body `{"session":"sessionId","name":"MY_RECORDING"}`
|
||||
* - With **openvidu-java-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, new RecordingProperties.Builder().name("MY_RECORDING").build())`
|
||||
* - With **openvidu-node-client**: `OpenVidu.startRecording(sessionId, "MY_RECORDING")` or `OpenVidu.startRecording(sessionId, {name: "MY_RECORDING"})`
|
||||
*
|
||||
* If no name is supplied, this property will be undefined and the recorded file will be named after property [[id]]
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* For 'recordingStopped' event:
|
||||
* - "recordingStoppedByServer": the recording has been gracefully stopped by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "automaticStop": see [Automatic stop of recordings](/en/stable/advanced-features/recording/#automatic-stop-of-recordings)
|
||||
* - "mediaServerDisconnect": OpenVidu Media Node has crashed or lost its connection. A new Media Node instance is active and the recording has been stopped (no media streams are available in the new Media Node)
|
||||
*
|
||||
* For 'recordingStarted' empty string
|
||||
*/
|
||||
reason?: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, type: string, id: string, name: string, reason?: string) {
|
||||
super(false, target, type);
|
||||
this.id = id;
|
||||
if (name !== id) {
|
||||
this.name = name;
|
||||
}
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
|
||||
/**
|
||||
* Defines event `sessionDisconnected` dispatched by [[Session]] after the local user has left the session. This is the local version of the `connectionDestroyed` event, which is only dispatched by remote users
|
||||
*/
|
||||
export class SessionDisconnectedEvent extends Event {
|
||||
|
||||
/**
|
||||
* - "disconnect": you have called `Session.disconnect()`
|
||||
* - "forceDisconnectByUser": you have been evicted from the Session by other user calling `Session.forceDisconnect()`
|
||||
* - "forceDisconnectByServer": you have been evicted from the Session by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "networkDisconnect": your network connection has dropped. Before a SessionDisconnectedEvent with this reason is triggered,
|
||||
* Session object will always have previously dispatched a `reconnecting` event. If the reconnection process succeeds,
|
||||
* Session object will dispatch a `reconnected` event. If it fails, Session object will dispatch a SessionDisconnectedEvent
|
||||
* with reason "networkDisconnect"
|
||||
*/
|
||||
reason: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, reason: string) {
|
||||
super(true, target, 'sessionDisconnected');
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
callDefaultBehavior() {
|
||||
|
||||
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'");
|
||||
|
||||
const session = <Session>this.target;
|
||||
|
||||
// Dispose and delete all remote Connections
|
||||
session.remoteConnections.forEach(remoteConnection => {
|
||||
const connectionId = remoteConnection.connectionId;
|
||||
if (!!session.remoteConnections.get(connectionId)?.stream) {
|
||||
session.remoteConnections.get(connectionId)?.stream!.disposeWebRtcPeer();
|
||||
session.remoteConnections.get(connectionId)?.stream!.disposeMediaStream();
|
||||
if (session.remoteConnections.get(connectionId)?.stream!.streamManager) {
|
||||
session.remoteConnections.get(connectionId)?.stream!.streamManager.removeAllVideos();
|
||||
}
|
||||
const streamId = session.remoteConnections.get(connectionId)?.stream?.streamId;
|
||||
if (!!streamId) {
|
||||
session.remoteStreamsCreated.delete(streamId);
|
||||
}
|
||||
session.remoteConnections.get(connectionId)?.dispose();
|
||||
}
|
||||
session.remoteConnections.delete(connectionId);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Connection } from '../../OpenVidu/Connection';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
|
||||
|
||||
/**
|
||||
* Defines the following events:
|
||||
* - `signal`: dispatched by [[Session]] when a signal is received
|
||||
* - `signal:TYPE`: dispatched by [[Session]] when a signal of type TYPE is received
|
||||
*/
|
||||
export class SignalEvent extends Event {
|
||||
|
||||
/**
|
||||
* The type of signal. It is string `"signal"` for those signals sent with no [[SignalOptions.type]] property, and `"signal:type"` if was sent with a
|
||||
* valid [[SignalOptions.type]] property.
|
||||
*
|
||||
* The client must be specifically subscribed to `Session.on('signal:type', function(signalEvent) {...})` to trigger that type of signal.
|
||||
*
|
||||
* Subscribing to `Session.on('signal', function(signalEvent) {...})` will trigger all signals, no matter their type.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* The message of the signal (can be empty)
|
||||
*/
|
||||
data?: string;
|
||||
|
||||
/**
|
||||
* The client that sent the signal. This property is undefined if the signal
|
||||
* was directly generated by the application server (not by other client)
|
||||
*/
|
||||
from?: Connection;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session, type: string, data?: string, from?: Connection) {
|
||||
super(false, target, 'signal');
|
||||
if (!!type) {
|
||||
this.type = 'signal:' + type;
|
||||
}
|
||||
this.data = data;
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Publisher } from '../../OpenVidu/Publisher';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Stream } from '../../OpenVidu/Stream';
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
* Defines the following events:
|
||||
* - `streamCreated`: dispatched by [[Session]] and [[Publisher]] after some user has started publishing to the session
|
||||
* - `streamDestroyed`: dispatched by [[Session]] and [[Publisher]] after some user has stopped publishing to the session
|
||||
*/
|
||||
export class StreamEvent extends Event {
|
||||
|
||||
/**
|
||||
* Stream object that was created or destroyed
|
||||
*/
|
||||
stream: Stream;
|
||||
|
||||
/**
|
||||
* For 'streamDestroyed' event:
|
||||
* - "unpublish": method `Session.unpublish()` has been called
|
||||
* - "disconnect": method `Session.disconnect()` has been called
|
||||
* - "forceUnpublishByUser": some user has called `Session.forceUnpublish()` over the Stream
|
||||
* - "forceDisconnectByUser": some user has called `Session.forceDisconnect()` over the Stream
|
||||
* - "forceUnpublishByServer": the user's stream has been unpublished from the Session by the application
|
||||
* - "forceDisconnectByServer": the user has been evicted from the Session by the application
|
||||
* - "sessionClosedByServer": the Session has been closed by the application
|
||||
* - "networkDisconnect": the user's network connection has dropped
|
||||
* - "mediaServerDisconnect": OpenVidu Media Node has crashed or lost its connection. A new Media Node instance is active and no media streams are available in the Media Node
|
||||
*
|
||||
* For 'streamCreated' empty string
|
||||
*/
|
||||
reason: string;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(cancelable: boolean, target: Session | Publisher, type: string, stream: Stream, reason: string) {
|
||||
super(cancelable, target, type);
|
||||
this.stream = stream;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
callDefaultBehavior() {
|
||||
if (this.type === 'streamDestroyed') {
|
||||
|
||||
if (this.target instanceof Session) {
|
||||
// Remote Stream
|
||||
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Session'");
|
||||
this.stream.disposeWebRtcPeer();
|
||||
} else if (this.target instanceof Publisher) {
|
||||
// Local Stream
|
||||
logger.info("Calling default behavior upon '" + this.type + "' event dispatched by 'Publisher'");
|
||||
clearInterval((<Publisher>this.target).screenShareResizeInterval);
|
||||
this.stream.isLocalStreamReadyToPublish = false;
|
||||
|
||||
// Delete Publisher object from OpenVidu publishers array
|
||||
const openviduPublishers = (<Publisher>this.target).openvidu.publishers;
|
||||
for (let i = 0; i < openviduPublishers.length; i++) {
|
||||
if (openviduPublishers[i] === (<Publisher>this.target)) {
|
||||
openviduPublishers.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dispose the MediaStream local object
|
||||
this.stream.disposeMediaStream();
|
||||
|
||||
// Remove from DOM all video elements associated to this Stream, if there's a StreamManager defined
|
||||
// (method Session.subscribe must have been called)
|
||||
if (this.stream.streamManager) this.stream.streamManager.removeAllVideos();
|
||||
|
||||
// Delete stream from Session.remoteStreamsCreated map
|
||||
this.stream.session.remoteStreamsCreated.delete(this.stream.streamId);
|
||||
|
||||
// Delete StreamOptionsServer from remote Connection
|
||||
const remoteConnection = this.stream.session.remoteConnections.get(this.stream.connection.connectionId);
|
||||
if (!!remoteConnection && !!remoteConnection.remoteOptions) {
|
||||
const streamOptionsServer = remoteConnection.remoteOptions.streams;
|
||||
for (let i = streamOptionsServer.length - 1; i >= 0; --i) {
|
||||
if (streamOptionsServer[i].id === this.stream.streamId) {
|
||||
streamOptionsServer.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
|
||||
/**
|
||||
* Defines the following events:
|
||||
* - `streamPlaying`: dispatched by [[StreamManager]] ([[Publisher]] and [[Subscriber]]) whenever its media stream starts playing (one of its videos has media
|
||||
* and has begun to play). This event will be dispatched when these 3 conditions are met 1) The StreamManager has no video associated in the DOM 2) It is associated to one video 3) That video starts playing
|
||||
* - `streamAudioVolumeChange`: dispatched by [[StreamManager]] ([[Publisher]] and [[Subscriber]]) when the volume of its Stream's audio track
|
||||
* changes. Only applies if [[Stream.hasAudio]] is `true`. The frequency this event is fired with is defined by property `interval` of
|
||||
* [[OpenViduAdvancedConfiguration.publisherSpeakingEventsOptions]] (default 100ms)
|
||||
*/
|
||||
export class StreamManagerEvent extends Event {
|
||||
|
||||
/**
|
||||
* For `streamAudioVolumeChange` event:
|
||||
* - `{newValue: number, oldValue: number}`: new and old audio volume values. These values are between -100 (silence) and 0 (loudest possible volume).
|
||||
* They are not exact and depend on how the browser is managing the audio track, but -100 and 0 can be taken as limit values.
|
||||
*
|
||||
* For `streamPlaying` event undefined
|
||||
*/
|
||||
value: Object | undefined;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: StreamManager, type: string, value: Object | undefined) {
|
||||
super(false, target, type);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { Session } from '../../OpenVidu/Session';
|
||||
import { Stream } from '../../OpenVidu/Stream';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
|
||||
/**
|
||||
* Defines event `streamPropertyChanged` dispatched by [[Session]] as well as by [[StreamManager]] ([[Publisher]] and [[Subscriber]]).
|
||||
* This event is fired when any remote stream (owned by a Subscriber) or local stream (owned by a Publisher) undergoes
|
||||
* any change in any of its mutable properties (see [[changedProperty]]).
|
||||
*/
|
||||
export class StreamPropertyChangedEvent extends Event {
|
||||
|
||||
/**
|
||||
* The Stream whose property has changed. You can always identify the user publishing the changed stream by consulting property [[Stream.connection]]
|
||||
*/
|
||||
stream: Stream;
|
||||
|
||||
/**
|
||||
* The property of the stream that changed. This value is either `"videoActive"`, `"audioActive"`, `"videoDimensions"` or `"filter"`
|
||||
*/
|
||||
changedProperty: string;
|
||||
|
||||
/**
|
||||
* Cause of the change on the stream's property:
|
||||
* - For `videoActive`: `"publishVideo"`
|
||||
* - For `audioActive`: `"publishAudio"`
|
||||
* - For `videoDimensions`: `"deviceRotated"` or `"screenResized"`
|
||||
* - For `filter`: `"applyFilter"`, `"execFilterMethod"` or `"removeFilter"`
|
||||
*/
|
||||
reason: string;
|
||||
|
||||
/**
|
||||
* New value of the property (after change, current value)
|
||||
*/
|
||||
newValue: Object;
|
||||
|
||||
/**
|
||||
* Previous value of the property (before change)
|
||||
*/
|
||||
oldValue: Object;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(target: Session | StreamManager, stream: Stream, changedProperty: string, newValue: Object, oldValue: Object, reason: string) {
|
||||
super(false, target, 'streamPropertyChanged');
|
||||
this.stream = stream;
|
||||
this.changedProperty = changedProperty;
|
||||
this.newValue = newValue;
|
||||
this.oldValue = oldValue;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Event } from './Event';
|
||||
import { StreamManager } from '../../OpenVidu/StreamManager';
|
||||
|
||||
|
||||
/**
|
||||
* Defines the following events:
|
||||
* - `videoElementCreated`: dispatched by [[Publisher]] and [[Subscriber]] whenever a new HTML video element has been inserted into DOM by OpenVidu Browser library. See
|
||||
* [Manage video players](/en/stable/cheatsheet/manage-videos) section.
|
||||
* - `videoElementDestroyed`: dispatched by [[Publisher]] and [[Subscriber]] whenever an HTML video element has been removed from DOM by OpenVidu Browser library.
|
||||
*/
|
||||
export class VideoElementEvent extends Event {
|
||||
|
||||
/**
|
||||
* Video element that was created or destroyed
|
||||
*/
|
||||
element: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
constructor(element: HTMLVideoElement, target: StreamManager, type: string) {
|
||||
super(false, target, type);
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
// tslint:disable-next-line:no-empty
|
||||
callDefaultBehavior() { }
|
||||
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface CustomMediaStreamConstraints {
|
||||
constraints: MediaStreamConstraints;
|
||||
audioTrack: MediaStreamTrack | undefined;
|
||||
videoTrack: MediaStreamTrack | undefined;
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../../OpenVidu/Connection';
|
||||
import { Filter } from '../../../OpenVidu/Filter';
|
||||
|
||||
export interface InboundStreamOptions {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
connection: Connection;
|
||||
hasAudio: boolean;
|
||||
hasVideo: boolean;
|
||||
audioActive: boolean;
|
||||
videoActive: boolean;
|
||||
typeOfVideo: string;
|
||||
frameRate: number;
|
||||
videoDimensions: { width: number, height: number };
|
||||
filter?: Filter;
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { RemoteConnectionOptions } from './RemoteConnectionOptions';
|
||||
|
||||
export interface LocalConnectionOptions {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
metadata: string;
|
||||
value: RemoteConnectionOptions[];
|
||||
session: string; // OpenVidu Session identifier
|
||||
sessionId: string; // JSON-RPC session identifier
|
||||
role: string;
|
||||
record: boolean;
|
||||
coturnIp: string;
|
||||
turnUsername: string;
|
||||
turnCredential: string;
|
||||
version: string;
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { PublisherProperties } from '../Public/PublisherProperties';
|
||||
|
||||
export interface OutboundStreamOptions {
|
||||
publisherProperties: PublisherProperties;
|
||||
mediaConstraints: MediaStreamConstraints;
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { StreamOptionsServer } from './StreamOptionsServer';
|
||||
|
||||
export interface RemoteConnectionOptions {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
metadata: string;
|
||||
streams: StreamOptionsServer[];
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
export interface SessionOptions {
|
||||
sessionId: string;
|
||||
participantId: string;
|
||||
metadata: string;
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../../OpenVidu/Connection';
|
||||
|
||||
export interface SignalOptions {
|
||||
type?: string;
|
||||
to?: Connection[];
|
||||
data?: string;
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Filter } from '../../../OpenVidu/Filter';
|
||||
|
||||
export interface StreamOptionsServer {
|
||||
id: string;
|
||||
createdAt: number;
|
||||
hasAudio: boolean;
|
||||
hasVideo: boolean;
|
||||
audioActive: boolean;
|
||||
videoActive: boolean;
|
||||
typeOfVideo: string;
|
||||
frameRate: number;
|
||||
videoDimensions: string;
|
||||
filter: Filter;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* See [[Session.capabilities]]
|
||||
*/
|
||||
export interface Capabilities {
|
||||
|
||||
/**
|
||||
* `true` if the client can call [[Session.forceDisconnect]], `false` if not
|
||||
*/
|
||||
forceDisconnect: boolean;
|
||||
|
||||
/**
|
||||
* `true` if the client can call [[Session.forceUnpublish]], `false` if not
|
||||
*/
|
||||
forceUnpublish: boolean;
|
||||
|
||||
/**
|
||||
* `true` if the client can call [[Session.publish]], `false` if not
|
||||
*/
|
||||
publish: boolean;
|
||||
|
||||
/**
|
||||
* `true` if the client can call [[Session.subscribe]], `false` if not (true for every user for now)
|
||||
*/
|
||||
subscribe: boolean;
|
||||
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* See [[OpenVidu.getDevices]]
|
||||
*/
|
||||
export interface Device {
|
||||
|
||||
/**
|
||||
* `"videoinput"`, `"audioinput"`
|
||||
*/
|
||||
kind: string;
|
||||
|
||||
/**
|
||||
* Unique ID for the device. Use it on `audioSource` or `videoSource` properties of [[PublisherProperties]]
|
||||
*/
|
||||
deviceId: string;
|
||||
|
||||
/**
|
||||
* Description of the device. An empty string if the user hasn't granted permissions to the site to access the device
|
||||
*/
|
||||
label: string;
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* See [[OpenVidu.setAdvancedConfiguration]]
|
||||
*/
|
||||
export interface OpenViduAdvancedConfiguration {
|
||||
|
||||
/**
|
||||
* Array of [RTCIceServer](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer) to be used by OpenVidu Browser. By default OpenVidu will generate the required credentials to use the COTURN server hosted along OpenVidu Server
|
||||
* You can also set this property to string 'freeice' to force the use of free STUN servers instead (got thanks to [freeice](https://github.com/DamonOehlman/freeice) library).
|
||||
*/
|
||||
iceServers?: RTCIceServer[] | string;
|
||||
|
||||
/**
|
||||
* URL to a custom screen share extension for Chrome (always based on ours: [openvidu-screen-sharing-chrome-extension](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension)) to be used instead of the default one.
|
||||
* Must be something like this: `https://chrome.google.com/webstore/detail/YOUR_WEBSTORE_EXTENSION_NAME/YOUR_EXTENSION_ID`
|
||||
*/
|
||||
screenShareChromeExtension?: string;
|
||||
|
||||
/**
|
||||
* Custom configuration for the [[PublisherSpeakingEvent]] feature and the [StreamManagerEvent.streamAudioVolumeChange](/en/stable/api/openvidu-browser/classes/streammanagerevent.html) feature. It is an object which includes the following optional properties:
|
||||
* - `interval`: (number) how frequently the analyser polls the audio stream to check if speaking has started/stopped or audio volume has changed. Default **100** (ms)
|
||||
* - `threshold`: (number) the volume at which _publisherStartSpeaking_ and _publisherStopSpeaking_ events will be fired. Default **-50** (dB)
|
||||
*
|
||||
* This sets the global default configuration that will affect all streams, but you can later customize these values for each specific stream by calling [[StreamManager.updatePublisherSpeakingEventsOptions]]
|
||||
*/
|
||||
publisherSpeakingEventsOptions?: any;
|
||||
|
||||
/**
|
||||
* Determines the automatic reconnection process policy. Whenever the client's network drops, OpenVidu Browser starts a reconnection process with OpenVidu Server. After network is recovered, OpenVidu Browser automatically
|
||||
* inspects all of its media streams to see their status. For any of them that are broken, it asks OpenVidu Server for a forced and silent reconnection.
|
||||
*
|
||||
* This policy is technically enough to recover any broken media connection after a network drop, but in practice it has been proven that OpenVidu Browser may think a media connection has properly recovered when in fact it has not.
|
||||
* This is not a common case, and it only affects Publisher streams, but it may occur. This property allows **forcing OpenVidu Browser to reconnect all of its outgoing media streams** after a network drop regardless of their supposed status.
|
||||
*
|
||||
* Default to `false`.
|
||||
*/
|
||||
forceMediaReconnectionAfterNetworkDrop?: boolean;
|
||||
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Filter } from '../../../OpenVidu/Filter';
|
||||
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
|
||||
|
||||
/**
|
||||
* See [[OpenVidu.initPublisher]]
|
||||
*/
|
||||
export interface PublisherProperties {
|
||||
|
||||
/**
|
||||
* Which device should provide the audio source. Can be:
|
||||
* - Property `deviceId` of a [[Device]]
|
||||
* - A MediaStreamTrack obtained from a MediaStream object with [[OpenVidu.getUserMedia]]
|
||||
* - `false` or null to have a video-only publisher
|
||||
* @default _Default microphone_
|
||||
*/
|
||||
audioSource?: string | MediaStreamTrack | boolean;
|
||||
|
||||
/**
|
||||
* Desired framerate of the video in frames per second.
|
||||
* Limiting the framerate has always effect on browsers Chrome and Opera. Firefox requires that the input device explicitly supports the desired framerate.
|
||||
* @default undefined
|
||||
*/
|
||||
frameRate?: number;
|
||||
|
||||
/**
|
||||
* How the video element of the publisher should be inserted in the DOM
|
||||
* @default VideoInsertMode.APPEND
|
||||
*/
|
||||
insertMode?: VideoInsertMode | string;
|
||||
|
||||
/**
|
||||
* Whether the publisher's video will be mirrored in the page or not. Only affects the local view of the publisher in the browser (remote streams will not be mirrored). If `videoSource` is set to "screen" this property is fixed to `false`
|
||||
* @default true
|
||||
*/
|
||||
mirror?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to initially publish to the session with the audio unmuted or muted. Only makes sense if property `audioSource` is NOT set to *false* or *null*. You can change the audio state later during the session with [[Publisher.publishAudio]]
|
||||
* @default true
|
||||
*/
|
||||
publishAudio?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to initially publish to the session with the video enabled or disabled. Only makes sense if property `videoSource` is NOT set to *false* or *null*. You can change the video state later during the session with [[Publisher.publishVideo]]
|
||||
* @default true
|
||||
*/
|
||||
publishVideo?: boolean;
|
||||
|
||||
/**
|
||||
* Resolution of the video: `"320x240"`, `"640x480"`, `"1280x720"` (low, medium and high quality respectively)
|
||||
* @default "640x480"
|
||||
*/
|
||||
resolution?: string;
|
||||
|
||||
/**
|
||||
* Which device should provide the video source. Can be:
|
||||
* - Property `deviceId` of a [[Device]]
|
||||
* - `"screen"` to screen-share. We provide a default screen-shraring extension for Chrome that can run in any domain, but you can customize it so it has your own icon, your own name, etc. Visit this
|
||||
* [GitHub repository](https://github.com/OpenVidu/openvidu-screen-sharing-chrome-extension/) to learn how. Once you have uploaded your own extension to Chrome Web Store,
|
||||
* simply call `OpenVidu.setAdvancedConfiguration({screenShareChromeExtension : "https://chrome.google.com/webstore/detail/YOUR_EXTENSION_NAME/YOUR_EXTENSION_ID"})` before calling `OpenVidu.initPublisher(targetElement, {videoSource: "screen"})`.
|
||||
* For Firefox (<66) `"screen"` string will ask for permissions to share the entire screen. To ask for a specific window or application, use `"window"` string instead (this only applies to Firefox).
|
||||
* - A MediaStreamTrack obtained from a MediaStream object with [[OpenVidu.getUserMedia]]
|
||||
* - `false` or null to have an audio-only publisher
|
||||
* @default _Default camera_
|
||||
*/
|
||||
videoSource?: string | MediaStreamTrack | boolean;
|
||||
|
||||
/**
|
||||
* **WARNING**: experimental option. This property may change in the near future
|
||||
*
|
||||
* Define a filter to apply in the Publisher's stream
|
||||
*/
|
||||
filter?: Filter;
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Connection } from '../../../OpenVidu/Connection';
|
||||
|
||||
/**
|
||||
* See [[Session.signal]]
|
||||
*/
|
||||
export interface SignalOptions {
|
||||
|
||||
/**
|
||||
* The actual message of the signal.
|
||||
*/
|
||||
data?: string;
|
||||
|
||||
/**
|
||||
* The participants to whom to send the signal. They will only receive it if they are subscribed to
|
||||
* event `Session.on('signal')`. If empty or undefined, the signal will be send to all participants.
|
||||
*/
|
||||
to?: Connection[];
|
||||
|
||||
/**
|
||||
* The type of the signal. Participants subscribed to event `Session.on('signal:type')` will
|
||||
* receive it. Participants subscribed to `Session.on('signal')` will receive all signals.
|
||||
*/
|
||||
type?: string;
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
|
||||
|
||||
|
||||
export interface StreamManagerVideo {
|
||||
|
||||
/**
|
||||
* DOM video element displaying the StreamManager's stream
|
||||
*/
|
||||
video: HTMLVideoElement;
|
||||
|
||||
/**
|
||||
* `id` attribute of the DOM video element displaying the StreamManager's stream
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The DOM HTMLElement assigned as target element when creating a video for the StreamManager. This property is defined when:
|
||||
* - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing a valid `targetElement` parameter.
|
||||
* - [[StreamManager.createVideoElement]] has been called.
|
||||
*
|
||||
* This property is undefined when:
|
||||
* - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing *null* or *undefined* as `targetElement` parameter.
|
||||
* - [[StreamManager.addVideoElement]] has been called.
|
||||
*/
|
||||
targetElement?: HTMLElement;
|
||||
|
||||
/**
|
||||
* How the DOM video element should be inserted with respect to `targetElement`. This property is defined when:
|
||||
* - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing a valid `targetElement` parameter.
|
||||
* - [[StreamManager.createVideoElement]] has been called.
|
||||
*
|
||||
* This property is undefined when:
|
||||
* - [[OpenVidu.initPublisher]] or [[Session.subscribe]] methods have been called passing *null* or *undefined* as `targetElement` parameter.
|
||||
* - [[StreamManager.addVideoElement]] has been called.
|
||||
*/
|
||||
insertMode?: VideoInsertMode;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
canplayListenerAdded: boolean;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import { VideoInsertMode } from '../../Enums/VideoInsertMode';
|
||||
|
||||
/**
|
||||
* See [[Session.subscribe]]
|
||||
*/
|
||||
export interface SubscriberProperties {
|
||||
|
||||
/**
|
||||
* How the video element of the subscriber should be inserted in the DOM
|
||||
* @default VideoInsertMode.APPEND
|
||||
*/
|
||||
insertMode?: VideoInsertMode | string;
|
||||
|
||||
/**
|
||||
* Whether to initially subscribe to the audio track of the stream or not. You can change the audio state later with [[Subscriber.subscribeToAudio]]
|
||||
* @default true
|
||||
*/
|
||||
subscribeToAudio?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to initially subscribe to the video track of the stream or not. You can change the video state later with [[Subscriber.subscribeToVideo]]
|
||||
* @default true
|
||||
*/
|
||||
subscribeToVideo?: boolean;
|
||||
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
function Mapper()
|
||||
{
|
||||
var sources = {};
|
||||
|
||||
|
||||
this.forEach = function(callback)
|
||||
{
|
||||
for(var key in sources)
|
||||
{
|
||||
var source = sources[key];
|
||||
|
||||
for(var key2 in source)
|
||||
callback(source[key2]);
|
||||
};
|
||||
};
|
||||
|
||||
this.get = function(id, source)
|
||||
{
|
||||
var ids = sources[source];
|
||||
if(ids == undefined)
|
||||
return undefined;
|
||||
|
||||
return ids[id];
|
||||
};
|
||||
|
||||
this.remove = function(id, source)
|
||||
{
|
||||
var ids = sources[source];
|
||||
if(ids == undefined)
|
||||
return;
|
||||
|
||||
delete ids[id];
|
||||
|
||||
// Check it's empty
|
||||
for(var i in ids){return false}
|
||||
|
||||
delete sources[source];
|
||||
};
|
||||
|
||||
this.set = function(value, id, source)
|
||||
{
|
||||
if(value == undefined)
|
||||
return this.remove(id, source);
|
||||
|
||||
var ids = sources[source];
|
||||
if(ids == undefined)
|
||||
sources[source] = ids = {};
|
||||
|
||||
ids[id] = value;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Mapper.prototype.pop = function(id, source)
|
||||
{
|
||||
var value = this.get(id, source);
|
||||
if(value == undefined)
|
||||
return undefined;
|
||||
|
||||
this.remove(id, source);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
module.exports = Mapper;
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var JsonRpcClient = require('./jsonrpcclient');
|
||||
|
||||
|
||||
exports.JsonRpcClient = JsonRpcClient;
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var RpcBuilder = require('../');
|
||||
var WebSocketWithReconnection = require('./transports/webSocketWithReconnection');
|
||||
var OpenViduLogger = require('../../../Logger/OpenViduLogger').OpenViduLogger;
|
||||
|
||||
Date.now = Date.now || function () {
|
||||
return +new Date;
|
||||
};
|
||||
|
||||
var PING_INTERVAL = 5000;
|
||||
|
||||
var RECONNECTING = 'RECONNECTING';
|
||||
var CONNECTED = 'CONNECTED';
|
||||
var DISCONNECTED = 'DISCONNECTED';
|
||||
|
||||
var Logger = OpenViduLogger.getInstance();
|
||||
|
||||
/**
|
||||
*
|
||||
* heartbeat: interval in ms for each heartbeat message,
|
||||
* <pre>
|
||||
* ws : {
|
||||
* uri : URI to conntect to,
|
||||
* onconnected : callback method to invoke when connection is successful,
|
||||
* ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
|
||||
* onreconnecting : callback method to invoke when the client is reconnecting,
|
||||
* onreconnected : callback method to invoke when the client successfully reconnects,
|
||||
* onerror : callback method to invoke when there is an error
|
||||
* },
|
||||
* rpc : {
|
||||
* requestTimeout : timeout for a request,
|
||||
* sessionStatusChanged: callback method for changes in session status,
|
||||
* mediaRenegotiation: mediaRenegotiation
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
function JsonRpcClient(configuration) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var wsConfig = configuration.ws;
|
||||
|
||||
var notReconnectIfNumLessThan = -1;
|
||||
|
||||
var pingNextNum = 0;
|
||||
var enabledPings = true;
|
||||
var pingPongStarted = false;
|
||||
var pingInterval;
|
||||
|
||||
var status = DISCONNECTED;
|
||||
|
||||
var onreconnecting = wsConfig.onreconnecting;
|
||||
var onreconnected = wsConfig.onreconnected;
|
||||
var onconnected = wsConfig.onconnected;
|
||||
var onerror = wsConfig.onerror;
|
||||
|
||||
configuration.rpc.pull = function (params, request) {
|
||||
request.reply(null, "push");
|
||||
}
|
||||
|
||||
wsConfig.onreconnecting = function () {
|
||||
Logger.debug("--------- ONRECONNECTING -----------");
|
||||
if (status === RECONNECTING) {
|
||||
Logger.error("Websocket already in RECONNECTING state when receiving a new ONRECONNECTING message. Ignoring it");
|
||||
return;
|
||||
}
|
||||
|
||||
stopPing();
|
||||
|
||||
status = RECONNECTING;
|
||||
if (onreconnecting) {
|
||||
onreconnecting();
|
||||
}
|
||||
}
|
||||
|
||||
wsConfig.onreconnected = function () {
|
||||
Logger.debug("--------- ONRECONNECTED -----------");
|
||||
if (status === CONNECTED) {
|
||||
Logger.error("Websocket already in CONNECTED state when receiving a new ONRECONNECTED message. Ignoring it");
|
||||
return;
|
||||
}
|
||||
status = CONNECTED;
|
||||
|
||||
updateNotReconnectIfLessThan();
|
||||
|
||||
if (onreconnected) {
|
||||
onreconnected();
|
||||
}
|
||||
}
|
||||
|
||||
wsConfig.onconnected = function () {
|
||||
Logger.debug("--------- ONCONNECTED -----------");
|
||||
if (status === CONNECTED) {
|
||||
Logger.error("Websocket already in CONNECTED state when receiving a new ONCONNECTED message. Ignoring it");
|
||||
return;
|
||||
}
|
||||
status = CONNECTED;
|
||||
|
||||
enabledPings = true;
|
||||
usePing();
|
||||
|
||||
if (onconnected) {
|
||||
onconnected();
|
||||
}
|
||||
}
|
||||
|
||||
wsConfig.onerror = function (error) {
|
||||
Logger.debug("--------- ONERROR -----------");
|
||||
|
||||
status = DISCONNECTED;
|
||||
|
||||
stopPing();
|
||||
|
||||
if (onerror) {
|
||||
onerror(error);
|
||||
}
|
||||
}
|
||||
|
||||
var ws = new WebSocketWithReconnection(wsConfig);
|
||||
|
||||
Logger.debug('Connecting websocket to URI: ' + wsConfig.uri);
|
||||
|
||||
var rpcBuilderOptions = {
|
||||
request_timeout: configuration.rpc.requestTimeout,
|
||||
ping_request_timeout: configuration.rpc.heartbeatRequestTimeout
|
||||
};
|
||||
|
||||
var rpc = new RpcBuilder(RpcBuilder.packers.JsonRPC, rpcBuilderOptions, ws,
|
||||
function (request) {
|
||||
|
||||
Logger.debug('Received request: ' + JSON.stringify(request));
|
||||
|
||||
try {
|
||||
var func = configuration.rpc[request.method];
|
||||
|
||||
if (func === undefined) {
|
||||
Logger.error("Method " + request.method + " not registered in client");
|
||||
} else {
|
||||
func(request.params, request);
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error('Exception processing request: ' + JSON.stringify(request));
|
||||
Logger.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
this.send = function (method, params, callback) {
|
||||
if (method !== 'ping') {
|
||||
Logger.debug('Request: method:' + method + " params:" + JSON.stringify(params));
|
||||
}
|
||||
|
||||
var requestTime = Date.now();
|
||||
|
||||
rpc.encode(method, params, function (error, result) {
|
||||
if (error) {
|
||||
try {
|
||||
Logger.error("ERROR:" + error.message + " in Request: method:" +
|
||||
method + " params:" + JSON.stringify(params) + " request:" +
|
||||
error.request);
|
||||
if (error.data) {
|
||||
Logger.error("ERROR DATA:" + JSON.stringify(error.data));
|
||||
}
|
||||
} catch (e) {}
|
||||
error.requestTime = requestTime;
|
||||
}
|
||||
if (callback) {
|
||||
if (result != undefined && result.value !== 'pong') {
|
||||
Logger.debug('Response: ' + JSON.stringify(result));
|
||||
}
|
||||
callback(error, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateNotReconnectIfLessThan() {
|
||||
Logger.debug("notReconnectIfNumLessThan = " + pingNextNum + ' (old=' +
|
||||
notReconnectIfNumLessThan + ')');
|
||||
notReconnectIfNumLessThan = pingNextNum;
|
||||
}
|
||||
|
||||
function sendPing() {
|
||||
if (enabledPings) {
|
||||
var params = null;
|
||||
if (pingNextNum == 0 || pingNextNum == notReconnectIfNumLessThan) {
|
||||
params = {
|
||||
interval: configuration.heartbeat || PING_INTERVAL
|
||||
};
|
||||
}
|
||||
pingNextNum++;
|
||||
|
||||
self.send('ping', params, (function (pingNum) {
|
||||
return function (error, result) {
|
||||
if (error) {
|
||||
Logger.debug("Error in ping request #" + pingNum + " (" +
|
||||
error.message + ")");
|
||||
if (pingNum > notReconnectIfNumLessThan) {
|
||||
enabledPings = false;
|
||||
updateNotReconnectIfLessThan();
|
||||
Logger.debug("Server did not respond to ping message #" +
|
||||
pingNum + ". Reconnecting... ");
|
||||
ws.reconnectWs();
|
||||
}
|
||||
}
|
||||
}
|
||||
})(pingNextNum));
|
||||
} else {
|
||||
Logger.debug("Trying to send ping, but ping is not enabled");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If configuration.hearbeat has any value, the ping-pong will work with the interval
|
||||
* of configuration.hearbeat
|
||||
*/
|
||||
function usePing() {
|
||||
if (!pingPongStarted) {
|
||||
Logger.debug("Starting ping (if configured)")
|
||||
pingPongStarted = true;
|
||||
|
||||
if (configuration.heartbeat != undefined) {
|
||||
pingInterval = setInterval(sendPing, configuration.heartbeat);
|
||||
sendPing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stopPing() {
|
||||
clearInterval(pingInterval);
|
||||
pingPongStarted = false;
|
||||
enabledPings = false;
|
||||
pingNextNum = -1;
|
||||
rpc.cancel();
|
||||
}
|
||||
|
||||
this.close = function (code, reason) {
|
||||
Logger.debug("Closing with code: " + code + " because: " + reason);
|
||||
if (pingInterval != undefined) {
|
||||
Logger.debug("Clearing ping interval");
|
||||
clearInterval(pingInterval);
|
||||
}
|
||||
pingPongStarted = false;
|
||||
enabledPings = false;
|
||||
ws.close(code, reason);
|
||||
}
|
||||
|
||||
// This method is only for testing
|
||||
this.forceClose = function (millis) {
|
||||
ws.forceClose(millis);
|
||||
}
|
||||
|
||||
this.reconnect = function () {
|
||||
ws.reconnectWs();
|
||||
}
|
||||
|
||||
this.resetPing = function () {
|
||||
enabledPings = true;
|
||||
pingNextNum = 0;
|
||||
usePing();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = JsonRpcClient;
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
var WebSocketWithReconnection = require('./webSocketWithReconnection');
|
||||
|
||||
|
||||
exports.WebSocketWithReconnection = WebSocketWithReconnection;
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2013-2015 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var Logger = console;
|
||||
|
||||
var MAX_RETRIES = 2000; // Forever...
|
||||
var RETRY_TIME_MS = 3000; // FIXME: Implement exponential wait times...
|
||||
|
||||
var CONNECTING = 0;
|
||||
var OPEN = 1;
|
||||
var CLOSING = 2;
|
||||
var CLOSED = 3;
|
||||
|
||||
/*
|
||||
config = {
|
||||
uri : wsUri,
|
||||
onconnected : callback method to invoke when connection is successful,
|
||||
ondisconnect : callback method to invoke when the connection is lost (max retries for reconnecting reached),
|
||||
onreconnecting : callback method to invoke when the client is reconnecting,
|
||||
onreconnected : callback method to invoke when the client successfully reconnects,
|
||||
};
|
||||
*/
|
||||
function WebSocketWithReconnection(config) {
|
||||
var closing = false;
|
||||
var registerMessageHandler;
|
||||
var wsUri = config.uri;
|
||||
var reconnecting = false;
|
||||
|
||||
var ws = new WebSocket(wsUri);
|
||||
|
||||
ws.onopen = () => {
|
||||
Logger.debug("WebSocket connected to " + wsUri);
|
||||
if (config.onconnected) {
|
||||
config.onconnected();
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = error => {
|
||||
Logger.error(
|
||||
"Could not connect to " + wsUri + " (invoking onerror if defined)",
|
||||
error
|
||||
);
|
||||
if (config.onerror) {
|
||||
config.onerror(error);
|
||||
}
|
||||
};
|
||||
|
||||
var reconnectionOnClose = () => {
|
||||
if (ws.readyState === CLOSED) {
|
||||
if (closing) {
|
||||
Logger.debug("Connection closed by user");
|
||||
} else {
|
||||
Logger.debug("Connection closed unexpectecly. Reconnecting...");
|
||||
reconnect(MAX_RETRIES, 1);
|
||||
}
|
||||
} else {
|
||||
Logger.debug("Close callback from previous websocket. Ignoring it");
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = reconnectionOnClose;
|
||||
|
||||
function reconnect(maxRetries, numRetries) {
|
||||
Logger.debug(
|
||||
"reconnect (attempt #" + numRetries + ", max=" + maxRetries + ")"
|
||||
);
|
||||
if (numRetries === 1) {
|
||||
if (reconnecting) {
|
||||
Logger.warn(
|
||||
"Trying to reconnect when already reconnecting... Ignoring this reconnection."
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
reconnecting = true;
|
||||
}
|
||||
if (config.onreconnecting) {
|
||||
config.onreconnecting();
|
||||
}
|
||||
}
|
||||
reconnectAux(maxRetries, numRetries);
|
||||
}
|
||||
|
||||
function reconnectAux(maxRetries, numRetries) {
|
||||
Logger.debug("Reconnection attempt #" + numRetries);
|
||||
ws.close();
|
||||
ws = new WebSocket(wsUri);
|
||||
|
||||
ws.onopen = () => {
|
||||
Logger.debug(
|
||||
"Reconnected to " + wsUri + " after " + numRetries + " attempts..."
|
||||
);
|
||||
reconnecting = false;
|
||||
registerMessageHandler();
|
||||
if (config.onreconnected()) {
|
||||
config.onreconnected();
|
||||
}
|
||||
ws.onclose = reconnectionOnClose;
|
||||
};
|
||||
|
||||
ws.onerror = error => {
|
||||
Logger.warn("Reconnection error: ", error);
|
||||
if (numRetries === maxRetries) {
|
||||
if (config.ondisconnect) {
|
||||
config.ondisconnect();
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
reconnect(maxRetries, numRetries + 1);
|
||||
}, RETRY_TIME_MS);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.close = () => {
|
||||
closing = true;
|
||||
ws.close();
|
||||
};
|
||||
|
||||
this.reconnectWs = () => {
|
||||
Logger.debug("reconnectWs");
|
||||
reconnect(MAX_RETRIES, 1);
|
||||
};
|
||||
|
||||
this.send = message => {
|
||||
ws.send(message);
|
||||
};
|
||||
|
||||
this.addEventListener = (type, callback) => {
|
||||
registerMessageHandler = () => {
|
||||
ws.addEventListener(type, callback);
|
||||
};
|
||||
registerMessageHandler();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = WebSocketWithReconnection;
|
||||
|
|
@ -1,822 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2014 Kurento (http://kurento.org/)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
var defineProperty_IE8 = false
|
||||
if(Object.defineProperty)
|
||||
{
|
||||
try
|
||||
{
|
||||
Object.defineProperty({}, "x", {});
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
defineProperty_IE8 = true
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function(oThis) {
|
||||
if (typeof this !== 'function') {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
fNOP = function() {},
|
||||
fBound = function() {
|
||||
return fToBind.apply(this instanceof fNOP && oThis
|
||||
? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
fNOP.prototype = this.prototype;
|
||||
fBound.prototype = new fNOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var inherits = require('inherits');
|
||||
|
||||
var packers = require('./packers');
|
||||
var Mapper = require('./Mapper');
|
||||
|
||||
|
||||
var BASE_TIMEOUT = 5000;
|
||||
|
||||
|
||||
function unifyResponseMethods(responseMethods)
|
||||
{
|
||||
if(!responseMethods) return {};
|
||||
|
||||
for(var key in responseMethods)
|
||||
{
|
||||
var value = responseMethods[key];
|
||||
|
||||
if(typeof value == 'string')
|
||||
responseMethods[key] =
|
||||
{
|
||||
response: value
|
||||
}
|
||||
};
|
||||
|
||||
return responseMethods;
|
||||
};
|
||||
|
||||
function unifyTransport(transport)
|
||||
{
|
||||
if(!transport) return;
|
||||
|
||||
// Transport as a function
|
||||
if(transport instanceof Function)
|
||||
return {send: transport};
|
||||
|
||||
// WebSocket & DataChannel
|
||||
if(transport.send instanceof Function)
|
||||
return transport;
|
||||
|
||||
// Message API (Inter-window & WebWorker)
|
||||
if(transport.postMessage instanceof Function)
|
||||
{
|
||||
transport.send = transport.postMessage;
|
||||
return transport;
|
||||
}
|
||||
|
||||
// Stream API
|
||||
if(transport.write instanceof Function)
|
||||
{
|
||||
transport.send = transport.write;
|
||||
return transport;
|
||||
}
|
||||
|
||||
// Transports that only can receive messages, but not send
|
||||
if(transport.onmessage !== undefined) return;
|
||||
if(transport.pause instanceof Function) return;
|
||||
|
||||
throw new SyntaxError("Transport is not a function nor a valid object");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Representation of a RPC notification
|
||||
*
|
||||
* @class
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {String} method -method of the notification
|
||||
* @param params - parameters of the notification
|
||||
*/
|
||||
function RpcNotification(method, params)
|
||||
{
|
||||
if(defineProperty_IE8)
|
||||
{
|
||||
this.method = method
|
||||
this.params = params
|
||||
}
|
||||
else
|
||||
{
|
||||
Object.defineProperty(this, 'method', {value: method, enumerable: true});
|
||||
Object.defineProperty(this, 'params', {value: params, enumerable: true});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {object} packer
|
||||
*
|
||||
* @param {object} [options]
|
||||
*
|
||||
* @param {object} [transport]
|
||||
*
|
||||
* @param {Function} [onRequest]
|
||||
*/
|
||||
function RpcBuilder(packer, options, transport, onRequest)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
if(!packer)
|
||||
throw new SyntaxError('Packer is not defined');
|
||||
|
||||
if(!packer.pack || !packer.unpack)
|
||||
throw new SyntaxError('Packer is invalid');
|
||||
|
||||
var responseMethods = unifyResponseMethods(packer.responseMethods);
|
||||
|
||||
|
||||
if(options instanceof Function)
|
||||
{
|
||||
if(transport != undefined)
|
||||
throw new SyntaxError("There can't be parameters after onRequest");
|
||||
|
||||
onRequest = options;
|
||||
transport = undefined;
|
||||
options = undefined;
|
||||
};
|
||||
|
||||
if(options && options.send instanceof Function)
|
||||
{
|
||||
if(transport && !(transport instanceof Function))
|
||||
throw new SyntaxError("Only a function can be after transport");
|
||||
|
||||
onRequest = transport;
|
||||
transport = options;
|
||||
options = undefined;
|
||||
};
|
||||
|
||||
if(transport instanceof Function)
|
||||
{
|
||||
if(onRequest != undefined)
|
||||
throw new SyntaxError("There can't be parameters after onRequest");
|
||||
|
||||
onRequest = transport;
|
||||
transport = undefined;
|
||||
};
|
||||
|
||||
if(transport && transport.send instanceof Function)
|
||||
if(onRequest && !(onRequest instanceof Function))
|
||||
throw new SyntaxError("Only a function can be after transport");
|
||||
|
||||
options = options || {};
|
||||
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
if(onRequest)
|
||||
this.on('request', onRequest);
|
||||
|
||||
|
||||
if(defineProperty_IE8)
|
||||
this.peerID = options.peerID
|
||||
else
|
||||
Object.defineProperty(this, 'peerID', {value: options.peerID});
|
||||
|
||||
var max_retries = options.max_retries || 0;
|
||||
|
||||
|
||||
function transportMessage(event)
|
||||
{
|
||||
self.decode(event.data || event);
|
||||
};
|
||||
|
||||
this.getTransport = function()
|
||||
{
|
||||
return transport;
|
||||
}
|
||||
this.setTransport = function(value)
|
||||
{
|
||||
// Remove listener from old transport
|
||||
if(transport)
|
||||
{
|
||||
// W3C transports
|
||||
if(transport.removeEventListener)
|
||||
transport.removeEventListener('message', transportMessage);
|
||||
|
||||
// Node.js Streams API
|
||||
else if(transport.removeListener)
|
||||
transport.removeListener('data', transportMessage);
|
||||
};
|
||||
|
||||
// Set listener on new transport
|
||||
if(value)
|
||||
{
|
||||
// W3C transports
|
||||
if(value.addEventListener)
|
||||
value.addEventListener('message', transportMessage);
|
||||
|
||||
// Node.js Streams API
|
||||
else if(value.addListener)
|
||||
value.addListener('data', transportMessage);
|
||||
};
|
||||
|
||||
transport = unifyTransport(value);
|
||||
}
|
||||
|
||||
if(!defineProperty_IE8)
|
||||
Object.defineProperty(this, 'transport',
|
||||
{
|
||||
get: this.getTransport.bind(this),
|
||||
set: this.setTransport.bind(this)
|
||||
})
|
||||
|
||||
this.setTransport(transport);
|
||||
|
||||
|
||||
var request_timeout = options.request_timeout || BASE_TIMEOUT;
|
||||
var ping_request_timeout = options.ping_request_timeout || request_timeout;
|
||||
var response_timeout = options.response_timeout || BASE_TIMEOUT;
|
||||
var duplicates_timeout = options.duplicates_timeout || BASE_TIMEOUT;
|
||||
|
||||
|
||||
var requestID = 0;
|
||||
|
||||
var requests = new Mapper();
|
||||
var responses = new Mapper();
|
||||
var processedResponses = new Mapper();
|
||||
|
||||
var message2Key = {};
|
||||
|
||||
|
||||
/**
|
||||
* Store the response to prevent to process duplicate request later
|
||||
*/
|
||||
function storeResponse(message, id, dest)
|
||||
{
|
||||
var response =
|
||||
{
|
||||
message: message,
|
||||
/** Timeout to auto-clean old responses */
|
||||
timeout: setTimeout(function()
|
||||
{
|
||||
responses.remove(id, dest);
|
||||
},
|
||||
response_timeout)
|
||||
};
|
||||
|
||||
responses.set(response, id, dest);
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the response to ignore duplicated messages later
|
||||
*/
|
||||
function storeProcessedResponse(ack, from)
|
||||
{
|
||||
var timeout = setTimeout(function()
|
||||
{
|
||||
processedResponses.remove(ack, from);
|
||||
},
|
||||
duplicates_timeout);
|
||||
|
||||
processedResponses.set(timeout, ack, from);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Representation of a RPC request
|
||||
*
|
||||
* @class
|
||||
* @extends RpcNotification
|
||||
*
|
||||
* @constructor
|
||||
*
|
||||
* @param {String} method -method of the notification
|
||||
* @param params - parameters of the notification
|
||||
* @param {Integer} id - identifier of the request
|
||||
* @param [from] - source of the notification
|
||||
*/
|
||||
function RpcRequest(method, params, id, from, transport)
|
||||
{
|
||||
RpcNotification.call(this, method, params);
|
||||
|
||||
this.getTransport = function()
|
||||
{
|
||||
return transport;
|
||||
}
|
||||
this.setTransport = function(value)
|
||||
{
|
||||
transport = unifyTransport(value);
|
||||
}
|
||||
|
||||
if(!defineProperty_IE8)
|
||||
Object.defineProperty(this, 'transport',
|
||||
{
|
||||
get: this.getTransport.bind(this),
|
||||
set: this.setTransport.bind(this)
|
||||
})
|
||||
|
||||
var response = responses.get(id, from);
|
||||
|
||||
/**
|
||||
* @constant {Boolean} duplicated
|
||||
*/
|
||||
if(!(transport || self.getTransport()))
|
||||
{
|
||||
if(defineProperty_IE8)
|
||||
this.duplicated = Boolean(response)
|
||||
else
|
||||
Object.defineProperty(this, 'duplicated',
|
||||
{
|
||||
value: Boolean(response)
|
||||
});
|
||||
}
|
||||
|
||||
var responseMethod = responseMethods[method];
|
||||
|
||||
this.pack = packer.pack.bind(packer, this, id)
|
||||
|
||||
/**
|
||||
* Generate a response to this request
|
||||
*
|
||||
* @param {Error} [error]
|
||||
* @param {*} [result]
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
this.reply = function(error, result, transport)
|
||||
{
|
||||
// Fix optional parameters
|
||||
if(error instanceof Function || error && error.send instanceof Function)
|
||||
{
|
||||
if(result != undefined)
|
||||
throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
transport = error;
|
||||
result = null;
|
||||
error = undefined;
|
||||
}
|
||||
|
||||
else if(result instanceof Function
|
||||
|| result && result.send instanceof Function)
|
||||
{
|
||||
if(transport != undefined)
|
||||
throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
transport = result;
|
||||
result = null;
|
||||
};
|
||||
|
||||
transport = unifyTransport(transport);
|
||||
|
||||
// Duplicated request, remove old response timeout
|
||||
if(response)
|
||||
clearTimeout(response.timeout);
|
||||
|
||||
if(from != undefined)
|
||||
{
|
||||
if(error)
|
||||
error.dest = from;
|
||||
|
||||
if(result)
|
||||
result.dest = from;
|
||||
};
|
||||
|
||||
var message;
|
||||
|
||||
// New request or overriden one, create new response with provided data
|
||||
if(error || result != undefined)
|
||||
{
|
||||
if(self.peerID != undefined)
|
||||
{
|
||||
if(error)
|
||||
error.from = self.peerID;
|
||||
else
|
||||
result.from = self.peerID;
|
||||
}
|
||||
|
||||
// Protocol indicates that responses has own request methods
|
||||
if(responseMethod)
|
||||
{
|
||||
if(responseMethod.error == undefined && error)
|
||||
message =
|
||||
{
|
||||
error: error
|
||||
};
|
||||
|
||||
else
|
||||
{
|
||||
var method = error
|
||||
? responseMethod.error
|
||||
: responseMethod.response;
|
||||
|
||||
message =
|
||||
{
|
||||
method: method,
|
||||
params: error || result
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
message =
|
||||
{
|
||||
error: error,
|
||||
result: result
|
||||
};
|
||||
|
||||
message = packer.pack(message, id);
|
||||
}
|
||||
|
||||
// Duplicate & not-overriden request, re-send old response
|
||||
else if(response)
|
||||
message = response.message;
|
||||
|
||||
// New empty reply, response null value
|
||||
else
|
||||
message = packer.pack({result: null}, id);
|
||||
|
||||
// Store the response to prevent to process a duplicated request later
|
||||
storeResponse(message, id, from);
|
||||
|
||||
// Return the stored response so it can be directly send back
|
||||
transport = transport || this.getTransport() || self.getTransport();
|
||||
|
||||
if(transport)
|
||||
return transport.send(message);
|
||||
|
||||
return message;
|
||||
}
|
||||
};
|
||||
inherits(RpcRequest, RpcNotification);
|
||||
|
||||
|
||||
function cancel(message)
|
||||
{
|
||||
var key = message2Key[message];
|
||||
if(!key) return;
|
||||
|
||||
delete message2Key[message];
|
||||
|
||||
var request = requests.pop(key.id, key.dest);
|
||||
if(!request) return;
|
||||
|
||||
clearTimeout(request.timeout);
|
||||
|
||||
// Start duplicated responses timeout
|
||||
storeProcessedResponse(key.id, key.dest);
|
||||
};
|
||||
|
||||
/**
|
||||
* Allow to cancel a request and don't wait for a response
|
||||
*
|
||||
* If `message` is not given, cancel all the request
|
||||
*/
|
||||
this.cancel = function(message)
|
||||
{
|
||||
if(message) return cancel(message);
|
||||
|
||||
for(var message in message2Key)
|
||||
cancel(message);
|
||||
};
|
||||
|
||||
|
||||
this.close = function()
|
||||
{
|
||||
// Prevent to receive new messages
|
||||
var transport = this.getTransport();
|
||||
if(transport && transport.close)
|
||||
transport.close(4003, "Cancel request");
|
||||
|
||||
// Request & processed responses
|
||||
this.cancel();
|
||||
|
||||
processedResponses.forEach(clearTimeout);
|
||||
|
||||
// Responses
|
||||
responses.forEach(function(response)
|
||||
{
|
||||
clearTimeout(response.timeout);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generates and encode a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {String} method -method of the notification
|
||||
* @param params - parameters of the notification
|
||||
* @param [dest] - destination of the notification
|
||||
* @param {object} [transport] - transport where to send the message
|
||||
* @param [callback] - function called when a response to this request is
|
||||
* received. If not defined, a notification will be send instead
|
||||
*
|
||||
* @returns {string} A raw JsonRPC 2.0 request or notification string
|
||||
*/
|
||||
this.encode = function(method, params, dest, transport, callback)
|
||||
{
|
||||
// Fix optional parameters
|
||||
if(params instanceof Function)
|
||||
{
|
||||
if(dest != undefined)
|
||||
throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
callback = params;
|
||||
transport = undefined;
|
||||
dest = undefined;
|
||||
params = undefined;
|
||||
}
|
||||
|
||||
else if(dest instanceof Function)
|
||||
{
|
||||
if(transport != undefined)
|
||||
throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
callback = dest;
|
||||
transport = undefined;
|
||||
dest = undefined;
|
||||
}
|
||||
|
||||
else if(transport instanceof Function)
|
||||
{
|
||||
if(callback != undefined)
|
||||
throw new SyntaxError("There can't be parameters after callback");
|
||||
|
||||
callback = transport;
|
||||
transport = undefined;
|
||||
};
|
||||
|
||||
if(self.peerID != undefined)
|
||||
{
|
||||
params = params || {};
|
||||
|
||||
params.from = self.peerID;
|
||||
};
|
||||
|
||||
if(dest != undefined)
|
||||
{
|
||||
params = params || {};
|
||||
|
||||
params.dest = dest;
|
||||
};
|
||||
|
||||
// Encode message
|
||||
var message =
|
||||
{
|
||||
method: method,
|
||||
params: params
|
||||
};
|
||||
|
||||
if(callback)
|
||||
{
|
||||
var id = requestID++;
|
||||
var retried = 0;
|
||||
|
||||
message = packer.pack(message, id);
|
||||
|
||||
function dispatchCallback(error, result)
|
||||
{
|
||||
self.cancel(message);
|
||||
|
||||
callback(error, result);
|
||||
};
|
||||
|
||||
var request =
|
||||
{
|
||||
message: message,
|
||||
callback: dispatchCallback,
|
||||
responseMethods: responseMethods[method] || {}
|
||||
};
|
||||
|
||||
var encode_transport = unifyTransport(transport);
|
||||
|
||||
function sendRequest(transport)
|
||||
{
|
||||
var rt = (method === 'ping' ? ping_request_timeout : request_timeout);
|
||||
request.timeout = setTimeout(timeout, rt*Math.pow(2, retried++));
|
||||
message2Key[message] = {id: id, dest: dest};
|
||||
requests.set(request, id, dest);
|
||||
|
||||
transport = transport || encode_transport || self.getTransport();
|
||||
if(transport)
|
||||
return transport.send(message);
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
function retry(transport)
|
||||
{
|
||||
transport = unifyTransport(transport);
|
||||
|
||||
console.warn(retried+' retry for request message:',message);
|
||||
|
||||
var timeout = processedResponses.pop(id, dest);
|
||||
clearTimeout(timeout);
|
||||
|
||||
return sendRequest(transport);
|
||||
};
|
||||
|
||||
function timeout()
|
||||
{
|
||||
if(retried < max_retries)
|
||||
return retry(transport);
|
||||
|
||||
var error = new Error('Request has timed out');
|
||||
error.request = message;
|
||||
|
||||
error.retry = retry;
|
||||
|
||||
dispatchCallback(error)
|
||||
};
|
||||
|
||||
return sendRequest(transport);
|
||||
};
|
||||
|
||||
// Return the packed message
|
||||
message = packer.pack(message);
|
||||
|
||||
transport = transport || this.getTransport();
|
||||
if(transport)
|
||||
return transport.send(message);
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode and process a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {string} message - string with the content of the message
|
||||
*
|
||||
* @returns {RpcNotification|RpcRequest|undefined} - the representation of the
|
||||
* notification or the request. If a response was processed, it will return
|
||||
* `undefined` to notify that it was processed
|
||||
*
|
||||
* @throws {TypeError} - Message is not defined
|
||||
*/
|
||||
this.decode = function(message, transport)
|
||||
{
|
||||
if(!message)
|
||||
throw new TypeError("Message is not defined");
|
||||
|
||||
try
|
||||
{
|
||||
message = packer.unpack(message);
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
// Ignore invalid messages
|
||||
return console.debug(e, message);
|
||||
};
|
||||
|
||||
var id = message.id;
|
||||
var ack = message.ack;
|
||||
var method = message.method;
|
||||
var params = message.params || {};
|
||||
|
||||
var from = params.from;
|
||||
var dest = params.dest;
|
||||
|
||||
// Ignore messages send by us
|
||||
if(self.peerID != undefined && from == self.peerID) return;
|
||||
|
||||
// Notification
|
||||
if(id == undefined && ack == undefined)
|
||||
{
|
||||
var notification = new RpcNotification(method, params);
|
||||
|
||||
if(self.emit('request', notification)) return;
|
||||
return notification;
|
||||
};
|
||||
|
||||
|
||||
function processRequest()
|
||||
{
|
||||
// If we have a transport and it's a duplicated request, reply inmediatly
|
||||
transport = unifyTransport(transport) || self.getTransport();
|
||||
if(transport)
|
||||
{
|
||||
var response = responses.get(id, from);
|
||||
if(response)
|
||||
return transport.send(response.message);
|
||||
};
|
||||
|
||||
var idAck = (id != undefined) ? id : ack;
|
||||
var request = new RpcRequest(method, params, idAck, from, transport);
|
||||
|
||||
if(self.emit('request', request)) return;
|
||||
return request;
|
||||
};
|
||||
|
||||
function processResponse(request, error, result)
|
||||
{
|
||||
request.callback(error, result);
|
||||
};
|
||||
|
||||
function duplicatedResponse(timeout)
|
||||
{
|
||||
console.warn("Response already processed", message);
|
||||
|
||||
// Update duplicated responses timeout
|
||||
clearTimeout(timeout);
|
||||
storeProcessedResponse(ack, from);
|
||||
};
|
||||
|
||||
|
||||
// Request, or response with own method
|
||||
if(method)
|
||||
{
|
||||
// Check if it's a response with own method
|
||||
if(dest == undefined || dest == self.peerID)
|
||||
{
|
||||
var request = requests.get(ack, from);
|
||||
if(request)
|
||||
{
|
||||
var responseMethods = request.responseMethods;
|
||||
|
||||
if(method == responseMethods.error)
|
||||
return processResponse(request, params);
|
||||
|
||||
if(method == responseMethods.response)
|
||||
return processResponse(request, null, params);
|
||||
|
||||
return processRequest();
|
||||
}
|
||||
|
||||
var processed = processedResponses.get(ack, from);
|
||||
if(processed)
|
||||
return duplicatedResponse(processed);
|
||||
}
|
||||
|
||||
// Request
|
||||
return processRequest();
|
||||
};
|
||||
|
||||
var error = message.error;
|
||||
var result = message.result;
|
||||
|
||||
// Ignore responses not send to us
|
||||
if(error && error.dest && error.dest != self.peerID) return;
|
||||
if(result && result.dest && result.dest != self.peerID) return;
|
||||
|
||||
// Response
|
||||
var request = requests.get(ack, from);
|
||||
if(!request)
|
||||
{
|
||||
var processed = processedResponses.get(ack, from);
|
||||
if(processed)
|
||||
return duplicatedResponse(processed);
|
||||
|
||||
return console.warn("No callback was defined for this message", message);
|
||||
};
|
||||
|
||||
// Process response
|
||||
processResponse(request, error, result);
|
||||
};
|
||||
};
|
||||
inherits(RpcBuilder, EventEmitter);
|
||||
|
||||
|
||||
RpcBuilder.RpcNotification = RpcNotification;
|
||||
|
||||
|
||||
module.exports = RpcBuilder;
|
||||
|
||||
var clients = require('./clients');
|
||||
var transports = require('./clients/transports');
|
||||
|
||||
RpcBuilder.clients = clients;
|
||||
RpcBuilder.clients.transports = transports;
|
||||
RpcBuilder.packers = packers;
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/**
|
||||
* JsonRPC 2.0 packer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Pack a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {Object} message - object to be packaged. It requires to have all the
|
||||
* fields needed by the JsonRPC 2.0 message that it's going to be generated
|
||||
*
|
||||
* @return {String} - the stringified JsonRPC 2.0 message
|
||||
*/
|
||||
function pack(message, id)
|
||||
{
|
||||
var result =
|
||||
{
|
||||
jsonrpc: "2.0"
|
||||
};
|
||||
|
||||
// Request
|
||||
if(message.method)
|
||||
{
|
||||
result.method = message.method;
|
||||
|
||||
if(message.params)
|
||||
result.params = message.params;
|
||||
|
||||
// Request is a notification
|
||||
if(id != undefined)
|
||||
result.id = id;
|
||||
}
|
||||
|
||||
// Response
|
||||
else if(id != undefined)
|
||||
{
|
||||
if(message.error)
|
||||
{
|
||||
if(message.result !== undefined)
|
||||
throw new TypeError("Both result and error are defined");
|
||||
|
||||
result.error = message.error;
|
||||
}
|
||||
else if(message.result !== undefined)
|
||||
result.result = message.result;
|
||||
else
|
||||
throw new TypeError("No result or error is defined");
|
||||
|
||||
result.id = id;
|
||||
};
|
||||
|
||||
return JSON.stringify(result);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unpack a JsonRPC 2.0 message
|
||||
*
|
||||
* @param {String} message - string with the content of the JsonRPC 2.0 message
|
||||
*
|
||||
* @throws {TypeError} - Invalid JsonRPC version
|
||||
*
|
||||
* @return {Object} - object filled with the JsonRPC 2.0 message content
|
||||
*/
|
||||
function unpack(message)
|
||||
{
|
||||
var result = message;
|
||||
|
||||
if(typeof message === 'string' || message instanceof String) {
|
||||
result = JSON.parse(message);
|
||||
}
|
||||
|
||||
// Check if it's a valid message
|
||||
|
||||
var version = result.jsonrpc;
|
||||
if(version !== '2.0')
|
||||
throw new TypeError("Invalid JsonRPC version '" + version + "': " + message);
|
||||
|
||||
// Response
|
||||
if(result.method == undefined)
|
||||
{
|
||||
if(result.id == undefined)
|
||||
throw new TypeError("Invalid message: "+message);
|
||||
|
||||
var result_defined = result.result !== undefined;
|
||||
var error_defined = result.error !== undefined;
|
||||
|
||||
// Check only result or error is defined, not both or none
|
||||
if(result_defined && error_defined)
|
||||
throw new TypeError("Both result and error are defined: "+message);
|
||||
|
||||
if(!result_defined && !error_defined)
|
||||
throw new TypeError("No result or error is defined: "+message);
|
||||
|
||||
result.ack = result.id;
|
||||
delete result.id;
|
||||
}
|
||||
|
||||
// Return unpacked message
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
exports.pack = pack;
|
||||
exports.unpack = unpack;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
function pack(message)
|
||||
{
|
||||
throw new TypeError("Not yet implemented");
|
||||
};
|
||||
|
||||
function unpack(message)
|
||||
{
|
||||
throw new TypeError("Not yet implemented");
|
||||
};
|
||||
|
||||
|
||||
exports.pack = pack;
|
||||
exports.unpack = unpack;
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
var JsonRPC = require('./JsonRPC');
|
||||
var XmlRPC = require('./XmlRPC');
|
||||
|
||||
|
||||
exports.JsonRPC = JsonRPC;
|
||||
exports.XmlRPC = XmlRPC;
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
export class OpenViduLogger {
|
||||
|
||||
private static instance: OpenViduLogger;
|
||||
private logger: Console = window.console;
|
||||
private LOG_FNS = [this.logger.log, this.logger.debug, this.logger.info, this.logger.warn, this.logger.error];
|
||||
private isProdMode = false;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): OpenViduLogger {
|
||||
if(!OpenViduLogger.instance){
|
||||
OpenViduLogger.instance = new OpenViduLogger();
|
||||
}
|
||||
return OpenViduLogger.instance;
|
||||
}
|
||||
|
||||
log(...args: any[]){
|
||||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[0].apply(this.logger, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
debug(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[1].apply(this.logger, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
info(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[2].apply(this.logger, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
warn(...args: any[]) {
|
||||
if (!this.isProdMode) {
|
||||
this.LOG_FNS[3].apply(this.logger, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
error(...args: any[]) {
|
||||
this.LOG_FNS[4].apply(this.logger, arguments);
|
||||
}
|
||||
|
||||
enableProdMode(){
|
||||
this.isProdMode = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
// Last time updated on June 08, 2018
|
||||
|
||||
// Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js
|
||||
|
||||
// Muaz Khan - www.MuazKhan.com
|
||||
// MIT License - www.WebRTC-Experiment.com/licence
|
||||
// Documentation - https://github.com/muaz-khan/getScreenId.
|
||||
|
||||
// ______________
|
||||
// getScreenId.js
|
||||
|
||||
/*
|
||||
getScreenId(function (error, sourceId, screen_constraints) {
|
||||
// error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome'
|
||||
// sourceId == null || 'string' || 'firefox'
|
||||
|
||||
if(microsoftEdge) {
|
||||
navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
|
||||
}
|
||||
else {
|
||||
navigator.mediaDevices.getUserMedia(screen_constraints).then(onSuccess)catch(onFailure);
|
||||
}
|
||||
}, 'pass second parameter only if you want system audio');
|
||||
*/
|
||||
|
||||
window.getScreenId = function (firefoxString, callback, custom_parameter) {
|
||||
if (navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveOrOpenBlob || !!navigator.msSaveBlob)) {
|
||||
// microsoft edge => navigator.getDisplayMedia(screen_constraints).then(onSuccess, onFailure);
|
||||
callback({
|
||||
video: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// for Firefox:
|
||||
// sourceId == 'firefox'
|
||||
// screen_constraints = {...}
|
||||
if (!!navigator.mozGetUserMedia) {
|
||||
callback(null, 'firefox', {
|
||||
video: {
|
||||
mozMediaSource: firefoxString,
|
||||
mediaSource: firefoxString
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('message', onIFrameCallback);
|
||||
|
||||
function onIFrameCallback(event) {
|
||||
if (!event.data) return;
|
||||
|
||||
if (event.data.chromeMediaSourceId) {
|
||||
if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
|
||||
callback('permission-denied');
|
||||
} else {
|
||||
callback(null, event.data.chromeMediaSourceId, getScreenConstraints(null, event.data.chromeMediaSourceId, event.data.canRequestAudioTrack));
|
||||
}
|
||||
|
||||
// this event listener is no more needed
|
||||
window.removeEventListener('message', onIFrameCallback);
|
||||
}
|
||||
|
||||
if (event.data.chromeExtensionStatus) {
|
||||
callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus));
|
||||
|
||||
// this event listener is no more needed
|
||||
window.removeEventListener('message', onIFrameCallback);
|
||||
}
|
||||
}
|
||||
|
||||
if (!custom_parameter) {
|
||||
setTimeout(postGetSourceIdMessage, 100);
|
||||
}
|
||||
else {
|
||||
setTimeout(function () {
|
||||
postGetSourceIdMessage(custom_parameter);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
function getScreenConstraints(error, sourceId, canRequestAudioTrack) {
|
||||
var screen_constraints = {
|
||||
audio: false,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: error ? 'screen' : 'desktop',
|
||||
maxWidth: window.screen.width > 1920 ? window.screen.width : 1920,
|
||||
maxHeight: window.screen.height > 1080 ? window.screen.height : 1080
|
||||
},
|
||||
optional: []
|
||||
}
|
||||
};
|
||||
|
||||
if (!!canRequestAudioTrack) {
|
||||
screen_constraints.audio = {
|
||||
mandatory: {
|
||||
chromeMediaSource: error ? 'screen' : 'desktop',
|
||||
// echoCancellation: true
|
||||
},
|
||||
optional: []
|
||||
};
|
||||
}
|
||||
|
||||
if (sourceId) {
|
||||
screen_constraints.video.mandatory.chromeMediaSourceId = sourceId;
|
||||
|
||||
if (screen_constraints.audio && screen_constraints.audio.mandatory) {
|
||||
screen_constraints.audio.mandatory.chromeMediaSourceId = sourceId;
|
||||
}
|
||||
}
|
||||
|
||||
return screen_constraints;
|
||||
}
|
||||
|
||||
function postGetSourceIdMessage(custom_parameter) {
|
||||
if (!iframe) {
|
||||
loadIFrame(function () {
|
||||
postGetSourceIdMessage(custom_parameter);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iframe.isLoaded) {
|
||||
setTimeout(function () {
|
||||
postGetSourceIdMessage(custom_parameter);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!custom_parameter) {
|
||||
iframe.contentWindow.postMessage({
|
||||
captureSourceId: true
|
||||
}, '*');
|
||||
}
|
||||
else if (!!custom_parameter.forEach) {
|
||||
iframe.contentWindow.postMessage({
|
||||
captureCustomSourceId: custom_parameter
|
||||
}, '*');
|
||||
}
|
||||
else {
|
||||
iframe.contentWindow.postMessage({
|
||||
captureSourceIdWithAudio: true
|
||||
}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
var iframe;
|
||||
|
||||
// this function is used in RTCMultiConnection v3
|
||||
window.getScreenConstraints = function (callback) {
|
||||
loadIFrame(function () {
|
||||
getScreenId(function (error, sourceId, screen_constraints) {
|
||||
if (!screen_constraints) {
|
||||
screen_constraints = {
|
||||
video: true
|
||||
};
|
||||
}
|
||||
|
||||
callback(error, screen_constraints.video);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function loadIFrame(loadCallback) {
|
||||
if (iframe) {
|
||||
loadCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
iframe = document.createElement('iframe');
|
||||
iframe.onload = function () {
|
||||
iframe.isLoaded = true;
|
||||
loadCallback();
|
||||
};
|
||||
iframe.src = 'https://openvidu.github.io/openvidu-screen-sharing-chrome-extension/';
|
||||
iframe.style.display = 'none';
|
||||
(document.body || document.documentElement).appendChild(iframe);
|
||||
}
|
||||
|
||||
window.getChromeExtensionStatus = function (callback) {
|
||||
// for Firefox:
|
||||
if (!!navigator.mozGetUserMedia) {
|
||||
callback('installed-enabled');
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('message', onIFrameCallback);
|
||||
|
||||
function onIFrameCallback(event) {
|
||||
if (!event.data) return;
|
||||
|
||||
if (event.data.chromeExtensionStatus) {
|
||||
callback(event.data.chromeExtensionStatus);
|
||||
|
||||
// this event listener is no more needed
|
||||
window.removeEventListener('message', onIFrameCallback);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(postGetChromeExtensionStatusMessage, 100);
|
||||
};
|
||||
|
||||
function postGetChromeExtensionStatusMessage() {
|
||||
if (!iframe) {
|
||||
loadIFrame(postGetChromeExtensionStatusMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iframe.isLoaded) {
|
||||
setTimeout(postGetChromeExtensionStatusMessage, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
iframe.contentWindow.postMessage({
|
||||
getChromeExtensionStatus: true
|
||||
}, '*');
|
||||
}
|
||||
|
||||
exports.getScreenId = window.getScreenId;
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
// global variables
|
||||
var chromeMediaSource = 'screen';
|
||||
var sourceId;
|
||||
var screenCallback;
|
||||
|
||||
if(typeof window !== 'undefined' && typeof navigator !== 'undefined' && typeof navigator.userAgent !== 'undefined'){
|
||||
var isFirefox = typeof window.InstallTrigger !== 'undefined';
|
||||
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
|
||||
var isChrome = !!window.chrome && !isOpera;
|
||||
|
||||
window.addEventListener('message', function (event) {
|
||||
if (event.origin != window.location.origin) {
|
||||
return;
|
||||
}
|
||||
onMessageCallback(event.data);
|
||||
});
|
||||
}
|
||||
|
||||
// and the function that handles received messages
|
||||
function onMessageCallback(data) {
|
||||
// "cancel" button is clicked
|
||||
if (data == 'PermissionDeniedError') {
|
||||
if (screenCallback)
|
||||
return screenCallback('PermissionDeniedError');
|
||||
else
|
||||
throw new Error('PermissionDeniedError');
|
||||
}
|
||||
// extension notified his presence
|
||||
if (data == 'rtcmulticonnection-extension-loaded') {
|
||||
chromeMediaSource = 'desktop';
|
||||
}
|
||||
// extension shared temp sourceId
|
||||
if (data.sourceId && screenCallback) {
|
||||
screenCallback(sourceId = data.sourceId, data.canRequestAudioTrack === true);
|
||||
}
|
||||
}
|
||||
|
||||
// this method can be used to check if chrome extension is installed & enabled.
|
||||
function isChromeExtensionAvailable(callback) {
|
||||
if (!callback) return;
|
||||
if (chromeMediaSource == 'desktop') return callback(true);
|
||||
|
||||
// ask extension if it is available
|
||||
window.postMessage('are-you-there', '*');
|
||||
setTimeout(function () {
|
||||
if (chromeMediaSource == 'screen') {
|
||||
callback(false);
|
||||
} else callback(true);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// this function can be used to get "source-id" from the extension
|
||||
function getSourceId(callback) {
|
||||
if (!callback)
|
||||
throw '"callback" parameter is mandatory.';
|
||||
if (sourceId)
|
||||
return callback(sourceId);
|
||||
screenCallback = callback;
|
||||
window.postMessage('get-sourceId', '*');
|
||||
}
|
||||
|
||||
// this function can be used to get "source-id" from the extension
|
||||
function getCustomSourceId(arr, callback) {
|
||||
if (!arr || !arr.forEach) throw '"arr" parameter is mandatory and it must be an array.';
|
||||
if (!callback) throw '"callback" parameter is mandatory.';
|
||||
|
||||
if (sourceId) return callback(sourceId);
|
||||
|
||||
screenCallback = callback;
|
||||
window.postMessage({
|
||||
'get-custom-sourceId': arr
|
||||
}, '*');
|
||||
}
|
||||
|
||||
// this function can be used to get "source-id" from the extension
|
||||
function getSourceIdWithAudio(callback) {
|
||||
if (!callback) throw '"callback" parameter is mandatory.';
|
||||
if (sourceId) return callback(sourceId);
|
||||
|
||||
screenCallback = callback;
|
||||
window.postMessage('audio-plus-tab', '*');
|
||||
}
|
||||
|
||||
function getChromeExtensionStatus(extensionid, callback) {
|
||||
if (isFirefox)
|
||||
return callback('not-chrome');
|
||||
if (arguments.length != 2) {
|
||||
callback = extensionid;
|
||||
extensionid = 'lfcgfepafnobdloecchnfaclibenjold'; // default extension-id
|
||||
}
|
||||
var image = document.createElement('img');
|
||||
image.src = 'chrome-extension://' + extensionid + '/icon.png';
|
||||
image.onload = function () {
|
||||
chromeMediaSource = 'screen';
|
||||
window.postMessage('are-you-there', '*');
|
||||
setTimeout(function () {
|
||||
if (chromeMediaSource == 'screen') {
|
||||
callback('installed-disabled');
|
||||
} else
|
||||
callback('installed-enabled');
|
||||
}, 2000);
|
||||
};
|
||||
image.onerror = function () {
|
||||
callback('not-installed');
|
||||
};
|
||||
}
|
||||
|
||||
function getScreenConstraintsWithAudio(callback) {
|
||||
getScreenConstraints(callback, true);
|
||||
}
|
||||
|
||||
// this function explains how to use above methods/objects
|
||||
function getScreenConstraints(callback, captureSourceIdWithAudio) {
|
||||
sourceId = '';
|
||||
var firefoxScreenConstraints = {
|
||||
mozMediaSource: 'window',
|
||||
mediaSource: 'window'
|
||||
};
|
||||
if (isFirefox)
|
||||
return callback(null, firefoxScreenConstraints);
|
||||
// this statement defines getUserMedia constraints
|
||||
// that will be used to capture content of screen
|
||||
var screen_constraints = {
|
||||
mandatory: {
|
||||
chromeMediaSource: chromeMediaSource,
|
||||
maxWidth: screen.width > 1920 ? screen.width : 1920,
|
||||
maxHeight: screen.height > 1080 ? screen.height : 1080
|
||||
},
|
||||
optional: []
|
||||
};
|
||||
// this statement verifies chrome extension availability
|
||||
// if installed and available then it will invoke extension API
|
||||
// otherwise it will fallback to command-line based screen capturing API
|
||||
if (chromeMediaSource == 'desktop' && !sourceId) {
|
||||
if (captureSourceIdWithAudio) {
|
||||
getSourceIdWithAudio(function (sourceId, canRequestAudioTrack) {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
|
||||
if (canRequestAudioTrack) {
|
||||
screen_constraints.canRequestAudioTrack = true;
|
||||
}
|
||||
callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints);
|
||||
});
|
||||
}
|
||||
else {
|
||||
getSourceId(function (sourceId) {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
|
||||
if (chromeMediaSource == 'desktop') {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
}
|
||||
|
||||
// now invoking native getUserMedia API
|
||||
callback(null, screen_constraints);
|
||||
}
|
||||
|
||||
exports.getScreenConstraints = getScreenConstraints;
|
||||
exports.getScreenConstraintsWithAudio = getScreenConstraintsWithAudio;
|
||||
exports.isChromeExtensionAvailable = isChromeExtensionAvailable;
|
||||
exports.getChromeExtensionStatus = getChromeExtensionStatus;
|
||||
exports.getSourceId = getSourceId;
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
import platform = require("platform");
|
||||
|
||||
export class PlatformUtils {
|
||||
protected static instance: PlatformUtils;
|
||||
constructor() {}
|
||||
|
||||
static getInstance(): PlatformUtils {
|
||||
if (!this.instance) {
|
||||
this.instance = new PlatformUtils();
|
||||
}
|
||||
return PlatformUtils.instance;
|
||||
}
|
||||
|
||||
public isChromeBrowser(): boolean {
|
||||
return platform.name === "Chrome";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isSafariBrowser(): boolean {
|
||||
return platform.name === "Safari";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isChromeMobileBrowser(): boolean {
|
||||
return platform.name === "Chrome Mobile";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isFirefoxBrowser(): boolean {
|
||||
return platform.name === "Firefox";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isFirefoxMobileBrowser(): boolean {
|
||||
return platform.name === "Firefox Mobile";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isOperaBrowser(): boolean {
|
||||
return platform.name === "Opera";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isOperaMobileBrowser(): boolean {
|
||||
return platform.name === "Opera Mobile";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isEdgeBrowser(): boolean {
|
||||
const version = platform?.version ? parseFloat(platform.version) : -1;
|
||||
return platform.name === "Microsoft Edge" && version >= 80;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isEdgeMobileBrowser(): boolean {
|
||||
const version = platform?.version ? parseFloat(platform.version) : -1;
|
||||
return platform.name === "Microsoft Edge" && platform.os?.family === 'Android' && version > 45;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isAndroidBrowser(): boolean {
|
||||
return platform.name === "Android Browser";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isElectron(): boolean {
|
||||
return platform.name === "Electron";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isSamsungBrowser(): boolean {
|
||||
return (
|
||||
platform.name === "Samsung Internet Mobile" ||
|
||||
platform.name === "Samsung Internet"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIPhoneOrIPad(): boolean {
|
||||
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
|
||||
|
||||
const isTouchable = "ontouchend" in document;
|
||||
const isIPad = /\b(\w*Macintosh\w*)\b/.test(userAgent) && isTouchable;
|
||||
const isIPhone =
|
||||
/\b(\w*iPhone\w*)\b/.test(userAgent) &&
|
||||
/\b(\w*Mobile\w*)\b/.test(userAgent) &&
|
||||
isTouchable;
|
||||
|
||||
return isIPad || isIPhone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIOSWithSafari(): boolean {
|
||||
const userAgent = !!platform.ua ? platform.ua : navigator.userAgent;
|
||||
return (
|
||||
/\b(\w*Apple\w*)\b/.test(navigator.vendor) &&
|
||||
/\b(\w*Safari\w*)\b/.test(userAgent) &&
|
||||
!/\b(\w*CriOS\w*)\b/.test(userAgent) &&
|
||||
!/\b(\w*FxiOS\w*)\b/.test(userAgent)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIonicIos(): boolean {
|
||||
return this.isIPhoneOrIPad() && platform.ua!!.indexOf("Safari") === -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isIonicAndroid(): boolean {
|
||||
return (
|
||||
platform.os!!.family === "Android" && platform.name == "Android Browser"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isMobileDevice(): boolean {
|
||||
return platform.os!!.family === "iOS" || platform.os!!.family === "Android";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public isReactNative(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public canScreenShare(): boolean {
|
||||
const version = platform?.version ? parseFloat(platform.version) : -1;
|
||||
// Reject mobile devices
|
||||
if (this.isMobileDevice()) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
this.isChromeBrowser() ||
|
||||
this.isFirefoxBrowser() ||
|
||||
this.isOperaBrowser() ||
|
||||
this.isElectron() ||
|
||||
this.isEdgeBrowser() ||
|
||||
(this.isSafariBrowser() && version >= 13)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getName(): string {
|
||||
return platform.name || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getVersion(): string {
|
||||
return platform.version || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getFamily(): string {
|
||||
return platform.os!!.family || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
public getDescription(): string {
|
||||
return platform.description || "";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,329 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import freeice = require('freeice');
|
||||
import uuid = require('uuid');
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../Utils/Platform';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
|
||||
export interface WebRtcPeerConfiguration {
|
||||
mediaConstraints: {
|
||||
audio: boolean,
|
||||
video: boolean
|
||||
};
|
||||
simulcast: boolean;
|
||||
onicecandidate: (event) => void;
|
||||
iceServers: RTCIceServer[] | undefined;
|
||||
mediaStream?: MediaStream;
|
||||
mode?: 'sendonly' | 'recvonly' | 'sendrecv';
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export class WebRtcPeer {
|
||||
|
||||
pc: RTCPeerConnection;
|
||||
id: string;
|
||||
remoteCandidatesQueue: RTCIceCandidate[] = [];
|
||||
localCandidatesQueue: RTCIceCandidate[] = [];
|
||||
|
||||
iceCandidateList: RTCIceCandidate[] = [];
|
||||
|
||||
private candidategatheringdone = false;
|
||||
|
||||
constructor(protected configuration: WebRtcPeerConfiguration) {
|
||||
platform = PlatformUtils.getInstance();
|
||||
this.configuration.iceServers = (!!this.configuration.iceServers && this.configuration.iceServers.length > 0) ? this.configuration.iceServers : freeice();
|
||||
|
||||
this.pc = new RTCPeerConnection({ iceServers: this.configuration.iceServers });
|
||||
this.id = !!configuration.id ? configuration.id : this.generateUniqueId();
|
||||
|
||||
this.pc.onicecandidate = event => {
|
||||
if (!!event.candidate) {
|
||||
const candidate: RTCIceCandidate = event.candidate;
|
||||
if (candidate) {
|
||||
this.localCandidatesQueue.push(<RTCIceCandidate>{ candidate: candidate.candidate });
|
||||
this.candidategatheringdone = false;
|
||||
this.configuration.onicecandidate(event.candidate);
|
||||
} else if (!this.candidategatheringdone) {
|
||||
this.candidategatheringdone = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.pc.onsignalingstatechange = () => {
|
||||
if (this.pc.signalingState === 'stable') {
|
||||
while (this.iceCandidateList.length > 0) {
|
||||
let candidate = this.iceCandidateList.shift();
|
||||
this.pc.addIceCandidate(<RTCIceCandidate>candidate);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function creates the RTCPeerConnection object taking into account the
|
||||
* properties received in the constructor. It starts the SDP negotiation
|
||||
* process: generates the SDP offer and invokes the onsdpoffer callback. This
|
||||
* callback is expected to send the SDP offer, in order to obtain an SDP
|
||||
* answer from another peer.
|
||||
*/
|
||||
start(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
reject('The peer connection object is in "closed" state. This is most likely due to an invocation of the dispose method before accepting in the dialogue');
|
||||
}
|
||||
if (!!this.configuration.mediaStream) {
|
||||
for (const track of this.configuration.mediaStream.getTracks()) {
|
||||
this.pc.addTrack(track, this.configuration.mediaStream);
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method frees the resources used by WebRtcPeer
|
||||
*/
|
||||
dispose() {
|
||||
logger.debug('Disposing WebRtcPeer');
|
||||
if (this.pc) {
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
return;
|
||||
}
|
||||
this.pc.close();
|
||||
this.remoteCandidatesQueue = [];
|
||||
this.localCandidatesQueue = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that creates an offer, sets it as local description and returns the offer param
|
||||
* to send to OpenVidu Server (will be the remote description of other peer)
|
||||
*/
|
||||
generateOffer(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let offerAudio, offerVideo = true;
|
||||
|
||||
// Constraints must have both blocks
|
||||
if (!!this.configuration.mediaConstraints) {
|
||||
offerAudio = (typeof this.configuration.mediaConstraints.audio === 'boolean') ?
|
||||
this.configuration.mediaConstraints.audio : true;
|
||||
offerVideo = (typeof this.configuration.mediaConstraints.video === 'boolean') ?
|
||||
this.configuration.mediaConstraints.video : true;
|
||||
}
|
||||
|
||||
const constraints: RTCOfferOptions = {
|
||||
offerToReceiveAudio: (this.configuration.mode !== 'sendonly' && offerAudio),
|
||||
offerToReceiveVideo: (this.configuration.mode !== 'sendonly' && offerVideo)
|
||||
};
|
||||
|
||||
logger.debug('RTCPeerConnection constraints: ' + JSON.stringify(constraints));
|
||||
|
||||
if (platform.isSafariBrowser() && !platform.isIonicIos()) {
|
||||
// Safari (excluding Ionic), at least on iOS just seems to support unified plan, whereas in other browsers is not yet ready and considered experimental
|
||||
if (offerAudio) {
|
||||
this.pc.addTransceiver('audio', {
|
||||
direction: this.configuration.mode,
|
||||
});
|
||||
}
|
||||
|
||||
if (offerVideo) {
|
||||
this.pc.addTransceiver('video', {
|
||||
direction: this.configuration.mode,
|
||||
});
|
||||
}
|
||||
|
||||
this.pc
|
||||
.createOffer()
|
||||
.then(offer => {
|
||||
logger.debug('Created SDP offer');
|
||||
return this.pc.setLocalDescription(offer);
|
||||
})
|
||||
.then(() => {
|
||||
const localDescription = this.pc.localDescription;
|
||||
|
||||
if (!!localDescription) {
|
||||
logger.debug('Local description set', localDescription.sdp);
|
||||
resolve(localDescription.sdp);
|
||||
} else {
|
||||
reject('Local description is not defined');
|
||||
}
|
||||
})
|
||||
.catch(error => reject(error));
|
||||
|
||||
} else {
|
||||
|
||||
// Rest of platforms
|
||||
this.pc.createOffer(constraints).then(offer => {
|
||||
logger.debug('Created SDP offer');
|
||||
return this.pc.setLocalDescription(offer);
|
||||
})
|
||||
.then(() => {
|
||||
const localDescription = this.pc.localDescription;
|
||||
if (!!localDescription) {
|
||||
logger.debug('Local description set', localDescription.sdp);
|
||||
resolve(localDescription.sdp);
|
||||
} else {
|
||||
reject('Local description is not defined');
|
||||
}
|
||||
})
|
||||
.catch(error => reject(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function invoked when a SDP answer is received. Final step in SDP negotiation, the peer
|
||||
* just needs to set the answer as its remote description
|
||||
*/
|
||||
processAnswer(sdpAnswer: string, needsTimeoutOnProcessAnswer: boolean): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const answer: RTCSessionDescriptionInit = {
|
||||
type: 'answer',
|
||||
sdp: sdpAnswer
|
||||
};
|
||||
logger.debug('SDP answer received, setting remote description');
|
||||
|
||||
if (this.pc.signalingState === 'closed') {
|
||||
reject('RTCPeerConnection is closed');
|
||||
}
|
||||
|
||||
this.setRemoteDescription(answer, needsTimeoutOnProcessAnswer, resolve, reject);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
setRemoteDescription(answer: RTCSessionDescriptionInit, needsTimeoutOnProcessAnswer: boolean, resolve: (value?: string | PromiseLike<string> | undefined) => void, reject: (reason?: any) => void) {
|
||||
if (platform.isIonicIos()) {
|
||||
// Ionic iOS platform
|
||||
if (needsTimeoutOnProcessAnswer) {
|
||||
// 400 ms have not elapsed yet since first remote stream triggered Stream#initWebRtcPeerReceive
|
||||
setTimeout(() => {
|
||||
logger.info('setRemoteDescription run after timeout for Ionic iOS device');
|
||||
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
|
||||
}, 250);
|
||||
} else {
|
||||
// 400 ms have elapsed
|
||||
this.pc.setRemoteDescription(new RTCSessionDescription(answer)).then(() => resolve()).catch(error => reject(error));
|
||||
}
|
||||
} else {
|
||||
// Rest of platforms
|
||||
this.pc.setRemoteDescription(answer).then(() => resolve()).catch(error => reject(error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function invoked when an ICE candidate is received
|
||||
*/
|
||||
addIceCandidate(iceCandidate: RTCIceCandidate): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
logger.debug('Remote ICE candidate received', iceCandidate);
|
||||
this.remoteCandidatesQueue.push(iceCandidate);
|
||||
switch (this.pc.signalingState) {
|
||||
case 'closed':
|
||||
reject(new Error('PeerConnection object is closed'));
|
||||
break;
|
||||
case 'stable':
|
||||
if (!!this.pc.remoteDescription) {
|
||||
this.pc.addIceCandidate(iceCandidate).then(() => resolve()).catch(error => reject(error));
|
||||
} else {
|
||||
this.iceCandidateList.push(iceCandidate);
|
||||
resolve();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.iceCandidateList.push(iceCandidate);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addIceConnectionStateChangeListener(otherId: string) {
|
||||
this.pc.oniceconnectionstatechange = () => {
|
||||
const iceConnectionState: RTCIceConnectionState = this.pc.iceConnectionState;
|
||||
switch (iceConnectionState) {
|
||||
case 'disconnected':
|
||||
// Possible network disconnection
|
||||
logger.warn('IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') change to "disconnected". Possible network disconnection');
|
||||
break;
|
||||
case 'failed':
|
||||
logger.error('IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') to "failed"');
|
||||
break;
|
||||
case 'closed':
|
||||
logger.log('IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') change to "closed"');
|
||||
break;
|
||||
case 'new':
|
||||
logger.log('IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') change to "new"');
|
||||
break;
|
||||
case 'checking':
|
||||
logger.log('IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') change to "checking"');
|
||||
break;
|
||||
case 'connected':
|
||||
logger.log('IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') change to "connected"');
|
||||
break;
|
||||
case 'completed':
|
||||
logger.log('IceConnectionState of RTCPeerConnection ' + this.id + ' (' + otherId + ') change to "completed"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
generateUniqueId(): string {
|
||||
return uuid.v4();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export class WebRtcPeerRecvonly extends WebRtcPeer {
|
||||
constructor(configuration: WebRtcPeerConfiguration) {
|
||||
configuration.mode = 'recvonly';
|
||||
super(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
export class WebRtcPeerSendonly extends WebRtcPeer {
|
||||
constructor(configuration: WebRtcPeerConfiguration) {
|
||||
configuration.mode = 'sendonly';
|
||||
super(configuration);
|
||||
}
|
||||
}
|
||||
|
||||
export class WebRtcPeerSendrecv extends WebRtcPeer {
|
||||
constructor(configuration: WebRtcPeerConfiguration) {
|
||||
configuration.mode = 'sendrecv';
|
||||
super(configuration);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,245 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// tslint:disable:no-string-literal
|
||||
|
||||
import { Stream } from '../../OpenVidu/Stream';
|
||||
import { OpenViduLogger } from '../Logger/OpenViduLogger';
|
||||
import { PlatformUtils } from '../Utils/Platform';
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const logger: OpenViduLogger = OpenViduLogger.getInstance();
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
let platform: PlatformUtils;
|
||||
|
||||
interface WebrtcStatsConfig {
|
||||
interval: number,
|
||||
httpEndpoint: string
|
||||
}
|
||||
|
||||
interface JSONStats {
|
||||
'@timestamp': string,
|
||||
participant_id: string,
|
||||
session_id: string,
|
||||
platform: string,
|
||||
platform_description: string,
|
||||
stream: string,
|
||||
webrtc_stats: RTCStatsReport
|
||||
}
|
||||
|
||||
export class WebRtcStats {
|
||||
|
||||
private readonly STATS_ITEM_NAME = 'webrtc-stats-config';
|
||||
|
||||
private webRtcStatsEnabled = false;
|
||||
private webRtcStatsIntervalId: NodeJS.Timer;
|
||||
private statsInterval = 1;
|
||||
private POST_URL: string;
|
||||
|
||||
constructor(private stream: Stream) {
|
||||
platform = PlatformUtils.getInstance();
|
||||
}
|
||||
|
||||
public isEnabled(): boolean {
|
||||
return this.webRtcStatsEnabled;
|
||||
}
|
||||
|
||||
public initWebRtcStats(): void {
|
||||
|
||||
const webrtcObj = localStorage.getItem(this.STATS_ITEM_NAME);
|
||||
|
||||
if (!!webrtcObj) {
|
||||
|
||||
this.webRtcStatsEnabled = true;
|
||||
const webrtcStatsConfig: WebrtcStatsConfig = JSON.parse(webrtcObj);
|
||||
this.POST_URL = webrtcStatsConfig.httpEndpoint;
|
||||
this.statsInterval = webrtcStatsConfig.interval; // Interval in seconds
|
||||
|
||||
// webrtc object found in local storage
|
||||
logger.warn('WebRtc stats enabled for stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId);
|
||||
logger.warn('localStorage item: ' + JSON.stringify(webrtcStatsConfig));
|
||||
|
||||
this.webRtcStatsIntervalId = setInterval(async () => {
|
||||
await this.sendStatsToHttpEndpoint();
|
||||
}, this.statsInterval * 1000);
|
||||
|
||||
}else {
|
||||
logger.debug('WebRtc stats not enabled');
|
||||
}
|
||||
}
|
||||
|
||||
// Used in test-app
|
||||
public getSelectedIceCandidateInfo(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getStats().then(
|
||||
(stats) => {
|
||||
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
|
||||
let localCandidateId, remoteCandidateId, googCandidatePair;
|
||||
const localCandidates = {};
|
||||
const remoteCandidates = {};
|
||||
for (const key in stats) {
|
||||
const stat = stats[key];
|
||||
if (stat.type === 'localcandidate') {
|
||||
localCandidates[stat.id] = stat;
|
||||
} else if (stat.type === 'remotecandidate') {
|
||||
remoteCandidates[stat.id] = stat;
|
||||
} else if (stat.type === 'googCandidatePair' && (stat.googActiveConnection === 'true')) {
|
||||
googCandidatePair = stat;
|
||||
localCandidateId = stat.localCandidateId;
|
||||
remoteCandidateId = stat.remoteCandidateId;
|
||||
}
|
||||
}
|
||||
let finalLocalCandidate = localCandidates[localCandidateId];
|
||||
if (!!finalLocalCandidate) {
|
||||
const candList = this.stream.getLocalIceCandidateList();
|
||||
const cand = candList.filter((c: RTCIceCandidate) => {
|
||||
return (!!c.candidate &&
|
||||
c.candidate.indexOf(finalLocalCandidate.ipAddress) >= 0 &&
|
||||
c.candidate.indexOf(finalLocalCandidate.portNumber) >= 0 &&
|
||||
c.candidate.indexOf(finalLocalCandidate.priority) >= 0);
|
||||
});
|
||||
finalLocalCandidate.raw = !!cand[0] ? cand[0].candidate : 'ERROR: Cannot find local candidate in list of sent ICE candidates';
|
||||
} else {
|
||||
finalLocalCandidate = 'ERROR: No active local ICE candidate. Probably ICE-TCP is being used';
|
||||
}
|
||||
|
||||
let finalRemoteCandidate = remoteCandidates[remoteCandidateId];
|
||||
if (!!finalRemoteCandidate) {
|
||||
const candList = this.stream.getRemoteIceCandidateList();
|
||||
const cand = candList.filter((c: RTCIceCandidate) => {
|
||||
return (!!c.candidate &&
|
||||
c.candidate.indexOf(finalRemoteCandidate.ipAddress) >= 0 &&
|
||||
c.candidate.indexOf(finalRemoteCandidate.portNumber) >= 0 &&
|
||||
c.candidate.indexOf(finalRemoteCandidate.priority) >= 0);
|
||||
});
|
||||
finalRemoteCandidate.raw = !!cand[0] ? cand[0].candidate : 'ERROR: Cannot find remote candidate in list of received ICE candidates';
|
||||
} else {
|
||||
finalRemoteCandidate = 'ERROR: No active remote ICE candidate. Probably ICE-TCP is being used';
|
||||
}
|
||||
|
||||
resolve({
|
||||
googCandidatePair,
|
||||
localCandidate: finalLocalCandidate,
|
||||
remoteCandidate: finalRemoteCandidate
|
||||
});
|
||||
} else {
|
||||
reject('Selected ICE candidate info only available for Chrome');
|
||||
}
|
||||
}).catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public stopWebRtcStats() {
|
||||
if (this.webRtcStatsEnabled) {
|
||||
clearInterval(this.webRtcStatsIntervalId);
|
||||
logger.warn('WebRtc stats stopped for disposed stream ' + this.stream.streamId + ' of connection ' + this.stream.connection.connectionId);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendStats(url: string, json: JSONStats): Promise<void> {
|
||||
try {
|
||||
const configuration: RequestInit = {
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(json),
|
||||
method: 'POST',
|
||||
};
|
||||
await fetch(url, configuration);
|
||||
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendStatsToHttpEndpoint(): Promise<void> {
|
||||
try {
|
||||
const stats: RTCStatsReport = await this.getStats();
|
||||
const json = this.generateJSONStats(stats);
|
||||
// this.parseAndSendStats(stats);
|
||||
await this.sendStats(this.POST_URL, json);
|
||||
} catch (error) {
|
||||
logger.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async getStats(): Promise<any> {
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
|
||||
|
||||
const pc: any = this.stream.getRTCPeerConnection();
|
||||
pc.getStats((statsReport) => {
|
||||
resolve(this.standardizeReport(statsReport));
|
||||
});
|
||||
} else {
|
||||
const statsReport = await this.stream.getRTCPeerConnection().getStats();
|
||||
resolve(this.standardizeReport(statsReport));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private generateJSONStats(stats: RTCStatsReport): JSONStats {
|
||||
return {
|
||||
'@timestamp': new Date().toISOString(),
|
||||
participant_id: this.stream.connection.data,
|
||||
session_id: this.stream.session.sessionId,
|
||||
platform: platform.getName(),
|
||||
platform_description: platform.getDescription(),
|
||||
stream: 'webRTC',
|
||||
webrtc_stats: stats
|
||||
};
|
||||
}
|
||||
|
||||
private standardizeReport(response: RTCStatsReport | any) {
|
||||
let standardReport = {};
|
||||
|
||||
if (platform.isChromeBrowser() || platform.isChromeMobileBrowser() || platform.isOperaBrowser() || platform.isOperaMobileBrowser()) {
|
||||
response.result().forEach(report => {
|
||||
const standardStats = {
|
||||
id: report.id,
|
||||
timestamp: report.timestamp,
|
||||
type: report.type
|
||||
};
|
||||
report.names().forEach((name) => {
|
||||
standardStats[name] = report.stat(name);
|
||||
});
|
||||
standardReport[standardStats.id] = standardStats;
|
||||
});
|
||||
|
||||
return standardReport;
|
||||
}
|
||||
|
||||
// Others platforms
|
||||
response.forEach((values) => {
|
||||
let standardStats: any = {};
|
||||
Object.keys(values).forEach((value: any) => {
|
||||
standardStats[value] = values[value];
|
||||
});
|
||||
standardReport[standardStats.id] = standardStats
|
||||
});
|
||||
|
||||
return standardReport;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
export { OpenVidu } from './OpenVidu/OpenVidu';
|
||||
export { Session } from './OpenVidu/Session';
|
||||
export { Publisher } from './OpenVidu/Publisher';
|
||||
export { Subscriber } from './OpenVidu/Subscriber';
|
||||
export { StreamManager } from './OpenVidu/StreamManager';
|
||||
export { Stream } from './OpenVidu/Stream';
|
||||
export { Connection } from './OpenVidu/Connection';
|
||||
export { LocalRecorder } from './OpenVidu/LocalRecorder';
|
||||
export { Filter } from './OpenVidu/Filter';
|
||||
|
||||
export { LocalRecorderState } from './OpenViduInternal/Enums/LocalRecorderState';
|
||||
export { OpenViduError } from './OpenViduInternal/Enums/OpenViduError';
|
||||
export { VideoInsertMode } from './OpenViduInternal/Enums/VideoInsertMode';
|
||||
|
||||
export { Event } from './OpenViduInternal/Events/Event';
|
||||
export { ConnectionEvent } from './OpenViduInternal/Events/ConnectionEvent';
|
||||
export { PublisherSpeakingEvent } from './OpenViduInternal/Events/PublisherSpeakingEvent';
|
||||
export { RecordingEvent } from './OpenViduInternal/Events/RecordingEvent';
|
||||
export { SessionDisconnectedEvent } from './OpenViduInternal/Events/SessionDisconnectedEvent';
|
||||
export { SignalEvent } from './OpenViduInternal/Events/SignalEvent';
|
||||
export { StreamEvent } from './OpenViduInternal/Events/StreamEvent';
|
||||
export { StreamManagerEvent } from './OpenViduInternal/Events/StreamManagerEvent';
|
||||
export { VideoElementEvent } from './OpenViduInternal/Events/VideoElementEvent';
|
||||
export { StreamPropertyChangedEvent } from './OpenViduInternal/Events/StreamPropertyChangedEvent';
|
||||
export { ConnectionPropertyChangedEvent } from './OpenViduInternal/Events/ConnectionPropertyChangedEvent';
|
||||
export { FilterEvent } from './OpenViduInternal/Events/FilterEvent';
|
||||
export { NetworkQualityLevelChangedEvent } from './OpenViduInternal/Events/NetworkQualityLevelChangedEvent';
|
||||
|
||||
export { Capabilities } from './OpenViduInternal/Interfaces/Public/Capabilities';
|
||||
export { Device } from './OpenViduInternal/Interfaces/Public/Device';
|
||||
export { EventDispatcher } from './OpenVidu/EventDispatcher';
|
||||
export { OpenViduAdvancedConfiguration } from './OpenViduInternal/Interfaces/Public/OpenViduAdvancedConfiguration';
|
||||
export { PublisherProperties } from './OpenViduInternal/Interfaces/Public/PublisherProperties';
|
||||
export { SignalOptions } from './OpenViduInternal/Interfaces/Public/SignalOptions';
|
||||
export { StreamManagerVideo } from './OpenViduInternal/Interfaces/Public/StreamManagerVideo';
|
||||
export { SubscriberProperties } from './OpenViduInternal/Interfaces/Public/SubscriberProperties';
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
//"allowUnusedLabels": true,
|
||||
"allowUnreachableCode": false,
|
||||
"buildOnSave": false,
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"emitBOM": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015.promise",
|
||||
"es5",
|
||||
"scripthost"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
//"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
//"noUnusedLocals": true,
|
||||
//"noUnusedParameters": true,
|
||||
"outDir": "../../lib",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"rootDir": "./src",
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strictNullChecks": true,
|
||||
"suppressExcessPropertyErrors": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"target": "es5"
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
/target/
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](https://docs.openvidu.io/en/stable/?badge=stable)
|
||||
[](https://hub.docker.com/r/openvidu/)
|
||||
[](https://groups.google.com/forum/#!forum/openvidu)
|
||||
|
||||
[![][OpenViduLogo]](https://openvidu.io)
|
||||
|
||||
openvidu-client
|
||||
===
|
||||
|
||||
Internal Java client used by [openvidu-server](https://github.com/OpenVidu/openvidu/tree/master/openvidu-server). Can be used to implement an Android client.
|
||||
|
||||
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.openvidu</groupId>
|
||||
<artifactId>openvidu-parent</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openvidu-client</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>OpenVidu Client</name>
|
||||
<description>
|
||||
OpenVidu client library for the client-side of OpenVidu Server
|
||||
</description>
|
||||
<url>https://github.com/OpenVidu/openvidu</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<organization>
|
||||
<name>OpenVidu</name>
|
||||
<url>https://github.com/OpenVidu/openvidu</url>
|
||||
</organization>
|
||||
|
||||
<scm>
|
||||
<url>${openvidu.scm.url}</url>
|
||||
<connection>scm:git:${openvidu.scm.connection}</connection>
|
||||
<developerConnection>scm:git:${openvidu.scm.connection}</developerConnection>
|
||||
<tag>develop</tag>
|
||||
</scm>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>openvidu.io</id>
|
||||
<name>-openvidu.io Community</name>
|
||||
<organization>OpenVidu</organization>
|
||||
<organizationUrl>https://openvidu.io</organizationUrl>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.kurento</groupId>
|
||||
<artifactId>kurento-jsonrpc-client</artifactId>
|
||||
<version>${version.kurento}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.kurento</groupId>
|
||||
<artifactId>kurento-jsonrpc-client-jetty</artifactId>
|
||||
<version>${version.kurento}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${version.junit}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${version.mockito.core}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>default</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>default</name>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client;
|
||||
|
||||
import static io.openvidu.client.internal.ProtocolElements.CUSTOMREQUEST_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERID_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMID_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_PEERSTREAMS_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_ROOM_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.JOINROOM_USER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.LEAVEROOM_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_CANDIDATE_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_EPNAME_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMIDPARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.ONICECANDIDATE_SDPMLINEINDEX_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_DOLOOPBACK_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPANSWER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.PUBLISHVIDEO_SDPOFFER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPANSWER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SDPOFFER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.RECEIVEVIDEO_SENDER_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_MESSAGE_PARAM;
|
||||
import static io.openvidu.client.internal.ProtocolElements.SENDMESSAGE_ROOM_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.UNPUBLISHVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_METHOD;
|
||||
import static io.openvidu.client.internal.ProtocolElements.UNSUBSCRIBEFROMVIDEO_SENDER_PARAM;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.kurento.jsonrpc.client.JsonRpcClient;
|
||||
import org.kurento.jsonrpc.client.JsonRpcClientWebSocket;
|
||||
import org.kurento.jsonrpc.client.JsonRpcWSConnectionListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.openvidu.client.internal.JsonRoomUtils;
|
||||
import io.openvidu.client.internal.Notification;
|
||||
|
||||
/**
|
||||
* Java client for the room server.
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class OpenViduClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OpenViduClient.class);
|
||||
|
||||
private JsonRpcClient client;
|
||||
private ServerJsonRpcHandler handler;
|
||||
|
||||
public OpenViduClient(String wsUri) {
|
||||
this(new JsonRpcClientWebSocket(wsUri, new JsonRpcWSConnectionListener() {
|
||||
|
||||
@Override
|
||||
public void reconnected(boolean sameServer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected() {
|
||||
log.warn("JsonRpcWebsocket connection: Disconnected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionFailed() {
|
||||
log.warn("JsonRpcWebsocket connection: Connection failed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reconnecting() {
|
||||
log.warn("JsonRpcWebsocket connection: is reconnecting");
|
||||
}
|
||||
}, new SslContextFactory(true)));
|
||||
}
|
||||
|
||||
public OpenViduClient(JsonRpcClient client) {
|
||||
this.client = client;
|
||||
this.handler = new ServerJsonRpcHandler();
|
||||
this.client.setServerRequestHandler(this.handler);
|
||||
}
|
||||
|
||||
public OpenViduClient(JsonRpcClient client, ServerJsonRpcHandler handler) {
|
||||
this.client = client;
|
||||
this.handler = handler;
|
||||
this.client.setServerRequestHandler(this.handler);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
this.client.close();
|
||||
}
|
||||
|
||||
public Map<String, List<String>> joinRoom(String roomName, String userName)
|
||||
throws IOException {
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(JOINROOM_ROOM_PARAM, roomName);
|
||||
params.addProperty(JOINROOM_USER_PARAM, userName);
|
||||
|
||||
JsonElement result = client.sendRequest(JOINROOM_METHOD, params);
|
||||
Map<String, List<String>> peers = new HashMap<String, List<String>>();
|
||||
JsonArray jsonPeers = JsonRoomUtils.getResponseProperty(result, "value", JsonArray.class);
|
||||
if (jsonPeers.size() > 0) {
|
||||
Iterator<JsonElement> peerIt = jsonPeers.iterator();
|
||||
while (peerIt.hasNext()) {
|
||||
JsonElement peer = peerIt.next();
|
||||
String peerId = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERID_PARAM,
|
||||
String.class);
|
||||
List<String> streams = new ArrayList<String>();
|
||||
JsonArray jsonStreams = JsonRoomUtils.getResponseProperty(peer, JOINROOM_PEERSTREAMS_PARAM,
|
||||
JsonArray.class, true);
|
||||
if (jsonStreams != null) {
|
||||
Iterator<JsonElement> streamIt = jsonStreams.iterator();
|
||||
while (streamIt.hasNext()) {
|
||||
streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(),
|
||||
JOINROOM_PEERSTREAMID_PARAM, String.class));
|
||||
}
|
||||
}
|
||||
peers.put(peerId, streams);
|
||||
}
|
||||
}
|
||||
return peers;
|
||||
}
|
||||
|
||||
public void leaveRoom() throws IOException {
|
||||
client.sendRequest(LEAVEROOM_METHOD, new JsonObject());
|
||||
}
|
||||
|
||||
public String publishVideo(String sdpOffer, boolean doLoopback) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(PUBLISHVIDEO_SDPOFFER_PARAM, sdpOffer);
|
||||
params.addProperty(PUBLISHVIDEO_DOLOOPBACK_PARAM, doLoopback);
|
||||
JsonElement result = client.sendRequest(PUBLISHVIDEO_METHOD, params);
|
||||
return JsonRoomUtils.getResponseProperty(result, PUBLISHVIDEO_SDPANSWER_PARAM, String.class);
|
||||
}
|
||||
|
||||
public void unpublishVideo() throws IOException {
|
||||
client.sendRequest(UNPUBLISHVIDEO_METHOD, new JsonObject());
|
||||
}
|
||||
|
||||
// sender should look like 'username_streamId'
|
||||
public String receiveVideoFrom(String sender, String sdpOffer) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(RECEIVEVIDEO_SENDER_PARAM, sender);
|
||||
params.addProperty(RECEIVEVIDEO_SDPOFFER_PARAM, sdpOffer);
|
||||
JsonElement result = client.sendRequest(RECEIVEVIDEO_METHOD, params);
|
||||
return JsonRoomUtils.getResponseProperty(result, RECEIVEVIDEO_SDPANSWER_PARAM, String.class);
|
||||
}
|
||||
|
||||
// sender should look like 'username_streamId'
|
||||
public void unsubscribeFromVideo(String sender) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(UNSUBSCRIBEFROMVIDEO_SENDER_PARAM, sender);
|
||||
client.sendRequest(UNSUBSCRIBEFROMVIDEO_METHOD, params);
|
||||
}
|
||||
|
||||
public void onIceCandidate(String endpointName, String candidate, String sdpMid,
|
||||
int sdpMLineIndex) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(ONICECANDIDATE_EPNAME_PARAM, endpointName);
|
||||
params.addProperty(ONICECANDIDATE_CANDIDATE_PARAM, candidate);
|
||||
params.addProperty(ONICECANDIDATE_SDPMIDPARAM, sdpMid);
|
||||
params.addProperty(ONICECANDIDATE_SDPMLINEINDEX_PARAM, sdpMLineIndex);
|
||||
client.sendRequest(ONICECANDIDATE_METHOD, params);
|
||||
}
|
||||
|
||||
public void sendMessage(String userName, String roomName, String message) throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(SENDMESSAGE_MESSAGE_PARAM, message);
|
||||
client.sendRequest(SENDMESSAGE_ROOM_METHOD, params);
|
||||
}
|
||||
|
||||
public JsonElement customRequest(JsonObject customReqParams) throws IOException {
|
||||
return client.sendRequest(CUSTOMREQUEST_METHOD, customReqParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the notifications list maintained by this client to obtain new events sent by server.
|
||||
* This method blocks until there is a notification to return. This is a one-time operation for
|
||||
* the returned element.
|
||||
*
|
||||
* @return a server notification object, null when interrupted while waiting
|
||||
*/
|
||||
public Notification getServerNotification() {
|
||||
return this.handler.getNotification();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.openvidu.client;
|
||||
|
||||
import org.kurento.jsonrpc.JsonRpcErrorException;
|
||||
|
||||
public class OpenViduException extends JsonRpcErrorException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static enum Code {
|
||||
GENERIC_ERROR_CODE(999), WRONG_PATH_CODE(998),
|
||||
|
||||
TRANSPORT_ERROR_CODE(803), TRANSPORT_RESPONSE_ERROR_CODE(802), TRANSPORT_REQUEST_ERROR_CODE(801),
|
||||
|
||||
MEDIA_TYPE_STREAM_INCOMPATIBLE_WITH_RECORDING_PROPERTIES_ERROR_CODE(309),
|
||||
MEDIA_TYPE_RECORDING_PROPERTIES_ERROR_CODE(308), MEDIA_MUTE_ERROR_CODE(307),
|
||||
MEDIA_NOT_A_WEB_ENDPOINT_ERROR_CODE(306), MEDIA_RTP_ENDPOINT_ERROR_CODE(305),
|
||||
MEDIA_WEBRTC_ENDPOINT_ERROR_CODE(304), MEDIA_ENDPOINT_ERROR_CODE(303), MEDIA_SDP_ERROR_CODE(302),
|
||||
MEDIA_GENERIC_ERROR_CODE(301),
|
||||
|
||||
ROOM_CANNOT_BE_CREATED_ERROR_CODE(204), ROOM_CLOSED_ERROR_CODE(203), ROOM_NOT_FOUND_ERROR_CODE(202),
|
||||
ROOM_GENERIC_ERROR_CODE(201),
|
||||
|
||||
USER_ALREADY_STREAMING_ERROR_CODE(106), USER_NOT_STREAMING_ERROR_CODE(105), EXISTING_USER_IN_ROOM_ERROR_CODE(104), USER_CLOSED_ERROR_CODE(103),
|
||||
USER_NOT_FOUND_ERROR_CODE(102), USER_GENERIC_ERROR_CODE(10),
|
||||
|
||||
USER_UNAUTHORIZED_ERROR_CODE(401), ROLE_NOT_FOUND_ERROR_CODE(402), SESSIONID_CANNOT_BE_CREATED_ERROR_CODE(403),
|
||||
TOKEN_CANNOT_BE_CREATED_ERROR_CODE(404), EXISTING_FILTER_ALREADY_APPLIED_ERROR_CODE(405),
|
||||
FILTER_NOT_APPLIED_ERROR_CODE(406), FILTER_EVENT_LISTENER_NOT_FOUND(407),
|
||||
|
||||
USER_METADATA_FORMAT_INVALID_ERROR_CODE(500),
|
||||
|
||||
SIGNAL_FORMAT_INVALID_ERROR_CODE(600), SIGNAL_TO_INVALID_ERROR_CODE(601),
|
||||
|
||||
DOCKER_NOT_FOUND(709), RECORDING_PATH_NOT_VALID(708), RECORDING_FILE_EMPTY_ERROR(707),
|
||||
RECORDING_DELETE_ERROR_CODE(706), RECORDING_LIST_ERROR_CODE(705), RECORDING_STOP_ERROR_CODE(704),
|
||||
RECORDING_START_ERROR_CODE(703), RECORDING_REPORT_ERROR_CODE(702), RECORDING_COMPLETION_ERROR_CODE(701),
|
||||
|
||||
FORCED_CODEC_NOT_FOUND_IN_SDPOFFER(800),
|
||||
|
||||
MEDIA_NODE_NOT_FOUND(900), MEDIA_NODE_STATUS_WRONG(901);
|
||||
|
||||
private int value;
|
||||
|
||||
private Code(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
private Code code = Code.GENERIC_ERROR_CODE;
|
||||
|
||||
public OpenViduException(Code code, String message) {
|
||||
super(code.getValue(), message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCodeValue() {
|
||||
return code.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import org.kurento.jsonrpc.DefaultJsonRpcHandler;
|
||||
import org.kurento.jsonrpc.Transaction;
|
||||
import org.kurento.jsonrpc.message.Request;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.openvidu.client.internal.IceCandidate;
|
||||
import io.openvidu.client.internal.IceCandidateInfo;
|
||||
import io.openvidu.client.internal.JsonRoomUtils;
|
||||
import io.openvidu.client.internal.MediaErrorInfo;
|
||||
import io.openvidu.client.internal.Notification;
|
||||
import io.openvidu.client.internal.ParticipantEvictedInfo;
|
||||
import io.openvidu.client.internal.ParticipantJoinedInfo;
|
||||
import io.openvidu.client.internal.ParticipantLeftInfo;
|
||||
import io.openvidu.client.internal.ParticipantPublishedInfo;
|
||||
import io.openvidu.client.internal.ParticipantUnpublishedInfo;
|
||||
import io.openvidu.client.internal.ProtocolElements;
|
||||
import io.openvidu.client.internal.RoomClosedInfo;
|
||||
import io.openvidu.client.internal.SendMessageInfo;
|
||||
|
||||
/**
|
||||
* Service that handles server JSON-RPC events.
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ServerJsonRpcHandler extends DefaultJsonRpcHandler<JsonObject> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ServerJsonRpcHandler.class);
|
||||
|
||||
private BlockingQueue<Notification> notifications = new ArrayBlockingQueue<Notification>(100);
|
||||
|
||||
@Override
|
||||
public void handleRequest(Transaction transaction, Request<JsonObject> request) throws Exception {
|
||||
Notification notif = null;
|
||||
try {
|
||||
switch (request.getMethod()) {
|
||||
case ProtocolElements.ICECANDIDATE_METHOD:
|
||||
notif = iceCandidate(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.MEDIAERROR_METHOD:
|
||||
notif = mediaError(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTJOINED_METHOD:
|
||||
notif = participantJoined(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTLEFT_METHOD:
|
||||
notif = participantLeft(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTEVICTED_METHOD:
|
||||
notif = participantEvicted(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTPUBLISHED_METHOD:
|
||||
notif = participantPublished(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD:
|
||||
notif = participantUnpublished(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.ROOMCLOSED_METHOD:
|
||||
notif = roomClosed(transaction, request);
|
||||
break;
|
||||
case ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD:
|
||||
notif = participantSendMessage(transaction, request);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unrecognized request " + request.getMethod());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Exception processing request {}", request, e);
|
||||
transaction.sendError(e);
|
||||
return;
|
||||
}
|
||||
if (notif != null) {
|
||||
try {
|
||||
notifications.put(notif);
|
||||
log.debug("Enqueued notification {}", notif);
|
||||
} catch (InterruptedException e) {
|
||||
log.warn("Interrupted when enqueuing notification {}", notif, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Notification participantSendMessage(Transaction transaction,
|
||||
Request<JsonObject> request) {
|
||||
String data = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTSENDMESSAGE_DATA_PARAM, String.class);
|
||||
String from = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTSENDMESSAGE_FROM_PARAM, String.class);
|
||||
String type = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTSENDMESSAGE_TYPE_PARAM, String.class);
|
||||
SendMessageInfo eventInfo = new SendMessageInfo(data, from, type);
|
||||
log.debug("Recvd send message event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification roomClosed(Transaction transaction, Request<JsonObject> request) {
|
||||
String room = JsonRoomUtils.getRequestParam(request, ProtocolElements.ROOMCLOSED_ROOM_PARAM,
|
||||
String.class);
|
||||
RoomClosedInfo eventInfo = new RoomClosedInfo(room);
|
||||
log.debug("Recvd room closed event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantUnpublished(Transaction transaction,
|
||||
Request<JsonObject> request) {
|
||||
String name = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTUNPUBLISHED_NAME_PARAM, String.class);
|
||||
ParticipantUnpublishedInfo eventInfo = new ParticipantUnpublishedInfo(name);
|
||||
log.debug("Recvd participant unpublished event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantPublished(Transaction transaction, Request<JsonObject> request) {
|
||||
String id = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTPUBLISHED_USER_PARAM, String.class);
|
||||
JsonArray jsonStreams = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTPUBLISHED_STREAMS_PARAM, JsonArray.class);
|
||||
Iterator<JsonElement> streamIt = jsonStreams.iterator();
|
||||
List<String> streams = new ArrayList<String>();
|
||||
while (streamIt.hasNext()) {
|
||||
streams.add(JsonRoomUtils.getResponseProperty(streamIt.next(),
|
||||
ProtocolElements.PARTICIPANTPUBLISHED_STREAMID_PARAM, String.class));
|
||||
}
|
||||
ParticipantPublishedInfo eventInfo = new ParticipantPublishedInfo(id, streams);
|
||||
log.debug("Recvd published event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantEvicted(Transaction transaction, Request<JsonObject> request) {
|
||||
ParticipantEvictedInfo eventInfo = new ParticipantEvictedInfo();
|
||||
log.debug("Recvd participant evicted event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantLeft(Transaction transaction, Request<JsonObject> request) {
|
||||
String name = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTLEFT_NAME_PARAM, String.class);
|
||||
ParticipantLeftInfo eventInfo = new ParticipantLeftInfo(name);
|
||||
log.debug("Recvd participant left event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification participantJoined(Transaction transaction, Request<JsonObject> request) {
|
||||
String id = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.PARTICIPANTJOINED_USER_PARAM, String.class);
|
||||
ParticipantJoinedInfo eventInfo = new ParticipantJoinedInfo(id);
|
||||
log.debug("Recvd participant joined event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification mediaError(Transaction transaction, Request<JsonObject> request) {
|
||||
String description = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.MEDIAERROR_ERROR_PARAM, String.class);
|
||||
MediaErrorInfo eventInfo = new MediaErrorInfo(description);
|
||||
log.debug("Recvd media error event {}", eventInfo);
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
private Notification iceCandidate(Transaction transaction, Request<JsonObject> request) {
|
||||
|
||||
String candidate = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_CANDIDATE_PARAM, String.class);
|
||||
String sdpMid = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_SDPMID_PARAM, String.class);
|
||||
int sdpMLineIndex = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_SDPMLINEINDEX_PARAM, Integer.class);
|
||||
|
||||
IceCandidate iceCandidate = new IceCandidate(candidate, sdpMid, sdpMLineIndex);
|
||||
|
||||
String endpoint = JsonRoomUtils.getRequestParam(request,
|
||||
ProtocolElements.ICECANDIDATE_EPNAME_PARAM, String.class);
|
||||
|
||||
IceCandidateInfo eventInfo = new IceCandidateInfo(iceCandidate, endpoint);
|
||||
log.debug("Recvd ICE candidate event {}", eventInfo);
|
||||
|
||||
return eventInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until an element is available and then returns it by removing it from the queue.
|
||||
*
|
||||
* @return a {@link Notification} from the queue, null when interrupted
|
||||
* @see BlockingQueue#take()
|
||||
*/
|
||||
public Notification getNotification() {
|
||||
try {
|
||||
Notification notif = notifications.take();
|
||||
log.debug("Dequeued notification {}", notif);
|
||||
return notif;
|
||||
} catch (InterruptedException e) {
|
||||
log.info("Interrupted while polling notifications' queue");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package io.openvidu.client.internal;
|
||||
|
||||
public class IceCandidate {
|
||||
|
||||
private String candidate;
|
||||
private String sdpMid;
|
||||
private int sdpMLineIndex;
|
||||
|
||||
public IceCandidate(String candidate, String sdpMid, int sdpMLineIndex) {
|
||||
super();
|
||||
this.candidate = candidate;
|
||||
this.sdpMid = sdpMid;
|
||||
this.sdpMLineIndex = sdpMLineIndex;
|
||||
}
|
||||
|
||||
public String getCandidate() {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
public String getSdpMid() {
|
||||
return sdpMid;
|
||||
}
|
||||
|
||||
public int getSdpMLineIndex() {
|
||||
return sdpMLineIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IceCandidate [candidate=" + candidate + ", sdpMid=" + sdpMid + ", sdpMLineIndex="
|
||||
+ sdpMLineIndex + "]";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class IceCandidateInfo extends Notification {
|
||||
|
||||
private IceCandidate iceCandidate;
|
||||
private String endpointName;
|
||||
|
||||
public IceCandidateInfo(IceCandidate iceCandidate, String endpointName) {
|
||||
super(ProtocolElements.ICECANDIDATE_METHOD);
|
||||
this.iceCandidate = iceCandidate;
|
||||
this.endpointName = endpointName;
|
||||
}
|
||||
|
||||
public IceCandidate getIceCandidate() {
|
||||
return iceCandidate;
|
||||
}
|
||||
|
||||
public void setIceCandidate(IceCandidate iceCandidate) {
|
||||
this.iceCandidate = iceCandidate;
|
||||
}
|
||||
|
||||
public String getEndpointName() {
|
||||
return endpointName;
|
||||
}
|
||||
|
||||
public void setEndpointName(String endpointName) {
|
||||
this.endpointName = endpointName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (endpointName != null) {
|
||||
builder.append("endpointName=").append(endpointName).append(", ");
|
||||
}
|
||||
if (iceCandidate != null) {
|
||||
builder.append("iceCandidate=[sdpMLineIndex= ").append(iceCandidate.getSdpMLineIndex())
|
||||
.append(", sdpMid=").append(iceCandidate.getSdpMid()).append(", candidate=")
|
||||
.append(iceCandidate.getCandidate()).append("]");
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
import org.kurento.jsonrpc.message.Request;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.openvidu.client.OpenViduException;
|
||||
import io.openvidu.client.OpenViduException.Code;
|
||||
|
||||
/**
|
||||
* JSON tools for extracting info from request or response elements.
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class JsonRoomUtils {
|
||||
|
||||
public static <T> T getRequestParam(Request<JsonObject> request, String paramName, Class<T> type) {
|
||||
return getRequestParam(request, paramName, type, false);
|
||||
}
|
||||
|
||||
public static <T> T getRequestParam(Request<JsonObject> request, String paramName, Class<T> type,
|
||||
boolean allowNull) {
|
||||
JsonObject params = request.getParams();
|
||||
if (params == null) {
|
||||
if (!allowNull) {
|
||||
throw new OpenViduException(Code.TRANSPORT_REQUEST_ERROR_CODE,
|
||||
"Invalid request lacking parameter '" + paramName + "'");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return getConverted(params.get(paramName), paramName, type, allowNull);
|
||||
}
|
||||
|
||||
public static <T> T getResponseProperty(JsonElement result, String property, Class<T> type) {
|
||||
return getResponseProperty(result, property, type, false);
|
||||
}
|
||||
|
||||
public static <T> T getResponseProperty(JsonElement result, String property, Class<T> type,
|
||||
boolean allowNull) {
|
||||
if (!(result instanceof JsonObject)) {
|
||||
throw new OpenViduException(Code.TRANSPORT_RESPONSE_ERROR_CODE,
|
||||
"Invalid response format. The response '" + result + "' should be a Json object");
|
||||
}
|
||||
return getConverted(result.getAsJsonObject().get(property), property, type, allowNull);
|
||||
}
|
||||
|
||||
public static JsonArray getResponseArray(JsonElement result) {
|
||||
if (!result.isJsonArray()) {
|
||||
throw new OpenViduException(Code.TRANSPORT_RESPONSE_ERROR_CODE,
|
||||
"Invalid response format. The response '" + result + "' should be a Json array");
|
||||
}
|
||||
return result.getAsJsonArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T getConverted(JsonElement paramValue, String property, Class<T> type,
|
||||
boolean allowNull) {
|
||||
if (paramValue == null) {
|
||||
if (allowNull) {
|
||||
return null;
|
||||
} else {
|
||||
throw new OpenViduException(Code.TRANSPORT_ERROR_CODE, "Invalid method lacking parameter '"
|
||||
+ property + "'");
|
||||
}
|
||||
}
|
||||
|
||||
if (type == String.class) {
|
||||
if (paramValue.isJsonPrimitive()) {
|
||||
return (T) paramValue.getAsString();
|
||||
}
|
||||
}
|
||||
|
||||
if (type == Integer.class) {
|
||||
if (paramValue.isJsonPrimitive()) {
|
||||
return (T) Integer.valueOf(paramValue.getAsInt());
|
||||
}
|
||||
}
|
||||
|
||||
if (type == JsonArray.class) {
|
||||
if (paramValue.isJsonArray()) {
|
||||
return (T) paramValue.getAsJsonArray();
|
||||
}
|
||||
}
|
||||
|
||||
throw new OpenViduException(Code.TRANSPORT_ERROR_CODE, "Param '" + property + "' with value '"
|
||||
+ paramValue + "' is not a " + type.getName());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*
|
||||
* @see Notification
|
||||
*/
|
||||
public class MediaErrorInfo extends Notification {
|
||||
|
||||
private String description;
|
||||
|
||||
public MediaErrorInfo(String description) {
|
||||
super(ProtocolElements.MEDIAERROR_METHOD);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (description != null) {
|
||||
builder.append("description=").append(description);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* Wrapper for server events.
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public abstract class Notification {
|
||||
|
||||
public enum Method {
|
||||
ICECANDIDATE_METHOD(ProtocolElements.ICECANDIDATE_METHOD), MEDIAERROR_METHOD(
|
||||
ProtocolElements.MEDIAERROR_METHOD), PARTICIPANTJOINED_METHOD(
|
||||
ProtocolElements.PARTICIPANTJOINED_METHOD), PARTICIPANTLEFT_METHOD(
|
||||
ProtocolElements.PARTICIPANTLEFT_METHOD), PARTICIPANTEVICTED_METHOD(
|
||||
ProtocolElements.PARTICIPANTEVICTED_METHOD), PARTICIPANTPUBLISHED_METHOD(
|
||||
ProtocolElements.PARTICIPANTPUBLISHED_METHOD), PARTICIPANTUNPUBLISHED_METHOD(
|
||||
ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD), ROOMCLOSED_METHOD(
|
||||
ProtocolElements.ROOMCLOSED_METHOD), PARTICIPANTSENDMESSAGE_METHOD(
|
||||
ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD);
|
||||
|
||||
private String methodValue;
|
||||
|
||||
private Method(String val) {
|
||||
this.methodValue = val;
|
||||
}
|
||||
|
||||
public String getMethodValue() {
|
||||
return methodValue;
|
||||
}
|
||||
|
||||
public static Method getFromValue(String val) {
|
||||
for (Method m : Method.values()) {
|
||||
if (m.methodValue.equals(val)) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getMethodValue().toString();
|
||||
}
|
||||
}
|
||||
|
||||
private Method method;
|
||||
|
||||
public Notification(Method method) {
|
||||
this.setMethod(method);
|
||||
}
|
||||
|
||||
public Notification(String methodValue) {
|
||||
this(Method.getFromValue(methodValue));
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(Method method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (method != null) {
|
||||
builder.append("method=").append(method);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ParticipantEvictedInfo extends Notification {
|
||||
|
||||
public ParticipantEvictedInfo() {
|
||||
super(ProtocolElements.PARTICIPANTEVICTED_METHOD);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ParticipantJoinedInfo extends Notification {
|
||||
|
||||
private String id;
|
||||
|
||||
public ParticipantJoinedInfo(String id) {
|
||||
super(ProtocolElements.PARTICIPANTJOINED_METHOD);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (id != null) {
|
||||
builder.append("id=").append(id);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ParticipantLeftInfo extends Notification {
|
||||
|
||||
private String name;
|
||||
|
||||
public ParticipantLeftInfo(String name) {
|
||||
super(ProtocolElements.PARTICIPANTLEFT_METHOD);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (name != null) {
|
||||
builder.append("name=").append(name);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ParticipantPublishedInfo extends Notification {
|
||||
|
||||
private String id;
|
||||
private List<String> streams;
|
||||
|
||||
public ParticipantPublishedInfo(String id, List<String> streams) {
|
||||
super(ProtocolElements.PARTICIPANTPUBLISHED_METHOD);
|
||||
this.id = id;
|
||||
this.streams = streams;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<String> getStreams() {
|
||||
return streams;
|
||||
}
|
||||
|
||||
public void setStreams(List<String> streams) {
|
||||
this.streams = streams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (id != null) {
|
||||
builder.append("id=").append(id).append(", ");
|
||||
}
|
||||
if (streams != null) {
|
||||
builder.append("streams=").append(streams);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ParticipantUnpublishedInfo extends Notification {
|
||||
|
||||
private String name;
|
||||
|
||||
public ParticipantUnpublishedInfo(String name) {
|
||||
super(ProtocolElements.PARTICIPANTUNPUBLISHED_METHOD);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (name != null) {
|
||||
builder.append("name=").append(name);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* This class defines constant values of client-server messages and their
|
||||
* parameters.
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class ProtocolElements {
|
||||
|
||||
// ---------------------------- CLIENT REQUESTS -----------------------
|
||||
|
||||
public static final String SENDMESSAGE_ROOM_METHOD = "sendMessage";
|
||||
public static final String SENDMESSAGE_MESSAGE_PARAM = "message";
|
||||
|
||||
public static final String LEAVEROOM_METHOD = "leaveRoom";
|
||||
|
||||
public static final String JOINROOM_METHOD = "joinRoom";
|
||||
public static final String JOINROOM_USER_PARAM = "user";
|
||||
public static final String JOINROOM_TOKEN_PARAM = "token";
|
||||
public static final String JOINROOM_ROOM_PARAM = "session";
|
||||
public static final String JOINROOM_METADATA_PARAM = "metadata";
|
||||
public static final String JOINROOM_SECRET_PARAM = "secret";
|
||||
public static final String JOINROOM_PLATFORM_PARAM = "platform";
|
||||
public static final String JOINROOM_RECORDER_PARAM = "recorder";
|
||||
|
||||
public static final String JOINROOM_PEERID_PARAM = "id";
|
||||
public static final String JOINROOM_PEERCREATEDAT_PARAM = "createdAt";
|
||||
public static final String JOINROOM_PEERSTREAMS_PARAM = "streams";
|
||||
public static final String JOINROOM_PEERSTREAMID_PARAM = "id";
|
||||
public static final String JOINROOM_PEERSTREAMHASAUDIO_PARAM = "hasAudio";
|
||||
public static final String JOINROOM_PEERSTREAMHASVIDEO_PARAM = "hasVideo";
|
||||
public static final String JOINROOM_PEERSTREAMAUDIOACTIVE_PARAM = "audioActive";
|
||||
public static final String JOINROOM_PEERSTREAMVIDEOACTIVE_PARAM = "videoActive";
|
||||
public static final String JOINROOM_PEERSTREAMTYPEOFVIDEO_PARAM = "typeOfVideo";
|
||||
public static final String JOINROOM_PEERSTREAMFRAMERATE_PARAM = "frameRate";
|
||||
public static final String JOINROOM_PEERSTREAMVIDEODIMENSIONS_PARAM = "videoDimensions";
|
||||
public static final String JOINROOM_PEERSTREAMFILTER_PARAM = "filter";
|
||||
|
||||
public static final String PUBLISHVIDEO_METHOD = "publishVideo";
|
||||
public static final String PUBLISHVIDEO_SDPOFFER_PARAM = "sdpOffer";
|
||||
public static final String PUBLISHVIDEO_DOLOOPBACK_PARAM = "doLoopback";
|
||||
public static final String PUBLISHVIDEO_SDPANSWER_PARAM = "sdpAnswer";
|
||||
public static final String PUBLISHVIDEO_STREAMID_PARAM = "id";
|
||||
public static final String PUBLISHVIDEO_CREATEDAT_PARAM = "createdAt";
|
||||
public static final String PUBLISHVIDEO_HASAUDIO_PARAM = "hasAudio";
|
||||
public static final String PUBLISHVIDEO_HASVIDEO_PARAM = "hasVideo";
|
||||
public static final String PUBLISHVIDEO_AUDIOACTIVE_PARAM = "audioActive";
|
||||
public static final String PUBLISHVIDEO_VIDEOACTIVE_PARAM = "videoActive";
|
||||
public static final String PUBLISHVIDEO_TYPEOFVIDEO_PARAM = "typeOfVideo";
|
||||
public static final String PUBLISHVIDEO_FRAMERATE_PARAM = "frameRate";
|
||||
public static final String PUBLISHVIDEO_VIDEODIMENSIONS_PARAM = "videoDimensions";
|
||||
public static final String PUBLISHVIDEO_KURENTOFILTER_PARAM = "filter";
|
||||
|
||||
public static final String UNPUBLISHVIDEO_METHOD = "unpublishVideo";
|
||||
|
||||
public static final String RECEIVEVIDEO_METHOD = "receiveVideoFrom";
|
||||
public static final String RECEIVEVIDEO_SDPOFFER_PARAM = "sdpOffer";
|
||||
public static final String RECEIVEVIDEO_SENDER_PARAM = "sender";
|
||||
public static final String RECEIVEVIDEO_SDPANSWER_PARAM = "sdpAnswer";
|
||||
|
||||
public static final String UNSUBSCRIBEFROMVIDEO_METHOD = "unsubscribeFromVideo";
|
||||
public static final String UNSUBSCRIBEFROMVIDEO_SENDER_PARAM = "sender";
|
||||
|
||||
public static final String ONICECANDIDATE_METHOD = "onIceCandidate";
|
||||
public static final String ONICECANDIDATE_EPNAME_PARAM = "endpointName";
|
||||
public static final String ONICECANDIDATE_CANDIDATE_PARAM = "candidate";
|
||||
public static final String ONICECANDIDATE_SDPMIDPARAM = "sdpMid";
|
||||
public static final String ONICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex";
|
||||
|
||||
public static final String CUSTOMREQUEST_METHOD = "customRequest";
|
||||
|
||||
public static final String STREAMPROPERTYCHANGED_METHOD = "streamPropertyChanged";
|
||||
public static final String STREAMPROPERTYCHANGED_CONNECTIONID_PARAM = "connectionId";
|
||||
public static final String STREAMPROPERTYCHANGED_STREAMID_PARAM = "streamId";
|
||||
public static final String STREAMPROPERTYCHANGED_PROPERTY_PARAM = "property";
|
||||
public static final String STREAMPROPERTYCHANGED_NEWVALUE_PARAM = "newValue";
|
||||
public static final String STREAMPROPERTYCHANGED_REASON_PARAM = "reason";
|
||||
|
||||
public static final String CONNECTIONPERTYCHANGED_METHOD = "connectionPropertyChanged";
|
||||
public static final String CONNECTIONROPERTYCHANGED_PROPERTY_PARAM = "property";
|
||||
public static final String CONNECTIONPROPERTYCHANGED_NEWVALUE_PARAM = "newValue";
|
||||
|
||||
public static final String NETWORKQUALITYLEVELCHANGED_METHOD = "networkQualityLevelChanged";
|
||||
public static final String NETWORKQUALITYCHANGED_CONNECTIONID_PARAM = "connectionId";
|
||||
public static final String NETWORKQUALITYCHANGED_NEWVALUE_PARAM = "newValue";
|
||||
public static final String NETWORKQUALITYCHANGED_OLDVALUE_PARAM = "oldValue";
|
||||
|
||||
public static final String FORCEDISCONNECT_METHOD = "forceDisconnect";
|
||||
public static final String FORCEDISCONNECT_CONNECTIONID_PARAM = "connectionId";
|
||||
|
||||
public static final String FORCEUNPUBLISH_METHOD = "forceUnpublish";
|
||||
public static final String FORCEUNPUBLISH_STREAMID_PARAM = "streamId";
|
||||
|
||||
public static final String APPLYFILTER_METHOD = "applyFilter";
|
||||
public static final String FILTER_STREAMID_PARAM = "streamId";
|
||||
public static final String FILTER_TYPE_PARAM = "type";
|
||||
public static final String FILTER_OPTIONS_PARAM = "options";
|
||||
public static final String FILTER_METHOD_PARAM = "method";
|
||||
public static final String FILTER_PARAMS_PARAM = "params";
|
||||
public static final String EXECFILTERMETHOD_METHOD = "execFilterMethod";
|
||||
public static final String EXECFILTERMETHOD_LASTEXECMETHOD_PARAM = "lastExecMethod";
|
||||
public static final String REMOVEFILTER_METHOD = "removeFilter";
|
||||
public static final String ADDFILTEREVENTLISTENER_METHOD = "addFilterEventListener";
|
||||
public static final String REMOVEFILTEREVENTLISTENER_METHOD = "removeFilterEventListener";
|
||||
|
||||
public static final String FILTEREVENTDISPATCHED_METHOD = "filterEventDispatched";
|
||||
public static final String FILTEREVENTLISTENER_CONNECTIONID_PARAM = "connectionId";
|
||||
public static final String FILTEREVENTLISTENER_STREAMID_PARAM = "streamId";
|
||||
public static final String FILTEREVENTLISTENER_FILTERTYPE_PARAM = "filterType";
|
||||
public static final String FILTEREVENTLISTENER_EVENTTYPE_PARAM = "eventType";
|
||||
public static final String FILTEREVENTLISTENER_DATA_PARAM = "data";
|
||||
|
||||
public static final String RECONNECTSTREAM_METHOD = "reconnectStream";
|
||||
public static final String RECONNECTSTREAM_STREAM_PARAM = "stream";
|
||||
public static final String RECONNECTSTREAM_SDPOFFER_PARAM = "sdpOffer";
|
||||
|
||||
public static final String VIDEODATA_METHOD = "videoData";
|
||||
|
||||
// ---------------------------- SERVER RESPONSES & EVENTS -----------------
|
||||
|
||||
public static final String PARTICIPANTJOINED_METHOD = "participantJoined";
|
||||
public static final String PARTICIPANTJOINED_USER_PARAM = "id";
|
||||
public static final String PARTICIPANTJOINED_CREATEDAT_PARAM = "createdAt";
|
||||
public static final String PARTICIPANTJOINED_METADATA_PARAM = "metadata";
|
||||
public static final String PARTICIPANTJOINED_VALUE_PARAM = "value";
|
||||
public static final String PARTICIPANTJOINED_SESSION_PARAM = "session";
|
||||
public static final String PARTICIPANTJOINED_VERSION_PARAM = "version";
|
||||
public static final String PARTICIPANTJOINED_RECORD_PARAM = "record";
|
||||
public static final String PARTICIPANTJOINED_ROLE_PARAM = "role";
|
||||
public static final String PARTICIPANTJOINED_COTURNIP_PARAM = "coturnIp";
|
||||
public static final String PARTICIPANTJOINED_TURNUSERNAME_PARAM = "turnUsername";
|
||||
public static final String PARTICIPANTJOINED_TURNCREDENTIAL_PARAM = "turnCredential";
|
||||
|
||||
public static final String PARTICIPANTLEFT_METHOD = "participantLeft";
|
||||
public static final String PARTICIPANTLEFT_NAME_PARAM = "connectionId";
|
||||
public static final String PARTICIPANTLEFT_REASON_PARAM = "reason";
|
||||
|
||||
public static final String PARTICIPANTEVICTED_METHOD = "participantEvicted";
|
||||
public static final String PARTICIPANTEVICTED_CONNECTIONID_PARAM = "connectionId";
|
||||
public static final String PARTICIPANTEVICTED_REASON_PARAM = "reason";
|
||||
|
||||
public static final String PARTICIPANTPUBLISHED_METHOD = "participantPublished";
|
||||
public static final String PARTICIPANTPUBLISHED_USER_PARAM = "id";
|
||||
public static final String PARTICIPANTPUBLISHED_STREAMS_PARAM = "streams";
|
||||
public static final String PARTICIPANTPUBLISHED_STREAMID_PARAM = "id";
|
||||
public static final String PARTICIPANTPUBLISHED_CREATEDAT_PARAM = "createdAt";
|
||||
public static final String PARTICIPANTPUBLISHED_HASAUDIO_PARAM = "hasAudio";
|
||||
public static final String PARTICIPANTPUBLISHED_HASVIDEO_PARAM = "hasVideo";
|
||||
public static final String PARTICIPANTPUBLISHED_AUDIOACTIVE_PARAM = "audioActive";
|
||||
public static final String PARTICIPANTPUBLISHED_VIDEOACTIVE_PARAM = "videoActive";
|
||||
public static final String PARTICIPANTPUBLISHED_TYPEOFVIDEO_PARAM = "typeOfVideo";
|
||||
public static final String PARTICIPANTPUBLISHED_FRAMERATE_PARAM = "frameRate";
|
||||
public static final String PARTICIPANTPUBLISHED_VIDEODIMENSIONS_PARAM = "videoDimensions";
|
||||
public static final String PARTICIPANTPUBLISHED_FILTER_PARAM = "filter";
|
||||
|
||||
public static final String PARTICIPANTUNPUBLISHED_METHOD = "participantUnpublished";
|
||||
public static final String PARTICIPANTUNPUBLISHED_NAME_PARAM = "connectionId";
|
||||
public static final String PARTICIPANTUNPUBLISHED_REASON_PARAM = "reason";
|
||||
|
||||
public static final String PARTICIPANTSENDMESSAGE_METHOD = "sendMessage";
|
||||
public static final String PARTICIPANTSENDMESSAGE_DATA_PARAM = "data";
|
||||
public static final String PARTICIPANTSENDMESSAGE_FROM_PARAM = "from";
|
||||
public static final String PARTICIPANTSENDMESSAGE_TYPE_PARAM = "type";
|
||||
|
||||
public static final String ROOMCLOSED_METHOD = "roomClosed";
|
||||
public static final String ROOMCLOSED_ROOM_PARAM = "sessionId";
|
||||
|
||||
public static final String MEDIAERROR_METHOD = "mediaError";
|
||||
public static final String MEDIAERROR_ERROR_PARAM = "error";
|
||||
|
||||
public static final String ICECANDIDATE_METHOD = "iceCandidate";
|
||||
public static final String ICECANDIDATE_SENDERCONNECTIONID_PARAM = "senderConnectionId";
|
||||
public static final String ICECANDIDATE_EPNAME_PARAM = "endpointName";
|
||||
public static final String ICECANDIDATE_CANDIDATE_PARAM = "candidate";
|
||||
public static final String ICECANDIDATE_SDPMID_PARAM = "sdpMid";
|
||||
public static final String ICECANDIDATE_SDPMLINEINDEX_PARAM = "sdpMLineIndex";
|
||||
|
||||
public static final String RECORDINGSTARTED_METHOD = "recordingStarted";
|
||||
public static final String RECORDINGSTARTED_ID_PARAM = "id";
|
||||
public static final String RECORDINGSTARTED_NAME_PARAM = "name";
|
||||
public static final String RECORDINGSTOPPED_REASON_PARAM = "reason";
|
||||
|
||||
public static final String RECORDINGSTOPPED_METHOD = "recordingStopped";
|
||||
public static final String RECORDINGSTOPPED_ID_PARAM = "id";
|
||||
|
||||
public static final String CUSTOM_NOTIFICATION = "custonNotification";
|
||||
|
||||
public static final String RECORDER_PARTICIPANT_PUBLICID = "RECORDER";
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class RoomClosedInfo extends Notification {
|
||||
|
||||
private String room;
|
||||
|
||||
public RoomClosedInfo(String room) {
|
||||
super(ProtocolElements.ROOMCLOSED_METHOD);
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
||||
public void setRoom(String room) {
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (room != null) {
|
||||
builder.append("room=").append(room);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.openvidu.client.internal;
|
||||
|
||||
/**
|
||||
* @see Notification
|
||||
*
|
||||
* @author <a href="mailto:rvlad@naevatec.com">Radu Tom Vlad</a>
|
||||
*/
|
||||
public class SendMessageInfo extends Notification {
|
||||
|
||||
private String room;
|
||||
private String user;
|
||||
private String message;
|
||||
|
||||
public SendMessageInfo(String room, String user, String message) {
|
||||
super(ProtocolElements.PARTICIPANTSENDMESSAGE_METHOD);
|
||||
this.room = room;
|
||||
this.user = user;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getRoom() {
|
||||
return room;
|
||||
}
|
||||
|
||||
public void setRoom(String room) {
|
||||
this.room = room;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
if (getMethod() != null) {
|
||||
builder.append("method=").append(getMethod()).append(", ");
|
||||
}
|
||||
if (room != null) {
|
||||
builder.append("room=").append(room).append(", ");
|
||||
}
|
||||
if (user != null) {
|
||||
builder.append("user=").append(user).append(", ");
|
||||
}
|
||||
if (message != null) {
|
||||
builder.append("message=").append(message);
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* (C) Copyright 2017-2020 OpenVidu (https://openvidu.io)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.openvidu.client.test;
|
||||
|
||||
import static io.openvidu.client.internal.ProtocolElements.*;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.kurento.jsonrpc.client.JsonRpcClient;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import io.openvidu.client.OpenViduClient;
|
||||
import io.openvidu.client.ServerJsonRpcHandler;
|
||||
|
||||
/**
|
||||
* Unit tests for the room client protocol.
|
||||
*
|
||||
* @author Radu Tom Vlad (rvlad@naevatec.com)
|
||||
* @since 6.3.1
|
||||
*/
|
||||
public class OpenViduClientTest {
|
||||
|
||||
private OpenViduClient client;
|
||||
private ServerJsonRpcHandler serverHandler;
|
||||
private JsonRpcClient jsonRpcClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
jsonRpcClient = mock(JsonRpcClient.class);
|
||||
serverHandler = new ServerJsonRpcHandler();
|
||||
client = new OpenViduClient(jsonRpcClient, serverHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoomJoin() throws IOException {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty(JOINROOM_ROOM_PARAM, "room");
|
||||
params.addProperty(JOINROOM_USER_PARAM, "user");
|
||||
|
||||
JsonObject result = new JsonObject();
|
||||
JsonArray value = new JsonArray();
|
||||
result.add("value", value);
|
||||
|
||||
Map<String, List<String>> joinResult = new HashMap<String, List<String>>();
|
||||
|
||||
when(jsonRpcClient.sendRequest(JOINROOM_METHOD, params)).thenReturn(result);
|
||||
assertThat(client.joinRoom("room", "user"), is(joinResult));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
**/node_modules/
|
||||
**/.angular/
|
||||
**/.vscode
|
||||
|
||||
|
||||
node_modules
|
||||
dist/
|
||||
docs/
|
||||
coverage/**
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
build
|
||||
node_modules
|
||||
.github
|
||||
dist
|
||||
docs
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 140,
|
||||
"trailingComma": "none",
|
||||
"semi": true,
|
||||
"bracketSpacing": true,
|
||||
"useTabs": true,
|
||||
"jsxSingleQuote": true,
|
||||
"tabWidth": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# Openvidu Angular TestAPP
|
||||
|
||||
|
||||
## Architechture
|
||||
|
||||
```
|
||||
openvidu-components-angular
|
||||
│
|
||||
└─── src (openvidu-components-testapp)
|
||||
│
|
||||
└───projects
|
||||
│
|
||||
└─── openvidu-components-angular
|
||||
```
|
||||
|
||||
## How to develop with ease:
|
||||
|
||||
Run `ng serve` for a dev server.
|
||||
|
||||
Run, in a new terminal, `npm run lib:serve` for serving the openvidu-components-angular library with live reload for listening changes
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
For generate new components in openvidu-components-angular:
|
||||
|
||||
```bash
|
||||
ng g component components/component-name --project=openvidu-components-angular
|
||||
```
|
||||
|
||||
|
||||
## Build library
|
||||
|
||||
```bash
|
||||
npm run lib:build
|
||||
```
|
||||
|
||||
## Publishing
|
||||
|
||||
After the library is built, tun the following command:
|
||||
|
||||
```bash
|
||||
cd dist/ && npm publish
|
||||
```
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"openvidu-components-testapp": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"outputPath": {
|
||||
"base": "dist/openvidu-components-testapp",
|
||||
"browser": ""
|
||||
},
|
||||
"index": "src/index.html",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": [],
|
||||
"browser": "src/main.ts"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true
|
||||
},
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"proxyConfig": "src/proxy.conf.json",
|
||||
"buildTarget": "openvidu-components-testapp:build"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "openvidu-components-testapp:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "openvidu-components-testapp:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "openvidu-components-testapp:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": ["src/favicon.ico", "src/assets"],
|
||||
"styles": ["src/styles.scss"],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "openvidu-components-testapp:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "openvidu-components-testapp:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"openvidu-components-angular": {
|
||||
"projectType": "library",
|
||||
"root": "projects/openvidu-components-angular",
|
||||
"sourceRoot": "projects/openvidu-components-angular/src",
|
||||
"prefix": "ov",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"project": "projects/openvidu-components-angular/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "projects/openvidu-components-angular/tsconfig.lib.prod.json"
|
||||
},
|
||||
"development": {
|
||||
"tsConfig": "projects/openvidu-components-angular/tsconfig.lib.json"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "projects/openvidu-components-angular/src/test.ts",
|
||||
"tsConfig": "projects/openvidu-components-angular/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/openvidu-components-angular/karma.conf.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,621 @@
|
|||
import { Builder, WebDriver } from 'selenium-webdriver';
|
||||
import { TestAppConfig } from './selenium.conf';
|
||||
import { OpenViduComponentsPO } from './utils.po.test';
|
||||
|
||||
let url = '';
|
||||
|
||||
describe('Testing API Directives', () => {
|
||||
let browser: WebDriver;
|
||||
let utils: OpenViduComponentsPO;
|
||||
async function createChromeBrowser(): Promise<WebDriver> {
|
||||
return await new Builder()
|
||||
.forBrowser(TestAppConfig.browserName)
|
||||
.withCapabilities(TestAppConfig.browserCapabilities)
|
||||
.setChromeOptions(TestAppConfig.browserOptions)
|
||||
.usingServer(TestAppConfig.seleniumAddress)
|
||||
.build();
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
browser = await createChromeBrowser();
|
||||
utils = new OpenViduComponentsPO(browser);
|
||||
url = `${TestAppConfig.appUrl}&roomName=API_DIRECTIVES_${Math.floor(Math.random() * 1000)}`;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// console.log('data:image/png;base64,' + await browser.takeScreenshot());
|
||||
try {
|
||||
if (await utils.isPresent('#session-container')) {
|
||||
await utils.leaveRoom();
|
||||
}
|
||||
} catch (error) {}
|
||||
await browser.sleep(500);
|
||||
await browser.quit();
|
||||
});
|
||||
|
||||
it('should set the MINIMAL UI', async () => {
|
||||
await browser.get(`${url}&minimal=true`);
|
||||
// Checking if prejoin page exist
|
||||
await utils.checkPrejoinIsPresent();
|
||||
|
||||
// Checking if audio detection is not displayed
|
||||
expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
|
||||
|
||||
const joinButton = await utils.waitForElement('#join-button');
|
||||
await joinButton.click();
|
||||
|
||||
// Checking if session container is present
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if layout is present
|
||||
await utils.checkLayoutPresent();
|
||||
|
||||
// Checking if stream component is present
|
||||
utils.checkStreamIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if screenshare button is not present
|
||||
expect(await utils.isPresent('#screenshare-btn')).toBeFalse();
|
||||
|
||||
// Checking if more options button is not present
|
||||
expect(await utils.isPresent('#more-options-btn')).toBeFalse();
|
||||
|
||||
// Checking if participants panel button is not present
|
||||
expect(await utils.isPresent('#participants-panel-btn')).toBeFalse();
|
||||
|
||||
// Checking if activities panel button is not present
|
||||
expect(await utils.isPresent('#activities-panel-btn')).toBeFalse();
|
||||
|
||||
// Checking if logo is not displayed
|
||||
expect(await utils.isPresent('#branding-logo')).toBeFalse();
|
||||
|
||||
// Checking if session name is not displayed
|
||||
expect(await utils.isPresent('#session-name')).toBeFalse();
|
||||
|
||||
// Checking if nickname is not displayed
|
||||
expect(await utils.getNumberOfElements('#participant-name-container')).toEqual(0);
|
||||
|
||||
// Checking if audio detection is not displayed
|
||||
expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
|
||||
|
||||
// Checking if settings button is not displayed
|
||||
expect(await utils.isPresent('#settings-container')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should change the UI LANG in prejoin page', async () => {
|
||||
await browser.get(`${url}&lang=es`);
|
||||
|
||||
await utils.checkPrejoinIsPresent();
|
||||
|
||||
await utils.waitForElement('#lang-btn-compact');
|
||||
|
||||
const element = await utils.waitForElement('#join-button');
|
||||
expect(await element.getText()).toEqual('Unirme ahora');
|
||||
});
|
||||
|
||||
it('should change the UI LANG in room page', async () => {
|
||||
await browser.get(`${url}&prejoin=false&lang=es`);
|
||||
|
||||
await utils.checkLayoutPresent();
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
await utils.togglePanel('settings');
|
||||
|
||||
await utils.waitForElement('.sidenav-menu');
|
||||
expect(await utils.isPresent('#default-settings-panel')).toBeTrue();
|
||||
const panelTitle = await utils.waitForElement('.panel-title');
|
||||
expect(await panelTitle.getText()).toEqual('Configuración');
|
||||
|
||||
const element = await utils.waitForElement('#lang-selected-name');
|
||||
expect(await element.getAttribute('innerText')).toEqual('Español');
|
||||
});
|
||||
|
||||
it('should override the LANG OPTIONS', async () => {
|
||||
await browser.get(`${url}&prejoin=true&langOptions=true`);
|
||||
|
||||
await utils.checkPrejoinIsPresent();
|
||||
await utils.waitForElement('#lang-btn-compact');
|
||||
await utils.clickOn('#lang-btn-compact');
|
||||
await browser.sleep(500);
|
||||
expect(await utils.getNumberOfElements('.lang-menu-opt')).toEqual(2);
|
||||
|
||||
await utils.clickOn('.lang-menu-opt');
|
||||
await browser.sleep(500);
|
||||
|
||||
await utils.clickOn('#join-button');
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
await utils.togglePanel('settings');
|
||||
|
||||
await browser.sleep(500);
|
||||
|
||||
await utils.waitForElement('#settings-container');
|
||||
await utils.waitForElement('.lang-button');
|
||||
await utils.clickOn('.lang-button');
|
||||
|
||||
await browser.sleep(500);
|
||||
|
||||
expect(await utils.getNumberOfElements('.lang-menu-opt')).toEqual(2);
|
||||
});
|
||||
|
||||
it('should show the PREJOIN page', async () => {
|
||||
await browser.get(`${url}&prejoin=true`);
|
||||
await utils.checkPrejoinIsPresent();
|
||||
});
|
||||
|
||||
it('should not show the PREJOIN page', async () => {
|
||||
await browser.get(`${url}&prejoin=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
});
|
||||
|
||||
it('should join to Room', async () => {
|
||||
await browser.get(`${url}`);
|
||||
|
||||
// Checking if prejoin page exist
|
||||
await utils.checkPrejoinIsPresent();
|
||||
|
||||
const joinButton = await utils.waitForElement('#join-button');
|
||||
await joinButton.click();
|
||||
|
||||
// Checking if session container is present
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if screenshare button is not present
|
||||
expect(await utils.isPresent('#screenshare-btn')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should show the token error WITH prejoin page', async () => {
|
||||
const fixedUrl = `${TestAppConfig.appUrl}&roomName=TEST_TOKEN&participantName=PNAME`;
|
||||
await browser.get(`${fixedUrl}`);
|
||||
|
||||
// Checking if prejoin page exist
|
||||
await utils.checkPrejoinIsPresent();
|
||||
|
||||
await utils.waitForElement('#join-button');
|
||||
await utils.clickOn('#join-button');
|
||||
|
||||
// Checking if session container is present
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Starting new browser for adding a new participant
|
||||
const newTabScript = `window.open("${fixedUrl}")`;
|
||||
await browser.executeScript(newTabScript);
|
||||
|
||||
// Go to first tab
|
||||
const tabs = await browser.getAllWindowHandles();
|
||||
browser.switchTo().window(tabs[1]);
|
||||
|
||||
await utils.checkPrejoinIsPresent();
|
||||
await utils.waitForElement('#join-button');
|
||||
await utils.clickOn('#join-button');
|
||||
|
||||
// Checking if token error is displayed
|
||||
await utils.waitForElement('#token-error');
|
||||
expect(await utils.isPresent('#token-error')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should show the token error WITHOUT prejoin page', async () => {
|
||||
const fixedUrl = `${TestAppConfig.appUrl}&roomName=TOKEN_ERROR&prejoin=false&participantName=PNAME`;
|
||||
await browser.get(`${fixedUrl}`);
|
||||
|
||||
// Checking if session container is present
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Starting new browser for adding a new participant
|
||||
const newTabScript = `window.open("${fixedUrl}")`;
|
||||
await browser.executeScript(newTabScript);
|
||||
|
||||
// Go to first tab
|
||||
const tabs = await browser.getAllWindowHandles();
|
||||
browser.switchTo().window(tabs[1]);
|
||||
|
||||
// Checking if token error is displayed
|
||||
await utils.waitForElement('#openvidu-dialog');
|
||||
expect(await utils.isPresent('#openvidu-dialog')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should run the app with VIDEO DISABLED in prejoin page', async () => {
|
||||
await browser.get(`${url}&prejoin=true&videoEnabled=false`);
|
||||
|
||||
await utils.checkPrejoinIsPresent();
|
||||
|
||||
// Checking if video is displayed
|
||||
await utils.waitForElement('#video-poster');
|
||||
expect(await utils.getNumberOfElements('video')).toEqual(0);
|
||||
|
||||
await utils.waitForElement('#videocam_off');
|
||||
|
||||
await utils.clickOn('#join-button');
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
await utils.waitForElement('#videocam_off');
|
||||
expect(await utils.isPresent('#videocam_off')).toBeTrue();
|
||||
|
||||
await utils.waitForElement('#video-poster');
|
||||
expect(await utils.getNumberOfElements('video')).toEqual(0);
|
||||
});
|
||||
|
||||
it('should run the app with VIDEO DISABLED and WITHOUT PREJOIN page', async () => {
|
||||
await browser.get(`${url}&prejoin=false&videoEnabled=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
await utils.checkLayoutPresent();
|
||||
|
||||
// Checking if video is displayed
|
||||
await utils.waitForElement('#video-poster');
|
||||
expect(await utils.getNumberOfElements('video')).toEqual(0);
|
||||
expect(await utils.getNumberOfElements('#video-poster')).toEqual(1);
|
||||
|
||||
await utils.waitForElement('#videocam_off');
|
||||
expect(await utils.isPresent('#videocam_off')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should run the app with AUDIO DISABLED in prejoin page', async () => {
|
||||
await browser.get(`${url}&audioEnabled=false`);
|
||||
|
||||
await utils.checkPrejoinIsPresent();
|
||||
|
||||
// Checking if video is displayed
|
||||
await utils.checkVideoElementIsPresent();
|
||||
expect(await utils.getNumberOfElements('video')).toEqual(1);
|
||||
|
||||
expect(await utils.getNumberOfElements('audio')).toEqual(0);
|
||||
await utils.waitForElement('#mic_off');
|
||||
expect(await utils.isPresent('#mic_off')).toBeTrue();
|
||||
|
||||
await utils.clickOn('#join-button');
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
expect(await utils.getNumberOfElements('video')).toEqual(1);
|
||||
expect(await utils.getNumberOfElements('audio')).toEqual(0);
|
||||
await utils.waitForElement('#mic_off');
|
||||
expect(await utils.isPresent('#mic_off')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should run the app with AUDIO DISABLED and WITHOUT PREJOIN page', async () => {
|
||||
await browser.get(`${url}&prejoin=false&audioEnabled=false`);
|
||||
|
||||
await browser.sleep(1000);
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if video is displayed
|
||||
await utils.checkVideoElementIsPresent();
|
||||
expect(await utils.getNumberOfElements('video')).toEqual(1);
|
||||
|
||||
expect(await utils.getNumberOfElements('audio')).toEqual(0);
|
||||
await utils.waitForElement('#mic_off');
|
||||
expect(await utils.isPresent('#mic_off')).toBeTrue();
|
||||
});
|
||||
|
||||
it('should run the app without camera button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&cameraBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if camera button is not present
|
||||
expect(await utils.isPresent('#camera-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should run the app without microphone button', async () => {
|
||||
await browser.get(`${url}&prejoin=falseµphoneBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if microphone button is not present
|
||||
expect(await utils.isPresent('#microphone-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the SCREENSHARE button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&screenshareBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if screenshare button is not present
|
||||
expect(await utils.isPresent('#screenshare-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the FULLSCREEN button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&fullscreenBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
await utils.toggleToolbarMoreOptions();
|
||||
expect(await utils.getNumberOfElements('#fullscreen-btn')).toEqual(0);
|
||||
});
|
||||
|
||||
xit('should HIDE the CAPTIONS button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&toolbarCaptionsBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
await utils.toggleToolbarMoreOptions();
|
||||
|
||||
// Checking if captions button is not present
|
||||
expect(await utils.isPresent('#captions-btn')).toBeFalse();
|
||||
|
||||
await utils.clickOn('#toolbar-settings-btn');
|
||||
|
||||
await browser.sleep(500);
|
||||
|
||||
await utils.waitForElement('.settings-container');
|
||||
expect(await utils.isPresent('.settings-container')).toBeTrue();
|
||||
|
||||
expect(await utils.isPresent('#captions-opt')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the TOOLBAR RECORDING button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&toolbarRecordingButton=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
await utils.toggleToolbarMoreOptions();
|
||||
|
||||
// Checking if recording button is not present
|
||||
expect(await utils.isPresent('#recording-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the TOOLBAR BROADCASTING button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&toolbarBroadcastingButton=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
await utils.toggleToolbarMoreOptions();
|
||||
|
||||
// Checking if broadcasting button is not present
|
||||
expect(await utils.isPresent('#broadcasting-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the TOOLBAR SETTINGS button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&toolbarSettingsBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Open more options menu
|
||||
await utils.toggleToolbarMoreOptions();
|
||||
|
||||
expect(await utils.isPresent('#toolbar-settings-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the LEAVE button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&leaveBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if leave button is not present
|
||||
expect(await utils.getNumberOfElements('#leave-btn')).toEqual(0);
|
||||
});
|
||||
|
||||
it('should HIDE the ACTIVITIES PANEL button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&activitiesPanelBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if activities panel button is not present
|
||||
expect(await utils.isPresent('#activities-panel-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the CHAT PANEL button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&chatPanelBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if chat panel button is not present
|
||||
expect(await utils.isPresent('#chat-panel-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the PARTICIPANTS PANEL button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&participantsPanelBtn=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if participants panel button is not present
|
||||
expect(await utils.isPresent('#participants-panel-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the LOGO', async () => {
|
||||
await browser.get(`${url}&prejoin=false&displayLogo=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.waitForElement('#info-container');
|
||||
expect(await utils.isPresent('#info-container')).toBeTrue();
|
||||
|
||||
// Checking if logo is not displayed
|
||||
expect(await utils.isPresent('#branding-logo')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the ROOM NAME', async () => {
|
||||
await browser.get(`${url}&prejoin=false&displayRoomName=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.waitForElement('#info-container');
|
||||
expect(await utils.isPresent('#info-container')).toBeTrue();
|
||||
|
||||
// Checking if session name is not displayed
|
||||
expect(await utils.isPresent('#session-name')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the PARTICIPANT NAME', async () => {
|
||||
await browser.get(`${url}&prejoin=false&displayParticipantName=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if stream component is present
|
||||
await utils.checkStreamIsPresent();
|
||||
|
||||
// Checking if nickname is not present
|
||||
expect(await utils.isPresent('#participant-name-container')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the AUDIO DETECTION element', async () => {
|
||||
await browser.get(`${url}&prejoin=false&displayAudioDetection=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if stream component is present
|
||||
await utils.checkStreamIsPresent();
|
||||
|
||||
// Checking if audio detection is not present
|
||||
expect(await utils.isPresent('#audio-wave-container')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the STREAM VIDEO CONTROLS button', async () => {
|
||||
await browser.get(`${url}&prejoin=false&videoControls=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
// Checking if stream component is present
|
||||
await utils.checkStreamIsPresent();
|
||||
|
||||
// Checking if settings button is not present
|
||||
expect(await utils.isPresent('.stream-video-controls')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the MUTE button in participants panel', async () => {
|
||||
const roomName = 'e2etest';
|
||||
const fixedUrl = `${TestAppConfig.appUrl}&prejoin=false&participantMuteBtn=false&roomName=${roomName}`;
|
||||
await browser.get(fixedUrl);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
const participantsButton = await utils.waitForElement('#participants-panel-btn');
|
||||
await participantsButton.click();
|
||||
|
||||
// Checking if participatns panel is displayed
|
||||
await utils.waitForElement('#participants-container');
|
||||
expect(await utils.isPresent('#participants-container')).toBeTrue();
|
||||
|
||||
// Checking remote participants item
|
||||
expect(await utils.isPresent('#remote-participant-item')).toBeFalse();
|
||||
|
||||
// Starting new browser for adding a new participant
|
||||
const newTabScript = `window.open("${fixedUrl}&participantName=SecondParticipant")`;
|
||||
await browser.executeScript(newTabScript);
|
||||
await browser.sleep(10000);
|
||||
|
||||
// Go to first tab
|
||||
const tabs = await browser.getAllWindowHandles();
|
||||
browser.switchTo().window(tabs[0]);
|
||||
|
||||
// Checking if mute button is not displayed in participant item
|
||||
await utils.waitForElement('#remote-participant-item');
|
||||
expect(await utils.isPresent('#remote-participant-item')).toBeTrue();
|
||||
|
||||
expect(await utils.isPresent('#mute-btn')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the RECORDING ACTIVITY in activities panel', async () => {
|
||||
let element;
|
||||
const fixedUrl = `${url}&prejoin=false&activitiesPanelRecordingActivity=false`;
|
||||
await browser.get(fixedUrl);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
element = await utils.waitForElement('#activities-panel-btn');
|
||||
await element.click();
|
||||
|
||||
// Checking if participatns panel is displayed
|
||||
await utils.waitForElement('#default-activities-panel');
|
||||
expect(await utils.isPresent('#default-activities-panel')).toBeTrue();
|
||||
|
||||
// await browser.sleep(1000);
|
||||
|
||||
// Checking if recording activity exists
|
||||
await utils.waitForElement('.activities-body-container');
|
||||
expect(await utils.isPresent('ov-recording-activity')).toBeFalse();
|
||||
});
|
||||
|
||||
it('should HIDE the BROADCASTING ACTIVITY in activities panel', async () => {
|
||||
await browser.get(`${url}&prejoin=false&activitiesPanelBroadcastingActivity=false`);
|
||||
|
||||
await utils.checkSessionIsPresent();
|
||||
|
||||
// Checking if toolbar is present
|
||||
await utils.checkToolbarIsPresent();
|
||||
|
||||
await utils.waitForElement('#activities-panel-btn');
|
||||
await utils.clickOn('#activities-panel-btn');
|
||||
|
||||
// Checking if participatns panel is displayed
|
||||
await utils.waitForElement('#default-activities-panel');
|
||||
expect(await utils.isPresent('#default-activities-panel')).toBeTrue();
|
||||
|
||||
// await browser.sleep(1000);
|
||||
|
||||
// Checking if recording activity exists
|
||||
await utils.waitForElement('.activities-body-container');
|
||||
expect(await utils.isPresent('ov-broadcasting-activity')).toBeFalse();
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue