@wetspace/wetrtc 3.0.0 → 3.0.2

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 ADDED
@@ -0,0 +1,29 @@
1
+ # WetRTC
2
+
3
+ 本工具库是基于 WebRTC 的进一步封装,简化了其 WebRTC 中生涩的 API 的使用. 如果要考虑到兼容性,推荐引入`webrtc-adapter`这个适配库
4
+
5
+ 适配库的安装:
6
+
7
+ `npm install webrtc-adapter`
8
+
9
+ [适配库的使用文档地址](https://github.com/webrtchacks/adapter#readme)
10
+
11
+ 在 Electron 应用中是不需要使用适配库的,高版本的 Electron 对该部分支持非常到位
12
+
13
+ ## 1. 下载安装
14
+
15
+ `npm i @wetspace/wetrtc -S` 或 `yarn add @wetspace/wetrtc`
16
+
17
+ ## 2. 源码地址
18
+
19
+ [地址链接](https://gitee.com/wetspace/wetrtc)
20
+
21
+ ## 3. Chrome 对 RTC 的测试指标显示地址
22
+
23
+ **`chrome://webrtc-internals/`**:该地址就是当你开启了 WebRTC 的音视频传输,它就能显示一些通讯中的基本信息,可用于某些功能的调试
24
+
25
+ ## 4. 测试体验工具(音视频通讯)
26
+
27
+ [安装包地址(windows)](https://pan.baidu.com/s/10tniytcLmS1w7JFooT3Jsg?pwd=pov4)
28
+
29
+ <!-- 下载地址:[windows 版本]() -->
@@ -0,0 +1,6 @@
1
+ export interface WetCreateConnectOneOption {
2
+ configuration: RTCConfiguration;
3
+ constraints: MediaStreamConstraints;
4
+ transDirect: 'recvonly' | 'sendonly' | 'sendrecv';
5
+ }
6
+ export declare const DEFAULT_WetOption: WetCreateConnectOneOption;
@@ -0,0 +1,31 @@
1
+ import { WetRTCPlayer } from './index';
2
+ import type { ConnectionState } from './index';
3
+ import type { MutableRefObject } from 'react';
4
+ import type { ErrorMsgType } from '../libs';
5
+ import { WetCreateConnectOneOption } from './constant';
6
+ interface UseWebRtcRetrun {
7
+ connect: (sdp?: RTCSessionDescriptionInit) => Promise<void>;
8
+ disconnect: () => void;
9
+ play: (type: 'remote' | 'local', player: WetRTCPlayer, outType?: 'video' | 'audio') => void;
10
+ answerAction: (sdp: RTCSessionDescriptionInit) => void;
11
+ byts: number;
12
+ connectState: ConnectionState;
13
+ useAblesState: {
14
+ audioinput: boolean;
15
+ audiooutput: boolean;
16
+ videoinput: boolean;
17
+ };
18
+ }
19
+ interface UseWebRtcParams {
20
+ name?: string | number | MutableRefObject<string | number>;
21
+ senderSdp: (sdp: RTCSessionDescriptionInit) => void;
22
+ senderIce?: (ice: RTCIceCandidate) => void;
23
+ connectionConfig?: Partial<WetCreateConnectOneOption>;
24
+ onError?: (err: ErrorMsgType) => void;
25
+ isLisenByts?: boolean;
26
+ }
27
+ declare function useWetRTC(options: Omit<UseWebRtcParams, 'isLisenByts'> & {
28
+ isLisenByts: true;
29
+ }): UseWebRtcRetrun;
30
+ declare function useWetRTC(options: UseWebRtcParams): Omit<UseWebRtcRetrun, 'byts'>;
31
+ export { useWetRTC };
@@ -1,55 +1,39 @@
1
+ import type { WetCreateConnectOneOption } from './constant';
1
2
  export declare type WetRTCPlayer = HTMLVideoElement | HTMLAudioElement | null | undefined;
2
3
  export declare type ConnectionState = 'new' | 'connected' | 'disconnected' | 'closed' | 'failed';
3
- export interface WetRTCConnectctItem {
4
- RTCPeerIns: RTCPeerConnection;
5
- state: ConnectionState;
6
- uuid: string;
7
- name: string;
8
- }
9
- export interface WetRTCoptions {
10
- configuration?: RTCConfiguration;
11
- constraints?: MediaStreamConstraints;
12
- localPlayer?: WetRTCPlayer;
13
- }
4
+ export declare type ErrorFn = ((...p: any) => void) | null;
5
+ export declare type EventType = 'error' | 'state' | 'byts' | 'ice';
14
6
  export declare class WetRTC {
15
- streamControl: {
16
- stream: MediaStream;
17
- close: () => void;
18
- } | null;
19
- rtcConnectMap: Map<string | number, WetRTCConnectctItem>;
20
- private _options;
21
- private isUseAbles;
22
- constructor(configuration?: RTCConfiguration, constraints?: MediaStreamConstraints);
23
- private init;
24
- private captureStream;
25
- /**
26
- * 创建连接
27
- */
28
- createConnectOne(option: {
29
- sdp?: RTCSessionDescriptionInit;
30
- player: WetRTCPlayer;
31
- uuid?: string;
32
- name?: string;
33
- }): {
34
- uuid: string;
35
- setRemote: (sdp: RTCSessionDescriptionInit) => void;
36
- addIceCandidate: (sdp: RTCIceCandidate) => void;
37
- next: (icecandidateCb: (candidate: RTCIceCandidate) => void, stateChange?: ((state: ConnectionState) => void) | undefined) => Promise<RTCSessionDescriptionInit>;
7
+ constructor(options?: Partial<WetCreateConnectOneOption>);
8
+ state: ConnectionState;
9
+ isUseAbles: {
10
+ audiooutput: boolean;
11
+ audioinput: boolean;
12
+ videoinput: boolean;
38
13
  };
14
+ private streamControl;
15
+ private runError;
16
+ private runByt;
17
+ private EventMap;
18
+ private RTCPeerIns;
19
+ private removeListenFn;
20
+ private WetOption;
21
+ getUseAbleDevices(): Promise<MediaDeviceInfo[] | null>;
22
+ addEventListener(type: EventType, fn: (...p: any) => any): void;
23
+ removeEventListener(type: EventType, fn: (...p: any) => any): void;
24
+ private captureStream;
25
+ connect(sdp?: RTCSessionDescriptionInit): Promise<RTCSessionDescriptionInit | null>;
26
+ private closePeerInsTracks;
39
27
  /**
40
28
  * 关闭连接
41
29
  */
42
- disconnect(uuid?: string): void;
43
- /**
44
- * 获取特定的连接
45
- */
46
- getConnectById(uuid: string): WetRTCConnectctItem | undefined;
47
- /**
48
- * 设置远程sdp信息
49
- */
50
- setRemoteDescriptionById(uuid: string, sdp: RTCSessionDescriptionInit): Promise<unknown>;
30
+ disconnect(): void;
51
31
  /**
52
- * 设置远程IceCandidate信息
32
+ * 关闭所有的流
53
33
  */
54
- addIceCandidateById(uuid: string, sdp: RTCIceCandidateInit): Promise<unknown>;
34
+ close(): void;
35
+ setRemoteSdp(_sdp: RTCSessionDescriptionInit): Promise<void>;
36
+ addIceCandidate(_sdp: RTCIceCandidate): Promise<void>;
37
+ playRemote(player: WetRTCPlayer, type?: 'video' | 'audio'): Promise<void>;
38
+ playLocal(player: WetRTCPlayer, type?: 'video' | 'audio'): Promise<void>;
55
39
  }
package/es/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { getUseAbleDevices, getMediaStream } from './libs';
2
- import { SaveFileToLocal, createStreamToRecord } from './libs/record';
3
- import { WetRTC } from './core';
4
- export { getUseAbleDevices, getMediaStream, SaveFileToLocal, createStreamToRecord };
1
+ import { getUseAbleDevices, getMediaStream, concatStream, filterStream, getRtcPeerTracks, stopStreamTrack } from './libs';
2
+ import { streamToRecord } from './libs/record';
3
+ import { WetRTC } from './core/index';
4
+ import { useWetRTC } from './core/hook';
5
+ export { getUseAbleDevices, getMediaStream, concatStream, filterStream, streamToRecord, getRtcPeerTracks, useWetRTC, stopStreamTrack, };
5
6
  export default WetRTC;
package/es/index.js CHANGED
@@ -1 +1 @@
1
- function __awaiter(e,s,a,c){return new(a=a||Promise)(function(o,t){function n(e){try{r(c.next(e))}catch(e){t(e)}}function i(e){try{r(c.throw(e))}catch(e){t(e)}}function r(e){var t;e.done?o(e.value):((t=e.value)instanceof a?t:new a(function(e){e(t)})).then(n,i)}r((c=c.apply(e,s||[])).next())})}const getUseAbleDevices=()=>__awaiter(void 0,void 0,void 0,function*(){return navigator.mediaDevices.enumerateDevices?yield navigator.mediaDevices.enumerateDevices():Promise.reject({type:"CompatibilityError",message:"暂不支持-getUseAbleDevices"})}),getMediaStream=(n="display")=>{const i="display"===n?"getDisplayMedia":"getUserMedia";return o=>__awaiter(void 0,void 0,void 0,function*(){if(!navigator.mediaDevices[i])return Promise.reject({type:"CompatibilityError",message:`暂不支持-${i},`});try{var e="display"===n?o:o||{video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,enableBackground:!1,suppressLocalAudioPlayback:!0}};const t=yield navigator.mediaDevices[i](e);return{stream:t,close:()=>{t.getTracks().forEach(e=>{e.stop()})}}}catch(e){return"Permission denied"===e.message?Promise.reject({type:"CancelError",message:"请求已经取消"}):Promise.reject({type:"GeneralError",message:e.message||"未知错误"})}})},filterStream=(e,t)=>{let o=[];return e.forEach(e=>{o=[...o,...e.getTracks().filter(e=>t?e.kind===t:e)]}),new MediaStream(o)},getRemoteTracks=e=>e.getReceivers().map(e=>e.track),getSaveProgressNum=(e,i)=>{const t=e.getReader();let r=0;return new ReadableStream({start(n){return function o(){return t.read().then(e=>{var{done:e,value:t}=e;if(!e)return r+=(null==t?void 0:t.length)||0,console.log(r/i),n.enqueue(t),o();n.close()})}()}})},SaveFileToLocal=(e,t)=>{e=URL.createObjectURL(e);const o=document.createElement("a");o.href=e,o.target="_blank",o.style.display="none",document.body.appendChild(o),o.download=t,o.click(),URL.revokeObjectURL(e),document.body.removeChild(o)},createStreamToRecord=(e,t={audioBitsPerSecond:128e3,videoBitsPerSecond:25e5,mimeType:"video/webm"})=>{const n=t.mimeType?t.mimeType.split("/"):["webm"];let i=(new Date).getTime().toString();const o=MediaRecorder.isTypeSupported(t.mimeType||"");if(!o)throw new Error(t.mimeType+"类型不支持");const r=new MediaRecorder(e,t);r.addEventListener("dataavailable",e=>{const t=e.data,o=t.size;e=URL.createObjectURL(t);fetch(e).then(e=>e.ok?e:Promise.reject("保存视频失败")).then(e=>e.body).then(e=>e?(SaveFileToLocal(t,i+"."+n[(null===n||void 0===n?void 0:n.length)-1]),getSaveProgressNum(e,o)):Promise.reject("没有可用的流"))});const s=()=>{o&&r.stop()};return{start:()=>{o&&r.start()},pause:()=>{o&&r.pause()},save:e=>{o&&(i=e||(new Date).getTime().toString(),s())},stop:s,resume:()=>{o&&r.resume()}}};let getRandomValues;const rnds8=new Uint8Array(16);function rng(){if(!getRandomValues&&!(getRandomValues="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return getRandomValues(rnds8)}const byteToHex=[];for(let e=0;e<256;++e)byteToHex.push((e+256).toString(16).slice(1));function unsafeStringify(e,t=0){return(byteToHex[e[t+0]]+byteToHex[e[t+1]]+byteToHex[e[t+2]]+byteToHex[e[t+3]]+"-"+byteToHex[e[t+4]]+byteToHex[e[t+5]]+"-"+byteToHex[e[t+6]]+byteToHex[e[t+7]]+"-"+byteToHex[e[t+8]]+byteToHex[e[t+9]]+"-"+byteToHex[e[t+10]]+byteToHex[e[t+11]]+byteToHex[e[t+12]]+byteToHex[e[t+13]]+byteToHex[e[t+14]]+byteToHex[e[t+15]]).toLowerCase()}const randomUUID="undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto);var native={randomUUID:randomUUID};function v4(e,t,o){if(native.randomUUID&&!t&&!e)return native.randomUUID();const n=(e=e||{}).random||(e.rng||rng)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,t){o=o||0;for(let e=0;e<16;++e)t[o+e]=n[e];return t}return unsafeStringify(n)}class WetRTC{constructor(e,t){this.streamControl=null,this.rtcConnectMap=new Map,this._options={configuration:{iceServers:[{urls:"stun:stun.l.google.com:19302"}]}},this.isUseAbles={audiooutput:!1,audioinput:!1,videoinput:!1},this.init({configuration:e,constraints:t})}init(e){this._options.constraints=null==e?void 0:e.constraints,this._options.configuration=Object.assign(Object.assign({},this._options.configuration),null==e?void 0:e.configuration),getUseAbleDevices().then(e=>{const t=e.map(e=>e.kind);this.isUseAbles.audioinput=t.includes("audioinput"),this.isUseAbles.videoinput=t.includes("videoinput"),this.isUseAbles.audiooutput=t.includes("audiooutput")}).catch(e=>{"CompatibilityError"===e.type&&(this.isUseAbles.audioinput=!0,this.isUseAbles.videoinput=!0,this.isUseAbles.audiooutput=!0)})}captureStream(){return __awaiter(this,void 0,void 0,function*(){var e=yield getMediaStream("user")(this._options.constraints);this.streamControl=e})}createConnectOne(o){const i=null==o?void 0:o.sdp,r=new RTCPeerConnection(this._options.configuration),s=(null==o?void 0:o.uuid)||v4(),a={RTCPeerIns:r,state:"new",name:(null==o?void 0:o.name)||s,uuid:s};return r.addEventListener("track",e=>{const t=o.player;t?(t.setAttribute("autoplay",""),t.setAttribute("playsinline",""),console.log(e.streams,t),t.srcObject=e.streams[0]):console.warn("缺少媒体播放元素video或者是audio")}),{uuid:s,setRemote:e=>{r.setRemoteDescription(e)},addIceCandidate:e=>{r.addIceCandidate(e)},next:(o,n)=>__awaiter(this,void 0,void 0,function*(){if(this.rtcConnectMap.set(s,a),r.addEventListener("icecandidate",e=>{e.candidate&&(console.log("icecandidate",e),o(e.candidate))}),r.addEventListener("connectionstatechange",e=>{e=e.target;a.state=e.connectionState,console.log(a.state),n&&n(a.state)}),this.streamControl||(yield this.captureStream()),this.streamControl){const t=filterStream([this.streamControl.stream],"video");t.getTracks().forEach(e=>{r.addTrack(e,t)})}var e;return i?(yield r.setRemoteDescription(i),e=yield r.createAnswer(),yield r.setLocalDescription(e),e):(e=yield r.createOffer(),yield r.setLocalDescription(e),e)})}}disconnect(e){var t;if(e){if(!this.rtcConnectMap.has(e))return;const o=this.getConnectById(e);if(o){const n=getRemoteTracks(o.RTCPeerIns);n.forEach(e=>{null!=e&&e.stop()}),o.RTCPeerIns.close(),this.rtcConnectMap.delete(e)}}else this.rtcConnectMap.forEach(e=>{const t=getRemoteTracks(e.RTCPeerIns);t.forEach(e=>{null!=e&&e.stop()}),e.RTCPeerIns.close()}),this.rtcConnectMap.clear();0===this.rtcConnectMap.size&&null!==(t=this.streamControl)&&void 0!==t&&t.close()}getConnectById(e){return this.rtcConnectMap.get(e)}setRemoteDescriptionById(t,o){return __awaiter(this,void 0,void 0,function*(){const e=this.getConnectById(t);try{yield null===e||void 0===e?void 0:e.RTCPeerIns.setRemoteDescription(o)}catch(e){return e}})}addIceCandidateById(t,o){return __awaiter(this,void 0,void 0,function*(){const e=this.getConnectById(t);try{yield null===e||void 0===e?void 0:e.RTCPeerIns.addIceCandidate(o)}catch(e){return e}})}}export{SaveFileToLocal,createStreamToRecord,WetRTC as default,getMediaStream,getUseAbleDevices};
1
+ import{useState,useRef,useEffect,useCallback}from"react";function __awaiter(e,o,a,c){return new(a=a||Promise)(function(n,t){function r(e){try{s(c.next(e))}catch(e){t(e)}}function i(e){try{s(c.throw(e))}catch(e){t(e)}}function s(e){var t;e.done?n(e.value):((t=e.value)instanceof a?t:new a(function(e){e(t)})).then(r,i)}s((c=c.apply(e,o||[])).next())})}const getUseAbleDevices=()=>__awaiter(void 0,void 0,void 0,function*(){return navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices?yield navigator.mediaDevices.enumerateDevices():(console.error("navigator.mediaDevices.enumerateDevices"),null)}),getMediaStream=(e="display",r)=>{const i="display"===e?"getDisplayMedia":"getUserMedia";return n=>__awaiter(void 0,void 0,void 0,function*(){let e=null;if(navigator.mediaDevices[i]){var t=Object.assign({video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,enableBackground:!1,suppressLocalAudioPlayback:!0}},n);try{e=yield navigator.mediaDevices[i](t)}catch(e){r&&r({name:e.name||"Error",message:e.message||"内部错误"})}}else r&&r({name:"CompatibleError",message:"不支持navigator.mediaDevices"});return e})},stopStreamTrack=e=>{e&&e.getTracks().forEach(e=>{e.stop()})},concatStream=e=>{let t=[];return e.forEach(e=>{t=[...t,...e.getTracks()]}),new MediaStream(t)},filterStream=(e,t)=>{let n=[];return e.forEach(e=>{n=[...n,...e.getTracks().filter(e=>t?e.kind===t:e)]}),new MediaStream(n)},getRtcPeerTracks=(e,t)=>{const n={receivers(){const t=[];return e.getReceivers().forEach(e=>{e.track&&t.push(e.track)}),t},senders(){const t=[];return e.getSenders().forEach(e=>{e.track&&t.push(e.track)}),t}};return t?n[t]():[...n.receivers(),...n.senders()]},listenerEvent=(e,t)=>(e.addEventListener("track",t.track),e.addEventListener("icecandidate",t.icecandidate),e.addEventListener("connectionstatechange",t.connectionstatechange),()=>{e.removeEventListener("track",t.track),e.removeEventListener("icecandidate",t.icecandidate),e.removeEventListener("connectionstatechange",t.connectionstatechange)}),SaveFileToLocal=(e,t)=>{e=URL.createObjectURL(e);const n=document.createElement("a");n.href=e,n.target="_blank",n.style.display="none",document.body.appendChild(n),n.download=t,n.click(),URL.revokeObjectURL(e),document.body.removeChild(n)},streamToRecord=(e,t={audioBitsPerSecond:128e3,videoBitsPerSecond:25e5,mimeType:"video/webm"})=>{const n=t.mimeType?t.mimeType.split("/"):["webm"];let r=(new Date).getTime().toString();const i=MediaRecorder.isTypeSupported(t.mimeType||"");if(!i)throw new Error(t.mimeType+"类型不支持");const s=new MediaRecorder(e,t);s.addEventListener("dataavailable",e=>{SaveFileToLocal(e.data,r+"."+n[(null===n||void 0===n?void 0:n.length)-1])});const o=()=>{i&&s.stop()};return{start:()=>{i&&s.start()},pause:()=>{i&&s.pause()},save:e=>{i&&(r=e||(new Date).getTime().toString(),o())},stop:o,resume:()=>{i&&s.resume()}}},DEFAULT_WetOption={transDirect:"sendrecv",configuration:{iceServers:[{urls:"stun:stun.l.google.com:19302"}]},constraints:{video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,suppressLocalAudioPlayback:!0}}};class WetRTC{constructor(e){this.state="new",this.isUseAbles={audiooutput:!1,audioinput:!1,videoinput:!1},this.streamControl={close:e=>{const t={remote:()=>{stopStreamTrack(this.streamControl.remoteStreams),this.streamControl.remoteStreams=null},local:()=>{stopStreamTrack(this.streamControl.localStreams),this.streamControl.localStreams=null}};e?t[e]():(t.local(),t.remote())}},this.runByt={time:null,close:()=>{this.runByt.time&&clearInterval(this.runByt.time)},run:()=>{const i=this.EventMap.get("byts");let s=null;i&&0<i.length&&(null!==i&&void 0!==i&&i.forEach(e=>{e(0)}),this.runByt.time=setInterval(()=>{var e;null!==(e=this.RTCPeerIns)&&void 0!==e&&e.getStats().then(e=>{const t={bytesReceived:0};if(e.forEach(e=>{"transport"===e.type&&(t.bytesReceived+=e.bytesReceived)}),s){const n=s.bytesReceived,r=t.bytesReceived;null!==i&&void 0!==i&&i.forEach(e=>{e(Number(((r-n)/1024).toFixed(2)))}),s=Object.assign({},t)}else s=Object.assign({},t)})},1e3))}},this.EventMap=new Map([]),this.RTCPeerIns=null,this.removeListenFn=()=>{},this.WetOption=Object.assign({},DEFAULT_WetOption),this.WetOption=Object.assign(Object.assign({},this.WetOption),e)}runError(t){const e=this.EventMap.get("error");null!==e&&void 0!==e&&e.forEach(e=>{e(t)})}getUseAbleDevices(){return __awaiter(this,void 0,void 0,function*(){return yield getUseAbleDevices()})}addEventListener(e,t){const n=this.EventMap.get(e);n?n.find(e=>e===t)||(n.push(t),this.EventMap.set(e,n)):this.EventMap.set(e,[t])}removeEventListener(e,t){const n=this.EventMap.get(e);var r;n&&(r=n.filter(e=>e!==t),this.EventMap.set(e,r))}captureStream(){return __awaiter(this,void 0,void 0,function*(){const e=yield getUseAbleDevices();if(e){const n=e.map(e=>e.kind);this.isUseAbles.audioinput=n.includes("audioinput"),this.isUseAbles.videoinput=n.includes("videoinput"),this.isUseAbles.audiooutput=n.includes("audiooutput")}else this.isUseAbles.audioinput=!0,this.isUseAbles.videoinput=!0,this.isUseAbles.audiooutput=!0;var t;return this.isUseAbles.audioinput||this.isUseAbles.videoinput?((t=yield getMediaStream("user",e=>{this.runError(e)})({video:!!this.isUseAbles.videoinput&&this.WetOption.constraints.video,audio:!!this.isUseAbles.audioinput&&this.WetOption.constraints.audio}))&&(this.streamControl.localStreams=t),t):(this.runError({name:"StreamError",message:"音频输入设备,视频输入设备均不可用"}),null)})}connect(o){var a;return __awaiter(this,void 0,void 0,function*(){if(["sendrecv","sendonly"].includes(this.WetOption.transDirect)&&(!this.streamControl.localStreams&&!(yield this.captureStream())))return null;let n="new",r=this;Object.defineProperty(this,"state",{get(){return n},set(e){"connected"===(n=e)?r.runByt.run():["disconnected","closed","failed"].includes(e)&&r.runByt.close();const t=r.EventMap.get("state");null!==t&&void 0!==t&&t.forEach(e=>{e(n)})}});var e;if(this.state="new",this.RTCPeerIns=new RTCPeerConnection(this.WetOption.configuration),this.removeListenFn=listenerEvent(this.RTCPeerIns,{track:e=>{e.streams&&e.streams[0]&&(this.streamControl.remoteStreams=e.streams[0])},icecandidate:t=>{if(t.candidate){const e=r.EventMap.get("ice");null!==e&&void 0!==e&&e.forEach(e=>{e(t.candidate)})}},connectionstatechange:e=>{e=e.target;this.state=e.connectionState}}),["sendrecv","sendonly"].includes(this.WetOption.transDirect)){const i=null===(a=this.streamControl)||void 0===a?void 0:a.localStreams;null!==i&&void 0!==i&&i.getTracks().forEach(e=>{var t;null!==(t=this.RTCPeerIns)&&void 0!==t&&t.addTrack(e,i)})}else try{this.RTCPeerIns.addTransceiver("video",{direction:"recvonly"}),this.RTCPeerIns.addTransceiver("audio",{direction:"recvonly"})}catch(e){const t=document.createElement("canvas"),s=t.captureStream();null!==s&&void 0!==s&&s.getTracks().forEach(e=>{var t;null!==(t=this.RTCPeerIns)&&void 0!==t&&t.addTrack(e,s)})}return o?(yield this.setRemoteSdp(o),e=yield this.RTCPeerIns.createAnswer(),yield this.RTCPeerIns.setLocalDescription(e),e):(e=yield this.RTCPeerIns.createOffer(),yield this.RTCPeerIns.setLocalDescription(e),e)})}closePeerInsTracks(){if(this.RTCPeerIns){const e={recvonly:()=>getRtcPeerTracks(this.RTCPeerIns,"receivers"),sendonly:()=>getRtcPeerTracks(this.RTCPeerIns,"senders"),sendrecv:()=>getRtcPeerTracks(this.RTCPeerIns)},t=e[this.WetOption.transDirect]();t.forEach(e=>{null!=e&&e.stop()})}}disconnect(){this.state="closed",this.removeListenFn(),this.closePeerInsTracks(),this.RTCPeerIns=null}close(){this.disconnect(),this.streamControl.close()}setRemoteSdp(e){var t;return __awaiter(this,void 0,void 0,function*(){if(!e)return this.runError({name:"OperationError",message:"缺失sdp信息"}),void(this.state="failed");try{yield null===(t=this.RTCPeerIns)||void 0===t?void 0:t.setRemoteDescription(e)}catch(e){this.state="failed",this.runError({name:e.name||"Error",message:e.message||"内部错误"})}})}addIceCandidate(n){return __awaiter(this,void 0,void 0,function*(){if(!n)return this.runError({name:"OperationError",message:"缺失IceSdp信息"}),void(this.state="failed");const e=()=>__awaiter(this,void 0,void 0,function*(){var e;try{yield null===(e=this.RTCPeerIns)||void 0===e?void 0:e.addIceCandidate(n)}catch(e){this.state="failed",this.runError({name:e.name||"Error",message:e.message||"内部错误"})}});if(this.RTCPeerIns)e();else{const t=setInterval(()=>__awaiter(this,void 0,void 0,function*(){e(),clearInterval(t)}),1e3)}})}playRemote(t,n){return __awaiter(this,void 0,void 0,function*(){var e;this.RTCPeerIns&&(e=this.RTCPeerIns.getReceivers().map(e=>e.track),e=new MediaStream(e),null!=t&&t.setAttribute("autoplay",""),null!=t&&t.setAttribute("playsinline",""),t&&(t.srcObject=filterStream([e],n)))})}playLocal(t,n){return __awaiter(this,void 0,void 0,function*(){this.streamControl.localStreams||(yield this.captureStream());var e=this.streamControl.localStreams;null!=t&&t.setAttribute("autoplay",""),null!=t&&t.setAttribute("playsinline",""),t&&(t.srcObject=filterStream([e],n))})}}let getRandomValues;const rnds8=new Uint8Array(16);function rng(){if(!getRandomValues&&!(getRandomValues="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return getRandomValues(rnds8)}const byteToHex=[];for(let e=0;e<256;++e)byteToHex.push((e+256).toString(16).slice(1));function unsafeStringify(e,t=0){return(byteToHex[e[t+0]]+byteToHex[e[t+1]]+byteToHex[e[t+2]]+byteToHex[e[t+3]]+"-"+byteToHex[e[t+4]]+byteToHex[e[t+5]]+"-"+byteToHex[e[t+6]]+byteToHex[e[t+7]]+"-"+byteToHex[e[t+8]]+byteToHex[e[t+9]]+"-"+byteToHex[e[t+10]]+byteToHex[e[t+11]]+byteToHex[e[t+12]]+byteToHex[e[t+13]]+byteToHex[e[t+14]]+byteToHex[e[t+15]]).toLowerCase()}const randomUUID="undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto);var native={randomUUID:randomUUID};function v4(e,t,n){if(native.randomUUID&&!t&&!e)return native.randomUUID();const r=(e=e||{}).random||(e.rng||rng)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(let e=0;e<16;++e)t[n+e]=r[e];return t}return unsafeStringify(r)}function useWetRTC(e){const{connectionConfig:t,name:n,isLisenByts:r}=e,[u,l]=useState(0),i=useRef(""),s=useRef(e.senderSdp),o=useRef(e.senderIce),a=useRef(e.onError),c=(s.current=e.senderSdp,o.current=e.senderIce,a.current=e.onError,useRef(null)),[v,m]=useState({audioinput:!1,audiooutput:!1,videoinput:!1}),h=useRef({audioinput:!1,audiooutput:!1,videoinput:!1}),[d,p]=useState("new");useEffect(()=>{c.current=new WetRTC(t),c.current.addEventListener("error",e=>{a.current&&a.current(e)}),c.current.addEventListener("state",e=>{p(e)}),c.current.addEventListener("ice",e=>{o.current&&o.current(e)}),c.current.getUseAbleDevices().then(e=>{const t={audioinput:!0,audiooutput:!0,videoinput:!0};if(e){const n=e.map(e=>e.kind);t.audioinput=n.includes("audioinput"),t.audiooutput=n.includes("audiooutput"),t.videoinput=n.includes("videoinput"),h.current=Object.assign({},t)}m(()=>t)})},[]),useEffect(()=>{var e;"connected"===d&&r&&null!==(e=c.current)&&void 0!==e&&e.addEventListener("byts",e=>{l(e)})},[d,r]);return{connect:useCallback(t=>__awaiter(this,void 0,void 0,function*(){var e;n?i.current="object"==typeof n?n.current:n:i.current=i.current||v4(),c.current&&i.current&&((e=yield c.current.connect(t))&&s.current(e))}),[n&&"object"==typeof n?n.current:n]),disconnect:()=>{var e;null!==(e=c.current)&&void 0!==e&&e.close()},play:(e,t,n)=>{"local"===e?null!==(e=c.current)&&void 0!==e&&e.playLocal(t,n):null!==(e=c.current)&&void 0!==e&&e.playRemote(t,n)},answerAction:e=>{var t;null!==(t=c.current)&&void 0!==t&&t.setRemoteSdp(e)},byts:u,connectState:d,useAblesState:v}}export{concatStream,WetRTC as default,filterStream,getMediaStream,getRtcPeerTracks,getUseAbleDevices,stopStreamTrack,streamToRecord,useWetRTC};
@@ -2,7 +2,11 @@
2
2
  * 获取可用设备
3
3
  * @returns Promise
4
4
  */
5
- export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[]>;
5
+ export interface ErrorMsgType {
6
+ name: string;
7
+ message: string;
8
+ }
9
+ export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[] | null>;
6
10
  /**
7
11
  * 获取支持的约束
8
12
  */
@@ -11,10 +15,11 @@ export declare const getSupportedConsitains: () => MediaTrackSupportedConstraint
11
15
  * 选择分享流(本地视频流) / 捕获用户许可的输入信号流
12
16
  * @returns Promise
13
17
  */
14
- export declare const getMediaStream: (type?: 'user' | 'display') => (constraints?: DisplayMediaStreamConstraints | MediaStreamConstraints | undefined) => Promise<{
15
- stream: MediaStream;
16
- close: () => void;
17
- }>;
18
+ export declare const getMediaStream: (type?: 'user' | 'display', onError?: ((params: ErrorMsgType) => void) | undefined) => (constraints?: MediaStreamConstraints | undefined) => Promise<MediaStream | null>;
19
+ /**
20
+ * 关闭流和轨道追踪
21
+ */
22
+ export declare const stopStreamTrack: (streams?: MediaStream | null | undefined) => void;
18
23
  /**
19
24
  * 将多个stream 整合为一个stream
20
25
  * @param options
@@ -22,10 +27,15 @@ export declare const getMediaStream: (type?: 'user' | 'display') => (constraints
22
27
  */
23
28
  export declare const concatStream: (options: MediaStream[]) => MediaStream;
24
29
  /**
25
- * 将多个stream 按'audio' 或'video'进行过滤整合
30
+ * 将多个stream 按'audio' 或'video'进行过滤整合,当type没有输出的流类型时将不会过滤
26
31
  * @param options
27
32
  * @param type
28
33
  * @returns
29
34
  */
30
35
  export declare const filterStream: (options: MediaStream[], type?: "audio" | "video" | undefined) => MediaStream;
31
- export declare const getRemoteTracks: (peerConnection: RTCPeerConnection) => MediaStreamTrack[];
36
+ export declare const getRtcPeerTracks: (peerConnection: RTCPeerConnection, type?: "receivers" | "senders" | undefined) => MediaStreamTrack[];
37
+ export declare const listenerEvent: (RTCPeerIns: RTCPeerConnection, option: {
38
+ track: (e: RTCTrackEvent) => void;
39
+ icecandidate: (e: RTCPeerConnectionIceEvent) => void;
40
+ connectionstatechange: (e: Event) => void;
41
+ }) => () => void;
@@ -1,5 +1,5 @@
1
1
  export declare const SaveFileToLocal: (blob: Blob, fileName: string) => void;
2
- export declare const createStreamToRecord: (stream: MediaStream, options?: MediaRecorderOptions) => {
2
+ export declare const streamToRecord: (stream: MediaStream, options?: MediaRecorderOptions) => {
3
3
  start: () => void;
4
4
  pause: () => void;
5
5
  save: (_fileName?: string | undefined) => void;
@@ -0,0 +1,6 @@
1
+ export interface WetCreateConnectOneOption {
2
+ configuration: RTCConfiguration;
3
+ constraints: MediaStreamConstraints;
4
+ transDirect: 'recvonly' | 'sendonly' | 'sendrecv';
5
+ }
6
+ export declare const DEFAULT_WetOption: WetCreateConnectOneOption;
@@ -0,0 +1,31 @@
1
+ import { WetRTCPlayer } from './index';
2
+ import type { ConnectionState } from './index';
3
+ import type { MutableRefObject } from 'react';
4
+ import type { ErrorMsgType } from '../libs';
5
+ import { WetCreateConnectOneOption } from './constant';
6
+ interface UseWebRtcRetrun {
7
+ connect: (sdp?: RTCSessionDescriptionInit) => Promise<void>;
8
+ disconnect: () => void;
9
+ play: (type: 'remote' | 'local', player: WetRTCPlayer, outType?: 'video' | 'audio') => void;
10
+ answerAction: (sdp: RTCSessionDescriptionInit) => void;
11
+ byts: number;
12
+ connectState: ConnectionState;
13
+ useAblesState: {
14
+ audioinput: boolean;
15
+ audiooutput: boolean;
16
+ videoinput: boolean;
17
+ };
18
+ }
19
+ interface UseWebRtcParams {
20
+ name?: string | number | MutableRefObject<string | number>;
21
+ senderSdp: (sdp: RTCSessionDescriptionInit) => void;
22
+ senderIce?: (ice: RTCIceCandidate) => void;
23
+ connectionConfig?: Partial<WetCreateConnectOneOption>;
24
+ onError?: (err: ErrorMsgType) => void;
25
+ isLisenByts?: boolean;
26
+ }
27
+ declare function useWetRTC(options: Omit<UseWebRtcParams, 'isLisenByts'> & {
28
+ isLisenByts: true;
29
+ }): UseWebRtcRetrun;
30
+ declare function useWetRTC(options: UseWebRtcParams): Omit<UseWebRtcRetrun, 'byts'>;
31
+ export { useWetRTC };
@@ -1,55 +1,39 @@
1
+ import type { WetCreateConnectOneOption } from './constant';
1
2
  export declare type WetRTCPlayer = HTMLVideoElement | HTMLAudioElement | null | undefined;
2
3
  export declare type ConnectionState = 'new' | 'connected' | 'disconnected' | 'closed' | 'failed';
3
- export interface WetRTCConnectctItem {
4
- RTCPeerIns: RTCPeerConnection;
5
- state: ConnectionState;
6
- uuid: string;
7
- name: string;
8
- }
9
- export interface WetRTCoptions {
10
- configuration?: RTCConfiguration;
11
- constraints?: MediaStreamConstraints;
12
- localPlayer?: WetRTCPlayer;
13
- }
4
+ export declare type ErrorFn = ((...p: any) => void) | null;
5
+ export declare type EventType = 'error' | 'state' | 'byts' | 'ice';
14
6
  export declare class WetRTC {
15
- streamControl: {
16
- stream: MediaStream;
17
- close: () => void;
18
- } | null;
19
- rtcConnectMap: Map<string | number, WetRTCConnectctItem>;
20
- private _options;
21
- private isUseAbles;
22
- constructor(configuration?: RTCConfiguration, constraints?: MediaStreamConstraints);
23
- private init;
24
- private captureStream;
25
- /**
26
- * 创建连接
27
- */
28
- createConnectOne(option: {
29
- sdp?: RTCSessionDescriptionInit;
30
- player: WetRTCPlayer;
31
- uuid?: string;
32
- name?: string;
33
- }): {
34
- uuid: string;
35
- setRemote: (sdp: RTCSessionDescriptionInit) => void;
36
- addIceCandidate: (sdp: RTCIceCandidate) => void;
37
- next: (icecandidateCb: (candidate: RTCIceCandidate) => void, stateChange?: ((state: ConnectionState) => void) | undefined) => Promise<RTCSessionDescriptionInit>;
7
+ constructor(options?: Partial<WetCreateConnectOneOption>);
8
+ state: ConnectionState;
9
+ isUseAbles: {
10
+ audiooutput: boolean;
11
+ audioinput: boolean;
12
+ videoinput: boolean;
38
13
  };
14
+ private streamControl;
15
+ private runError;
16
+ private runByt;
17
+ private EventMap;
18
+ private RTCPeerIns;
19
+ private removeListenFn;
20
+ private WetOption;
21
+ getUseAbleDevices(): Promise<MediaDeviceInfo[] | null>;
22
+ addEventListener(type: EventType, fn: (...p: any) => any): void;
23
+ removeEventListener(type: EventType, fn: (...p: any) => any): void;
24
+ private captureStream;
25
+ connect(sdp?: RTCSessionDescriptionInit): Promise<RTCSessionDescriptionInit | null>;
26
+ private closePeerInsTracks;
39
27
  /**
40
28
  * 关闭连接
41
29
  */
42
- disconnect(uuid?: string): void;
43
- /**
44
- * 获取特定的连接
45
- */
46
- getConnectById(uuid: string): WetRTCConnectctItem | undefined;
47
- /**
48
- * 设置远程sdp信息
49
- */
50
- setRemoteDescriptionById(uuid: string, sdp: RTCSessionDescriptionInit): Promise<unknown>;
30
+ disconnect(): void;
51
31
  /**
52
- * 设置远程IceCandidate信息
32
+ * 关闭所有的流
53
33
  */
54
- addIceCandidateById(uuid: string, sdp: RTCIceCandidateInit): Promise<unknown>;
34
+ close(): void;
35
+ setRemoteSdp(_sdp: RTCSessionDescriptionInit): Promise<void>;
36
+ addIceCandidate(_sdp: RTCIceCandidate): Promise<void>;
37
+ playRemote(player: WetRTCPlayer, type?: 'video' | 'audio'): Promise<void>;
38
+ playLocal(player: WetRTCPlayer, type?: 'video' | 'audio'): Promise<void>;
55
39
  }
package/lib/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { getUseAbleDevices, getMediaStream } from './libs';
2
- import { SaveFileToLocal, createStreamToRecord } from './libs/record';
3
- import { WetRTC } from './core';
4
- export { getUseAbleDevices, getMediaStream, SaveFileToLocal, createStreamToRecord };
1
+ import { getUseAbleDevices, getMediaStream, concatStream, filterStream, getRtcPeerTracks, stopStreamTrack } from './libs';
2
+ import { streamToRecord } from './libs/record';
3
+ import { WetRTC } from './core/index';
4
+ import { useWetRTC } from './core/hook';
5
+ export { getUseAbleDevices, getMediaStream, concatStream, filterStream, streamToRecord, getRtcPeerTracks, useWetRTC, stopStreamTrack, };
5
6
  export default WetRTC;
package/lib/index.js CHANGED
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).index={})}(this,function(e){"use strict";function c(e,s,a,c){return new(a=a||Promise)(function(n,t){function i(e){try{r(c.next(e))}catch(e){t(e)}}function o(e){try{r(c.throw(e))}catch(e){t(e)}}function r(e){var t;e.done?n(e.value):((t=e.value)instanceof a?t:new a(function(e){e(t)})).then(i,o)}r((c=c.apply(e,s||[])).next())})}const t=()=>c(void 0,void 0,void 0,function*(){return navigator.mediaDevices.enumerateDevices?yield navigator.mediaDevices.enumerateDevices():Promise.reject({type:"CompatibilityError",message:"暂不支持-getUseAbleDevices"})}),n=(i="display")=>{const o="display"===i?"getDisplayMedia":"getUserMedia";return n=>c(void 0,void 0,void 0,function*(){if(!navigator.mediaDevices[o])return Promise.reject({type:"CompatibilityError",message:`暂不支持-${o},`});try{var e="display"===i?n:n||{video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,enableBackground:!1,suppressLocalAudioPlayback:!0}};const t=yield navigator.mediaDevices[o](e);return{stream:t,close:()=>{t.getTracks().forEach(e=>{e.stop()})}}}catch(e){return"Permission denied"===e.message?Promise.reject({type:"CancelError",message:"请求已经取消"}):Promise.reject({type:"GeneralError",message:e.message||"未知错误"})}})},o=e=>e.getReceivers().map(e=>e.track),a=(e,o)=>{const t=e.getReader();let r=0;return new ReadableStream({start(i){return function n(){return t.read().then(e=>{var{done:e,value:t}=e;if(!e)return r+=(null==t?void 0:t.length)||0,console.log(r/o),i.enqueue(t),n();i.close()})}()}})},d=(e,t)=>{e=URL.createObjectURL(e);const n=document.createElement("a");n.href=e,n.target="_blank",n.style.display="none",document.body.appendChild(n),n.download=t,n.click(),URL.revokeObjectURL(e),document.body.removeChild(n)};let r;const l=new Uint8Array(16);const s=[];for(let e=0;e<256;++e)s.push((e+256).toString(16).slice(1));var u={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function i(e,t,n){if(u.randomUUID&&!t&&!e)return u.randomUUID();const i=(e=e||{}).random||(e.rng||function(){if(!r&&!(r="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return r(l)})();if(i[6]=15&i[6]|64,i[8]=63&i[8]|128,t){n=n||0;for(let e=0;e<16;++e)t[n+e]=i[e];return t}return e=i,o=0,(s[e[o+0]]+s[e[o+1]]+s[e[o+2]]+s[e[o+3]]+"-"+s[e[o+4]]+s[e[o+5]]+"-"+s[e[o+6]]+s[e[o+7]]+"-"+s[e[o+8]]+s[e[o+9]]+"-"+s[e[o+10]]+s[e[o+11]]+s[e[o+12]]+s[e[o+13]]+s[e[o+14]]+s[e[o+15]]).toLowerCase();var o}e.SaveFileToLocal=d,e.createStreamToRecord=(e,t={audioBitsPerSecond:128e3,videoBitsPerSecond:25e5,mimeType:"video/webm"})=>{const i=t.mimeType?t.mimeType.split("/"):["webm"];let o=(new Date).getTime().toString();const n=MediaRecorder.isTypeSupported(t.mimeType||"");if(!n)throw new Error(t.mimeType+"类型不支持");const r=new MediaRecorder(e,t);r.addEventListener("dataavailable",e=>{const t=e.data,n=t.size;e=URL.createObjectURL(t);fetch(e).then(e=>e.ok?e:Promise.reject("保存视频失败")).then(e=>e.body).then(e=>e?(d(t,o+"."+i[(null===i||void 0===i?void 0:i.length)-1]),a(e,n)):Promise.reject("没有可用的流"))});const s=()=>{n&&r.stop()};return{start:()=>{n&&r.start()},pause:()=>{n&&r.pause()},save:e=>{n&&(o=e||(new Date).getTime().toString(),s())},stop:s,resume:()=>{n&&r.resume()}}},e.default=class{constructor(e,t){this.streamControl=null,this.rtcConnectMap=new Map,this._options={configuration:{iceServers:[{urls:"stun:stun.l.google.com:19302"}]}},this.isUseAbles={audiooutput:!1,audioinput:!1,videoinput:!1},this.init({configuration:e,constraints:t})}init(e){this._options.constraints=null==e?void 0:e.constraints,this._options.configuration=Object.assign(Object.assign({},this._options.configuration),null==e?void 0:e.configuration),t().then(e=>{const t=e.map(e=>e.kind);this.isUseAbles.audioinput=t.includes("audioinput"),this.isUseAbles.videoinput=t.includes("videoinput"),this.isUseAbles.audiooutput=t.includes("audiooutput")}).catch(e=>{"CompatibilityError"===e.type&&(this.isUseAbles.audioinput=!0,this.isUseAbles.videoinput=!0,this.isUseAbles.audiooutput=!0)})}captureStream(){return c(this,void 0,void 0,function*(){var e=yield n("user")(this._options.constraints);this.streamControl=e})}createConnectOne(n){const o=null==n?void 0:n.sdp,r=new RTCPeerConnection(this._options.configuration),s=(null==n?void 0:n.uuid)||i(),a={RTCPeerIns:r,state:"new",name:(null==n?void 0:n.name)||s,uuid:s};return r.addEventListener("track",e=>{const t=n.player;t?(t.setAttribute("autoplay",""),t.setAttribute("playsinline",""),console.log(e.streams,t),t.srcObject=e.streams[0]):console.warn("缺少媒体播放元素video或者是audio")}),{uuid:s,setRemote:e=>{r.setRemoteDescription(e)},addIceCandidate:e=>{r.addIceCandidate(e)},next:(n,i)=>c(this,void 0,void 0,function*(){if(this.rtcConnectMap.set(s,a),r.addEventListener("icecandidate",e=>{e.candidate&&(console.log("icecandidate",e),n(e.candidate))}),r.addEventListener("connectionstatechange",e=>{e=e.target;a.state=e.connectionState,console.log(a.state),i&&i(a.state)}),this.streamControl||(yield this.captureStream()),this.streamControl){const t=((e,t)=>{let n=[];return e.forEach(e=>{n=[...n,...e.getTracks().filter(e=>t?e.kind===t:e)]}),new MediaStream(n)})([this.streamControl.stream],"video");t.getTracks().forEach(e=>{r.addTrack(e,t)})}var e;return o?(yield r.setRemoteDescription(o),e=yield r.createAnswer(),yield r.setLocalDescription(e),e):(e=yield r.createOffer(),yield r.setLocalDescription(e),e)})}}disconnect(e){var t;if(e){if(!this.rtcConnectMap.has(e))return;const n=this.getConnectById(e);if(n){const i=o(n.RTCPeerIns);i.forEach(e=>{null!=e&&e.stop()}),n.RTCPeerIns.close(),this.rtcConnectMap.delete(e)}}else this.rtcConnectMap.forEach(e=>{const t=o(e.RTCPeerIns);t.forEach(e=>{null!=e&&e.stop()}),e.RTCPeerIns.close()}),this.rtcConnectMap.clear();0===this.rtcConnectMap.size&&null!==(t=this.streamControl)&&void 0!==t&&t.close()}getConnectById(e){return this.rtcConnectMap.get(e)}setRemoteDescriptionById(t,n){return c(this,void 0,void 0,function*(){const e=this.getConnectById(t);try{yield null===e||void 0===e?void 0:e.RTCPeerIns.setRemoteDescription(n)}catch(e){return e}})}addIceCandidateById(t,n){return c(this,void 0,void 0,function*(){const e=this.getConnectById(t);try{yield null===e||void 0===e?void 0:e.RTCPeerIns.addIceCandidate(n)}catch(e){return e}})}},e.getMediaStream=n,e.getUseAbleDevices=t,Object.defineProperty(e,"__esModule",{value:!0})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).index={},e.react)}(this,function(e,f){"use strict";function y(e,o,a,c){return new(a=a||Promise)(function(n,t){function r(e){try{s(c.next(e))}catch(e){t(e)}}function i(e){try{s(c.throw(e))}catch(e){t(e)}}function s(e){var t;e.done?n(e.value):((t=e.value)instanceof a?t:new a(function(e){e(t)})).then(r,i)}s((c=c.apply(e,o||[])).next())})}const r=()=>y(void 0,void 0,void 0,function*(){return navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices?yield navigator.mediaDevices.enumerateDevices():(console.error("navigator.mediaDevices.enumerateDevices"),null)}),i=(e="display",r)=>{const i="display"===e?"getDisplayMedia":"getUserMedia";return n=>y(void 0,void 0,void 0,function*(){let e=null;if(navigator.mediaDevices[i]){var t=Object.assign({video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,enableBackground:!1,suppressLocalAudioPlayback:!0}},n);try{e=yield navigator.mediaDevices[i](t)}catch(e){r&&r({name:e.name||"Error",message:e.message||"内部错误"})}}else r&&r({name:"CompatibleError",message:"不支持navigator.mediaDevices"});return e})},n=e=>{e&&e.getTracks().forEach(e=>{e.stop()})};const s=(e,t)=>{let n=[];return e.forEach(e=>{n=[...n,...e.getTracks().filter(e=>t?e.kind===t:e)]}),new MediaStream(n)},o=(e,t)=>{const n={receivers(){const t=[];return e.getReceivers().forEach(e=>{e.track&&t.push(e.track)}),t},senders(){const t=[];return e.getSenders().forEach(e=>{e.track&&t.push(e.track)}),t}};return t?n[t]():[...n.receivers(),...n.senders()]};const t={transDirect:"sendrecv",configuration:{iceServers:[{urls:"stun:stun.l.google.com:19302"}]},constraints:{video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,suppressLocalAudioPlayback:!0}}};class g{constructor(e){this.state="new",this.isUseAbles={audiooutput:!1,audioinput:!1,videoinput:!1},this.streamControl={close:e=>{const t={remote:()=>{n(this.streamControl.remoteStreams),this.streamControl.remoteStreams=null},local:()=>{n(this.streamControl.localStreams),this.streamControl.localStreams=null}};e?t[e]():(t.local(),t.remote())}},this.runByt={time:null,close:()=>{this.runByt.time&&clearInterval(this.runByt.time)},run:()=>{const i=this.EventMap.get("byts");let s=null;i&&0<i.length&&(null!==i&&void 0!==i&&i.forEach(e=>{e(0)}),this.runByt.time=setInterval(()=>{var e;null!==(e=this.RTCPeerIns)&&void 0!==e&&e.getStats().then(e=>{const t={bytesReceived:0};if(e.forEach(e=>{"transport"===e.type&&(t.bytesReceived+=e.bytesReceived)}),s){const n=s.bytesReceived,r=t.bytesReceived;null!==i&&void 0!==i&&i.forEach(e=>{e(Number(((r-n)/1024).toFixed(2)))}),s=Object.assign({},t)}else s=Object.assign({},t)})},1e3))}},this.EventMap=new Map([]),this.RTCPeerIns=null,this.removeListenFn=()=>{},this.WetOption=Object.assign({},t),this.WetOption=Object.assign(Object.assign({},this.WetOption),e)}runError(t){const e=this.EventMap.get("error");null!==e&&void 0!==e&&e.forEach(e=>{e(t)})}getUseAbleDevices(){return y(this,void 0,void 0,function*(){return yield r()})}addEventListener(e,t){const n=this.EventMap.get(e);n?n.find(e=>e===t)||(n.push(t),this.EventMap.set(e,n)):this.EventMap.set(e,[t])}removeEventListener(e,t){const n=this.EventMap.get(e);var r;n&&(r=n.filter(e=>e!==t),this.EventMap.set(e,r))}captureStream(){return y(this,void 0,void 0,function*(){const e=yield r();if(e){const n=e.map(e=>e.kind);this.isUseAbles.audioinput=n.includes("audioinput"),this.isUseAbles.videoinput=n.includes("videoinput"),this.isUseAbles.audiooutput=n.includes("audiooutput")}else this.isUseAbles.audioinput=!0,this.isUseAbles.videoinput=!0,this.isUseAbles.audiooutput=!0;var t;return this.isUseAbles.audioinput||this.isUseAbles.videoinput?((t=yield i("user",e=>{this.runError(e)})({video:!!this.isUseAbles.videoinput&&this.WetOption.constraints.video,audio:!!this.isUseAbles.audioinput&&this.WetOption.constraints.audio}))&&(this.streamControl.localStreams=t),t):(this.runError({name:"StreamError",message:"音频输入设备,视频输入设备均不可用"}),null)})}connect(c){var d;return y(this,void 0,void 0,function*(){if(["sendrecv","sendonly"].includes(this.WetOption.transDirect)&&(!this.streamControl.localStreams&&!(yield this.captureStream())))return null;let n="new",r=this;Object.defineProperty(this,"state",{get(){return n},set(e){"connected"===(n=e)?r.runByt.run():["disconnected","closed","failed"].includes(e)&&r.runByt.close();const t=r.EventMap.get("state");null!==t&&void 0!==t&&t.forEach(e=>{e(n)})}});var e,t,i;if(this.state="new",this.RTCPeerIns=new RTCPeerConnection(this.WetOption.configuration),this.removeListenFn=(e=this.RTCPeerIns,t={track:e=>{e.streams&&e.streams[0]&&(this.streamControl.remoteStreams=e.streams[0])},icecandidate:t=>{if(t.candidate){const e=r.EventMap.get("ice");null!==e&&void 0!==e&&e.forEach(e=>{e(t.candidate)})}},connectionstatechange:e=>{e=e.target;this.state=e.connectionState}},e.addEventListener("track",t.track),e.addEventListener("icecandidate",t.icecandidate),e.addEventListener("connectionstatechange",t.connectionstatechange),()=>{e.removeEventListener("track",t.track),e.removeEventListener("icecandidate",t.icecandidate),e.removeEventListener("connectionstatechange",t.connectionstatechange)}),["sendrecv","sendonly"].includes(this.WetOption.transDirect)){const s=null===(d=this.streamControl)||void 0===d?void 0:d.localStreams;null!==s&&void 0!==s&&s.getTracks().forEach(e=>{var t;null!==(t=this.RTCPeerIns)&&void 0!==t&&t.addTrack(e,s)})}else try{this.RTCPeerIns.addTransceiver("video",{direction:"recvonly"}),this.RTCPeerIns.addTransceiver("audio",{direction:"recvonly"})}catch(e){const o=document.createElement("canvas"),a=o.captureStream();null!==a&&void 0!==a&&a.getTracks().forEach(e=>{var t;null!==(t=this.RTCPeerIns)&&void 0!==t&&t.addTrack(e,a)})}return c?(yield this.setRemoteSdp(c),i=yield this.RTCPeerIns.createAnswer(),yield this.RTCPeerIns.setLocalDescription(i),i):(i=yield this.RTCPeerIns.createOffer(),yield this.RTCPeerIns.setLocalDescription(i),i)})}closePeerInsTracks(){if(this.RTCPeerIns){const e={recvonly:()=>o(this.RTCPeerIns,"receivers"),sendonly:()=>o(this.RTCPeerIns,"senders"),sendrecv:()=>o(this.RTCPeerIns)},t=e[this.WetOption.transDirect]();t.forEach(e=>{null!=e&&e.stop()})}}disconnect(){this.state="closed",this.removeListenFn(),this.closePeerInsTracks(),this.RTCPeerIns=null}close(){this.disconnect(),this.streamControl.close()}setRemoteSdp(e){var t;return y(this,void 0,void 0,function*(){if(!e)return this.runError({name:"OperationError",message:"缺失sdp信息"}),void(this.state="failed");try{yield null===(t=this.RTCPeerIns)||void 0===t?void 0:t.setRemoteDescription(e)}catch(e){this.state="failed",this.runError({name:e.name||"Error",message:e.message||"内部错误"})}})}addIceCandidate(n){return y(this,void 0,void 0,function*(){if(!n)return this.runError({name:"OperationError",message:"缺失IceSdp信息"}),void(this.state="failed");const e=()=>y(this,void 0,void 0,function*(){var e;try{yield null===(e=this.RTCPeerIns)||void 0===e?void 0:e.addIceCandidate(n)}catch(e){this.state="failed",this.runError({name:e.name||"Error",message:e.message||"内部错误"})}});if(this.RTCPeerIns)e();else{const t=setInterval(()=>y(this,void 0,void 0,function*(){e(),clearInterval(t)}),1e3)}})}playRemote(t,n){return y(this,void 0,void 0,function*(){var e;this.RTCPeerIns&&(e=this.RTCPeerIns.getReceivers().map(e=>e.track),e=new MediaStream(e),null!=t&&t.setAttribute("autoplay",""),null!=t&&t.setAttribute("playsinline",""),t&&(t.srcObject=s([e],n)))})}playLocal(t,n){return y(this,void 0,void 0,function*(){this.streamControl.localStreams||(yield this.captureStream());var e=this.streamControl.localStreams;null!=t&&t.setAttribute("autoplay",""),null!=t&&t.setAttribute("playsinline",""),t&&(t.srcObject=s([e],n))})}}let a;const d=new Uint8Array(16);const c=[];for(let e=0;e<256;++e)c.push((e+256).toString(16).slice(1));var u={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function b(e,t,n){if(u.randomUUID&&!t&&!e)return u.randomUUID();const r=(e=e||{}).random||(e.rng||function(){if(!a&&!(a="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return a(d)})();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(let e=0;e<16;++e)t[n+e]=r[e];return t}return e=r,i=0,(c[e[i+0]]+c[e[i+1]]+c[e[i+2]]+c[e[i+3]]+"-"+c[e[i+4]]+c[e[i+5]]+"-"+c[e[i+6]]+c[e[i+7]]+"-"+c[e[i+8]]+c[e[i+9]]+"-"+c[e[i+10]]+c[e[i+11]]+c[e[i+12]]+c[e[i+13]]+c[e[i+14]]+c[e[i+15]]).toLowerCase();var i}e.concatStream=e=>{let t=[];return e.forEach(e=>{t=[...t,...e.getTracks()]}),new MediaStream(t)},e.default=g,e.filterStream=s,e.getMediaStream=i,e.getRtcPeerTracks=o,e.getUseAbleDevices=r,e.stopStreamTrack=n,e.streamToRecord=(e,t={audioBitsPerSecond:128e3,videoBitsPerSecond:25e5,mimeType:"video/webm"})=>{const r=t.mimeType?t.mimeType.split("/"):["webm"];let i=(new Date).getTime().toString();const n=MediaRecorder.isTypeSupported(t.mimeType||"");if(!n)throw new Error(t.mimeType+"类型不支持");const s=new MediaRecorder(e,t);s.addEventListener("dataavailable",e=>{{e=e.data;var t=i+"."+r[(null===r||void 0===r?void 0:r.length)-1];e=URL.createObjectURL(e);const n=document.createElement("a");return n.href=e,n.target="_blank",n.style.display="none",document.body.appendChild(n),n.download=t,n.click(),URL.revokeObjectURL(e),void document.body.removeChild(n)}});const o=()=>{n&&s.stop()};return{start:()=>{n&&s.start()},pause:()=>{n&&s.pause()},save:e=>{n&&(i=e||(new Date).getTime().toString(),o())},stop:o,resume:()=>{n&&s.resume()}}},e.useWetRTC=function(e){const{connectionConfig:t,name:n,isLisenByts:r}=e,[u,l]=f.useState(0),i=f.useRef(""),s=f.useRef(e.senderSdp),o=f.useRef(e.senderIce),a=f.useRef(e.onError),c=(s.current=e.senderSdp,o.current=e.senderIce,a.current=e.onError,f.useRef(null)),[v,h]=f.useState({audioinput:!1,audiooutput:!1,videoinput:!1}),p=f.useRef({audioinput:!1,audiooutput:!1,videoinput:!1}),[d,m]=f.useState("new");return f.useEffect(()=>{c.current=new g(t),c.current.addEventListener("error",e=>{a.current&&a.current(e)}),c.current.addEventListener("state",e=>{m(e)}),c.current.addEventListener("ice",e=>{o.current&&o.current(e)}),c.current.getUseAbleDevices().then(e=>{const t={audioinput:!0,audiooutput:!0,videoinput:!0};if(e){const n=e.map(e=>e.kind);t.audioinput=n.includes("audioinput"),t.audiooutput=n.includes("audiooutput"),t.videoinput=n.includes("videoinput"),p.current=Object.assign({},t)}h(()=>t)})},[]),f.useEffect(()=>{var e;"connected"===d&&r&&null!==(e=c.current)&&void 0!==e&&e.addEventListener("byts",e=>{l(e)})},[d,r]),{connect:f.useCallback(t=>y(this,void 0,void 0,function*(){var e;n?i.current="object"==typeof n?n.current:n:i.current=i.current||b(),c.current&&i.current&&((e=yield c.current.connect(t))&&s.current(e))}),[n&&"object"==typeof n?n.current:n]),disconnect:()=>{var e;null!==(e=c.current)&&void 0!==e&&e.close()},play:(e,t,n)=>{"local"===e?null!==(e=c.current)&&void 0!==e&&e.playLocal(t,n):null!==(e=c.current)&&void 0!==e&&e.playRemote(t,n)},answerAction:e=>{var t;null!==(t=c.current)&&void 0!==t&&t.setRemoteSdp(e)},byts:u,connectState:d,useAblesState:v}},Object.defineProperty(e,"__esModule",{value:!0})});
@@ -2,7 +2,11 @@
2
2
  * 获取可用设备
3
3
  * @returns Promise
4
4
  */
5
- export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[]>;
5
+ export interface ErrorMsgType {
6
+ name: string;
7
+ message: string;
8
+ }
9
+ export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[] | null>;
6
10
  /**
7
11
  * 获取支持的约束
8
12
  */
@@ -11,10 +15,11 @@ export declare const getSupportedConsitains: () => MediaTrackSupportedConstraint
11
15
  * 选择分享流(本地视频流) / 捕获用户许可的输入信号流
12
16
  * @returns Promise
13
17
  */
14
- export declare const getMediaStream: (type?: 'user' | 'display') => (constraints?: DisplayMediaStreamConstraints | MediaStreamConstraints | undefined) => Promise<{
15
- stream: MediaStream;
16
- close: () => void;
17
- }>;
18
+ export declare const getMediaStream: (type?: 'user' | 'display', onError?: ((params: ErrorMsgType) => void) | undefined) => (constraints?: MediaStreamConstraints | undefined) => Promise<MediaStream | null>;
19
+ /**
20
+ * 关闭流和轨道追踪
21
+ */
22
+ export declare const stopStreamTrack: (streams?: MediaStream | null | undefined) => void;
18
23
  /**
19
24
  * 将多个stream 整合为一个stream
20
25
  * @param options
@@ -22,10 +27,15 @@ export declare const getMediaStream: (type?: 'user' | 'display') => (constraints
22
27
  */
23
28
  export declare const concatStream: (options: MediaStream[]) => MediaStream;
24
29
  /**
25
- * 将多个stream 按'audio' 或'video'进行过滤整合
30
+ * 将多个stream 按'audio' 或'video'进行过滤整合,当type没有输出的流类型时将不会过滤
26
31
  * @param options
27
32
  * @param type
28
33
  * @returns
29
34
  */
30
35
  export declare const filterStream: (options: MediaStream[], type?: "audio" | "video" | undefined) => MediaStream;
31
- export declare const getRemoteTracks: (peerConnection: RTCPeerConnection) => MediaStreamTrack[];
36
+ export declare const getRtcPeerTracks: (peerConnection: RTCPeerConnection, type?: "receivers" | "senders" | undefined) => MediaStreamTrack[];
37
+ export declare const listenerEvent: (RTCPeerIns: RTCPeerConnection, option: {
38
+ track: (e: RTCTrackEvent) => void;
39
+ icecandidate: (e: RTCPeerConnectionIceEvent) => void;
40
+ connectionstatechange: (e: Event) => void;
41
+ }) => () => void;
@@ -1,5 +1,5 @@
1
1
  export declare const SaveFileToLocal: (blob: Blob, fileName: string) => void;
2
- export declare const createStreamToRecord: (stream: MediaStream, options?: MediaRecorderOptions) => {
2
+ export declare const streamToRecord: (stream: MediaStream, options?: MediaRecorderOptions) => {
3
3
  start: () => void;
4
4
  pause: () => void;
5
5
  save: (_fileName?: string | undefined) => void;
package/package.json CHANGED
@@ -1,38 +1,42 @@
1
1
  {
2
- "name": "@wetspace/wetrtc",
3
- "version": "3.0.0",
4
- "description": "wetrtc",
5
- "author": "tangjie <981955667@qq.com>",
6
- "license": "ISC",
7
- "main": "lib/index.js",
8
- "module": "es/index.js",
9
- "types": "es/index.d.ts",
10
- "sideEffects": [
11
- "./src/**/*.less",
12
- "./es/**/*.less",
13
- "./lib/**/*.less"
14
- ],
15
- "files": [
16
- "lib",
17
- "es"
18
- ],
19
- "publishConfig": {
20
- "registry": "https://registry.npmjs.org/",
21
- "access": "public"
22
- },
23
- "repository": {
24
- "type": "git",
25
- "url": "https://gitee.com/wetspace/wetrtc.git"
26
- },
27
- "scripts": {
28
- "start": "rollup -c -w",
29
- "build": "rm -fr lib && rm -fr es && set NODE_ENV=production&& rollup -c rollup.config.js"
30
- },
31
- "devDependencies": {
32
- "@types/uuid": "^8.3.4",
33
- "rollup": "^2.67.3"
34
- },
35
- "dependencies": {
36
- "uuid": "^9.0.0"
37
- }
2
+ "name": "@wetspace/wetrtc",
3
+ "version": "3.0.2",
4
+ "description": "wetrtc",
5
+ "author": "tangjie <981955667@qq.com>",
6
+ "license": "ISC",
7
+ "main": "lib/index.js",
8
+ "module": "es/index.js",
9
+ "types": "es/index.d.ts",
10
+ "keywords": [
11
+ "component",
12
+ "webrtc",
13
+ "hooks",
14
+ "react",
15
+ "wetrtc"
16
+ ],
17
+ "sideEffects": [
18
+ "./src/**/*.less",
19
+ "./es/**/*.less",
20
+ "./lib/**/*.less"
21
+ ],
22
+ "files": [
23
+ "lib",
24
+ "es"
25
+ ],
26
+ "publishConfig": {
27
+ "registry": "https://registry.npmjs.org/",
28
+ "access": "public"
29
+ },
30
+ "homepage": "https://wetspace.gitee.io/wetrtc/",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://gitee.com/wetspace/wetrtc.git"
34
+ },
35
+ "scripts": {
36
+ "start": "rollup -c -w",
37
+ "build": "rm -fr lib && rm -fr es && set NODE_ENV=production&& rollup -c rollup.config.js"
38
+ },
39
+ "devDependencies": {
40
+ "rollup": "^2.67.3"
41
+ }
38
42
  }