@wetspace/wetrtc 2.0.1 → 2.0.3
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 +25 -0
- package/es/core/hook.d.ts +34 -0
- package/es/core/index.d.ts +34 -32
- package/es/index.d.ts +5 -4
- package/es/index.js +1 -1
- package/es/libs/index.d.ts +7 -7
- package/es/libs/record.d.ts +1 -1
- package/lib/core/hook.d.ts +34 -0
- package/lib/core/index.d.ts +34 -32
- package/lib/index.d.ts +5 -4
- package/lib/index.js +1 -1
- package/lib/libs/index.d.ts +7 -7
- package/lib/libs/record.d.ts +1 -1
- package/package.json +44 -36
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
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. 工具库主要方法和 API
|
|
18
|
+
|
|
19
|
+
## 源码地址
|
|
20
|
+
|
|
21
|
+
[地址](https://gitee.com/wetspace/wetrtc)
|
|
22
|
+
|
|
23
|
+
## 测试体验工具(音视频通讯)
|
|
24
|
+
|
|
25
|
+
下载地址:[windows 版本]()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { WetRTCPlayer } from './index';
|
|
2
|
+
import type { WetCreateConnectOneOption, ConnectionState } from './index';
|
|
3
|
+
import type { MutableRefObject } from 'react';
|
|
4
|
+
interface UseWebRtcParams {
|
|
5
|
+
name: string | number | MutableRefObject<string | number>;
|
|
6
|
+
senderSdp: (sdp: RTCSessionDescriptionInit) => void;
|
|
7
|
+
senderIce?: (ice: RTCIceCandidate) => void;
|
|
8
|
+
connectionConfig?: {
|
|
9
|
+
configuration?: RTCConfiguration;
|
|
10
|
+
constraints?: MediaStreamConstraints;
|
|
11
|
+
};
|
|
12
|
+
onError?: (err: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}) => void;
|
|
16
|
+
}
|
|
17
|
+
declare const useWetRTC: (options: UseWebRtcParams) => {
|
|
18
|
+
connect: (transDirect?: WetCreateConnectOneOption['transDirect'], sdp?: RTCSessionDescriptionInit | undefined) => Promise<void>;
|
|
19
|
+
disconnect: () => void;
|
|
20
|
+
play: (type: 'remote' | 'local', player: WetRTCPlayer, outType?: "audio" | "video" | undefined) => void;
|
|
21
|
+
answerAction: (sdp: RTCSessionDescriptionInit) => void;
|
|
22
|
+
connectState: ConnectionState;
|
|
23
|
+
useAblesState: {
|
|
24
|
+
audioinput: boolean;
|
|
25
|
+
audiooutput: boolean;
|
|
26
|
+
videoinput: boolean;
|
|
27
|
+
};
|
|
28
|
+
useAbleRef: MutableRefObject<{
|
|
29
|
+
audioinput: boolean;
|
|
30
|
+
audiooutput: boolean;
|
|
31
|
+
videoinput: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
export { useWetRTC };
|
package/es/core/index.d.ts
CHANGED
|
@@ -5,51 +5,53 @@ export interface WetRTCConnectctItem {
|
|
|
5
5
|
state: ConnectionState;
|
|
6
6
|
uuid: string;
|
|
7
7
|
name: string;
|
|
8
|
+
streams: MediaStream | null;
|
|
9
|
+
transDirect: 'recvonly' | 'sendonly' | 'sendrecv';
|
|
8
10
|
}
|
|
9
|
-
export interface
|
|
11
|
+
export interface WetRTCItemType {
|
|
12
|
+
connect: (sdp?: RTCSessionDescriptionInit | undefined, stateChange?: ((state: ConnectionState) => void) | undefined) => Promise<RTCSessionDescriptionInit | null>;
|
|
13
|
+
play?: (player: WetRTCPlayer, type?: 'video' | 'audio') => void;
|
|
14
|
+
setRemoteSdp: (sdp: RTCSessionDescriptionInit) => void;
|
|
15
|
+
addIceCandidate: (sdp: RTCIceCandidate) => void;
|
|
16
|
+
self: WetRTCConnectctItem;
|
|
17
|
+
}
|
|
18
|
+
export interface WetCreateConnectOneOption {
|
|
10
19
|
configuration?: RTCConfiguration;
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
uuid?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
transDirect?: 'recvonly' | 'sendonly' | 'sendrecv';
|
|
23
|
+
iceCb?: (candidate: RTCIceCandidate) => void;
|
|
13
24
|
}
|
|
14
25
|
export declare class WetRTC {
|
|
15
|
-
streamControl: {
|
|
16
|
-
stream: MediaStream;
|
|
17
|
-
close: () => void;
|
|
18
|
-
} | null;
|
|
19
26
|
rtcConnectMap: Map<string | number, WetRTCConnectctItem>;
|
|
20
|
-
private
|
|
27
|
+
private constraints;
|
|
28
|
+
private streamControl;
|
|
21
29
|
private isUseAbles;
|
|
22
|
-
constructor(
|
|
23
|
-
|
|
30
|
+
constructor(options?: MediaStreamConstraints);
|
|
31
|
+
getUseAbleDevices(): Promise<MediaDeviceInfo[] | null>;
|
|
24
32
|
private captureStream;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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>;
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* 关闭连接
|
|
41
|
-
*/
|
|
42
|
-
disconnect(uuid?: string): void;
|
|
33
|
+
closeCaptureStream(): void;
|
|
34
|
+
playLocalStream(player: WetRTCPlayer, type?: 'video' | 'audio'): void;
|
|
35
|
+
createConnectOne(option: Omit<WetCreateConnectOneOption, 'transDirect'> & {
|
|
36
|
+
transDirect: 'sendonly';
|
|
37
|
+
}): Omit<WetRTCItemType, 'play'>;
|
|
38
|
+
createConnectOne(option: Omit<WetCreateConnectOneOption, 'transDirect'> & {
|
|
39
|
+
transDirect?: 'recvonly' | 'sendrecv';
|
|
40
|
+
}): WetRTCItemType;
|
|
43
41
|
/**
|
|
44
42
|
* 获取特定的连接
|
|
45
43
|
*/
|
|
46
44
|
getConnectById(uuid: string): WetRTCConnectctItem | undefined;
|
|
47
45
|
/**
|
|
48
|
-
*
|
|
46
|
+
* 关闭流的
|
|
49
47
|
*/
|
|
50
|
-
|
|
48
|
+
private closeStream;
|
|
49
|
+
/**
|
|
50
|
+
* 关闭连接
|
|
51
|
+
*/
|
|
52
|
+
disconnect(uuid?: string): void;
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
54
|
+
* 关闭所有的流
|
|
53
55
|
*/
|
|
54
|
-
|
|
56
|
+
close(): void;
|
|
55
57
|
}
|
package/es/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { getUseAbleDevices, getMediaStream } from './libs';
|
|
2
|
-
import {
|
|
3
|
-
import { WetRTC } from './core';
|
|
4
|
-
|
|
1
|
+
import { getUseAbleDevices, getMediaStream, concatStream, filterStream, getRtcPeerTracks } 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, };
|
|
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(
|
|
1
|
+
import{useRef,useState,useEffect,useCallback}from"react";function __awaiter(e,s,a,c){return new(a=a||Promise)(function(n,t){function r(e){try{o(c.next(e))}catch(e){t(e)}}function i(e){try{o(c.throw(e))}catch(e){t(e)}}function o(e){var t;e.done?n(e.value):((t=e.value)instanceof a?t:new a(function(e){e(t)})).then(r,i)}o((c=c.apply(e,s||[])).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")=>{const r="display"===e?"getDisplayMedia":"getUserMedia";return n=>__awaiter(void 0,void 0,void 0,function*(){if(navigator.mediaDevices[r]){var e=n||{video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,enableBackground:!1,suppressLocalAudioPlayback:!0}};const t=yield navigator.mediaDevices[r](e);return{streams:t,close(){t.getTracks().forEach(e=>{e.stop()})}}}return console.error("不支持mediaDevices"),null})},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(){return e.getReceivers().map(e=>e.track)},senders(){return e.getSenders().map(e=>e.track)}};return t?n[t]():[...n.receivers(),...n.senders()]},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 o=new MediaRecorder(e,t);o.addEventListener("dataavailable",e=>{SaveFileToLocal(e.data,r+"."+n[(null===n||void 0===n?void 0:n.length)-1])});const s=()=>{i&&o.stop()};return{start:()=>{i&&o.start()},pause:()=>{i&&o.pause()},save:e=>{i&&(r=e||(new Date).getTime().toString(),s())},stop:s,resume:()=>{i&&o.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,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)}const DefaultConfiguration={iceServers:[{urls:"stun:stun.l.google.com:19302"}]};class WetRTC{constructor(e){this.rtcConnectMap=new Map,this.constraints={video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,suppressLocalAudioPlayback:!0}},this.streamControl=null,this.isUseAbles={audiooutput:!1,audioinput:!1,videoinput:!1},e&&(this.constraints=e),getUseAbleDevices().then(e=>{if(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")}else this.isUseAbles.audioinput=!0,this.isUseAbles.videoinput=!0,this.isUseAbles.audiooutput=!0})}getUseAbleDevices(){return __awaiter(this,void 0,void 0,function*(){return yield getUseAbleDevices()})}captureStream(){return __awaiter(this,void 0,void 0,function*(){var e;return this.isUseAbles.audioinput||this.isUseAbles.videoinput?((e=yield getMediaStream("user")({video:!!this.isUseAbles.videoinput&&this.constraints.audio,audio:!!this.isUseAbles.audioinput&&this.constraints.audio}))&&(this.streamControl=e),e):null})}closeCaptureStream(){var e;null!==(e=this.streamControl)&&void 0!==e&&e.close(),this.streamControl=null}playLocalStream(e,t){this.streamControl&&e&&(e.setAttribute("autoPlay",""),e.setAttribute("playsInline",""),e.srcObject=filterStream([this.streamControl.streams],t))}createConnectOne(e){let i="new";const{configuration:t,uuid:n,name:r,transDirect:d,iceCb:o}=e||{};e=t||DefaultConfiguration;const s=d||"sendrecv",a=n||v4(),c=new RTCPeerConnection(e),u={RTCPeerIns:c,state:"new",name:r||a,uuid:"",streams:null,transDirect:s};return{connect:(n,r)=>__awaiter(this,void 0,void 0,function*(){var e;if(["sendrecv","sendonly"].includes(s)&&!this.streamControl&&!(yield this.captureStream()))return null;if(u.uuid=a,this.rtcConnectMap.set(a,u),c.addEventListener("track",e=>{e.streams&&e.streams[0]&&(u.streams=e.streams[0])}),Object.defineProperty(u,"state",{get(){return i},set(e){i=e,r&&r(e)}}),c.addEventListener("icecandidate",e=>{e.candidate&&o&&o(e.candidate)}),c.addEventListener("connectionstatechange",e=>{e=e.target;u.state=e.connectionState}),["sendrecv","sendonly"].includes(s)){const t=null===(e=this.streamControl)||void 0===e?void 0:e.streams;null!==t&&void 0!==t&&t.getTracks().forEach(e=>{c.addTrack(e,t)})}else try{c.addTransceiver("video",{direction:"recvonly"}),c.addTransceiver("audio",{direction:"recvonly"})}catch(e){u.state="failed"}return n?(yield c.setRemoteDescription(n),e=yield c.createAnswer(),yield c.setLocalDescription(e),e):(e=yield c.createOffer(),yield c.setLocalDescription(e),e)}),play:"sendonly"===s?void 0:(e,t)=>{var n;u.uuid&&(n=c.getReceivers().map(e=>e.track),n=new MediaStream(n),null!=e&&e.setAttribute("autoplay",""),null!=e&&e.setAttribute("playsinline",""),e&&(e.srcObject=filterStream([n],t)))},self:u,setRemoteSdp:e=>__awaiter(this,void 0,void 0,function*(){try{c.setRemoteDescription(e)}catch(e){console.error(e),u.state="failed"}}),addIceCandidate:e=>{c.addIceCandidate(e)}}}getConnectById(e){return this.rtcConnectMap.get(e)}closeStream(e){if(e&&e.RTCPeerIns){const t={recvonly:()=>getRtcPeerTracks(e.RTCPeerIns,"receivers"),sendonly:()=>getRtcPeerTracks(e.RTCPeerIns,"senders"),sendrecv:()=>getRtcPeerTracks(e.RTCPeerIns)},n=t[e.transDirect]();n.forEach(e=>{null!=e&&e.stop()}),e.streams=null}}disconnect(e){if(e){if(this.rtcConnectMap.has(e)){const t=this.getConnectById(e);t.state="closed",this.closeStream(t),null!==t&&void 0!==t&&t.RTCPeerIns.close(),this.rtcConnectMap.delete(e)}}else this.rtcConnectMap.forEach(e=>{e.state="closed",this.closeStream(e),e.RTCPeerIns.close()}),this.rtcConnectMap.clear()}close(){this.disconnect(),this.closeCaptureStream()}}const useWetRTC=e=>{const{senderSdp:r,senderIce:i,connectionConfig:o,name:s,onError:a}=e,c=useRef(null),u=useRef(null),[t,l]=useState({audioinput:!1,audiooutput:!1,videoinput:!1}),d=useRef({audioinput:!1,audiooutput:!1,videoinput:!1}),[n,p]=useState("new");useEffect(()=>{c.current=new WetRTC(o&&(null===o||void 0===o?void 0:o.constraints)),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"),d.current=Object.assign({},t)}l(()=>t)})},[]);return{connect:useCallback((t="sendrecv",n)=>__awaiter(void 0,void 0,void 0,function*(){var e="object"==typeof s?s.current:s;c.current&&e&&(u.current=c.current.createConnectOne({transDirect:t,iceCb:i,configuration:o&&o.configuration}),(e=yield u.current.connect(n,e=>{p(()=>e)}))?r(e):(console.error("sdp获取失败,连接错误"),a&&a({code:"error",message:"sdp获取失败,连接错误"})))}),["object"==typeof s?s.current:s]),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.playLocalStream(t,n):u.current&&u.current.play&&u.current.play(t,n)},answerAction:e=>{var t;null!==(t=u.current)&&void 0!==t&&t.setRemoteSdp(e)},connectState:n,useAblesState:t,useAbleRef:d}};export{concatStream,WetRTC as default,filterStream,getMediaStream,getRtcPeerTracks,getUseAbleDevices,streamToRecord,useWetRTC};
|
package/es/libs/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 获取可用设备
|
|
3
3
|
* @returns Promise
|
|
4
4
|
*/
|
|
5
|
-
export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[]>;
|
|
5
|
+
export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[] | null>;
|
|
6
6
|
/**
|
|
7
7
|
* 获取支持的约束
|
|
8
8
|
*/
|
|
@@ -11,10 +11,10 @@ export declare const getSupportedConsitains: () => MediaTrackSupportedConstraint
|
|
|
11
11
|
* 选择分享流(本地视频流) / 捕获用户许可的输入信号流
|
|
12
12
|
* @returns Promise
|
|
13
13
|
*/
|
|
14
|
-
export declare const getMediaStream: (type?: 'user' | 'display') => (constraints?:
|
|
15
|
-
|
|
16
|
-
close
|
|
17
|
-
}>;
|
|
14
|
+
export declare const getMediaStream: (type?: 'user' | 'display') => (constraints?: MediaStreamConstraints | undefined) => Promise<{
|
|
15
|
+
streams: MediaStream;
|
|
16
|
+
close(): void;
|
|
17
|
+
} | null>;
|
|
18
18
|
/**
|
|
19
19
|
* 将多个stream 整合为一个stream
|
|
20
20
|
* @param options
|
|
@@ -22,10 +22,10 @@ export declare const getMediaStream: (type?: 'user' | 'display') => (constraints
|
|
|
22
22
|
*/
|
|
23
23
|
export declare const concatStream: (options: MediaStream[]) => MediaStream;
|
|
24
24
|
/**
|
|
25
|
-
* 将多个stream 按'audio' 或'video'
|
|
25
|
+
* 将多个stream 按'audio' 或'video'进行过滤整合,当type没有输出的流类型时将不会过滤
|
|
26
26
|
* @param options
|
|
27
27
|
* @param type
|
|
28
28
|
* @returns
|
|
29
29
|
*/
|
|
30
30
|
export declare const filterStream: (options: MediaStream[], type?: "audio" | "video" | undefined) => MediaStream;
|
|
31
|
-
export declare const
|
|
31
|
+
export declare const getRtcPeerTracks: (peerConnection: RTCPeerConnection, type?: "receivers" | "senders" | undefined) => (MediaStreamTrack | null)[];
|
package/es/libs/record.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const SaveFileToLocal: (blob: Blob, fileName: string) => void;
|
|
2
|
-
export declare const
|
|
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,34 @@
|
|
|
1
|
+
import { WetRTCPlayer } from './index';
|
|
2
|
+
import type { WetCreateConnectOneOption, ConnectionState } from './index';
|
|
3
|
+
import type { MutableRefObject } from 'react';
|
|
4
|
+
interface UseWebRtcParams {
|
|
5
|
+
name: string | number | MutableRefObject<string | number>;
|
|
6
|
+
senderSdp: (sdp: RTCSessionDescriptionInit) => void;
|
|
7
|
+
senderIce?: (ice: RTCIceCandidate) => void;
|
|
8
|
+
connectionConfig?: {
|
|
9
|
+
configuration?: RTCConfiguration;
|
|
10
|
+
constraints?: MediaStreamConstraints;
|
|
11
|
+
};
|
|
12
|
+
onError?: (err: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}) => void;
|
|
16
|
+
}
|
|
17
|
+
declare const useWetRTC: (options: UseWebRtcParams) => {
|
|
18
|
+
connect: (transDirect?: WetCreateConnectOneOption['transDirect'], sdp?: RTCSessionDescriptionInit | undefined) => Promise<void>;
|
|
19
|
+
disconnect: () => void;
|
|
20
|
+
play: (type: 'remote' | 'local', player: WetRTCPlayer, outType?: "audio" | "video" | undefined) => void;
|
|
21
|
+
answerAction: (sdp: RTCSessionDescriptionInit) => void;
|
|
22
|
+
connectState: ConnectionState;
|
|
23
|
+
useAblesState: {
|
|
24
|
+
audioinput: boolean;
|
|
25
|
+
audiooutput: boolean;
|
|
26
|
+
videoinput: boolean;
|
|
27
|
+
};
|
|
28
|
+
useAbleRef: MutableRefObject<{
|
|
29
|
+
audioinput: boolean;
|
|
30
|
+
audiooutput: boolean;
|
|
31
|
+
videoinput: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
export { useWetRTC };
|
package/lib/core/index.d.ts
CHANGED
|
@@ -5,51 +5,53 @@ export interface WetRTCConnectctItem {
|
|
|
5
5
|
state: ConnectionState;
|
|
6
6
|
uuid: string;
|
|
7
7
|
name: string;
|
|
8
|
+
streams: MediaStream | null;
|
|
9
|
+
transDirect: 'recvonly' | 'sendonly' | 'sendrecv';
|
|
8
10
|
}
|
|
9
|
-
export interface
|
|
11
|
+
export interface WetRTCItemType {
|
|
12
|
+
connect: (sdp?: RTCSessionDescriptionInit | undefined, stateChange?: ((state: ConnectionState) => void) | undefined) => Promise<RTCSessionDescriptionInit | null>;
|
|
13
|
+
play?: (player: WetRTCPlayer, type?: 'video' | 'audio') => void;
|
|
14
|
+
setRemoteSdp: (sdp: RTCSessionDescriptionInit) => void;
|
|
15
|
+
addIceCandidate: (sdp: RTCIceCandidate) => void;
|
|
16
|
+
self: WetRTCConnectctItem;
|
|
17
|
+
}
|
|
18
|
+
export interface WetCreateConnectOneOption {
|
|
10
19
|
configuration?: RTCConfiguration;
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
uuid?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
transDirect?: 'recvonly' | 'sendonly' | 'sendrecv';
|
|
23
|
+
iceCb?: (candidate: RTCIceCandidate) => void;
|
|
13
24
|
}
|
|
14
25
|
export declare class WetRTC {
|
|
15
|
-
streamControl: {
|
|
16
|
-
stream: MediaStream;
|
|
17
|
-
close: () => void;
|
|
18
|
-
} | null;
|
|
19
26
|
rtcConnectMap: Map<string | number, WetRTCConnectctItem>;
|
|
20
|
-
private
|
|
27
|
+
private constraints;
|
|
28
|
+
private streamControl;
|
|
21
29
|
private isUseAbles;
|
|
22
|
-
constructor(
|
|
23
|
-
|
|
30
|
+
constructor(options?: MediaStreamConstraints);
|
|
31
|
+
getUseAbleDevices(): Promise<MediaDeviceInfo[] | null>;
|
|
24
32
|
private captureStream;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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>;
|
|
38
|
-
};
|
|
39
|
-
/**
|
|
40
|
-
* 关闭连接
|
|
41
|
-
*/
|
|
42
|
-
disconnect(uuid?: string): void;
|
|
33
|
+
closeCaptureStream(): void;
|
|
34
|
+
playLocalStream(player: WetRTCPlayer, type?: 'video' | 'audio'): void;
|
|
35
|
+
createConnectOne(option: Omit<WetCreateConnectOneOption, 'transDirect'> & {
|
|
36
|
+
transDirect: 'sendonly';
|
|
37
|
+
}): Omit<WetRTCItemType, 'play'>;
|
|
38
|
+
createConnectOne(option: Omit<WetCreateConnectOneOption, 'transDirect'> & {
|
|
39
|
+
transDirect?: 'recvonly' | 'sendrecv';
|
|
40
|
+
}): WetRTCItemType;
|
|
43
41
|
/**
|
|
44
42
|
* 获取特定的连接
|
|
45
43
|
*/
|
|
46
44
|
getConnectById(uuid: string): WetRTCConnectctItem | undefined;
|
|
47
45
|
/**
|
|
48
|
-
*
|
|
46
|
+
* 关闭流的
|
|
49
47
|
*/
|
|
50
|
-
|
|
48
|
+
private closeStream;
|
|
49
|
+
/**
|
|
50
|
+
* 关闭连接
|
|
51
|
+
*/
|
|
52
|
+
disconnect(uuid?: string): void;
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
54
|
+
* 关闭所有的流
|
|
53
55
|
*/
|
|
54
|
-
|
|
56
|
+
close(): void;
|
|
55
57
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { getUseAbleDevices, getMediaStream } from './libs';
|
|
2
|
-
import {
|
|
3
|
-
import { WetRTC } from './core';
|
|
4
|
-
|
|
1
|
+
import { getUseAbleDevices, getMediaStream, concatStream, filterStream, getRtcPeerTracks } 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, };
|
|
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
|
|
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,v){"use strict";function m(e,s,a,c){return new(a=a||Promise)(function(n,t){function i(e){try{o(c.next(e))}catch(e){t(e)}}function r(e){try{o(c.throw(e))}catch(e){t(e)}}function o(e){var t;e.done?n(e.value):((t=e.value)instanceof a?t:new a(function(e){e(t)})).then(i,r)}o((c=c.apply(e,s||[])).next())})}const t=()=>m(void 0,void 0,void 0,function*(){return navigator.mediaDevices&&navigator.mediaDevices.enumerateDevices?yield navigator.mediaDevices.enumerateDevices():(console.error("navigator.mediaDevices.enumerateDevices"),null)}),n=(e="display")=>{const i="display"===e?"getDisplayMedia":"getUserMedia";return n=>m(void 0,void 0,void 0,function*(){if(navigator.mediaDevices[i]){var e=n||{video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,enableBackground:!1,suppressLocalAudioPlayback:!0}};const t=yield navigator.mediaDevices[i](e);return{streams:t,close(){t.getTracks().forEach(e=>{e.stop()})}}}return console.error("不支持mediaDevices"),null})};const l=(e,t)=>{let n=[];return e.forEach(e=>{n=[...n,...e.getTracks().filter(e=>t?e.kind===t:e)]}),new MediaStream(n)},i=(e,t)=>{const n={receivers(){return e.getReceivers().map(e=>e.track)},senders(){return e.getSenders().map(e=>e.track)}};return t?n[t]():[...n.receivers(),...n.senders()]};let o;const c=new Uint8Array(16);const s=[];for(let e=0;e<256;++e)s.push((e+256).toString(16).slice(1));var a={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function p(e,t,n){if(a.randomUUID&&!t&&!e)return a.randomUUID();const i=(e=e||{}).random||(e.rng||function(){if(!o&&!(o="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 o(c)})();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,r=0,(s[e[r+0]]+s[e[r+1]]+s[e[r+2]]+s[e[r+3]]+"-"+s[e[r+4]]+s[e[r+5]]+"-"+s[e[r+6]]+s[e[r+7]]+"-"+s[e[r+8]]+s[e[r+9]]+"-"+s[e[r+10]]+s[e[r+11]]+s[e[r+12]]+s[e[r+13]]+s[e[r+14]]+s[e[r+15]]).toLowerCase();var r}const f={iceServers:[{urls:"stun:stun.l.google.com:19302"}]};class h{constructor(e){this.rtcConnectMap=new Map,this.constraints={video:!0,audio:{noiseSuppression:!0,echoCancellation:!0,suppressLocalAudioPlayback:!0}},this.streamControl=null,this.isUseAbles={audiooutput:!1,audioinput:!1,videoinput:!1},e&&(this.constraints=e),t().then(e=>{if(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")}else this.isUseAbles.audioinput=!0,this.isUseAbles.videoinput=!0,this.isUseAbles.audiooutput=!0})}getUseAbleDevices(){return m(this,void 0,void 0,function*(){return yield t()})}captureStream(){return m(this,void 0,void 0,function*(){var e;return this.isUseAbles.audioinput||this.isUseAbles.videoinput?((e=yield n("user")({video:!!this.isUseAbles.videoinput&&this.constraints.audio,audio:!!this.isUseAbles.audioinput&&this.constraints.audio}))&&(this.streamControl=e),e):null})}closeCaptureStream(){var e;null!==(e=this.streamControl)&&void 0!==e&&e.close(),this.streamControl=null}playLocalStream(e,t){this.streamControl&&e&&(e.setAttribute("autoPlay",""),e.setAttribute("playsInline",""),e.srcObject=l([this.streamControl.streams],t))}createConnectOne(e){let r="new";const{configuration:t,uuid:n,name:i,transDirect:u,iceCb:o}=e||{};e=t||f;const s=u||"sendrecv",a=n||p(),c=new RTCPeerConnection(e),d={RTCPeerIns:c,state:"new",name:i||a,uuid:"",streams:null,transDirect:s};return{connect:(n,i)=>m(this,void 0,void 0,function*(){var e;if(["sendrecv","sendonly"].includes(s)&&!this.streamControl&&!(yield this.captureStream()))return null;if(d.uuid=a,this.rtcConnectMap.set(a,d),c.addEventListener("track",e=>{e.streams&&e.streams[0]&&(d.streams=e.streams[0])}),Object.defineProperty(d,"state",{get(){return r},set(e){r=e,i&&i(e)}}),c.addEventListener("icecandidate",e=>{e.candidate&&o&&o(e.candidate)}),c.addEventListener("connectionstatechange",e=>{e=e.target;d.state=e.connectionState}),["sendrecv","sendonly"].includes(s)){const t=null===(e=this.streamControl)||void 0===e?void 0:e.streams;null!==t&&void 0!==t&&t.getTracks().forEach(e=>{c.addTrack(e,t)})}else try{c.addTransceiver("video",{direction:"recvonly"}),c.addTransceiver("audio",{direction:"recvonly"})}catch(e){d.state="failed"}return n?(yield c.setRemoteDescription(n),e=yield c.createAnswer(),yield c.setLocalDescription(e),e):(e=yield c.createOffer(),yield c.setLocalDescription(e),e)}),play:"sendonly"===s?void 0:(e,t)=>{var n;d.uuid&&(n=c.getReceivers().map(e=>e.track),n=new MediaStream(n),null!=e&&e.setAttribute("autoplay",""),null!=e&&e.setAttribute("playsinline",""),e&&(e.srcObject=l([n],t)))},self:d,setRemoteSdp:e=>m(this,void 0,void 0,function*(){try{c.setRemoteDescription(e)}catch(e){console.error(e),d.state="failed"}}),addIceCandidate:e=>{c.addIceCandidate(e)}}}getConnectById(e){return this.rtcConnectMap.get(e)}closeStream(e){if(e&&e.RTCPeerIns){const t={recvonly:()=>i(e.RTCPeerIns,"receivers"),sendonly:()=>i(e.RTCPeerIns,"senders"),sendrecv:()=>i(e.RTCPeerIns)},n=t[e.transDirect]();n.forEach(e=>{null!=e&&e.stop()}),e.streams=null}}disconnect(e){if(e){if(this.rtcConnectMap.has(e)){const t=this.getConnectById(e);t.state="closed",this.closeStream(t),null!==t&&void 0!==t&&t.RTCPeerIns.close(),this.rtcConnectMap.delete(e)}}else this.rtcConnectMap.forEach(e=>{e.state="closed",this.closeStream(e),e.RTCPeerIns.close()}),this.rtcConnectMap.clear()}close(){this.disconnect(),this.closeCaptureStream()}}e.concatStream=e=>{let t=[];return e.forEach(e=>{t=[...t,...e.getTracks()]}),new MediaStream(t)},e.default=h,e.filterStream=l,e.getMediaStream=n,e.getRtcPeerTracks=i,e.getUseAbleDevices=t,e.streamToRecord=(e,t={audioBitsPerSecond:128e3,videoBitsPerSecond:25e5,mimeType:"video/webm"})=>{const i=t.mimeType?t.mimeType.split("/"):["webm"];let r=(new Date).getTime().toString();const n=MediaRecorder.isTypeSupported(t.mimeType||"");if(!n)throw new Error(t.mimeType+"类型不支持");const o=new MediaRecorder(e,t);o.addEventListener("dataavailable",e=>{{e=e.data;var t=r+"."+i[(null===i||void 0===i?void 0:i.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 s=()=>{n&&o.stop()};return{start:()=>{n&&o.start()},pause:()=>{n&&o.pause()},save:e=>{n&&(r=e||(new Date).getTime().toString(),s())},stop:s,resume:()=>{n&&o.resume()}}},e.useWetRTC=e=>{const{senderSdp:i,senderIce:r,connectionConfig:o,name:s,onError:a}=e,c=v.useRef(null),d=v.useRef(null),[t,l]=v.useState({audioinput:!1,audiooutput:!1,videoinput:!1}),u=v.useRef({audioinput:!1,audiooutput:!1,videoinput:!1}),[n,p]=v.useState("new");v.useEffect(()=>{c.current=new h(o&&(null===o||void 0===o?void 0:o.constraints)),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"),u.current=Object.assign({},t)}l(()=>t)})},[]);return{connect:v.useCallback((t="sendrecv",n)=>m(void 0,void 0,void 0,function*(){var e="object"==typeof s?s.current:s;c.current&&e&&(d.current=c.current.createConnectOne({transDirect:t,iceCb:r,configuration:o&&o.configuration}),(e=yield d.current.connect(n,e=>{p(()=>e)}))?i(e):(console.error("sdp获取失败,连接错误"),a&&a({code:"error",message:"sdp获取失败,连接错误"})))}),["object"==typeof s?s.current:s]),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.playLocalStream(t,n):d.current&&d.current.play&&d.current.play(t,n)},answerAction:e=>{var t;null!==(t=d.current)&&void 0!==t&&t.setRemoteSdp(e)},connectState:n,useAblesState:t,useAbleRef:u}},Object.defineProperty(e,"__esModule",{value:!0})});
|
package/lib/libs/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 获取可用设备
|
|
3
3
|
* @returns Promise
|
|
4
4
|
*/
|
|
5
|
-
export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[]>;
|
|
5
|
+
export declare const getUseAbleDevices: () => Promise<MediaDeviceInfo[] | null>;
|
|
6
6
|
/**
|
|
7
7
|
* 获取支持的约束
|
|
8
8
|
*/
|
|
@@ -11,10 +11,10 @@ export declare const getSupportedConsitains: () => MediaTrackSupportedConstraint
|
|
|
11
11
|
* 选择分享流(本地视频流) / 捕获用户许可的输入信号流
|
|
12
12
|
* @returns Promise
|
|
13
13
|
*/
|
|
14
|
-
export declare const getMediaStream: (type?: 'user' | 'display') => (constraints?:
|
|
15
|
-
|
|
16
|
-
close
|
|
17
|
-
}>;
|
|
14
|
+
export declare const getMediaStream: (type?: 'user' | 'display') => (constraints?: MediaStreamConstraints | undefined) => Promise<{
|
|
15
|
+
streams: MediaStream;
|
|
16
|
+
close(): void;
|
|
17
|
+
} | null>;
|
|
18
18
|
/**
|
|
19
19
|
* 将多个stream 整合为一个stream
|
|
20
20
|
* @param options
|
|
@@ -22,10 +22,10 @@ export declare const getMediaStream: (type?: 'user' | 'display') => (constraints
|
|
|
22
22
|
*/
|
|
23
23
|
export declare const concatStream: (options: MediaStream[]) => MediaStream;
|
|
24
24
|
/**
|
|
25
|
-
* 将多个stream 按'audio' 或'video'
|
|
25
|
+
* 将多个stream 按'audio' 或'video'进行过滤整合,当type没有输出的流类型时将不会过滤
|
|
26
26
|
* @param options
|
|
27
27
|
* @param type
|
|
28
28
|
* @returns
|
|
29
29
|
*/
|
|
30
30
|
export declare const filterStream: (options: MediaStream[], type?: "audio" | "video" | undefined) => MediaStream;
|
|
31
|
-
export declare const
|
|
31
|
+
export declare const getRtcPeerTracks: (peerConnection: RTCPeerConnection, type?: "receivers" | "senders" | undefined) => (MediaStreamTrack | null)[];
|
package/lib/libs/record.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const SaveFileToLocal: (blob: Blob, fileName: string) => void;
|
|
2
|
-
export declare const
|
|
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,46 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
2
|
+
"name": "@wetspace/wetrtc",
|
|
3
|
+
"version": "2.0.3",
|
|
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
|
+
"@types/uuid": "^8.3.4",
|
|
41
|
+
"rollup": "^2.67.3"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"uuid": "^9.0.0"
|
|
45
|
+
}
|
|
38
46
|
}
|