web/webcodecs: use polyfill as fallback

This commit is contained in:
dumbmoron 2024-08-23 18:40:44 +00:00
parent 43b3db1317
commit 9f21a6eb46
No known key found for this signature in database
4 changed files with 73 additions and 45 deletions

View file

@ -97,9 +97,12 @@ importers:
'@imput/libav.js-remux-cli':
specifier: ^5.7.6
version: 5.7.6
'@imput/libavjs-webcodecs-bridge':
specifier: ^0.1.2
version: 0.1.2
'@imput/libavjs-webcodecs-polyfill':
specifier: ^0.5.1
version: 0.5.1
specifier: ^0.5.2
version: 0.5.2
'@imput/version-info':
specifier: workspace:^
version: link:../packages/version-info
@ -109,9 +112,6 @@ importers:
'@vitejs/plugin-basic-ssl':
specifier: ^1.1.0
version: 1.1.0(vite@5.3.5(@types/node@20.14.14))
libavjs-webcodecs-bridge:
specifier: ^0.1.0
version: 0.1.0
mime:
specifier: ^4.0.4
version: 4.0.4
@ -544,8 +544,11 @@ packages:
'@imput/libav.js-remux-cli@5.7.6':
resolution: {integrity: sha512-ofSSLjRF9RfZ3QMBlb7fhxso8p8xlDqU4qX8eCJCukCB15g7iBShthCyGVnYz+3lLoFu9klbvVal9bEncBj/FQ==}
'@imput/libavjs-webcodecs-polyfill@0.5.1':
resolution: {integrity: sha512-uQ5vawG/4ppLKDumkg8BjnvqKWzoKXxt7cj0lvTpB9ph1hiuzjNx8wmwMcXbeTVAzRcts+GtCTPpSF9rFosYBg==}
'@imput/libavjs-webcodecs-bridge@0.1.2':
resolution: {integrity: sha512-8QVvCe9CRe3oVGpMrx/RtHmPunrK+I52nUImM/FDbOt13nEq0rd+zu5ADIMOw7rq/8OgLqqf/o1yQ+Sa4/mhSw==}
'@imput/libavjs-webcodecs-polyfill@0.5.2':
resolution: {integrity: sha512-3wyRFUTV7yJyYNJvSKzoZWNnGbSN/5RVj+Xx9vYH8/xt73TLpXBBqotGc7UiHNeSy6eFO0EjUzAmieBa+YjLrQ==}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
@ -1541,9 +1544,6 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
libavjs-webcodecs-bridge@0.1.0:
resolution: {integrity: sha512-K9pGvW+k2sGnbaCEuhbLG6ylzKoQyVAA1WxO+qIzaYEeVumosaFeKtMzVAThcIy6VHo11rGVF6IqLfSEEQL7sg==}
lilconfig@3.1.2:
resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
engines: {node: '>=14'}
@ -2522,7 +2522,9 @@ snapshots:
'@imput/libav.js-remux-cli@5.7.6': {}
'@imput/libavjs-webcodecs-polyfill@0.5.1':
'@imput/libavjs-webcodecs-bridge@0.1.2': {}
'@imput/libavjs-webcodecs-polyfill@0.5.2':
dependencies:
'@ungap/global-this': 0.4.4
@ -3572,8 +3574,6 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
libavjs-webcodecs-bridge@0.1.0: {}
lilconfig@3.1.2: {}
lines-and-columns@1.2.4: {}

View file

@ -51,11 +51,11 @@
"@fontsource/ibm-plex-mono": "^5.0.13",
"@imput/libav.js-encode-cli": "^5.7.6",
"@imput/libav.js-remux-cli": "^5.7.6",
"@imput/libavjs-webcodecs-polyfill": "^0.5.1",
"@imput/libavjs-webcodecs-bridge": "^0.1.2",
"@imput/libavjs-webcodecs-polyfill": "^0.5.2",
"@imput/version-info": "workspace:^",
"@tabler/icons-svelte": "3.6.0",
"@vitejs/plugin-basic-ssl": "^1.1.0",
"libavjs-webcodecs-bridge": "^0.1.0",
"mime": "^4.0.4",
"sveltekit-i18n": "^2.4.2",
"ts-deepmerge": "^7.0.0",

View file

@ -1,7 +1,6 @@
import LibAV, { type LibAV as LibAVInstance, type Packet, type Stream } from "@imput/libav.js-encode-cli";
import type { Chunk, ChunkMetadata, Decoder, FFmpegProgressCallback, OutputStream, Pipeline, RenderingPipeline } from "../types/libav";
import * as LibAVWebCodecs from "libavjs-webcodecs-bridge";
import { BufferStream } from "./buffer-stream";
import LibAV, { type Packet, type Stream } from "@imput/libav.js-encode-cli";
import type { Chunk, ChunkMetadata, Decoder, OutputStream, Pipeline, RenderingPipeline } from "../types/libav";
import * as LibAVWebCodecs from "@imput/libavjs-webcodecs-bridge";
import { BufferStream } from "../buffer-stream";
import WebCodecsWrapper from "./webcodecs";
import LibAVWrapper from "./instance";
@ -44,7 +43,8 @@ export default class EncodeLibAV extends LibAVWrapper {
const pipes: RenderingPipeline[] = [];
const output_streams: OutputStream[] = [];
for (const stream of streams) {
if (stream.codec_id === 61) {
// FIXME: support images.
if (stream.codec_id === 61 || stream.codec_id === 62) {
pipes.push(null);
output_streams.push(null);
continue;
@ -53,7 +53,7 @@ export default class EncodeLibAV extends LibAVWrapper {
const {
pipe,
stream: ostream
} = await this.#createEncoder(stream, 'avc1.64083e');
} = await this.#createEncoder(stream, 'mp3');
pipes.push({
decoder: await this.#createDecoder(stream),
@ -312,12 +312,11 @@ export default class EncodeLibAV extends LibAVWrapper {
if (stream.codec_type === libav.AVMEDIA_TYPE_VIDEO) {
streamToConfig = LibAVWebCodecs.videoStreamToConfig;
configToStream = LibAVWebCodecs.configToVideoStream;
initEncoder = webcodecs!.initVideoEncoder.bind(webcodecs);
initEncoder = webcodecs.initVideoEncoder.bind(webcodecs);
} else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
streamToConfig = LibAVWebCodecs.audioStreamToConfig;
configToStream = LibAVWebCodecs.configToAudioStream;
initEncoder = webcodecs.initAudioEncoder.bind(webcodecs);
codec = 'mp4a.40.29';
} else throw "Unknown type: " + stream.codec_type;
const config = await streamToConfig(libav, stream, true);

View file

@ -16,31 +16,46 @@ export default class WebCodecsWrapper {
async #load() {
if (typeof this.#ready === 'undefined') {
this.#ready = LibAVPolyfill.load({
polyfill: false,
polyfill: true,
LibAV: { LibAV: () => this.#libav }
});
}
await this.#ready;
}
// FIXME: save me generics. generics save me
async #getDecoder(config: VideoDecoderConfig | AudioDecoderConfig) {
if (has(config, 'numberOfChannels') && has(config, 'sampleRate')) {
const audioConfig = config as AudioDecoderConfig;
if ('AudioDecoder' in window && await window.AudioDecoder.isConfigSupported(audioConfig))
return window.AudioDecoder;
for (const source of [ window, LibAVPolyfill ]) {
if (source === LibAVPolyfill) {
await this.#load();
if (await LibAVPolyfill.AudioDecoder.isConfigSupported(audioConfig))
return LibAVPolyfill.AudioDecoder;
}
try {
const { supported } = await source.AudioDecoder.isConfigSupported(audioConfig);
if (supported) return source.AudioDecoder;
} catch(e) {
console.error('AudioDecoder missing or does not support', config);
console.error(e);
}
}
} else {
const videoConfig = config as VideoDecoderConfig;
if ('VideoDecoder' in window && await window.VideoDecoder.isConfigSupported(videoConfig))
return window.VideoDecoder;
for (const source of [ window, LibAVPolyfill ]) {
if (source === LibAVPolyfill) {
await this.#load();
if (await LibAVPolyfill.VideoDecoder.isConfigSupported(videoConfig))
return LibAVPolyfill.VideoDecoder;
}
try {
const { supported } = await source.VideoDecoder.isConfigSupported(videoConfig);
if (supported) return source.VideoDecoder;
} catch(e) {
console.error('VideoDecoder missing or does not support', config);
console.error(e);
}
}
}
return null;
@ -49,20 +64,34 @@ export default class WebCodecsWrapper {
async #getEncoder(config: VideoEncoderConfig | AudioEncoderConfig) {
if (has(config, 'numberOfChannels') && has(config, 'sampleRate')) {
const audioConfig = config as AudioEncoderConfig;
if ('AudioEncoder' in window && await window.AudioEncoder.isConfigSupported(audioConfig))
return window.AudioEncoder;
for (const source of [ window, LibAVPolyfill ]) {
if (source === LibAVPolyfill) {
await this.#load();
if (await LibAVPolyfill.AudioEncoder.isConfigSupported(audioConfig))
return LibAVPolyfill.AudioEncoder;
}
try {
const { supported } = await source.AudioEncoder.isConfigSupported(audioConfig);
if (supported) return source.AudioEncoder;
} catch(e) {
console.error('AudioEncoder missing or does not support', config);
console.error(e);
}
}
} else if (has(config, 'width') && has(config, 'height')) {
const videoConfig = config as VideoEncoderConfig;
if ('VideoEncoder' in window && await window.VideoEncoder.isConfigSupported(videoConfig))
return window.VideoEncoder;
for (const source of [ window, LibAVPolyfill ]) {
if (source === LibAVPolyfill) {
await this.#load();
if (await LibAVPolyfill.VideoEncoder.isConfigSupported(videoConfig))
return LibAVPolyfill.VideoEncoder;
}
try {
const { supported } = await source.VideoEncoder.isConfigSupported(videoConfig);
if (supported) return source.VideoEncoder;
} catch(e) {
console.error('VideoEncoder missing or does not support', config);
console.error(e);
}
}
} else throw new Error("unreachable");
return null;