@yume-chan/media-codec 3.0.0-beta.0
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/LICENSE +21 -0
- package/README.md +3 -0
- package/esm/av1.d.ts +470 -0
- package/esm/av1.d.ts.map +1 -0
- package/esm/av1.js +666 -0
- package/esm/av1.js.map +1 -0
- package/esm/format.d.ts +4 -0
- package/esm/format.d.ts.map +1 -0
- package/esm/format.js +44 -0
- package/esm/format.js.map +1 -0
- package/esm/h264.d.ts +47 -0
- package/esm/h264.d.ts.map +1 -0
- package/esm/h264.js +240 -0
- package/esm/h264.js.map +1 -0
- package/esm/h265.d.ts +501 -0
- package/esm/h265.d.ts.map +1 -0
- package/esm/h265.js +1095 -0
- package/esm/h265.js.map +1 -0
- package/esm/index.d.ts +6 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +6 -0
- package/esm/index.js.map +1 -0
- package/esm/nalu.d.ts +28 -0
- package/esm/nalu.d.ts.map +1 -0
- package/esm/nalu.js +250 -0
- package/esm/nalu.js.map +1 -0
- package/package.json +43 -0
- package/src/av1.ts +782 -0
- package/src/format.ts +53 -0
- package/src/h264.ts +306 -0
- package/src/h265.ts +1393 -0
- package/src/index.ts +5 -0
- package/src/nalu.ts +304 -0
- package/tsconfig.build.json +3 -0
package/src/index.ts
ADDED
package/src/nalu.ts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split NAL units from an H.264/H.265 Annex B stream.
|
|
3
|
+
*
|
|
4
|
+
* The input is not modified.
|
|
5
|
+
* The returned NAL units are views of the input (no memory allocation nor copy),
|
|
6
|
+
* and still contains emulation prevention bytes.
|
|
7
|
+
*
|
|
8
|
+
* This methods returns a generator, so it can be stopped immediately
|
|
9
|
+
* after the interested NAL unit is found.
|
|
10
|
+
*/
|
|
11
|
+
export function* annexBSplitNalu(buffer: Uint8Array): Generator<Uint8Array> {
|
|
12
|
+
// -1 means we haven't found the first start code
|
|
13
|
+
let start = -1;
|
|
14
|
+
// How many `0x00`s in a row we have counted
|
|
15
|
+
let zeroCount = 0;
|
|
16
|
+
let inEmulation = false;
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < buffer.length; i += 1) {
|
|
19
|
+
const byte = buffer[i]!;
|
|
20
|
+
|
|
21
|
+
if (inEmulation) {
|
|
22
|
+
if (byte > 0x03) {
|
|
23
|
+
// `0x00000304` or larger are invalid
|
|
24
|
+
throw new Error("Invalid data");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
inEmulation = false;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (byte === 0x00) {
|
|
32
|
+
zeroCount += 1;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const prevZeroCount = zeroCount;
|
|
37
|
+
zeroCount = 0;
|
|
38
|
+
|
|
39
|
+
if (start === -1) {
|
|
40
|
+
// 0x000001 is the start code
|
|
41
|
+
// But it can be preceded by any number of zeros
|
|
42
|
+
// So 2 is the minimal
|
|
43
|
+
if (prevZeroCount >= 2 && byte === 0x01) {
|
|
44
|
+
// Found start of first NAL unit
|
|
45
|
+
start = i + 1;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Not begin with start code
|
|
50
|
+
throw new Error("Invalid data");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (prevZeroCount < 2) {
|
|
54
|
+
// zero or one `0x00`s are acceptable
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (byte === 0x01) {
|
|
59
|
+
// Found another NAL unit
|
|
60
|
+
yield buffer.subarray(start, i - prevZeroCount);
|
|
61
|
+
|
|
62
|
+
start = i + 1;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (prevZeroCount > 2) {
|
|
67
|
+
// Too much `0x00`s
|
|
68
|
+
throw new Error("Invalid data");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
switch (byte) {
|
|
72
|
+
case 0x02:
|
|
73
|
+
// Didn't find why, but 7.4.1 NAL unit semantics forbids `0x000002` appearing in NAL units
|
|
74
|
+
throw new Error("Invalid data");
|
|
75
|
+
case 0x03:
|
|
76
|
+
// `0x000003` is the "emulation_prevention_three_byte"
|
|
77
|
+
// `0x00000300`, `0x00000301`, `0x00000302` and `0x00000303` represent
|
|
78
|
+
// `0x000000`, `0x000001`, `0x000002` and `0x000003` respectively
|
|
79
|
+
inEmulation = true;
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
// `0x000004` or larger are as-is
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (inEmulation) {
|
|
88
|
+
throw new Error("Invalid data");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
yield buffer.subarray(start, buffer.length);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export class NaluSodbBitReader {
|
|
95
|
+
readonly #nalu: Uint8Array;
|
|
96
|
+
// logical length is `#byteLength * 8 + (7 - #stopBitIndex)`
|
|
97
|
+
readonly #byteLength: number;
|
|
98
|
+
readonly #stopBitIndex: number;
|
|
99
|
+
|
|
100
|
+
#zeroCount = 0;
|
|
101
|
+
|
|
102
|
+
// logical position is `#bytePosition * 8 + (7 - #bitPosition)`
|
|
103
|
+
#bytePosition = 0;
|
|
104
|
+
#bitPosition = 7;
|
|
105
|
+
#byte = 0;
|
|
106
|
+
|
|
107
|
+
get byteLength() {
|
|
108
|
+
return this.#byteLength;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get stopBitIndex() {
|
|
112
|
+
return this.#stopBitIndex;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get bytePosition() {
|
|
116
|
+
return this.#bytePosition;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get bitPosition() {
|
|
120
|
+
return this.#bitPosition;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get ended() {
|
|
124
|
+
return (
|
|
125
|
+
this.#bytePosition >= this.#byteLength &&
|
|
126
|
+
this.#bitPosition <= this.#stopBitIndex
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
constructor(nalu: Uint8Array) {
|
|
131
|
+
this.#nalu = nalu;
|
|
132
|
+
|
|
133
|
+
// Search for the last bit being `1`, also known as the stop bit
|
|
134
|
+
for (let i = nalu.length - 1; i >= 0; i -= 1) {
|
|
135
|
+
if (this.#nalu[i] === 0) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const byte = nalu[i]!;
|
|
140
|
+
for (let j = 0; j < 8; j += 1) {
|
|
141
|
+
if (((byte >> j) & 1) === 1) {
|
|
142
|
+
this.#byteLength = i;
|
|
143
|
+
this.#stopBitIndex = j;
|
|
144
|
+
this.#loadByte();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
throw new Error("Stop bit not found");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#loadByte() {
|
|
154
|
+
this.#byte = this.#nalu[this.#bytePosition]!;
|
|
155
|
+
|
|
156
|
+
// If the current sequence is `0x000003`, skip to the next byte.
|
|
157
|
+
// `annexBSplitNalu` had validated the input, so skip the check here
|
|
158
|
+
if (this.#zeroCount === 2 && this.#byte === 3) {
|
|
159
|
+
this.#zeroCount = 0;
|
|
160
|
+
this.#bytePosition += 1;
|
|
161
|
+
// Call `#loadByte` again, because if the next byte is `0x00`,
|
|
162
|
+
// it need to be counted in `#zeroCount` as well.
|
|
163
|
+
this.#loadByte();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// `0x00000301` becomes `0x000001`, so only the `0x03` byte needs to be skipped
|
|
168
|
+
// All `0x00` bytes are returned as-is
|
|
169
|
+
if (this.#byte === 0) {
|
|
170
|
+
this.#zeroCount += 1;
|
|
171
|
+
} else {
|
|
172
|
+
this.#zeroCount = 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
next() {
|
|
177
|
+
if (this.ended) {
|
|
178
|
+
throw new Error("Bit index out of bounds");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const value = (this.#byte >> this.#bitPosition) & 1;
|
|
182
|
+
|
|
183
|
+
this.#bitPosition -= 1;
|
|
184
|
+
if (this.#bitPosition < 0) {
|
|
185
|
+
this.#bytePosition += 1;
|
|
186
|
+
this.#bitPosition = 7;
|
|
187
|
+
this.#loadByte();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
read(length: number): number {
|
|
194
|
+
if (length > 32) {
|
|
195
|
+
throw new Error("Read length too large");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let result = 0;
|
|
199
|
+
for (let i = 0; i < length; i += 1) {
|
|
200
|
+
result = (result << 1) | this.next();
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Throws an error if the current position is invalid for `skip`.
|
|
207
|
+
*
|
|
208
|
+
* Usually it will throw if `ended` is `true`,
|
|
209
|
+
* except when the bit position is at the stop bit,
|
|
210
|
+
* in which case `ended` will be `true`, but it won't throw.
|
|
211
|
+
* `skip` can skip all remaining bits, and stop at the end position.
|
|
212
|
+
* The next `next` call will throw since there is no more bits to read.
|
|
213
|
+
*/
|
|
214
|
+
#checkSkipPosition() {
|
|
215
|
+
if (
|
|
216
|
+
this.#bytePosition >= this.#byteLength &&
|
|
217
|
+
this.#bitPosition < this.#stopBitIndex
|
|
218
|
+
) {
|
|
219
|
+
throw new Error("Bit index out of bounds");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
skip(length: number) {
|
|
224
|
+
if (length <= this.#bitPosition + 1) {
|
|
225
|
+
this.#bitPosition -= length;
|
|
226
|
+
this.#checkSkipPosition();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Because of emulation prevention bytes,
|
|
231
|
+
// we don't know how many bits are left in the NAL,
|
|
232
|
+
// nor how many bits should be skipped.
|
|
233
|
+
// So we need to check each byte.
|
|
234
|
+
|
|
235
|
+
length -= this.#bitPosition + 1;
|
|
236
|
+
this.#bytePosition += 1;
|
|
237
|
+
this.#bitPosition = 7;
|
|
238
|
+
this.#loadByte();
|
|
239
|
+
this.#checkSkipPosition();
|
|
240
|
+
|
|
241
|
+
for (; length >= 8; length -= 8) {
|
|
242
|
+
this.#bytePosition += 1;
|
|
243
|
+
this.#loadByte();
|
|
244
|
+
this.#checkSkipPosition();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
this.#bitPosition = 7 - length;
|
|
248
|
+
this.#checkSkipPosition();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
decodeExponentialGolombNumber(): number {
|
|
252
|
+
let length = 0;
|
|
253
|
+
while (this.next() === 0) {
|
|
254
|
+
length += 1;
|
|
255
|
+
}
|
|
256
|
+
if (length === 0) {
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
return ((1 << length) | this.read(length)) - 1;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#save() {
|
|
263
|
+
return {
|
|
264
|
+
zeroCount: this.#zeroCount,
|
|
265
|
+
bytePosition: this.#bytePosition,
|
|
266
|
+
bitPosition: this.#bitPosition,
|
|
267
|
+
byte: this.#byte,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
#restore(state: {
|
|
272
|
+
zeroCount: number;
|
|
273
|
+
bytePosition: number;
|
|
274
|
+
bitPosition: number;
|
|
275
|
+
byte: number;
|
|
276
|
+
}) {
|
|
277
|
+
this.#zeroCount = state.zeroCount;
|
|
278
|
+
this.#bytePosition = state.bytePosition;
|
|
279
|
+
this.#bitPosition = state.bitPosition;
|
|
280
|
+
this.#byte = state.byte;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
peek(length: number) {
|
|
284
|
+
const state = this.#save();
|
|
285
|
+
const result = this.read(length);
|
|
286
|
+
this.#restore(state);
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
readBytes(length: number): Uint8Array {
|
|
291
|
+
const result = new Uint8Array(length);
|
|
292
|
+
for (let i = 0; i < length; i += 1) {
|
|
293
|
+
result[i] = this.read(8);
|
|
294
|
+
}
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
peekBytes(length: number): Uint8Array {
|
|
299
|
+
const state = this.#save();
|
|
300
|
+
const result = this.readBytes(length);
|
|
301
|
+
this.#restore(state);
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
}
|