@besovideo/webrtc-player 0.8.94 → 0.8.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,6 +7,7 @@
7
7
  ## 简介 Introduction
8
8
 
9
9
  > 设备视频播放、对讲封装
10
+ [Webrtc 与 平台接口关系](README.webrtc.md)
10
11
 
11
12
  ## 安装 Installation
12
13
 
@@ -170,9 +171,49 @@ instance.playerStatus : {
170
171
  // 设置录像状态
171
172
  Set(status: {type: "recordStart" | "recordStop"}): void;
172
173
  // 添加事件监听, type == "recordStop" recordInfo 才有值
173
- addEventListener(handle: ((status: {type: "recordStart" | "recordStop", recordInfo?: { data: Blob, cancelAutoDownload: () => void} }) => void)):void;
174
+ addEventListener(handle: ((status: {
175
+ type: "recordStart" | "recordStop",
176
+ recordInfo?: {
177
+ data: Blob; // 数据
178
+ startTime: number; // 录像开始时间 utc ms
179
+ endTime: number; // 录像结束时间 utc ms
180
+ duration: number; // 录像时长 ms
181
+ fileName: string; // 文件名
182
+ puOptions: {
183
+ id: string; // 设备名称
184
+ index: number; // 通道ID
185
+ name?: string; //设备名称,用于内部UI显示
186
+ indexName?: string; //通道名称,用于内部UI显示
187
+ },
188
+ //调用将取消自动下载,返回下载函数
189
+ cancelAutoDownload: () => {
190
+ //调用该接口,浏览器下载文件, filename 不填为默认的文件名,否则为[filename].webm
191
+ download(filename?: string): void;
192
+ }
193
+ }
194
+ }) => void)):void;
174
195
  // 移除事件监听
175
- removeEventListener(handle: ((status: {type: "recordStart" | "recordStop", recordInfo?: {data: Blob, cancelAutoDownload: () => void} }) => void)):void;
196
+ removeEventListener(handle: ((status: {
197
+ type: "recordStart" | "recordStop",
198
+ recordInfo?: {
199
+ data: Blob; // 数据
200
+ startTime: number; // 录像开始时间 utc ms
201
+ endTime: number; // 录像结束时间 utc ms
202
+ duration: number; // 录像时长 ms
203
+ fileName: string; // 文件名
204
+ puOptions: {
205
+ id: string; // 设备名称
206
+ index: number; // 通道ID
207
+ name?: string; //设备名称,用于内部UI显示
208
+ indexName?: string; //通道名称,用于内部UI显示
209
+ },
210
+ //调用将取消自动下载,返回下载函数
211
+ cancelAutoDownload: () => {
212
+ //调用该接口,浏览器下载文件, filename 不填为默认的文件名,否则为[filename].webm
213
+ download(filename?: string): void;
214
+ }
215
+ }
216
+ }) => void)):void;
176
217
  },
177
218
  rotate: {
178
219
  // 获取旋转状态,顺时针方向
@@ -0,0 +1,21 @@
1
+ 这个包封装了[平台接口](https://bvcsp.apifox.cn/)流媒体相关的操作。
2
+ 包含 实时视频 对讲 会议的流媒体 操作
3
+
4
+ 流媒体基于webrtc 和 bvrtc两种
5
+
6
+ ### 先介绍Webrtc,介绍之后说下webrtc和bvrtc的区别:
7
+
8
+ [简介](https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API/Session_lifetime)
9
+
10
+ [Webrtc的信令事务流程](https://developer.mozilla.org/zh-CN/docs/Web/API/WebRTC_API/Signaling_and_video_calling#%E4%BF%A1%E4%BB%A4%E4%BA%8B%E5%8A%A1%E6%B5%81%E7%A8%8B)
11
+
12
+
13
+ [平台接口打开设备音视频流webrtc](https://bvcsp.apifox.cn/api-171249025)接口用作信令交互,就是上图中交互[SDP](https://developer.mozilla.org/zh-CN/docs/Glossary/SDP)。
14
+
15
+ 通过webrtc打开设备实时视频 本包封装的过程就是,
16
+ 1、创建 const rtcPC = RTCPeerConnection
17
+ 2、创建Offer const {type, sdp} = await [rtcPC.createOffer()](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer)
18
+ 3、设置本地SDP await [rtcPC.setLocalDescription](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setLocalDescription)({type, sdp})
19
+ 4、通过 [平台接口打开设备音视频流webrtc](https://bvcsp.apifox.cn/api-171249025) 发送SDP到服务器,收到远程SDP, 保存 const remoteSDP = await post('/bvcsp/v1/dialog/device/webrtc', {...});
20
+ 5、设置rtcPC的远程SDP [rtcPC.setRemoteDescription()](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setRemoteDescription)({type: 'anwser', sdp: remoteSDP})
21
+
@@ -117,7 +117,7 @@ var bvPlayerCore = (() => {
117
117
  var define_processenv_default;
118
118
  var init_define_processenv = __esm({
119
119
  "<define:processenv>"() {
120
- define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.8.94", PROJECT_NAMESPACE: "bvplayer" };
120
+ define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.8.96", PROJECT_NAMESPACE: "bvplayer" };
121
121
  }
122
122
  });
123
123
 
@@ -7125,6 +7125,7 @@ If you want to allow non-zero first timestamps, set firstTimestampBehavior: 'per
7125
7125
  description: audioCodecPrivate
7126
7126
  }
7127
7127
  } : void 0;
7128
+ let maxTimestamp = -1;
7128
7129
  handleEle((timestamp, data, bVideo, bKeyFrame) => {
7129
7130
  if (bVideo) {
7130
7131
  muxer.addVideoChunkRaw(data, bKeyFrame ? "key" : "delta", timestamp);
@@ -7133,9 +7134,15 @@ If you want to allow non-zero first timestamps, set firstTimestampBehavior: 'per
7133
7134
  muxer.addAudioChunkRaw(data, "key", timestamp, config);
7134
7135
  }
7135
7136
  }
7137
+ if (maxTimestamp < timestamp) {
7138
+ maxTimestamp = timestamp;
7139
+ }
7136
7140
  });
7137
7141
  muxer.finalize();
7138
- return new Blob([muxer.target.buffer]);
7142
+ return {
7143
+ blob: new Blob([muxer.target.buffer]),
7144
+ maxTimestamp
7145
+ };
7139
7146
  });
7140
7147
  }
7141
7148
  var createMediaRecorder = (mediaStream, done, mediaRecorderPartner) => {
@@ -7173,6 +7180,7 @@ If you want to allow non-zero first timestamps, set firstTimestampBehavior: 'per
7173
7180
  return mediaRecorder;
7174
7181
  };
7175
7182
  var createRecord = (_mediaStream, fileName, isDownload) => {
7183
+ const startTime = Date.now();
7176
7184
  const tempMediaStreams = _mediaStream.getTracks().map((item) => {
7177
7185
  const mediaStream2 = new MediaStream([item]);
7178
7186
  item.addEventListener("ended", () => {
@@ -7189,14 +7197,30 @@ If you want to allow non-zero first timestamps, set firstTimestampBehavior: 'per
7189
7197
  const szWebM = [];
7190
7198
  function done(e) {
7191
7199
  return __async(this, null, function* () {
7200
+ let maxTimestamp = -1;
7192
7201
  szWebM.push(e.data);
7193
7202
  if (szWebM.length == mediaStreams.length) {
7194
- recordedChunks.push(yield combinaWebM(...szWebM));
7203
+ const { blob: blobWebM, maxTimestamp: _maxTimestamp } = yield combinaWebM(
7204
+ ...szWebM
7205
+ );
7206
+ recordedChunks.push(blobWebM);
7207
+ if (_maxTimestamp > maxTimestamp) {
7208
+ maxTimestamp = _maxTimestamp;
7209
+ }
7195
7210
  const blob = new Blob(recordedChunks, {
7196
7211
  type: "video/webm"
7197
7212
  });
7198
- if (yield isDownload(blob)) {
7199
- file_default.downLoadBlob(blob, fileName, "webm");
7213
+ const downloadFile = (inFileName) => {
7214
+ file_default.downLoadBlob(blob, inFileName ? inFileName : fileName, "webm");
7215
+ };
7216
+ if (isDownload(blob, { duration: maxTimestamp / 1e3, startTime, endTime: startTime + maxTimestamp / 1e3, fileName }, () => {
7217
+ return {
7218
+ download(file2) {
7219
+ downloadFile(file2);
7220
+ }
7221
+ };
7222
+ })) {
7223
+ downloadFile(fileName);
7200
7224
  }
7201
7225
  recordedChunks = [];
7202
7226
  }
@@ -7250,19 +7274,28 @@ If you want to allow non-zero first timestamps, set firstTimestampBehavior: 'per
7250
7274
  try {
7251
7275
  const mediaStream = stream || element.captureStream(25);
7252
7276
  let gOnMediaHolder = void 0;
7253
- const mediaRecorder = createRecord(mediaStream, fileName, (blob) => __async(void 0, null, function* () {
7254
- if (gOnMediaHolder) {
7255
- const holder = gOnMediaHolder;
7256
- gOnMediaHolder = void 0;
7257
- let bDownload = true;
7258
- const cancelAutoDownload = () => {
7259
- bDownload = false;
7260
- };
7261
- yield holder(blob, cancelAutoDownload);
7262
- return bDownload;
7277
+ const mediaRecorder = createRecord(
7278
+ mediaStream,
7279
+ fileName,
7280
+ (blob, metaInfo, downloadRecordGetter) => {
7281
+ if (gOnMediaHolder) {
7282
+ const holder = gOnMediaHolder;
7283
+ gOnMediaHolder = void 0;
7284
+ let bDownload = true;
7285
+ const cancelAutoDownload = () => {
7286
+ bDownload = false;
7287
+ return downloadRecordGetter();
7288
+ };
7289
+ holder(
7290
+ blob,
7291
+ metaInfo,
7292
+ cancelAutoDownload
7293
+ );
7294
+ return bDownload;
7295
+ }
7296
+ return true;
7263
7297
  }
7264
- return true;
7265
- }));
7298
+ );
7266
7299
  logger_default.info("\u5F55\u5236\u5B9E\u4F8B", mediaRecorder);
7267
7300
  return [
7268
7301
  mediaRecorder,
@@ -12594,8 +12627,9 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
12594
12627
  this.vSpeed = 50;
12595
12628
  this.iPresetPoint = 1;
12596
12629
  this._buttons = [];
12630
+ this._toastInject = {};
12597
12631
  this._speedSlider = new speedSlider_default(this.hSpeed);
12598
- this._toast = params.toast;
12632
+ this._toastInject.toast = params.toast;
12599
12633
  }
12600
12634
  // 由外界设置设备参数 也可直接通过public字段单独设置
12601
12635
  setPuParam(puOption, token) {
@@ -12607,6 +12641,7 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
12607
12641
  if (!operate)
12608
12642
  return;
12609
12643
  const sendOperate = (stop) => __async(this, null, function* () {
12644
+ var _a;
12610
12645
  logger_default.debug("sendOperate", operate, this.vSpeed, this.hSpeed, stop);
12611
12646
  if (!this.puOption || !this.token)
12612
12647
  return;
@@ -12624,7 +12659,7 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
12624
12659
  );
12625
12660
  } catch (e) {
12626
12661
  const errCamera = e;
12627
- this._toast.error(errCamera.getErrorCodeDesc());
12662
+ (_a = this._toastInject.toast) == null ? void 0 : _a.error(errCamera.getErrorCodeDesc());
12628
12663
  }
12629
12664
  });
12630
12665
  button.addEventListener("start", () => sendOperate(false));
@@ -12912,6 +12947,7 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
12912
12947
  this._video = new video_default({
12913
12948
  puOptions: options == null ? void 0 : options.puOptions
12914
12949
  });
12950
+ this._puOptions = options == null ? void 0 : options.puOptions;
12915
12951
  this._spinner = new spinner_default({ spin: false });
12916
12952
  this._toast = new toast_default();
12917
12953
  this._mask = new mask_default({ visible: false });
@@ -13187,12 +13223,15 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
13187
13223
  if ((_a = this._panel) == null ? void 0 : _a.controller.RecordButton) {
13188
13224
  (_b = this._panel) == null ? void 0 : _b.controller.RecordButton.dispatch("click");
13189
13225
  } else {
13190
- this._video.recordStop((blob, cancelAutoDownload) => {
13191
- this.dispatch("recordStop", {
13226
+ this._video.recordStop((blob, mataInfo, cancelAutoDownload) => {
13227
+ this.dispatch("recordStop", __spreadProps(__spreadValues({
13228
+ puOptions: this._puOptions
13229
+ }, mataInfo), {
13192
13230
  data: blob,
13193
13231
  cancelAutoDownload
13194
- });
13232
+ }));
13195
13233
  this._recordState.dispatch("RecordEnd");
13234
+ return false;
13196
13235
  });
13197
13236
  }
13198
13237
  }
@@ -13258,12 +13297,15 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
13258
13297
  this._recordState.dispatch("RecordStart");
13259
13298
  });
13260
13299
  panel.controller.addEventListener("recordStop", () => {
13261
- this._video.recordStop((blob, cancelAutoDownload) => {
13262
- this.dispatch("recordStop", {
13300
+ this._video.recordStop((blob, metaInfo, cancelAutoDownload) => {
13301
+ this.dispatch("recordStop", __spreadProps(__spreadValues({
13302
+ puOptions: this._puOptions
13303
+ }, metaInfo), {
13263
13304
  data: blob,
13264
13305
  cancelAutoDownload
13265
- });
13306
+ }));
13266
13307
  this._recordState.dispatch("RecordEnd");
13308
+ return false;
13267
13309
  });
13268
13310
  });
13269
13311
  panel.controller.addEventListener("rotate", () => {
@@ -13571,6 +13613,7 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
13571
13613
  // smooth: 播放平滑度。[1,7],值越小越实时。
13572
13614
  // algokey: 加解密算法:密钥。格式:算法:base64(密钥)。例如: SM4:MTIzNDU2Nzg=
13573
13615
  constructor() {
13616
+ console.log("================== bvrtc version: 1.0.1 20241028 ==================");
13574
13617
  this.hplayer = 0;
13575
13618
  this.canvas = null;
13576
13619
  this.dialogid = "";
@@ -14499,6 +14542,20 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
14499
14542
  this.audioCtx.close();
14500
14543
  this.audioCtx = null;
14501
14544
  };
14545
+ var woker_str = `
14546
+ class BVAudioProcessor extends AudioWorkletProcessor {
14547
+ process(inputs, outputs, parameters) {
14548
+ const inputdata = inputs[0][0];
14549
+ this.port.postMessage(inputdata);
14550
+ return true;
14551
+ }
14552
+ }
14553
+ registerProcessor('bv-audio-processor', BVAudioProcessor);
14554
+ `;
14555
+ var audioProcessorBolb = new Blob([woker_str], {
14556
+ type: "application/javascript"
14557
+ });
14558
+ var audioProcessorUrl = URL.createObjectURL(audioProcessorBolb);
14502
14559
  function BVPcmRecorder(config, cb) {
14503
14560
  if (!config) {
14504
14561
  config = {
@@ -14510,7 +14567,7 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
14510
14567
  };
14511
14568
  }
14512
14569
  this.config = config;
14513
- this.sampleRate = new (window.AudioContext || window.webkitAudioContext)().sampleRate;
14570
+ this.sampleRate = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: this.config.sampleRate }).sampleRate;
14514
14571
  this.fftSize = 512;
14515
14572
  this.analyser = null;
14516
14573
  this.audioInput = null;
@@ -14520,20 +14577,23 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
14520
14577
  this.audioData = null;
14521
14578
  this.audioSize = 0;
14522
14579
  this.cb = cb;
14523
- this.init();
14524
14580
  console.log("sampleRate " + this.sampleRate);
14525
14581
  }
14526
14582
  BVPcmRecorder.prototype.start = function() {
14527
- navigator.mediaDevices.getUserMedia({
14528
- audio: true
14529
- }).then((stream) => {
14530
- this.audioInput = this.context.createMediaStreamSource(stream);
14531
- }, (error2) => {
14532
- console.error(error2);
14533
- }).then(() => {
14534
- this.audioInput.connect(this.analyser);
14535
- this.analyser.connect(this.recorder);
14536
- this.recorder.connect(this.context.destination);
14583
+ return __async(this, null, function* () {
14584
+ yield this.init();
14585
+ navigator.mediaDevices.getUserMedia({
14586
+ audio: true,
14587
+ sampleRate: this.config.sampleRate
14588
+ }).then((stream) => {
14589
+ this.audioInput = this.context.createMediaStreamSource(stream);
14590
+ }, (error2) => {
14591
+ console.error(error2);
14592
+ }).then(() => {
14593
+ this.audioInput.connect(this.analyser);
14594
+ this.analyser.connect(this.recorder);
14595
+ this.recorder.connect(this.context.destination);
14596
+ });
14537
14597
  });
14538
14598
  };
14539
14599
  BVPcmRecorder.prototype.stop = function() {
@@ -14541,23 +14601,21 @@ ${event.candidate ? event.candidate.candidate : "(null)"}`
14541
14601
  this.audioInput.disconnect();
14542
14602
  if (this.recorder)
14543
14603
  this.recorder.disconnect();
14604
+ if (this.context)
14605
+ this.context.close();
14544
14606
  };
14545
14607
  BVPcmRecorder.prototype.init = function() {
14546
- this.context = new (window.AudioContext || window.webkitAudioContext)();
14547
- this.analyser = this.context.createAnalyser();
14548
- this.analyser.fftSize = this.fftSize;
14549
- const createScript = this.context.createScriptProcessor || this.context.createJavaScriptNode;
14550
- this.recorder = createScript.apply(
14551
- this.context,
14552
- [this.fftSize, this.config.numberChannels, this.config.numberChannels]
14553
- );
14554
- this.recorder.onaudioprocess = this.onaudioprocess.bind(this);
14555
- };
14556
- BVPcmRecorder.prototype.onaudioprocess = function(e) {
14557
- if (this.config.numberChannels === 1) {
14558
- const data = e.inputBuffer.getChannelData(0);
14559
- this.handlePcm(new Float32Array(data));
14560
- }
14608
+ return __async(this, null, function* () {
14609
+ this.context = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: this.config.sampleRate });
14610
+ this.analyser = this.context.createAnalyser();
14611
+ this.analyser.fftSize = this.fftSize;
14612
+ yield this.context.audioWorklet.addModule(audioProcessorUrl);
14613
+ this.recorder = new AudioWorkletNode(this.context, "bv-audio-processor");
14614
+ this.recorder.port.onmessage = (e) => {
14615
+ if (e.data)
14616
+ this.handlePcm(e.data);
14617
+ };
14618
+ });
14561
14619
  };
14562
14620
  BVPcmRecorder.prototype.handlePcm = function(bytes) {
14563
14621
  if (bytes[0] === 0 && bytes[1] === 0)
package/dist/main.es.js CHANGED
@@ -111,7 +111,7 @@ var __async = (__this, __arguments, generator) => {
111
111
  var define_processenv_default;
112
112
  var init_define_processenv = __esm({
113
113
  "<define:processenv>"() {
114
- define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.8.94", PROJECT_NAMESPACE: "bvplayer" };
114
+ define_processenv_default = { NODE_ENV: "production", SH_LIB_NAME: "@besovideo/webrtc-player", SH_LIB_VERSION: "0.8.96", PROJECT_NAMESPACE: "bvplayer" };
115
115
  }
116
116
  });
117
117
 
@@ -7109,6 +7109,7 @@ function combinaWebM(webM1, webM2) {
7109
7109
  description: audioCodecPrivate
7110
7110
  }
7111
7111
  } : void 0;
7112
+ let maxTimestamp = -1;
7112
7113
  handleEle((timestamp, data, bVideo, bKeyFrame) => {
7113
7114
  if (bVideo) {
7114
7115
  muxer.addVideoChunkRaw(data, bKeyFrame ? "key" : "delta", timestamp);
@@ -7117,9 +7118,15 @@ function combinaWebM(webM1, webM2) {
7117
7118
  muxer.addAudioChunkRaw(data, "key", timestamp, config);
7118
7119
  }
7119
7120
  }
7121
+ if (maxTimestamp < timestamp) {
7122
+ maxTimestamp = timestamp;
7123
+ }
7120
7124
  });
7121
7125
  muxer.finalize();
7122
- return new Blob([muxer.target.buffer]);
7126
+ return {
7127
+ blob: new Blob([muxer.target.buffer]),
7128
+ maxTimestamp
7129
+ };
7123
7130
  });
7124
7131
  }
7125
7132
  var createMediaRecorder = (mediaStream, done, mediaRecorderPartner) => {
@@ -7157,6 +7164,7 @@ var createMediaRecorder = (mediaStream, done, mediaRecorderPartner) => {
7157
7164
  return mediaRecorder;
7158
7165
  };
7159
7166
  var createRecord = (_mediaStream, fileName, isDownload) => {
7167
+ const startTime = Date.now();
7160
7168
  const tempMediaStreams = _mediaStream.getTracks().map((item) => {
7161
7169
  const mediaStream2 = new MediaStream([item]);
7162
7170
  item.addEventListener("ended", () => {
@@ -7173,14 +7181,30 @@ var createRecord = (_mediaStream, fileName, isDownload) => {
7173
7181
  const szWebM = [];
7174
7182
  function done(e) {
7175
7183
  return __async(this, null, function* () {
7184
+ let maxTimestamp = -1;
7176
7185
  szWebM.push(e.data);
7177
7186
  if (szWebM.length == mediaStreams.length) {
7178
- recordedChunks.push(yield combinaWebM(...szWebM));
7187
+ const { blob: blobWebM, maxTimestamp: _maxTimestamp } = yield combinaWebM(
7188
+ ...szWebM
7189
+ );
7190
+ recordedChunks.push(blobWebM);
7191
+ if (_maxTimestamp > maxTimestamp) {
7192
+ maxTimestamp = _maxTimestamp;
7193
+ }
7179
7194
  const blob = new Blob(recordedChunks, {
7180
7195
  type: "video/webm"
7181
7196
  });
7182
- if (yield isDownload(blob)) {
7183
- file_default.downLoadBlob(blob, fileName, "webm");
7197
+ const downloadFile = (inFileName) => {
7198
+ file_default.downLoadBlob(blob, inFileName ? inFileName : fileName, "webm");
7199
+ };
7200
+ if (isDownload(blob, { duration: maxTimestamp / 1e3, startTime, endTime: startTime + maxTimestamp / 1e3, fileName }, () => {
7201
+ return {
7202
+ download(file2) {
7203
+ downloadFile(file2);
7204
+ }
7205
+ };
7206
+ })) {
7207
+ downloadFile(fileName);
7184
7208
  }
7185
7209
  recordedChunks = [];
7186
7210
  }
@@ -7234,19 +7258,28 @@ var record = (element, stream, fileName = `record_${Date.now()}`) => {
7234
7258
  try {
7235
7259
  const mediaStream = stream || element.captureStream(25);
7236
7260
  let gOnMediaHolder = void 0;
7237
- const mediaRecorder = createRecord(mediaStream, fileName, (blob) => __async(void 0, null, function* () {
7238
- if (gOnMediaHolder) {
7239
- const holder = gOnMediaHolder;
7240
- gOnMediaHolder = void 0;
7241
- let bDownload = true;
7242
- const cancelAutoDownload = () => {
7243
- bDownload = false;
7244
- };
7245
- yield holder(blob, cancelAutoDownload);
7246
- return bDownload;
7261
+ const mediaRecorder = createRecord(
7262
+ mediaStream,
7263
+ fileName,
7264
+ (blob, metaInfo, downloadRecordGetter) => {
7265
+ if (gOnMediaHolder) {
7266
+ const holder = gOnMediaHolder;
7267
+ gOnMediaHolder = void 0;
7268
+ let bDownload = true;
7269
+ const cancelAutoDownload = () => {
7270
+ bDownload = false;
7271
+ return downloadRecordGetter();
7272
+ };
7273
+ holder(
7274
+ blob,
7275
+ metaInfo,
7276
+ cancelAutoDownload
7277
+ );
7278
+ return bDownload;
7279
+ }
7280
+ return true;
7247
7281
  }
7248
- return true;
7249
- }));
7282
+ );
7250
7283
  logger_default.info("\u5F55\u5236\u5B9E\u4F8B", mediaRecorder);
7251
7284
  return [
7252
7285
  mediaRecorder,
@@ -12578,8 +12611,9 @@ var PtzPluginBase = class extends Plugin {
12578
12611
  this.vSpeed = 50;
12579
12612
  this.iPresetPoint = 1;
12580
12613
  this._buttons = [];
12614
+ this._toastInject = {};
12581
12615
  this._speedSlider = new speedSlider_default(this.hSpeed);
12582
- this._toast = params.toast;
12616
+ this._toastInject.toast = params.toast;
12583
12617
  }
12584
12618
  // 由外界设置设备参数 也可直接通过public字段单独设置
12585
12619
  setPuParam(puOption, token) {
@@ -12591,6 +12625,7 @@ var PtzPluginBase = class extends Plugin {
12591
12625
  if (!operate)
12592
12626
  return;
12593
12627
  const sendOperate = (stop) => __async(this, null, function* () {
12628
+ var _a;
12594
12629
  logger_default.debug("sendOperate", operate, this.vSpeed, this.hSpeed, stop);
12595
12630
  if (!this.puOption || !this.token)
12596
12631
  return;
@@ -12608,7 +12643,7 @@ var PtzPluginBase = class extends Plugin {
12608
12643
  );
12609
12644
  } catch (e) {
12610
12645
  const errCamera = e;
12611
- this._toast.error(errCamera.getErrorCodeDesc());
12646
+ (_a = this._toastInject.toast) == null ? void 0 : _a.error(errCamera.getErrorCodeDesc());
12612
12647
  }
12613
12648
  });
12614
12649
  button.addEventListener("start", () => sendOperate(false));
@@ -12896,6 +12931,7 @@ var PlayerPlugin = class extends Plugin {
12896
12931
  this._video = new video_default({
12897
12932
  puOptions: options == null ? void 0 : options.puOptions
12898
12933
  });
12934
+ this._puOptions = options == null ? void 0 : options.puOptions;
12899
12935
  this._spinner = new spinner_default({ spin: false });
12900
12936
  this._toast = new toast_default();
12901
12937
  this._mask = new mask_default({ visible: false });
@@ -13171,12 +13207,15 @@ var PlayerPlugin = class extends Plugin {
13171
13207
  if ((_a = this._panel) == null ? void 0 : _a.controller.RecordButton) {
13172
13208
  (_b = this._panel) == null ? void 0 : _b.controller.RecordButton.dispatch("click");
13173
13209
  } else {
13174
- this._video.recordStop((blob, cancelAutoDownload) => {
13175
- this.dispatch("recordStop", {
13210
+ this._video.recordStop((blob, mataInfo, cancelAutoDownload) => {
13211
+ this.dispatch("recordStop", __spreadProps(__spreadValues({
13212
+ puOptions: this._puOptions
13213
+ }, mataInfo), {
13176
13214
  data: blob,
13177
13215
  cancelAutoDownload
13178
- });
13216
+ }));
13179
13217
  this._recordState.dispatch("RecordEnd");
13218
+ return false;
13180
13219
  });
13181
13220
  }
13182
13221
  }
@@ -13242,12 +13281,15 @@ var PlayerPlugin = class extends Plugin {
13242
13281
  this._recordState.dispatch("RecordStart");
13243
13282
  });
13244
13283
  panel.controller.addEventListener("recordStop", () => {
13245
- this._video.recordStop((blob, cancelAutoDownload) => {
13246
- this.dispatch("recordStop", {
13284
+ this._video.recordStop((blob, metaInfo, cancelAutoDownload) => {
13285
+ this.dispatch("recordStop", __spreadProps(__spreadValues({
13286
+ puOptions: this._puOptions
13287
+ }, metaInfo), {
13247
13288
  data: blob,
13248
13289
  cancelAutoDownload
13249
- });
13290
+ }));
13250
13291
  this._recordState.dispatch("RecordEnd");
13292
+ return false;
13251
13293
  });
13252
13294
  });
13253
13295
  panel.controller.addEventListener("rotate", () => {
@@ -13555,6 +13597,7 @@ var BVMedia = class {
13555
13597
  // smooth: 播放平滑度。[1,7],值越小越实时。
13556
13598
  // algokey: 加解密算法:密钥。格式:算法:base64(密钥)。例如: SM4:MTIzNDU2Nzg=
13557
13599
  constructor() {
13600
+ console.log("================== bvrtc version: 1.0.1 20241028 ==================");
13558
13601
  this.hplayer = 0;
13559
13602
  this.canvas = null;
13560
13603
  this.dialogid = "";
@@ -14483,6 +14526,20 @@ BVPCMPlayer.prototype.destroy = function() {
14483
14526
  this.audioCtx.close();
14484
14527
  this.audioCtx = null;
14485
14528
  };
14529
+ var woker_str = `
14530
+ class BVAudioProcessor extends AudioWorkletProcessor {
14531
+ process(inputs, outputs, parameters) {
14532
+ const inputdata = inputs[0][0];
14533
+ this.port.postMessage(inputdata);
14534
+ return true;
14535
+ }
14536
+ }
14537
+ registerProcessor('bv-audio-processor', BVAudioProcessor);
14538
+ `;
14539
+ var audioProcessorBolb = new Blob([woker_str], {
14540
+ type: "application/javascript"
14541
+ });
14542
+ var audioProcessorUrl = URL.createObjectURL(audioProcessorBolb);
14486
14543
  function BVPcmRecorder(config, cb) {
14487
14544
  if (!config) {
14488
14545
  config = {
@@ -14494,7 +14551,7 @@ function BVPcmRecorder(config, cb) {
14494
14551
  };
14495
14552
  }
14496
14553
  this.config = config;
14497
- this.sampleRate = new (window.AudioContext || window.webkitAudioContext)().sampleRate;
14554
+ this.sampleRate = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: this.config.sampleRate }).sampleRate;
14498
14555
  this.fftSize = 512;
14499
14556
  this.analyser = null;
14500
14557
  this.audioInput = null;
@@ -14504,20 +14561,23 @@ function BVPcmRecorder(config, cb) {
14504
14561
  this.audioData = null;
14505
14562
  this.audioSize = 0;
14506
14563
  this.cb = cb;
14507
- this.init();
14508
14564
  console.log("sampleRate " + this.sampleRate);
14509
14565
  }
14510
14566
  BVPcmRecorder.prototype.start = function() {
14511
- navigator.mediaDevices.getUserMedia({
14512
- audio: true
14513
- }).then((stream) => {
14514
- this.audioInput = this.context.createMediaStreamSource(stream);
14515
- }, (error2) => {
14516
- console.error(error2);
14517
- }).then(() => {
14518
- this.audioInput.connect(this.analyser);
14519
- this.analyser.connect(this.recorder);
14520
- this.recorder.connect(this.context.destination);
14567
+ return __async(this, null, function* () {
14568
+ yield this.init();
14569
+ navigator.mediaDevices.getUserMedia({
14570
+ audio: true,
14571
+ sampleRate: this.config.sampleRate
14572
+ }).then((stream) => {
14573
+ this.audioInput = this.context.createMediaStreamSource(stream);
14574
+ }, (error2) => {
14575
+ console.error(error2);
14576
+ }).then(() => {
14577
+ this.audioInput.connect(this.analyser);
14578
+ this.analyser.connect(this.recorder);
14579
+ this.recorder.connect(this.context.destination);
14580
+ });
14521
14581
  });
14522
14582
  };
14523
14583
  BVPcmRecorder.prototype.stop = function() {
@@ -14525,23 +14585,21 @@ BVPcmRecorder.prototype.stop = function() {
14525
14585
  this.audioInput.disconnect();
14526
14586
  if (this.recorder)
14527
14587
  this.recorder.disconnect();
14588
+ if (this.context)
14589
+ this.context.close();
14528
14590
  };
14529
14591
  BVPcmRecorder.prototype.init = function() {
14530
- this.context = new (window.AudioContext || window.webkitAudioContext)();
14531
- this.analyser = this.context.createAnalyser();
14532
- this.analyser.fftSize = this.fftSize;
14533
- const createScript = this.context.createScriptProcessor || this.context.createJavaScriptNode;
14534
- this.recorder = createScript.apply(
14535
- this.context,
14536
- [this.fftSize, this.config.numberChannels, this.config.numberChannels]
14537
- );
14538
- this.recorder.onaudioprocess = this.onaudioprocess.bind(this);
14539
- };
14540
- BVPcmRecorder.prototype.onaudioprocess = function(e) {
14541
- if (this.config.numberChannels === 1) {
14542
- const data = e.inputBuffer.getChannelData(0);
14543
- this.handlePcm(new Float32Array(data));
14544
- }
14592
+ return __async(this, null, function* () {
14593
+ this.context = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: this.config.sampleRate });
14594
+ this.analyser = this.context.createAnalyser();
14595
+ this.analyser.fftSize = this.fftSize;
14596
+ yield this.context.audioWorklet.addModule(audioProcessorUrl);
14597
+ this.recorder = new AudioWorkletNode(this.context, "bv-audio-processor");
14598
+ this.recorder.port.onmessage = (e) => {
14599
+ if (e.data)
14600
+ this.handlePcm(e.data);
14601
+ };
14602
+ });
14545
14603
  };
14546
14604
  BVPcmRecorder.prototype.handlePcm = function(bytes) {
14547
14605
  if (bytes[0] === 0 && bytes[1] === 0)
@@ -1,5 +1,6 @@
1
1
  import PlayerPlugin from "../../plugins/player";
2
2
  import { IDataHandle } from "src/core/events";
3
+ import { IRecordStopInfo } from "src/plugins/player/event";
3
4
  type EnumRotate = "Rotate0" | "Rotate90" | "Rotate180" | "Rotate270";
4
5
  export type IPlayerState = ReturnType<typeof CreatePlayerState>;
5
6
  export declare function CreatePlayerState(player: PlayerPlugin): {
@@ -8,10 +9,7 @@ export declare function CreatePlayerState(player: PlayerPlugin): {
8
9
  information: IDataHandle<boolean>;
9
10
  record: IDataHandle<{
10
11
  type: "recordStart" | "recordStop";
11
- recordInfo?: {
12
- data: Blob;
13
- cancelAutoDownload: () => void;
14
- } | undefined;
12
+ recordInfo?: IRecordStopInfo | undefined;
15
13
  }>;
16
14
  rotate: IDataHandle<{
17
15
  type: EnumRotate;
@@ -1,3 +1,4 @@
1
+ import { IPuOption } from "src/main";
1
2
  import { IPluginEvent, IPluginEventMap } from "../../plugins/types";
2
3
  import PlayerPlugin, { PLAYER_MODES } from "./index";
3
4
  interface IPlayerEvent extends IPluginEvent {
@@ -15,6 +16,17 @@ interface IRotateEvent extends IPlayerEvent {
15
16
  name: "rotateEvent";
16
17
  rotateDeg: number;
17
18
  }
19
+ export interface IRecordStopInfo {
20
+ data: Blob;
21
+ startTime: number;
22
+ endTime: number;
23
+ duration: number;
24
+ puOptions: IPuOption;
25
+ fileName: string;
26
+ cancelAutoDownload: () => {
27
+ download(filename?: string): void;
28
+ };
29
+ }
18
30
  export interface IPlayerPluginEventMap extends IPluginEventMap {
19
31
  init: IInitEvent;
20
32
  closeWebrtc: ICloseWebrtcEvent;
@@ -22,10 +34,7 @@ export interface IPlayerPluginEventMap extends IPluginEventMap {
22
34
  close: IPlayerEvent;
23
35
  contextmenu: Event;
24
36
  recordStart: undefined;
25
- recordStop: {
26
- data: Blob;
27
- cancelAutoDownload: () => void;
28
- };
37
+ recordStop: IRecordStopInfo;
29
38
  screenshot: undefined | {
30
39
  img: Blob;
31
40
  cancelAutoDownload: () => void;
@@ -56,6 +56,7 @@ declare class PlayerPlugin extends Plugin<HTMLDivElement, IPlayerPluginEventMap>
56
56
  private _videoOption?;
57
57
  private _pluginOption?;
58
58
  private _playerConfig;
59
+ private _puOptions?;
59
60
  private _video;
60
61
  private _spinner;
61
62
  private _toast;
@@ -24,7 +24,7 @@ declare class PtzPluginBase extends Plugin<HTMLDivElement, IControllerPluginEven
24
24
  iPresetPoint: number;
25
25
  private _speedSlider;
26
26
  private _buttons;
27
- private _toast;
27
+ private _toastInject;
28
28
  constructor(params: {
29
29
  toast: ToastPlugin;
30
30
  });
@@ -1,3 +1,4 @@
1
+ import { IOnMediaRecordHandle } from "src/utils/media";
1
2
  import { IPluginEvent, IPluginEventMap, Plugin } from "./types";
2
3
  import { IPuOption } from "src/main";
3
4
  interface IVideoPluginEvent extends IPluginEvent {
@@ -52,7 +53,7 @@ declare class VideoPlugin extends Plugin<HTMLDivElement, IVideoPluginEventMap> {
52
53
  } | undefined>;
53
54
  private _genFileName;
54
55
  recordStart(): void;
55
- recordStop(onRecordInfo?: (data: Blob, cancelAutoDownload: () => void) => void): void;
56
+ recordStop(onRecordInfo?: IOnMediaRecordHandle): void;
56
57
  rotate(degStep: number): void;
57
58
  beforeDestroy(): void;
58
59
  }
@@ -1,6 +1,16 @@
1
1
  interface IStreamProvider {
2
2
  captureStream(frameRate?: number): MediaStream;
3
3
  }
4
+ export interface IOnMediaRecordHandle {
5
+ (blob: Blob, metaInfo: {
6
+ duration: number;
7
+ startTime: number;
8
+ endTime: number;
9
+ fileName: string;
10
+ }, cancelAutoDownload: () => {
11
+ download(fileName?: string): void;
12
+ }): boolean;
13
+ }
4
14
  interface IMediaOption {
5
15
  video?: boolean;
6
16
  audio?: boolean;
@@ -42,7 +52,7 @@ declare const media: {
42
52
  download(): void;
43
53
  } | undefined>;
44
54
  record: (element: IStreamProvider, stream?: MediaStream | null, fileName?: string) => [(MediaRecorder | undefined)?, (MediaStream | undefined)?, ({
45
- setOnMedia(onMedia: (blob: Blob, cancelAutoDownload: () => void) => Promise<void> | void): void;
55
+ setOnMedia(onMedia: IOnMediaRecordHandle): void;
46
56
  } | undefined)?];
47
57
  getMediaDir: (option: IMediaOption) => number;
48
58
  video2Canvas: (video: HTMLVideoElement, opts: IVideo2CanvasOptions) => ICanvasControl;
@@ -1 +1,2 @@
1
- export declare const createRecord: (_mediaStream: MediaStream, fileName: string, isDownload: (blob: Blob) => Promise<boolean>) => MediaRecorder;
1
+ import { IOnMediaRecordHandle } from "./media";
2
+ export declare const createRecord: (_mediaStream: MediaStream, fileName: string, isDownload: IOnMediaRecordHandle) => MediaRecorder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@besovideo/webrtc-player",
3
- "version": "0.8.94",
3
+ "version": "0.8.96",
4
4
  "description": "@besovideo/webrtc-player desc",
5
5
  "type": "module",
6
6
  "types": "./dist/types/main.d.ts",