@besovideo/webrtc-player 0.10.43 → 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.
- package/dist/{bvmedia-7YQE7SZM.js → bvmedia-7JPROY4X.js} +15 -1
- package/dist/{chunk-7BKM3LDB.js → chunk-7SHCWKP3.js} +1 -1
- package/dist/main.browser.js +364 -69
- package/dist/main.es.js +351 -70
- package/dist/types/utils/bvrtcRecord.d.ts +25 -1
- package/dist/types/utils/hevcRecord.d.ts +1 -0
- package/dist/types/utils/pcmResampler.d.ts +16 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
__async,
|
|
3
3
|
init_define_processenv,
|
|
4
4
|
init_jsx_shim
|
|
5
|
-
} from "./chunk-
|
|
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.
|
|
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
|
|
package/dist/main.browser.js
CHANGED
|
@@ -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.
|
|
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),
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
37462
|
-
|
|
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
|
-
|
|
37494
|
-
|
|
37495
|
-
|
|
37496
|
-
|
|
37497
|
-
|
|
37498
|
-
|
|
37499
|
-
|
|
37500
|
-
|
|
37501
|
-
|
|
37502
|
-
|
|
37503
|
-
|
|
37504
|
-
|
|
37505
|
-
|
|
37506
|
-
|
|
37507
|
-
|
|
37508
|
-
|
|
37509
|
-
|
|
37510
|
-
|
|
37511
|
-
|
|
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) *
|
|
37779
|
+
return Math.max(0, Math.round((pts - this.firstPts) * 1e6 + this.videoClockOffset));
|
|
37518
37780
|
}
|
|
37519
37781
|
addAudio(packet) {
|
|
37520
|
-
var _a
|
|
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
|
-
|
|
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 (((
|
|
37794
|
+
if (((_a = this.player.getAudioInfo().codec) == null ? void 0 : _a.toLowerCase()) !== "aac")
|
|
37538
37795
|
return;
|
|
37539
|
-
const
|
|
37540
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
37558
|
-
(
|
|
37559
|
-
|
|
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
|
-
|
|
38953
|
+
// 接通前尚未返回远端 ID,取消呼叫时保留原始设备目标。
|
|
38954
|
+
id: remoteUserId || callInfo.id,
|
|
38660
38955
|
status: 6 /* END */
|
|
38661
38956
|
}),
|
|
38662
38957
|
token
|
package/dist/main.es.js
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
logger_default,
|
|
28
28
|
t,
|
|
29
29
|
util_default
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-7SHCWKP3.js";
|
|
31
31
|
|
|
32
32
|
// node_modules/.pnpm/ts-ebml@3.0.2/node_modules/ts-ebml/dist/EBML.min.js
|
|
33
33
|
var require_EBML_min = __commonJS({
|
|
@@ -29833,7 +29833,7 @@ init_define_processenv();
|
|
|
29833
29833
|
init_jsx_shim();
|
|
29834
29834
|
var bvMediaPromise;
|
|
29835
29835
|
function loadBvMedia() {
|
|
29836
|
-
bvMediaPromise != null ? bvMediaPromise : bvMediaPromise = import("./bvmedia-
|
|
29836
|
+
bvMediaPromise != null ? bvMediaPromise : bvMediaPromise = import("./bvmedia-7JPROY4X.js").then(
|
|
29837
29837
|
(module) => module.default
|
|
29838
29838
|
);
|
|
29839
29839
|
return bvMediaPromise;
|
|
@@ -31858,6 +31858,182 @@ var DialogManager = class {
|
|
|
31858
31858
|
// src/utils/bvrtcRecord.ts
|
|
31859
31859
|
init_define_processenv();
|
|
31860
31860
|
init_jsx_shim();
|
|
31861
|
+
|
|
31862
|
+
// src/utils/hevcRecord.ts
|
|
31863
|
+
init_define_processenv();
|
|
31864
|
+
init_jsx_shim();
|
|
31865
|
+
function hevcDecoderConfig(vps, sps, pps) {
|
|
31866
|
+
const rbsp = [];
|
|
31867
|
+
for (let i2 = 2; i2 < sps.length; i2++) {
|
|
31868
|
+
if (i2 >= 4 && sps[i2] === 3 && sps[i2 - 1] === 0 && sps[i2 - 2] === 0)
|
|
31869
|
+
continue;
|
|
31870
|
+
rbsp.push(sps[i2]);
|
|
31871
|
+
}
|
|
31872
|
+
let offset2 = 0;
|
|
31873
|
+
const read2 = (count) => {
|
|
31874
|
+
if (offset2 + count > rbsp.length * 8)
|
|
31875
|
+
throw new Error("Invalid HEVC SPS in BVRTC callback");
|
|
31876
|
+
let value = 0;
|
|
31877
|
+
for (let i2 = 0; i2 < count; i2++, offset2++)
|
|
31878
|
+
value = value * 2 + (rbsp[offset2 >> 3] >> 7 - (offset2 & 7) & 1);
|
|
31879
|
+
return value;
|
|
31880
|
+
};
|
|
31881
|
+
const ue = () => {
|
|
31882
|
+
let zeros = 0;
|
|
31883
|
+
while (read2(1) === 0) {
|
|
31884
|
+
if (++zeros > 31)
|
|
31885
|
+
throw new Error("Invalid HEVC SPS Exp-Golomb value");
|
|
31886
|
+
}
|
|
31887
|
+
return __pow(2, zeros) - 1 + read2(zeros);
|
|
31888
|
+
};
|
|
31889
|
+
read2(4);
|
|
31890
|
+
const subLayers = read2(3);
|
|
31891
|
+
const temporalNested = read2(1);
|
|
31892
|
+
const profileTierLevel = Uint8Array.from({ length: 12 }, () => read2(8));
|
|
31893
|
+
const subProfiles = [];
|
|
31894
|
+
const subLevels = [];
|
|
31895
|
+
for (let i2 = 0; i2 < subLayers; i2++) {
|
|
31896
|
+
subProfiles.push(read2(1));
|
|
31897
|
+
subLevels.push(read2(1));
|
|
31898
|
+
}
|
|
31899
|
+
if (subLayers)
|
|
31900
|
+
read2((8 - subLayers) * 2);
|
|
31901
|
+
for (let i2 = 0; i2 < subLayers; i2++) {
|
|
31902
|
+
if (subProfiles[i2]) {
|
|
31903
|
+
read2(32);
|
|
31904
|
+
read2(32);
|
|
31905
|
+
read2(24);
|
|
31906
|
+
}
|
|
31907
|
+
if (subLevels[i2])
|
|
31908
|
+
read2(8);
|
|
31909
|
+
}
|
|
31910
|
+
ue();
|
|
31911
|
+
const chromaFormat = ue();
|
|
31912
|
+
if (chromaFormat === 3)
|
|
31913
|
+
read2(1);
|
|
31914
|
+
ue();
|
|
31915
|
+
ue();
|
|
31916
|
+
if (read2(1)) {
|
|
31917
|
+
ue();
|
|
31918
|
+
ue();
|
|
31919
|
+
ue();
|
|
31920
|
+
ue();
|
|
31921
|
+
}
|
|
31922
|
+
const bitDepthLuma = ue();
|
|
31923
|
+
const bitDepthChroma = ue();
|
|
31924
|
+
if (chromaFormat > 3 || bitDepthLuma > 7 || bitDepthChroma > 7)
|
|
31925
|
+
throw new Error("Invalid HEVC SPS format");
|
|
31926
|
+
const sets = [vps, sps, pps];
|
|
31927
|
+
if (sets.some((set2) => set2.length < 2 || set2.length > 65535))
|
|
31928
|
+
throw new Error("Invalid HEVC parameter set length");
|
|
31929
|
+
const result = new Uint8Array(23 + sets.reduce((size, set2) => size + 5 + set2.length, 0));
|
|
31930
|
+
result[0] = 1;
|
|
31931
|
+
result.set(profileTierLevel, 1);
|
|
31932
|
+
result.set([
|
|
31933
|
+
240,
|
|
31934
|
+
0,
|
|
31935
|
+
252,
|
|
31936
|
+
252 | chromaFormat,
|
|
31937
|
+
248 | bitDepthLuma,
|
|
31938
|
+
248 | bitDepthChroma,
|
|
31939
|
+
0,
|
|
31940
|
+
0,
|
|
31941
|
+
subLayers + 1 << 3 | temporalNested << 2 | 3,
|
|
31942
|
+
3
|
|
31943
|
+
], 13);
|
|
31944
|
+
let position = 23;
|
|
31945
|
+
sets.forEach((set2, index) => {
|
|
31946
|
+
result[position++] = 128 | 32 + index;
|
|
31947
|
+
new DataView(result.buffer).setUint16(position, 1);
|
|
31948
|
+
position += 2;
|
|
31949
|
+
new DataView(result.buffer).setUint16(position, set2.length);
|
|
31950
|
+
position += 2;
|
|
31951
|
+
result.set(set2, position);
|
|
31952
|
+
position += set2.length;
|
|
31953
|
+
});
|
|
31954
|
+
return result;
|
|
31955
|
+
}
|
|
31956
|
+
|
|
31957
|
+
// src/utils/pcmResampler.ts
|
|
31958
|
+
init_define_processenv();
|
|
31959
|
+
init_jsx_shim();
|
|
31960
|
+
var PcmResampler = class {
|
|
31961
|
+
constructor(inputRate, outputRate, channels) {
|
|
31962
|
+
this.inputRate = inputRate;
|
|
31963
|
+
this.outputRate = outputRate;
|
|
31964
|
+
this.channels = channels;
|
|
31965
|
+
this.radius = 32;
|
|
31966
|
+
this.buffer = new Int16Array(0);
|
|
31967
|
+
this.bufferStart = 0;
|
|
31968
|
+
this.inputFrames = 0;
|
|
31969
|
+
this.outputFrames = 0;
|
|
31970
|
+
this.phases = [];
|
|
31971
|
+
if (!Number.isInteger(inputRate) || inputRate <= 0 || !Number.isInteger(outputRate) || outputRate <= 0 || !Number.isInteger(channels) || channels <= 0)
|
|
31972
|
+
throw new Error("Invalid PCM resampler format");
|
|
31973
|
+
const gcd = (a, b) => b ? gcd(b, a % b) : a;
|
|
31974
|
+
const count = outputRate / gcd(inputRate, outputRate);
|
|
31975
|
+
const cutoff = Math.min(1, outputRate / inputRate);
|
|
31976
|
+
for (let phase = 0; phase < count; phase++) {
|
|
31977
|
+
const fraction = phase * inputRate % outputRate / outputRate;
|
|
31978
|
+
const weights = [];
|
|
31979
|
+
for (let tap = -this.radius + 1; tap <= this.radius; tap++) {
|
|
31980
|
+
const distance = fraction - tap;
|
|
31981
|
+
const x = Math.PI * distance * cutoff;
|
|
31982
|
+
const sinc = Math.abs(x) < 1e-12 ? 1 : Math.sin(x) / x;
|
|
31983
|
+
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);
|
|
31984
|
+
weights.push(cutoff * sinc * window2);
|
|
31985
|
+
}
|
|
31986
|
+
const sum = weights.reduce((a, b) => a + b, 0);
|
|
31987
|
+
this.phases.push(weights.map((weight) => weight / sum));
|
|
31988
|
+
}
|
|
31989
|
+
}
|
|
31990
|
+
get pendingSeconds() {
|
|
31991
|
+
return this.inputFrames / this.inputRate - this.outputFrames / this.outputRate;
|
|
31992
|
+
}
|
|
31993
|
+
process(input) {
|
|
31994
|
+
if (input.length % this.channels)
|
|
31995
|
+
throw new Error("Incomplete interleaved PCM frame");
|
|
31996
|
+
if (this.inputRate === this.outputRate)
|
|
31997
|
+
return input;
|
|
31998
|
+
const merged = new Int16Array(this.buffer.length + input.length);
|
|
31999
|
+
merged.set(this.buffer);
|
|
32000
|
+
merged.set(input, this.buffer.length);
|
|
32001
|
+
this.buffer = merged;
|
|
32002
|
+
this.inputFrames += input.length / this.channels;
|
|
32003
|
+
return this.render(false);
|
|
32004
|
+
}
|
|
32005
|
+
flush() {
|
|
32006
|
+
return this.inputRate === this.outputRate ? new Int16Array(0) : this.render(true);
|
|
32007
|
+
}
|
|
32008
|
+
render(final) {
|
|
32009
|
+
const limit = Math.round(this.inputFrames * this.outputRate / this.inputRate);
|
|
32010
|
+
const result = [];
|
|
32011
|
+
while (this.outputFrames < limit) {
|
|
32012
|
+
const center = Math.floor(this.outputFrames * this.inputRate / this.outputRate);
|
|
32013
|
+
if (!final && center + this.radius >= this.inputFrames)
|
|
32014
|
+
break;
|
|
32015
|
+
const weights = this.phases[this.outputFrames % this.phases.length];
|
|
32016
|
+
for (let channel = 0; channel < this.channels; channel++) {
|
|
32017
|
+
let value = 0;
|
|
32018
|
+
for (let i2 = 0; i2 < weights.length; i2++) {
|
|
32019
|
+
const frame = center - this.radius + 1 + i2;
|
|
32020
|
+
if (frame >= 0 && frame < this.inputFrames)
|
|
32021
|
+
value += this.buffer[(frame - this.bufferStart) * this.channels + channel] * weights[i2];
|
|
32022
|
+
}
|
|
32023
|
+
result.push(Math.max(-32768, Math.min(32767, Math.round(value))));
|
|
32024
|
+
}
|
|
32025
|
+
this.outputFrames++;
|
|
32026
|
+
}
|
|
32027
|
+
const keepFrom = Math.max(0, Math.floor(this.outputFrames * this.inputRate / this.outputRate) - this.radius);
|
|
32028
|
+
if (keepFrom > this.bufferStart) {
|
|
32029
|
+
this.buffer = this.buffer.slice((keepFrom - this.bufferStart) * this.channels);
|
|
32030
|
+
this.bufferStart = keepFrom;
|
|
32031
|
+
}
|
|
32032
|
+
return Int16Array.from(result);
|
|
32033
|
+
}
|
|
32034
|
+
};
|
|
32035
|
+
|
|
32036
|
+
// src/utils/bvrtcRecord.ts
|
|
31861
32037
|
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);
|
|
31862
32038
|
function splitAnnexB(data) {
|
|
31863
32039
|
const units = [];
|
|
@@ -31918,32 +32094,21 @@ var alawToS16 = (sample) => {
|
|
|
31918
32094
|
return value & 128 ? pcm : -pcm;
|
|
31919
32095
|
};
|
|
31920
32096
|
var AAC_SAMPLE_RATE = 48e3;
|
|
31921
|
-
function resampleS16(input, channels, inputRate) {
|
|
31922
|
-
if (inputRate === AAC_SAMPLE_RATE)
|
|
31923
|
-
return input;
|
|
31924
|
-
const inputFrames = Math.floor(input.length / channels);
|
|
31925
|
-
if (!inputFrames)
|
|
31926
|
-
return input;
|
|
31927
|
-
const outputFrames = Math.round(inputFrames * AAC_SAMPLE_RATE / inputRate);
|
|
31928
|
-
const output = new Int16Array(outputFrames * channels);
|
|
31929
|
-
for (let frame = 0; frame < outputFrames; frame++) {
|
|
31930
|
-
const sourcePosition = frame * inputRate / AAC_SAMPLE_RATE;
|
|
31931
|
-
const lower = Math.floor(sourcePosition);
|
|
31932
|
-
const upper = Math.min(lower + 1, inputFrames - 1);
|
|
31933
|
-
const mix = sourcePosition - lower;
|
|
31934
|
-
for (let channel = 0; channel < channels; channel++) {
|
|
31935
|
-
output[frame * channels + channel] = input[lower * channels + channel] * (1 - mix) + input[upper * channels + channel] * mix;
|
|
31936
|
-
}
|
|
31937
|
-
}
|
|
31938
|
-
return output;
|
|
31939
|
-
}
|
|
31940
32097
|
var BvrtcRecorder = class {
|
|
31941
32098
|
constructor(player, fileName) {
|
|
31942
32099
|
this.player = player;
|
|
31943
32100
|
this.fileName = fileName;
|
|
31944
32101
|
this.startedAt = 0;
|
|
32102
|
+
this.hevc = false;
|
|
32103
|
+
this.videoStarted = false;
|
|
31945
32104
|
this.pendingAudio = [];
|
|
31946
32105
|
this.transcodeAlaw = false;
|
|
32106
|
+
this.transcodePcm = false;
|
|
32107
|
+
this.pendingPcmAudio = [];
|
|
32108
|
+
this.pcmChannels = 1;
|
|
32109
|
+
this.recordingClockStart = 0;
|
|
32110
|
+
this.videoClockOffset = 0;
|
|
32111
|
+
this.pcmClockOffset = 0;
|
|
31947
32112
|
this.stopped = false;
|
|
31948
32113
|
this.onVideo = (packet) => {
|
|
31949
32114
|
if (this.stopped)
|
|
@@ -31951,6 +32116,45 @@ var BvrtcRecorder = class {
|
|
|
31951
32116
|
const units = splitAnnexB(new Uint8Array(packet.data));
|
|
31952
32117
|
if (!units.length)
|
|
31953
32118
|
return;
|
|
32119
|
+
const hasPicture = units.some((unit) => this.hevc ? (unit[0] >> 1 & 63) < 32 : (unit[0] & 31) >= 1 && (unit[0] & 31) <= 5);
|
|
32120
|
+
if (this.transcodePcm && this.firstPts === void 0 && hasPicture) {
|
|
32121
|
+
this.videoClockOffset = (performance.now() - this.recordingClockStart) * 1e3;
|
|
32122
|
+
this.normalizePts(packet.pts);
|
|
32123
|
+
}
|
|
32124
|
+
if (this.hevc) {
|
|
32125
|
+
for (const unit of units) {
|
|
32126
|
+
const type = unit[0] >> 1 & 63;
|
|
32127
|
+
if (type === 32)
|
|
32128
|
+
this.vps = unit;
|
|
32129
|
+
if (type === 33)
|
|
32130
|
+
this.sps = unit;
|
|
32131
|
+
if (type === 34)
|
|
32132
|
+
this.pps = unit;
|
|
32133
|
+
}
|
|
32134
|
+
const sampleUnits = units.filter((unit) => {
|
|
32135
|
+
const type = unit[0] >> 1 & 63;
|
|
32136
|
+
return type < 32 || type > 34;
|
|
32137
|
+
});
|
|
32138
|
+
if (!sampleUnits.some((unit) => (unit[0] >> 1 & 63) < 32))
|
|
32139
|
+
return;
|
|
32140
|
+
const key2 = sampleUnits.some((unit) => {
|
|
32141
|
+
const type = unit[0] >> 1 & 63;
|
|
32142
|
+
return type >= 16 && type <= 21;
|
|
32143
|
+
});
|
|
32144
|
+
if (!this.videoStarted && !key2)
|
|
32145
|
+
return;
|
|
32146
|
+
this.createMuxer();
|
|
32147
|
+
if (!this.muxer)
|
|
32148
|
+
return;
|
|
32149
|
+
this.videoStarted = true;
|
|
32150
|
+
const pts2 = this.normalizePts(packet.pts);
|
|
32151
|
+
const duration2 = this.lastVideoPts === void 0 ? 0 : Math.max(0, pts2 - this.lastVideoPts);
|
|
32152
|
+
this.lastVideoPts = pts2;
|
|
32153
|
+
this.muxer.addVideoChunkRaw(avcSample(sampleUnits), key2 ? "key" : "delta", pts2, duration2, {
|
|
32154
|
+
decoderConfig: { codec: "hvc1", description: hevcDecoderConfig(this.vps, this.sps, this.pps) }
|
|
32155
|
+
});
|
|
32156
|
+
return;
|
|
32157
|
+
}
|
|
31954
32158
|
for (const unit of units) {
|
|
31955
32159
|
const type = unit[0] & 31;
|
|
31956
32160
|
if (type === 7)
|
|
@@ -31958,13 +32162,19 @@ var BvrtcRecorder = class {
|
|
|
31958
32162
|
if (type === 8)
|
|
31959
32163
|
this.pps = unit;
|
|
31960
32164
|
}
|
|
32165
|
+
if (!units.some((unit) => (unit[0] & 31) >= 1 && (unit[0] & 31) <= 5))
|
|
32166
|
+
return;
|
|
32167
|
+
const key = units.some((unit) => (unit[0] & 31) === 5);
|
|
32168
|
+
if (!this.videoStarted && !key)
|
|
32169
|
+
return;
|
|
31961
32170
|
this.createMuxer();
|
|
31962
32171
|
if (!this.muxer)
|
|
31963
32172
|
return;
|
|
32173
|
+
this.videoStarted = true;
|
|
31964
32174
|
const pts = this.normalizePts(packet.pts);
|
|
31965
32175
|
const duration = this.lastVideoPts === void 0 ? 0 : Math.max(0, pts - this.lastVideoPts);
|
|
31966
32176
|
this.lastVideoPts = pts;
|
|
31967
|
-
this.muxer.addVideoChunkRaw(avcSample(units),
|
|
32177
|
+
this.muxer.addVideoChunkRaw(avcSample(units), key ? "key" : "delta", pts, duration, {
|
|
31968
32178
|
decoderConfig: { codec: "avc1", description: avcDecoderConfig(this.sps, this.pps) }
|
|
31969
32179
|
});
|
|
31970
32180
|
};
|
|
@@ -31980,77 +32190,115 @@ var BvrtcRecorder = class {
|
|
|
31980
32190
|
}
|
|
31981
32191
|
this.addAudio(packet);
|
|
31982
32192
|
};
|
|
32193
|
+
this.onPcmAudio = (packet) => {
|
|
32194
|
+
if (this.stopped)
|
|
32195
|
+
return;
|
|
32196
|
+
if (this.pcmClockStart === void 0) {
|
|
32197
|
+
this.pcmClockStart = packet.pts;
|
|
32198
|
+
this.pcmClockOffset = (performance.now() - this.recordingClockStart) * 1e3;
|
|
32199
|
+
}
|
|
32200
|
+
if (!this.muxer) {
|
|
32201
|
+
this.pendingPcmAudio.push(packet);
|
|
32202
|
+
return;
|
|
32203
|
+
}
|
|
32204
|
+
this.addPcmAudio(packet);
|
|
32205
|
+
};
|
|
31983
32206
|
}
|
|
31984
32207
|
start() {
|
|
31985
32208
|
return __async(this, null, function* () {
|
|
31986
|
-
var _a, _b;
|
|
32209
|
+
var _a, _b, _c;
|
|
31987
32210
|
const videoCodec = (_a = this.player.getVideoInfo().codec) == null ? void 0 : _a.toLowerCase();
|
|
31988
32211
|
const audioCodec = (_b = this.player.getAudioInfo().codec) == null ? void 0 : _b.toLowerCase();
|
|
31989
|
-
|
|
32212
|
+
this.hevc = /^(hevc|h265|h\.265|hvc1|hev1)$/.test(videoCodec || "");
|
|
32213
|
+
if (videoCodec && videoCodec !== "none" && !/(h264|avc)/.test(videoCodec) && !this.hevc)
|
|
31990
32214
|
throw new Error(`BVRTC passthrough recording does not support video codec: ${videoCodec}`);
|
|
31991
|
-
|
|
32215
|
+
this.transcodePcm = /^(adpcm_)?g726(le)?$/.test(audioCodec || "");
|
|
32216
|
+
if (audioCodec && audioCodec !== "aac" && audioCodec !== "pcm_alaw" && !this.transcodePcm)
|
|
31992
32217
|
throw new Error(`BVRTC passthrough recording does not support audio codec: ${audioCodec}`);
|
|
31993
32218
|
this.transcodeAlaw = audioCodec === "pcm_alaw";
|
|
31994
|
-
if (this.transcodeAlaw
|
|
31995
|
-
|
|
32219
|
+
if (this.transcodeAlaw || this.transcodePcm) {
|
|
32220
|
+
if (!(yield this.createAudioEncoder())) {
|
|
32221
|
+
this.transcodeAlaw = false;
|
|
32222
|
+
this.transcodePcm = false;
|
|
32223
|
+
}
|
|
32224
|
+
}
|
|
32225
|
+
if (this.transcodePcm && !this.player.setDecodedAudioCallback) {
|
|
32226
|
+
(_c = this.audioEncoder) == null ? void 0 : _c.close();
|
|
32227
|
+
throw new Error("BVRTC G.726 recording requires decoded PCM audio callbacks");
|
|
31996
32228
|
}
|
|
31997
32229
|
this.startedAt = Date.now();
|
|
32230
|
+
this.recordingClockStart = performance.now();
|
|
31998
32231
|
if (videoCodec === "none")
|
|
31999
32232
|
this.createMuxer();
|
|
32000
32233
|
yield Promise.all([
|
|
32001
32234
|
this.player.setVideoCallback(this.onVideo),
|
|
32002
|
-
this.player.setAudioCallback(this.onAudio)
|
|
32235
|
+
this.transcodePcm ? this.player.setDecodedAudioCallback(this.onPcmAudio) : this.player.setAudioCallback(this.onAudio)
|
|
32003
32236
|
]);
|
|
32004
32237
|
});
|
|
32005
32238
|
}
|
|
32006
32239
|
createMuxer() {
|
|
32007
32240
|
var _a, _b;
|
|
32008
32241
|
const hasVideo = ((_a = this.player.getVideoInfo().codec) == null ? void 0 : _a.toLowerCase()) !== "none";
|
|
32009
|
-
if (this.muxer || hasVideo && (!this.sps || !this.pps))
|
|
32242
|
+
if (this.muxer || hasVideo && (!this.sps || !this.pps || this.hevc && !this.vps))
|
|
32010
32243
|
return;
|
|
32011
32244
|
const video = this.player.getVideoInfo();
|
|
32012
32245
|
const audio = this.player.getAudioInfo();
|
|
32013
32246
|
this.target = new ArrayBufferTarget2();
|
|
32014
32247
|
this.muxer = new Muxer2({
|
|
32015
32248
|
target: this.target,
|
|
32016
|
-
video: hasVideo ? { codec: "avc", width: video.width || 1, height: video.height || 1 } : void 0,
|
|
32017
|
-
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,
|
|
32249
|
+
video: hasVideo ? { codec: this.hevc ? "hevc" : "avc", width: video.width || 1, height: video.height || 1 } : void 0,
|
|
32250
|
+
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,
|
|
32018
32251
|
fastStart: "in-memory",
|
|
32019
32252
|
firstTimestampBehavior: "cross-track-offset"
|
|
32020
32253
|
});
|
|
32021
|
-
if (this.transcodeAlaw)
|
|
32022
|
-
this.createAlawEncoder();
|
|
32023
32254
|
for (const packet of this.pendingAudio.splice(0))
|
|
32024
32255
|
this.addAudio(packet);
|
|
32256
|
+
for (const packet of this.pendingPcmAudio.splice(0))
|
|
32257
|
+
this.addPcmAudio(packet);
|
|
32025
32258
|
}
|
|
32026
|
-
|
|
32027
|
-
|
|
32028
|
-
|
|
32029
|
-
|
|
32030
|
-
|
|
32031
|
-
|
|
32032
|
-
|
|
32033
|
-
|
|
32034
|
-
|
|
32035
|
-
|
|
32036
|
-
|
|
32037
|
-
|
|
32038
|
-
|
|
32039
|
-
|
|
32040
|
-
|
|
32041
|
-
|
|
32042
|
-
|
|
32043
|
-
|
|
32044
|
-
|
|
32259
|
+
createAudioEncoder() {
|
|
32260
|
+
return __async(this, null, function* () {
|
|
32261
|
+
var _a, _b;
|
|
32262
|
+
const audio = this.player.getAudioInfo();
|
|
32263
|
+
const Encoder3 = globalThis.AudioEncoder;
|
|
32264
|
+
if (!Encoder3 || !globalThis.AudioData)
|
|
32265
|
+
return false;
|
|
32266
|
+
const config = {
|
|
32267
|
+
codec: "mp4a.40.2",
|
|
32268
|
+
sampleRate: AAC_SAMPLE_RATE,
|
|
32269
|
+
numberOfChannels: audio.channels || 1,
|
|
32270
|
+
// Chromium's AAC WebCodecs implementation currently accepts 96 kbps+
|
|
32271
|
+
// only (even for 8 kHz G.711 input).
|
|
32272
|
+
bitrate: 96e3
|
|
32273
|
+
};
|
|
32274
|
+
try {
|
|
32275
|
+
if (!(yield Encoder3.isConfigSupported(config)).supported)
|
|
32276
|
+
return false;
|
|
32277
|
+
this.audioEncoder = new Encoder3({
|
|
32278
|
+
output: (chunk, meta) => {
|
|
32279
|
+
var _a2;
|
|
32280
|
+
return (_a2 = this.muxer) == null ? void 0 : _a2.addAudioChunk(chunk, meta);
|
|
32281
|
+
},
|
|
32282
|
+
error: (error) => console.error("BVRTC AAC encoder error", error)
|
|
32283
|
+
});
|
|
32284
|
+
this.audioEncoder.configure(config);
|
|
32285
|
+
return true;
|
|
32286
|
+
} catch (e) {
|
|
32287
|
+
if (((_a = this.audioEncoder) == null ? void 0 : _a.state) !== "closed")
|
|
32288
|
+
(_b = this.audioEncoder) == null ? void 0 : _b.close();
|
|
32289
|
+
this.audioEncoder = void 0;
|
|
32290
|
+
return false;
|
|
32291
|
+
}
|
|
32045
32292
|
});
|
|
32046
32293
|
}
|
|
32047
32294
|
normalizePts(pts) {
|
|
32048
32295
|
var _a;
|
|
32296
|
+
pts = pts >= 1e11 ? pts / 1e3 : pts;
|
|
32049
32297
|
(_a = this.firstPts) != null ? _a : this.firstPts = pts;
|
|
32050
|
-
return Math.max(0, (pts - this.firstPts) *
|
|
32298
|
+
return Math.max(0, Math.round((pts - this.firstPts) * 1e6 + this.videoClockOffset));
|
|
32051
32299
|
}
|
|
32052
32300
|
addAudio(packet) {
|
|
32053
|
-
var _a
|
|
32301
|
+
var _a;
|
|
32054
32302
|
if (!this.muxer)
|
|
32055
32303
|
return;
|
|
32056
32304
|
if (this.transcodeAlaw) {
|
|
@@ -32059,37 +32307,69 @@ var BvrtcRecorder = class {
|
|
|
32059
32307
|
const decoded = new Int16Array(encoded.byteLength);
|
|
32060
32308
|
for (let i2 = 0; i2 < encoded.byteLength; i2++)
|
|
32061
32309
|
decoded[i2] = alawToS16(encoded[i2]);
|
|
32062
|
-
|
|
32063
|
-
const pcm = resampleS16(decoded, channels, audio.sampleRate || 8e3);
|
|
32064
|
-
const AudioDataCtor = globalThis.AudioData;
|
|
32065
|
-
const frame = new AudioDataCtor({ format: "s16", sampleRate: AAC_SAMPLE_RATE, numberOfFrames: pcm.length / channels, numberOfChannels: channels, timestamp: this.normalizePts(packet.pts), data: pcm });
|
|
32066
|
-
(_a = this.audioEncoder) == null ? void 0 : _a.encode(frame);
|
|
32067
|
-
frame.close();
|
|
32310
|
+
this.encodePcm(decoded, audio.channels || 1, audio.sampleRate || 8e3, packet.pts);
|
|
32068
32311
|
return;
|
|
32069
32312
|
}
|
|
32070
|
-
if (((
|
|
32313
|
+
if (((_a = this.player.getAudioInfo().codec) == null ? void 0 : _a.toLowerCase()) !== "aac")
|
|
32071
32314
|
return;
|
|
32072
|
-
const
|
|
32073
|
-
const
|
|
32315
|
+
const duration = 1024 * 1e6 / (this.player.getAudioInfo().sampleRate || 8e3);
|
|
32316
|
+
const sourcePts = this.normalizePts(packet.pts);
|
|
32317
|
+
const pts = this.lastAudioPts === void 0 ? sourcePts : Math.max(sourcePts, this.lastAudioPts + duration);
|
|
32074
32318
|
this.lastAudioPts = pts;
|
|
32075
32319
|
this.muxer.addAudioChunkRaw(stripAdts(new Uint8Array(packet.data)), "key", pts, duration);
|
|
32076
32320
|
}
|
|
32321
|
+
encodePcm(decoded, channels, sampleRate, sourcePts, normalizedPts = this.normalizePts(sourcePts)) {
|
|
32322
|
+
var _a;
|
|
32323
|
+
(_a = this.pcmResampler) != null ? _a : this.pcmResampler = new PcmResampler(sampleRate, AAC_SAMPLE_RATE, channels);
|
|
32324
|
+
this.pcmChannels = channels;
|
|
32325
|
+
const pendingSeconds = this.pcmResampler.pendingSeconds;
|
|
32326
|
+
const pcm = this.pcmResampler.process(decoded);
|
|
32327
|
+
this.writePcm(pcm, channels, normalizedPts - pendingSeconds * 1e6);
|
|
32328
|
+
}
|
|
32329
|
+
writePcm(pcm, channels, normalizedPts) {
|
|
32330
|
+
var _a;
|
|
32331
|
+
const numberOfFrames = pcm.length / channels;
|
|
32332
|
+
if (!numberOfFrames)
|
|
32333
|
+
return;
|
|
32334
|
+
const timestamp = Math.max(normalizedPts, (_a = this.nextPcmPts) != null ? _a : 0);
|
|
32335
|
+
this.nextPcmPts = timestamp + numberOfFrames * 1e6 / AAC_SAMPLE_RATE;
|
|
32336
|
+
const AudioDataCtor = globalThis.AudioData;
|
|
32337
|
+
const frame = new AudioDataCtor({ format: "s16", sampleRate: AAC_SAMPLE_RATE, numberOfFrames, numberOfChannels: channels, timestamp: Math.round(timestamp), data: pcm });
|
|
32338
|
+
try {
|
|
32339
|
+
this.audioEncoder.encode(frame);
|
|
32340
|
+
} finally {
|
|
32341
|
+
frame.close();
|
|
32342
|
+
}
|
|
32343
|
+
}
|
|
32344
|
+
addPcmAudio(packet) {
|
|
32345
|
+
if (packet.format !== "s16")
|
|
32346
|
+
throw new Error(`Unsupported BVRTC decoded PCM format: ${packet.format}`);
|
|
32347
|
+
const timestamp = (packet.pts - this.pcmClockStart) * 1e6 + this.pcmClockOffset;
|
|
32348
|
+
this.encodePcm(new Int16Array(packet.data), packet.channels, packet.sampleRate, packet.pts, timestamp);
|
|
32349
|
+
}
|
|
32077
32350
|
stop(onRecordInfo) {
|
|
32078
32351
|
return __async(this, null, function* () {
|
|
32079
|
-
var _a, _b, _c;
|
|
32352
|
+
var _a, _b, _c, _d, _e;
|
|
32080
32353
|
if (this.stopped)
|
|
32081
32354
|
return;
|
|
32082
32355
|
this.stopped = true;
|
|
32083
32356
|
const disableCallbacks = Promise.allSettled([
|
|
32084
32357
|
this.player.setVideoCallback(null),
|
|
32085
|
-
this.player.setAudioCallback(null)
|
|
32358
|
+
this.transcodePcm ? this.player.setDecodedAudioCallback(null) : this.player.setAudioCallback(null)
|
|
32086
32359
|
]);
|
|
32087
32360
|
const endTime = Date.now();
|
|
32088
|
-
|
|
32361
|
+
this.pendingAudio = [];
|
|
32362
|
+
this.pendingPcmAudio = [];
|
|
32363
|
+
if (!this.muxer || !this.target) {
|
|
32364
|
+
(_a = this.audioEncoder) == null ? void 0 : _a.close();
|
|
32365
|
+
yield disableCallbacks;
|
|
32089
32366
|
return;
|
|
32090
|
-
|
|
32091
|
-
(
|
|
32092
|
-
|
|
32367
|
+
}
|
|
32368
|
+
if (this.pcmResampler)
|
|
32369
|
+
this.writePcm(this.pcmResampler.flush(), this.pcmChannels, (_b = this.nextPcmPts) != null ? _b : 0);
|
|
32370
|
+
yield (_c = this.audioEncoder) == null ? void 0 : _c.flush();
|
|
32371
|
+
(_d = this.audioEncoder) == null ? void 0 : _d.close();
|
|
32372
|
+
(_e = this.muxer) == null ? void 0 : _e.finalize();
|
|
32093
32373
|
const blob = new Blob([this.target.buffer], { type: "video/mp4" });
|
|
32094
32374
|
const suffix = "mp4";
|
|
32095
32375
|
const download2 = (name) => file_default.downLoadBlob(blob, name || this.fileName, suffix);
|
|
@@ -33189,7 +33469,8 @@ function CreateCallConnectionManager(params) {
|
|
|
33189
33469
|
processHandle.reg(() => __async(this, null, function* () {
|
|
33190
33470
|
yield callApis.avCall(
|
|
33191
33471
|
__spreadProps(__spreadValues({}, callInfo), {
|
|
33192
|
-
|
|
33472
|
+
// 接通前尚未返回远端 ID,取消呼叫时保留原始设备目标。
|
|
33473
|
+
id: remoteUserId || callInfo.id,
|
|
33193
33474
|
status: 6 /* END */
|
|
33194
33475
|
}),
|
|
33195
33476
|
token
|
|
@@ -4,6 +4,13 @@ export interface IBvrtcPacket {
|
|
|
4
4
|
flags: number;
|
|
5
5
|
data: ArrayBuffer;
|
|
6
6
|
}
|
|
7
|
+
export interface IBvrtcPcmPacket {
|
|
8
|
+
pts: number;
|
|
9
|
+
data: ArrayBuffer;
|
|
10
|
+
channels: number;
|
|
11
|
+
sampleRate: number;
|
|
12
|
+
format: string;
|
|
13
|
+
}
|
|
7
14
|
interface IBvrtcPlayer {
|
|
8
15
|
getVideoInfo(): {
|
|
9
16
|
codec?: string;
|
|
@@ -17,6 +24,7 @@ interface IBvrtcPlayer {
|
|
|
17
24
|
};
|
|
18
25
|
setVideoCallback(callback: ((packet: IBvrtcPacket) => void) | null): Promise<void>;
|
|
19
26
|
setAudioCallback(callback: ((packet: IBvrtcPacket) => void) | null): Promise<void>;
|
|
27
|
+
setDecodedAudioCallback?(callback: ((packet: IBvrtcPcmPacket) => void) | null): Promise<void>;
|
|
20
28
|
}
|
|
21
29
|
export declare class BvrtcRecorder {
|
|
22
30
|
private player;
|
|
@@ -29,18 +37,34 @@ export declare class BvrtcRecorder {
|
|
|
29
37
|
private lastAudioPts?;
|
|
30
38
|
private sps?;
|
|
31
39
|
private pps?;
|
|
40
|
+
private vps?;
|
|
41
|
+
private hevc;
|
|
42
|
+
private videoStarted;
|
|
32
43
|
private pendingAudio;
|
|
33
44
|
private transcodeAlaw;
|
|
45
|
+
private transcodePcm;
|
|
46
|
+
private pendingPcmAudio;
|
|
47
|
+
private nextPcmPts?;
|
|
48
|
+
private pcmResampler?;
|
|
49
|
+
private pcmChannels;
|
|
50
|
+
private recordingClockStart;
|
|
51
|
+
private videoClockOffset;
|
|
52
|
+
private pcmClockStart?;
|
|
53
|
+
private pcmClockOffset;
|
|
34
54
|
private audioEncoder?;
|
|
35
55
|
private stopped;
|
|
36
56
|
constructor(player: IBvrtcPlayer, fileName: string);
|
|
37
57
|
start(): Promise<void>;
|
|
38
58
|
private createMuxer;
|
|
39
|
-
private
|
|
59
|
+
private createAudioEncoder;
|
|
40
60
|
private normalizePts;
|
|
41
61
|
private onVideo;
|
|
42
62
|
private addAudio;
|
|
43
63
|
private onAudio;
|
|
64
|
+
private encodePcm;
|
|
65
|
+
private writePcm;
|
|
66
|
+
private addPcmAudio;
|
|
67
|
+
private onPcmAudio;
|
|
44
68
|
stop(onRecordInfo?: IOnMediaRecordHandle): Promise<void>;
|
|
45
69
|
}
|
|
46
70
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hevcDecoderConfig(vps: Uint8Array, sps: Uint8Array, pps: Uint8Array): Uint8Array;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class PcmResampler {
|
|
2
|
+
private inputRate;
|
|
3
|
+
private outputRate;
|
|
4
|
+
private channels;
|
|
5
|
+
private readonly radius;
|
|
6
|
+
private buffer;
|
|
7
|
+
private bufferStart;
|
|
8
|
+
private inputFrames;
|
|
9
|
+
private outputFrames;
|
|
10
|
+
private readonly phases;
|
|
11
|
+
constructor(inputRate: number, outputRate: number, channels: number);
|
|
12
|
+
get pendingSeconds(): number;
|
|
13
|
+
process(input: Int16Array): Int16Array;
|
|
14
|
+
flush(): Int16Array;
|
|
15
|
+
private render;
|
|
16
|
+
}
|