mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-30 12:23:52 +02:00
Merge main into release for tag v1.55.2
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@ node_modules
|
||||
/dist
|
||||
/dist-docs
|
||||
.DS_Store
|
||||
/docs/.vitepress/cache
|
||||
docs/.vitepress/cache
|
||||
/docs/api
|
||||
*.tsbuildinfo
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
});
|
||||
|
||||
const track = await input.getPrimaryVideoTrack();
|
||||
console.log(await track.computeFrameRateMetrics());
|
||||
console.log(await track.getDecoderConfig());
|
||||
|
||||
/*
|
||||
|
||||
|
||||
Generated
+10
-10
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "mediabunny",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mediabunny",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"workspaces": [
|
||||
".",
|
||||
@@ -12958,7 +12958,7 @@
|
||||
},
|
||||
"packages/aac-encoder": {
|
||||
"name": "@mediabunny/aac-encoder",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -12973,7 +12973,7 @@
|
||||
},
|
||||
"packages/ac3": {
|
||||
"name": "@mediabunny/ac3",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -12988,7 +12988,7 @@
|
||||
},
|
||||
"packages/dts": {
|
||||
"name": "@mediabunny/dts",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -13003,7 +13003,7 @@
|
||||
},
|
||||
"packages/flac-encoder": {
|
||||
"name": "@mediabunny/flac-encoder",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -13018,7 +13018,7 @@
|
||||
},
|
||||
"packages/mp3-encoder": {
|
||||
"name": "@mediabunny/mp3-encoder",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"devDependencies": {
|
||||
"@types/emscripten": "^1.40.1"
|
||||
@@ -13033,7 +13033,7 @@
|
||||
},
|
||||
"packages/prores": {
|
||||
"name": "@mediabunny/prores",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"turbores": "^1.2.2"
|
||||
@@ -13048,10 +13048,10 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@mediabunny/server",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@mediabunny/prores": "^1.55.1",
|
||||
"@mediabunny/prores": "^1.55.2",
|
||||
"node-av": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mediabunny",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.",
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/aac-encoder",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "AAC encoder extension for Mediabunny, based on libavcodec.",
|
||||
"main": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
||||
"module": "./dist/bundles/mediabunny-aac-encoder.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/ac3",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "AC-3 and E-AC-3 (Dolby Digital) decoder and encoder extension for Mediabunny, based on libavcodec.",
|
||||
"main": "./dist/bundles/mediabunny-ac3.mjs",
|
||||
"module": "./dist/bundles/mediabunny-ac3.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/dts",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "DTS decoder and encoder extension for Mediabunny, based on libavcodec.",
|
||||
"main": "./dist/bundles/mediabunny-dts.mjs",
|
||||
"module": "./dist/bundles/mediabunny-dts.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/flac-encoder",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "FLAC encoder extension for Mediabunny, based on libFLAC.",
|
||||
"main": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
||||
"module": "./dist/bundles/mediabunny-flac-encoder.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/mp3-encoder",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "MP3 encoder extension for Mediabunny, based on LAME.",
|
||||
"main": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||
"module": "./dist/bundles/mediabunny-mp3-encoder.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/prores",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "Apple ProRes decoder extension for Mediabunny, based on TurboRes.",
|
||||
"main": "./dist/bundles/mediabunny-prores.mjs",
|
||||
"module": "./dist/bundles/mediabunny-prores.mjs",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@mediabunny/server",
|
||||
"author": "Vanilagy",
|
||||
"version": "1.55.1",
|
||||
"version": "1.55.2",
|
||||
"description": "Adds full video and audio decoder and encoder support to Mediabunny for use in server-side environments (Node, Bun, Deno). Based on NodeAV.",
|
||||
"main": "./dist/bundles/mediabunny-server.cjs",
|
||||
"module": "./dist/bundles/mediabunny-server.mjs",
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"node-av": "^6.0.0",
|
||||
"@mediabunny/prores": "^1.55.1"
|
||||
"@mediabunny/prores": "^1.55.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"mediabunny": "^1.45.0"
|
||||
|
||||
@@ -39,6 +39,8 @@ import {
|
||||
parseDtsSpecificBox,
|
||||
DTS_SPECIFIC_BOX_SIZE,
|
||||
AC3_ACMOD_CHANNEL_COUNTS,
|
||||
extractAvcDecoderConfigurationRecord,
|
||||
extractHevcDecoderConfigurationRecord,
|
||||
} from '../codec-data';
|
||||
import { Demuxer } from '../demuxer';
|
||||
import { Input } from '../input';
|
||||
@@ -1390,6 +1392,11 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
}
|
||||
assert(track.info);
|
||||
|
||||
if (boxInfo.contentSize === 0) {
|
||||
// avcC box is empty, let's treat this like an Annex B stream
|
||||
break;
|
||||
}
|
||||
|
||||
track.info.codecDescription = readBytes(slice, boxInfo.contentSize);
|
||||
}; break;
|
||||
|
||||
@@ -1400,6 +1407,11 @@ export class IsobmffDemuxer extends Demuxer {
|
||||
}
|
||||
assert(track.info);
|
||||
|
||||
if (boxInfo.contentSize === 0) {
|
||||
// hvcC box is empty, let's treat this like an Annex B stream
|
||||
break;
|
||||
}
|
||||
|
||||
track.info.codecDescription = readBytes(slice, boxInfo.contentSize);
|
||||
}; break;
|
||||
|
||||
@@ -3407,7 +3419,15 @@ class IsobmffVideoTrackBacking extends IsobmffTrackBacking implements InputVideo
|
||||
}
|
||||
|
||||
return this.decoderConfigPromise ??= (async (): Promise<VideoDecoderConfig> => {
|
||||
if (this.internalTrack.info.codec === 'vp9' && !this.internalTrack.info.vp9CodecInfo) {
|
||||
if (this.internalTrack.info.codec === 'avc' && !this.internalTrack.info.codecDescription) {
|
||||
const firstPacket = await this.getFirstPacket({});
|
||||
this.internalTrack.info.avcCodecInfo
|
||||
= firstPacket && extractAvcDecoderConfigurationRecord(firstPacket.data);
|
||||
} else if (this.internalTrack.info.codec === 'hevc' && !this.internalTrack.info.codecDescription) {
|
||||
const firstPacket = await this.getFirstPacket({});
|
||||
this.internalTrack.info.hevcCodecInfo
|
||||
= firstPacket && extractHevcDecoderConfigurationRecord(firstPacket.data);
|
||||
} else if (this.internalTrack.info.codec === 'vp9' && !this.internalTrack.info.vp9CodecInfo) {
|
||||
const firstPacket = await this.getFirstPacket({});
|
||||
this.internalTrack.info.vp9CodecInfo = firstPacket && extractVp9CodecInfoFromPacket(firstPacket.data);
|
||||
} else if (this.internalTrack.info.codec === 'av1' && !this.internalTrack.info.av1CodecInfo) {
|
||||
|
||||
+11
-2
@@ -38,6 +38,7 @@ import {
|
||||
isWebKit,
|
||||
last,
|
||||
mapAsyncGenerator,
|
||||
missingWebCodecsClassMessage,
|
||||
promiseWithResolvers,
|
||||
removeItem,
|
||||
Rotation,
|
||||
@@ -1691,8 +1692,12 @@ export class VideoSampleSink extends BaseMediaSampleSink<VideoSample> {
|
||||
onError: (error: unknown) => unknown,
|
||||
) {
|
||||
if (!(await this._track.canDecode())) {
|
||||
if (typeof VideoDecoder === 'undefined') {
|
||||
throw new Error(missingWebCodecsClassMessage('VideoDecoder'));
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'This video track cannot be decoded by this browser. Make sure to check decodability before using'
|
||||
'This video track cannot be decoded in this environment. Make sure to check decodability before using'
|
||||
+ ' a track.',
|
||||
);
|
||||
}
|
||||
@@ -2426,8 +2431,12 @@ export class AudioSampleSink extends BaseMediaSampleSink<AudioSample> {
|
||||
onError: (error: unknown) => unknown,
|
||||
) {
|
||||
if (!(await this._track.canDecode())) {
|
||||
if (typeof AudioDecoder === 'undefined') {
|
||||
throw new Error(missingWebCodecsClassMessage('AudioDecoder'));
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'This audio track cannot be decoded by this browser. Make sure to check decodability before using'
|
||||
'This audio track cannot be decoded in this environment. Make sure to check decodability before using'
|
||||
+ ' a track.',
|
||||
);
|
||||
}
|
||||
|
||||
+8
-6
@@ -29,6 +29,7 @@ import {
|
||||
clearIntervalUnthrottled,
|
||||
floorToDivisor,
|
||||
last,
|
||||
missingWebCodecsClassMessage,
|
||||
promiseWithResolvers,
|
||||
roundToDivisor,
|
||||
setInt24,
|
||||
@@ -718,7 +719,7 @@ class VideoEncoderWrapper {
|
||||
|
||||
if (!selected) {
|
||||
if (typeof VideoEncoder === 'undefined') {
|
||||
throw new Error('VideoEncoder is not supported by this browser.');
|
||||
throw new Error(missingWebCodecsClassMessage('VideoEncoder'));
|
||||
}
|
||||
|
||||
// The candidates only differ in their rate control, so we describe them as one config with a
|
||||
@@ -731,7 +732,7 @@ class VideoEncoderWrapper {
|
||||
throw new Error(
|
||||
`This specific encoder configuration (${firstConfig.codec}, ${rateControls.join(' / ')},`
|
||||
+ ` ${firstConfig.width}x${firstConfig.height}, hardware acceleration:`
|
||||
+ ` ${firstConfig.hardwareAcceleration ?? 'no-preference'}) is not supported by this browser.`
|
||||
+ ` ${firstConfig.hardwareAcceleration ?? 'no-preference'}) is not supported in this environment.`
|
||||
+ ` Consider using another codec or changing your video parameters.`,
|
||||
);
|
||||
}
|
||||
@@ -1694,8 +1695,8 @@ export class MediaStreamVideoTrackSource extends VideoSource {
|
||||
});
|
||||
} else {
|
||||
throw new Error(
|
||||
'When no explicit frame rate is set, MediaStreamTrackProcessor is required; but it\'s not supported'
|
||||
+ ' by this browser.',
|
||||
'When no explicit frame rate is set, MediaStreamTrackProcessor is required; but it\'s not available'
|
||||
+ ' in this environment.',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2210,7 +2211,7 @@ class AudioEncoderWrapper {
|
||||
this.initPcmEncoder();
|
||||
} else {
|
||||
if (typeof AudioEncoder === 'undefined') {
|
||||
throw new Error('AudioEncoder is not supported by this browser.');
|
||||
throw new Error(missingWebCodecsClassMessage('AudioEncoder'));
|
||||
}
|
||||
|
||||
let supported: boolean;
|
||||
@@ -2226,7 +2227,8 @@ class AudioEncoderWrapper {
|
||||
throw new Error(
|
||||
`This specific encoder configuration (${encoderConfig.codec}, ${encoderConfig.bitrate} bps,`
|
||||
+ ` ${encoderConfig.numberOfChannels} channels, ${encoderConfig.sampleRate} Hz) is not`
|
||||
+ ` supported by this browser. Consider using another codec or changing your audio parameters.`,
|
||||
+ ` supported in this environment. Consider using another codec or changing your`
|
||||
+ ` audio parameters.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -721,6 +721,16 @@ export const getChromiumVersion = () => {
|
||||
return chromiumVersionCache = Number(match[1]!);
|
||||
};
|
||||
|
||||
export const missingWebCodecsClassMessage = (className: string) => {
|
||||
if (typeof globalThis.isSecureContext !== 'undefined' && !globalThis.isSecureContext) {
|
||||
// WebCodecs is not exposed in insecure contexts
|
||||
return `${className} is not available in this environment; this may be because this page is running in an`
|
||||
+ ` insecure context. Try serving your page over HTTPS or use localhost.`;
|
||||
}
|
||||
|
||||
return `${className} is not available in this environment.`;
|
||||
};
|
||||
|
||||
/**
|
||||
* T or a promise that resolves to T.
|
||||
* @group Miscellaneous
|
||||
|
||||
+3
-3
@@ -155,7 +155,7 @@ export class EncodedPacket {
|
||||
throw new TypeError('Metadata-only packets cannot be converted to a video chunk.');
|
||||
}
|
||||
if (typeof EncodedVideoChunk === 'undefined') {
|
||||
throw new Error('Your browser does not support EncodedVideoChunk.');
|
||||
throw new Error('EncodedVideoChunk is not available in this environment.');
|
||||
}
|
||||
|
||||
return new EncodedVideoChunk({
|
||||
@@ -179,7 +179,7 @@ export class EncodedPacket {
|
||||
throw new TypeError('Metadata-only packets cannot be converted to a video chunk.');
|
||||
}
|
||||
if (typeof EncodedVideoChunk === 'undefined') {
|
||||
throw new Error('Your browser does not support EncodedVideoChunk.');
|
||||
throw new Error('EncodedVideoChunk is not available in this environment.');
|
||||
}
|
||||
|
||||
return new EncodedVideoChunk({
|
||||
@@ -198,7 +198,7 @@ export class EncodedPacket {
|
||||
throw new TypeError('Metadata-only packets cannot be converted to an audio chunk.');
|
||||
}
|
||||
if (typeof EncodedAudioChunk === 'undefined') {
|
||||
throw new Error('Your browser does not support EncodedAudioChunk.');
|
||||
throw new Error('EncodedAudioChunk is not available in this environment.');
|
||||
}
|
||||
|
||||
return new EncodedAudioChunk({
|
||||
|
||||
@@ -76,6 +76,7 @@ const convertVideo = async (quality: Quality, codec: VideoCodec, allowMissingQua
|
||||
trim: { start: 0, end: 2 },
|
||||
video: { codec, quality },
|
||||
audio: { discard: true },
|
||||
showWarnings: false,
|
||||
});
|
||||
|
||||
if (allowMissingQuantizerSupport && !conversion.isValid) {
|
||||
|
||||
@@ -140,3 +140,23 @@ test('QuickTime nclc color information', async () => {
|
||||
});
|
||||
expect(await track.hasHighDynamicRange()).toBe(true);
|
||||
});
|
||||
|
||||
// Annex B isn't supposed to exist in MP4, but some files have it anyway, with an empty avcC box
|
||||
test('Annex B', async () => {
|
||||
const filePath = path.join(__dirname, '..', 'public/annex-b.mp4');
|
||||
using input = new Input({
|
||||
source: new FilePathSource(filePath),
|
||||
formats: ALL_FORMATS,
|
||||
});
|
||||
|
||||
const track = await input.getPrimaryVideoTrack();
|
||||
if (!track) throw new Error('No video track found');
|
||||
|
||||
const decoderConfig = (await track.getDecoderConfig())!;
|
||||
expect(decoderConfig.codec).toBe('avc1.424028');
|
||||
expect(decoderConfig.description).toBeUndefined();
|
||||
|
||||
const sink = new EncodedPacketSink(track);
|
||||
const firstPacket = (await sink.getFirstPacket())!;
|
||||
expect([...firstPacket.data.slice(0, 4)]).toEqual([0, 0, 0, 1]);
|
||||
});
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user