@besovideo/webrtc-player 0.10.42 → 0.10.44

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.
@@ -2,7 +2,7 @@ import {
2
2
  __async,
3
3
  init_define_processenv,
4
4
  init_jsx_shim
5
- } from "./chunk-26DTQELB.js";
5
+ } from "./chunk-7SHCWKP3.js";
6
6
 
7
7
  // src/lib/bvmedia/index.ts
8
8
  init_define_processenv();
@@ -1028,7 +1028,21 @@ var BVMedia = class {
1028
1028
  this.webcodecPlayer.decodeAudio(packet.data, packet.pts);
1029
1029
  }
1030
1030
  }
1031
+ // Recording subscribes independently of playback volume and onaudioData.
1032
+ setDecodedAudioCallback(callback) {
1033
+ this.onDecodedAudioCallback = callback;
1034
+ return Promise.resolve();
1035
+ }
1031
1036
  _OnAudioData(frame) {
1037
+ var _a;
1038
+ (_a = this.onDecodedAudioCallback) == null ? void 0 : _a.call(this, {
1039
+ pts: frame.pts / 1e3,
1040
+ // decoded PCM uses milliseconds; recording uses seconds
1041
+ data: frame.data,
1042
+ channels: this.audioInfo.channels,
1043
+ sampleRate: this.audioInfo.sampleRate,
1044
+ format: this.audioInfo.encoding
1045
+ });
1032
1046
  if (this.audioPlayer) {
1033
1047
  let numberOfFrames = frame.len / this.audioInfo.channels / this.audioPlayer.getNumofSamples(this.audioInfo.encoding);
1034
1048
  let ar = new Uint8Array(frame.data);
@@ -119,7 +119,7 @@ var __async = (__this, __arguments, generator) => {
119
119
  var define_processenv_default;
120
120
  var init_define_processenv = __esm({
121
121
  "<define:processenv>"() {
122
- define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.10.42", PROJECT_NAMESPACE: "bvplayer", SH_LIB_BUILD_TIME: "2026-08-28 17:40:57" };
122
+ define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.10.44", PROJECT_NAMESPACE: "bvplayer", SH_LIB_BUILD_TIME: "2026-09-10 18:05:58" };
123
123
  }
124
124
  });
125
125
 
@@ -125,7 +125,7 @@ var bvPlayerCore = (() => {
125
125
  var define_processenv_default;
126
126
  var init_define_processenv = __esm({
127
127
  "<define:processenv>"() {
128
- define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.10.42", PROJECT_NAMESPACE: "bvplayer", SH_LIB_BUILD_TIME: "2026-08-28 17:40:57" };
128
+ define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.10.44", PROJECT_NAMESPACE: "bvplayer", SH_LIB_BUILD_TIME: "2026-09-10 18:05:58" };
129
129
  }
130
130
  });
131
131
 
@@ -14840,7 +14840,21 @@ CustomVideoTrackGeneratorWorker();`;
14840
14840
  this.webcodecPlayer.decodeAudio(packet.data, packet.pts);
14841
14841
  }
14842
14842
  }
14843
+ // Recording subscribes independently of playback volume and onaudioData.
14844
+ setDecodedAudioCallback(callback) {
14845
+ this.onDecodedAudioCallback = callback;
14846
+ return Promise.resolve();
14847
+ }
14843
14848
  _OnAudioData(frame) {
14849
+ var _a;
14850
+ (_a = this.onDecodedAudioCallback) == null ? void 0 : _a.call(this, {
14851
+ pts: frame.pts / 1e3,
14852
+ // decoded PCM uses milliseconds; recording uses seconds
14853
+ data: frame.data,
14854
+ channels: this.audioInfo.channels,
14855
+ sampleRate: this.audioInfo.sampleRate,
14856
+ format: this.audioInfo.encoding
14857
+ });
14844
14858
  if (this.audioPlayer) {
14845
14859
  let numberOfFrames = frame.len / this.audioInfo.channels / this.audioPlayer.getNumofSamples(this.audioInfo.encoding);
14846
14860
  let ar = new Uint8Array(frame.data);
@@ -37325,6 +37339,182 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
37325
37339
  // src/utils/bvrtcRecord.ts
37326
37340
  init_define_processenv();
37327
37341
  init_jsx_shim();
37342
+
37343
+ // src/utils/hevcRecord.ts
37344
+ init_define_processenv();
37345
+ init_jsx_shim();
37346
+ function hevcDecoderConfig(vps, sps, pps) {
37347
+ const rbsp = [];
37348
+ for (let i3 = 2; i3 < sps.length; i3++) {
37349
+ if (i3 >= 4 && sps[i3] === 3 && sps[i3 - 1] === 0 && sps[i3 - 2] === 0)
37350
+ continue;
37351
+ rbsp.push(sps[i3]);
37352
+ }
37353
+ let offset2 = 0;
37354
+ const read2 = (count) => {
37355
+ if (offset2 + count > rbsp.length * 8)
37356
+ throw new Error("Invalid HEVC SPS in BVRTC callback");
37357
+ let value = 0;
37358
+ for (let i3 = 0; i3 < count; i3++, offset2++)
37359
+ value = value * 2 + (rbsp[offset2 >> 3] >> 7 - (offset2 & 7) & 1);
37360
+ return value;
37361
+ };
37362
+ const ue = () => {
37363
+ let zeros = 0;
37364
+ while (read2(1) === 0) {
37365
+ if (++zeros > 31)
37366
+ throw new Error("Invalid HEVC SPS Exp-Golomb value");
37367
+ }
37368
+ return __pow(2, zeros) - 1 + read2(zeros);
37369
+ };
37370
+ read2(4);
37371
+ const subLayers = read2(3);
37372
+ const temporalNested = read2(1);
37373
+ const profileTierLevel = Uint8Array.from({ length: 12 }, () => read2(8));
37374
+ const subProfiles = [];
37375
+ const subLevels = [];
37376
+ for (let i3 = 0; i3 < subLayers; i3++) {
37377
+ subProfiles.push(read2(1));
37378
+ subLevels.push(read2(1));
37379
+ }
37380
+ if (subLayers)
37381
+ read2((8 - subLayers) * 2);
37382
+ for (let i3 = 0; i3 < subLayers; i3++) {
37383
+ if (subProfiles[i3]) {
37384
+ read2(32);
37385
+ read2(32);
37386
+ read2(24);
37387
+ }
37388
+ if (subLevels[i3])
37389
+ read2(8);
37390
+ }
37391
+ ue();
37392
+ const chromaFormat = ue();
37393
+ if (chromaFormat === 3)
37394
+ read2(1);
37395
+ ue();
37396
+ ue();
37397
+ if (read2(1)) {
37398
+ ue();
37399
+ ue();
37400
+ ue();
37401
+ ue();
37402
+ }
37403
+ const bitDepthLuma = ue();
37404
+ const bitDepthChroma = ue();
37405
+ if (chromaFormat > 3 || bitDepthLuma > 7 || bitDepthChroma > 7)
37406
+ throw new Error("Invalid HEVC SPS format");
37407
+ const sets2 = [vps, sps, pps];
37408
+ if (sets2.some((set2) => set2.length < 2 || set2.length > 65535))
37409
+ throw new Error("Invalid HEVC parameter set length");
37410
+ const result = new Uint8Array(23 + sets2.reduce((size, set2) => size + 5 + set2.length, 0));
37411
+ result[0] = 1;
37412
+ result.set(profileTierLevel, 1);
37413
+ result.set([
37414
+ 240,
37415
+ 0,
37416
+ 252,
37417
+ 252 | chromaFormat,
37418
+ 248 | bitDepthLuma,
37419
+ 248 | bitDepthChroma,
37420
+ 0,
37421
+ 0,
37422
+ subLayers + 1 << 3 | temporalNested << 2 | 3,
37423
+ 3
37424
+ ], 13);
37425
+ let position = 23;
37426
+ sets2.forEach((set2, index) => {
37427
+ result[position++] = 128 | 32 + index;
37428
+ new DataView(result.buffer).setUint16(position, 1);
37429
+ position += 2;
37430
+ new DataView(result.buffer).setUint16(position, set2.length);
37431
+ position += 2;
37432
+ result.set(set2, position);
37433
+ position += set2.length;
37434
+ });
37435
+ return result;
37436
+ }
37437
+
37438
+ // src/utils/pcmResampler.ts
37439
+ init_define_processenv();
37440
+ init_jsx_shim();
37441
+ var PcmResampler = class {
37442
+ constructor(inputRate, outputRate, channels) {
37443
+ this.inputRate = inputRate;
37444
+ this.outputRate = outputRate;
37445
+ this.channels = channels;
37446
+ this.radius = 32;
37447
+ this.buffer = new Int16Array(0);
37448
+ this.bufferStart = 0;
37449
+ this.inputFrames = 0;
37450
+ this.outputFrames = 0;
37451
+ this.phases = [];
37452
+ if (!Number.isInteger(inputRate) || inputRate <= 0 || !Number.isInteger(outputRate) || outputRate <= 0 || !Number.isInteger(channels) || channels <= 0)
37453
+ throw new Error("Invalid PCM resampler format");
37454
+ const gcd = (a, b2) => b2 ? gcd(b2, a % b2) : a;
37455
+ const count = outputRate / gcd(inputRate, outputRate);
37456
+ const cutoff = Math.min(1, outputRate / inputRate);
37457
+ for (let phase = 0; phase < count; phase++) {
37458
+ const fraction = phase * inputRate % outputRate / outputRate;
37459
+ const weights = [];
37460
+ for (let tap = -this.radius + 1; tap <= this.radius; tap++) {
37461
+ const distance = fraction - tap;
37462
+ const x2 = Math.PI * distance * cutoff;
37463
+ const sinc = Math.abs(x2) < 1e-12 ? 1 : Math.sin(x2) / x2;
37464
+ const window2 = Math.abs(distance) >= this.radius ? 0 : 0.42 + 0.5 * Math.cos(Math.PI * distance / this.radius) + 0.08 * Math.cos(2 * Math.PI * distance / this.radius);
37465
+ weights.push(cutoff * sinc * window2);
37466
+ }
37467
+ const sum = weights.reduce((a, b2) => a + b2, 0);
37468
+ this.phases.push(weights.map((weight) => weight / sum));
37469
+ }
37470
+ }
37471
+ get pendingSeconds() {
37472
+ return this.inputFrames / this.inputRate - this.outputFrames / this.outputRate;
37473
+ }
37474
+ process(input) {
37475
+ if (input.length % this.channels)
37476
+ throw new Error("Incomplete interleaved PCM frame");
37477
+ if (this.inputRate === this.outputRate)
37478
+ return input;
37479
+ const merged = new Int16Array(this.buffer.length + input.length);
37480
+ merged.set(this.buffer);
37481
+ merged.set(input, this.buffer.length);
37482
+ this.buffer = merged;
37483
+ this.inputFrames += input.length / this.channels;
37484
+ return this.render(false);
37485
+ }
37486
+ flush() {
37487
+ return this.inputRate === this.outputRate ? new Int16Array(0) : this.render(true);
37488
+ }
37489
+ render(final) {
37490
+ const limit = Math.round(this.inputFrames * this.outputRate / this.inputRate);
37491
+ const result = [];
37492
+ while (this.outputFrames < limit) {
37493
+ const center = Math.floor(this.outputFrames * this.inputRate / this.outputRate);
37494
+ if (!final && center + this.radius >= this.inputFrames)
37495
+ break;
37496
+ const weights = this.phases[this.outputFrames % this.phases.length];
37497
+ for (let channel = 0; channel < this.channels; channel++) {
37498
+ let value = 0;
37499
+ for (let i3 = 0; i3 < weights.length; i3++) {
37500
+ const frame = center - this.radius + 1 + i3;
37501
+ if (frame >= 0 && frame < this.inputFrames)
37502
+ value += this.buffer[(frame - this.bufferStart) * this.channels + channel] * weights[i3];
37503
+ }
37504
+ result.push(Math.max(-32768, Math.min(32767, Math.round(value))));
37505
+ }
37506
+ this.outputFrames++;
37507
+ }
37508
+ const keepFrom = Math.max(0, Math.floor(this.outputFrames * this.inputRate / this.outputRate) - this.radius);
37509
+ if (keepFrom > this.bufferStart) {
37510
+ this.buffer = this.buffer.slice((keepFrom - this.bufferStart) * this.channels);
37511
+ this.bufferStart = keepFrom;
37512
+ }
37513
+ return Int16Array.from(result);
37514
+ }
37515
+ };
37516
+
37517
+ // src/utils/bvrtcRecord.ts
37328
37518
  var startCodeLength = (data, offset2) => data[offset2] === 0 && data[offset2 + 1] === 0 && (data[offset2 + 2] === 1 ? 3 : data[offset2 + 2] === 0 && data[offset2 + 3] === 1 ? 4 : 0);
37329
37519
  function splitAnnexB(data) {
37330
37520
  const units = [];
@@ -37385,32 +37575,21 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
37385
37575
  return value & 128 ? pcm : -pcm;
37386
37576
  };
37387
37577
  var AAC_SAMPLE_RATE = 48e3;
37388
- function resampleS16(input, channels, inputRate) {
37389
- if (inputRate === AAC_SAMPLE_RATE)
37390
- return input;
37391
- const inputFrames = Math.floor(input.length / channels);
37392
- if (!inputFrames)
37393
- return input;
37394
- const outputFrames = Math.round(inputFrames * AAC_SAMPLE_RATE / inputRate);
37395
- const output2 = new Int16Array(outputFrames * channels);
37396
- for (let frame = 0; frame < outputFrames; frame++) {
37397
- const sourcePosition = frame * inputRate / AAC_SAMPLE_RATE;
37398
- const lower = Math.floor(sourcePosition);
37399
- const upper = Math.min(lower + 1, inputFrames - 1);
37400
- const mix = sourcePosition - lower;
37401
- for (let channel = 0; channel < channels; channel++) {
37402
- output2[frame * channels + channel] = input[lower * channels + channel] * (1 - mix) + input[upper * channels + channel] * mix;
37403
- }
37404
- }
37405
- return output2;
37406
- }
37407
37578
  var BvrtcRecorder = class {
37408
37579
  constructor(player, fileName) {
37409
37580
  this.player = player;
37410
37581
  this.fileName = fileName;
37411
37582
  this.startedAt = 0;
37583
+ this.hevc = false;
37584
+ this.videoStarted = false;
37412
37585
  this.pendingAudio = [];
37413
37586
  this.transcodeAlaw = false;
37587
+ this.transcodePcm = false;
37588
+ this.pendingPcmAudio = [];
37589
+ this.pcmChannels = 1;
37590
+ this.recordingClockStart = 0;
37591
+ this.videoClockOffset = 0;
37592
+ this.pcmClockOffset = 0;
37414
37593
  this.stopped = false;
37415
37594
  this.onVideo = (packet) => {
37416
37595
  if (this.stopped)
@@ -37418,6 +37597,45 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
37418
37597
  const units = splitAnnexB(new Uint8Array(packet.data));
37419
37598
  if (!units.length)
37420
37599
  return;
37600
+ const hasPicture = units.some((unit) => this.hevc ? (unit[0] >> 1 & 63) < 32 : (unit[0] & 31) >= 1 && (unit[0] & 31) <= 5);
37601
+ if (this.transcodePcm && this.firstPts === void 0 && hasPicture) {
37602
+ this.videoClockOffset = (performance.now() - this.recordingClockStart) * 1e3;
37603
+ this.normalizePts(packet.pts);
37604
+ }
37605
+ if (this.hevc) {
37606
+ for (const unit of units) {
37607
+ const type = unit[0] >> 1 & 63;
37608
+ if (type === 32)
37609
+ this.vps = unit;
37610
+ if (type === 33)
37611
+ this.sps = unit;
37612
+ if (type === 34)
37613
+ this.pps = unit;
37614
+ }
37615
+ const sampleUnits = units.filter((unit) => {
37616
+ const type = unit[0] >> 1 & 63;
37617
+ return type < 32 || type > 34;
37618
+ });
37619
+ if (!sampleUnits.some((unit) => (unit[0] >> 1 & 63) < 32))
37620
+ return;
37621
+ const key2 = sampleUnits.some((unit) => {
37622
+ const type = unit[0] >> 1 & 63;
37623
+ return type >= 16 && type <= 21;
37624
+ });
37625
+ if (!this.videoStarted && !key2)
37626
+ return;
37627
+ this.createMuxer();
37628
+ if (!this.muxer)
37629
+ return;
37630
+ this.videoStarted = true;
37631
+ const pts2 = this.normalizePts(packet.pts);
37632
+ const duration2 = this.lastVideoPts === void 0 ? 0 : Math.max(0, pts2 - this.lastVideoPts);
37633
+ this.lastVideoPts = pts2;
37634
+ this.muxer.addVideoChunkRaw(avcSample(sampleUnits), key2 ? "key" : "delta", pts2, duration2, {
37635
+ decoderConfig: { codec: "hvc1", description: hevcDecoderConfig(this.vps, this.sps, this.pps) }
37636
+ });
37637
+ return;
37638
+ }
37421
37639
  for (const unit of units) {
37422
37640
  const type = unit[0] & 31;
37423
37641
  if (type === 7)
@@ -37425,13 +37643,19 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
37425
37643
  if (type === 8)
37426
37644
  this.pps = unit;
37427
37645
  }
37646
+ if (!units.some((unit) => (unit[0] & 31) >= 1 && (unit[0] & 31) <= 5))
37647
+ return;
37648
+ const key = units.some((unit) => (unit[0] & 31) === 5);
37649
+ if (!this.videoStarted && !key)
37650
+ return;
37428
37651
  this.createMuxer();
37429
37652
  if (!this.muxer)
37430
37653
  return;
37654
+ this.videoStarted = true;
37431
37655
  const pts = this.normalizePts(packet.pts);
37432
37656
  const duration = this.lastVideoPts === void 0 ? 0 : Math.max(0, pts - this.lastVideoPts);
37433
37657
  this.lastVideoPts = pts;
37434
- this.muxer.addVideoChunkRaw(avcSample(units), packet.flags & 1 ? "key" : "delta", pts, duration, {
37658
+ this.muxer.addVideoChunkRaw(avcSample(units), key ? "key" : "delta", pts, duration, {
37435
37659
  decoderConfig: { codec: "avc1", description: avcDecoderConfig(this.sps, this.pps) }
37436
37660
  });
37437
37661
  };
@@ -37447,77 +37671,115 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
37447
37671
  }
37448
37672
  this.addAudio(packet);
37449
37673
  };
37674
+ this.onPcmAudio = (packet) => {
37675
+ if (this.stopped)
37676
+ return;
37677
+ if (this.pcmClockStart === void 0) {
37678
+ this.pcmClockStart = packet.pts;
37679
+ this.pcmClockOffset = (performance.now() - this.recordingClockStart) * 1e3;
37680
+ }
37681
+ if (!this.muxer) {
37682
+ this.pendingPcmAudio.push(packet);
37683
+ return;
37684
+ }
37685
+ this.addPcmAudio(packet);
37686
+ };
37450
37687
  }
37451
37688
  start() {
37452
37689
  return __async(this, null, function* () {
37453
- var _a, _b;
37690
+ var _a, _b, _c;
37454
37691
  const videoCodec = (_a = this.player.getVideoInfo().codec) == null ? void 0 : _a.toLowerCase();
37455
37692
  const audioCodec = (_b = this.player.getAudioInfo().codec) == null ? void 0 : _b.toLowerCase();
37456
- if (videoCodec && videoCodec !== "none" && !/(h264|avc)/.test(videoCodec))
37693
+ this.hevc = /^(hevc|h265|h\.265|hvc1|hev1)$/.test(videoCodec || "");
37694
+ if (videoCodec && videoCodec !== "none" && !/(h264|avc)/.test(videoCodec) && !this.hevc)
37457
37695
  throw new Error(`BVRTC passthrough recording does not support video codec: ${videoCodec}`);
37458
- if (audioCodec && audioCodec !== "aac" && audioCodec !== "pcm_alaw")
37696
+ this.transcodePcm = /^(adpcm_)?g726(le)?$/.test(audioCodec || "");
37697
+ if (audioCodec && audioCodec !== "aac" && audioCodec !== "pcm_alaw" && !this.transcodePcm)
37459
37698
  throw new Error(`BVRTC passthrough recording does not support audio codec: ${audioCodec}`);
37460
37699
  this.transcodeAlaw = audioCodec === "pcm_alaw";
37461
- if (this.transcodeAlaw && !globalThis.AudioEncoder) {
37462
- throw new Error("This browser does not support WebCodecs AAC audio encoding required for pcm_alaw MP4 recording");
37700
+ if (this.transcodeAlaw || this.transcodePcm) {
37701
+ if (!(yield this.createAudioEncoder())) {
37702
+ this.transcodeAlaw = false;
37703
+ this.transcodePcm = false;
37704
+ }
37705
+ }
37706
+ if (this.transcodePcm && !this.player.setDecodedAudioCallback) {
37707
+ (_c = this.audioEncoder) == null ? void 0 : _c.close();
37708
+ throw new Error("BVRTC G.726 recording requires decoded PCM audio callbacks");
37463
37709
  }
37464
37710
  this.startedAt = Date.now();
37711
+ this.recordingClockStart = performance.now();
37465
37712
  if (videoCodec === "none")
37466
37713
  this.createMuxer();
37467
37714
  yield Promise.all([
37468
37715
  this.player.setVideoCallback(this.onVideo),
37469
- this.player.setAudioCallback(this.onAudio)
37716
+ this.transcodePcm ? this.player.setDecodedAudioCallback(this.onPcmAudio) : this.player.setAudioCallback(this.onAudio)
37470
37717
  ]);
37471
37718
  });
37472
37719
  }
37473
37720
  createMuxer() {
37474
37721
  var _a, _b;
37475
37722
  const hasVideo = ((_a = this.player.getVideoInfo().codec) == null ? void 0 : _a.toLowerCase()) !== "none";
37476
- if (this.muxer || hasVideo && (!this.sps || !this.pps))
37723
+ if (this.muxer || hasVideo && (!this.sps || !this.pps || this.hevc && !this.vps))
37477
37724
  return;
37478
37725
  const video = this.player.getVideoInfo();
37479
37726
  const audio = this.player.getAudioInfo();
37480
37727
  this.target = new ArrayBufferTarget2();
37481
37728
  this.muxer = new Muxer2({
37482
37729
  target: this.target,
37483
- video: hasVideo ? { codec: "avc", width: video.width || 1, height: video.height || 1 } : void 0,
37484
- audio: ((_b = audio.codec) == null ? void 0 : _b.toLowerCase()) === "aac" || this.transcodeAlaw ? { codec: "aac", numberOfChannels: audio.channels || 1, sampleRate: this.transcodeAlaw ? AAC_SAMPLE_RATE : audio.sampleRate || 8e3 } : void 0,
37730
+ video: hasVideo ? { codec: this.hevc ? "hevc" : "avc", width: video.width || 1, height: video.height || 1 } : void 0,
37731
+ audio: ((_b = audio.codec) == null ? void 0 : _b.toLowerCase()) === "aac" || this.transcodeAlaw || this.transcodePcm ? { codec: "aac", numberOfChannels: audio.channels || 1, sampleRate: this.transcodeAlaw || this.transcodePcm ? AAC_SAMPLE_RATE : audio.sampleRate || 8e3 } : void 0,
37485
37732
  fastStart: "in-memory",
37486
37733
  firstTimestampBehavior: "cross-track-offset"
37487
37734
  });
37488
- if (this.transcodeAlaw)
37489
- this.createAlawEncoder();
37490
37735
  for (const packet of this.pendingAudio.splice(0))
37491
37736
  this.addAudio(packet);
37737
+ for (const packet of this.pendingPcmAudio.splice(0))
37738
+ this.addPcmAudio(packet);
37492
37739
  }
37493
- createAlawEncoder() {
37494
- if (this.audioEncoder || !this.muxer)
37495
- return;
37496
- const audio = this.player.getAudioInfo();
37497
- const Encoder3 = globalThis.AudioEncoder;
37498
- this.audioEncoder = new Encoder3({
37499
- output: (chunk, meta) => {
37500
- var _a;
37501
- return (_a = this.muxer) == null ? void 0 : _a.addAudioChunk(chunk, meta);
37502
- },
37503
- error: (error2) => console.error("BVRTC AAC encoder error", error2)
37504
- });
37505
- this.audioEncoder.configure({
37506
- codec: "mp4a.40.2",
37507
- sampleRate: AAC_SAMPLE_RATE,
37508
- numberOfChannels: audio.channels || 1,
37509
- // Chromium's AAC WebCodecs implementation currently accepts 96 kbps+
37510
- // only (even for 8 kHz G.711 input).
37511
- bitrate: 96e3
37740
+ createAudioEncoder() {
37741
+ return __async(this, null, function* () {
37742
+ var _a, _b;
37743
+ const audio = this.player.getAudioInfo();
37744
+ const Encoder3 = globalThis.AudioEncoder;
37745
+ if (!Encoder3 || !globalThis.AudioData)
37746
+ return false;
37747
+ const config = {
37748
+ codec: "mp4a.40.2",
37749
+ sampleRate: AAC_SAMPLE_RATE,
37750
+ numberOfChannels: audio.channels || 1,
37751
+ // Chromium's AAC WebCodecs implementation currently accepts 96 kbps+
37752
+ // only (even for 8 kHz G.711 input).
37753
+ bitrate: 96e3
37754
+ };
37755
+ try {
37756
+ if (!(yield Encoder3.isConfigSupported(config)).supported)
37757
+ return false;
37758
+ this.audioEncoder = new Encoder3({
37759
+ output: (chunk, meta) => {
37760
+ var _a2;
37761
+ return (_a2 = this.muxer) == null ? void 0 : _a2.addAudioChunk(chunk, meta);
37762
+ },
37763
+ error: (error2) => console.error("BVRTC AAC encoder error", error2)
37764
+ });
37765
+ this.audioEncoder.configure(config);
37766
+ return true;
37767
+ } catch (e) {
37768
+ if (((_a = this.audioEncoder) == null ? void 0 : _a.state) !== "closed")
37769
+ (_b = this.audioEncoder) == null ? void 0 : _b.close();
37770
+ this.audioEncoder = void 0;
37771
+ return false;
37772
+ }
37512
37773
  });
37513
37774
  }
37514
37775
  normalizePts(pts) {
37515
37776
  var _a;
37777
+ pts = pts >= 1e11 ? pts / 1e3 : pts;
37516
37778
  (_a = this.firstPts) != null ? _a : this.firstPts = pts;
37517
- return Math.max(0, (pts - this.firstPts) * 1e3);
37779
+ return Math.max(0, Math.round((pts - this.firstPts) * 1e6 + this.videoClockOffset));
37518
37780
  }
37519
37781
  addAudio(packet) {
37520
- var _a, _b;
37782
+ var _a;
37521
37783
  if (!this.muxer)
37522
37784
  return;
37523
37785
  if (this.transcodeAlaw) {
@@ -37526,37 +37788,69 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
37526
37788
  const decoded = new Int16Array(encoded.byteLength);
37527
37789
  for (let i3 = 0; i3 < encoded.byteLength; i3++)
37528
37790
  decoded[i3] = alawToS16(encoded[i3]);
37529
- const channels = audio.channels || 1;
37530
- const pcm = resampleS16(decoded, channels, audio.sampleRate || 8e3);
37531
- const AudioDataCtor = globalThis.AudioData;
37532
- const frame = new AudioDataCtor({ format: "s16", sampleRate: AAC_SAMPLE_RATE, numberOfFrames: pcm.length / channels, numberOfChannels: channels, timestamp: this.normalizePts(packet.pts), data: pcm });
37533
- (_a = this.audioEncoder) == null ? void 0 : _a.encode(frame);
37534
- frame.close();
37791
+ this.encodePcm(decoded, audio.channels || 1, audio.sampleRate || 8e3, packet.pts);
37535
37792
  return;
37536
37793
  }
37537
- if (((_b = this.player.getAudioInfo().codec) == null ? void 0 : _b.toLowerCase()) !== "aac")
37794
+ if (((_a = this.player.getAudioInfo().codec) == null ? void 0 : _a.toLowerCase()) !== "aac")
37538
37795
  return;
37539
- const pts = this.normalizePts(packet.pts);
37540
- const duration = this.lastAudioPts === void 0 ? 0 : Math.max(0, pts - this.lastAudioPts);
37796
+ const duration = 1024 * 1e6 / (this.player.getAudioInfo().sampleRate || 8e3);
37797
+ const sourcePts = this.normalizePts(packet.pts);
37798
+ const pts = this.lastAudioPts === void 0 ? sourcePts : Math.max(sourcePts, this.lastAudioPts + duration);
37541
37799
  this.lastAudioPts = pts;
37542
37800
  this.muxer.addAudioChunkRaw(stripAdts(new Uint8Array(packet.data)), "key", pts, duration);
37543
37801
  }
37802
+ encodePcm(decoded, channels, sampleRate, sourcePts, normalizedPts = this.normalizePts(sourcePts)) {
37803
+ var _a;
37804
+ (_a = this.pcmResampler) != null ? _a : this.pcmResampler = new PcmResampler(sampleRate, AAC_SAMPLE_RATE, channels);
37805
+ this.pcmChannels = channels;
37806
+ const pendingSeconds = this.pcmResampler.pendingSeconds;
37807
+ const pcm = this.pcmResampler.process(decoded);
37808
+ this.writePcm(pcm, channels, normalizedPts - pendingSeconds * 1e6);
37809
+ }
37810
+ writePcm(pcm, channels, normalizedPts) {
37811
+ var _a;
37812
+ const numberOfFrames = pcm.length / channels;
37813
+ if (!numberOfFrames)
37814
+ return;
37815
+ const timestamp = Math.max(normalizedPts, (_a = this.nextPcmPts) != null ? _a : 0);
37816
+ this.nextPcmPts = timestamp + numberOfFrames * 1e6 / AAC_SAMPLE_RATE;
37817
+ const AudioDataCtor = globalThis.AudioData;
37818
+ const frame = new AudioDataCtor({ format: "s16", sampleRate: AAC_SAMPLE_RATE, numberOfFrames, numberOfChannels: channels, timestamp: Math.round(timestamp), data: pcm });
37819
+ try {
37820
+ this.audioEncoder.encode(frame);
37821
+ } finally {
37822
+ frame.close();
37823
+ }
37824
+ }
37825
+ addPcmAudio(packet) {
37826
+ if (packet.format !== "s16")
37827
+ throw new Error(`Unsupported BVRTC decoded PCM format: ${packet.format}`);
37828
+ const timestamp = (packet.pts - this.pcmClockStart) * 1e6 + this.pcmClockOffset;
37829
+ this.encodePcm(new Int16Array(packet.data), packet.channels, packet.sampleRate, packet.pts, timestamp);
37830
+ }
37544
37831
  stop(onRecordInfo) {
37545
37832
  return __async(this, null, function* () {
37546
- var _a, _b, _c;
37833
+ var _a, _b, _c, _d, _e;
37547
37834
  if (this.stopped)
37548
37835
  return;
37549
37836
  this.stopped = true;
37550
37837
  const disableCallbacks = Promise.allSettled([
37551
37838
  this.player.setVideoCallback(null),
37552
- this.player.setAudioCallback(null)
37839
+ this.transcodePcm ? this.player.setDecodedAudioCallback(null) : this.player.setAudioCallback(null)
37553
37840
  ]);
37554
37841
  const endTime = Date.now();
37555
- if (!this.muxer || !this.target)
37842
+ this.pendingAudio = [];
37843
+ this.pendingPcmAudio = [];
37844
+ if (!this.muxer || !this.target) {
37845
+ (_a = this.audioEncoder) == null ? void 0 : _a.close();
37846
+ yield disableCallbacks;
37556
37847
  return;
37557
- yield (_a = this.audioEncoder) == null ? void 0 : _a.flush();
37558
- (_b = this.audioEncoder) == null ? void 0 : _b.close();
37559
- (_c = this.muxer) == null ? void 0 : _c.finalize();
37848
+ }
37849
+ if (this.pcmResampler)
37850
+ this.writePcm(this.pcmResampler.flush(), this.pcmChannels, (_b = this.nextPcmPts) != null ? _b : 0);
37851
+ yield (_c = this.audioEncoder) == null ? void 0 : _c.flush();
37852
+ (_d = this.audioEncoder) == null ? void 0 : _d.close();
37853
+ (_e = this.muxer) == null ? void 0 : _e.finalize();
37560
37854
  const blob = new Blob([this.target.buffer], { type: "video/mp4" });
37561
37855
  const suffix = "mp4";
37562
37856
  const download2 = (name) => file_default.downLoadBlob(blob, name || this.fileName, suffix);
@@ -38656,7 +38950,8 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
38656
38950
  processHandle.reg(() => __async(this, null, function* () {
38657
38951
  yield callApis.avCall(
38658
38952
  __spreadProps(__spreadValues({}, callInfo), {
38659
- id: remoteUserId,
38953
+ // 接通前尚未返回远端 ID,取消呼叫时保留原始设备目标。
38954
+ id: remoteUserId || callInfo.id,
38660
38955
  status: 6 /* END */
38661
38956
  }),
38662
38957
  token
@@ -39090,7 +39385,7 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
39090
39385
  return {
39091
39386
  open(openParams, sinks) {
39092
39387
  return __async(this, null, function* () {
39093
- var _a2, _b, _c, _d, _e;
39388
+ var _a2, _b, _c, _d, _e, _f;
39094
39389
  const rc = {
39095
39390
  code: "SUCCESS"
39096
39391
  };
@@ -39106,11 +39401,18 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
39106
39401
  const mediaOpenError = t("huo-qu-ben-di-mei-ti-shi-bai");
39107
39402
  SetCode(rc, "GET_MEDIA_ERROR", mediaOpenError);
39108
39403
  const hasVideo = !!(openParams.beCall && openParams.beCall.callData.videoCall) || !!((_a2 = openParams.call) == null ? void 0 : _a2.videoCall) || ((_c = (_b = openParams.call) == null ? void 0 : _b.displayMedia) == null ? void 0 : _c.video);
39109
- let mediaStream = null;
39404
+ let mediaStream = ((_d = openParams.call) == null ? void 0 : _d.mediaStream) || null;
39405
+ let mediaInactiveHandler;
39110
39406
  try {
39111
- if ((_d = openParams.call) == null ? void 0 : _d.displayMedia) {
39407
+ if (mediaStream) {
39408
+ mediaInactiveHandler = () => {
39409
+ var _a3, _b2;
39410
+ (_b2 = (_a3 = openParams.call) == null ? void 0 : _a3.onMediaInactive) == null ? void 0 : _b2.call(_a3);
39411
+ };
39412
+ mediaStream.addEventListener("inactive", mediaInactiveHandler);
39413
+ } else if ((_e = openParams.call) == null ? void 0 : _e.displayMedia) {
39112
39414
  mediaStream = yield navigator.mediaDevices.getDisplayMedia({
39113
- video: (_e = openParams.call) == null ? void 0 : _e.displayMedia.video,
39415
+ video: (_f = openParams.call) == null ? void 0 : _f.displayMedia.video,
39114
39416
  audio: !!openParams.call.displayMedia.audio
39115
39417
  });
39116
39418
  mediaStream.addEventListener("inactive", () => {
@@ -39189,6 +39491,9 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
39189
39491
  }
39190
39492
  releaseHandle.regRelease(() => __async(this, null, function* () {
39191
39493
  var _a3;
39494
+ if (mediaInactiveHandler && mediaStream) {
39495
+ mediaStream.removeEventListener("inactive", mediaInactiveHandler);
39496
+ }
39192
39497
  if ((_a3 = openParams.call) == null ? void 0 : _a3.displayMedia) {
39193
39498
  delete openParams.call.displayMedia.onInactive;
39194
39499
  }
@@ -39274,7 +39579,7 @@ a=fmtp:${pt} x-google-start-bitrate=${bitrate}`
39274
39579
  );
39275
39580
  const { promise: waitPCConnected, resolve: resolvePCConnected } = Promise.withResolvers();
39276
39581
  const onPcStateChange = () => {
39277
- var _a3, _b2, _c2, _d2, _e2, _f;
39582
+ var _a3, _b2, _c2, _d2, _e2, _f2;
39278
39583
  if (pc.connectionState === "connected") {
39279
39584
  if (pushResult.code == "WAIT_PEER_CONNECTED_ERROR") {
39280
39585
  resolvePCConnected();
@@ -39290,7 +39595,7 @@ a=fmtp:${pt} x-google-start-bitrate=${bitrate}`
39290
39595
  if (pc.connectionState === "failed") {
39291
39596
  if ((_d2 = openParams.events) == null ? void 0 : _d2.onClose) {
39292
39597
  ((_e2 = openParams.events) == null ? void 0 : _e2.onClose) && openParams.events.onClose();
39293
- (_f = openParams.events) == null ? true : delete _f.onClose;
39598
+ (_f2 = openParams.events) == null ? true : delete _f2.onClose;
39294
39599
  }
39295
39600
  if (pushResult.close) {
39296
39601
  pushResult.close();
@@ -40432,6 +40737,8 @@ a=fmtp:${pt} x-google-start-bitrate=${bitrate}`
40432
40737
  call: {
40433
40738
  videoCall: !!params.video,
40434
40739
  name: "",
40740
+ mediaStream: params.mediaStream,
40741
+ onMediaInactive: params.onMediaInactive,
40435
40742
  displayMedia: params.displayMedia
40436
40743
  },
40437
40744
  events: {