react源码教学:web技术分享React版本anyRTC示例对等连接
react源码教学:web技术分享React版本anyRTC示例对等连接
前言大家好,笔者以往发布过很多关于WebRTC的知识,和很多基于anyRTC WebSDK开发的音视频通讯示例,收获了很多开发人员的一致好评,在以往的示例中笔者是基于Vue框架进行编写,后台有小伙伴私信笔者,希望笔者出一个基于React框架的音视频示例,笔者在收到私信的第一时间就开始着手编写,现在把它发布出来供大家参考。
开发准备在开始写代码之前还需要做一些准备工作,如果你之前没有使用过 anyRTC Web SDK 这里还需要花几分钟时间准备一下, 详见:[开发前期准备](https://juejin.cn/post/6956045461396389901/)
创建React程序- 全局安装
```
npm install -g create-react-app
```
- 通过 create-react-app 项目名称(注:项目名称不能有大写)
```
create-react-app peerconnection
```
下载并引入项目依赖
```javascript
npm install ar-rtc-sdk@4.1.3 -S
```
- 在App.js文件中引入
```javascript
import React { Component } from 'react';
import ArRTC from 'ar-rtc-sdk';
import Config from '../anyrtc_config';
import './App.css';
```
定义初始state数据
```
export default class App extends Component {
state = {
channel: ''
isLogin: false
rtcClient: null
videoDevices: null
audioDevices: null
audioTrack: null
videoTrack: null
list: []
}
}
```
创建rtcClient对象
- 通过 [`ArRTC.createClient`](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client) 创建的客户端对象。
```
componentDidMount() {
this.createRtcClient();
}
createRtcClient = () => {
const config = { mode: "rtc" codec: "h264" };
const rtcClient = ArRTC.createClient(config);
// 更新state后,开始监听远端用户发布和取消发布音视频
this.setState({ rtcClient } () => {
this.listenUserPublished();
this.listenUserUnpublished();
});
}
```
监听远端用户发布和取消发布音视频
- [user-published](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#user-published) 该回调通知远端用户发布了新的音频轨道或者视频轨道,你可以在该回调中订阅并播放远端用户的音视频轨道。详见 [subscribe](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#subscribe) 和 [RemoteTrack.play](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_remote_track#play)。
- [user-unpublishe](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#user-unpublished) 该回调通知远端用户取消发布了音频或视频轨道。
```
listenUserUnpublished = () => {
const { rtcClient list } = this.state;
rtcClient.on("user-unpublished" async (user mediaType) => {
if (mediaType === 'video') {
const index = list.findIndex(item => item.uid === user.uid);
if (index !== -1) {
list.splice(index 1);
this.setState({ list });
}
}
});
};
listenUserPublished = () => {
const { rtcClient list } = this.state;
rtcClient.on("user-published" async (user mediaType) => {
await rtcClient.subscribe(user mediaType);
if (mediaType === 'video') {
list.push(user);
this.setState({ list } () => {
user.videoTrack.play('distal' user.uid);
});
} else if (mediaType === 'audio') {
user.audioTrack.play();
}
});
}
```
加入频道
- [join](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#join) 加入频道 该方法让用户加入通话频道,在同一个频道内的用户可以互相通话。
- 调用该方法加入频道时,本地会触发 [ArRTCClient.on("connection-state-change")](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#connection-state-change) 回调。
- 通信场景下的用户和直播场景下的主播加入频道后,远端会触发 [ArRTCClient.on("user-joined")](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#user-joined) 回调。
```
join = () => {
const { rtcClient channel } = this.state;
const { appid uid } = Config;
rtcClient.join(appid channel null uid).then((uid) => {
this.setState({ isLogin: true } async () => {
await this.getDevices();
await this.createTrack();
const { videoTrack audioTrack } = this.state;
videoTrack && videoTrack.play('local');
audioTrack && rtcClient.publish(audioTrack);
videoTrack && rtcClient.publish(videoTrack);
});
}).catch(e => {
alert('加入频道失败');
});
}
```
获取本地摄像头和麦克风并创建音视频轨道
- [getCameras](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc#getcameras) 该方法枚举可用的视频输入设备,比如摄像头,调用成功后 SDK 会通过 [MediaDeviceInfo](https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo) 对象返回可用的视频输入设备。
- [getMicrophones](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc#getMicrophones) 该方法枚举可用的音频输入设备,比如麦克风,调用成功后SDK会通过 [MediaDeviceInfo](https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo) 对象返回可用的音频输入设备。
- [createMicrophoneAudioTrack](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc#createmicrophoneaudiotrack) 通过麦克风采集的音频创建一个音频轨道。
- [createCameraVideoTrack](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc#createcameravideotrack) 通过摄像头采集的视频创建一个视频轨道。
```
getDevices = async () => {
const [ videoDevices audioDevices ] = await Promise.all([
ArRTC.getCameras()
ArRTC.getMicrophones()
]);
this.setState({
videoDevices
audioDevices
});
}
createTrack = async () => {
this.setState({
audioTrack: await ArRTC.createMicrophoneAudioTrack()
});
if (this.state.videoDevices?.length) {
this.setState({
videoTrack: await ArRTC.createCameraVideoTrack()
});
}
}
```
挂断离开
- [leave](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#leave) 调用该方法离开频道时,本地会触发 [ArRTCClient.on("connection-state-change")](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#connection-state-change) 回调。
- 通信场景下的用户和直播场景下的主播离开频道后,远端会触发 [ArRTCClient.on("user-left")](https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/interfaces/i_rtc_client#user-left) 回调。
```
hangUp = () => {
const { videoTrack rtcClient } = this.state;
videoTrack && videoTrack.stop();
rtcClient.leave();
this.setState({
channel: ''
isLogin: false
videoDevices: null
audioDevices: null
audioTrack: null
videoTrack: null
});
}
```
获取频道输入框的值
```
channelInputChange = (e) => {
this.setState({
channel: e.target.value
});
}
```
render函数
```
render() {
const { isLogin channel list } = this.state;
return (
<div id='container'>
<div className='title'>
<a href="https://www.anyrtc.io/" target='_blank'>anyRTC samples</a>
<span>Peer connection</span>
</div>
<div className='instructions'>The local user id is <span className='userId'>{ Config.uid }</span></div>
<div id='playContainer'>
{ isLogin && <div id='local'></div> }
{ isLogin && list.map((user) => {
return (<div id={ 'distal' user.uid } className='distal'></div>)
}) }
</div>
{ isLogin && <button onClick={ this.hangUp } className='btn'>Hang Up</button> }
{ !isLogin && <input type="text" value={ channel } ref={ this.channelInput } onChange={ this.channelInputChange } className='channelInput' placeholder='Please enter the channel'/> }
{ !isLogin && <button onClick={ this.join } disabled={ !channel } className='joinBtn'>join</button> }
<div className='instructions'>View the console to see logging. The MediaStream object localStream and the RTCPeerConnection objects pc1 and pc2 are in global scope so you can inspect them in the console as well.</div>
<div className='instructions'>For more information about anyRTC WebRTC see Getting Started With <a href="https://docs.anyrtc.io/cn/Video/api-ref/rtc_web/overview" target='_blank'>anyRTC</a></div>
<a href="https://github.com/941725931/webRTC_React" id="viewSource" target='_blank'>View source on GitHub</a>
</div>
);
}
```
CSS
```
#container {
margin: 0 auto 0 auto;
max-width: 60em;
padding: 1em 1.5em 1.3em 1.5em;
}
.title {
border-bottom: 1px solid #ccc;
margin: 0 0 0.8em 0;
padding: 0 0 0.2em 0;
font-family: 'Roboto' sans-serif;
font-weight: 500;
font-size: 2em;
white-space: nowrap;
}
.title a .instructions a {
text-decoration: none;
margin-right: 10px;
color: #1D6EEE;
}
.title a:hover {
border-bottom: 2px solid #1D6EEE;
}
.instructions {
margin: 0.8em 0;
font-family: 'Roboto' sans-serif;
color: #444;
font-weight: 300;
}
.instructions .userId {
color: #1D6EEE;
}
#playContainer {
display: flex;
align-items: center;
}
#local .distal {
width: 400px;
height: 220px;
margin-right: 20px;
background-color: #222222;
}
.btn {
width: 70px;
margin-top: 10px;
line-height: 26px;
text-align: center;
color: #fff;
background-color: #409EFF;
border: none;
border-radius: 2px;
cursor: pointer;
transition: .1s;
}
.btn:active {
transform: translateY(2px);
}
.channelInput {
width: 240px;
height: 32px;
border-radius: 4px;
border: 1px solid #409EFF;
outline: none;
text-indent: .5rem;
margin-right: 10px;
}
.joinBtn {
width: 80px;
line-height: 34px;
text-align: center;
color: #fff;
background-color: #409EFF;
border: none;
border-radius: 4px;
cursor: pointer;
transition: .1s;
}
.joinBtn:active {
transform: translateY(2px);
}
.joinBtn[disabled] {
background-color: #E1E1E2;
cursor: not-allowed;
}
#viewSource {
display: block;
margin: 1.3em 0 0 0;
border-top: 1px solid #999;
padding: 1em 0 0 0;
color: #1D6EEE;
font-weight: 300;
text-decoration: none;
}
#viewSource:hover .instructions a:hover {
text-decoration: underline;
}
```
运行效果和示例代码
感兴趣的小伙伴可以下载源码 [github](https://github.com/941725931/webRTC_React)