@mediabunny/server 1.48.1 → 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.
@@ -9,6 +9,7 @@
9
9
  // packages/server/src/index.ts
10
10
  import {
11
11
  AudioSample as AudioSample4,
12
+ Logging as Logging2,
12
13
  registerDecoder,
13
14
  registerEncoder,
14
15
  registerVideoSampleTransformer,
@@ -28,6 +29,7 @@ var CODEC_TO_CODEC_ID = {
28
29
  vp8: NodeAv.AV_CODEC_ID_VP8,
29
30
  vp9: NodeAv.AV_CODEC_ID_VP9,
30
31
  av1: NodeAv.AV_CODEC_ID_AV1,
32
+ prores: NodeAv.AV_CODEC_ID_PRORES,
31
33
  aac: NodeAv.AV_CODEC_ID_AAC,
32
34
  opus: NodeAv.AV_CODEC_ID_OPUS,
33
35
  mp3: NodeAv.AV_CODEC_ID_MP3,
@@ -343,6 +345,56 @@ var getChannelLayout = (numChannels) => {
343
345
  }
344
346
  };
345
347
 
348
+ // src/logging.ts
349
+ var _Logging = class _Logging {
350
+ constructor() {
351
+ }
352
+ /** The current log level. Defaults to {@link LogLevel.Info}. */
353
+ static get level() {
354
+ return _Logging._level;
355
+ }
356
+ static set level(value) {
357
+ if (value !== 0 /* Silent */ && value !== 1 /* Errors */ && value !== 2 /* Warnings */ && value !== 3 /* Info */) {
358
+ throw new TypeError("Invalid log level. Use one of the values of the LogLevel enum.");
359
+ }
360
+ _Logging._level = value;
361
+ }
362
+ /** @internal */
363
+ static get _emitter() {
364
+ return _Logging._emitterInstance ??= new EventEmitter();
365
+ }
366
+ /** Registers a listener for a log event. Returns a function that, when called, removes the listener again. */
367
+ static on(event, listener, options) {
368
+ return _Logging._emitter.on(event, listener, options);
369
+ }
370
+ /** @internal */
371
+ static _error(...args) {
372
+ _Logging._emitter._emit("error", args);
373
+ if (_Logging._level >= 1 /* Errors */) {
374
+ console.error(...args);
375
+ }
376
+ }
377
+ /** @internal */
378
+ static _warn(...args) {
379
+ _Logging._emitter._emit("warn", args);
380
+ if (_Logging._level >= 2 /* Warnings */) {
381
+ console.warn(...args);
382
+ }
383
+ }
384
+ /** @internal */
385
+ static _info(...args) {
386
+ _Logging._emitter._emit("info", args);
387
+ if (_Logging._level >= 3 /* Info */) {
388
+ console.info(...args);
389
+ }
390
+ }
391
+ };
392
+ /** @internal */
393
+ _Logging._level = 3 /* Info */;
394
+ /** @internal */
395
+ _Logging._emitterInstance = null;
396
+ var Logging = _Logging;
397
+
346
398
  // src/misc.ts
347
399
  function assert(x) {
348
400
  if (!x) {
@@ -450,6 +502,9 @@ var binarySearchLessOrEqual = (arr, key, valueGetter) => {
450
502
  }
451
503
  return ans;
452
504
  };
505
+ var assertNever = (x) => {
506
+ throw new Error(`Unexpected value: ${x}`);
507
+ };
453
508
  var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON);
454
509
  var simplifyRational = (rational) => {
455
510
  assert(Number.isInteger(rational.num));
@@ -468,6 +523,41 @@ var simplifyRational = (rational) => {
468
523
  den: rational.den / gcd
469
524
  };
470
525
  };
526
+ var EventEmitter = class {
527
+ constructor() {
528
+ /** @internal */
529
+ this._listeners = /* @__PURE__ */ new Map();
530
+ }
531
+ /** Registers a listener for the given event. Returns a function that, when called, removes the listener again. */
532
+ on(event, listener, options) {
533
+ if (!this._listeners.has(event)) {
534
+ this._listeners.set(event, /* @__PURE__ */ new Set());
535
+ }
536
+ const entry = { fn: listener, once: options?.once ?? false };
537
+ this._listeners.get(event).add(entry);
538
+ return () => {
539
+ this._listeners.get(event)?.delete(entry);
540
+ };
541
+ }
542
+ /** @internal */
543
+ _emit(...args) {
544
+ const [event, data] = args;
545
+ const listeners = this._listeners.get(event);
546
+ if (!listeners) {
547
+ return;
548
+ }
549
+ for (const entry of listeners) {
550
+ try {
551
+ entry.fn(data);
552
+ } catch (error) {
553
+ console.error(error);
554
+ }
555
+ if (entry.once) {
556
+ listeners.delete(entry);
557
+ }
558
+ }
559
+ }
560
+ };
471
561
 
472
562
  // packages/server/src/video-sample.ts
473
563
  import {
@@ -1072,8 +1162,31 @@ var VP9_LEVEL_TABLE = [
1072
1162
  ];
1073
1163
  var VP9_DEFAULT_SUFFIX = ".01.01.01.01.00";
1074
1164
  var AV1_DEFAULT_SUFFIX = ".0.110.01.01.01.0";
1165
+ var PRORES_FOURCCS = [
1166
+ "ap4x",
1167
+ // ProRes 4444 XQ
1168
+ "ap4h",
1169
+ // ProRes 4444
1170
+ "apch",
1171
+ // ProRes 422 High Quality
1172
+ "apcn",
1173
+ // ProRes 422 Standard Definition
1174
+ "apcs",
1175
+ // ProRes 422 LT
1176
+ "apco"
1177
+ // ProRes 422 Proxy
1178
+ ];
1075
1179
  var extractVideoCodecString = (trackInfo) => {
1076
- const { codec, codecDescription, colorSpace, avcCodecInfo, hevcCodecInfo, vp9CodecInfo, av1CodecInfo } = trackInfo;
1180
+ const {
1181
+ codec,
1182
+ codecDescription,
1183
+ colorSpace,
1184
+ avcCodecInfo,
1185
+ hevcCodecInfo,
1186
+ vp9CodecInfo,
1187
+ av1CodecInfo,
1188
+ proresFormat
1189
+ } = trackInfo;
1077
1190
  if (codec === "avc") {
1078
1191
  assert(trackInfo.avcType !== null);
1079
1192
  if (avcCodecInfo) {
@@ -1193,9 +1306,14 @@ var extractVideoCodecString = (trackInfo) => {
1193
1306
  string = string.slice(0, -AV1_DEFAULT_SUFFIX.length);
1194
1307
  }
1195
1308
  return string;
1309
+ } else if (codec === "prores") {
1310
+ return proresFormat ?? "apch";
1311
+ } else if (codec !== null) {
1312
+ assertNever(codec);
1196
1313
  }
1197
1314
  throw new TypeError(`Unhandled codec '${codec}'.`);
1198
1315
  };
1316
+ var VALID_VIDEO_CODEC_STRING_PREFIXES = ["avc1", "avc3", "hev1", "hvc1", "vp8", "vp09", "av01", ...PRORES_FOURCCS];
1199
1317
 
1200
1318
  // src/codec-data.ts
1201
1319
  var iterateNalUnitsInAnnexB = function* (packetData) {
@@ -1291,7 +1409,7 @@ var extractAvcDecoderConfigurationRecord = (packetData) => {
1291
1409
  sequenceParameterSetExt: hasExtendedData ? spsExtUnits : null
1292
1410
  };
1293
1411
  } catch (error) {
1294
- console.error("Error building AVC Decoder Configuration Record:", error);
1412
+ Logging._error("Error building AVC Decoder Configuration Record:", error);
1295
1413
  return null;
1296
1414
  }
1297
1415
  };
@@ -1565,7 +1683,7 @@ var parseAvcSps = (sps) => {
1565
1683
  maxDecFrameBuffering
1566
1684
  };
1567
1685
  } catch (error) {
1568
- console.error("Error parsing AVC SPS:", error);
1686
+ Logging._error("Error parsing AVC SPS:", error);
1569
1687
  return null;
1570
1688
  }
1571
1689
  };
@@ -1709,7 +1827,7 @@ var parseHevcSps = (sps) => {
1709
1827
  minSpatialSegmentationIdc
1710
1828
  };
1711
1829
  } catch (error) {
1712
- console.error("Error parsing HEVC SPS:", error);
1830
+ Logging._error("Error parsing HEVC SPS:", error);
1713
1831
  return null;
1714
1832
  }
1715
1833
  };
@@ -1820,7 +1938,7 @@ var extractHevcDecoderConfigurationRecord = (packetData) => {
1820
1938
  };
1821
1939
  return record;
1822
1940
  } catch (error) {
1823
- console.error("Error building HEVC Decoder Configuration Record:", error);
1941
+ Logging._error("Error building HEVC Decoder Configuration Record:", error);
1824
1942
  return null;
1825
1943
  }
1826
1944
  };
@@ -2494,6 +2612,14 @@ var AC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 65, 67, 45, 51]);
2494
2612
  var EAC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 69, 65, 67, 51]);
2495
2613
 
2496
2614
  // packages/server/src/video-encoder.ts
2615
+ var PRORES_FOURCC_TO_PROFILE = {
2616
+ apco: NodeAv4.AV_PROFILE_PRORES_PROXY,
2617
+ apcs: NodeAv4.AV_PROFILE_PRORES_LT,
2618
+ apcn: NodeAv4.AV_PROFILE_PRORES_STANDARD,
2619
+ apch: NodeAv4.AV_PROFILE_PRORES_HQ,
2620
+ ap4h: NodeAv4.AV_PROFILE_PRORES_4444,
2621
+ ap4x: NodeAv4.AV_PROFILE_PRORES_XQ
2622
+ };
2497
2623
  var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2498
2624
  constructor() {
2499
2625
  super(...arguments);
@@ -2507,7 +2633,7 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2507
2633
  this.preciseTimings = [];
2508
2634
  }
2509
2635
  static supports(codec, config) {
2510
- return (codec === "avc" || codec === "hevc" || codec === "vp8" || codec === "vp9" || codec === "av1") && config.bitrateMode !== "quantizer";
2636
+ return (codec === "avc" || codec === "hevc" || codec === "vp8" || codec === "vp9" || codec === "av1" || codec === "prores") && config.bitrateMode !== "quantizer";
2511
2637
  }
2512
2638
  async init() {
2513
2639
  this.frame = new NodeAv4.Frame();
@@ -2517,13 +2643,22 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2517
2643
  this.packet.alloc();
2518
2644
  const codecId = CODEC_TO_CODEC_ID[this.codec];
2519
2645
  assert(codecId !== void 0);
2646
+ const getSoftwareCodec = () => {
2647
+ if (this.codec === "prores") {
2648
+ const proresKs = NodeAv4.Codec.findEncoderByName(NodeAv4.FF_ENCODER_PRORES_KS);
2649
+ if (proresKs) {
2650
+ return proresKs;
2651
+ }
2652
+ }
2653
+ return NodeAv4.Codec.findEncoder(codecId);
2654
+ };
2520
2655
  let codec = null;
2521
2656
  if (this.codec === "vp9" && this.config.alpha === "keep") {
2522
2657
  codec = NodeAv4.Codec.findEncoderByName(NodeAv4.FF_ENCODER_LIBVPX_VP9) ?? NodeAv4.Codec.findEncoder(codecId);
2523
2658
  } else if (this.config.hardwareAcceleration === "prefer-software") {
2524
- codec = NodeAv4.Codec.findEncoder(codecId);
2659
+ codec = getSoftwareCodec();
2525
2660
  } else {
2526
- codec = await getHardwareEncoderCodec(codecId) ?? NodeAv4.Codec.findEncoder(codecId);
2661
+ codec = await getHardwareEncoderCodec(codecId) ?? getSoftwareCodec();
2527
2662
  }
2528
2663
  if (!codec) {
2529
2664
  throw new Error(`Unable to obtain libav codec for '${this.codec}'.`);
@@ -2540,8 +2675,15 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2540
2675
  if (!this.avCodec.pixelFormats.includes(NodeAv4.AV_PIX_FMT_YUV420P)) {
2541
2676
  pixelFormat = this.avCodec.pixelFormats[0];
2542
2677
  }
2543
- if (this.config.alpha === "keep" && this.avCodec.pixelFormats.includes(NodeAv4.AV_PIX_FMT_YUVA420P)) {
2544
- pixelFormat = NodeAv4.AV_PIX_FMT_YUVA420P;
2678
+ if (this.config.alpha === "keep") {
2679
+ if (this.avCodec.pixelFormats.includes(NodeAv4.AV_PIX_FMT_YUVA420P)) {
2680
+ pixelFormat = NodeAv4.AV_PIX_FMT_YUVA420P;
2681
+ } else {
2682
+ pixelFormat = NodeAv4.avcodecFindBestPixFmtOfList(
2683
+ this.avCodec.pixelFormats,
2684
+ NodeAv4.AV_PIX_FMT_YUVA444P12LE
2685
+ );
2686
+ }
2545
2687
  }
2546
2688
  }
2547
2689
  const pixelAspectRatio = simplifyRational({
@@ -2594,6 +2736,11 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2594
2736
  codecContext.setOption("preset", "12");
2595
2737
  }
2596
2738
  }
2739
+ if (this.codec === "prores") {
2740
+ const profile = PRORES_FOURCC_TO_PROFILE[this.config.codec];
2741
+ assert(profile !== void 0);
2742
+ codecContext.setOption("profile", String(profile));
2743
+ }
2597
2744
  const ret = await codecContext.open2();
2598
2745
  NodeAv4.FFmpegError.throwIfError(ret, "Open codec context");
2599
2746
  this.codecContext = codecContext;
@@ -2744,7 +2891,8 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2744
2891
  avcCodecInfo: null,
2745
2892
  hevcCodecInfo: null,
2746
2893
  vp9CodecInfo: null,
2747
- av1CodecInfo: null
2894
+ av1CodecInfo: null,
2895
+ proresFormat: null
2748
2896
  });
2749
2897
  if (!expectsAnnexB) {
2750
2898
  decoderConfigDescription = serializedRecord;
@@ -2797,7 +2945,8 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2797
2945
  avcCodecInfo: null,
2798
2946
  hevcCodecInfo: null,
2799
2947
  vp9CodecInfo: null,
2800
- av1CodecInfo: null
2948
+ av1CodecInfo: null,
2949
+ proresFormat: null
2801
2950
  });
2802
2951
  }
2803
2952
  } else if (this.codec === "vp9") {
@@ -2813,7 +2962,8 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2813
2962
  avcCodecInfo: null,
2814
2963
  hevcCodecInfo: null,
2815
2964
  vp9CodecInfo,
2816
- av1CodecInfo: null
2965
+ av1CodecInfo: null,
2966
+ proresFormat: null
2817
2967
  });
2818
2968
  }
2819
2969
  } else if (this.codec === "av1") {
@@ -2829,7 +2979,24 @@ var NodeAvVideoEncoder = class extends CustomVideoEncoder {
2829
2979
  avcCodecInfo: null,
2830
2980
  hevcCodecInfo: null,
2831
2981
  vp9CodecInfo: null,
2832
- av1CodecInfo
2982
+ av1CodecInfo,
2983
+ proresFormat: null
2984
+ });
2985
+ }
2986
+ } else if (this.codec === "prores") {
2987
+ if (!this.packetEmitted) {
2988
+ decoderConfigCodecString = extractVideoCodecString({
2989
+ width: this.config.width,
2990
+ height: this.config.height,
2991
+ codec: "prores",
2992
+ codecDescription: null,
2993
+ colorSpace: null,
2994
+ avcType: null,
2995
+ avcCodecInfo: null,
2996
+ hevcCodecInfo: null,
2997
+ vp9CodecInfo: null,
2998
+ av1CodecInfo: null,
2999
+ proresFormat: this.config.codec
2833
3000
  });
2834
3001
  }
2835
3002
  } else {
@@ -3323,9 +3490,10 @@ var NodeAvAudioEncoder = class extends CustomAudioEncoder {
3323
3490
  };
3324
3491
 
3325
3492
  // packages/server/src/index.ts
3493
+ import { registerProresDecoder } from "@mediabunny/prores";
3326
3494
  var SERVER_LOADED_SYMBOL = Symbol.for("@mediabunny/server loaded");
3327
3495
  if (globalThis[SERVER_LOADED_SYMBOL]) {
3328
- console.error(
3496
+ Logging2._error(
3329
3497
  "[WARNING]\n@mediabunny/server was loaded twice. This will likely cause the package not to work correctly. Check if multiple dependencies are importing different versions of @mediabunny/server, or if something is being bundled incorrectly."
3330
3498
  );
3331
3499
  }
@@ -3348,6 +3516,7 @@ var registerMediabunnyServer = (options = {}) => {
3348
3516
  _serverOptions = options;
3349
3517
  NodeAv8.Log.setLevel(NodeAv8.AV_LOG_ERROR);
3350
3518
  registerDecoder(NodeAvVideoDecoder);
3519
+ registerProresDecoder();
3351
3520
  registerEncoder(NodeAvVideoEncoder);
3352
3521
  registerDecoder(NodeAvAudioDecoder);
3353
3522
  registerEncoder(NodeAvAudioEncoder);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,WAAW,EACX,YAAY,EAIZ,WAAW,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAqBlC;;;;GAIG;AACH,KAAK,uBAAuB,GAAG;IAC9B;;;;;;;OAOG;IACH,eAAe,CAAC,EACb,MAAM,CAAC,eAAe,GACtB,IAAI,GACJ,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,KAAK,YAAY,CAAC,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;CAChF,CAAC;AAKF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,GAAI,UAAS,uBAA4B,SAiC7E,CAAC;AAEF,YAAY,EAAE,uBAAuB,EAAE,CAAC;AAExC,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAU,QAAQ,WAAW,GAAG,WAAW,EAAE,OAAO,MAAM,CAAC,KAAK,kBAqCrF,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,WAAW,EAEX,YAAY,EAIZ,WAAW,EACX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAsBlC;;;;GAIG;AACH,KAAK,uBAAuB,GAAG;IAC9B;;;;;;;OAOG;IACH,eAAe,CAAC,EACb,MAAM,CAAC,eAAe,GACtB,IAAI,GACJ,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,KAAK,YAAY,CAAC,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;CAChF,CAAC;AAKF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,GAAI,UAAS,uBAA4B,SAkC7E,CAAC;AAEF,YAAY,EAAE,uBAAuB,EAAE,CAAC;AAExC,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAU,QAAQ,WAAW,GAAG,WAAW,EAAE,OAAO,MAAM,CAAC,KAAK,kBAqCrF,CAAC"}
@@ -49,9 +49,10 @@ const audio_decoder_1 = require("./audio-decoder");
49
49
  const audio_encoder_1 = require("./audio-encoder");
50
50
  const video_sample_1 = require("./video-sample");
51
51
  const audio_sample_1 = require("./audio-sample");
52
+ const prores_1 = require("@mediabunny/prores");
52
53
  const SERVER_LOADED_SYMBOL = Symbol.for('@mediabunny/server loaded');
53
54
  if (globalThis[SERVER_LOADED_SYMBOL]) {
54
- console.error('[WARNING]\n@mediabunny/server was loaded twice.'
55
+ mediabunny_1.Logging._error('[WARNING]\n@mediabunny/server was loaded twice.'
55
56
  + ' This will likely cause the package not to work correctly.'
56
57
  + ' Check if multiple dependencies are importing different versions of @mediabunny/server,'
57
58
  + ' or if something is being bundled incorrectly.');
@@ -91,6 +92,7 @@ const registerMediabunnyServer = (options = {}) => {
91
92
  NodeAv.Log.setLevel(NodeAv.AV_LOG_ERROR);
92
93
  // Video
93
94
  (0, mediabunny_1.registerDecoder)(video_decoder_1.NodeAvVideoDecoder);
95
+ (0, prores_1.registerProresDecoder)(); // Faster than FFmpeg, so we use it
94
96
  (0, mediabunny_1.registerEncoder)(video_encoder_1.NodeAvVideoEncoder);
95
97
  // Audio
96
98
  (0, mediabunny_1.registerDecoder)(audio_decoder_1.NodeAvAudioDecoder);
@@ -1 +1 @@
1
- {"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../src/misc.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAGlC,eAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,CAc3E,CAAC;AAGF,eAAO,MAAM,kBAAkB,QAAO,MAAM,CAAC,eAAe,GAAG,IAU9D,CAAC;AAYF,eAAO,MAAM,uBAAuB,GAAU,SAAS,MAAM,CAAC,SAAS,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAapG,CAAC;AAGF,eAAO,MAAM,uBAAuB,GAAU,SAAS,MAAM,CAAC,SAAS,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAapG,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,mCAUlD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,qEAUpD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,UAAU,MAAM,gDAW1D,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,4EAW5D,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,+BAUnD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,oEAUrD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,mBAAmB,MAAM,CAAC,aAAa,KAAG,sBAAsB,GAAG,IAqChG,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,aAAa,sBAAsB,yBA+BlE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,oBAAoB,MAAM,CAAC,cAAc,KAAG,iBAAiB,GAAG,IAanG,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,cAAc,iBAAiB,KAAG,MAAM,CAAC,cAa9E,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,KAAG,MAAM,CAAC,aAS7D,CAAC"}
1
+ {"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../src/misc.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAGlC,eAAO,MAAM,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,CAe3E,CAAC;AAGF,eAAO,MAAM,kBAAkB,QAAO,MAAM,CAAC,eAAe,GAAG,IAU9D,CAAC;AAYF,eAAO,MAAM,uBAAuB,GAAU,SAAS,MAAM,CAAC,SAAS,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAapG,CAAC;AAGF,eAAO,MAAM,uBAAuB,GAAU,SAAS,MAAM,CAAC,SAAS,KAAG,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAapG,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,mCAUlD,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,WAAW,MAAM,qEAUpD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,UAAU,MAAM,gDAW1D,CAAC;AAEF,eAAO,MAAM,4BAA4B,GAAI,UAAU,MAAM,4EAW5D,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,+BAUnD,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,MAAM,oEAUrD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,mBAAmB,MAAM,CAAC,aAAa,KAAG,sBAAsB,GAAG,IAqChG,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,aAAa,sBAAsB,yBA+BlE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,oBAAoB,MAAM,CAAC,cAAc,KAAG,iBAAiB,GAAG,IAanG,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,cAAc,iBAAiB,KAAG,MAAM,CAAC,cAa9E,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,KAAG,MAAM,CAAC,aAS7D,CAAC"}
@@ -49,6 +49,7 @@ exports.CODEC_TO_CODEC_ID = {
49
49
  vp8: NodeAv.AV_CODEC_ID_VP8,
50
50
  vp9: NodeAv.AV_CODEC_ID_VP9,
51
51
  av1: NodeAv.AV_CODEC_ID_AV1,
52
+ prores: NodeAv.AV_CODEC_ID_PRORES,
52
53
  aac: NodeAv.AV_CODEC_ID_AAC,
53
54
  opus: NodeAv.AV_CODEC_ID_OPUS,
54
55
  mp3: NodeAv.AV_CODEC_ID_MP3,
@@ -1 +1 @@
1
- {"version":3,"file":"video-encoder.d.ts","sourceRoot":"","sources":["../../../src/video-encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,kBAAkB,EAClB,KAAK,YAAY,EAEjB,UAAU,EACV,WAAW,EAGX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AA0BlC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAQ;IAChD,MAAM,EAAE,MAAM,CAAC,oBAAoB,GAAG,IAAI,CAAQ;IAClD,QAAQ,EAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAQ;IACrC,OAAO,EAAG,MAAM,CAAC,KAAK,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC,aAAa,UAAS;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGpC,cAAc,EAAE;QACf,oBAAoB,EAAE,MAAM,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;KACzB,EAAE,CAAM;WAEO,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO;IAK1E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BrB,kBAAkB;IAoFlB,MAAM,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwHzF,aAAa,CAAC,GAAG,EAAE,MAAM;IA0OnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC;CAO3B"}
1
+ {"version":3,"file":"video-encoder.d.ts","sourceRoot":"","sources":["../../../src/video-encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,kBAAkB,EAClB,KAAK,YAAY,EAEjB,UAAU,EACV,WAAW,EAGX,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAmClC,qBAAa,kBAAmB,SAAQ,kBAAkB;IACzD,KAAK,EAAG,MAAM,CAAC,KAAK,CAAC;IACrB,MAAM,EAAG,MAAM,CAAC,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAQ;IAChD,MAAM,EAAE,MAAM,CAAC,oBAAoB,GAAG,IAAI,CAAQ;IAClD,QAAQ,EAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAQ;IACrC,OAAO,EAAG,MAAM,CAAC,KAAK,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IACjC,aAAa,UAAS;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGpC,cAAc,EAAE;QACf,oBAAoB,EAAE,MAAM,CAAC;QAC7B,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;KACzB,EAAE,CAAM;WAEO,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO;IAO1E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyCrB,kBAAkB;IAqGlB,MAAM,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwHzF,aAAa,CAAC,GAAG,EAAE,MAAM;IA8PnB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC;CAO3B"}
@@ -48,6 +48,14 @@ const video_sample_1 = require("./video-sample");
48
48
  const codec_data_1 = require("../../../src/codec-data");
49
49
  const codec_1 = require("../../../src/codec");
50
50
  const misc_2 = require("../../../src/misc");
51
+ const PRORES_FOURCC_TO_PROFILE = {
52
+ apco: NodeAv.AV_PROFILE_PRORES_PROXY,
53
+ apcs: NodeAv.AV_PROFILE_PRORES_LT,
54
+ apcn: NodeAv.AV_PROFILE_PRORES_STANDARD,
55
+ apch: NodeAv.AV_PROFILE_PRORES_HQ,
56
+ ap4h: NodeAv.AV_PROFILE_PRORES_4444,
57
+ ap4x: NodeAv.AV_PROFILE_PRORES_XQ,
58
+ };
51
59
  class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
52
60
  constructor() {
53
61
  super(...arguments);
@@ -61,8 +69,8 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
61
69
  this.preciseTimings = [];
62
70
  }
63
71
  static supports(codec, config) {
64
- return (codec === 'avc' || codec === 'hevc' || codec === 'vp8' || codec === 'vp9' || codec === 'av1')
65
- && config.bitrateMode !== 'quantizer';
72
+ return (codec === 'avc' || codec === 'hevc' || codec === 'vp8' || codec === 'vp9' || codec === 'av1'
73
+ || codec === 'prores') && config.bitrateMode !== 'quantizer';
66
74
  }
67
75
  async init() {
68
76
  this.frame = new NodeAv.Frame();
@@ -72,15 +80,25 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
72
80
  this.packet.alloc();
73
81
  const codecId = misc_1.CODEC_TO_CODEC_ID[this.codec];
74
82
  (0, misc_2.assert)(codecId !== undefined);
83
+ const getSoftwareCodec = () => {
84
+ if (this.codec === 'prores') {
85
+ // Prefer prores_ks for ProRes
86
+ const proresKs = NodeAv.Codec.findEncoderByName(NodeAv.FF_ENCODER_PRORES_KS);
87
+ if (proresKs) {
88
+ return proresKs;
89
+ }
90
+ }
91
+ return NodeAv.Codec.findEncoder(codecId);
92
+ };
75
93
  let codec = null;
76
94
  if (this.codec === 'vp9' && this.config.alpha === 'keep') {
77
95
  codec = NodeAv.Codec.findEncoderByName(NodeAv.FF_ENCODER_LIBVPX_VP9) ?? NodeAv.Codec.findEncoder(codecId);
78
96
  }
79
97
  else if (this.config.hardwareAcceleration === 'prefer-software') {
80
- codec = NodeAv.Codec.findEncoder(codecId);
98
+ codec = getSoftwareCodec();
81
99
  }
82
100
  else {
83
- codec = (await (0, misc_1.getHardwareEncoderCodec)(codecId)) ?? NodeAv.Codec.findEncoder(codecId);
101
+ codec = (await (0, misc_1.getHardwareEncoderCodec)(codecId)) ?? getSoftwareCodec();
84
102
  }
85
103
  if (!codec) {
86
104
  throw new Error(`Unable to obtain libav codec for '${this.codec}'.`);
@@ -97,8 +115,15 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
97
115
  if (!this.avCodec.pixelFormats.includes(NodeAv.AV_PIX_FMT_YUV420P)) {
98
116
  pixelFormat = this.avCodec.pixelFormats[0];
99
117
  }
100
- if (this.config.alpha === 'keep' && this.avCodec.pixelFormats.includes(NodeAv.AV_PIX_FMT_YUVA420P)) {
101
- pixelFormat = NodeAv.AV_PIX_FMT_YUVA420P;
118
+ if (this.config.alpha === 'keep') {
119
+ if (this.avCodec.pixelFormats.includes(NodeAv.AV_PIX_FMT_YUVA420P)) {
120
+ pixelFormat = NodeAv.AV_PIX_FMT_YUVA420P;
121
+ }
122
+ else {
123
+ // Let FFmpeg pick the best alpha-capable format it supports, minimizing data loss versus a
124
+ // high-quality YUVA source
125
+ pixelFormat = NodeAv.avcodecFindBestPixFmtOfList(this.avCodec.pixelFormats, NodeAv.AV_PIX_FMT_YUVA444P12LE);
126
+ }
102
127
  }
103
128
  }
104
129
  const pixelAspectRatio = (0, misc_2.simplifyRational)({
@@ -157,6 +182,12 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
157
182
  codecContext.setOption('preset', '12');
158
183
  }
159
184
  }
185
+ if (this.codec === 'prores') {
186
+ // Pick the encoder profile from the requested ProRes four-character code
187
+ const profile = PRORES_FOURCC_TO_PROFILE[this.config.codec];
188
+ (0, misc_2.assert)(profile !== undefined);
189
+ codecContext.setOption('profile', String(profile));
190
+ }
160
191
  const ret = await codecContext.open2();
161
192
  NodeAv.FFmpegError.throwIfError(ret, 'Open codec context');
162
193
  this.codecContext = codecContext;
@@ -318,6 +349,7 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
318
349
  hevcCodecInfo: null,
319
350
  vp9CodecInfo: null,
320
351
  av1CodecInfo: null,
352
+ proresFormat: null,
321
353
  });
322
354
  if (!expectsAnnexB) {
323
355
  decoderConfigDescription = serializedRecord;
@@ -377,6 +409,7 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
377
409
  hevcCodecInfo: null,
378
410
  vp9CodecInfo: null,
379
411
  av1CodecInfo: null,
412
+ proresFormat: null,
380
413
  });
381
414
  }
382
415
  }
@@ -394,6 +427,7 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
394
427
  hevcCodecInfo: null,
395
428
  vp9CodecInfo,
396
429
  av1CodecInfo: null,
430
+ proresFormat: null,
397
431
  });
398
432
  }
399
433
  }
@@ -411,6 +445,24 @@ class NodeAvVideoEncoder extends mediabunny_1.CustomVideoEncoder {
411
445
  hevcCodecInfo: null,
412
446
  vp9CodecInfo: null,
413
447
  av1CodecInfo,
448
+ proresFormat: null,
449
+ });
450
+ }
451
+ }
452
+ else if (this.codec === 'prores') {
453
+ if (!this.packetEmitted) {
454
+ decoderConfigCodecString = (0, codec_1.extractVideoCodecString)({
455
+ width: this.config.width,
456
+ height: this.config.height,
457
+ codec: 'prores',
458
+ codecDescription: null,
459
+ colorSpace: null,
460
+ avcType: null,
461
+ avcCodecInfo: null,
462
+ hevcCodecInfo: null,
463
+ vp9CodecInfo: null,
464
+ av1CodecInfo: null,
465
+ proresFormat: this.config.codec,
414
466
  });
415
467
  }
416
468
  }