bumble 0.0.155__py3-none-any.whl → 0.0.156__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bumble/_version.py +2 -2
- bumble/a2dp.py +1 -0
- bumble/apps/speaker/__init__.py +0 -0
- bumble/apps/speaker/logo.svg +42 -0
- bumble/apps/speaker/speaker.css +76 -0
- bumble/apps/speaker/speaker.html +34 -0
- bumble/apps/speaker/speaker.js +315 -0
- bumble/apps/speaker/speaker.py +747 -0
- bumble/avdtp.py +50 -31
- bumble/codecs.py +381 -0
- bumble/device.py +7 -3
- bumble/hci.py +13 -9
- bumble/host.py +7 -1
- {bumble-0.0.155.dist-info → bumble-0.0.156.dist-info}/METADATA +5 -4
- {bumble-0.0.155.dist-info → bumble-0.0.156.dist-info}/RECORD +19 -12
- {bumble-0.0.155.dist-info → bumble-0.0.156.dist-info}/entry_points.txt +1 -0
- {bumble-0.0.155.dist-info → bumble-0.0.156.dist-info}/LICENSE +0 -0
- {bumble-0.0.155.dist-info → bumble-0.0.156.dist-info}/WHEEL +0 -0
- {bumble-0.0.155.dist-info → bumble-0.0.156.dist-info}/top_level.txt +0 -0
bumble/_version.py
CHANGED
bumble/a2dp.py
CHANGED
|
File without changes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Vectornator for iOS (http://vectornator.io/) --><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
2
|
+
<svg height="100%" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" xmlns:vectornator="http://vectornator.io" version="1.1" viewBox="0 0 745 744.634">
|
|
3
|
+
<metadata>
|
|
4
|
+
<vectornator:setting key="DimensionsVisible" value="1"/>
|
|
5
|
+
<vectornator:setting key="PencilOnly" value="0"/>
|
|
6
|
+
<vectornator:setting key="SnapToPoints" value="0"/>
|
|
7
|
+
<vectornator:setting key="OutlineMode" value="0"/>
|
|
8
|
+
<vectornator:setting key="CMYKEnabledKey" value="0"/>
|
|
9
|
+
<vectornator:setting key="RulersVisible" value="1"/>
|
|
10
|
+
<vectornator:setting key="SnapToEdges" value="0"/>
|
|
11
|
+
<vectornator:setting key="GuidesVisible" value="1"/>
|
|
12
|
+
<vectornator:setting key="DisplayWhiteBackground" value="0"/>
|
|
13
|
+
<vectornator:setting key="doHistoryDisabled" value="0"/>
|
|
14
|
+
<vectornator:setting key="SnapToGuides" value="1"/>
|
|
15
|
+
<vectornator:setting key="TimeLapseWatermarkDisabled" value="0"/>
|
|
16
|
+
<vectornator:setting key="Units" value="Pixels"/>
|
|
17
|
+
<vectornator:setting key="DynamicGuides" value="0"/>
|
|
18
|
+
<vectornator:setting key="IsolateActiveLayer" value="0"/>
|
|
19
|
+
<vectornator:setting key="SnapToGrid" value="0"/>
|
|
20
|
+
</metadata>
|
|
21
|
+
<defs/>
|
|
22
|
+
<g id="Layer 1" vectornator:layerName="Layer 1">
|
|
23
|
+
<path stroke="#000000" stroke-width="18.6464" d="M368.753+729.441L58.8847+550.539L58.8848+192.734L368.753+13.8313L678.621+192.734L678.621+550.539L368.753+729.441Z" fill="#0082fc" stroke-linecap="butt" fill-opacity="0.307489" opacity="1" stroke-linejoin="round"/>
|
|
24
|
+
<g opacity="1">
|
|
25
|
+
<g opacity="1">
|
|
26
|
+
<path stroke="#000000" stroke-width="20" d="M292.873+289.256L442.872+289.256L442.872+539.254L292.873+539.254L292.873+289.256Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
27
|
+
<path stroke="#000000" stroke-width="20" d="M292.873+289.256C292.873+247.835+326.452+214.257+367.873+214.257C409.294+214.257+442.872+247.835+442.872+289.256C442.872+330.677+409.294+364.256+367.873+364.256C326.452+364.256+292.873+330.677+292.873+289.256Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
28
|
+
<path stroke="#000000" stroke-width="20" d="M292.873+539.254C292.873+497.833+326.452+464.255+367.873+464.255C409.294+464.255+442.872+497.833+442.872+539.254C442.872+580.675+409.294+614.254+367.873+614.254C326.452+614.254+292.873+580.675+292.873+539.254Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
29
|
+
<path stroke="#0082fc" stroke-width="0.1" d="M302.873+289.073L432.872+289.073L432.872+539.072L302.873+539.072L302.873+289.073Z" fill="#fcd100" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
30
|
+
</g>
|
|
31
|
+
<path stroke="#000000" stroke-width="0.1" d="M103.161+309.167L226.956+443.903L366.671+309.604L103.161+309.167Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
|
|
32
|
+
<path stroke="#000000" stroke-width="0.1" d="M383.411+307.076L508.887+440.112L650.5+307.507L383.411+307.076Z" fill="#0082fc" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
|
|
33
|
+
<path stroke="#000000" stroke-width="20" d="M522.045+154.808L229.559+448.882L83.8397+300.104L653.666+302.936L511.759+444.785L223.101+156.114" fill="none" stroke-linecap="round" opacity="1" stroke-linejoin="round"/>
|
|
34
|
+
<path stroke="#000000" stroke-width="61.8698" d="M295.857+418.738L438.9+418.738" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
35
|
+
<path stroke="#000000" stroke-width="61.8698" d="M295.857+521.737L438.9+521.737" fill="none" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
36
|
+
<g opacity="1">
|
|
37
|
+
<path stroke="#0082fc" stroke-width="0.1" d="M367.769+667.024L367.821+616.383L403.677+616.336C383.137+626.447+368.263+638.69+367.769+667.024Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
38
|
+
<path stroke="#0082fc" stroke-width="0.1" d="M367.836+667.024L367.784+616.383L331.928+616.336C352.468+626.447+367.341+638.69+367.836+667.024Z" fill="#000000" stroke-linecap="butt" opacity="1" stroke-linejoin="round"/>
|
|
39
|
+
</g>
|
|
40
|
+
</g>
|
|
41
|
+
</g>
|
|
42
|
+
</svg>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
body, h1, h2, h3, h4, h5, h6 {
|
|
2
|
+
font-family: sans-serif;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
#controlsDiv {
|
|
6
|
+
margin: 6px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#connectionText {
|
|
10
|
+
background-color: rgb(239, 89, 75);
|
|
11
|
+
border: none;
|
|
12
|
+
border-radius: 4px;
|
|
13
|
+
padding: 8px;
|
|
14
|
+
display: inline-block;
|
|
15
|
+
margin: 4px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#startButton {
|
|
19
|
+
padding: 4px;
|
|
20
|
+
margin: 6px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#fftCanvas {
|
|
24
|
+
border-radius: 16px;
|
|
25
|
+
margin: 6px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#bandwidthCanvas {
|
|
29
|
+
border: grey;
|
|
30
|
+
border-style: solid;
|
|
31
|
+
border-radius: 8px;
|
|
32
|
+
margin: 6px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#streamStateText {
|
|
36
|
+
background-color: rgb(93, 165, 93);
|
|
37
|
+
border: none;
|
|
38
|
+
border-radius: 8px;
|
|
39
|
+
padding: 10px 20px;
|
|
40
|
+
display: inline-block;
|
|
41
|
+
margin: 6px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#connectionStateText {
|
|
45
|
+
background-color: rgb(112, 146, 206);
|
|
46
|
+
border: none;
|
|
47
|
+
border-radius: 8px;
|
|
48
|
+
padding: 10px 20px;
|
|
49
|
+
display: inline-block;
|
|
50
|
+
margin: 6px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#propertiesTable {
|
|
54
|
+
border: grey;
|
|
55
|
+
border-style: solid;
|
|
56
|
+
border-radius: 4px;
|
|
57
|
+
padding: 4px;
|
|
58
|
+
margin: 6px;
|
|
59
|
+
margin-left: 0px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
th, td {
|
|
63
|
+
padding-left: 6px;
|
|
64
|
+
padding-right: 6px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.properties td:nth-child(even) {
|
|
68
|
+
background-color: #D6EEEE;
|
|
69
|
+
font-family: monospace;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.properties td:nth-child(odd) {
|
|
73
|
+
font-weight: bold;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.properties tr td:nth-child(2) { width: 150px; }
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Bumble Speaker</title>
|
|
5
|
+
<script type="text/javascript" src="speaker.js"></script>
|
|
6
|
+
<link rel="stylesheet" href="speaker.css">
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<h1><img src="logo.svg" width=100 height=100 style="vertical-align:middle" alt=""/>Bumble Virtual Speaker</h1>
|
|
10
|
+
<div id="connectionText"></div>
|
|
11
|
+
<div id="speaker">
|
|
12
|
+
<table><tr>
|
|
13
|
+
<td>
|
|
14
|
+
<table id="propertiesTable" class="properties">
|
|
15
|
+
<tr><td>Codec</td><td><span id="codecText"></span></td></tr>
|
|
16
|
+
<tr><td>Packets</td><td><span id="packetsReceivedText"></span></td></tr>
|
|
17
|
+
<tr><td>Bytes</td><td><span id="bytesReceivedText"></span></td></tr>
|
|
18
|
+
</table>
|
|
19
|
+
</td>
|
|
20
|
+
<td>
|
|
21
|
+
<canvas id="bandwidthCanvas" width="500", height="100">Bandwidth Graph</canvas>
|
|
22
|
+
</td>
|
|
23
|
+
</tr></table>
|
|
24
|
+
<span id="streamStateText">IDLE</span>
|
|
25
|
+
<span id="connectionStateText">NOT CONNECTED</span>
|
|
26
|
+
<div id="controlsDiv">
|
|
27
|
+
<button id="audioOnButton">Audio On</button>
|
|
28
|
+
<span id="audioSupportMessageText"></span>
|
|
29
|
+
</div>
|
|
30
|
+
<canvas id="fftCanvas" width="1024", height="300">Audio Frequencies Animation</canvas>
|
|
31
|
+
<audio id="audio"></audio>
|
|
32
|
+
</div>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const channelUrl = ((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/channel";
|
|
5
|
+
let channelSocket;
|
|
6
|
+
let connectionText;
|
|
7
|
+
let codecText;
|
|
8
|
+
let packetsReceivedText;
|
|
9
|
+
let bytesReceivedText;
|
|
10
|
+
let streamStateText;
|
|
11
|
+
let connectionStateText;
|
|
12
|
+
let controlsDiv;
|
|
13
|
+
let audioOnButton;
|
|
14
|
+
let mediaSource;
|
|
15
|
+
let sourceBuffer;
|
|
16
|
+
let audioElement;
|
|
17
|
+
let audioContext;
|
|
18
|
+
let audioAnalyzer;
|
|
19
|
+
let audioFrequencyBinCount;
|
|
20
|
+
let audioFrequencyData;
|
|
21
|
+
let packetsReceived = 0;
|
|
22
|
+
let bytesReceived = 0;
|
|
23
|
+
let audioState = "stopped";
|
|
24
|
+
let streamState = "IDLE";
|
|
25
|
+
let audioSupportMessageText;
|
|
26
|
+
let fftCanvas;
|
|
27
|
+
let fftCanvasContext;
|
|
28
|
+
let bandwidthCanvas;
|
|
29
|
+
let bandwidthCanvasContext;
|
|
30
|
+
let bandwidthBinCount;
|
|
31
|
+
let bandwidthBins = [];
|
|
32
|
+
|
|
33
|
+
const FFT_WIDTH = 800;
|
|
34
|
+
const FFT_HEIGHT = 256;
|
|
35
|
+
const BANDWIDTH_WIDTH = 500;
|
|
36
|
+
const BANDWIDTH_HEIGHT = 100;
|
|
37
|
+
|
|
38
|
+
function hexToBytes(hex) {
|
|
39
|
+
return Uint8Array.from(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function init() {
|
|
43
|
+
initUI();
|
|
44
|
+
initMediaSource();
|
|
45
|
+
initAudioElement();
|
|
46
|
+
initAnalyzer();
|
|
47
|
+
|
|
48
|
+
connect();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function initUI() {
|
|
52
|
+
controlsDiv = document.getElementById("controlsDiv");
|
|
53
|
+
controlsDiv.style.visibility = "hidden";
|
|
54
|
+
connectionText = document.getElementById("connectionText");
|
|
55
|
+
audioOnButton = document.getElementById("audioOnButton");
|
|
56
|
+
codecText = document.getElementById("codecText");
|
|
57
|
+
packetsReceivedText = document.getElementById("packetsReceivedText");
|
|
58
|
+
bytesReceivedText = document.getElementById("bytesReceivedText");
|
|
59
|
+
streamStateText = document.getElementById("streamStateText");
|
|
60
|
+
connectionStateText = document.getElementById("connectionStateText");
|
|
61
|
+
audioSupportMessageText = document.getElementById("audioSupportMessageText");
|
|
62
|
+
|
|
63
|
+
audioOnButton.onclick = () => startAudio();
|
|
64
|
+
|
|
65
|
+
setConnectionText("");
|
|
66
|
+
|
|
67
|
+
requestAnimationFrame(onAnimationFrame);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function initMediaSource() {
|
|
71
|
+
mediaSource = new MediaSource();
|
|
72
|
+
mediaSource.onsourceopen = onMediaSourceOpen;
|
|
73
|
+
mediaSource.onsourceclose = onMediaSourceClose;
|
|
74
|
+
mediaSource.onsourceended = onMediaSourceEnd;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function initAudioElement() {
|
|
78
|
+
audioElement = document.getElementById("audio");
|
|
79
|
+
audioElement.src = URL.createObjectURL(mediaSource);
|
|
80
|
+
// audioElement.controls = true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function initAnalyzer() {
|
|
84
|
+
fftCanvas = document.getElementById("fftCanvas");
|
|
85
|
+
fftCanvas.width = FFT_WIDTH
|
|
86
|
+
fftCanvas.height = FFT_HEIGHT
|
|
87
|
+
fftCanvasContext = fftCanvas.getContext('2d');
|
|
88
|
+
fftCanvasContext.fillStyle = "rgb(0, 0, 0)";
|
|
89
|
+
fftCanvasContext.fillRect(0, 0, FFT_WIDTH, FFT_HEIGHT);
|
|
90
|
+
|
|
91
|
+
bandwidthCanvas = document.getElementById("bandwidthCanvas");
|
|
92
|
+
bandwidthCanvas.width = BANDWIDTH_WIDTH
|
|
93
|
+
bandwidthCanvas.height = BANDWIDTH_HEIGHT
|
|
94
|
+
bandwidthCanvasContext = bandwidthCanvas.getContext('2d');
|
|
95
|
+
bandwidthCanvasContext.fillStyle = "rgb(255, 255, 255)";
|
|
96
|
+
bandwidthCanvasContext.fillRect(0, 0, BANDWIDTH_WIDTH, BANDWIDTH_HEIGHT);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function startAnalyzer() {
|
|
100
|
+
// FFT
|
|
101
|
+
if (audioElement.captureStream !== undefined) {
|
|
102
|
+
audioContext = new AudioContext();
|
|
103
|
+
audioAnalyzer = audioContext.createAnalyser();
|
|
104
|
+
audioAnalyzer.fftSize = 128;
|
|
105
|
+
audioFrequencyBinCount = audioAnalyzer.frequencyBinCount;
|
|
106
|
+
audioFrequencyData = new Uint8Array(audioFrequencyBinCount);
|
|
107
|
+
const stream = audioElement.captureStream();
|
|
108
|
+
const source = audioContext.createMediaStreamSource(stream);
|
|
109
|
+
source.connect(audioAnalyzer);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Bandwidth
|
|
113
|
+
bandwidthBinCount = BANDWIDTH_WIDTH / 2;
|
|
114
|
+
bandwidthBins = [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function setConnectionText(message) {
|
|
118
|
+
connectionText.innerText = message;
|
|
119
|
+
if (message.length == 0) {
|
|
120
|
+
connectionText.style.display = "none";
|
|
121
|
+
} else {
|
|
122
|
+
connectionText.style.display = "inline-block";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function setStreamState(state) {
|
|
127
|
+
streamState = state;
|
|
128
|
+
streamStateText.innerText = streamState;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function onAnimationFrame() {
|
|
132
|
+
// FFT
|
|
133
|
+
if (audioAnalyzer !== undefined) {
|
|
134
|
+
audioAnalyzer.getByteFrequencyData(audioFrequencyData);
|
|
135
|
+
fftCanvasContext.fillStyle = "rgb(0, 0, 0)";
|
|
136
|
+
fftCanvasContext.fillRect(0, 0, FFT_WIDTH, FFT_HEIGHT);
|
|
137
|
+
const barCount = audioFrequencyBinCount;
|
|
138
|
+
const barWidth = (FFT_WIDTH / audioFrequencyBinCount) - 1;
|
|
139
|
+
for (let bar = 0; bar < barCount; bar++) {
|
|
140
|
+
const barHeight = audioFrequencyData[bar];
|
|
141
|
+
fftCanvasContext.fillStyle = `rgb(${barHeight / 256 * 200 + 50}, 50, ${50 + 2 * bar})`;
|
|
142
|
+
fftCanvasContext.fillRect(bar * (barWidth + 1), FFT_HEIGHT - barHeight, barWidth, barHeight);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Bandwidth
|
|
147
|
+
bandwidthCanvasContext.fillStyle = "rgb(255, 255, 255)";
|
|
148
|
+
bandwidthCanvasContext.fillRect(0, 0, BANDWIDTH_WIDTH, BANDWIDTH_HEIGHT);
|
|
149
|
+
bandwidthCanvasContext.fillStyle = `rgb(100, 100, 100)`;
|
|
150
|
+
for (let t = 0; t < bandwidthBins.length; t++) {
|
|
151
|
+
const lineHeight = (bandwidthBins[t] / 1000) * BANDWIDTH_HEIGHT;
|
|
152
|
+
bandwidthCanvasContext.fillRect(t * 2, BANDWIDTH_HEIGHT - lineHeight, 2, lineHeight);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Display again at the next frame
|
|
156
|
+
requestAnimationFrame(onAnimationFrame);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function onMediaSourceOpen() {
|
|
160
|
+
console.log(this.readyState);
|
|
161
|
+
sourceBuffer = mediaSource.addSourceBuffer("audio/aac");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function onMediaSourceClose() {
|
|
165
|
+
console.log(this.readyState);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function onMediaSourceEnd() {
|
|
169
|
+
console.log(this.readyState);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function startAudio() {
|
|
173
|
+
try {
|
|
174
|
+
console.log("starting audio...");
|
|
175
|
+
audioOnButton.disabled = true;
|
|
176
|
+
audioState = "starting";
|
|
177
|
+
await audioElement.play();
|
|
178
|
+
console.log("audio started");
|
|
179
|
+
audioState = "playing";
|
|
180
|
+
startAnalyzer();
|
|
181
|
+
} catch(error) {
|
|
182
|
+
console.error(`play failed: ${error}`);
|
|
183
|
+
audioState = "stopped";
|
|
184
|
+
audioOnButton.disabled = false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function onAudioPacket(packet) {
|
|
189
|
+
if (audioState != "stopped") {
|
|
190
|
+
// Queue the audio packet.
|
|
191
|
+
sourceBuffer.appendBuffer(packet);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
packetsReceived += 1;
|
|
195
|
+
packetsReceivedText.innerText = packetsReceived;
|
|
196
|
+
bytesReceived += packet.byteLength;
|
|
197
|
+
bytesReceivedText.innerText = bytesReceived;
|
|
198
|
+
|
|
199
|
+
bandwidthBins[bandwidthBins.length] = packet.byteLength;
|
|
200
|
+
if (bandwidthBins.length > bandwidthBinCount) {
|
|
201
|
+
bandwidthBins.shift();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function onChannelOpen() {
|
|
206
|
+
console.log('channel OPEN');
|
|
207
|
+
setConnectionText("");
|
|
208
|
+
controlsDiv.style.visibility = "visible";
|
|
209
|
+
|
|
210
|
+
// Handshake with the backend.
|
|
211
|
+
sendMessage({
|
|
212
|
+
type: "hello"
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function onChannelClose() {
|
|
217
|
+
console.log('channel CLOSED');
|
|
218
|
+
setConnectionText("Connection to CLI app closed, restart it and reload this page.");
|
|
219
|
+
controlsDiv.style.visibility = "hidden";
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function onChannelError(error) {
|
|
223
|
+
console.log(`channel ERROR: ${error}`);
|
|
224
|
+
setConnectionText(`Connection to CLI app error ({${error}}), restart it and reload this page.`);
|
|
225
|
+
controlsDiv.style.visibility = "hidden";
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function onChannelMessage(message) {
|
|
229
|
+
if (typeof message.data === 'string' || message.data instanceof String) {
|
|
230
|
+
// JSON message.
|
|
231
|
+
const jsonMessage = JSON.parse(message.data);
|
|
232
|
+
console.log(`channel MESSAGE: ${message.data}`);
|
|
233
|
+
|
|
234
|
+
// Dispatch the message.
|
|
235
|
+
const handlerName = `on${jsonMessage.type.charAt(0).toUpperCase()}${jsonMessage.type.slice(1)}Message`
|
|
236
|
+
const handler = messageHandlers[handlerName];
|
|
237
|
+
if (handler !== undefined) {
|
|
238
|
+
const params = jsonMessage.params;
|
|
239
|
+
if (params === undefined) {
|
|
240
|
+
params = {};
|
|
241
|
+
}
|
|
242
|
+
handler(params);
|
|
243
|
+
} else {
|
|
244
|
+
console.warn(`unhandled message: ${jsonMessage.type}`)
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
// BINARY audio data.
|
|
248
|
+
onAudioPacket(message.data);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function onHelloMessage(params) {
|
|
253
|
+
codecText.innerText = params.codec;
|
|
254
|
+
if (params.codec != "aac") {
|
|
255
|
+
audioOnButton.disabled = true;
|
|
256
|
+
audioSupportMessageText.innerText = "Only AAC can be played, audio will be disabled";
|
|
257
|
+
audioSupportMessageText.style.display = "inline-block";
|
|
258
|
+
} else {
|
|
259
|
+
audioSupportMessageText.innerText = "";
|
|
260
|
+
audioSupportMessageText.style.display = "none";
|
|
261
|
+
}
|
|
262
|
+
if (params.streamState) {
|
|
263
|
+
setStreamState(params.streamState);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function onStartMessage(params) {
|
|
268
|
+
setStreamState("STARTED");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function onStopMessage(params) {
|
|
272
|
+
setStreamState("STOPPED");
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function onSuspendMessage(params) {
|
|
276
|
+
setStreamState("SUSPENDED");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function onConnectionMessage(params) {
|
|
280
|
+
connectionStateText.innerText = `CONNECTED: ${params.peer_name} (${params.peer_address})`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function onDisconnectionMessage(params) {
|
|
284
|
+
connectionStateText.innerText = "DISCONNECTED";
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function sendMessage(message) {
|
|
288
|
+
channelSocket.send(JSON.stringify(message));
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function connect() {
|
|
292
|
+
console.log("connecting to CLI app");
|
|
293
|
+
|
|
294
|
+
channelSocket = new WebSocket(channelUrl);
|
|
295
|
+
channelSocket.binaryType = "arraybuffer";
|
|
296
|
+
channelSocket.onopen = onChannelOpen;
|
|
297
|
+
channelSocket.onclose = onChannelClose;
|
|
298
|
+
channelSocket.onerror = onChannelError;
|
|
299
|
+
channelSocket.onmessage = onChannelMessage;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const messageHandlers = {
|
|
303
|
+
onHelloMessage,
|
|
304
|
+
onStartMessage,
|
|
305
|
+
onStopMessage,
|
|
306
|
+
onSuspendMessage,
|
|
307
|
+
onConnectionMessage,
|
|
308
|
+
onDisconnectionMessage
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
window.onload = (event) => {
|
|
312
|
+
init();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
}());
|