@mentra/bluetooth-sdk 0.1.20 → 0.1.21-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -12
- package/android/build.gradle +3 -0
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalytics.kt +34 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +169 -26
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +72 -16
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +127 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +4 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +400 -99
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +333 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +43 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +18 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +53 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Ar99.java +2653 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +16 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +342 -42
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +7 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +19 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +52 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +115 -47
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +7 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BleJsonCompact.java +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitor.kt +45 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/StreamStatusEventTest.kt +29 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +45 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/BleJsonCompactTest.java +20 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
- package/build/BluetoothSdk.types.d.ts +100 -22
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +2 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +3 -0
- package/build/_internal.d.ts.map +1 -1
- package/build/_internal.js +3 -0
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +31 -4
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +26 -3
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/MentraLocalNetworkModule.d.ts +37 -0
- package/build/_private/MentraLocalNetworkModule.d.ts.map +1 -0
- package/build/_private/MentraLocalNetworkModule.js +3 -0
- package/build/_private/MentraLocalNetworkModule.js.map +1 -0
- package/build/_private/cameraRequestPayload.d.ts +1 -1
- package/build/_private/cameraRequestPayload.d.ts.map +1 -1
- package/build/_private/cameraRequestPayload.js +7 -0
- package/build/_private/cameraRequestPayload.js.map +1 -1
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +10 -0
- package/build/_private/photoRequestPayload.js.map +1 -1
- package/build/_private/sdkOtaManifest.d.ts +20 -0
- package/build/_private/sdkOtaManifest.d.ts.map +1 -0
- package/build/_private/sdkOtaManifest.js +28 -0
- package/build/_private/sdkOtaManifest.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +11 -1
- package/build/index.js.map +1 -1
- package/expo-module.config.json +1 -0
- package/ios/BluetoothSdkModule.swift +113 -2
- package/ios/Source/BluetoothSdkDefaults.swift +2 -1
- package/ios/Source/Bridge.swift +87 -21
- package/ios/Source/DeviceManager.swift +118 -20
- package/ios/Source/DeviceStore.swift +4 -0
- package/ios/Source/MentraBluetoothSDK.swift +326 -33
- package/ios/Source/OtaManifest.swift +47 -4
- package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
- package/ios/Source/camera/CameraModels.swift +26 -6
- package/ios/Source/internal/BleTraceLogger.swift +1 -1
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +39 -0
- package/ios/Source/sgcs/Ar99.swift +2451 -0
- package/ios/Source/sgcs/G1.swift +1 -1
- package/ios/Source/sgcs/G2.swift +19 -7
- package/ios/Source/sgcs/Mach1.swift +1 -1
- package/ios/Source/sgcs/MentraLive.swift +478 -61
- package/ios/Source/sgcs/MentraNex.swift +1 -1
- package/ios/Source/sgcs/Nimo.swift +1 -1
- package/ios/Source/sgcs/SGCManager.swift +13 -2
- package/ios/Source/sgcs/Simulated.swift +1 -1
- package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
- package/ios/Source/status/WifiHotspotStatus.swift +21 -2
- package/ios/Source/streaming/StreamModels.swift +68 -2
- package/ios/Source/tts/TTSTools.swift +1 -1
- package/ios/Source/types/DeviceModels.swift +13 -0
- package/ios/Source/utils/BleJsonCompact.swift +8 -0
- package/ios/Source/utils/Constants.swift +2 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +4 -0
- package/package.json +1 -2
- package/src/BluetoothSdk.types.ts +106 -24
- package/src/_internal.ts +3 -0
- package/src/_private/BluetoothSdkModule.ts +83 -18
- package/src/_private/MentraLocalNetworkModule.ts +50 -0
- package/src/_private/cameraRequestPayload.ts +9 -2
- package/src/_private/photoRequestPayload.ts +13 -4
- package/src/_private/sdkOtaManifest.ts +31 -0
- package/src/index.ts +13 -1
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk.utils.audio;
|
|
2
|
+
|
|
3
|
+
import android.util.Log;
|
|
4
|
+
|
|
5
|
+
import java.util.Arrays;
|
|
6
|
+
import java.util.concurrent.ArrayBlockingQueue;
|
|
7
|
+
|
|
8
|
+
import io.github.jaredmdobson.OpusDecoder;
|
|
9
|
+
import io.github.jaredmdobson.OpusException;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* AR99 Opus decoder worker.
|
|
13
|
+
*
|
|
14
|
+
* <p>The caller feeds BLE notify bytes through {@link #submitBleNotify(byte[])}. This class owns
|
|
15
|
+
* queueing, frame reassembly, Opus decoder state, and decoded PCM callbacks.
|
|
16
|
+
*/
|
|
17
|
+
public final class Ar99OpusPcmDecoder extends Thread {
|
|
18
|
+
public interface Callback {
|
|
19
|
+
void onPcmDecoded(byte[] pcmData);
|
|
20
|
+
|
|
21
|
+
default void onNotifyProcessed(int rawLen, int pcmFrames, int pcmBytesOut) {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private enum TaskType {
|
|
25
|
+
DECODE,
|
|
26
|
+
RESET,
|
|
27
|
+
STOP
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private static final class Task {
|
|
31
|
+
final TaskType type;
|
|
32
|
+
final byte[] payload;
|
|
33
|
+
|
|
34
|
+
Task(TaskType type, byte[] payload) {
|
|
35
|
+
this.type = type;
|
|
36
|
+
this.payload = payload;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private static final int SAMPLE_RATE = 16000;
|
|
41
|
+
private static final int CHANNELS = 1;
|
|
42
|
+
private static final int MAX_FRAME_SAMPLES = 5760;
|
|
43
|
+
private static final int FRAME_BYTES = 48;
|
|
44
|
+
private static final int HEADER_BYTES = 8;
|
|
45
|
+
private static final int MAX_BUFFER_SIZE = 10000;
|
|
46
|
+
private static final int TASK_QUEUE_CAPACITY = 256;
|
|
47
|
+
private static final int MAX_DIAG_FRAMES = 8;
|
|
48
|
+
private static final byte[] EXPECTED_PREFIX = new byte[] {0x00, 0x00, 0x00, 0x28};
|
|
49
|
+
private static final String TAG = "Ar99OpusPcmDecoder";
|
|
50
|
+
|
|
51
|
+
private final Callback callback;
|
|
52
|
+
private final OpusDecoder decoder;
|
|
53
|
+
private final ArrayBlockingQueue<Task> taskQueue = new ArrayBlockingQueue<>(TASK_QUEUE_CAPACITY);
|
|
54
|
+
|
|
55
|
+
private volatile boolean running = true;
|
|
56
|
+
|
|
57
|
+
private byte[] buffer = new byte[0];
|
|
58
|
+
private long totalNotifyCount = 0L;
|
|
59
|
+
private long totalFrameCount = 0L;
|
|
60
|
+
private long totalDecodeFailCount = 0L;
|
|
61
|
+
private long totalHeaderMismatchCount = 0L;
|
|
62
|
+
private long totalShortChunkCount = 0L;
|
|
63
|
+
private long totalResyncDropBytes = 0L;
|
|
64
|
+
private boolean headerLooksStable = true;
|
|
65
|
+
|
|
66
|
+
public Ar99OpusPcmDecoder(Callback callback) throws OpusException {
|
|
67
|
+
super("ar99-opus-decode");
|
|
68
|
+
this.callback = callback;
|
|
69
|
+
this.decoder = new OpusDecoder(SAMPLE_RATE, CHANNELS);
|
|
70
|
+
start();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public void submitBleNotify(byte[] opusPayload) {
|
|
74
|
+
if (!running || opusPayload == null || opusPayload.length == 0) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
byte[] payloadCopy = Arrays.copyOf(opusPayload, opusPayload.length);
|
|
78
|
+
if (!taskQueue.offer(new Task(TaskType.DECODE, payloadCopy))) {
|
|
79
|
+
Log.w(TAG, "AR99 Opus task queue full, drop notify len=" + payloadCopy.length);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public void resetSession() {
|
|
84
|
+
if (!running) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
taskQueue.clear();
|
|
88
|
+
taskQueue.offer(new Task(TaskType.RESET, null));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public void resetBuffer() {
|
|
92
|
+
resetSession();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public void shutdownDecoder() {
|
|
96
|
+
if (!running) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
running = false;
|
|
100
|
+
taskQueue.clear();
|
|
101
|
+
taskQueue.offer(new Task(TaskType.STOP, null));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public synchronized byte[] decodePacketToPcm16Mono(byte[] opusPayload) {
|
|
105
|
+
return decodePayload(opusPayload);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Override
|
|
109
|
+
public void run() {
|
|
110
|
+
try {
|
|
111
|
+
while (true) {
|
|
112
|
+
Task task = taskQueue.take();
|
|
113
|
+
if (task.type == TaskType.STOP) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
if (task.type == TaskType.RESET) {
|
|
117
|
+
resetDecoderState();
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (task.payload != null && task.payload.length > 0) {
|
|
121
|
+
handleChunk(task.payload);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} catch (InterruptedException e) {
|
|
125
|
+
if (running) {
|
|
126
|
+
Log.w(TAG, "AR99 Opus worker interrupted: " + e.getMessage());
|
|
127
|
+
}
|
|
128
|
+
} finally {
|
|
129
|
+
taskQueue.clear();
|
|
130
|
+
resetDecoderState();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private synchronized void resetDecoderState() {
|
|
135
|
+
decoder.resetState();
|
|
136
|
+
buffer = new byte[0];
|
|
137
|
+
totalNotifyCount = 0L;
|
|
138
|
+
totalFrameCount = 0L;
|
|
139
|
+
totalDecodeFailCount = 0L;
|
|
140
|
+
totalHeaderMismatchCount = 0L;
|
|
141
|
+
totalShortChunkCount = 0L;
|
|
142
|
+
totalResyncDropBytes = 0L;
|
|
143
|
+
headerLooksStable = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private synchronized void handleChunk(byte[] chunk) {
|
|
147
|
+
totalNotifyCount++;
|
|
148
|
+
if (chunk.length < FRAME_BYTES) {
|
|
149
|
+
totalShortChunkCount++;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (buffer.length + chunk.length > MAX_BUFFER_SIZE) {
|
|
153
|
+
Log.w(
|
|
154
|
+
TAG,
|
|
155
|
+
"AR99 Opus buffer overflow, clear"
|
|
156
|
+
+ ", oldBuf="
|
|
157
|
+
+ buffer.length
|
|
158
|
+
+ ", chunk="
|
|
159
|
+
+ chunk.length
|
|
160
|
+
+ ", notify#="
|
|
161
|
+
+ totalNotifyCount);
|
|
162
|
+
buffer = new byte[0];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
int oldBufferLen = buffer.length;
|
|
166
|
+
buffer = concat(buffer, chunk);
|
|
167
|
+
|
|
168
|
+
int pcmFrames = 0;
|
|
169
|
+
int pcmBytesOut = 0;
|
|
170
|
+
int decodeFail = 0;
|
|
171
|
+
int skipPayload = 0;
|
|
172
|
+
int headerMismatch = 0;
|
|
173
|
+
|
|
174
|
+
while (buffer.length >= EXPECTED_PREFIX.length) {
|
|
175
|
+
int frameStart = findFrameStart(buffer, 0);
|
|
176
|
+
if (frameStart < 0) {
|
|
177
|
+
int keep = Math.min(buffer.length, EXPECTED_PREFIX.length - 1);
|
|
178
|
+
int drop = buffer.length - keep;
|
|
179
|
+
if (drop > 0) {
|
|
180
|
+
totalResyncDropBytes += drop;
|
|
181
|
+
headerLooksStable = false;
|
|
182
|
+
Log.w(
|
|
183
|
+
TAG,
|
|
184
|
+
"AR99 Opus resync drop(no header)"
|
|
185
|
+
+ " notify#="
|
|
186
|
+
+ totalNotifyCount
|
|
187
|
+
+ " dropBytes="
|
|
188
|
+
+ drop
|
|
189
|
+
+ " keepTail="
|
|
190
|
+
+ keep
|
|
191
|
+
+ " tailHex="
|
|
192
|
+
+ hex(Arrays.copyOfRange(buffer, drop, buffer.length), keep));
|
|
193
|
+
buffer = Arrays.copyOfRange(buffer, drop, buffer.length);
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (frameStart > 0) {
|
|
199
|
+
totalResyncDropBytes += frameStart;
|
|
200
|
+
headerLooksStable = false;
|
|
201
|
+
headerMismatch++;
|
|
202
|
+
totalHeaderMismatchCount++;
|
|
203
|
+
Log.w(
|
|
204
|
+
TAG,
|
|
205
|
+
"AR99 Opus resync shift"
|
|
206
|
+
+ " notify#="
|
|
207
|
+
+ totalNotifyCount
|
|
208
|
+
+ " dropBytes="
|
|
209
|
+
+ frameStart
|
|
210
|
+
+ " bufLen="
|
|
211
|
+
+ buffer.length
|
|
212
|
+
+ " droppedHex="
|
|
213
|
+
+ hex(Arrays.copyOfRange(buffer, 0, frameStart), Math.min(frameStart, 24)));
|
|
214
|
+
buffer = Arrays.copyOfRange(buffer, frameStart, buffer.length);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (buffer.length < FRAME_BYTES) {
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
byte[] frame = Arrays.copyOfRange(buffer, 0, FRAME_BYTES);
|
|
222
|
+
buffer = Arrays.copyOfRange(buffer, FRAME_BYTES, buffer.length);
|
|
223
|
+
totalFrameCount++;
|
|
224
|
+
|
|
225
|
+
boolean prefixOk = true;
|
|
226
|
+
for (int i = 0; i < EXPECTED_PREFIX.length; i++) {
|
|
227
|
+
if (frame[i] != EXPECTED_PREFIX[i]) {
|
|
228
|
+
prefixOk = false;
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (!prefixOk) {
|
|
233
|
+
headerMismatch++;
|
|
234
|
+
totalHeaderMismatchCount++;
|
|
235
|
+
headerLooksStable = false;
|
|
236
|
+
if (totalFrameCount <= MAX_DIAG_FRAMES || !headerLooksStable) {
|
|
237
|
+
Log.w(
|
|
238
|
+
TAG,
|
|
239
|
+
"AR99 Opus frame header mismatch"
|
|
240
|
+
+ " frame#="
|
|
241
|
+
+ totalFrameCount
|
|
242
|
+
+ " notify#="
|
|
243
|
+
+ totalNotifyCount
|
|
244
|
+
+ " remain="
|
|
245
|
+
+ buffer.length
|
|
246
|
+
+ " frameHex="
|
|
247
|
+
+ hex(frame, FRAME_BYTES));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
byte[] opusPayload = Arrays.copyOfRange(frame, HEADER_BYTES, FRAME_BYTES);
|
|
252
|
+
if (opusPayload.length == 0) {
|
|
253
|
+
skipPayload++;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
byte[] pcm = decodePayload(opusPayload);
|
|
258
|
+
if (pcm != null && pcm.length > 0) {
|
|
259
|
+
pcmFrames++;
|
|
260
|
+
pcmBytesOut += pcm.length;
|
|
261
|
+
if (callback != null) {
|
|
262
|
+
callback.onPcmDecoded(pcm);
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
decodeFail++;
|
|
266
|
+
totalDecodeFailCount++;
|
|
267
|
+
if (totalFrameCount <= MAX_DIAG_FRAMES || !headerLooksStable) {
|
|
268
|
+
Log.w(
|
|
269
|
+
TAG,
|
|
270
|
+
"AR99 Opus frame decode failed"
|
|
271
|
+
+ " frame#="
|
|
272
|
+
+ totalFrameCount
|
|
273
|
+
+ " notify#="
|
|
274
|
+
+ totalNotifyCount
|
|
275
|
+
+ " prefixOk="
|
|
276
|
+
+ prefixOk
|
|
277
|
+
+ " frameHex="
|
|
278
|
+
+ hex(frame, FRAME_BYTES)
|
|
279
|
+
+ " payloadHex="
|
|
280
|
+
+ hex(opusPayload, opusPayload.length));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (chunk.length != FRAME_BYTES
|
|
286
|
+
|| oldBufferLen != 0
|
|
287
|
+
|| decodeFail > 0
|
|
288
|
+
|| headerMismatch > 0
|
|
289
|
+
|| skipPayload > 0
|
|
290
|
+
|| pcmFrames == 0
|
|
291
|
+
|| !headerLooksStable) {
|
|
292
|
+
Log.d(
|
|
293
|
+
TAG,
|
|
294
|
+
"AR99 Opus decode summary"
|
|
295
|
+
+ " notify#="
|
|
296
|
+
+ totalNotifyCount
|
|
297
|
+
+ " inLen="
|
|
298
|
+
+ chunk.length
|
|
299
|
+
+ " pcmFrames="
|
|
300
|
+
+ pcmFrames
|
|
301
|
+
+ " decodeFail="
|
|
302
|
+
+ decodeFail
|
|
303
|
+
+ " headerMismatch="
|
|
304
|
+
+ headerMismatch
|
|
305
|
+
+ " skipPayload="
|
|
306
|
+
+ skipPayload
|
|
307
|
+
+ " bufRemain="
|
|
308
|
+
+ buffer.length
|
|
309
|
+
+ " totals={frames="
|
|
310
|
+
+ totalFrameCount
|
|
311
|
+
+ ",fail="
|
|
312
|
+
+ totalDecodeFailCount
|
|
313
|
+
+ ",headerMismatch="
|
|
314
|
+
+ totalHeaderMismatchCount
|
|
315
|
+
+ ",shortChunk="
|
|
316
|
+
+ totalShortChunkCount
|
|
317
|
+
+ ",resyncDropBytes="
|
|
318
|
+
+ totalResyncDropBytes
|
|
319
|
+
+ "}");
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (callback != null) {
|
|
323
|
+
callback.onNotifyProcessed(chunk.length, pcmFrames, pcmBytesOut);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private byte[] decodePayload(byte[] opusPayload) {
|
|
328
|
+
if (opusPayload == null || opusPayload.length == 0) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
byte[] pcm = new byte[MAX_FRAME_SAMPLES * CHANNELS * 2];
|
|
332
|
+
try {
|
|
333
|
+
int samples = decoder.decode(opusPayload, 0, opusPayload.length, pcm, 0, MAX_FRAME_SAMPLES, false);
|
|
334
|
+
if (samples <= 0) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
int bytes = samples * CHANNELS * 2;
|
|
338
|
+
return Arrays.copyOf(pcm, bytes);
|
|
339
|
+
} catch (OpusException | AssertionError | RuntimeException e) {
|
|
340
|
+
Log.w(
|
|
341
|
+
TAG,
|
|
342
|
+
"AR99 Opus decode failed len="
|
|
343
|
+
+ opusPayload.length
|
|
344
|
+
+ ", payloadHex="
|
|
345
|
+
+ hex(opusPayload, opusPayload.length)
|
|
346
|
+
+ ": "
|
|
347
|
+
+ e.getMessage());
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private static int findFrameStart(byte[] data, int fromIndex) {
|
|
353
|
+
if (data == null || data.length < EXPECTED_PREFIX.length) {
|
|
354
|
+
return -1;
|
|
355
|
+
}
|
|
356
|
+
int max = data.length - EXPECTED_PREFIX.length;
|
|
357
|
+
for (int i = Math.max(0, fromIndex); i <= max; i++) {
|
|
358
|
+
boolean match = true;
|
|
359
|
+
for (int j = 0; j < EXPECTED_PREFIX.length; j++) {
|
|
360
|
+
if (data[i + j] != EXPECTED_PREFIX[j]) {
|
|
361
|
+
match = false;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (match) {
|
|
366
|
+
return i;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return -1;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private static String hex(byte[] data, int maxBytes) {
|
|
373
|
+
if (data == null || data.length == 0 || maxBytes <= 0) {
|
|
374
|
+
return "";
|
|
375
|
+
}
|
|
376
|
+
int len = Math.min(data.length, maxBytes);
|
|
377
|
+
StringBuilder sb = new StringBuilder(len * 3);
|
|
378
|
+
for (int i = 0; i < len; i++) {
|
|
379
|
+
if (i > 0) {
|
|
380
|
+
sb.append(' ');
|
|
381
|
+
}
|
|
382
|
+
sb.append(String.format("%02X", data[i] & 0xFF));
|
|
383
|
+
}
|
|
384
|
+
if (data.length > len) {
|
|
385
|
+
sb.append("...");
|
|
386
|
+
}
|
|
387
|
+
return sb.toString();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private static byte[] concat(byte[] a, byte[] b) {
|
|
391
|
+
if (a.length == 0) {
|
|
392
|
+
return Arrays.copyOf(b, b.length);
|
|
393
|
+
}
|
|
394
|
+
byte[] out = Arrays.copyOf(a, a.length + b.length);
|
|
395
|
+
System.arraycopy(b, 0, out, a.length, b.length);
|
|
396
|
+
return out;
|
|
397
|
+
}
|
|
398
|
+
}
|