Commit ba3e05cf by Иван Кубота

merge

parents c472dbe0 4d232112
* text=auto eol=lf
build/apps
build/awrtc
build/bundle
node_modules
.idea
yarn.lock
\ No newline at end of file
BSD 3-Clause License
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# MR 2021 Amazon Web
# based on awrtc_browser repo
Run the following commands in the project root directory:
* npm install
* npm run build
Now you should have the final build in the ./build/bundle directory. You can test it using the test applications:
* ./build/callapp.html for the typescrypt example using ./build/bundle/apps.js (contains the merged source code from ./src/apps and ./src/awrtc )
* ./build/callapp_js.html for javascript example that runs the same app but as javascript within the html file so you can easily change the code and experiment. It is using the library only bundle from ./build/bundle/awrtc.js (source code at ./src/awrtc)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.4.1</title>
<link rel="shortcut icon" type="image/png" href="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine_favicon.png">
<link rel="stylesheet" href="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script src="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script src="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script src="node_modules/jasmine/node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
<!-- include source files here... -->
<script src="./build/bundle/test.js"></script>
</head>
<body>
</body>
</html>
<svg width="88" height="32" viewBox="0 0 88 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.63192 4H10.1722C11.1963 4.05596 12.2439 4.27109 13.12 4.82819C13.6976 5.19813 14.1739 5.74093 14.4158 6.38694C14.6912 7.1113 14.7154 7.89782 14.7167 8.66321C14.7142 10.1965 14.7154 11.7304 14.7148 13.2636C14.7117 13.7188 14.7832 14.1813 14.984 14.593C15.1619 14.9766 15.4435 15.2955 15.6754 15.645C15.8023 15.8253 15.8222 16.1057 15.6307 16.2499C15.0599 16.7368 14.4904 17.2255 13.9308 17.7254C13.7778 17.8578 13.6068 18.0139 13.3892 17.9965C13.1436 17.9909 12.9869 17.7739 12.8135 17.6303C12.3813 17.2504 12.0655 16.766 11.7465 16.2922C11.2031 16.8381 10.6162 17.366 9.90674 17.6856C9.2657 17.9797 8.55316 18.0717 7.85368 18.0817C6.83461 18.1196 5.75834 17.8249 5.02653 17.0837C4.33637 16.4029 4.04041 15.4205 4 14.4717V14.0458C4.03606 13.0578 4.37741 12.0661 5.04953 11.3293C5.74342 10.5447 6.7314 10.0877 7.73617 9.85078C8.8914 9.57285 10.0802 9.49202 11.2572 9.35834C11.2361 8.65326 11.3617 7.86984 10.9525 7.2456C10.6068 6.74197 9.94466 6.57036 9.36269 6.62259C8.76767 6.66425 8.16456 6.94839 7.84435 7.46819C7.69326 7.68891 7.63855 7.95378 7.55959 8.20497C7.50114 8.39275 7.32207 8.56933 7.11192 8.53637C6.39503 8.46611 5.68 8.37907 4.96311 8.30881C4.79088 8.29264 4.6 8.26155 4.4887 8.1142C4.33326 7.92394 4.4371 7.66964 4.48187 7.45824C4.71378 6.51254 5.2715 5.64394 6.06798 5.07627C7.09513 4.32953 8.38342 4.05098 9.63192 4ZM9.09907 11.6396C8.63212 11.8093 8.1913 12.1009 7.92643 12.5299C7.62114 13.018 7.55461 13.6199 7.6199 14.182C7.67834 14.6589 7.90218 15.1482 8.32808 15.4012C8.87586 15.7258 9.60145 15.6369 10.1057 15.2682C10.6852 14.8504 11.0259 14.1708 11.1652 13.4831C11.297 12.7749 11.2473 12.0512 11.2578 11.3349C10.5304 11.3405 9.78798 11.3884 9.09907 11.6396Z" fill="white"/>
<path d="M41.9044 4H42.4466C43.3674 4.04601 44.2995 4.2257 45.1214 4.65845C45.7942 5.01161 46.3699 5.57679 46.6653 6.28435C46.9712 7.01306 47.0048 7.81762 47.0072 8.59793C47.0054 10.1536 47.006 11.7092 47.0054 13.2655C47.0048 13.6665 47.0557 14.0738 47.2106 14.4468C47.3728 14.8566 47.6526 15.2023 47.9082 15.5561C48.0251 15.7146 48.1177 15.9329 48.0232 16.1237C47.9436 16.2475 47.8199 16.3333 47.7111 16.429C47.1913 16.8642 46.6839 17.3132 46.1759 17.7621C46.0454 17.8727 45.9005 17.9983 45.7183 17.9971C45.451 18.0126 45.2794 17.777 45.0953 17.6222C44.6669 17.2448 44.3529 16.7641 44.0377 16.2929C43.2978 17.0508 42.4242 17.7552 41.3573 17.9585C40.2195 18.1668 38.9598 18.1743 37.9364 17.5656C37.097 17.0812 36.5529 16.1878 36.3838 15.2458C36.1631 14.0645 36.3173 12.7699 37.0031 11.7602C37.5658 10.9134 38.4605 10.3333 39.418 10.0249C40.7523 9.59337 42.1637 9.51938 43.5478 9.35834C43.5304 8.7142 43.628 8.02839 43.3451 7.42591C43.1386 6.97326 42.6636 6.70466 42.1855 6.63813C41.5457 6.55047 40.84 6.70715 40.3712 7.17223C40.11 7.42218 39.9621 7.76166 39.8806 8.1086C39.8358 8.33927 39.6406 8.58301 39.3807 8.53513C38.6309 8.45617 37.881 8.37596 37.1318 8.29264C36.9073 8.27959 36.6729 8.09679 36.6972 7.8543C36.8533 6.74757 37.4638 5.70301 38.3822 5.05762C39.4012 4.32829 40.6709 4.05409 41.9044 4ZM41.2348 11.6999C40.7368 11.9057 40.2922 12.28 40.0796 12.7836C39.8676 13.2848 39.8352 13.8543 39.944 14.3847C40.0342 14.8155 40.2848 15.234 40.6858 15.4392C41.2236 15.7159 41.9032 15.6232 42.3869 15.2738C42.954 14.8715 43.2947 14.2149 43.4427 13.5478C43.5931 12.8203 43.5366 12.0723 43.5484 11.3343C42.7668 11.3418 41.9635 11.3946 41.2348 11.6999Z" fill="white"/>
<path d="M21.8278 4.91769C22.6404 4.16723 23.8385 3.99127 24.8931 4.20391C25.6143 4.34629 26.2504 4.79707 26.6707 5.39396C26.9132 5.7235 27.1028 6.08847 27.2688 6.46153C27.5679 5.65448 28.1405 4.93759 28.9115 4.54028C29.9225 4.01303 31.1753 3.98443 32.2143 4.44951C32.9486 4.77842 33.5778 5.38028 33.8651 6.14008C34.102 6.76432 34.1474 7.4408 34.1554 8.10298C34.1306 11.1073 34.1449 14.1123 34.1405 17.1173C34.1299 17.315 34.1797 17.5388 34.0553 17.7111C33.9546 17.8597 33.7687 17.9206 33.5952 17.9113C32.8068 17.9088 32.0184 17.9144 31.2301 17.9088C30.977 17.9131 30.7476 17.6874 30.7625 17.4313C30.7606 15.1917 30.7612 12.9515 30.7625 10.7113C30.7612 9.98443 30.8023 9.25386 30.719 8.52951C30.663 8.09241 30.4528 7.63044 30.0313 7.43894C29.5264 7.21945 28.904 7.22878 28.4352 7.5322C28.0814 7.7523 27.8184 8.10919 27.7096 8.51085C27.5629 9.04432 27.5536 9.60391 27.5442 10.1535C27.5424 12.5809 27.5455 15.0083 27.5424 17.435C27.5561 17.6912 27.3254 17.9156 27.0717 17.9088C26.2802 17.9144 25.4887 17.9088 24.6972 17.9113C24.4765 17.9274 24.2427 17.7975 24.1842 17.5755C24.1532 17.4257 24.1675 17.2715 24.165 17.1198C24.1662 14.9641 24.1612 12.8078 24.1637 10.6522C24.1631 9.88432 24.2234 9.10028 24.0269 8.34919C23.9163 7.92267 23.6458 7.5036 23.2124 7.35438C22.8058 7.21759 22.3432 7.22878 21.9478 7.39852C21.5523 7.57075 21.2931 7.95313 21.1637 8.3523C20.9728 8.93365 20.9548 9.5523 20.9449 10.1585C20.9424 12.584 20.9461 15.0089 20.943 17.4338C20.9567 17.6856 20.7328 17.9113 20.4823 17.9082C19.6709 17.9144 18.8595 17.9106 18.0481 17.9094C17.7932 17.9206 17.5488 17.6999 17.5668 17.4387C17.5656 13.2474 17.5656 9.05614 17.5668 4.86546C17.5469 4.6037 17.7888 4.37925 18.045 4.38982C18.7948 4.38484 19.5447 4.38733 20.2945 4.38857C20.5295 4.37427 20.7658 4.55831 20.7795 4.79894C20.7963 5.36785 20.7733 5.938 20.7907 6.50754C21.0512 5.9293 21.3409 5.34236 21.8278 4.91769Z" fill="white"/>
<path d="M65.2099 4.17417C66.2955 4.03303 67.4358 4.17169 68.4164 4.67531C69.2141 5.08008 69.8819 5.71614 70.3693 6.46226C70.9824 7.39925 71.3349 8.48671 71.5028 9.58848C71.7745 11.4301 71.6234 13.3843 70.7871 15.0699C70.3824 15.8826 69.8104 16.6206 69.081 17.1659C67.2829 18.535 64.544 18.5188 62.7907 17.0795C62.0215 16.4546 61.4601 15.6034 61.092 14.69C60.6176 13.5118 60.4373 12.231 60.4528 10.9663C60.469 9.74205 60.6767 8.50661 61.1704 7.38122C61.559 6.49645 62.1422 5.68319 62.92 5.10122C63.5853 4.59822 64.3849 4.28423 65.2099 4.17417ZM65.5158 6.77625C64.9357 6.94972 64.5751 7.50122 64.3973 8.04962C64.0727 9.04754 64.0845 10.1126 64.0864 11.1503C64.0945 12.0867 64.1274 13.0355 64.3712 13.9452C64.5086 14.4332 64.7156 14.9331 65.1161 15.2639C65.4493 15.5462 65.915 15.6183 66.3347 15.535C66.8228 15.4517 67.2226 15.0967 67.4483 14.6676C67.8555 13.896 67.932 13.0044 67.9768 12.1483C68.0079 10.9955 68.0079 9.83345 67.812 8.69376C67.7044 8.16464 67.5913 7.59822 67.2238 7.18039C66.8072 6.7091 66.0972 6.61521 65.5158 6.77625Z" fill="white"/>
<path d="M77.8951 4.8431C78.5088 4.29533 79.3575 4.09699 80.1633 4.1231C80.9685 4.1343 81.7899 4.39481 82.3874 4.9488C82.7772 5.3057 83.0806 5.75958 83.2448 6.26321C83.5196 7.08642 83.5569 7.9631 83.5855 8.82362V17.5513C83.5177 17.7652 83.3163 17.9206 83.09 17.9094C82.2823 17.9119 81.474 17.9138 80.6663 17.9082C80.4151 17.9119 80.182 17.6912 80.205 17.4344C80.2056 14.9455 80.2006 12.456 80.2075 9.96704C80.2037 9.38756 80.1957 8.79502 80.0178 8.23792C79.9009 7.8686 79.6634 7.51419 79.2978 7.35751C78.8129 7.14673 78.2147 7.20518 77.7963 7.53533C77.2846 7.91709 77.0769 8.56062 76.9557 9.16124C76.7865 10.1057 76.8668 11.07 76.8475 12.0238C76.8462 13.8263 76.85 15.6294 76.8462 17.4319C76.8587 17.6887 76.6274 17.9119 76.3743 17.9076C75.5853 17.9163 74.7963 17.9082 74.0079 17.9113C73.7766 17.9293 73.5254 17.7869 73.4837 17.5476C73.4539 17.3231 73.4726 17.0968 73.4694 16.8711C73.4688 12.8694 73.4657 8.86715 73.467 4.86487C73.4464 4.6031 73.6858 4.37989 73.9426 4.39046C74.6918 4.38424 75.4423 4.38611 76.1921 4.38984C76.4234 4.38051 76.6665 4.56331 76.6622 4.80704C76.6734 5.44186 76.6491 6.07792 76.6746 6.71274C76.9892 6.03813 77.3144 5.33492 77.8951 4.8431Z" fill="white"/>
<path d="M50.0041 4.80904C49.9892 4.54044 50.2479 4.32345 50.5096 4.35453C53.1826 4.35516 55.8568 4.35329 58.5297 4.35516C58.7815 4.33277 59.0309 4.54168 59.0215 4.80096C59.029 5.2592 59.0191 5.71868 59.0253 6.17754C59.0669 6.69422 58.6783 7.09091 58.4172 7.49319C57.0182 9.49215 55.618 11.4905 54.2203 13.4901C55.6448 13.4584 57.1034 13.6461 58.4029 14.2592C58.6062 14.3612 58.8201 14.4519 58.9979 14.5974C59.2012 14.7734 59.2752 15.0532 59.2671 15.3137C59.2628 15.9168 59.2709 16.5199 59.2634 17.123C59.2603 17.3661 59.0315 17.6117 58.7778 17.5663C58.6099 17.5135 58.4601 17.4177 58.3003 17.3469C55.8624 16.2177 52.9258 16.2078 50.4928 17.3556C50.3318 17.4283 50.1807 17.5241 50.0128 17.5794C49.7604 17.613 49.5428 17.3643 49.5434 17.1237C49.5372 16.6449 49.544 16.1661 49.5415 15.6874C49.544 15.2584 49.5154 14.8144 49.6646 14.4041C49.7498 14.1442 49.9252 13.9297 50.0775 13.7077C51.6182 11.4961 53.1602 9.28573 54.6997 7.07412C53.2827 7.07101 51.8651 7.07847 50.4481 7.07039C50.2056 7.07661 49.9886 6.85899 50.0041 6.61526C50.001 6.01339 50.0004 5.41091 50.0041 4.80904Z" fill="white"/>
<path d="M53.3399 18.5392C54.239 18.4453 55.1517 18.4547 56.044 18.6039C56.4164 18.6754 56.8 18.7519 57.1314 18.9459C57.4063 19.1019 57.4062 19.4594 57.428 19.7355C57.4436 20.7073 57.2197 21.6679 56.9132 22.585C56.4568 23.9001 55.7536 25.1697 54.6873 26.0868C54.5405 26.2136 54.3242 26.3473 54.1321 26.2404C53.9928 26.1409 54.0338 25.9488 54.0904 25.8145C54.5039 24.7749 54.9136 23.7297 55.2071 22.6485C55.3377 22.143 55.4626 21.6263 55.4489 21.1009C55.4402 20.8528 55.3762 20.5699 55.148 20.4331C54.7464 20.1944 54.2632 20.167 53.8087 20.1347C52.4956 20.0775 51.1861 20.2398 49.8836 20.3852C49.7604 20.3977 49.6075 20.3828 49.5403 20.264C49.4688 20.1266 49.5559 19.9705 49.6541 19.8723C49.8058 19.728 49.9911 19.6255 50.1683 19.516C51.1327 18.9508 52.2357 18.6605 53.3399 18.5392Z" fill="#FF9900"/>
<path d="M15.3745 19.355C15.6717 19.2138 15.9472 19.4656 16.1977 19.5937C22.0013 22.8803 28.6187 24.6741 35.2759 24.9191C41.2491 25.1342 47.2572 23.984 52.7604 21.6636C53.1372 21.4975 53.6806 21.7096 53.713 22.1547C53.7378 22.5359 53.3872 22.7659 53.1124 22.955C51.6624 23.9865 50.0844 24.8289 48.4473 25.521C46.0641 26.5245 43.5534 27.2159 40.9998 27.6182C39.8502 27.8109 38.6862 27.8855 37.5273 27.9999H34.9121C34.1213 27.9091 33.3254 27.8781 32.5351 27.7792C29.6191 27.4379 26.7497 26.6886 24.045 25.5433C20.8653 24.2003 17.9132 22.317 15.3602 19.9941C15.1644 19.8368 15.1264 19.4799 15.3745 19.355Z" fill="#FF9900"/>
</svg>
let content, remoteVideoEl;
const store = window.store = new Store({
conference_id: 'LUCID-4',
connection: false,
webcamtypes: [
{text: 'Center', value: 'Center'},
{text: 'Right', value: 'Right'},
{text: 'Left', value: 'Left'},
],
userRoles: [
{text: 'Remote User', value: 'RemoteUser'},
{text: 'Office User Right', value: 'OfficeUserRight'},
{text: 'Office User Left', value: 'OfficeUserLeft'},
],
});
const isConnected = new Store.Value.Boolean(false);
store.sub('connection', function(val) {
isConnected.set(val)
});
Screen.show('Main');
/*
canvasCtx.save();
canvasCtx.clearRect(0, 0, canvasElement.width, canvasElement.height);
canvasCtx.drawImage(results.segmentationMask, 0, 0,
canvasElement.width, canvasElement.height);
// Only overwrite existing pixels.
canvasCtx.globalCompositeOperation = 'source-in';
canvasCtx.fillStyle = '#ffffff';
canvasCtx.fillRect(0, 0, canvasElement.width, canvasElement.height);
// Only overwrite missing pixels.
canvasCtx.globalCompositeOperation = 'destination-atop';
canvasCtx.drawImage(
results.image, 0, 0, canvasElement.width, canvasElement.height);
canvasCtx.restore();
}
*/
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./bundle/apps.js"></script>
</head>
<body>
<div id="callapp1">
<h1>Callapp1:</h1>
URL to connect:
<p class="callapp_url">
</p>
<input type="checkbox" name="audio" class="callapp_send_audio" checked autocomplete="off"> Audio
<input type="checkbox" name="video" class="callapp_send_video" checked autocomplete="off"> Video
<input type= "text" class="callapp_address" autocomplete="off">
<div class="resolution">
width: <input type="text" class="callapp_width" size="4" value="1280"> </input>
height:<input type="text" class="callapp_height" size="4" value="720"> </input>
</div>
<button class="callapp_button"> Join </button>
<div class="callapp_local_video">local video</div>
<div class="callapp_remote_video">remote video</div>
</div>
<!--
<div id="callapp2">
<h1>Callapp2:</h1>
URL to connect:
<p class="callapp_url">
</p>
<input type="checkbox" name="audio" class="callapp_send_audio" checked autocomplete="off"> Audio
<input type="checkbox" name="video" class="callapp_send_video" checked autocomplete="off"> Video
<input type= "text" class="callapp_address" autocomplete="off">
<button class="callapp_button"> Start / Stop </button>
<div class="callapp_local_video">local video</div>
<div class="callapp_remote_video">remote video</div>
</div>
-->
<script>
apps.callapp(document.querySelector("#callapp1"));
//apps.callapp(document.querySelector("#callapp2"));
</script>
</body>
</html>
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUXh+iPwPcKyq6o4sOPt9sc1t11rgwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTA5MTUwMTQzNTNaFw0zMTA5
MTMwMTQzNTNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCmaz2F06WDP8tEQ/fQH3nrjqmVTfsyBx2ngkR6s2p+
CVEWQLjcEP7B7q9mI83nTwCWHdOBa4Xs1e5BivUSOniGJcPKL6X3M9c9sKVO6V9x
7yMBSc1NpBbOvT46znDQXV6uGjwFOxeVijX1RiS3QQH5XAiZA47TScyWXDIO4fZ/
ZvQp4LLdGeWmnJwTp2Mn9qxni7fVKjjfjAwP+ba1t8QckQicOsfv+THrvvHNrImy
h14Bp7l7A8pYZnXUXggSDONStZlKroAMJ1vu4CejmiQqt4vWNQFZ/hknJ8GTp+40
2RBoflYEnGwJLNpU6rIwDF+W7Nnv+5o6ZWHIN/EHmcPbAgMBAAGjUzBRMB0GA1Ud
DgQWBBS4FuRfsr87fWho9wUnpOrv/CwsiDAfBgNVHSMEGDAWgBS4FuRfsr87fWho
9wUnpOrv/CwsiDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBA
/dHAbjLewomJeDUkJyAiVzD6qmmzulEtEnV61HXNI3n2YMmQCtYQYJha7qqmkW8y
ivqXkc/Gz3I3xxsWulZ4TUTFWPSit7Fs/EHdNl77/4CwUaMEowZx5J9GfRuXoima
cLNkluTbzE7VUwKTsd20/gWcma7Z/I5FhtwFE4/UfYcgXZLxADhKb/1O05JYLQXF
aECyteOKtcxw0KOPMzBBsqJKomOhONtvPe5BscUMyJd4IMGengN/F296g0bl+5PH
0xx+zKc5mANUM2PuZTjmTXVXzxQZixNV5iThVRp6rO95sr0k3orG5vzA1dejGZ9O
iGBcXTbdRuE54TY542NX
-----END CERTIFICATE-----
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./bundle/apps.js"></script>
<button onclick="apps.WebRtcNetwork_minimal()">WebRtcNetwork_minimal</button><br>
<button onclick="apps.BrowserWebRtcCall_minimal()">BrowserWebRtcCall_minimal</button><br>
</head>
<body>
<script>
</script>
</body>
</html>
\ No newline at end of file
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmaz2F06WDP8tE
Q/fQH3nrjqmVTfsyBx2ngkR6s2p+CVEWQLjcEP7B7q9mI83nTwCWHdOBa4Xs1e5B
ivUSOniGJcPKL6X3M9c9sKVO6V9x7yMBSc1NpBbOvT46znDQXV6uGjwFOxeVijX1
RiS3QQH5XAiZA47TScyWXDIO4fZ/ZvQp4LLdGeWmnJwTp2Mn9qxni7fVKjjfjAwP
+ba1t8QckQicOsfv+THrvvHNrImyh14Bp7l7A8pYZnXUXggSDONStZlKroAMJ1vu
4CejmiQqt4vWNQFZ/hknJ8GTp+402RBoflYEnGwJLNpU6rIwDF+W7Nnv+5o6ZWHI
N/EHmcPbAgMBAAECggEAa+0EezPlFRdcNat2nUysfu2IYUGvdKRUwPukeYa9u3tD
UREsUdvbu92VdsIlmKsNkE1Li5U1x4B+AZeik+3DmAwJy6fXFFhUcLeMnX6P3Y3e
v8kjEgUmnXDxWKXlV9wVuQdegW3vP5XgpG6XBRlttE/Ra5p5F+G2gpTMCgbVG42X
ch24ksvJB6dqduf5DEnG03lCEPFkjxMboq7+NABxTM+49I+tMVdSwlBmkgUlWsCj
GsTFRLgd7VRqtLyiHgiyTtgvRfToEWuO3Pn89ZJc0Dzz+wC85CW/jqJm3F1pQrUm
QbXcm79Aa9xqv8GGahwxkGYFZBS/ZCyyMxfevIKR8QKBgQDQ4OOGfRHv7QvbvaZi
PXrbj8UaoCZrrii9T8UqltR/BCR3Dd78Pm9TG0ExRgriowGZq+GvW4Z1zwo1hiqJ
mXV5jpP6uizzlGXMpA7hAbT4qZlPyGA1FBqmz3pMYhVKQ7J6ZadNzDEpoTEM3kRm
5SIRtIYXaJYBDyEso2q7R0Td9wKBgQDL9jwNR67XSWjv24AhZ11bBUl3QmnvSBwP
dbS6jcvCZT0I1rfR/+8bxZgd082aGWaUVVc2KwWSeI2tf/5HjyM5ATQ+GGbE6mq3
AZ4s0cWkVs/uWrtR+9W3JlXb1lGp89Wp9HSrq2dRtehFhIyxvPrVgA5smcRVyZS2
SI4fnp4gPQKBgAbaOdyhocMDPc5ZrGmwpqUpVEgJVPlXX1LGmStKg3IfSqr1M4mG
6tQJItxg9d51honqD59XG1QmFKmo2yBkfmwcd1JUZUEgby6Fe096ZNt7hOIPKsjS
/gTW7aYv2Y70JZjKWPC+cCZzeU54xDmz7qgyCewerD0gp+/09H1sWyAFAoGAHATQ
jRZ621JXWxPxTyVu+rEIPZzfBqMGT81grXwLMMJNlyhAClY5V7xWQEq3ZOL2Z4wt
teBFHLD47Yu2t8ffE9apgZpCi+yUCl3rh8atevz+BYVrVEDfRBC9HL0dbNZ1VKqj
WG3sfJobtWkXFteaMbgswxiAkzRk/IGNHKTinhUCgYEAuaSMFyqDuFQWeG1qkl/q
ViQjWnvvpJijz/CO3afu/YT3ZvTteDR/GGx8i7DZevSOfRRSuYFxsKa8iMZ05UeL
jEFs+EC5Xlft6qK+J/0tu7DGUzq2RFHxq9X+qkiynRAGaHQv2a+JI0TlePmJa1Ax
f44L21DaDQqn0R8PFLhGZvc=
-----END PRIVATE KEY-----
<!DOCTYPE html>
<html>
<head>
<link rel="icon"
type="image/png"
href="favicon.png">
<meta charset="utf-8">
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/control_utils/control_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js" crossorigin="anonymous"></script>
<script src="https://form.dev/vanilla/build/rDOM_latest.js"></script>
<style>
@font-face {
font-family: "Amazon Ember";
src: url("font/AmazonEmber_W_Bd.woff2"), url("font/AmazonEmber_W_Bd.woff");
font-weight: bold;
}
@font-face {
font-family: "Amazon Ember";
src: url("font/AmazonEmber_W_Rg.woff2"), url("font/AmazonEmber_W_Rg.woff");
font-weight: normal;
}
* {
font-family: "Amazon Ember", Verdana;
}
img.logo {
position: absolute;
top: 40px;
left: 40px;
}
html {
height: 100%;
}
.main-content {
margin: 16px 0;
}
#content {
height: 100%;
position: relative;
}
.main-background {
height: 100%;
background: url(/img/back.jpg);
background-size: cover;
background-position-x: center;
}
.main-background:before {
content: '';
position: absolute;
left: 0;
top: 0;
background: rgba(0,0,0,0.5);
right: 0;
bottom: 0;
}
.main-background .p, .main-background .h1 {
color: #fff;
}
.main-background .p {
margin-top: 24px;
}
.top-bar {
height: 64px;
background: #1E143A;
padding: 21px 44px;
box-sizing: border-box;
}
body {
margin: 0;
height: 100%;
}
.button {
box-sizing: border-box;
border-radius: 20px;
padding: 4px 20px;
margin-left: 16px;
cursor: pointer;
}
.button.primary {
background: #584DDA;
color: #fff;
border: 2px solid #584DDA;
}
.button.secondary {
background: transparent;
color: #fff;
border: 2px solid;
}
.buttons {
float: right;
display: flex;
}
.main-buttons {
display: flex;
margin-top: 40px;
}
.sub-content {
position: absolute;
left: 12.4%;
top: 50%;
transform: translateY(-50%);
}
.main-background .button {
margin-right: 16px;
margin-left: 0;
padding: 8px 32px;
font-size: 16px;
}
button.dropdown-field__toggler:after {
content: '▼';
}
button.dropdown-field__toggler {
background: transparent !important;
border: 0;
border-bottom: 2px solid #594CD7;
width: 366px;
position: relative;
text-align: left;
padding: 4px;
font-size: 14px;
line-height: 24px;
cursor: pointer;
}
button.dropdown-field__toggler:after {
content: '▼';
position: absolute;
right: 10px;
font-family: arial;
font-size: 10px;
transform: scaleY(0.7) translateY(4px);
}
.dropdown-field--opened button.dropdown-field__toggler:after {
content: '▲';
}
input[type="text"] {
background: transparent !important;
border: 0;
line-height: 36px;
border-bottom: 2px solid #fff;
font-size: 16px;
color: #fff;
outline: none;
}
input[type="text"]:focus {
border-bottom: 2px solid #594CD7;
}
.content {
margin: 112px 178px;
}
.block {
margin-bottom: 40px;
}
.header {
font-size: 32px;
margin-bottom: 8px;
}
.h1 {
font-size: 40px;
line-height: 64px;
font-style: normal;
font-weight: bold;
}
.p {
font-size: 16px;
line-height: 32px;
font-style: normal;
font-weight: normal;
}
.input {
border: none;
padding: 4px 8px;
border-bottom: 2px solid #00000066;
width: 366px;
}
.input:focus-visible {
outline: none;
}
.input:focus {
border-bottom: 2px solid #594CD7;
}
.label_label {
display: inline-block;
width: 108px;
font-size: 16px;
padding: 4px 0 0 2px;
}
.callapp_local_video {
display: none;
}
.hidden {
display: none;
}
.income-video {
display: inline-block;
margin: 20px 8px 0 0;
}
.income-video-id {
position: absolute;
z-index: 1;
}
.dropdown-field__tooltip {
display: none;
}
.dropdown-field--opened .dropdown-field__tooltip {
display: block;
position: absolute;
background: #fff;
border: 2px solid #594CD7;
z-index: 1;
padding: 8px 0;
margin-top: -2px;
min-width: 362px;
}
.dropdown-item {
padding: 8px 20px;
font-size: 14px;
line-height: 20px;
/* margin-bottom: 8px; */
}
.dropdown-item:hover {
background: #594CD7;
color: #fff;
cursor: pointer;
}
.form-field__label-text {
font-size: 24px;
font-weight: bold;
margin: 40px 0 4px;
display: block;
line-height: 32px;
}
.form-field {
margin: 2px 0 12px;
}
.canvas-resizer canvas {
display: block;
}
.canvas-resizer {
float: right;
position: relative;
top: 66px;
}
.description.gray {
color: #5C5C5C;
}
.description {
font-size: 14px;
line-height: 24px;
}
</style>
</head>
<body>
<div id="videoinputapp1">
<!-- URL:
<span class="callapp_url">
</span>-->
<div class="main-content hidden">
<input type="checkbox" name="audio" class="callapp_send_audio" checked autocomplete="off"> Audio
<input type="checkbox" name="video" class="callapp_send_video" checked autocomplete="off"> Video
<div class="callapp_local_video">My camera feed</div>
<div class="callapp_remote_video">Remote video</div>
</div>
</div>
<!--<canvas id="canvas1"> </canvas>-->
<div class="container">
<video class="input_video" style="display: none"></video>
</div>
<div id="content"></div>
<script src="view/Select.js"></script>
<script src="view/Input.js"></script>
<script src="view/CanvasResizer.js"></script>
<script src="view/Screen.js"></script>
<script src="view/MainScreen.js"></script>
<script src="view/CallScreen.js"></script>
<script src="app.js"></script>
<script src="./bundle/apps.js"></script>
<script>
store.set('appLoaded', true);
D.ext(D('.callapp_button')[0], {onclick: function() {
}});
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./bundle/apps.js"></script>
<button onclick="apps.BrowserMediaNetwork_TestLocalCamera()">BrowserMediaNetwork_TestLocalCamera</button><br>
<button onclick="apps.BrowserMediaNetwork_frameaccess()">BrowserMediaNetwork_frameaccess</button><br>
<button onclick="apps.WebsocketNetwork_sharedaddress()">WebsocketNetwork_sharedaddress</button><br>
<button onclick="apps.WebsocketNetwork_test1()">WebsocketNetwork_test1</button><br>
<button onclick="apps.CAPI_WebRtcNetwork_testapp()">CAPI_WebRtcNetwork_testapp</button><br>
<button onclick="apps.CAPI_MediaNetwork_testapp()">CAPI_MediaNetwork_testapp</button><br>
</head>
<body>
<script>
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="./bundle/apps.js"></script>
</head>
<body>
<div id="videoinputapp1">
<h1>Callapp1:</h1>
URL to connect:
<p class="callapp_url">
</p>
<input type="checkbox" name="audio" class="callapp_send_audio" checked autocomplete="off"> Audio
<input type="checkbox" name="video" class="callapp_send_video" checked autocomplete="off"> Video
<input type= "text" class="callapp_address" autocomplete="off">
<button class="callapp_button"> Join </button>
<div class="callapp_local_video">local video</div>
<div class="callapp_remote_video">remote video</div>
</div>
<canvas id="canvas1"> </canvas>
<script>
var rgbToHex = function (rgb) {
var hex = Number(rgb).toString(16);
if (hex.length < 2) {
hex = "0" + hex;
}
return hex;
};
const canvas = document.querySelector("#canvas1");
const ctx = canvas.getContext("2d");
let counter = 0;
setInterval(()=>{
const color = "#FFFF" + rgbToHex(counter%255);
ctx.fillStyle = color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
counter++;
}, 50);
apps.videoinputapp(document.querySelector("#videoinputapp1"), canvas);
//apps.callapp(document.querySelector("#callapp2"));
</script>
</body>
</html>
\ No newline at end of file
AddCss('/view/canvasResizer.css');
const resizeMinGap = 20; //%
var CanvasResizer = function(canvas) {
var leftMover = D.div({cls: 'canvas-resizer--mover canvas-resizer--mover-left'});
var rightMover = D.div({cls: 'canvas-resizer--mover canvas-resizer--mover-right'});
var els = [leftMover, rightMover];
var position = [0, 100];
canvas.addEventListener('mousedown', function(e) {
var percent = e.offsetX/e.currentTarget.clientWidth*100,
moving;
if(percent<position[0]){
moving = 0;
}else if(percent>position[1]){
moving = 1;
}else{
moving = Math.abs(position[0] - percent) < Math.abs(position[1] - percent) ? 0 : 1;
}
var rect = D.getRect(canvas);
var move = function(e) {
var percent = ((e.clientX-rect.left)/(rect.width)*100);
percent = Math.min(percent, 100);
percent = Math.max(percent, 0);
if(moving === 0){
percent = Math.min(position[1]-resizeMinGap, percent);
}
if(moving === 1){
percent = Math.max(position[0]+resizeMinGap, percent);
}
position[moving] = ((percent*10)|0)/10;
els[0].style.width = position[0]+'%';
els[1].style.width = (100-position[1])+'%';
e.preventDefault();
e.stopPropagation();
};
var up = function(e) {
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', up);
e.preventDefault();
e.stopPropagation();
}
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', up);
});
var dom = D.div({cls: 'canvas-resizer'}, canvas, leftMover, rightMover);
canvas.position = position;
return dom;
}
\ No newline at end of file
const AddCss = function(href) {
D.h('link', {
href,
rel: 'stylesheet',
renderTo: document.head
})
};
const Input = function(cfg, bind) {
cfg.type = 'text';
cfg.onkeyup = cfg.oninput = cfg.onchange = function() {
store.set(bind, input.value);
};
const input = D.h('input', cfg);
store.sub(bind, function(val) {
input.value = val;
});
return input;
};
const LabeledInput = function(cfg, bind) {
return D.h('label', {},
D.div.apply(D, [{cls: 'label_label'}].concat(cfg.label)),
Input(cfg, bind)
)
}
\ No newline at end of file
Screen.Main = function() {
const showJoinForm = new Store.Value.Boolean(false);
const dom = D.div({cls: 'main-background'},
D.h('img', {src: 'amazon-logo.svg', cls: 'logo'}),
D.div({cls: 'sub-content'},
D.div({cls: ['main-connection-type-select', {hidden: showJoinForm}]},
D.div({cls: 'h1'}, 'Conferences made easy'),
D.div({cls: 'p'},
'Use VR calls with remote guests to immerse in conversation and',
D.h('br'),
'feel more involved'
),
D.div({cls: 'main-buttons'},
D.h('button', {cls: 'button primary', onclick: ()=>Screen.show('Conference')}, 'Create New Session'),
D.h('button', {cls: 'button primary', onclick: ()=> showJoinForm.set(true)}, 'Connect to Session'),
)
),
D.div({cls: ['join-screen', {hidden: Store.NOT(showJoinForm)}]},
D.div({cls: 'h1'}, 'Enter Session ID'),
D.div({cls: 'p'},
Input({placeholder: 'Session ID'}, 'conference_id')
),
D.div({cls: 'main-buttons'},
D.h('button', {cls: 'button primary', onclick: ()=>Screen.show('Conference')}, 'Connect to Session'),
D.h('button', {cls: 'button secondary', onclick: ()=> showJoinForm.set(false)}, 'Go Back'),
)
)
)
);
return dom;
};
\ No newline at end of file
const Screen = {
cache: {},
show: function(name) {
const renderEl = document.getElementById('content');
D.removeChildren(renderEl);
if(!(name in Screen.cache)){
Screen.cache[ name ] = Screen[ name ]();
}
renderEl.appendChild(Screen.cache[name]);
}
};
\ No newline at end of file
var Select = function(cfg) {
let isOpen = this.isOpen = new Store.Value.Boolean(false);
var table = D.div({cls: 'dropdown-list'}, _=>{
store.sub(cfg.values, function(val) {
if(val){
_(val.map(function(v) {
return D.div({cls: 'dropdown-item', 'data-id': v.value, onclick: function() {
cfg.bind.set(v.value);
isOpen.set(false);
}}, v.text)
}));
}
});
});
var dom = D.div({cls: 'form-field'},
D.div({cls: 'form-field__label'},
D.span({cls: 'form-field__label-text'}, cfg.label),
D.div({cls: ['dropdown-field', {
'dropdown-field--opened': isOpen
}]},
D.h('button', {
cls: 'dropdown-field__toggler',
onclick: () => isOpen.toggle()
},
D.span({cls: D.cls(
"dropdown-field__placeholder", {
"dropdown-field__placeholder--filled": _=>cfg.bind.sub(val=>_(!!(cfg.multivalue ? val && val.length : val && val.name)))
} )},
_ => {
cfg.bind.sub( val => {
if( val ){
var vals = store.get(cfg.values) || [],
theVal = vals.filter(v=>v.value === val)[0];
_(theVal?theVal.text: 'Select')
}else{
_( cfg.placeholder );
}
} );
}
)
),
D.div({cls: 'dropdown-field__tooltip'},
table
)
)
)
);
return dom;
}
\ No newline at end of file
.canvas-resizer--mover {
position: absolute;
top: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
cursor: ew-resize;
pointer-events: none;
}
.canvas-resizer {
display: none;
}
.canvas-resizer canvas {
cursor: ew-resize;
}
.canvas-resizer--mover-left {
left: 0;
width: 0px;
border-right: 2px solid #594CD7;
}
.canvas-resizer--mover-right {
right: 0;
width: 0px;
border-left: 2px solid #594CD7;
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebGL Demo</title>
<link rel="stylesheet" href="../webgl.css" type="text/css">
</head>
<body>
<canvas id="glcanvas" width="640" height="480"></canvas>
</body>
<script src="../bundle/awrtc.js"></script>
<script src="gl-matrix.js"></script>
<script src="webgl-demo_changed.js"></script>
<script>
let canvas = document.querySelector("#glcanvas");
let nconfig = new awrtc.NetworkConfig();
let call = new awrtc.BrowserWebRtcCall(nconfig);
call.addEventListener((sender, args) => {
if(args.Type === awrtc.CallEventType.FrameUpdate)
{
let gl = canvas.getContext("webgl");
if(args.Frame.Width != globalTextureWidth || args.Frame.Height != globalTextureHeight)
{
const pixel = new Uint8Array(args.Frame.Width * args.Frame.Height * 3 );
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, args.Frame.Width, args.Frame.Height, 0, gl.RGB, gl.UNSIGNED_BYTE, pixel);
globalTextureWidth = args.Frame.Width ;
globalTextureHeight = args.Frame.Height ;
}
args.Frame.ToTexture(gl, globalTextureId);
}
});
//As the system is designed for realtime graphics we have to call the Update method. Events are only
//triggered during this Update call!
let intervalId = setInterval(() => {
call.Update();
}, 50);
let config = new awrtc.MediaConfig();
config.Audio = false;
config.Video = true;
config.FrameUpdates = true;
config.IdealWidth = 640;
config.IdealHeight = 480;
config.IdealFps = 30;
console.log("requested config:" + JSON.stringify(config));
call.Configure(config);
</script>
</html>
\ No newline at end of file
Slightly changed WebGL example from mozzila to test the frame copy from WebRTC -> VideoElement -> WebGL Texture
Source:
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Using_textures_in_WebGL
https://github.com/mdn/webgl-examples/tree/gh-pages/tutorial/sample6
\ No newline at end of file
// Karma configuration
// Generated on Mon Jun 24 2019 19:59:32 GMT+1200 (New Zealand Standard Time)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'build/bundle/*.js'
],
// list of files / patterns to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['FirefoxCustom'],
customLaunchers: {
FirefoxCustom: {
base: 'Firefox',
prefs: {
'media.navigator.permission.disabled': true,
'media.navigator.streams.fake' : true
}
}
},
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "awrtc_browser",
"version": "1.985.2",
"description": "Compatible browser implementation to the Unity asset WebRTC Video Chat. Try examples in build folder",
"author": "because-why-not.com Limited",
"license": "BSD-3-Clause",
"main": "build/bundle/awrtc.js",
"types": "build/awrtc/index.d.ts",
"scripts": {
"tsc": "tsc",
"webpack": "webpack",
"build": "webpack && tsc -p ./src/awrtc",
"watch": "webpack --watch",
"clean": "shx rm -rf ./build/awrtc ./build/bundle"
},
"files": [
"build",
"src"
],
"repository": {
"type": "git",
"url": "https://github.com/because-why-not/awrtc_browser"
},
"devDependencies": {
"@types/jasmine": "^2.8.17",
"jasmine": "^2.99.0",
"jasmine-core": "^3.6.0",
"karma": "^6.3.3",
"karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.3.0",
"karma-jasmine": "^2.0.1",
"shx": "^0.3.2",
"source-map-loader": "^0.2.4",
"ts-loader": "^5.4.5",
"tsconfig-paths-webpack-plugin": "^3.3.0",
"typescript": "^3.9.7",
"uglify-js": "^2.8.29",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webrtc-adapter": "^7.x"
}
}
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import * as awrtc from "../awrtc";
/**
* Contains default values / servers used for example and test apps.
*
* Note that these servers might not be online forever. Feel free to
* run your own servers and update the url's below.
*/
export class DefaultValues
{
private static SignalingUrl= "ws://signaling.because-why-not.com";
private static SecureSignalingUrl= "wss://signaling.because-why-not.com";
private static get SignalingBase():string
{
if (window.location.protocol != "https:") {
return DefaultValues.SignalingUrl;
} else
{
return DefaultValues.SecureSignalingUrl;
}
}
/**
* Returns the signaling server URL using ws for http pages and
* wss for https. The server url here ends with "test" to avoid
* clashes with existing callapp.
*/
public static get Signaling():string
{
return DefaultValues.SignalingBase + "/test";
}
/**
* Returns the signaling server URL using ws for http pages and
* wss for https. The server url here ends with "testshared" to avoid
* clashes with existing conference app.
* This url of the server usually allows shared addresses for
* n to n connections / conference calls.
*/
public static get SignalingShared():string
{
return DefaultValues.SignalingBase + "/testshared";
}
private static get StunServer() : RTCIceServer
{
let res : RTCIceServer = {
urls: "stun:stun.l.google.com:19302"
};
return res;
}
/**
* Returns ice servers used for testing.
* Might only return the free google stun server. Without an
* additional turn server connections might fail due to firewall.
* Server might be unavailable in China.
*/
public static get IceServers(): RTCIceServer[]
{
return [DefaultValues.StunServer];
}
}
//
export function GetParameterByName(name : string, url?:string) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
//Returns a random string
export function GetRandomKey(): string {
var result = "";
for (var i = 0; i < 7; i++) {
result += String.fromCharCode(65 + Math.round(Math.random() * 25));
}
return result;
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import * as apps from "./index"
import * as awrtc from "../awrtc/index"
(window as any).awrtc = awrtc;
(window as any).apps = apps;
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
export * from "./apphelpers"
export * from "./testapps"
export * from "./examples"
export * from "./callapp"
export * from "./videoinputapp"
\ No newline at end of file
{
"extends": "../awrtc/tsconfig_base",
"compilerOptions": {
"declaration": false,
"target": "es6",
"module": "es2015",
"outDir": "../../build/apps",
"lib": [
"es2015",
"dom"
]
},
"files": [
"callapp.ts"
]
}
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//obsolete. not needed for unity build anymore
//special entry point only needed for backwards compatibility
//it will merge awrtc namespace into window so old code still works
//that accesses objects directly instead using the global awrtc object
//the index will include all external modules
import * as awrtc from "./index"
//we merge awrtc into the global namespace
Object.assign(window, awrtc);
//for less risky global access
(window as any).awrtc = awrtc;
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
console.debug("loading awrtc modules ...");
//this should trigger webpack to include
//the webrtc adapter.js. It changes the
//global WebRTC calls and adds backwards
//and browser compatibility.
declare function require(moduleName: string)
let adapter = require("webrtc-adapter");
export * from "./network/index"
export * from "./media/index"
//for simplicity browser and unity are merged here
//it could as well be built and deployed separately
export * from "./media_browser/index"
export * from "./unity/index"
console.debug("loading awrtc modules completed!");
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { ConnectionId } from "../network/index";
import { IFrameData } from "./RawFrame";
export interface CallEventHandler {
(sender: any, args: CallEventArgs): void;
}
/// <summary>
/// Type of the event.
/// </summary>
export enum CallEventType {
/// <summary>
/// Used if the event value wasn't initialized
/// </summary>
Invalid = 0,
/// <summary>
/// The call object is successfully connected to the server waiting for another user
/// to connect.
/// </summary>
WaitForIncomingCall = 1,
/// <summary>
/// A call was accepted
/// </summary>
CallAccepted = 2,
/// <summary>
/// The call ended
/// </summary>
CallEnded = 3,
/**
* Backwards compatibility. Use MediaUpdate
*/
FrameUpdate = 4,
/// <summary>
/// Text message arrived
/// </summary>
Message = 5,
/// <summary>
/// Connection failed. Might be due to an server, network error or the address didn't exist
/// Using ErrorEventArgs
/// </summary>
ConnectionFailed = 6,
/// <summary>
/// Listening failed. Address might be in use or due to server/network error
/// Using ErrorEventArgs
/// </summary>
ListeningFailed = 7,
/// <summary>
/// Event triggered after the local media was successfully configured.
/// If requested the call object will have access to the users camera and/or audio now and
/// the local camera frames can be received in events.
/// </summary>
ConfigurationComplete = 8,
/// <summary>
/// Configuration failed. This happens if the configuration requested features
/// the system doesn't support e.g. no camera, camera doesn't support the requested resolution
/// or the user didn't allow the website to access the camera/microphone in WebGL mode.
/// </summary>
ConfigurationFailed = 9,
/// <summary>
/// Reliable or unreliable data msg arrived
/// </summary>
DataMessage = 10,
/**
*
*/
MediaUpdate = 20,
}
export class CallEventArgs {
private mType = CallEventType.Invalid;
public get Type(): CallEventType {
return this.mType;
}
public constructor(type: CallEventType) {
this.mType = type;
}
}
export class CallAcceptedEventArgs extends CallEventArgs
{
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
public constructor(connectionId: ConnectionId) {
super(CallEventType.CallAccepted);
this.mConnectionId = connectionId;
}
}
export class CallEndedEventArgs extends CallEventArgs {
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId{
return this.mConnectionId;
}
public constructor(connectionId: ConnectionId) {
super(CallEventType.CallEnded);
this.mConnectionId = connectionId;
}
}
export enum CallErrorType {
Unknown
}
export class ErrorEventArgs extends CallEventArgs {
private mErrorMessage: string;
public get ErrorMessage(): string {
return this.mErrorMessage;
}
private mErrorType: CallErrorType = CallErrorType.Unknown;
public get ErrorType(): CallErrorType {
return this.mErrorType;
}
public constructor(eventType: CallEventType, type?: CallErrorType, errorMessage?: string) {
super(eventType);
this.mErrorType = type;
this.mErrorMessage = errorMessage;
if (this.mErrorMessage == null) {
switch (eventType) {
//use some generic error messages as the underlaying system doesn't report the errors yet.
case CallEventType.ConnectionFailed:
this.mErrorMessage = "Connection failed.";
break;
case CallEventType.ListeningFailed:
this.mErrorMessage = "Failed to allow incoming connections. Address already in use or server connection failed.";
break;
default:
this.mErrorMessage = "Unknown error.";
break;
}
}
}
}
export class WaitForIncomingCallEventArgs extends CallEventArgs
{
private mAddress: string;
public get Address(): string {
return this.mAddress;
}
public constructor(address: string) {
super(CallEventType.WaitForIncomingCall);
this.mAddress = address;
}
}
export class MessageEventArgs extends CallEventArgs {
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
private mContent: string;
public get Content(): string {
return this.mContent;
}
private mReliable: boolean;
public get Reliable(): boolean {
return this.mReliable;
}
public constructor(id: ConnectionId, message: string, reliable: boolean) {
super(CallEventType.Message);
this.mConnectionId = id;
this.mContent = message;
this.mReliable = reliable;
}
}
export class DataMessageEventArgs extends CallEventArgs {
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
private mContent: Uint8Array;
public get Content(): Uint8Array {
return this.mContent;
}
private mReliable: boolean;
public get Reliable(): boolean {
return this.mReliable;
}
public constructor(id: ConnectionId, message: Uint8Array, reliable: boolean) {
super(CallEventType.DataMessage);
this.mConnectionId = id;
this.mContent = message;
this.mReliable = reliable;
}
}
/**
* Replaces the FrameUpdateEventArgs. Instead of
* giving access to video frames only this gives access to
* video html tag once it is created.
* TODO: Add audio + video tracks + flag that indicates added, updated or removed
* after renegotiation is added.
*/
export class MediaUpdatedEventArgs extends CallEventArgs
{
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
/// <summary>
/// False if the frame is from a local camera. True if it is received from
/// via network.
/// </summary>
public get IsRemote(): boolean {
return this.mConnectionId.id != ConnectionId.INVALID.id;
}
private mVideoElement:HTMLVideoElement;
public get VideoElement():HTMLVideoElement
{
return this.mVideoElement;
}
public constructor(conId: ConnectionId, videoElement:HTMLVideoElement)
{
super(CallEventType.MediaUpdate);
this.mConnectionId = conId;
this.mVideoElement = videoElement;
}
}
/// <summary>
/// Will be replaced with MediaUpdatedEventArgs.
/// It doesn't make a lot of sense in HTML only
/// </summary>
export class FrameUpdateEventArgs extends CallEventArgs {
private mFrame: IFrameData;
/// <summary>
/// Raw image data. Note that the byte array contained in RawFrame will be reused
/// for the next frames received. Only valid until the next call of ICall.Update
/// </summary>
public get Frame(): IFrameData {
return this.mFrame;
}
private mConnectionId: ConnectionId = ConnectionId.INVALID;
public get ConnectionId(): ConnectionId {
return this.mConnectionId;
}
/// <summary>
/// False if the frame is from a local camera. True if it is received from
/// via network.
/// </summary>
public get IsRemote(): boolean {
return this.mConnectionId.id != ConnectionId.INVALID.id;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="conId"></param>
/// <param name="frame"></param>
public constructor(conId: ConnectionId, frame: IFrameData)
{
super(CallEventType.FrameUpdate);
this.mConnectionId = conId;
this.mFrame = frame;
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { CallEventHandler } from "./CallEventArgs";
import { ConnectionId } from "../network/index";
import { MediaConfig } from "./MediaConfig";
/** Mostly the same as the C# side ICall interface.
*
* Usage of this interface usually follows a specific pattern:
* 1. Create a platform specific instance via a factory with a specific
* NetworkConfig
* 2. Register an event handler at CallEvent and call Update regularly
* (ideally once for each frame shown to the user in realtime
* applcations so 30-60 times per second)
* 3. Call configure with your own MediaConfig instance defining what
* features you need.
* 4. Wait for a ConfigurationComplete (or failed) event. During this
* time the platform might ask the user the allow access to the devices.
* 5. Either call Listen with an address to wait for an incoming connection
* or use Call to conect another ICall that already listens on that address.
* 6. Wait for CallAccepted and other events. The call is now active and
* you can use Send messages, change volume, ...
* 7. Call Dispose to cleanup
*
* Do not forget to call Dispose method after you finished the call or the connection
* might run forever in the background!
*
* See example apps and guides for more information.
*/
export interface ICall {
addEventListener(listener: CallEventHandler): void;
removeEventListener(listener: CallEventHandler): void;
Call(address: string): void;
Configure(config: MediaConfig): void;
Listen(address: string): void;
Send(message: string, reliable?:boolean, id? :ConnectionId): void
SendData(message: Uint8Array, reliable: boolean, id: ConnectionId): void
Update(): void;
Dispose(): void;
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { ConnectionId, IWebRtcNetwork } from "../network/index";
import { MediaConfig } from "./MediaConfig";
import { IFrameData } from "./RawFrame";
export enum MediaConfigurationState {
Invalid = 0,
NoConfiguration = 1,
InProgress = 2,
Successful = 3,
Failed = 4
}
export enum MediaEventType
{
Invalid = 0,
StreamAdded = 20
}
/**
* Will replace frame event / configuration system in the future.
*
* So far it only delivers HTMLVideoElements once connection and
* all tracks are ready and it plays.
*
* This is all temporary and will be updated soon to handle
* all events from configuration of local streams to frame updates and
* renegotation.
*
*/
export class MediaEvent
{
private mEventType = MediaEventType.Invalid;
public get EventType()
{
return this.mEventType;
}
private mConnectionId = ConnectionId.INVALID;
public get ConnectionId()
{
return this.mConnectionId;
}
private mArgs:any;
public get Args():any
{
return this.mArgs;
}
public constructor(type:MediaEventType, id: ConnectionId, args:any)
{
this.mEventType = type;
this.mConnectionId = id;
this.mArgs = args;
}
}
/**Interface adds media functionality to IWebRtcNetwork.
* It is used to ensure compatibility to all other platforms.
*/
export interface IMediaNetwork extends IWebRtcNetwork
{
Configure(config: MediaConfig): void;
GetConfigurationState(): MediaConfigurationState;
GetConfigurationError(): string;
ResetConfiguration(): void;
TryGetFrame(id: ConnectionId): IFrameData;
PeekFrame(id: ConnectionId): IFrameData;
SetVolume(volume: number, id: ConnectionId): void;
HasAudioTrack(id: ConnectionId): boolean;
HasVideoTrack(id: ConnectionId): boolean;
//Only used for browser specific events for now
//Not part of the C# api yet.
DequeueMediaEvent(): MediaEvent;
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/// <summary>
/// Configuration for the WebRtcCall class.
///
/// Allows to turn on / off video and audio + configure the used servers to initialize the connection and
/// avoid firewalls.
/// </summary>
export class MediaConfig {
private mAudio: boolean = true;
public get Audio(): boolean {
return this.mAudio;
}
public set Audio(value: boolean) {
this.mAudio = value;
}
private mVideo: boolean = true;
public get Video(): boolean {
return this.mVideo;
}
public set Video(value: boolean) {
this.mVideo = value;
}
private mVideoDeviceName : string = "";
public get VideoDeviceName(): string {
return this.mVideoDeviceName;
}
public set VideoDeviceName(value: string) {
this.mVideoDeviceName = value;
}
private mMinWidth = -1;
public get MinWidth(): number {
return this.mMinWidth;
}
public set MinWidth(value: number) {
this.mMinWidth = value;
}
private mMinHeight = -1;
public get MinHeight(): number {
return this.mMinHeight;
}
public set MinHeight(value: number) {
this.mMinHeight = value;
}
private mMaxWidth = -1;
public get MaxWidth(): number {
return this.mMaxWidth;
}
public set MaxWidth(value: number) {
this.mMaxWidth = value;
}
private mMaxHeight = -1;
public get MaxHeight(): number {
return this.mMaxHeight;
}
public set MaxHeight(value: number) {
this.mMaxHeight = value;
}
private mIdealWidth = -1;
public get IdealWidth(): number {
return this.mIdealWidth;
}
public set IdealWidth(value: number) {
this.mIdealWidth = value;
}
private mIdealHeight = -1;
public get IdealHeight(): number {
return this.mIdealHeight;
}
public set IdealHeight(value: number) {
this.mIdealHeight = value;
}
private mMinFps = -1;
public get MinFps(): number {
return this.mMinFps;
}
public set MinFps(value: number) {
this.mMinFps = value;
}
private mMaxFps = -1;
public get MaxFps(): number {
return this.mMaxFps;
}
public set MaxFps(value: number) {
this.mMaxFps = value;
}
private mIdealFps = -1;
public get IdealFps(): number {
return this.mIdealFps;
}
public set IdealFps(value: number) {
this.mIdealFps = value;
}
private mFrameUpdates = false;
/** false - frame updates aren't generated. Useful for browser mode
* true - library will deliver frames as ByteArray
*/
public get FrameUpdates(): boolean {
return this.mFrameUpdates;
}
public set FrameUpdates(value: boolean) {
this.mFrameUpdates = value;
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
export class NetworkConfig {
private mIceServers = new Array<RTCIceServer>();
public get IceServers(): RTCIceServer[] {
return this.mIceServers;
}
public set IceServers(value: RTCIceServer[]) {
this.mIceServers = value;
}
private mSignalingUrl = "ws://because-why-not.com:12776";
public get SignalingUrl() {
return this.mSignalingUrl;
}
public set SignalingUrl(value: string) {
this.mSignalingUrl = value;
}
private mIsConference = false;
public get IsConference(): boolean {
return this.mIsConference;
}
public set IsConference(value:boolean) {
this.mIsConference = value;
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { SLog } from "../network/Helper";
import { BrowserMediaStream } from "../media_browser/index";
export enum FramePixelFormat {
Invalid = 0,
Format32bppargb
}
//replace with interface after typescript 2.0 update (properties in interfaces aren't supported yet)
export class IFrameData {
public get Format(): FramePixelFormat{
return FramePixelFormat.Format32bppargb;
}
public get Buffer(): Uint8Array {
return null;
}
public get Width(): number {
return -1;
}
public get Height(): number {
return -1;
}
public constructor() { }
public ToTexture(gl: WebGL2RenderingContext, texture: WebGLTexture) : boolean{
return false;
}
/*
public ToTexture2(gl: WebGL2RenderingContext) : WebGLTexture{
return null;
}
*/
}
//Container for the raw bytes of the current frame + height and width.
//Format is currently fixed based on the browser getImageData format
export class RawFrame extends IFrameData{
private mBuffer: Uint8Array = null;
public get Buffer(): Uint8Array {
return this.mBuffer;
}
private mWidth: number;
public get Width(): number {
return this.mWidth;
}
private mHeight: number;
public get Height(): number {
return this.mHeight;
}
constructor(buffer: Uint8Array, width: number, height: number) {
super();
this.mBuffer = buffer;
this.mWidth = width;
this.mHeight = height;
}
}
/**
* This class is suppose to increase the speed of the java script implementation.
* Instead of creating RawFrames every Update call (because the real fps are unknown currently) it will
* only create a lazy frame which will delay the creation of the RawFrame until the user actually tries
* to access any data.
* Thus if the game slows down or the user doesn't access any data the expensive copy is avoided.
*
* This comes with the downside of risking a change in Width / Height at the moment. In theory the video could
* change the resolution causing the values of Width / Height to change over time before Buffer is accessed to create
* a copy that will be save to use. This should be ok as long as the frame is used at the time it is received.
*/
export class LazyFrame extends IFrameData{
private mFrameGenerator: BrowserMediaStream;
public get FrameGenerator() {
return this.mFrameGenerator;
}
private mRawFrame: RawFrame;
public get Buffer(): Uint8Array {
this.GenerateFrame();
if (this.mRawFrame == null)
return null;
return this.mRawFrame.Buffer;
}
/**Returns the expected width of the frame.
* Watch out this might change inbetween frames!
*
*/
public get Width(): number {
if (this.mRawFrame == null)
{
return this.mFrameGenerator.VideoElement.videoWidth;
}else{
return this.mRawFrame.Width;
}
/*
this.GenerateFrame();
if (this.mRawFrame == null)
return -1;
return this.mRawFrame.Width;
*/
}
/**Returns the expected height of the frame.
* Watch out this might change inbetween frames!
*
*/
public get Height(): number {
if (this.mRawFrame == null)
{
return this.mFrameGenerator.VideoElement.videoHeight;
}else{
return this.mRawFrame.Height;
}
/*
this.GenerateFrame();
if (this.mRawFrame == null)
return -1;
return this.mRawFrame.Height;
*/
}
constructor(frameGenerator: BrowserMediaStream) {
super();
this.mFrameGenerator = frameGenerator;
}
/**Intendet for use via the Unity plugin.
* Will copy the image directly into a texture to avoid overhead of a CPU side copy.
*
* The given texture should have the correct size before calling this method.
*
* @param gl
* @param texture
*/
public ToTexture(gl: WebGL2RenderingContext, texture: WebGLTexture) : boolean{
gl.bindTexture(gl.TEXTURE_2D, texture);
/*
const level = 0;
const internalFormat = gl.RGBA;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, srcFormat, srcType, this.mFrameGenerator.VideoElement);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
*/
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGB, gl.UNSIGNED_BYTE, this.mFrameGenerator.VideoElement);
return true;
}
/*
public ToTexture2(gl: WebGL2RenderingContext) : WebGLTexture{
let tex = gl.createTexture()
this.ToTexture(gl, tex)
return;
}
*/
//Called before access of any frame data triggering the creation of the raw frame data
private GenerateFrame() {
if (this.mRawFrame == null) {
try {
this.mRawFrame = this.mFrameGenerator.CreateFrame();
} catch (exception) {
this.mRawFrame = null;
SLog.LogWarning("frame skipped in GenerateFrame due to exception: " + JSON.stringify(exception));
}
}
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
export * from './AWebRtcCall'
export * from './CallEventArgs'
export * from './ICall'
export * from './IMediaNetwork'
export * from './MediaConfig'
export * from './NetworkConfig'
export * from './RawFrame'
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { AWebRtcCall } from "../media/AWebRtcCall";
import { NetworkConfig } from "../media/NetworkConfig";
import { IMediaNetwork } from "../media/IMediaNetwork";
import { BrowserMediaNetwork } from "./BrowserMediaNetwork";
/**Browser version of the C# version of WebRtcCall.
*
* See ICall interface for detailed documentation.
* BrowserWebRtcCall mainly exists to allow other versions
* in the future that might build on a different IMediaNetwork
* interface (Maybe something running inside Webassembly?).
*/
export class BrowserWebRtcCall extends AWebRtcCall {
public constructor(config: NetworkConfig) {
super(config);
this.Initialize(this.CreateNetwork());
}
private CreateNetwork(): IMediaNetwork {
return new BrowserMediaNetwork(this.mNetworkConfig);
}
protected DisposeInternal(disposing: boolean): void {
super.DisposeInternal(disposing);
if (disposing) {
if (this.mNetwork != null)
this.mNetwork.Dispose();
this.mNetwork = null;
}
}
}
\ No newline at end of file
import { DeviceApi } from "./DeviceApi";
import { VideoInput } from "./VideoInput";
import { MediaConfig } from "media/MediaConfig";
export class Media{
//experimental. Will be used instead of the device api to create streams
private static sSharedInstance :Media = new Media();
/**
* Singleton used for now as the browser version is missing a proper factory yet.
* Might be removed later.
*/
public static get SharedInstance(){
return this.sSharedInstance;
}
public static ResetSharedInstance(){
this.sSharedInstance = new Media();
}
private videoInput: VideoInput = null;
public get VideoInput() : VideoInput{
if(this.videoInput === null)
this.videoInput = new VideoInput();
return this.videoInput;
}
public constructor(){
}
public GetVideoDevices(): string[] {
const real_devices = DeviceApi.GetVideoDevices();
const virtual_devices : string[] = this.VideoInput.GetDeviceNames();
return real_devices.concat(virtual_devices);
}
public static IsNameSet(videoDeviceName: string) : boolean{
if(videoDeviceName !== null && videoDeviceName !== "" )
{
return true;
}
return false;
}
public getUserMedia(config: MediaConfig): Promise<MediaStream>{
if(config.Video && Media.IsNameSet(config.VideoDeviceName)
&& this.videoInput != null
&& this.videoInput.HasDevice(config.VideoDeviceName))
{
let res = Promise.resolve().then(async ()=>{
let stream = this.videoInput.GetStream(config.VideoDeviceName);
if(config.Audio)
{
let constraints = {} as MediaStreamConstraints
constraints.audio = true;
let audio_stream = await DeviceApi.getBrowserUserMedia(constraints);
stream.addTrack(audio_stream.getTracks()[0])
}
return stream;
})
return res;
}
return DeviceApi.getAssetUserMedia(config);
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { WebRtcDataPeer, SLog } from "../network/index";
import { BrowserMediaStream } from "./BrowserMediaStream";
import { IFrameData } from "../media/RawFrame";
//TODO: Not part of typescript as it is obsolete but
//chrome might still need this
export interface RTCMediaStreamEvent extends Event {
stream: MediaStream;
}
export interface RTCPeerConnectionObsolete extends RTCPeerConnection
{
onaddstream: ((this: RTCPeerConnection, streamEvent: RTCMediaStreamEvent) => any) | null;
addStream(stream: MediaStream): void;
}
export class MediaPeer extends WebRtcDataPeer
{
private mRemoteStream: BrowserMediaStream = null;
//quick workaround to allow html user to get the HTMLVideoElement once it is
//created. Might be done via events later to make wrapping to unity/emscripten possible
public InternalStreamAdded: (peer:MediaPeer, stream: BrowserMediaStream) => void = null;
//true - will use obsolete onstream / add stream
//false - will use ontrack / addtrack (seems to work fine now even on chrome)
public static sUseObsolete = false;
protected OnSetup(): void {
super.OnSetup();
//TODO: test in different browsers if boolean works now
//this is unclear in the API. according to typescript they are boolean, in native code they are int
//and some browser failed in the past if boolean was used ...
this.mOfferOptions = { "offerToReceiveAudio": true, "offerToReceiveVideo": true };
if(MediaPeer.sUseObsolete) {
SLog.LW("Using obsolete onaddstream as not all browsers support ontrack");
(this.mPeer as RTCPeerConnectionObsolete).onaddstream = (streamEvent: RTCMediaStreamEvent) => { this.OnAddStream(streamEvent); };
}
else{
this.mPeer.ontrack = (ev:RTCTrackEvent)=>{this.OnTrack(ev);}
}
}
protected OnCleanup() {
super.OnCleanup();
if (this.mRemoteStream != null) {
this.mRemoteStream.Dispose();
this.mRemoteStream = null;
}
}
private OnAddStream(streamEvent: RTCMediaStreamEvent) {
this.SetupStream(streamEvent.stream);
}
private OnTrack(ev:RTCTrackEvent){
if(ev && ev.streams && ev.streams.length > 0)
{
//this is getting called twice if audio and video is active
if(this.mRemoteStream == null)
this.SetupStream(ev.streams[0]);
}else{
SLog.LE("Unexpected RTCTrackEvent: " + JSON.stringify(ev));
}
}
private SetupStream(stream:MediaStream)
{
this.mRemoteStream = new BrowserMediaStream(stream);
//trigger events once the stream has its meta data available
this.mRemoteStream.InternalStreamAdded = (stream) =>{
if(this.InternalStreamAdded != null)
{
this.InternalStreamAdded(this, stream);
}
};
}
public TryGetRemoteFrame(): IFrameData
{
if (this.mRemoteStream == null)
return null;
return this.mRemoteStream.TryGetFrame();
}
public PeekFrame(): IFrameData {
if (this.mRemoteStream == null)
return null;
return this.mRemoteStream.PeekFrame();
}
public AddLocalStream(stream: MediaStream) {
if(MediaPeer.sUseObsolete) {
(this.mPeer as RTCPeerConnectionObsolete).addStream(stream);
}
else{
for(let v of stream.getTracks())
{
this.mPeer.addTrack(v, stream);
}
}
}
public Update() {
super.Update();
if (this.mRemoteStream != null) {
this.mRemoteStream.Update();
}
}
public SetVolume(volume: number): void {
if (this.mRemoteStream != null)
this.mRemoteStream.SetVolume(volume);
}
public HasAudioTrack(): boolean {
if (this.mRemoteStream != null)
return this.mRemoteStream.HasAudioTrack();
return false;
}
public HasVideoTrack(): boolean {
if (this.mRemoteStream != null)
return this.mRemoteStream.HasVideoTrack();
return false;
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
export * from './BrowserMediaNetwork'
export * from './BrowserWebRtcCall'
export * from './BrowserMediaStream'
export * from './MediaPeer'
export * from './DeviceApi'
export * from './VideoInput'
export * from './Media'
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**Contains some helper classes to keep the typescript implementation
* similar to the C# implementation.
*
*/
export class Queue<T> {
private mArr: Array<T> = new Array<T>();
constructor() {
}
public Enqueue(val: T) {
this.mArr.push(val);
}
public TryDequeue(outp: Output<T> ): boolean{
var res = false
if (this.mArr.length > 0) {
outp.val = this.mArr.shift();
res = true;
}
return res;
}
public Dequeue(): T {
if (this.mArr.length > 0) {
return this.mArr.shift();
} else {
return null;
}
}
public Peek(): T {
if (this.mArr.length > 0) {
return this.mArr[0];
} else {
return null;
}
}
public Count(): number{
return this.mArr.length;
}
public Clear():void
{
this.mArr = new Array<T>();
}
}
export class List<T> {
private mArr: Array<T> = new Array<T>();
public get Internal() : Array<T>
{
return this.mArr;
}
constructor() {
}
public Add(val: T) {
this.mArr.push(val);
}
public get Count(): number {
return this.mArr.length;
}
}
export class Output<T>
{
public val : T;
}
export class Debug {
public static Log(s: any) {
SLog.Log(s);
}
public static LogError(s: any) {
SLog.LogError(s);
}
public static LogWarning(s: any) {
SLog.LogWarning(s);
}
}
export abstract class Encoder {
public abstract GetBytes(text: string): Uint8Array;
public abstract GetString(buffer: Uint8Array): string;
}
export class UTF16Encoding extends Encoder{
constructor() {
super();
}
public GetBytes(text: string): Uint8Array {
return this.stringToBuffer(text);
}
public GetString(buffer: Uint8Array): string {
return this.bufferToString(buffer);
}
private bufferToString(buffer: Uint8Array): string {
let arr = new Uint16Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 2);
return String.fromCharCode.apply(null, arr);
}
private stringToBuffer(str: string): Uint8Array {
let buf = new ArrayBuffer(str.length * 2);
let bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
let result = new Uint8Array(buf);
return result;
}
}
export class Encoding {
public static get UTF16() {
return new UTF16Encoding();
}
constructor() {
}
}
export class Random {
public static getRandomInt(min, max): number {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
}
export class Helper {
public static tryParseInt(value : string): number {
try {
if (/^(\-|\+)?([0-9]+)$/.test(value)) {
let result = Number(value);
if (isNaN(result) == false)
return result;
}
} catch ( e) {
}
return null;
}
}
export enum SLogLevel
{
None = 0,
Errors = 1,
Warnings = 2,
Info = 3
}
//Simplified logger
export class SLog {
private static sLogLevel: SLogLevel = SLogLevel.Warnings;
public static SetLogLevel(level: SLogLevel)
{
SLog.sLogLevel = level;
}
public static RequestLogLevel(level: SLogLevel)
{
if(level > SLog.sLogLevel)
SLog.sLogLevel = level;
}
public static L(msg: any, tag?:string): void {
SLog.Log(msg, tag);
}
public static LW(msg: any, tag?:string): void {
SLog.LogWarning(msg, tag);
}
public static LE(msg: any, tag?:string): void {
SLog.LogError(msg, tag);
}
public static Log(msg: any, tag?:string): void {
if(SLog.sLogLevel >= SLogLevel.Info)
{
if(tag)
{
console.log(msg, tag);
}else{
console.log(msg);
}
}
}
public static LogWarning(msg: any, tag?:string): void {
if(!tag)
tag = "";
if(SLog.sLogLevel >= SLogLevel.Warnings)
{
if(tag)
{
console.warn(msg, tag);
}else{
console.warn(msg);
}
}
}
public static LogError(msg: any, tag?:string) {
if(SLog.sLogLevel >= SLogLevel.Errors)
{
if(tag)
{
console.error(msg, tag);
}else{
console.error(msg);
}
}
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import {ConnectionId, NetworkEvent, NetEventType} from "./index"
import { Queue } from "./Helper";
interface IIdNetworkDictionary {
[id: number]: LocalNetwork;
}
interface IAddressNetworkDictionary {
[address: string]: LocalNetwork;
}
/**Helper to simulate the WebsocketNetwork or WebRtcNetwork
* within a local application without
* any actual network components.
*
* This implementation might lack some features.
*/
export class LocalNetwork{
private static sNextId:number = 1;
private static mServers = {} as IAddressNetworkDictionary;
private mId:number;
private mNextNetworkId = new ConnectionId(1);
private mServerAddress : string = null;
private mEvents = new Queue<NetworkEvent>();
private mConnectionNetwork = {} as IIdNetworkDictionary;
private mIsDisposed = false;
public constructor() {
this.mId = LocalNetwork.sNextId;
LocalNetwork.sNextId++;
}
public get IsServer() {
return this.mServerAddress != null;
}
public StartServer(serverAddress: string = null): void
{
if (serverAddress == null)
serverAddress = "" + this.mId;
if (serverAddress in LocalNetwork.mServers) {
this.Enqueue(NetEventType.ServerInitFailed, ConnectionId.INVALID, serverAddress);
return;
}
LocalNetwork.mServers[serverAddress] = this;
this.mServerAddress = serverAddress;
this.Enqueue(NetEventType.ServerInitialized, ConnectionId.INVALID, serverAddress);
}
public StopServer() : void
{
if (this.IsServer) {
this.Enqueue(NetEventType.ServerClosed, ConnectionId.INVALID, this.mServerAddress);
delete LocalNetwork.mServers[this.mServerAddress];
this.mServerAddress = null;
}
}
public Connect(address: string): ConnectionId
{
var connectionId = this.NextConnectionId();
var sucessful = false;
if (address in LocalNetwork.mServers) {
let server = LocalNetwork.mServers[address];
if (server != null) {
server.ConnectClient(this);
//add the server as local connection
this.mConnectionNetwork[connectionId.id] = LocalNetwork.mServers[address];
this.Enqueue(NetEventType.NewConnection, connectionId, null);
sucessful = true;
}
}
if (sucessful == false) {
this.Enqueue(NetEventType.ConnectionFailed, connectionId, "Couldn't connect to the given server with id " + address);
}
return connectionId;
}
public Shutdown() : void
{
for(var id in this.mConnectionNetwork) //can be changed while looping?
{
this.Disconnect(new ConnectionId(+id));
}
//this.mConnectionNetwork.Clear();
this.StopServer();
}
public Dispose() : void{
if (this.mIsDisposed == false) {
this.Shutdown();
}
}
public SendData(userId: ConnectionId, data: Uint8Array, reliable: boolean): boolean
{
if (userId.id in this.mConnectionNetwork)
{
let net = this.mConnectionNetwork[userId.id];
net.ReceiveData(this, data, reliable);
return true;
}
return false;
}
public Update(): void
{
//work around for the GarbageCollection bug
//usually weak references are removed during garbage collection but that
//fails sometimes as others weak references get null to even though
//the objects still exist!
this.CleanupWreakReferences();
}
public Dequeue(): NetworkEvent
{
return this.mEvents.Dequeue();
}
public Peek(): NetworkEvent
{
return this.mEvents.Peek();
}
public Flush(): void
{
}
public Disconnect(id: ConnectionId): void
{
if (id.id in this.mConnectionNetwork) {
let other = this.mConnectionNetwork[id.id];
if (other != null) {
other.InternalDisconnectNetwork(this);
this.InternalDisconnect(id);
}
else {
//this is suppose to never happen but it does
//if a server is destroyed by the garbage collector the client
//weak reference appears to be NULL even though it still exists
//bug?
this.CleanupWreakReferences();
}
}
}
private FindConnectionId(network: LocalNetwork): ConnectionId
{
for(var kvp in this.mConnectionNetwork)
{
let network = this.mConnectionNetwork[kvp];
if (network != null) {
return new ConnectionId(+kvp);
}
}
return ConnectionId.INVALID;
}
private NextConnectionId(): ConnectionId
{
let res = this.mNextNetworkId;
this.mNextNetworkId = new ConnectionId(res.id + 1);
return res;
}
private ConnectClient(client: LocalNetwork): void
{
//if (this.IsServer == false)
// throw new InvalidOperationException();
let nextId = this.NextConnectionId();
//server side only
this.mConnectionNetwork[nextId.id] = client;
this.Enqueue(NetEventType.NewConnection, nextId, null);
}
private Enqueue(type: NetEventType, id: ConnectionId, data: any): void
{
let ev = new NetworkEvent(type, id, data);
this.mEvents.Enqueue(ev);
}
private ReceiveData(network: LocalNetwork, data: Uint8Array, reliable : boolean): void
{
let userId = this.FindConnectionId(network);
let buffer = new Uint8Array(data.length);
for (let i = 0; i < buffer.length; i++) {
buffer[i] = data[i];
}
let type = NetEventType.UnreliableMessageReceived;
if (reliable)
type = NetEventType.ReliableMessageReceived;
this.Enqueue(type, userId, buffer);
}
private InternalDisconnect(id: ConnectionId): void
{
if (id.id in this.mConnectionNetwork) {
this.Enqueue(NetEventType.Disconnected, id, null);
delete this.mConnectionNetwork[id.id];
}
}
private InternalDisconnectNetwork(ln: LocalNetwork): void
{
//if it can't be found it will return invalid which is ignored in internal disconnect
this.InternalDisconnect(this.FindConnectionId(ln));
}
private CleanupWreakReferences(): void
{
//foreach(var kvp in mConnectionNetwork.Keys.ToList())
//{
// var val = mConnectionNetwork[kvp];
// if (val.Get() == null) {
// InternalDisconnect(kvp);
// }
//}
}
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
export * from './INetwork'
export * from './Helper'
export * from './WebRtcPeer'
export * from './WebRtcNetwork'
export * from './WebsocketNetwork'
export * from './LocalNetwork'
\ No newline at end of file
{
"extends": "./tsconfig_base",
"compilerOptions": {
"declaration": true,
"target": "es5",
"lib" : ["ES2016", "dom"],
"module": "es2015",
"outDir": "../../build/awrtc"
}
}
{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"declaration": false,
"baseUrl": ".",
"esModuleInterop": true
},
"exclude": [
"node_modules",
"wwwroot"
],
"files": [
"./network/Helper.ts",
"./network/INetwork.ts",
"./network/WebRtcNetwork.ts",
"./network/WebRtcPeer.ts",
"./network/WebsocketNetwork.ts",
"./network/LocalNetwork.ts",
"./network/index.ts",
"./media/CallEventArgs.ts",
"./media/ICall.ts",
"./media/IMediaNetwork.ts",
"./media/MediaConfig.ts",
"./media/NetworkConfig.ts",
"./media/RawFrame.ts",
"./media/AWebRtcCall.ts",
"./media/index.ts",
"./media_browser/BrowserMediaNetwork.ts",
"./media_browser/BrowserWebRtcCall.ts",
"./media_browser/BrowserMediaStream.ts",
"./media_browser/DeviceApi.ts",
"./media_browser/MediaPeer.ts",
"./media_browser/VideoInput.ts",
"./media_browser/Media.ts",
"./media_browser/index.ts",
"./unity/CAPI.ts",
"./unity/index.ts",
"./index.ts"
]
}
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { Media } from "../media_browser/Media";
import { GetUnityCanvas } from "./CAPI";
export * from "./CAPI"
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//current setup needs to load everything as a module
export function some_random_export_1()
{
}
describe("BrowserApiTest_MediaStreamApi", () => {
beforeEach(()=>{
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
it("devices", (done) => {
navigator.mediaDevices.enumerateDevices()
.then(function(devices)
{
expect(devices).not.toBeNull();
devices.forEach(function(device) {
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
done();
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
fail();
});
});
it("devices2", (done) => {
let gStream;
let constraints = {video:{deviceId:undefined}, audio:{deviceId:undefined}} as MediaStreamConstraints;
navigator.mediaDevices.getUserMedia(constraints)
.then((stream)=>{
//if this stream stops the access to labels disapears again after
//a few ms (tested in firefox)
gStream = stream;
navigator.mediaDevices.enumerateDevices()
.then(function(devices)
{
expect(devices).not.toBeNull();
devices.forEach(function(device) {
expect(device.label).not.toBeNull();
expect(device.label).not.toBe("");
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
gStream.getTracks().forEach(t => {
t.stop();
});
done();
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
fail();
});
})
.catch((err)=>{
console.log(err.name + ": " + err.message);
fail();
});
});
it("devices3", (done) => {
let gStream;
let constraints = {video: true, audio:false} as MediaStreamConstraints;
navigator.mediaDevices.getUserMedia(constraints)
.then((stream)=>{
//if this stream stops the access to labels disapears again after
//a few ms (tested in firefox)
gStream = stream;
navigator.mediaDevices.enumerateDevices()
.then(function(devices)
{
expect(devices).not.toBeNull();
devices.forEach(function(device) {
expect(device.label).not.toBeNull();
expect(device.label).not.toBe("");
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
gStream.getTracks().forEach(t => {
t.stop();
});
done();
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
fail();
});
})
.catch((err)=>{
console.log(err.name + ": " + err.message);
fail();
});
});
});
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import {ICall, NetworkConfig, ConnectionId,
MediaConfig, BrowserWebRtcCall, CallEventType,
DataMessageEventArgs, MessageEventArgs,
CallAcceptedEventArgs, CallEventArgs } from "../awrtc/index";
export class CallTestHelper
{
static CreateCall(video:boolean, audio: boolean) : ICall {
var nconfig = new NetworkConfig();
nconfig.SignalingUrl = "wss://signaling.because-why-not.com:443/test";
var call = new BrowserWebRtcCall(nconfig);
return call;
}
}
describe("CallTest", () => {
var originalTimeout;
beforeEach(() => {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000;
});
afterEach(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL =originalTimeout;
});
it("CallTest normal", () => {
expect(true).toBe(true);
});
it("CallTest async", (done) => {
setTimeout(()=>{
expect(true).toBe(true);
done();
}, 1000);
});
it("Send test", (done) => {
var call1 : ICall = null;
var call2 : ICall = null;
let call1ToCall2:ConnectionId;
let call2ToCall1:ConnectionId;
var address = "webunittest";
var teststring1 = "teststring1";
var teststring2 = "teststring2";
var testdata1 = new Uint8Array([1, 2]);
var testdata2 = new Uint8Array([3, 4]);
call1 = CallTestHelper.CreateCall(false, false);
expect(call1).not.toBeNull();
call2 = CallTestHelper.CreateCall(false, false);
expect(call2).not.toBeNull();
expect(true).toBe(true);
var mconfig = new MediaConfig();
mconfig.Audio = false;
mconfig.Video = false;
call1.addEventListener((sender: any, args: CallEventArgs)=>{
if(args.Type == CallEventType.ConfigurationComplete)
{
console.debug("call1 ConfigurationComplete");
call2.Configure(mconfig);
}else if(args.Type == CallEventType.WaitForIncomingCall)
{
console.debug("call1 WaitForIncomingCall");
call2.Call(address);
}else if(args.Type == CallEventType.CallAccepted)
{
let ar = args as CallAcceptedEventArgs;
call1ToCall2 = ar.ConnectionId;
//wait for message
}else if(args.Type == CallEventType.Message)
{
console.debug("call1 Message");
var margs = args as MessageEventArgs;
expect(margs.Content).toBe(teststring1);
expect(margs.Reliable).toBe(true);
call1.Send(teststring2, false, call1ToCall2);
}else if(args.Type == CallEventType.DataMessage)
{
console.debug("call1 DataMessage");
var dargs = args as DataMessageEventArgs;
expect(dargs.Reliable).toBe(true);
var recdata = dargs.Content;
expect(testdata1[0]).toBe(recdata[0]);
expect(testdata1[1]).toBe(recdata[1]);
console.debug("call1 send DataMessage");
call1.SendData(testdata2, false, call1ToCall2)
}else{
console.error("unexpected event: " + args.Type);
expect(true).toBe(false);
}
});
call2.addEventListener((sender: any, args: CallEventArgs)=>{
if(args.Type == CallEventType.ConfigurationComplete)
{
console.debug("call2 ConfigurationComplete");
call1.Listen(address);
}else if(args.Type == CallEventType.CallAccepted)
{
let ar = args as CallAcceptedEventArgs;
call2ToCall1 = ar.ConnectionId;
expect(call2ToCall1).toBeDefined();
call2.Send(teststring1);
}else if(args.Type == CallEventType.Message)
{
console.debug("call2 Message");
var margs = args as MessageEventArgs;
expect(margs.Content).toBe(teststring2);
expect(margs.Reliable).toBe(false);
console.debug("call2 send DataMessage " + call2ToCall1.id);
call2.SendData(testdata1, true, call2ToCall1)
}else if(args.Type == CallEventType.DataMessage)
{
console.debug("call2 DataMessage");
var dargs = args as DataMessageEventArgs;
expect(dargs.Reliable).toBe(false);
var recdata = dargs.Content;
expect(testdata2[0]).toBe(recdata[0]);
expect(testdata2[1]).toBe(recdata[1]);
done();
}else{
console.error("unexpected event: " + args.Type);
expect(true).toBe(false);
}
});
setInterval(()=>{
call1.Update();
call2.Update();
}, 50);
call1.Configure(mconfig);
});
});
\ No newline at end of file
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//current setup needs to load everything as a module
import {DeviceApi, CAPI_DeviceApi_Update,
CAPI_DeviceApi_RequestUpdate, CAPI_Media_GetVideoDevices_Length,
CAPI_Media_GetVideoDevices,
MediaConfig,
Media} from "../awrtc/index"
export function DeviceApiTest_export()
{
}
describe("DeviceApiTest", () => {
beforeEach(()=>{
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
DeviceApi.Reset();
});
function printall()
{
console.log("current DeviceApi.Devices:");
for(let k in DeviceApi.Devices)
{
let v = DeviceApi.Devices[k];
console.log(v.deviceId + " defaultLabel:" + v.defaultLabel + " label:" + v.label + " guessed:" + v.isLabelGuessed);
}
}
it("update", (done) => {
let update1complete = false;
let update2complete = false;
let deviceCount = 0;
expect(Object.keys(DeviceApi.Devices).length).toBe(0);
//first without device labels
let updatecall1 = ()=>{
expect(update1complete).toBe(false);
expect(update2complete).toBe(false);
console.debug("updatecall1");
printall();
let devices1 = DeviceApi.Devices;
deviceCount = Object.keys(devices1).length;
expect(deviceCount).toBeGreaterThan(0);
let key1 = Object.keys(devices1)[0];
//these tests don't work anymore due to forcing permissions for devices in
//unit tests.
//In a real browser we don't have access to device names until GetUserMedia
//returned. Meaning the API will fill in the names using "videoinput 1"
//"videoinput 2" and so on.
//Now the tests force permissions = true so we already have full
//access at the start
/*
expect(devices1[key1].label).toBe("videoinput 1");
expect(devices1[key1].isLabelGuessed).toBe(true);
if(deviceCount > 1)
{
let key2 = Object.keys(devices1)[1];
expect(devices1[key2].label).toBe("videoinput 2");
expect(devices1[key2].isLabelGuessed).toBe(true);
}
*/
DeviceApi.RemOnChangedHandler(updatecall1);
//second call with device labels
let updatecall2 = ()=>{
console.debug("updatecall2");
printall();
//check if the handler work properly
expect(update1complete).toBe(true);
expect(update2complete).toBe(false);
//sadly can't simulate fixed device names for testing
let devices2 = DeviceApi.Devices;
expect(Object.keys(devices2).length).toBe(deviceCount);
let key2 = Object.keys(devices2)[0];
//should have original label now
expect(devices2[key1].label).not.toBe("videodevice 1");
//and not be guessed anymore
expect(devices2[key1].isLabelGuessed).toBe(false, "Chrome fails this now. Likely due to file://. Check for better test setup");
update2complete = true;
DeviceApi.Reset();
expect(Object.keys(DeviceApi.Devices).length).toBe(0);
done();
}
update1complete = true;
DeviceApi.AddOnChangedHandler(updatecall2);
DeviceApi.RequestUpdate();
};
DeviceApi.AddOnChangedHandler(updatecall1);
DeviceApi.Update();
});
it("capi_update", (done) => {
let update1complete = false;
let update2complete = false;
let deviceCount = 0;
const devices_length_unitialized = CAPI_Media_GetVideoDevices_Length();
expect(devices_length_unitialized).toBe(0);
DeviceApi.AddOnChangedHandler(()=>{
let dev_length = CAPI_Media_GetVideoDevices_Length();
expect(dev_length).not.toBe(0);
expect(dev_length).toBe(Object.keys(DeviceApi.Devices).length);
let keys = Object.keys(DeviceApi.Devices);
let counter = 0;
for(let k of keys)
{
let expectedVal = DeviceApi.Devices[k].label;
let actual = CAPI_Media_GetVideoDevices(counter);
expect(actual).toBe(expectedVal);
counter++;
}
done();
});
CAPI_DeviceApi_Update();
});
it("isMediaAvailable", () => {
const res = DeviceApi.IsUserMediaAvailable();
expect(res).toBe(true);
});
it("getUserMedia", async () => {
let stream = await DeviceApi.getBrowserUserMedia({audio:true});
expect(stream).not.toBeNull();
expect(stream.getVideoTracks().length).toBe(0);
expect(stream.getAudioTracks().length).toBe(1);
stream = await DeviceApi.getBrowserUserMedia({video:true});
expect(stream).not.toBeNull();
expect(stream.getAudioTracks().length).toBe(0);
expect(stream.getVideoTracks().length).toBe(1);
});
it("getAssetMedia", async () => {
let config = new MediaConfig();
config.Audio = true;
config.Video = false;
let stream = await DeviceApi.getAssetUserMedia(config);
expect(stream).not.toBeNull();
expect(stream.getVideoTracks().length).toBe(0);
expect(stream.getAudioTracks().length).toBe(1);
config = new MediaConfig();
config.Audio = false;
config.Video = true;
stream = await DeviceApi.getAssetUserMedia(config);
expect(stream).not.toBeNull();
expect(stream.getAudioTracks().length).toBe(0);
expect(stream.getVideoTracks().length).toBe(1);
});
it("getAssetMedia_invalid", async () => {
let config = new MediaConfig();
config.Audio = false;
config.Video = true;
config.VideoDeviceName = "invalid name"
let error = null;
let stream :MediaStream = null;
console.log("Expecting error message: Failed to find deviceId for label invalid name");
try
{
stream = await DeviceApi.getAssetUserMedia(config);
}catch(err){
error = err;
}
expect(stream).toBeNull();
expect(error).toBeTruthy();
});
//check for a specific bug causing promise catch not to trigger correctly
//due to error in ToConstraints
it("getAssetMedia_invalid_promise", (done) => {
let config = new MediaConfig();
config.Audio = false;
config.Video = true;
config.VideoDeviceName = "invalid name"
let result: Promise<MediaStream> = null;
result = DeviceApi.getAssetUserMedia(config);
result.then(()=>{
fail("getAssetUserMedia returned but was expected to fail");
}).catch((error)=>{
expect(error).toBeTruthy();
done();
})
});
it("UpdateAsync", async (done) => {
expect(DeviceApi.GetVideoDevices().length).toBe(0);
await DeviceApi.UpdateAsync();
expect(DeviceApi.GetVideoDevices().length).toBeGreaterThan(0);
expect(DeviceApi.GetVideoDevices().length).toBe(CAPI_Media_GetVideoDevices_Length());
done();
});
/*
it("Devices", async () => {
DeviceApi.RequestUpdate
let config = new MediaConfig();
config.Audio = false;
config.Video = true;
config.VideoDeviceName = "invalid name"
let error = null;
let stream :MediaStream = null;
console.log("Expecting error message: Failed to find deviceId for label invalid name");
try
{
stream = await DeviceApi.getAssetUserMedia(config);
}catch(err){
error = err;
}
expect(stream).toBeNull();
expect(error).toBeTruthy();
});
*/
});
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { LocalNetwork, IBasicNetwork } from "../awrtc/index";
import { IBasicNetworkTest } from "helper/IBasicNetworkTest";
export class LocalNetworkTest extends IBasicNetworkTest {
public setup(): void {
super.setup();
//special tests
}
public _CreateNetworkImpl(): IBasicNetwork {
return new LocalNetwork();
}
}
describe("LocalNetworkTest", () => {
it("TestEnvironment", () => {
expect(null).toBeNull();
});
var test = new LocalNetworkTest();
test.setup();
});
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { BrowserMediaNetwork, NetworkConfig, MediaConfig,
ConnectionId, MediaEvent, MediaEventType,
MediaConfigurationState, NetEventType, BrowserMediaStream } from "../awrtc/index";
export class MediaNetworkTest{
createdNetworks:Array<BrowserMediaNetwork> = [];
createDefault() : BrowserMediaNetwork
{
let netConfig = new NetworkConfig();
netConfig.SignalingUrl = null;
let createdNetwork = new BrowserMediaNetwork(netConfig);
this.createdNetworks.push(createdNetwork);
return createdNetwork;
}
public setup(): void {
beforeEach(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
})
afterEach(() => {
for(let net of this.createdNetworks)
net.Dispose();
this.createdNetworks = new Array<BrowserMediaNetwork>();
})
it("FrameUpdates", (done) => {
let mediaConfig = new MediaConfig();
let network = this.createDefault();
network.Configure(mediaConfig);
setInterval(()=>{
network.Update();
let localFrame = network.TryGetFrame(ConnectionId.INVALID);
if(localFrame != null)
{
expect(localFrame.Height).toBeGreaterThan(0);
expect(localFrame.Width).toBeGreaterThan(0);
expect(localFrame.Buffer).not.toBeNull();
done();
}
network.Flush();
}, 10);
});
it("MediaEventLocal", (done) => {
BrowserMediaStream.DEBUG_SHOW_ELEMENTS = true;
let mediaConfig = new MediaConfig();
let network = this.createDefault();
network.Configure(mediaConfig);
setInterval(()=>{
network.Update();
let evt : MediaEvent = null;
while((evt = network.DequeueMediaEvent()) != null)
{
console.log("Stream added",evt );
expect(evt.EventType).toBe(MediaEventType.StreamAdded);
expect(evt.Args.videoHeight).toBeGreaterThan(0);
expect(evt.Args.videoWidth).toBeGreaterThan(0);
done();
}
network.Flush();
}, 10);
});
it("MediaEventRemote", (done) => {
BrowserMediaStream.DEBUG_SHOW_ELEMENTS = true;
let testaddress = "testaddress" + Math.random();
let sender = this.createDefault();
let receiver = this.createDefault();
let configureComplete = false;
let senderFrame = false;
let receiverFrame = false;
sender.Configure(new MediaConfig());
setInterval(()=>{
sender.Update();
receiver.Update();
if(configureComplete == false)
{
let state = sender.GetConfigurationState();
if(state == MediaConfigurationState.Successful)
{
configureComplete = true;
sender.StartServer(testaddress);
}else if(state == MediaConfigurationState.Failed)
{
fail();
}
}
let sndEvt = sender.Dequeue();
if(sndEvt != null)
{
console.log("sender event: " + sndEvt);
if(sndEvt.Type == NetEventType.ServerInitialized)
{
receiver.Connect(testaddress);
}
}
let recEvt = receiver.Dequeue();
if(recEvt != null)
{
console.log("receiver event: " + recEvt);
}
let evt : MediaEvent = null;
while((evt = sender.DequeueMediaEvent()) != null)
{
expect(evt.EventType).toBe(MediaEventType.StreamAdded);
expect(evt.Args.videoHeight).toBeGreaterThan(0);
expect(evt.Args.videoWidth).toBeGreaterThan(0);
senderFrame = true;
console.log("sender received first frame");
}
while((evt = receiver.DequeueMediaEvent()) != null)
{
expect(evt.EventType).toBe(MediaEventType.StreamAdded);
expect(evt.Args.videoHeight).toBeGreaterThan(0);
expect(evt.Args.videoWidth).toBeGreaterThan(0);
receiverFrame = true;
console.log("receiver received first frame");
}
sender.Flush();
receiver.Flush();
if(senderFrame && receiverFrame)
done();
}, 40);
}, 15000);
}
}
describe("MediaNetworkTest", () => {
it("TestEnvironment", () => {
expect(null).toBeNull();
});
var test = new MediaNetworkTest();
test.setup();
});
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { WebsocketTest } from "WebsocketNetworkTest";
import { IBasicNetworkTest } from "helper/IBasicNetworkTest";
import { NetworkEvent, IBasicNetwork, NetEventType, WebsocketNetwork,
ConnectionId, SignalingConfig, LocalNetwork, WebRtcNetwork, IWebRtcNetwork }
from "../awrtc/index";
export class WebRtcNetworkTest extends IBasicNetworkTest {
public static sUrl = 'ws://localhost:12776/test';
public static sUrlShared = 'ws://localhost:12776/testshared';
public static sDefaultIceServer = { urls: ["stun:stun.l.google.com:19302"] } as RTCIceServer;
private mUrl = WebsocketTest.sUrl;
//allows each test to overwrite the default behaviour
private mUseWebsockets = false;
//will set use websocket flag for each test
public static mAlwaysUseWebsockets = false;
public setup(): void {
beforeEach(() => {
this.mUrl = WebsocketTest.sUrl;
this.mUseWebsockets = WebRtcNetworkTest.mAlwaysUseWebsockets;
})
it("GetBufferedAmount", (done) => {
var srv: IWebRtcNetwork;
var address: string;
var srvToCltId: ConnectionId;
var clt: IWebRtcNetwork;
var cltToSrvId: ConnectionId;
var evt: NetworkEvent;
this.thenAsync((finished) => {
this._CreateServerClient((rsrv, raddress, rsrvToCltId, rclt, rcltToSrvId) => {
srv = rsrv as IWebRtcNetwork;
address = raddress;
srvToCltId = rsrvToCltId;
clt = rclt as IWebRtcNetwork;
cltToSrvId = rcltToSrvId;
finished();
});
});
this.then(() => {
//TODO: more detailed testing by actually triggering the buffer to fill?
//might be tricky as this is very system dependent
let buf:number;
buf = srv.GetBufferedAmount(srvToCltId, false);
expect(buf).toBe(0);
buf = srv.GetBufferedAmount(srvToCltId, true);
expect(buf).toBe(0);
buf = clt.GetBufferedAmount(cltToSrvId, false);
expect(buf).toBe(0);
buf = clt.GetBufferedAmount(cltToSrvId, true);
expect(buf).toBe(0);
done();
});
this.start();
});
it("SharedAddress", (done) => {
//turn off websockets and use shared websockets for this test as local network doesn't support shared mode
this.mUseWebsockets = true;
this.mUrl = WebsocketTest.sUrlShared;
var sharedAddress = "sharedtestaddress";
var evt: NetworkEvent;
var net1: IBasicNetwork;
var net2: IBasicNetwork;
this.thenAsync((finished) => {
net1 = this._CreateNetwork();
net1.StartServer(sharedAddress);
this.waitForEvent(net1, finished);
});
this.thenAsync((finished) => {
evt = net1.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
net2 = this._CreateNetwork() as WebsocketNetwork;
net2.StartServer(sharedAddress);
this.waitForEvent(net2, finished);
});
this.thenAsync((finished) => {
evt = net2.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
this.waitForEvent(net1, finished);
});
this.thenAsync((finished) => {
evt = net1.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
this.waitForEvent(net2, finished);
});
this.then(() => {
evt = net2.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
done();
});
this.start();
});
//connect using only direct local connections (give no ice servers)
it("ConnectLocalOnly", (done) => {
var srv: IBasicNetwork;
var address: string;
var clt: IBasicNetwork;
var cltId: ConnectionId;
var evt: NetworkEvent;
this.thenAsync((finished) => {
srv = this._CreateNetwork();
this._CreateServerNetwork((rsrv, raddress) => {
srv = rsrv;
address = raddress;
finished();
});
});
this.thenAsync((finished) => {
clt = this._CreateNetwork();
cltId = clt.Connect(address);
this.waitForEvent(clt, finished);
});
this.then(() => {
evt = clt.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).toBe(cltId.id);
});
this.thenAsync((finished) => {
this.waitForEvent(srv, finished);
});
this.then(() => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).not.toBe(ConnectionId.INVALID.id);
done();
});
this.start();
});
super.setup();
//special tests
}
public _CreateNetworkImpl(): IBasicNetwork {
let rtcConfig: RTCConfiguration = { iceServers: [WebRtcNetworkTest.sDefaultIceServer]};
var sigConfig: SignalingConfig;
if (this.mUseWebsockets) {
sigConfig = new SignalingConfig(new WebsocketNetwork(this.mUrl));
}
else {
sigConfig = new SignalingConfig(new LocalNetwork());
}
return new WebRtcNetwork(sigConfig, rtcConfig);
}
}
describe("WebRtcNetworkTest", () => {
it("TestEnvironment", () => {
expect(null).toBeNull();
});
var test = new WebRtcNetworkTest();
test.mDefaultWaitTimeout = 5000;
test.setup();
});
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import { IBasicNetwork, NetworkEvent, ConnectionId, NetEventType }
from "../../awrtc/network/index";
export interface Task {
(): void;
}
export interface AsyncTask {
(finished: Task): void;
}
export class TestTaskRunner {
public _toDoList = new Array<AsyncTask>();
public then(syncTask: Task) {
var wrap = (finished: Task) => {
syncTask();
finished();
};
this._toDoList.push(wrap);
}
public thenAsync(task: AsyncTask) {
this._toDoList.push(task);
}
public start() {
var task = this._toDoList.shift();
this._run(task);
}
public stop() {
}
private _run(task: AsyncTask): void {
task(() => {
if (this._toDoList.length > 0) {
setTimeout(() => {
this._run(this._toDoList.shift());
}, 1);
}
});
}
}
export abstract class BasicNetworkTestBase {
private mTestRunner = new TestTaskRunner();
private mCreatedNetworks = new Array<IBasicNetwork>();
public mDefaultWaitTimeout = 5000;
public abstract _CreateNetworkImpl(): IBasicNetwork;
public setup(): void {
beforeEach(() => {
this.mTestRunner.stop();
this.mTestRunner = new TestTaskRunner();
this.mCreatedNetworks = new Array<IBasicNetwork>();
});
}
public _CreateNetwork(): IBasicNetwork {
let net = this._CreateNetworkImpl();
this.mCreatedNetworks.push(net);
return net;
}
public then(syncTask: Task) {
this.mTestRunner.then(syncTask);
}
public thenAsync(task: AsyncTask) {
this.mTestRunner.thenAsync(task);
}
public start() {
this.mTestRunner.start();
}
//public waitForEvent(net: IBasicNetwork) {
// var wrap = (finished: Task) => {
// var timeout = 1000;
// var interval = 100;
// var intervalHandle;
// intervalHandle = setInterval(() => {
// this.UpdateAll();
// this.FlushAll();
// timeout -= interval;
// if (net.Peek() != null) {
// clearInterval(intervalHandle);
// finished();
// } else if (timeout <= 0) {
// clearInterval(intervalHandle);
// finished();
// }
// }, interval);
// };
// this.mTestRunner.thenAsync(wrap);
//}
public waitForEvent(net: IBasicNetwork, finished : Task, timeout?:number) {
if (timeout == null)
timeout = this.mDefaultWaitTimeout;
var interval = 50;
var intervalHandle;
intervalHandle = setInterval(() => {
this.UpdateAll();
this.FlushAll();
timeout -= interval;
if (net.Peek() != null) {
clearInterval(intervalHandle);
finished();
} else if (timeout <= 0) {
clearInterval(intervalHandle);
finished();
}
}, interval);
}
public UpdateAll(): void {
for (let v of this.mCreatedNetworks) {
v.Update();
}
}
public FlushAll(): void {
for (let v of this.mCreatedNetworks) {
v.Flush();
}
}
public ShutdownAll(): void {
for (let v of this.mCreatedNetworks) {
v.Shutdown();
}
this.mCreatedNetworks = new Array<IBasicNetwork>();
}
public _CreateServerNetwork(result: (IBasicNetwork, string) => void)
{
var srv = this._CreateNetwork();
srv.StartServer();
this.waitForEvent(srv, () => {
var evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.ServerInitialized);
expect(evt.Info).not.toBeNull();
var address = evt.Info;
result(srv, address);
});
}
public _Connect(srv: IBasicNetwork, address: string, clt: IBasicNetwork, result: (srvToCltId: ConnectionId, cltToSrvId: ConnectionId) => void) {
var evt: NetworkEvent;
var cltToSrvId = clt.Connect(address);
var srvToCltId: ConnectionId;
this.waitForEvent(clt, () => {
evt = clt.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).toBe(cltToSrvId.id);
this.waitForEvent(srv, () => {
evt = srv.Dequeue();
expect(evt).not.toBeNull();
expect(evt.Type).toBe(NetEventType.NewConnection);
expect(evt.ConnectionId.id).not.toBe(ConnectionId.INVALID.id);
srvToCltId = evt.ConnectionId;
result(srvToCltId, cltToSrvId);
});
});
}
public _CreateServerClient(result: (srv: IBasicNetwork, address: string, srvToCltId: ConnectionId, clt: IBasicNetwork, cltToSrvId: ConnectionId) => void) {
let srv: IBasicNetwork;
let address: string;
let srvToCltId: ConnectionId;
let clt: IBasicNetwork;
let cltToSrvId: ConnectionId;
this._CreateServerNetwork((rsrv, raddress) => {
srv = rsrv;
address = raddress;
clt = this._CreateNetwork();
this._Connect(srv, address, clt, (rsrvToCltId, rcltToSrvId) => {
srvToCltId = rsrvToCltId;
cltToSrvId = rcltToSrvId;
result(srv, address, srvToCltId, clt, cltToSrvId);
});
});
}
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment