Files
shaka-player/lib/util/uint8array_utils.js
T
Ivan 921206dc1d feat: transmux in a worker (#9914)
This PR introduces a Web Worker for transmuxing resolving
https://github.com/shaka-project/shaka-player/issues/1735

- The worker bundle is compiled separately
- The build output is embedded as a string constant and then wrapped in
a Blob to create an inline Worker URL (HLS.js does this very similarly)
- `TransmuxerProxy` is created wrapping a real transmuxer, but no worker
is started yet - on the first `transmux()` call, it checks if the device
supports worker transmuxing
- For each transmux() call: the buffer is copied, then zero-copy
transferred to the worker. A PublicPromise is stored under a reqId with
a timeout timer, and the main thread awaits it.
- The worker transmuxes and posts back transmuxed (or error). The shared
message listener routes the response to the right proxy instance by id,
which resolves the promise and cancels the timer.
- When the last proxy instance is destroyed, the worker is terminated
and the blob URL is revoked.
  loaded inside the worker.
- Some low-end devices have been excluded since their Worker support is
questionable

There most likely is a better way to do this - please let me know
2026-05-27 21:51:03 +02:00

168 lines
4.7 KiB
JavaScript

/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.util.Uint8ArrayUtils');
goog.require('shaka.util.BufferUtils');
goog.require('shaka.util.StringUtils');
// TODO: revisit this when Closure Compiler supports partially-exported classes.
/**
* @summary A set of Uint8Array utility functions.
* @export
*/
shaka.util.Uint8ArrayUtils = class {
/**
* Convert a buffer to a base64 string. The output will be standard
* alphabet as opposed to base64url safe alphabet.
* @param {BufferSource} data
* @return {string}
* @export
*/
static toStandardBase64(data) {
const arr = shaka.util.BufferUtils.toUint8(data);
// eslint-disable-next-line no-restricted-syntax
if (!Uint8Array.prototype.toBase64) {
const bytes = shaka.util.StringUtils.fromCharCode(arr);
return btoa(bytes);
}
return arr.toBase64({alphabet: 'base64', omitPadding: false});
}
/**
* Convert a buffer to a base64 string. The output will always use the
* alternate encoding/alphabet also known as "base64url".
* @param {BufferSource} data
* @param {boolean=} padding If true, pad the output with equals signs.
* Defaults to true.
* @return {string}
* @export
*/
static toBase64(data, padding) {
padding = (padding == undefined) ? true : padding;
// eslint-disable-next-line no-restricted-syntax
if (!Uint8Array.prototype.toBase64) {
const base64 = shaka.util.Uint8ArrayUtils.toStandardBase64(data)
.replace(/\+/g, '-').replace(/\//g, '_');
return padding ? base64 : base64.replace(/[=]*$/, '');
}
const arr = shaka.util.BufferUtils.toUint8(data);
return arr.toBase64({alphabet: 'base64url', omitPadding: !padding});
}
/**
* Convert a base64 string to a Uint8Array. Accepts either the standard
* alphabet or the alternate "base64url" alphabet.
* @param {string} str
* @return {!Uint8Array}
* @export
*/
static fromBase64(str) {
if (!('fromBase64' in Uint8Array)) {
// atob creates a "raw string" where each character is interpreted as a
// byte.
const bytes = self.atob(str.replace(/-/g, '+').replace(/_/g, '/'));
const result = new Uint8Array(bytes.length);
for (let i = 0; i < bytes.length; ++i) {
result[i] = bytes.charCodeAt(i);
}
return result;
}
const input = str.replace(/\s+/g, '');
const usesUrlAlphabet = /[-_]/.test(input);
return Uint8Array.fromBase64(input, {
alphabet: usesUrlAlphabet ? 'base64url' : 'base64',
});
}
/**
* Convert a hex string to a Uint8Array.
* @param {string} str
* @return {!Uint8Array}
* @export
*/
static fromHex(str) {
if (!('fromHex' in Uint8Array)) {
const size = str.length / 2;
const arr = new Uint8Array(size);
for (let i = 0; i < size; i++) {
arr[i] = parseInt(str.substr(i * 2, 2), 16);
}
return arr;
}
return Uint8Array.fromHex(str);
}
/**
* Convert a buffer to a hex string.
* @param {BufferSource} data
* @return {string}
* @export
*/
static toHex(data) {
const arr = shaka.util.BufferUtils.toUint8(data);
// eslint-disable-next-line no-restricted-syntax
if (!Uint8Array.prototype.toHex) {
let hex = '';
for (let value of arr) {
value = value.toString(16);
if (value.length == 1) {
value = '0' + value;
}
hex += value;
}
return hex;
}
return arr.toHex();
}
/**
* Concatenate buffers.
* @param {...BufferSource} varArgs
* @return {!Uint8Array}
* @export
*/
static concat(...varArgs) {
return shaka.util.Uint8ArrayUtils.concatRange(varArgs, 0, varArgs.length);
}
/**
* Concatenate a range of buffers from a flat array.
* Avoids a subarray allocation compared to concat(...array.slice(s, e)).
* @param {!Array<!BufferSource>} array
* @param {number=} start Inclusive start index.
* @param {number=} end Exclusive end index.
* @return {!Uint8Array}
*/
static concatRange(array, start = 0, end = array.length) {
const BufferUtils = shaka.util.BufferUtils;
let totalLength = 0;
for (let i = start; i < end; ++i) {
totalLength += array[i].byteLength;
}
const result = new Uint8Array(totalLength);
let offset = 0;
for (let i = start; i < end; ++i) {
const value = array[i];
if (ArrayBuffer.isView(value) &&
/** @type {TypedArray} */ (value).BYTES_PER_ELEMENT === 1) {
result.set(/** @type {!Uint8Array} */(value), offset);
} else {
result.set(BufferUtils.toUint8(value), offset);
}
offset += value.byteLength;
}
return result;
}
};