eufy-security-client 3.8.0-dev.14 → 3.8.0-dev.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Normalize a buffer of ADTS audio data by scanning for ADTS frames and splitting
3
+ * any multi-RDB frames into individual single-RDB frames.
4
+ *
5
+ * The input buffer may contain one or more concatenated ADTS frames. Non-ADTS
6
+ * data (or data without a valid sync word) is returned unchanged in a
7
+ * single-element array.
8
+ *
9
+ * For the common case of a single-RDB frame, no allocation occurs — the original
10
+ * buffer region is returned via subarray.
11
+ */
12
+ export declare function normalizeAdtsFrames(data: Buffer): Buffer[];
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeAdtsFrames = normalizeAdtsFrames;
4
+ const logging_1 = require("../logging");
5
+ // ADTS header: 7 bytes (protection_absent=1) or 9 bytes (protection_absent=0).
6
+ // Byte 6 bits 0-1: number_of_raw_data_blocks_in_frame (0 = 1 RDB, 3 = 4 RDBs).
7
+ // Multi-RDB frames pack several AAC raw data blocks into one ADTS frame.
8
+ // Most decoders (including FFmpeg's native aac) only support single-RDB frames,
9
+ // so we split multi-RDB frames into individual single-RDB ADTS frames.
10
+ const ADTS_SYNC_WORD = 0xfff;
11
+ const ADTS_MIN_HEADER_SIZE = 7;
12
+ /**
13
+ * Returns true when the first two bytes contain an ADTS sync word (0xFFF).
14
+ */
15
+ function hasAdtsSyncWord(buf, offset) {
16
+ if (offset + 1 >= buf.length)
17
+ return false;
18
+ return ((buf[offset] << 4) | (buf[offset + 1] >> 4)) === ADTS_SYNC_WORD;
19
+ }
20
+ /**
21
+ * Extract the 13-bit frame_length field from an ADTS header starting at `offset`.
22
+ * frame_length includes the header itself.
23
+ */
24
+ function getFrameLength(buf, offset) {
25
+ return ((buf[offset + 3] & 0x03) << 11) | (buf[offset + 4] << 3) | ((buf[offset + 5] >> 5) & 0x07);
26
+ }
27
+ /**
28
+ * Extract the 2-bit number_of_raw_data_blocks_in_frame field (0-indexed, so actual
29
+ * count is value + 1).
30
+ */
31
+ function getNumRawDataBlocks(buf, offset) {
32
+ return buf[offset + 6] & 0x03;
33
+ }
34
+ /**
35
+ * Returns true when `protection_absent` is 0 (CRC present).
36
+ */
37
+ function hasCrc(buf, offset) {
38
+ return (buf[offset + 1] & 0x01) === 0;
39
+ }
40
+ /**
41
+ * Build a single-RDB ADTS frame by prepending a rewritten 7-byte header (without
42
+ * CRC) to a raw data block. The template header is cloned from the original
43
+ * multi-RDB frame and patched:
44
+ * - frame_length → 7 + rdbData.length
45
+ * - num_rdb → 0 (meaning 1 block)
46
+ * - protection_absent → 1 (no CRC, since we don't recompute it)
47
+ */
48
+ function buildSingleRdbFrame(templateHeader, headerOffset, rdbData) {
49
+ const newLen = ADTS_MIN_HEADER_SIZE + rdbData.length;
50
+ const out = Buffer.allocUnsafe(newLen);
51
+ // Copy the 7-byte header template.
52
+ templateHeader.copy(out, 0, headerOffset, headerOffset + ADTS_MIN_HEADER_SIZE);
53
+ // Set protection_absent = 1 (bit 0 of byte 1).
54
+ out[1] |= 0x01;
55
+ // Rewrite frame_length (13 bits spanning bytes 3-5).
56
+ out[3] = (out[3] & 0xfc) | ((newLen >> 11) & 0x03);
57
+ out[4] = (newLen >> 3) & 0xff;
58
+ out[5] = ((newLen & 0x07) << 5) | (out[5] & 0x1f);
59
+ // Set number_of_raw_data_blocks_in_frame = 0 (bits 0-1 of byte 6).
60
+ out[6] &= 0xfc;
61
+ // Append the raw data block.
62
+ rdbData.copy(out, ADTS_MIN_HEADER_SIZE);
63
+ return out;
64
+ }
65
+ /**
66
+ * Split a single multi-RDB ADTS frame into individual single-RDB frames.
67
+ *
68
+ * For multi-RDB frames the spec (ISO 14496-3 §1.A.3) places a
69
+ * `raw_data_block_position` table after the header (and CRC when present) that
70
+ * gives the byte offset of each RDB relative to the start of the first RDB.
71
+ *
72
+ * Layout:
73
+ * [7-byte header]
74
+ * [2-byte CRC] ← only if protection_absent=0
75
+ * [2-byte position] × (N) ← N = number_of_raw_data_blocks_in_frame (i.e. num_rdb)
76
+ * [RDB 0 data ...]
77
+ * [RDB 1 data ...]
78
+ * ...
79
+ *
80
+ * The position entries give byte offsets relative to the start of RDB 0.
81
+ * RDB 0 implicitly starts at offset 0. Position[i] gives the start of RDB i+1.
82
+ */
83
+ function splitMultiRdbFrame(buf, offset, frameLength) {
84
+ const numRdbMinusOne = getNumRawDataBlocks(buf, offset); // 1-3
85
+ const numRdb = numRdbMinusOne + 1;
86
+ const crcPresent = hasCrc(buf, offset);
87
+ // Calculate where the position table starts.
88
+ let posTableStart = offset + ADTS_MIN_HEADER_SIZE;
89
+ if (crcPresent) {
90
+ posTableStart += 2; // skip CRC-16
91
+ }
92
+ // We need (numRdb - 1) position entries, each 2 bytes.
93
+ const posTableSize = (numRdb - 1) * 2;
94
+ const rdbDataStart = posTableStart + posTableSize;
95
+ if (rdbDataStart > offset + frameLength) {
96
+ logging_1.rootP2PLogger.warn("ADTS multi-RDB frame too short for position table", {
97
+ frameLength,
98
+ numRdb,
99
+ rdbDataStart: rdbDataStart - offset,
100
+ });
101
+ return [buf.subarray(offset, offset + frameLength)];
102
+ }
103
+ // Read position table: offsets relative to the start of RDB 0.
104
+ const rdbOffsets = [0]; // RDB 0 always starts at relative offset 0
105
+ for (let i = 0; i < numRdb - 1; i++) {
106
+ rdbOffsets.push(buf.readUInt16BE(posTableStart + i * 2));
107
+ }
108
+ const rdbSectionEnd = offset + frameLength;
109
+ const result = [];
110
+ for (let i = 0; i < numRdb; i++) {
111
+ const start = rdbDataStart + rdbOffsets[i];
112
+ const end = i < numRdb - 1 ? rdbDataStart + rdbOffsets[i + 1] : rdbSectionEnd;
113
+ if (start >= end || end > rdbSectionEnd) {
114
+ logging_1.rootP2PLogger.warn("ADTS multi-RDB frame has invalid RDB boundaries", {
115
+ rdbIndex: i,
116
+ start: start - offset,
117
+ end: end - offset,
118
+ frameLength,
119
+ });
120
+ // Return remaining data as a single frame rather than losing it.
121
+ if (start < rdbSectionEnd) {
122
+ result.push(buildSingleRdbFrame(buf, offset, buf.subarray(start, rdbSectionEnd)));
123
+ }
124
+ break;
125
+ }
126
+ result.push(buildSingleRdbFrame(buf, offset, buf.subarray(start, end)));
127
+ }
128
+ return result;
129
+ }
130
+ /**
131
+ * Normalize a buffer of ADTS audio data by scanning for ADTS frames and splitting
132
+ * any multi-RDB frames into individual single-RDB frames.
133
+ *
134
+ * The input buffer may contain one or more concatenated ADTS frames. Non-ADTS
135
+ * data (or data without a valid sync word) is returned unchanged in a
136
+ * single-element array.
137
+ *
138
+ * For the common case of a single-RDB frame, no allocation occurs — the original
139
+ * buffer region is returned via subarray.
140
+ */
141
+ function normalizeAdtsFrames(data) {
142
+ if (data.length < ADTS_MIN_HEADER_SIZE || !hasAdtsSyncWord(data, 0)) {
143
+ // Not ADTS data — return as-is.
144
+ return [data];
145
+ }
146
+ const result = [];
147
+ let pos = 0;
148
+ while (pos + ADTS_MIN_HEADER_SIZE <= data.length) {
149
+ if (!hasAdtsSyncWord(data, pos)) {
150
+ // Lost sync — push remaining bytes and stop.
151
+ logging_1.rootP2PLogger.debug("ADTS sync lost, pushing remaining bytes as-is", {
152
+ offset: pos,
153
+ remaining: data.length - pos,
154
+ });
155
+ result.push(data.subarray(pos));
156
+ break;
157
+ }
158
+ const frameLength = getFrameLength(data, pos);
159
+ if (frameLength < ADTS_MIN_HEADER_SIZE || pos + frameLength > data.length) {
160
+ // Incomplete or invalid frame — push remaining bytes.
161
+ logging_1.rootP2PLogger.debug("ADTS frame length invalid or truncated", {
162
+ offset: pos,
163
+ frameLength,
164
+ available: data.length - pos,
165
+ });
166
+ result.push(data.subarray(pos));
167
+ break;
168
+ }
169
+ const numRdbMinusOne = getNumRawDataBlocks(data, pos);
170
+ if (numRdbMinusOne === 0) {
171
+ // Single RDB — zero-copy, return subarray of original buffer.
172
+ result.push(data.subarray(pos, pos + frameLength));
173
+ }
174
+ else {
175
+ // Multi-RDB — split into individual frames.
176
+ const splitFrames = splitMultiRdbFrame(data, pos, frameLength);
177
+ for (const frame of splitFrames) {
178
+ result.push(frame);
179
+ }
180
+ }
181
+ pos += frameLength;
182
+ }
183
+ return result.length > 0 ? result : [data];
184
+ }
185
+ //# sourceMappingURL=adts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adts.js","sourceRoot":"","sources":["../../src/p2p/adts.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@ const ble_1 = require("./ble");
18
18
  const http_1 = require("../http");
19
19
  const utils_3 = require("../utils");
20
20
  const logging_1 = require("../logging");
21
+ const adts_1 = require("./adts");
21
22
  class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
22
23
  MAX_RETRIES = 10;
23
24
  MAX_COMMAND_RESULT_WAIT = 30 * 1000;
@@ -2059,7 +2060,18 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
2059
2060
  this.emitStreamStartEvent(message.dataType);
2060
2061
  }
2061
2062
  }
2062
- this.currentMessageState[message.dataType].audioStream?.push(audio_data);
2063
+ {
2064
+ const codec = this.currentMessageState[message.dataType].p2pStreamMetadata.audioCodec;
2065
+ const stream = this.currentMessageState[message.dataType].audioStream;
2066
+ if (stream && (codec === types_1.AudioCodec.AAC || codec === types_1.AudioCodec.AAC_LC)) {
2067
+ for (const frame of (0, adts_1.normalizeAdtsFrames)(audio_data)) {
2068
+ stream.push(frame);
2069
+ }
2070
+ }
2071
+ else {
2072
+ stream?.push(audio_data);
2073
+ }
2074
+ }
2063
2075
  break;
2064
2076
  default:
2065
2077
  logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Not implemented message`, {