@mediabunny/server 1.49.0 → 1.50.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 +3 -1
- package/dist/bundles/mediabunny-server.cjs +93 -10
- package/dist/bundles/mediabunny-server.min.cjs +2 -2
- package/dist/bundles/mediabunny-server.min.mjs +2 -2
- package/dist/bundles/mediabunny-server.mjs +93 -10
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/index.js +2 -0
- package/dist/modules/src/misc.d.ts.map +1 -1
- package/dist/modules/src/misc.js +1 -0
- package/dist/modules/src/video-encoder.d.ts.map +1 -1
- package/dist/modules/src/video-encoder.js +58 -6
- package/dist/modules/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/index.ts +2 -0
- package/src/misc.ts +1 -0
- package/src/video-encoder.ts +67 -7
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mediabunny/server",
|
|
3
3
|
"author": "Vanilagy",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.50.0",
|
|
5
5
|
"description": "Adds full video and audio decoder and encoder support to Mediabunny for use in server-side environments (Node, Bun, Deno). Based on NodeAV.",
|
|
6
6
|
"main": "./dist/bundles/mediabunny-server.cjs",
|
|
7
7
|
"module": "./dist/bundles/mediabunny-server.mjs",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"url": "https://github.com/sponsors/Vanilagy"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"node-av": "^6.0.0"
|
|
37
|
+
"node-av": "^6.0.0",
|
|
38
|
+
"@mediabunny/prores": "^1.50.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"mediabunny": "^1.45.0"
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { NodeAvAudioDecoder } from './audio-decoder';
|
|
|
22
22
|
import { NodeAvAudioEncoder } from './audio-encoder';
|
|
23
23
|
import { copyVideoSampleToAvFrame, AvFrameVideoSampleResource, transformVideoSample } from './video-sample';
|
|
24
24
|
import { copyAudioSampleToAvFrame, AvFrameAudioSampleResource } from './audio-sample';
|
|
25
|
+
import { registerProresDecoder } from '@mediabunny/prores';
|
|
25
26
|
|
|
26
27
|
const SERVER_LOADED_SYMBOL = Symbol.for('@mediabunny/server loaded');
|
|
27
28
|
if ((globalThis as Record<symbol, unknown>)[SERVER_LOADED_SYMBOL]) {
|
|
@@ -100,6 +101,7 @@ export const registerMediabunnyServer = (options: MediabunnyServerOptions = {})
|
|
|
100
101
|
|
|
101
102
|
// Video
|
|
102
103
|
registerDecoder(NodeAvVideoDecoder);
|
|
104
|
+
registerProresDecoder(); // Faster than FFmpeg, so we use it
|
|
103
105
|
registerEncoder(NodeAvVideoEncoder);
|
|
104
106
|
|
|
105
107
|
// Audio
|
package/src/misc.ts
CHANGED
|
@@ -16,6 +16,7 @@ export const CODEC_TO_CODEC_ID: Partial<Record<MediaCodec, NodeAv.AVCodecID>> =
|
|
|
16
16
|
vp8: NodeAv.AV_CODEC_ID_VP8,
|
|
17
17
|
vp9: NodeAv.AV_CODEC_ID_VP9,
|
|
18
18
|
av1: NodeAv.AV_CODEC_ID_AV1,
|
|
19
|
+
prores: NodeAv.AV_CODEC_ID_PRORES,
|
|
19
20
|
|
|
20
21
|
aac: NodeAv.AV_CODEC_ID_AAC,
|
|
21
22
|
opus: NodeAv.AV_CODEC_ID_OPUS,
|
package/src/video-encoder.ts
CHANGED
|
@@ -38,9 +38,18 @@ import {
|
|
|
38
38
|
serializeAvcDecoderConfigurationRecord,
|
|
39
39
|
serializeHevcDecoderConfigurationRecord,
|
|
40
40
|
} from '../../../src/codec-data';
|
|
41
|
-
import { extractVideoCodecString } from '../../../src/codec';
|
|
41
|
+
import { extractVideoCodecString, ProresFourCc } from '../../../src/codec';
|
|
42
42
|
import { assert, binarySearchLessOrEqual, simplifyRational, toUint8Array } from '../../../src/misc';
|
|
43
43
|
|
|
44
|
+
const PRORES_FOURCC_TO_PROFILE: Record<ProresFourCc, NodeAv.AVProfile> = {
|
|
45
|
+
apco: NodeAv.AV_PROFILE_PRORES_PROXY,
|
|
46
|
+
apcs: NodeAv.AV_PROFILE_PRORES_LT,
|
|
47
|
+
apcn: NodeAv.AV_PROFILE_PRORES_STANDARD,
|
|
48
|
+
apch: NodeAv.AV_PROFILE_PRORES_HQ,
|
|
49
|
+
ap4h: NodeAv.AV_PROFILE_PRORES_4444,
|
|
50
|
+
ap4x: NodeAv.AV_PROFILE_PRORES_XQ,
|
|
51
|
+
};
|
|
52
|
+
|
|
44
53
|
export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
45
54
|
frame!: NodeAv.Frame;
|
|
46
55
|
packet!: NodeAv.Packet;
|
|
@@ -62,8 +71,10 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
62
71
|
}[] = [];
|
|
63
72
|
|
|
64
73
|
static override supports(codec: VideoCodec, config: VideoEncoderConfig): boolean {
|
|
65
|
-
return (
|
|
66
|
-
|
|
74
|
+
return (
|
|
75
|
+
codec === 'avc' || codec === 'hevc' || codec === 'vp8' || codec === 'vp9' || codec === 'av1'
|
|
76
|
+
|| codec === 'prores'
|
|
77
|
+
) && config.bitrateMode !== 'quantizer';
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
async init(): Promise<void> {
|
|
@@ -77,13 +88,25 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
77
88
|
const codecId = CODEC_TO_CODEC_ID[this.codec];
|
|
78
89
|
assert(codecId !== undefined);
|
|
79
90
|
|
|
91
|
+
const getSoftwareCodec = () => {
|
|
92
|
+
if (this.codec === 'prores') {
|
|
93
|
+
// Prefer prores_ks for ProRes
|
|
94
|
+
const proresKs = NodeAv.Codec.findEncoderByName(NodeAv.FF_ENCODER_PRORES_KS);
|
|
95
|
+
if (proresKs) {
|
|
96
|
+
return proresKs;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return NodeAv.Codec.findEncoder(codecId);
|
|
101
|
+
};
|
|
102
|
+
|
|
80
103
|
let codec: NodeAv.Codec | null = null;
|
|
81
104
|
if (this.codec === 'vp9' && this.config.alpha === 'keep') {
|
|
82
105
|
codec = NodeAv.Codec.findEncoderByName(NodeAv.FF_ENCODER_LIBVPX_VP9) ?? NodeAv.Codec.findEncoder(codecId);
|
|
83
106
|
} else if (this.config.hardwareAcceleration === 'prefer-software') {
|
|
84
|
-
codec =
|
|
107
|
+
codec = getSoftwareCodec();
|
|
85
108
|
} else {
|
|
86
|
-
codec = (await getHardwareEncoderCodec(codecId)) ??
|
|
109
|
+
codec = (await getHardwareEncoderCodec(codecId)) ?? getSoftwareCodec();
|
|
87
110
|
}
|
|
88
111
|
|
|
89
112
|
if (!codec) {
|
|
@@ -108,8 +131,17 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
108
131
|
pixelFormat = this.avCodec.pixelFormats[0]!;
|
|
109
132
|
}
|
|
110
133
|
|
|
111
|
-
if (this.config.alpha === 'keep'
|
|
112
|
-
|
|
134
|
+
if (this.config.alpha === 'keep') {
|
|
135
|
+
if (this.avCodec.pixelFormats.includes(NodeAv.AV_PIX_FMT_YUVA420P)) {
|
|
136
|
+
pixelFormat = NodeAv.AV_PIX_FMT_YUVA420P;
|
|
137
|
+
} else {
|
|
138
|
+
// Let FFmpeg pick the best alpha-capable format it supports, minimizing data loss versus a
|
|
139
|
+
// high-quality YUVA source
|
|
140
|
+
pixelFormat = NodeAv.avcodecFindBestPixFmtOfList(
|
|
141
|
+
this.avCodec.pixelFormats,
|
|
142
|
+
NodeAv.AV_PIX_FMT_YUVA444P12LE,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
113
145
|
}
|
|
114
146
|
}
|
|
115
147
|
|
|
@@ -173,6 +205,14 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
173
205
|
}
|
|
174
206
|
}
|
|
175
207
|
|
|
208
|
+
if (this.codec === 'prores') {
|
|
209
|
+
// Pick the encoder profile from the requested ProRes four-character code
|
|
210
|
+
const profile = PRORES_FOURCC_TO_PROFILE[this.config.codec as ProresFourCc];
|
|
211
|
+
assert(profile !== undefined);
|
|
212
|
+
|
|
213
|
+
codecContext.setOption('profile', String(profile));
|
|
214
|
+
}
|
|
215
|
+
|
|
176
216
|
const ret = await codecContext.open2();
|
|
177
217
|
NodeAv.FFmpegError.throwIfError(ret, 'Open codec context');
|
|
178
218
|
|
|
@@ -375,6 +415,7 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
375
415
|
hevcCodecInfo: null,
|
|
376
416
|
vp9CodecInfo: null,
|
|
377
417
|
av1CodecInfo: null,
|
|
418
|
+
proresFormat: null,
|
|
378
419
|
});
|
|
379
420
|
|
|
380
421
|
if (!expectsAnnexB) {
|
|
@@ -450,6 +491,7 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
450
491
|
hevcCodecInfo: null,
|
|
451
492
|
vp9CodecInfo: null,
|
|
452
493
|
av1CodecInfo: null,
|
|
494
|
+
proresFormat: null,
|
|
453
495
|
});
|
|
454
496
|
}
|
|
455
497
|
} else if (this.codec === 'vp9') {
|
|
@@ -467,6 +509,7 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
467
509
|
hevcCodecInfo: null,
|
|
468
510
|
vp9CodecInfo,
|
|
469
511
|
av1CodecInfo: null,
|
|
512
|
+
proresFormat: null,
|
|
470
513
|
});
|
|
471
514
|
}
|
|
472
515
|
} else if (this.codec === 'av1') {
|
|
@@ -484,6 +527,23 @@ export class NodeAvVideoEncoder extends CustomVideoEncoder {
|
|
|
484
527
|
hevcCodecInfo: null,
|
|
485
528
|
vp9CodecInfo: null,
|
|
486
529
|
av1CodecInfo,
|
|
530
|
+
proresFormat: null,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
} else if (this.codec === 'prores') {
|
|
534
|
+
if (!this.packetEmitted) {
|
|
535
|
+
decoderConfigCodecString = extractVideoCodecString({
|
|
536
|
+
width: this.config.width,
|
|
537
|
+
height: this.config.height,
|
|
538
|
+
codec: 'prores',
|
|
539
|
+
codecDescription: null,
|
|
540
|
+
colorSpace: null,
|
|
541
|
+
avcType: null,
|
|
542
|
+
avcCodecInfo: null,
|
|
543
|
+
hevcCodecInfo: null,
|
|
544
|
+
vp9CodecInfo: null,
|
|
545
|
+
av1CodecInfo: null,
|
|
546
|
+
proresFormat: this.config.codec as ProresFourCc,
|
|
487
547
|
});
|
|
488
548
|
}
|
|
489
549
|
} else {
|