unbikit 0.7.0 → 0.9.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/README.md CHANGED
@@ -3,13 +3,10 @@
3
3
  [![minzipped bundle size](https://img.shields.io/bundlephobia/minzip/unbikit?style=flat-square)](https://bundlephobia.com/package/unbikit)
4
4
  [![types included](https://img.shields.io/npm/types/unbikit?style=flat-square)](https://www.npmjs.com/package/unbikit)
5
5
 
6
- > ⚠️
7
- > This package is at an early development stage. Expect API changes!
8
-
9
6
  <img src="/app/src/images/unbikit-logo.svg" alt="logo of unbikit" width="24" height="24"> unbikit
10
7
  (_un-bik-ɪt_) is a decoder for `.bik` ([Bink Video](https://en.wikipedia.org/wiki/Bink_Video))
11
- files that can be used to play or transcode videos using the Web Streams API.
12
- The format was first released in 1999 and has since been used in many classic computer games.
8
+ files that can be used to play or transcode videos. The format was first released in 1999 and has
9
+ since been used in many classic computer games.
13
10
 
14
11
  ⭐ [Documentation](https://blennies.github.io/unbikit/) and a
15
12
  ⭐ [video player demo](https://blennies.github.io/unbikit/demo/)
@@ -20,9 +17,10 @@ are available, as well as a
20
17
 
21
18
  - Supports Bink 1, revisions `c` to `i` inclusive
22
19
  - Handles demuxing and decompression of audio and video streams
23
- - TypeScript/JavaScript only (no WASM or native code)
20
+ - TypeScript/JavaScript (no WASM or native code)
24
21
  - No dependencies
25
- - Uses Web Streams API for efficient reading of video data
22
+ - Straightforward API: supply a video via `Blob`, `File`, `Request` or `URL`
23
+ - the Web Streams API will be used where possible for efficient reading of video data
26
24
  - Isomorphic
27
25
  - runs on client/server runtimes that support at least ES2022
28
26
  - can be run with older runtimes by using the syntax lowering feature of some bundlers
package/dist/unbikit.d.ts CHANGED
@@ -4,31 +4,48 @@
4
4
  * not used by the decoder for any subsequent processing, so can be used by an application freely.
5
5
  */
6
6
  interface BikVideoFrame {
7
+ /**
8
+ * Coded width of the video frame (in pixels).
9
+ */
7
10
  width: number;
11
+ /**
12
+ * Coded height of the video frame (in pixels).
13
+ */
8
14
  height: number;
15
+ /**
16
+ * Pixel data for the video frame, encoded in Planar YUV 4:2:0 format with an optional alpha
17
+ * channel. The planes are stored in the order: Y, U, V, alpha. Each of the Y and alpha planes
18
+ * occupy 4 times as much space in the buffer as either the U or V plane (as would be expected
19
+ * given the encoding format), and the total buffer size is set to be just enough to hold all
20
+ * of the planes.
21
+ */
9
22
  yuv: Uint8Array<ArrayBuffer>;
23
+ /**
24
+ * The number of pixels per line of the video frame, stored as an array of per-plane values in
25
+ * the order: Y, U, V, alpha.
26
+ */
10
27
  lineSize: number[];
11
28
  }
12
29
  //#endregion
13
30
  //#region src/bik-decoder.d.ts
14
31
  /**
15
- * Decoded header of a BIK file.
32
+ * Decoded header of a BIK data source.
16
33
  */
17
34
  interface BikHeader {
18
35
  /**
19
- * Version of the encoded file format.
36
+ * Version of the encoding format.
20
37
  */
21
38
  version: 1 | 2;
22
39
  /**
23
- * Sub-version of the encoded file format.
40
+ * Sub-version of the encoding format.
24
41
  */
25
42
  subVersion: number;
26
43
  /**
27
- * Total size of the encoded file (in bytes).
44
+ * Total size of the encoded video data (in bytes).
28
45
  */
29
- fileSize: number;
46
+ totalSize: number;
30
47
  /**
31
- * Number of (video) frames stored in the file.
48
+ * Number of frames in the video.
32
49
  *
33
50
  * Note that audio is stored with each frame but doesn't necessarily correspond to the audio
34
51
  * that should be played when the video frame is shown.
@@ -48,20 +65,20 @@ interface BikHeader {
48
65
  */
49
66
  height: number;
50
67
  /**
51
- * Number of frames per second that the video in the file should be played at.
68
+ * Number of frames per second that the video should be played at.
52
69
  */
53
70
  fps: number;
54
71
  /**
55
- * Flags providing additional information about the encoded file.
72
+ * Flags providing additional information about the encoded video.
56
73
  */
57
74
  videoFlags: {
58
75
  /**
59
- * When `true`, the encoded file contains an alpha plane for each frame. Otherwise the file
76
+ * When `true`, the encoded video contains an alpha plane for each frame. Otherwise the video
60
77
  * contains no alpha information.
61
78
  */
62
79
  hasAlpha: boolean;
63
80
  /**
64
- * When `true`, the U and V planes in the encoded file should be swapped during the decoding
81
+ * When `true`, the U and V planes in the encoded video should be swapped during the decoding
65
82
  * process.
66
83
  *
67
84
  * This is a value intended for internal use by the decoder and should not be needed by
@@ -69,7 +86,7 @@ interface BikHeader {
69
86
  */
70
87
  hasSwappedUVPlanes: boolean;
71
88
  /**
72
- * When `true`, the encoded file doesn't contain U or V planes. Otherwise the file contains
89
+ * When `true`, the encoded video doesn't contain U or V planes. Otherwise the video contains
73
90
  * both U and V planes.
74
91
  */
75
92
  isGrayscale: boolean;
@@ -87,55 +104,84 @@ interface BikHeader {
87
104
  scaling: number;
88
105
  };
89
106
  /**
90
- * Total number of audio tracks stored in the encoded file.
107
+ * Total number of audio tracks stored in the encoded video.
91
108
  *
92
109
  * Each track can have one or more channels (e.g. for stereo or surround sound).
93
110
  */
94
111
  numAudioTracks: number;
95
112
  /**
96
113
  * Array containing decoded header information for each individual audio track stored in the
97
- * encoded file.
114
+ * encoded video.
98
115
  */
99
116
  audioTracks: BikAudioTrackHeader[];
100
117
  /**
101
118
  * Array containing decoded header information for each individual video frame stored in the
102
- * encoded file.
119
+ * encoded video.
103
120
  */
104
121
  frames: BikFrameHeader[];
105
122
  }
106
123
  /**
107
- * Decoded header of an audio track of a BIK file.
124
+ * Decoded header of an audio track of a BIK video.
108
125
  */
109
126
  interface BikAudioTrackHeader {
127
+ /**
128
+ * Identifier for the audio track. This may not match the index of the track in the
129
+ * {@link BikFrame.audioTracks} array.
130
+ */
110
131
  trackId: number;
132
+ /**
133
+ * Number of separate audio channels stored by the track.
134
+ *
135
+ * Stereo channels (when {@link flags stereo} is `true`) are separate planes for audio
136
+ * encoded via DCTs (when {@link flags usesDCT} is `true`), so the value of {@link numChannels}
137
+ * will include the number of stereo channels.
138
+ *
139
+ * When audio is encoded via RDFTs (when {@link flags usesDCT} is `false`) then stereo channels
140
+ * are interleaved, so the value of {@link numChannels} will _not_ include the number of
141
+ * channels. This means that for a regular stereo track (when {@link flags stereo} is `true`),
142
+ * {@link numChannels} will have the value `1`.
143
+ */
111
144
  numChannels: number;
145
+ /**
146
+ * Sample rate/frequency of the audio data (in Hz).
147
+ */
112
148
  sampleRate: number;
149
+ /**
150
+ * Flags indicating whether specific features apply to the audio track.
151
+ */
113
152
  flags: {
153
+ /**
154
+ * `true` when the audio track is stereo, otherwise `false` (usually indicating mono audio).
155
+ */
114
156
  stereo: boolean;
115
- useDCT: boolean;
157
+ /**
158
+ * `true` when the audio data is encoded using DCTs (discrete cosine transforms), otherwise
159
+ * `false` when the audio data is encoded using RDFTs (real discrete Fourier transforms).
160
+ */
161
+ usesDCT: boolean;
116
162
  };
117
163
  }
118
164
  /**
119
- * Decoded header of a (video) frame of a BIK file.
165
+ * Decoded header of a frame of a BIK video.
120
166
  */
121
167
  interface BikFrameHeader {
122
168
  /**
123
- * Offset of the start of the frame in the encoded file.
169
+ * Offset of the start of the frame in the encoded video.
124
170
  */
125
171
  offset: number;
126
172
  /**
127
- * Total size (in bytes) of the frame in the encoded file, including both audio and video that
173
+ * Total size (in bytes) of the frame in the encoded video, including both audio and video that
128
174
  * are stored with the frame.
129
175
  */
130
176
  size: number;
131
177
  /**
132
178
  * When `true`, this frame is a "key frame" that can be used as a starting point for random
133
- * access decoding. Should be `true` for at least the first frame of a BIK file.
179
+ * access decoding. Must be `true` for the first frame.
134
180
  */
135
181
  keyframe: boolean;
136
182
  }
137
183
  /**
138
- * Decoded contents of a single frame of a BIK file.
184
+ * Decoded contents of a single frame of a BIK video.
139
185
  */
140
186
  interface BikFrame {
141
187
  /**
@@ -148,7 +194,7 @@ interface BikFrame {
148
194
  videoFrame: BikVideoFrame | null;
149
195
  }
150
196
  /**
151
- * Decoded contents of a single "packet" of data for an audio track in a BIK file.
197
+ * Decoded contents of a single "packet" of data for an audio track in a BIK video.
152
198
  *
153
199
  * In each packet, audio samples are stored in one or more blocks, each block containing one or
154
200
  * more stereo-interleaved channels (so the number of stereo-interleaved channels in a stereo
@@ -174,62 +220,54 @@ interface BikAudioTrack {
174
220
  blocks: Float32Array[][];
175
221
  }
176
222
  /**
177
- * Function for returning a Web Streams API readable stream for reading sequential data
178
- * from a BIK file.
179
- *
180
- * Currently `len` is always undefined, so the function should just return a stream for
181
- * accessing the rest of the file from the given start position.
182
- *
183
- * **Advance deprecation warning:** It is intended that this function will soon be replaced with
184
- * a more flexible and easier to use system for accessing streams.
185
- *
186
- * @experimental
187
- */
188
- type GetReadStreamFn = (offset: number, len?: number | undefined) => ReadableStream<Uint8Array> | Promise<ReadableStream<Uint8Array>>;
189
- /**
190
223
  * Main class for managing the state of a BIK decoder instance. One instance can decode a single
191
- * file at a time.
224
+ * video from a single data source at a time.
192
225
  */
193
226
  declare class BikDecoder {
194
227
  #private;
195
228
  private constructor();
196
229
  /**
197
- * Decoded header of the BIK file.
230
+ * Decoded header of the video.
198
231
  * @returns Decoded header.
199
232
  */
200
233
  get header(): BikHeader | null;
201
234
  /**
202
- * Whether the audio/video streams in the BIK file can be processed by the decoder or not.
203
- * @returns `true` when the audio/video streams can be processed by the decoder, otherwise
204
- * `false`.
235
+ * Whether the audio/images in the video can be processed by the decoder or not.
236
+ * @returns `true` when the audio/images can be processed by the decoder, otherwise `false`.
205
237
  */
206
238
  get isSupported(): boolean;
207
239
  /**
208
- * Get the next frame of the BIK file and decode it.
240
+ * Get the next frame of the video and decode it.
209
241
  * @param prevFrame Optional data structure for a previously decoded frame to re-use (to reduce
210
242
  * garbage collection).
211
243
  * @returns Next decoded frame (audio and video).
212
244
  */
213
245
  getNextFrame(prevFrame?: BikFrame | null): Promise<BikFrame | null>;
214
246
  /**
215
- * Skip the specified number of frames of the BIK file. They will still be decoded as decoding
216
- * a frame can effectively require data from any number of earlier frames.
247
+ * Skip the specified number of frames of the video. They will still be decoded as decoding a
248
+ * frame can effectively require data from any number of earlier frames.
217
249
  * @param numFrames Number of frames to skip, but still decode.
218
250
  */
219
251
  skipFrames(numFrames: number): Promise<void>;
220
252
  /**
221
- * Reset the current frame index so the decoder is ready to start decoding the BIK file from the
222
- * beginning. Note that this will result in the decoder requesting a new readable stream.
253
+ * Reset the state of the decoder so it is ready to start decoding the video from the beginning.
254
+ * Note that this will result in the decoder streaming the video again from the data source.
223
255
  */
224
256
  reset(): void;
225
257
  /**
226
- * Attempt to read and parse the headers of a BIK file. If successful, return an instance of
227
- * {@link BikDecoder} for decoding the rest of the file.
228
- * @param getReadStreamFn -
229
- * Function that returns a stream for linear access to part of the file.
230
- * @returns Decoder instance. Use {@link header} to access the parsed headers.
258
+ * Attempt to read and parse the headers of a BIK video. If successful, return an instance of
259
+ * {@link BikDecoder} for decoding the rest of the video from the data source.
260
+ * @param source Data source that will provide the encoded video data.
261
+ * @returns Decoder instance. Use {@link BikDecoder.header} to access the parsed headers.
231
262
  */
232
- static open(getReadStreamFn: GetReadStreamFn): Promise<BikDecoder>;
263
+ static open_(source: Blob | File | URL | Request): Promise<BikDecoder>;
233
264
  }
265
+ /**
266
+ * Attempt to read and parse the headers of a BIK video. If successful, return an instance of
267
+ * {@link BikDecoder} for decoding the rest of the video from the data source.
268
+ * @param source Data source that will provide the encoded video data.
269
+ * @returns Decoder instance. Use {@link BikDecoder.header} to access the parsed headers.
270
+ */
271
+ declare const createBikDecoder: (source: Blob | File | URL | Request) => Promise<BikDecoder>;
234
272
  //#endregion
235
- export { type BikAudioTrack, type BikAudioTrackHeader, BikDecoder, type BikFrame, type BikFrameHeader, type BikHeader, type BikVideoFrame, type GetReadStreamFn };
273
+ export { type BikAudioTrack, type BikAudioTrackHeader, type BikDecoder, type BikFrame, type BikFrameHeader, type BikHeader, type BikVideoFrame, createBikDecoder };
package/dist/unbikit.js CHANGED
@@ -46,72 +46,60 @@ const BIK_QUANT = generateQuantTables(BASE_QUANT);
46
46
  //#region src/bik-decoder-utils.ts
47
47
  var BitReader = class {
48
48
  #buffer;
49
- #pos = 0;
49
+ #bufferBitLength;
50
50
  #bitPos = 0;
51
51
  constructor(buffer) {
52
52
  this.#buffer = buffer;
53
+ this.#bufferBitLength = buffer.length << 3;
53
54
  }
54
- m(buffer) {
55
+ n(buffer) {
55
56
  this.#buffer = buffer;
56
- this.#pos = 0;
57
+ this.#bufferBitLength = buffer.length << 3;
57
58
  this.#bitPos = 0;
58
59
  }
60
+ /**
61
+ * Read the specified number of bits from the bit-stream.
62
+ * @param n Number of bits to read. Must be less than or equal to 32.
63
+ * @param peek When `true`, do not update the bit position in the bit-stream, otherwise do
64
+ * update it.
65
+ * @returns The bits read from the bit-stream.
66
+ */
59
67
  a(n, peek = false) {
60
68
  let result = 0;
61
- let pos = this.#pos;
62
69
  let bitPos = this.#bitPos;
63
- let bitsRead = 0;
64
70
  let bitsRemaining = n;
65
71
  do {
66
72
  const mask = (1 << bitsRemaining) - 1 & 255;
67
- const bits = (this.#buffer[pos] ?? 0) >>> bitPos & mask;
68
- result |= bits << bitsRead;
69
- const newBitPos = bitPos + bitsRemaining;
70
- if (newBitPos > 7) {
71
- const bitChange = 8 - bitPos;
72
- bitsRead += bitChange;
73
- bitsRemaining -= bitChange;
74
- pos++;
75
- bitPos = 0;
73
+ const bits = (this.#buffer[bitPos >>> 3] ?? 0) >>> (bitPos & 7) & mask;
74
+ result |= bits << n - bitsRemaining;
75
+ if ((bitPos & 7) + bitsRemaining > 7) {
76
+ const posChange = 8 - (bitPos & 7);
77
+ bitsRemaining -= posChange;
78
+ bitPos += posChange;
76
79
  } else {
77
- bitPos = newBitPos;
80
+ bitPos += bitsRemaining;
78
81
  break;
79
82
  }
80
- } while (bitsRead < n);
81
- if (!peek) {
82
- this.#pos = pos;
83
- this.#bitPos = bitPos;
84
- }
83
+ } while (bitsRemaining);
84
+ if (!peek) this.#bitPos = bitPos;
85
85
  return result;
86
86
  }
87
- d() {
88
- const bit = (this.#buffer[this.#pos] ?? 0) >>> this.#bitPos++ & 1;
89
- if (this.#bitPos > 7) {
90
- this.#pos++;
91
- this.#bitPos = 0;
92
- }
93
- return bit;
94
- }
95
- q() {
96
- return (this.#pos << 3) + this.#bitPos;
87
+ c() {
88
+ return (this.#buffer[this.#bitPos >>> 3] ?? 0) >>> (this.#bitPos++ & 7) & 1;
97
89
  }
98
- j(n) {
90
+ i(n) {
99
91
  if (n < 1) return;
100
92
  this.#bitPos += n;
101
- while (this.#bitPos > 7) {
102
- this.#pos++;
103
- this.#bitPos -= 8;
104
- }
105
- }
106
- n() {
107
- const n = 32 - (this.q() & 31) & 31;
108
- this.j(n);
109
93
  }
110
94
  o() {
111
- return (this.#buffer.length - this.#pos << 3) - this.#bitPos;
95
+ const n = 32 - (this.#bitPos & 31) & 31;
96
+ this.i(n);
97
+ }
98
+ get p() {
99
+ return this.#bufferBitLength - this.#bitPos;
112
100
  }
113
101
  e(v) {
114
- return this.d() ? -v : v;
102
+ return this.c() ? -v : v;
115
103
  }
116
104
  };
117
105
 
@@ -130,15 +118,15 @@ var BikAudioDecoder = class {
130
118
  #bands;
131
119
  #output;
132
120
  #overlapWindow = [];
133
- #useDCT;
121
+ #usesDCT;
134
122
  #idt;
135
- constructor(sampleRate, numChannels, useDCT) {
123
+ constructor(sampleRate, numChannels, usesDCT) {
136
124
  let frameLenBits;
137
125
  if (sampleRate < 22050) frameLenBits = 9;
138
126
  else if (sampleRate < 44100) frameLenBits = 10;
139
127
  else frameLenBits = 11;
140
128
  let numInternalChannels = numChannels;
141
- if (!useDCT) {
129
+ if (!usesDCT) {
142
130
  sampleRate *= numChannels;
143
131
  frameLenBits += Math.ceil(Math.log2(numChannels)) & 3;
144
132
  frameLenBits = frameLenBits;
@@ -150,7 +138,7 @@ var BikAudioDecoder = class {
150
138
  this.#overlapLen = this.#frameLen / 16;
151
139
  this.#blockSize = (this.#frameLen - this.#overlapLen) * numInternalChannels;
152
140
  const sampleRateHalf = sampleRate + 1 >>> 1;
153
- this.#baseQuant = (useDCT ? this.#frameLen : 2) / (Math.sqrt(this.#frameLen) * 32768);
141
+ this.#baseQuant = (usesDCT ? this.#frameLen : 2) / (Math.sqrt(this.#frameLen) * 32768);
154
142
  const expMultiplier = .0664 / Math.log10(Math.E);
155
143
  for (let i = 0; i < 96; i++) this.#quantTable[i] = Math.exp(i * expMultiplier) * this.#baseQuant;
156
144
  let numBands = AUDIO_CRITICAL_FREQS.findIndex((x) => sampleRateHalf <= x);
@@ -163,8 +151,8 @@ var BikAudioDecoder = class {
163
151
  this.#output = new Array(this.#numInternalChannels);
164
152
  for (const [index] of this.#output.entries()) this.#output[index] = new Float32Array(this.#frameLen);
165
153
  for (let i = 0; i < numInternalChannels; i++) this.#overlapWindow.push(new Float32Array(this.#overlapLen));
166
- this.#useDCT = useDCT;
167
- this.#idt = useDCT ? new IDCT(frameLenBits) : new IRDFT(frameLenBits);
154
+ this.#usesDCT = usesDCT;
155
+ this.#idt = usesDCT ? new IDCT(frameLenBits) : new IRDFT(frameLenBits);
168
156
  }
169
157
  /**
170
158
  * Read a 29-bit floating point value (5 bits exponent, 23 bits mantissa, 1 bit sign) from a
@@ -174,20 +162,19 @@ var BikAudioDecoder = class {
174
162
  */
175
163
  #getFloat(reader) {
176
164
  const power = reader.a(5);
177
- const f = reader.a(23) * 2 ** (power - 23);
178
- return reader.d() ? -f : f;
165
+ return reader.e(reader.a(23) * 2 ** (power - 23));
179
166
  }
180
167
  /**
181
168
  * Decode all blocks and channels of a frame of audio data.
182
169
  * @param data Encoded frame audio data.
183
170
  * @returns Decoded data indexed by block number and then by channel number.
184
171
  */
185
- v(data) {
172
+ s(data) {
186
173
  const reader = new BitReader(data);
187
174
  const output = this.#output;
188
175
  const allOutput = [];
189
- while (reader.o() > 0) {
190
- if (this.#useDCT) reader.j(2);
176
+ while (reader.p > 0) {
177
+ if (this.#usesDCT) reader.i(2);
191
178
  for (let ch = 0; ch < this.#numInternalChannels; ch++) {
192
179
  const coeffs = output[ch];
193
180
  coeffs[0] = this.#getFloat(reader) * this.#baseQuant;
@@ -199,7 +186,7 @@ var BikAudioDecoder = class {
199
186
  let i = 2;
200
187
  while (i < this.#frameLen) {
201
188
  let j;
202
- if (reader.d()) {
189
+ if (reader.c()) {
203
190
  const v = reader.a(4);
204
191
  j = i + AUDIO_RLE_LENGTH_TABLE[v];
205
192
  } else j = i + 8;
@@ -212,13 +199,13 @@ var BikAudioDecoder = class {
212
199
  } else while (i < j) {
213
200
  if (this.#bands[k] === i) q = quant[k++] ?? 0;
214
201
  const coeff = reader.a(width);
215
- if (coeff) coeffs[i] = reader.d() ? -q * coeff : q * coeff;
202
+ if (coeff) coeffs[i] = reader.c() ? -q * coeff : q * coeff;
216
203
  else coeffs[i] = 0;
217
204
  i++;
218
205
  }
219
206
  }
220
207
  this.#idt.k(coeffs);
221
- if (this.#useDCT) {
208
+ if (this.#usesDCT) {
222
209
  const dctModifier = 4 * this.#baseQuant;
223
210
  for (let i2 = 0; i2 < coeffs.length; i2++) coeffs[i2] *= dctModifier;
224
211
  }
@@ -255,7 +242,7 @@ var BikAudioDecoder = class {
255
242
  }
256
243
  allOutput.push(outputBlock);
257
244
  this.#first = false;
258
- reader.n();
245
+ reader.o();
259
246
  }
260
247
  return allOutput;
261
248
  }
@@ -443,27 +430,22 @@ var FFT = class {
443
430
  //#region src/bik-video-decoder.ts
444
431
  const EMPTY_UINT8_ARRAY = new Uint8Array();
445
432
  var HuffTable = class HuffTable {
446
- #symbols;
447
- #lens;
433
+ #symbolsLens;
448
434
  #maxBits;
449
- static #predefinedTables = new Array(16).fill(null).map((_, bikTreeIndex) => new HuffTable(bikTreeIndex));
435
+ static l = new Array(16).fill(null).map((_, bikTreeIndex) => new HuffTable(bikTreeIndex));
450
436
  constructor(bikTreeIndex) {
451
437
  const referenceTableOffset = bikTreeIndex << 4;
452
438
  const maxBits = BIK_TREE_LENS[referenceTableOffset + 15];
453
439
  this.#maxBits = maxBits;
454
440
  const tableSize = 1 << maxBits;
455
- this.#symbols = new Array(tableSize);
456
- this.#lens = new Array(tableSize);
441
+ this.#symbolsLens = new Uint8Array(tableSize);
457
442
  for (let i = 0; i < 16; i++) {
458
443
  const code = BIK_TREE_CODES[referenceTableOffset + i];
459
444
  const len = BIK_TREE_LENS[referenceTableOffset + i];
460
445
  const numEntries = 1 << maxBits - len;
461
446
  for (let j = 0; j < numEntries; j++) {
462
447
  const index = j << len | code;
463
- if (index < tableSize) {
464
- this.#symbols[index] = i;
465
- this.#lens[index] = len;
466
- }
448
+ if (index < tableSize) this.#symbolsLens[index] = i << 4 | len;
467
449
  }
468
450
  }
469
451
  }
@@ -474,12 +456,11 @@ var HuffTable = class HuffTable {
474
456
  * @returns 4-bit value, Huffman-decoded from the bit-stream.
475
457
  */
476
458
  static f(reader, tree) {
477
- const table = HuffTable.#predefinedTables[tree.l];
459
+ const table = tree.m;
478
460
  const peek = reader.a(table.#maxBits, true);
479
- const len = table.#lens[peek];
480
- const symbol = table.#symbols[peek];
481
- reader.j(len);
482
- return tree.g[symbol];
461
+ const entry = table.#symbolsLens[peek];
462
+ reader.i(entry & 15);
463
+ return tree.g[entry >>> 4];
483
464
  }
484
465
  };
485
466
  var BikVideoDecoder = class {
@@ -490,8 +471,6 @@ var BikVideoDecoder = class {
490
471
  #hasSwappedUVPlanes;
491
472
  #numPixels;
492
473
  #uvSize;
493
- #blockXPos = 0;
494
- #blockYPos = 0;
495
474
  #stride = 0;
496
475
  #data = EMPTY_UINT8_ARRAY;
497
476
  #prevData = EMPTY_UINT8_ARRAY;
@@ -519,19 +498,19 @@ var BikVideoDecoder = class {
519
498
  this.#numPixels = numPixels;
520
499
  this.#uvSize = uvSize;
521
500
  this.#prevData = new Uint8Array(frameSize);
522
- const blocks = numPixels + 63 >>> 6;
501
+ const numBlockPixels = numPixels + 63 >>> 6 << 6;
523
502
  for (let i = 0; i < 9; i++) this.#blockParams[i] = {
524
- p: 0,
503
+ q: 0,
525
504
  h: {
526
- l: 0,
505
+ m: HuffTable.l[0],
527
506
  g: new Uint8Array(16)
528
507
  },
529
- c: new Uint8Array(blocks * 64),
508
+ d: i > 3 && i < 8 ? new Int16Array(numBlockPixels) : new Uint8Array(numBlockPixels),
530
509
  b: 0,
531
- i: 0
510
+ j: 0
532
511
  };
533
512
  for (let i = 0; i < 16; i++) this.#colHigh[i] = {
534
- l: 0,
513
+ m: HuffTable.l[0],
535
514
  g: new Uint8Array(16)
536
515
  };
537
516
  }
@@ -563,12 +542,15 @@ var BikVideoDecoder = class {
563
542
  const planeOffset = planeIndex === 0 ? 0 : planeIndex === 1 ? numPixels : planeIndex === 2 ? numPixels + uvSize : numPixels + (uvSize << 1);
564
543
  const blockLineIncr = this.#stride * 7;
565
544
  const mainDataBuf = this.#data;
566
- this.#data = new Uint8Array(this.#data.buffer, planeOffset, isChroma ? uvSize : numPixels);
545
+ this.#data = new Uint8Array(mainDataBuf.buffer, planeOffset, isChroma ? uvSize : numPixels);
567
546
  this.#dataPtr = 0;
568
547
  const prevMainDataBuf = this.#prevData;
569
- this.#prevData = new Uint8Array(this.#prevData.buffer, planeOffset, isChroma ? uvSize : numPixels);
570
- for (this.#blockYPos = 0; this.#blockYPos < blockHeight; this.#blockYPos++) {
571
- this.#readBlockTypes(blockParams[0]);
548
+ this.#prevData = new Uint8Array(prevMainDataBuf.buffer, planeOffset, isChroma ? uvSize : numPixels);
549
+ const blockTypeParam = blockParams[0];
550
+ const blockTypes = blockTypeParam.d;
551
+ let blockYPos = 0;
552
+ while (blockYPos++ < blockHeight) {
553
+ this.#readBlockTypes(blockTypeParam);
572
554
  this.#readBlockTypes(blockParams[1]);
573
555
  this.#readColors(blockParams[2]);
574
556
  this.#readPatterns(blockParams[3]);
@@ -577,14 +559,16 @@ var BikVideoDecoder = class {
577
559
  this.#readDCs(blockParams[6], false);
578
560
  this.#readDCs(blockParams[7], true);
579
561
  this.#readRuns(blockParams[8]);
580
- for (this.#blockXPos = 0; this.#blockXPos < blockWidth; this.#blockXPos++) {
581
- const blockType = this.#getValue(0);
562
+ let blockXPos = 0;
563
+ while (blockXPos++ < blockWidth) {
564
+ const blockType = blockTypes[blockTypeParam.j++];
582
565
  switch (blockType) {
583
566
  case 0: break;
584
567
  case 1:
585
- this.#decodeScaledBlock();
586
- this.#dataPtr += 8;
587
- break;
568
+ if (blockYPos & 1) this.#decodeScaledBlock();
569
+ blockXPos++;
570
+ this.#dataPtr += 16;
571
+ continue;
588
572
  case 2:
589
573
  this.#decodeMotionBlock();
590
574
  break;
@@ -615,18 +599,22 @@ var BikVideoDecoder = class {
615
599
  }
616
600
  this.#dataPtr += blockLineIncr;
617
601
  }
618
- this.#reader.n();
602
+ this.#reader.o();
619
603
  this.#prevData = prevMainDataBuf;
620
604
  this.#data = mainDataBuf;
621
605
  }
622
606
  #copyBlock(srcOffset) {
623
- if (this.#dataPtr === srcOffset) return;
624
- let srcIndex = srcOffset;
625
- let destIndex = this.#dataPtr;
626
- for (let y = 0; y < 8; y++) {
627
- for (let x = 0; x < 8; x++) this.#data[destIndex + x] = this.#prevData[srcIndex + x] ?? 0;
628
- srcIndex += this.#stride;
629
- destIndex += this.#stride;
607
+ const indexDiff = this.#dataPtr - srcOffset;
608
+ if (!indexDiff) return;
609
+ const strideMinusBlock = this.#stride - 8;
610
+ let lineCount = 8;
611
+ while (lineCount--) {
612
+ const srcOffsetMax = srcOffset + 8;
613
+ while (srcOffset < srcOffsetMax) {
614
+ this.#data[srcOffset + indexDiff] = this.#prevData[srcOffset];
615
+ srcOffset++;
616
+ }
617
+ srcOffset += strideMinusBlock;
630
618
  }
631
619
  }
632
620
  #decodeMotionBlock() {
@@ -641,7 +629,7 @@ var BikVideoDecoder = class {
641
629
  do {
642
630
  const run = this.#getValue(8) + 1;
643
631
  i += run;
644
- if (this.#reader.d()) {
632
+ if (this.#reader.c()) {
645
633
  const v = this.#getValue(2);
646
634
  for (let j = 0; j < run; j++) {
647
635
  const pos = BIK_PATTERNS[scanIndex++] ?? 0;
@@ -701,16 +689,14 @@ var BikVideoDecoder = class {
701
689
  }
702
690
  #decodeRawBlock() {
703
691
  const blockParamValues = this.#blockParams[2];
704
- for (let y = 0; y < 8; y++) for (let x = 0; x < 8; x++) this.#data[this.#dataPtr + y * this.#stride + x] = blockParamValues.c[blockParamValues.i++] ?? 0;
692
+ for (let y = 0; y < 8; y++) for (let x = 0; x < 8; x++) this.#data[this.#dataPtr + y * this.#stride + x] = blockParamValues.d[blockParamValues.j++];
705
693
  }
706
694
  #decodeScaledBlock() {
707
- this.#blockXPos++;
708
- if (this.#blockYPos & 1) return;
709
695
  const tmpScalingBuf = this.#tmpScalingBuf;
710
696
  const subBlk = this.#getValue(1);
711
697
  switch (subBlk) {
712
- case 3:
713
- this.#decodeRunBlock(tmpScalingBuf);
698
+ case 9:
699
+ for (let i = 0; i < 64; i++) tmpScalingBuf[i] = this.#getValue(2);
714
700
  break;
715
701
  case 5:
716
702
  this.#decodeIntraBlock(tmpScalingBuf);
@@ -718,30 +704,25 @@ var BikVideoDecoder = class {
718
704
  case 6:
719
705
  this.#decodeFillBlock(16);
720
706
  return;
707
+ case 3:
708
+ this.#decodeRunBlock(tmpScalingBuf);
709
+ break;
721
710
  case 8:
722
711
  this.#decodePatternBlock(tmpScalingBuf);
723
712
  break;
724
- case 9:
725
- for (let j = 0; j < 8; j++) for (let i = 0; i < 8; i++) tmpScalingBuf[i + (j << 3)] = this.#getValue(2);
726
- break;
727
713
  default: throw new Error(`Invalid sub-block type ${subBlk}`);
728
714
  }
729
715
  const dest = this.#data;
716
+ const stride = this.#stride;
730
717
  let srcPos = 0;
731
- let destPosLine0 = this.#dataPtr;
732
- let destPosLine1 = this.#dataPtr + this.#stride;
733
- const lineIncrement = this.#stride << 1;
734
- for (let j = 0; j < 8; j++) {
735
- let halfWordOffset = 0;
736
- for (let i = 0; i < 8; i++) {
737
- const v = tmpScalingBuf[srcPos++] ?? 0;
738
- dest[destPosLine0 + halfWordOffset] = v;
739
- dest[destPosLine1 + halfWordOffset++] = v;
740
- dest[destPosLine0 + halfWordOffset] = v;
741
- dest[destPosLine1 + halfWordOffset++] = v;
742
- }
743
- destPosLine0 += lineIncrement;
744
- destPosLine1 += lineIncrement;
718
+ let destPosLine = this.#dataPtr;
719
+ const maxDestPosLine = destPosLine + (stride << 4);
720
+ const lineIncrement = (stride << 1) - 15;
721
+ while (destPosLine < maxDestPosLine) {
722
+ const value = tmpScalingBuf[srcPos++];
723
+ dest[destPosLine] = dest[stride + destPosLine++] = value;
724
+ dest[destPosLine] = dest[stride + destPosLine] = value;
725
+ destPosLine += srcPos & 7 ? 1 : lineIncrement;
745
726
  }
746
727
  }
747
728
  /**
@@ -751,17 +732,18 @@ var BikVideoDecoder = class {
751
732
  */
752
733
  #readTree(tree) {
753
734
  const reader = this.#reader;
754
- tree.l = reader.a(4);
755
- if (!tree.l) {
735
+ const tableNum = reader.a(4);
736
+ tree.m = HuffTable.l[tableNum];
737
+ if (!tableNum) {
756
738
  for (let i = 0; i < 16; i++) tree.g[i] = i;
757
739
  return;
758
740
  }
759
- if (reader.d()) {
741
+ if (reader.c()) {
760
742
  let len = reader.a(3);
761
743
  const tmp = this.#inputTreeBuf.fill(0);
762
744
  for (let i = 0; i <= len; i++) {
763
745
  tree.g[i] = reader.a(4);
764
- tmp[tree.g[i] ?? 0] = 1;
746
+ tmp[tree.g[i]] = 1;
765
747
  }
766
748
  for (let i = 0; i < 16; i++) if (!tmp[i]) tree.g[++len] = i;
767
749
  } else {
@@ -769,7 +751,7 @@ var BikVideoDecoder = class {
769
751
  let input = this.#inputTreeBuf;
770
752
  let output = this.#outputTreeBuf;
771
753
  for (let i = 0; i < 16; i += 2) {
772
- const bit = this.#reader.d();
754
+ const bit = this.#reader.c();
773
755
  input[i] = i + bit;
774
756
  input[i + 1] = i + (bit ^ 1);
775
757
  }
@@ -785,15 +767,15 @@ var BikVideoDecoder = class {
785
767
  let src2Index = src1Index + size1;
786
768
  let size2 = size1;
787
769
  let destIndex = src1Index;
788
- while (size1 && size2) if (this.#reader.d()) {
789
- dest[destIndex++] = src[src2Index++] ?? 0;
770
+ while (size1 && size2) if (this.#reader.c()) {
771
+ dest[destIndex++] = src[src2Index++];
790
772
  size2--;
791
773
  } else {
792
- dest[destIndex++] = src[src1Index++] ?? 0;
774
+ dest[destIndex++] = src[src1Index++];
793
775
  size1--;
794
776
  }
795
- while (size1--) dest[destIndex++] = src[src1Index++] ?? 0;
796
- while (size2--) dest[destIndex++] = src[src2Index++] ?? 0;
777
+ while (size1--) dest[destIndex++] = src[src1Index++];
778
+ while (size2--) dest[destIndex++] = src[src2Index++];
797
779
  }
798
780
  /**
799
781
  * Read Huffman tree information from the bit-stream for each block type in the current
@@ -806,7 +788,7 @@ var BikVideoDecoder = class {
806
788
  const extraLen = 511;
807
789
  const commonLen = (adjustedWidth >>> 3) + extraLen;
808
790
  for (const [blockParamNum, blockParamValues] of this.#blockParams.entries()) {
809
- blockParamValues.p = ~~Math.log2([
791
+ blockParamValues.q = ~~Math.log2([
810
792
  commonLen,
811
793
  (adjustedWidth >>> 4) + extraLen,
812
794
  (blockWidth << 6) + extraLen,
@@ -822,7 +804,7 @@ var BikVideoDecoder = class {
822
804
  this.#colLastValue = 0;
823
805
  }
824
806
  if (blockParamNum < 6 || blockParamNum > 7) this.#readTree(blockParamValues.h);
825
- blockParamValues.b = blockParamValues.i = 0;
807
+ blockParamValues.b = blockParamValues.j = 0;
826
808
  }
827
809
  }
828
810
  /**
@@ -831,8 +813,8 @@ var BikVideoDecoder = class {
831
813
  * @returns Number of block parameter values.
832
814
  */
833
815
  #readCodedDataCount(blockParamValues) {
834
- if (blockParamValues.b < 0 || blockParamValues.b > blockParamValues.i) return 0;
835
- const count = this.#reader.a(blockParamValues.p);
816
+ if (blockParamValues.b < 0 || blockParamValues.b > blockParamValues.j) return 0;
817
+ const count = this.#reader.a(blockParamValues.q);
836
818
  if (count === 0) blockParamValues.b = -1;
837
819
  return count;
838
820
  }
@@ -840,19 +822,19 @@ var BikVideoDecoder = class {
840
822
  const count = this.#readCodedDataCount(blockParamValues);
841
823
  if (!count) return;
842
824
  const reader = this.#reader;
843
- if (reader.d()) {
825
+ if (reader.c()) {
844
826
  const v = reader.a(4);
845
- for (let i = 0; i < count; i++) blockParamValues.c[blockParamValues.b++] = v;
827
+ for (let i = 0; i < count; i++) blockParamValues.d[blockParamValues.b++] = v;
846
828
  } else {
847
829
  let prevValue = 0;
848
830
  for (let i = 0; i < count; i++) {
849
831
  const v = HuffTable.f(reader, blockParamValues.h);
850
832
  if (v < 12) {
851
833
  prevValue = v;
852
- blockParamValues.c[blockParamValues.b++] = v;
834
+ blockParamValues.d[blockParamValues.b++] = v;
853
835
  } else {
854
836
  const runLength = RLE_LENGTHS[v - 12];
855
- for (let j = 0; j < runLength; j++) blockParamValues.c[blockParamValues.b++] = prevValue;
837
+ for (let j = 0; j < runLength; j++) blockParamValues.d[blockParamValues.b++] = prevValue;
856
838
  i += runLength - 1;
857
839
  }
858
840
  }
@@ -862,7 +844,7 @@ var BikVideoDecoder = class {
862
844
  const count = this.#readCodedDataCount(blockParamValues);
863
845
  if (!count) return;
864
846
  const reader = this.#reader;
865
- const isRun = reader.d();
847
+ const isRun = reader.c();
866
848
  let loopCount = isRun ? 1 : count;
867
849
  do {
868
850
  const colHighValue = HuffTable.f(reader, this.#colHigh[this.#colLastValue]);
@@ -870,9 +852,9 @@ var BikVideoDecoder = class {
870
852
  this.#colLastValue = colHighValue;
871
853
  if (this.#version < 105) v = v > 127 ? 256 - v : v + 128;
872
854
  if (isRun) {
873
- blockParamValues.c.fill(v, blockParamValues.b, blockParamValues.b + count);
855
+ blockParamValues.d.fill(v, blockParamValues.b, blockParamValues.b + count);
874
856
  blockParamValues.b += count;
875
- } else blockParamValues.c[blockParamValues.b++] = v;
857
+ } else blockParamValues.d[blockParamValues.b++] = v;
876
858
  } while (--loopCount);
877
859
  }
878
860
  #readPatterns(blockParamValues) {
@@ -881,23 +863,25 @@ var BikVideoDecoder = class {
881
863
  const reader = this.#reader;
882
864
  for (let i = 0; i < count; i++) {
883
865
  const v = HuffTable.f(reader, blockParamValues.h) | HuffTable.f(reader, blockParamValues.h) << 4;
884
- blockParamValues.c[blockParamValues.b++] = v;
866
+ blockParamValues.d[blockParamValues.b++] = v;
885
867
  }
886
868
  }
887
869
  #readMotionValues(blockParamValues) {
888
870
  const count = this.#readCodedDataCount(blockParamValues);
889
871
  if (!count) return;
890
872
  const reader = this.#reader;
891
- const items = blockParamValues.c;
873
+ const items = blockParamValues.d;
892
874
  const maxDec = blockParamValues.b + count;
893
875
  let v;
894
- if (reader.d()) {
876
+ if (reader.c()) {
895
877
  v = reader.a(4);
896
878
  if (v) v = reader.e(v);
879
+ v = v << 24 >> 24;
897
880
  items.fill(v, blockParamValues.b, maxDec);
898
881
  } else while (blockParamValues.b < maxDec) {
899
882
  v = HuffTable.f(reader, blockParamValues.h);
900
883
  if (v) v = reader.e(v);
884
+ v = v << 24 >> 24;
901
885
  items[blockParamValues.b++] = v;
902
886
  }
903
887
  blockParamValues.b = maxDec;
@@ -906,25 +890,21 @@ var BikVideoDecoder = class {
906
890
  const count = this.#readCodedDataCount(blockParamValues);
907
891
  if (!count) return;
908
892
  const reader = this.#reader;
909
- const view = new DataView(blockParamValues.c.buffer, blockParamValues.c.byteOffset, blockParamValues.c.byteLength);
910
893
  let v = reader.a(hasSign ? 10 : 11);
911
894
  if (v && hasSign) v = reader.e(v);
912
- view.setInt16(blockParamValues.b, v, true);
913
- blockParamValues.b += 2;
914
- for (let i = 1; i < count;) {
895
+ const items = blockParamValues.d;
896
+ items[blockParamValues.b++] = v;
897
+ let i = 1;
898
+ while (i < count) {
915
899
  const len = Math.min(count - i, 8);
916
- const bsize = reader.a(4);
917
- if (bsize) for (let j = 0; j < len; j++) {
918
- let v2 = reader.a(bsize);
900
+ const v2Size = reader.a(4);
901
+ if (v2Size) for (let j = 0; j < len; j++) {
902
+ let v2 = reader.a(v2Size);
919
903
  if (v2) v2 = reader.e(v2);
920
904
  v += v2;
921
- view.setInt16(blockParamValues.b, v, true);
922
- blockParamValues.b += 2;
923
- }
924
- else for (let j = 0; j < len; j++) {
925
- view.setInt16(blockParamValues.b, v, true);
926
- blockParamValues.b += 2;
905
+ items[blockParamValues.b++] = v;
927
906
  }
907
+ else for (let j = 0; j < len; j++) items[blockParamValues.b++] = v;
928
908
  i += len;
929
909
  }
930
910
  }
@@ -932,10 +912,10 @@ var BikVideoDecoder = class {
932
912
  const count = this.#readCodedDataCount(blockParamValues);
933
913
  if (!count) return;
934
914
  const reader = this.#reader;
935
- if (reader.d()) {
915
+ if (reader.c()) {
936
916
  const v = reader.a(4);
937
- for (let i = 0; i < count; i++) blockParamValues.c[blockParamValues.b++] = v;
938
- } else for (let i = 0; i < count; i++) blockParamValues.c[blockParamValues.b++] = HuffTable.f(reader, blockParamValues.h);
917
+ for (let i = 0; i < count; i++) blockParamValues.d[blockParamValues.b++] = v;
918
+ } else for (let i = 0; i < count; i++) blockParamValues.d[blockParamValues.b++] = HuffTable.f(reader, blockParamValues.h);
939
919
  }
940
920
  /**
941
921
  * Get the next block parameter value for the current line.
@@ -944,11 +924,7 @@ var BikVideoDecoder = class {
944
924
  */
945
925
  #getValue(blockParamNum) {
946
926
  const blockParamValues = this.#blockParams[blockParamNum];
947
- if (blockParamNum < 4 || blockParamNum === 8) return blockParamValues.c[blockParamValues.i++] ?? 0;
948
- if (blockParamNum < 6) return (blockParamValues.c[blockParamValues.i++] ?? 0) << 24 >> 24;
949
- const val = new DataView(blockParamValues.c.buffer, blockParamValues.c.byteOffset, blockParamValues.c.byteLength).getInt16(blockParamValues.i, true);
950
- blockParamValues.i += 2;
951
- return val;
927
+ return blockParamValues.d[blockParamValues.j++] ?? 0;
952
928
  }
953
929
  /**
954
930
  * Mini-VM (virtual machine) to decode and optionally unquantize a block of integer values.
@@ -992,10 +968,10 @@ var BikVideoDecoder = class {
992
968
  let bits = isResidue ? 1 << reader.a(3) : reader.a(4) - 1;
993
969
  while (isResidue ? bits : bits >= 0) {
994
970
  if (isResidue) {
995
- for (i = 0; i < coeffCount; i++) if (reader.d()) {
971
+ for (i = 0; i < coeffCount; i++) if (reader.c()) {
996
972
  const curNzCoeff = coeffIndex[i] ?? 0;
997
- if ((block[curNzCoeff] ?? 0) < 0) block[curNzCoeff] -= bits;
998
- else block[curNzCoeff] += bits;
973
+ const value = block[curNzCoeff] ?? 0;
974
+ block[curNzCoeff] = value < 0 ? value - bits : value + bits;
999
975
  if (!masksCount--) return;
1000
976
  }
1001
977
  }
@@ -1003,7 +979,7 @@ var BikVideoDecoder = class {
1003
979
  while (listPos < listEnd) {
1004
980
  const ccoeff = coeffList[listPos] ?? 0;
1005
981
  const mode = modeList[listPos] ?? 0;
1006
- if (!(mode | ccoeff) || !reader.d()) {
982
+ if (!(mode | ccoeff) || !reader.c()) {
1007
983
  listPos++;
1008
984
  continue;
1009
985
  }
@@ -1017,7 +993,7 @@ var BikVideoDecoder = class {
1017
993
  coeffList[listPos] = 0;
1018
994
  modeList[listPos++] = 0;
1019
995
  }
1020
- for (i = ccoeff; i < ccoeff + 4; i++) if (reader.d()) {
996
+ for (i = ccoeff; i < ccoeff + 4; i++) if (reader.c()) {
1021
997
  coeffList[--listStart] = i;
1022
998
  modeList[listStart] = 3;
1023
999
  } else if (isResidue) {
@@ -1026,7 +1002,7 @@ var BikVideoDecoder = class {
1026
1002
  block[offset] = reader.e(bits);
1027
1003
  if (!masksCount--) return;
1028
1004
  } else {
1029
- const offset = bits ? reader.e(reader.a(bits) | 1 << bits) : 1 - (reader.d() << 1);
1005
+ const offset = bits ? reader.e(reader.a(bits) | 1 << bits) : 1 - (reader.c() << 1);
1030
1006
  block[BIK_SCAN[i] ?? 0] = offset;
1031
1007
  coeffIndex[coeffCount++] = i;
1032
1008
  }
@@ -1047,7 +1023,7 @@ var BikVideoDecoder = class {
1047
1023
  block[offset] = reader.e(bits);
1048
1024
  if (!masksCount--) return;
1049
1025
  } else {
1050
- const offset = bits ? reader.e(reader.a(bits) | 1 << bits) : 1 - (reader.d() << 1);
1026
+ const offset = bits ? reader.e(reader.a(bits) | 1 << bits) : 1 - (reader.c() << 1);
1051
1027
  block[BIK_SCAN[ccoeff] ?? 0] = offset;
1052
1028
  coeffIndex[coeffCount++] = ccoeff;
1053
1029
  }
@@ -1058,11 +1034,11 @@ var BikVideoDecoder = class {
1058
1034
  }
1059
1035
  if (!isResidue) {
1060
1036
  const quantOffset = (reader.a(4) << 6) + quantStartIndex;
1061
- block[0] = Math.imul(block[0] ?? 0, BIK_QUANT[quantOffset] ?? 0) >> 11;
1037
+ block[0] = (block[0] ?? 0) * (BIK_QUANT[quantOffset] ?? 0) >> 11;
1062
1038
  while (coeffCount--) {
1063
1039
  const index = coeffIndex[coeffCount] ?? 0;
1064
1040
  const blockIndex = BIK_SCAN[index] ?? 0;
1065
- block[blockIndex] = Math.imul(block[blockIndex] ?? 0, BIK_QUANT[quantOffset + index] ?? 0) >> 11;
1041
+ block[blockIndex] = (block[blockIndex] ?? 0) * (BIK_QUANT[quantOffset + index] ?? 0) >> 11;
1066
1042
  }
1067
1043
  }
1068
1044
  }
@@ -1152,11 +1128,15 @@ var BikVideoDecoder = class {
1152
1128
  this.#addPixels8x8(block, dest, destOffset, stride);
1153
1129
  }
1154
1130
  #addPixels8x8(block, dest, destOffset, stride) {
1155
- let destPos = destOffset;
1156
- let blockPos = 0;
1157
- for (let y = 0; y < 8; y++) {
1158
- for (let x = 0; x < 8; x++) dest[destPos + x] += block[blockPos++] ?? 0;
1159
- destPos += stride;
1131
+ stride -= 8;
1132
+ let i = -1;
1133
+ let iMax = 6;
1134
+ while (i++ < 63) {
1135
+ dest[destOffset + i] += block[i];
1136
+ if (i > iMax) {
1137
+ destOffset += stride;
1138
+ iMax += 8;
1139
+ }
1160
1140
  }
1161
1141
  }
1162
1142
  /**
@@ -1165,9 +1145,9 @@ var BikVideoDecoder = class {
1165
1145
  * @param existingFrame Decoded frame to re-use for the returned value.
1166
1146
  * @returns Decoded frame.
1167
1147
  */
1168
- w(data, existingFrame = null) {
1148
+ t(data, existingFrame = null) {
1169
1149
  const reader = this.#reader;
1170
- reader.m(data);
1150
+ reader.n(data);
1171
1151
  let frame;
1172
1152
  if (existingFrame) {
1173
1153
  frame = existingFrame;
@@ -1175,18 +1155,18 @@ var BikVideoDecoder = class {
1175
1155
  } else frame = this.#createFrame();
1176
1156
  this.#data = frame.yuv;
1177
1157
  if (this.#hasAlpha) {
1178
- if (this.#version > 104) reader.j(32);
1158
+ if (this.#version > 104) reader.i(32);
1179
1159
  this.#decodePlane(frame, 3);
1180
1160
  }
1181
- if (this.#version > 104) reader.j(32);
1161
+ if (this.#version > 104) reader.i(32);
1182
1162
  for (let plane = 0; plane < 3; plane++) {
1183
1163
  const planeIndex = plane && this.#hasSwappedUVPlanes ? plane ^ 3 : plane;
1184
1164
  this.#decodePlane(frame, planeIndex);
1185
- if (reader.o() < 1) break;
1165
+ if (reader.p < 1) break;
1186
1166
  }
1187
1167
  this.#prevData.set(frame.yuv);
1188
1168
  this.#data = EMPTY_UINT8_ARRAY;
1189
- this.#reader.m(EMPTY_UINT8_ARRAY);
1169
+ this.#reader.n(EMPTY_UINT8_ARRAY);
1190
1170
  return frame;
1191
1171
  }
1192
1172
  };
@@ -1194,35 +1174,69 @@ var BikVideoDecoder = class {
1194
1174
  //#endregion
1195
1175
  //#region src/bik-decoder.ts
1196
1176
  var BikDecoder = class BikDecoder {
1197
- #getReadStreamFn;
1177
+ /**
1178
+ * Source of the video.
1179
+ */
1180
+ #dataSource;
1181
+ /**
1182
+ * A {@link ReadableStreamDefaultReader} for the current position in the encoded video data.
1183
+ */
1198
1184
  #streamReader = null;
1185
+ /**
1186
+ * Buffer of encoded video data being processed by the decoder.
1187
+ */
1199
1188
  #bufBytes = null;
1189
+ /**
1190
+ * Start of the {@link #bufBytes} buffer relative to the start of the encoded video data.
1191
+ */
1200
1192
  #bufPos = 0;
1201
1193
  #curFrame = -1;
1202
1194
  #audioTrackDecoders = [];
1203
1195
  #videoDecoder = null;
1204
1196
  #header = null;
1205
1197
  #isSupported = false;
1206
- constructor(getReadStreamFn) {
1207
- this.#getReadStreamFn = getReadStreamFn;
1198
+ constructor(source) {
1199
+ this.#dataSource = source;
1208
1200
  }
1209
1201
  /**
1210
- * Set the current file read position to the specified position, updating the read buffer and
1211
- * potentially requesting a new readable stream if the file read position actually changes.
1212
- * @param pos Position in the file to seek to. Specified in bytes from the start of the file.
1202
+ * Set the current video data read position to the specified position, updating the read
1203
+ * buffer and potentially creating a new readable stream if the read position actually
1204
+ * changes.
1205
+ * @param pos Position in the video to seek to. Specified in bytes from the start of the
1206
+ * video.
1213
1207
  */
1214
1208
  async #seek(pos) {
1215
- if (this.#bufBytes && pos === this.#bufPos) return;
1216
- this.#bufPos = pos;
1217
- this.#bufBytes = null;
1209
+ if (pos < 0) return;
1210
+ if (this.#bufBytes) {
1211
+ if (pos === this.#bufPos) return;
1212
+ if (pos > this.#bufPos && pos < this.#bufPos + this.#bufBytes.byteLength) {
1213
+ this.#bufBytes = this.#bufBytes.subarray(pos - this.#bufPos);
1214
+ this.#bufPos = pos;
1215
+ return;
1216
+ }
1217
+ }
1218
1218
  if (this.#streamReader) {
1219
1219
  await this.#streamReader.cancel();
1220
1220
  this.#streamReader.releaseLock();
1221
1221
  this.#streamReader = null;
1222
1222
  }
1223
- const streamReader = (await this.#getReadStreamFn(pos))?.getReader();
1223
+ const dataSource = this.#dataSource;
1224
+ let streamReader;
1225
+ if (dataSource instanceof Blob) streamReader = dataSource.slice(pos).stream()?.getReader();
1226
+ else {
1227
+ const isRequest = dataSource instanceof Request;
1228
+ if (dataSource instanceof URL || isRequest) {
1229
+ const request = isRequest ? dataSource : new Request(dataSource);
1230
+ const headers = request.headers;
1231
+ headers.delete("range");
1232
+ if (pos) headers.set("range", `bytes=${pos}-`);
1233
+ streamReader = (await fetch(request))?.body?.getReader();
1234
+ }
1235
+ }
1224
1236
  if (!streamReader) throw new Error("Invalid stream reader");
1225
1237
  this.#streamReader = streamReader;
1238
+ this.#bufBytes = null;
1239
+ this.#bufPos = pos;
1226
1240
  }
1227
1241
  /**
1228
1242
  * Read data from the readable stream.
@@ -1274,9 +1288,13 @@ var BikDecoder = class BikDecoder {
1274
1288
  */
1275
1289
  async #ensureReadBytes(len) {
1276
1290
  const bufBytes = await this.#readBytes(len);
1277
- if (bufBytes?.byteLength !== len) throw new Error(`Read ${bufBytes?.byteLength ?? 0} bytes but expected ${len}`);
1291
+ if (bufBytes?.byteLength !== len) throw new Error(`Read ${bufBytes?.byteLength} bytes but expected ${len}`);
1278
1292
  return bufBytes;
1279
1293
  }
1294
+ /**
1295
+ * Initialize the decoder by parsing the header from the readable stream to get the required
1296
+ * information about the encoded video, including the offsets of all the frames.
1297
+ */
1280
1298
  async #init() {
1281
1299
  await this.#seek(0);
1282
1300
  const headerBytes = await this.#ensureReadBytes(44);
@@ -1310,12 +1328,12 @@ var BikDecoder = class BikDecoder {
1310
1328
  sampleRate: audioHeaderDataView.getUint16((numAudioTracks << 2) + (index << 2), true),
1311
1329
  flags: {
1312
1330
  stereo: isStereo,
1313
- useDCT: !!(flags & 4096)
1331
+ usesDCT: !!(flags & 4096)
1314
1332
  }
1315
1333
  };
1316
1334
  });
1317
1335
  audioTracks.forEach((audioTrack) => {
1318
- this.#audioTrackDecoders.push(new BikAudioDecoder(audioTrack.sampleRate, audioTrack.numChannels, audioTrack.flags.useDCT));
1336
+ this.#audioTrackDecoders.push(new BikAudioDecoder(audioTrack.sampleRate, audioTrack.numChannels, audioTrack.flags.usesDCT));
1319
1337
  });
1320
1338
  }
1321
1339
  const videoHeaderBytes = await this.#ensureReadBytes(frameListSize);
@@ -1334,7 +1352,7 @@ var BikDecoder = class BikDecoder {
1334
1352
  this.#header = {
1335
1353
  version,
1336
1354
  subVersion,
1337
- fileSize: headerWords[1] + 8,
1355
+ totalSize: headerWords[1] + 8,
1338
1356
  numFrames,
1339
1357
  largestFrameSize: headerWords[3],
1340
1358
  width,
@@ -1352,22 +1370,21 @@ var BikDecoder = class BikDecoder {
1352
1370
  };
1353
1371
  }
1354
1372
  /**
1355
- * Decoded header of the BIK file.
1373
+ * Decoded header of the video.
1356
1374
  * @returns Decoded header.
1357
1375
  */
1358
1376
  get header() {
1359
1377
  return this.#header;
1360
1378
  }
1361
1379
  /**
1362
- * Whether the audio/video streams in the BIK file can be processed by the decoder or not.
1363
- * @returns `true` when the audio/video streams can be processed by the decoder, otherwise
1364
- * `false`.
1380
+ * Whether the audio/images in the video can be processed by the decoder or not.
1381
+ * @returns `true` when the audio/images can be processed by the decoder, otherwise `false`.
1365
1382
  */
1366
1383
  get isSupported() {
1367
1384
  return this.#isSupported;
1368
1385
  }
1369
1386
  /**
1370
- * Get the next frame of the BIK file and decode it.
1387
+ * Get the next frame of the video and decode it.
1371
1388
  * @param prevFrame Optional data structure for a previously decoded frame to re-use (to reduce
1372
1389
  * garbage collection).
1373
1390
  * @returns Next decoded frame (audio and video).
@@ -1407,7 +1424,7 @@ var BikDecoder = class BikDecoder {
1407
1424
  header: audioHeader,
1408
1425
  size: audioTrackFrame.size,
1409
1426
  numSamples: audioTrackFrame.numSamples,
1410
- blocks: audioDecoder.v(audioTrackFrame.bytes)
1427
+ blocks: audioDecoder.s(audioTrackFrame.bytes)
1411
1428
  });
1412
1429
  }
1413
1430
  }
@@ -1416,12 +1433,12 @@ var BikDecoder = class BikDecoder {
1416
1433
  const videoFrameBytes = frameBytes.subarray(videoFramePos, videoFramePos + videoFrameSize);
1417
1434
  return {
1418
1435
  audioTracks,
1419
- videoFrame: this.#videoDecoder?.w(videoFrameBytes, prevFrame?.videoFrame) ?? null
1436
+ videoFrame: this.#videoDecoder?.t(videoFrameBytes, prevFrame?.videoFrame) ?? null
1420
1437
  };
1421
1438
  }
1422
1439
  /**
1423
- * Skip the specified number of frames of the BIK file. They will still be decoded as decoding
1424
- * a frame can effectively require data from any number of earlier frames.
1440
+ * Skip the specified number of frames of the video. They will still be decoded as decoding a
1441
+ * frame can effectively require data from any number of earlier frames.
1425
1442
  * @param numFrames Number of frames to skip, but still decode.
1426
1443
  */
1427
1444
  async skipFrames(numFrames) {
@@ -1432,25 +1449,25 @@ var BikDecoder = class BikDecoder {
1432
1449
  }
1433
1450
  }
1434
1451
  /**
1435
- * Reset the current frame index so the decoder is ready to start decoding the BIK file from the
1436
- * beginning. Note that this will result in the decoder requesting a new readable stream.
1452
+ * Reset the state of the decoder so it is ready to start decoding the video from the beginning.
1453
+ * Note that this will result in the decoder streaming the video again from the data source.
1437
1454
  */
1438
1455
  reset() {
1439
1456
  this.#curFrame = -1;
1440
1457
  }
1441
1458
  /**
1442
- * Attempt to read and parse the headers of a BIK file. If successful, return an instance of
1443
- * {@link BikDecoder} for decoding the rest of the file.
1444
- * @param getReadStreamFn -
1445
- * Function that returns a stream for linear access to part of the file.
1446
- * @returns Decoder instance. Use {@link header} to access the parsed headers.
1459
+ * Attempt to read and parse the headers of a BIK video. If successful, return an instance of
1460
+ * {@link BikDecoder} for decoding the rest of the video from the data source.
1461
+ * @param source Data source that will provide the encoded video data.
1462
+ * @returns Decoder instance. Use {@link BikDecoder.header} to access the parsed headers.
1447
1463
  */
1448
- static async open(getReadStreamFn) {
1449
- const decoder = new BikDecoder(getReadStreamFn);
1464
+ static async u(source) {
1465
+ const decoder = new BikDecoder(source);
1450
1466
  await decoder.#init();
1451
1467
  return decoder;
1452
1468
  }
1453
1469
  };
1470
+ const createBikDecoder = BikDecoder.u;
1454
1471
 
1455
1472
  //#endregion
1456
- export { BikDecoder };
1473
+ export { createBikDecoder };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unbikit",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Decoder for `.bik` video files.",
5
5
  "keywords": [
6
6
  "audio",
@@ -34,7 +34,7 @@
34
34
  "@arethetypeswrong/core": "0.18.2",
35
35
  "@astrojs/check": "0.9.5",
36
36
  "@astrojs/mdx": "4.3.12",
37
- "@astrojs/starlight": "0.36.2",
37
+ "@astrojs/starlight": "0.36.3",
38
38
  "@biomejs/biome": "2.3.7",
39
39
  "@commitlint/config-conventional": "20.0.0",
40
40
  "@commitlint/types": "20.0.0",
@@ -42,8 +42,8 @@
42
42
  "@types/audioworklet": "0.0.91",
43
43
  "@types/node": "24.10.1",
44
44
  "@types/pngjs": "6.0.5",
45
- "@vitest/coverage-v8": "4.0.13",
46
- "@vitest/ui": "4.0.13",
45
+ "@vitest/coverage-v8": "4.0.14",
46
+ "@vitest/ui": "4.0.14",
47
47
  "astro": "5.16.0",
48
48
  "commitlint": "20.1.0",
49
49
  "globals": "16.5.0",
@@ -58,14 +58,14 @@
58
58
  "starlight-typedoc": "0.21.4",
59
59
  "tinybench": "5.1.0",
60
60
  "ts-extras": "0.16.0",
61
- "tsdown": "0.16.6",
61
+ "tsdown": "0.16.7",
62
62
  "type-fest": "5.2.0",
63
63
  "typedoc": "0.28.14",
64
64
  "typedoc-plugin-markdown": "4.9.0",
65
65
  "typescript": "5.9.3",
66
66
  "unplugin-unused": "0.5.6",
67
67
  "vite": "npm:rolldown-vite@7.2.7",
68
- "vitest": "4.0.13"
68
+ "vitest": "4.0.14"
69
69
  },
70
70
  "exports": {
71
71
  ".": {