@ahmedrowaihi/mediabunny-server 1.65.2-beta.539 → 1.65.2-beta.544

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.
@@ -80,6 +80,19 @@ var MediabunnyServer = (() => {
80
80
  await peer.closep().catch(() => {
81
81
  });
82
82
  };
83
+ var checkUrlAndOptions = (url, options) => {
84
+ if (typeof url !== "string") {
85
+ throw new TypeError("url must be a string.");
86
+ }
87
+ if (!options || typeof options !== "object") {
88
+ throw new TypeError("options must be an object.");
89
+ }
90
+ };
91
+ var positive = (value, name) => {
92
+ if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) {
93
+ throw new TypeError(`options.${name}, when provided, must be a positive number.`);
94
+ }
95
+ };
83
96
  var CODEC_TO_CODEC_ID = {
84
97
  avc: NodeAv.AV_CODEC_ID_H264,
85
98
  hevc: NodeAv.AV_CODEC_ID_HEVC,
@@ -4909,12 +4922,7 @@ var MediabunnyServer = (() => {
4909
4922
  };
4910
4923
  var NetworkSource = class extends import_mediabunny8.ReadableStreamSource {
4911
4924
  constructor(url, options = {}) {
4912
- if (typeof url !== "string") {
4913
- throw new TypeError("url must be a string.");
4914
- }
4915
- if (!options || typeof options !== "object") {
4916
- throw new TypeError("options must be an object.");
4917
- }
4925
+ checkUrlAndOptions(url, options);
4918
4926
  if (options.connectTimeout !== void 0 && (!Number.isFinite(options.connectTimeout) || options.connectTimeout <= 0)) {
4919
4927
  throw new TypeError("options.connectTimeout, when provided, must be a positive number.");
4920
4928
  }
@@ -4936,11 +4944,6 @@ var MediabunnyServer = (() => {
4936
4944
  var import_node_stream = __require("node:stream");
4937
4945
  var PER_SECOND = 1e6;
4938
4946
  var MOVFLAGS = "frag_keyframe+empty_moov+default_base_moof";
4939
- var positive = (value, name) => {
4940
- if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) {
4941
- throw new TypeError(`options.${name}, when provided, must be a positive number.`);
4942
- }
4943
- };
4944
4947
  var inputOptions = (options) => {
4945
4948
  const result = {
4946
4949
  rw_timeout: String(Math.max(1, Math.round(options.readTimeout * PER_SECOND)))
@@ -5092,12 +5095,7 @@ var MediabunnyServer = (() => {
5092
5095
  };
5093
5096
  var RtmpSource = class extends import_mediabunny9.ReadableStreamSource {
5094
5097
  constructor(url, options = {}) {
5095
- if (typeof url !== "string") {
5096
- throw new TypeError("url must be a string.");
5097
- }
5098
- if (!options || typeof options !== "object") {
5099
- throw new TypeError("options must be an object.");
5100
- }
5098
+ checkUrlAndOptions(url, options);
5101
5099
  if (options.mode !== void 0 && options.mode !== "listener" && options.mode !== "caller") {
5102
5100
  throw new TypeError("options.mode, when provided, must be 'listener' or 'caller'.");
5103
5101
  }
@@ -5130,11 +5128,6 @@ var MediabunnyServer = (() => {
5130
5128
  var RTCP_RTPFB = 205;
5131
5129
  var RTPFB_GENERIC_NACK = 1;
5132
5130
  var RIST_APP_NAME = 1380537172;
5133
- var positive2 = (value, name) => {
5134
- if (value !== void 0 && (!Number.isFinite(value) || value <= 0)) {
5135
- throw new TypeError(`options.${name}, when provided, must be a positive number.`);
5136
- }
5137
- };
5138
5131
  var seqDistance = (a, b) => (a - b & 65535 ^ 32768) - 32768;
5139
5132
  var parseRtp = (bytes) => {
5140
5133
  if (bytes.length < RTP_HEADER_SIZE || bytes[0] >> 6 !== RTP_VERSION) {
@@ -5499,18 +5492,13 @@ var MediabunnyServer = (() => {
5499
5492
  };
5500
5493
  var RistSource = class extends import_mediabunny10.ReadableStreamSource {
5501
5494
  constructor(url, options = {}) {
5502
- if (typeof url !== "string") {
5503
- throw new TypeError("url must be a string.");
5504
- }
5505
- if (!options || typeof options !== "object") {
5506
- throw new TypeError("options must be an object.");
5507
- }
5495
+ checkUrlAndOptions(url, options);
5508
5496
  if (options.mode !== void 0 && options.mode !== "listener" && options.mode !== "caller") {
5509
5497
  throw new TypeError("options.mode, when provided, must be 'listener' or 'caller'.");
5510
5498
  }
5511
- positive2(options.bufferMs, "bufferMs");
5512
- positive2(options.readTimeout, "readTimeout");
5513
- positive2(options.nackIntervalMs, "nackIntervalMs");
5499
+ positive(options.bufferMs, "bufferMs");
5500
+ positive(options.readTimeout, "readTimeout");
5501
+ positive(options.nackIntervalMs, "nackIntervalMs");
5514
5502
  const scheme = url.slice(0, url.indexOf(":"));
5515
5503
  if (scheme !== "rist") {
5516
5504
  throw new TypeError(`RistSource: ${JSON.stringify(url)} is not a rist:// URL.`);
@@ -5532,12 +5520,13 @@ var MediabunnyServer = (() => {
5532
5520
  var PAYLOAD_SIZE = 1316;
5533
5521
  var DEFAULT_MAX_QUEUE_BYTES = 2 * 1024 * 1024;
5534
5522
  var DEFAULT_LINGER_SECONDS = 5;
5535
- var PBKEYLEN_BY_BITS = { 128: 16, 192: 24, 256: 32 };
5523
+ var KEY_LENGTHS = [128, 192, 256];
5536
5524
  var SrtSink = class {
5537
- constructor(url, maxQueueBytes) {
5525
+ constructor(url, maxQueueBytes, listening) {
5538
5526
  this.url = url;
5539
5527
  this.maxQueueBytes = maxQueueBytes;
5540
- this.opening = null;
5528
+ this.listening = listening;
5529
+ this.opening = false;
5541
5530
  this.pumping = null;
5542
5531
  this.failure = null;
5543
5532
  this.closed = false;
@@ -5549,22 +5538,13 @@ var MediabunnyServer = (() => {
5549
5538
  this.sentBytes = 0;
5550
5539
  this.writable = new WritableStream({
5551
5540
  write: (chunk) => this.enqueue(chunk),
5552
- close: () => this.finish(),
5553
- abort: () => this.finish(true)
5541
+ close: () => this.finish()
5554
5542
  });
5555
5543
  }
5556
- /** @internal */
5557
- async open() {
5558
- const context = new import_node_av4.IOContext();
5559
- const ret = await context.open2(this.url, import_constants3.AVIO_FLAG_WRITE);
5560
- if (ret < 0) {
5561
- throw new Error(`SrtTarget: could not open ${this.url} (AVERROR ${ret}).`);
5562
- }
5563
- this.io = context;
5564
- }
5565
5544
  /**
5566
- * Takes the bytes and returns, rather than awaiting the socket: a target that pushed back here would
5567
- * stall the muxer for as long as the receiver was behind, which for a live feed is unbounded.
5545
+ * Takes the bytes and returns rather than awaiting the socket: pushing back here would stall the
5546
+ * muxer for as long as the receiver was behind, which on a live feed is unbounded. Past the queue
5547
+ * bound the *arriving* bytes go, not the queued ones, so what is queued stays a contiguous run.
5568
5548
  */
5569
5549
  enqueue(chunk) {
5570
5550
  if (this.failure) {
@@ -5577,11 +5557,15 @@ var MediabunnyServer = (() => {
5577
5557
  }
5578
5558
  this.queue.chunks.push(chunk);
5579
5559
  this.queue.bytes += chunk.byteLength;
5560
+ void this.kick();
5561
+ }
5562
+ /** Starts the pump unless one is already running, which is the only way one ever starts. */
5563
+ kick() {
5580
5564
  this.pumping ??= this.pump().finally(() => {
5581
5565
  this.pumping = null;
5582
5566
  });
5567
+ return this.pumping;
5583
5568
  }
5584
- /** @internal */
5585
5569
  async writeDatagram(bytes) {
5586
5570
  const io = this.io;
5587
5571
  await io.write(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength));
@@ -5590,12 +5574,16 @@ var MediabunnyServer = (() => {
5590
5574
  }
5591
5575
  this.sentBytes += bytes.byteLength;
5592
5576
  }
5593
- /** @internal */
5594
5577
  async pump() {
5595
5578
  try {
5596
5579
  if (!this.io) {
5597
- this.opening ??= this.open();
5598
- await this.opening;
5580
+ const context = new import_node_av4.IOContext();
5581
+ this.opening = true;
5582
+ const ret = await context.open2(this.url, import_constants3.AVIO_FLAG_WRITE);
5583
+ if (ret < 0) {
5584
+ throw new Error(`SrtTarget: could not open ${this.url} (AVERROR ${ret}).`);
5585
+ }
5586
+ this.io = context;
5599
5587
  }
5600
5588
  while (this.queue.chunks.length > 0) {
5601
5589
  const chunk = this.queue.chunks.shift();
@@ -5607,8 +5595,8 @@ var MediabunnyServer = (() => {
5607
5595
  this.partialLength += take;
5608
5596
  offset += take;
5609
5597
  if (this.partialLength === PAYLOAD_SIZE) {
5610
- this.partialLength = 0;
5611
5598
  await this.writeDatagram(this.partial);
5599
+ this.partialLength = 0;
5612
5600
  }
5613
5601
  }
5614
5602
  }
@@ -5618,19 +5606,19 @@ var MediabunnyServer = (() => {
5618
5606
  }
5619
5607
  }
5620
5608
  /** Drains what is queued, then closes. Awaited by the output's `finalize()`. */
5621
- async finish(aborting = false) {
5609
+ async finish() {
5622
5610
  if (this.closed) {
5623
5611
  return;
5624
5612
  }
5625
5613
  this.closed = true;
5614
+ if (this.listening && this.io === void 0 && this.opening) {
5615
+ await wakePendingAccept(this.url);
5616
+ }
5626
5617
  while (this.pumping !== null || this.queue.chunks.length > 0) {
5627
- this.pumping ??= this.pump().finally(() => {
5628
- this.pumping = null;
5629
- });
5630
- await this.pumping.catch(() => {
5618
+ await this.kick().catch(() => {
5631
5619
  });
5632
5620
  }
5633
- if (!aborting && !this.failure && this.io && this.partialLength > 0) {
5621
+ if (!this.failure && this.io && this.partialLength > 0) {
5634
5622
  await this.writeDatagram(this.partial.subarray(0, this.partialLength)).catch((error) => {
5635
5623
  this.failure ??= error instanceof Error ? error : new Error(String(error));
5636
5624
  });
@@ -5641,7 +5629,7 @@ var MediabunnyServer = (() => {
5641
5629
  await this.io.closep().catch(() => {
5642
5630
  });
5643
5631
  }
5644
- if (this.failure && !aborting) {
5632
+ if (this.failure) {
5645
5633
  throw this.failure;
5646
5634
  }
5647
5635
  }
@@ -5672,43 +5660,39 @@ var MediabunnyServer = (() => {
5672
5660
  }
5673
5661
  if (options.passphrase !== void 0) {
5674
5662
  url.searchParams.set("passphrase", options.passphrase);
5675
- url.searchParams.set("pbkeylen", String(PBKEYLEN_BY_BITS[options.keyLength ?? 128]));
5663
+ url.searchParams.set("pbkeylen", String((options.keyLength ?? 128) / 8));
5676
5664
  }
5677
5665
  if (options.latency !== void 0) {
5678
- url.searchParams.set("latency", micros(options.latency / 1e3));
5666
+ url.searchParams.set("latency", String(Math.max(1, Math.round(options.latency * 1e3))));
5679
5667
  }
5680
5668
  if (url.searchParams.get("mode") === "listener") {
5681
5669
  url.searchParams.set("listen_timeout", micros(connectTimeout));
5682
5670
  }
5683
5671
  url.searchParams.set("timeout", micros(connectTimeout));
5684
- return url.toString();
5672
+ return { url: url.toString(), listening: url.searchParams.get("mode") === "listener" };
5685
5673
  };
5686
5674
  var SrtTarget = class extends import_mediabunny11.AppendOnlyStreamTarget {
5687
5675
  constructor(url, options = {}) {
5688
- if (typeof url !== "string") {
5689
- throw new TypeError("url must be a string.");
5690
- }
5691
- if (!options || typeof options !== "object") {
5692
- throw new TypeError("options must be an object.");
5693
- }
5694
- if (options.connectTimeout !== void 0 && (!Number.isFinite(options.connectTimeout) || options.connectTimeout <= 0)) {
5695
- throw new TypeError("options.connectTimeout, when provided, must be a positive number.");
5696
- }
5676
+ checkUrlAndOptions(url, options);
5677
+ positive(options.connectTimeout, "connectTimeout");
5697
5678
  if (options.passphrase !== void 0 && (typeof options.passphrase !== "string" || options.passphrase.length < 10 || options.passphrase.length > 79)) {
5698
5679
  throw new TypeError("options.passphrase, when provided, must be a string of 10 to 79 characters.");
5699
5680
  }
5700
- if (options.keyLength !== void 0 && PBKEYLEN_BY_BITS[options.keyLength] === void 0) {
5681
+ if (options.keyLength !== void 0 && !KEY_LENGTHS.includes(options.keyLength)) {
5701
5682
  throw new TypeError("options.keyLength, when provided, must be 128, 192 or 256.");
5702
5683
  }
5703
- if (options.latency !== void 0 && (!Number.isFinite(options.latency) || options.latency <= 0)) {
5704
- throw new TypeError("options.latency, when provided, must be a positive number of milliseconds.");
5705
- }
5684
+ positive(options.latency, "latency");
5706
5685
  if (options.maxQueueBytes !== void 0 && (!Number.isInteger(options.maxQueueBytes) || options.maxQueueBytes < PAYLOAD_SIZE)) {
5707
5686
  throw new TypeError(
5708
5687
  `options.maxQueueBytes, when provided, must be an integer of at least ${PAYLOAD_SIZE}.`
5709
5688
  );
5710
5689
  }
5711
- const sink = new SrtSink(srtUrl(url, options), options.maxQueueBytes ?? DEFAULT_MAX_QUEUE_BYTES);
5690
+ const resolved = srtUrl(url, options);
5691
+ const sink = new SrtSink(
5692
+ resolved.url,
5693
+ options.maxQueueBytes ?? DEFAULT_MAX_QUEUE_BYTES,
5694
+ resolved.listening
5695
+ );
5712
5696
  super(sink.writable);
5713
5697
  this.sink = sink;
5714
5698
  }