|  | @@ -1,2 +1,354 @@
 | 
	
		
			
			| 1 | 1 |  []()
 | 
	
		
			
			|  | 2 | +
 | 
	
		
			
			| 2 | 3 |  # react-native-agora
 | 
	
		
			
			|  | 4 | +
 | 
	
		
			
			|  | 5 | +## Installation and linking libraries
 | 
	
		
			
			|  | 6 | +
 | 
	
		
			
			|  | 7 | +Install with npm:
 | 
	
		
			
			|  | 8 | +
 | 
	
		
			
			|  | 9 | + `npm install --save react-native-agora`
 | 
	
		
			
			|  | 10 | +
 | 
	
		
			
			|  | 11 | +Or, install with yarn:
 | 
	
		
			
			|  | 12 | +
 | 
	
		
			
			|  | 13 | + `yarn add react-native-agora`
 | 
	
		
			
			|  | 14 | +
 | 
	
		
			
			|  | 15 | +Either way, then link with:
 | 
	
		
			
			|  | 16 | +
 | 
	
		
			
			|  | 17 | + `react-native link react-native-agora`
 | 
	
		
			
			|  | 18 | +
 | 
	
		
			
			|  | 19 | +#### iOS
 | 
	
		
			
			|  | 20 | +
 | 
	
		
			
			|  | 21 | +TARGETS->Build Phases-> Link Binary With Libaries中点击“+”按钮,选择
 | 
	
		
			
			|  | 22 | +
 | 
	
		
			
			|  | 23 | +    libresolv.tbd
 | 
	
		
			
			|  | 24 | +    libc++.tbd
 | 
	
		
			
			|  | 25 | +    AVFoundation.framework
 | 
	
		
			
			|  | 26 | +    AudioToolbox.framework
 | 
	
		
			
			|  | 27 | +    VideoToolbox.framework
 | 
	
		
			
			|  | 28 | +    CoreMotion.framework
 | 
	
		
			
			|  | 29 | +    CoreMedia.framework
 | 
	
		
			
			|  | 30 | +    CoreTelephony.framework
 | 
	
		
			
			|  | 31 | +TARGETS->Build Phases-> Link Binary With Libaries中点击“+”按钮,在弹出的窗口中点击“Add Other”按钮,选择
 | 
	
		
			
			|  | 32 | +    node_modules/react-native-agora/ios/RCTAgora/libs/libcrypto.a
 | 
	
		
			
			|  | 33 | +    node_modules/react-native-agora/ios/RCTAgora/libs/AgoraRtcCryptoLoader.framework
 | 
	
		
			
			|  | 34 | +    node_modules/react-native-agora/ios/RCTAgora/libs/AgoraRtcEngineKit.framework
 | 
	
		
			
			|  | 35 | +TARGETS->Build Settings->Search Paths->Framework Search Paths添加
 | 
	
		
			
			|  | 36 | +    "$(SRCROOT)/../node_modules/react-native-agora/ios/RCTAgora/libs"
 | 
	
		
			
			|  | 37 | +TARGETS->Build Settings->Search Paths->Library Search Paths添加
 | 
	
		
			
			|  | 38 | +    "$(SRCROOT)/../node_modules/react-native-agora/ios/RCTAgora/libs"
 | 
	
		
			
			|  | 39 | +TARGETS->Build Settings->Enable Bitcode设置为No
 | 
	
		
			
			|  | 40 | +
 | 
	
		
			
			|  | 41 | +TARGETS->Capabilities->Background Modes->Modes勾选Audio,AirPlay,and Picture In Picture
 | 
	
		
			
			|  | 42 | +
 | 
	
		
			
			|  | 43 | +项目目录->Info.plist->增加2项
 | 
	
		
			
			|  | 44 | +
 | 
	
		
			
			|  | 45 | +    "Privacy - Camera Usage Description":"use camera to start video call"
 | 
	
		
			
			|  | 46 | +    "Privacy - Microphone Usage Description":"use microphone to start video call"
 | 
	
		
			
			|  | 47 | +
 | 
	
		
			
			|  | 48 | +
 | 
	
		
			
			|  | 49 | +#### Android
 | 
	
		
			
			|  | 50 | +
 | 
	
		
			
			|  | 51 | +Add following to `AndroidManifest.xml`
 | 
	
		
			
			|  | 52 | +
 | 
	
		
			
			|  | 53 | +    <uses-permission android:name="android.permission.RECORD_AUDIO" />
 | 
	
		
			
			|  | 54 | +    <uses-permission android:name="android.permission.CAMERA" />
 | 
	
		
			
			|  | 55 | +    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
 | 
	
		
			
			|  | 56 | +    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 | 
	
		
			
			|  | 57 | +
 | 
	
		
			
			|  | 58 | +当您在写混淆代码时,请添加以下代码:
 | 
	
		
			
			|  | 59 | +
 | 
	
		
			
			|  | 60 | +    -keep class io.agora.**{*;}
 | 
	
		
			
			|  | 61 | +
 | 
	
		
			
			|  | 62 | +
 | 
	
		
			
			|  | 63 | +## Documentation
 | 
	
		
			
			|  | 64 | +
 | 
	
		
			
			|  | 65 | +[声网API文档](https://docs.agora.io/cn/1.11.1/user_guide/API/ios_api_live_cn.html)
 | 
	
		
			
			|  | 66 | +
 | 
	
		
			
			|  | 67 | +##### RtcEngine方法
 | 
	
		
			
			|  | 68 | +
 | 
	
		
			
			|  | 69 | +| Property                         | Type                                     | Description                           |
 | 
	
		
			
			|  | 70 | +| -------------------------------- | ---------------------------------------- | ------------------------------------- |
 | 
	
		
			
			|  | 71 | +| init                             | object {appid: 'agora注册的应用id', channelProfile: '频道模式', videoProfile: '视频模式', clientRole: '角色'} | 初始化Agora引擎                            |
 | 
	
		
			
			|  | 72 | +| joinChannel                      | string channelName (房间名称)   number uid (用户设置的uid 传0系统会自动分配) | 加入房间                                  |
 | 
	
		
			
			|  | 73 | +| leaveChannel                     |                                          | 离开频道                                  |
 | 
	
		
			
			|  | 74 | +| destroy                          |                                          | 销毁引擎实例                                |
 | 
	
		
			
			|  | 75 | +| startPreview                     |                                          | 开启视频预览                                |
 | 
	
		
			
			|  | 76 | +| stopPreview                      |                                          | 关闭视频预览                                |
 | 
	
		
			
			|  | 77 | +| switchCamera                     |                                          | 切换前置/后置摄像头                            |
 | 
	
		
			
			|  | 78 | +| enableVideo                      |                                          | 开启视频模式                                |
 | 
	
		
			
			|  | 79 | +| disableVideo                     |                                          | 关闭视频                                  |
 | 
	
		
			
			|  | 80 | +| setEnableSpeakerphone            | bool                                     | 开启扬声器  trun: 音频输出至扬声器  false: 音频输出至听筒 |
 | 
	
		
			
			|  | 81 | +| muteLocalAudioStream             | bool (default false)                     | 将自己静音                                 |
 | 
	
		
			
			|  | 82 | +| muteAllRemoteAudioStreams        | bool (default false)                     | 静音所有远端 音频                             |
 | 
	
		
			
			|  | 83 | +| muteRemoteAudioStream            | number uid(用户uid) bool  mute(是否静音)       | 静音指定用户 音频                             |
 | 
	
		
			
			|  | 84 | +| muteLocalVideoStream             | bool (default false)                     | 暂停发送本地 视频流                            |
 | 
	
		
			
			|  | 85 | +| enableLocalVideo                 | bool (default false)                     | 禁用本地视频功能                              |
 | 
	
		
			
			|  | 86 | +| muteAllRemoteVideoStreams        | bool (default false)                     | 暂停所有远端视频流                             |
 | 
	
		
			
			|  | 87 | +| muteRemoteVideoStream            | number uid(用户uid) bool  mute(是否暂停)       | 暂停指定远端视频流                             |
 | 
	
		
			
			|  | 88 | +| startRecordingService (ios only) | string  recordingKey                     | 启动服务端录制服务                             |
 | 
	
		
			
			|  | 89 | +| stopRecordingService (ios only)  | string  recordingKey                     | 停止服务端录制服务                             |
 | 
	
		
			
			|  | 90 | +| getSdkVersion                    | callback                                 | 获取版本号                                 |
 | 
	
		
			
			|  | 91 | +
 | 
	
		
			
			|  | 92 | +##### 原生通知事件
 | 
	
		
			
			|  | 93 | +
 | 
	
		
			
			|  | 94 | +```
 | 
	
		
			
			|  | 95 | +RtcEngine.eventEmitter({
 | 
	
		
			
			|  | 96 | +  onFirstRemoteVideoDecoded: data => {},
 | 
	
		
			
			|  | 97 | +  onJoinChannelSuccess: data => {},
 | 
	
		
			
			|  | 98 | +  onUserOffline: data => {},
 | 
	
		
			
			|  | 99 | +  onUserJoined: data => {},
 | 
	
		
			
			|  | 100 | +  onError: data => {},
 | 
	
		
			
			|  | 101 | +  onWarning: data => {},
 | 
	
		
			
			|  | 102 | +  onLeaveChannel: data => {}
 | 
	
		
			
			|  | 103 | +})
 | 
	
		
			
			|  | 104 | +```
 | 
	
		
			
			|  | 105 | +
 | 
	
		
			
			|  | 106 | +| Name                      | Description  |
 | 
	
		
			
			|  | 107 | +| ------------------------- | ------------ |
 | 
	
		
			
			|  | 108 | +| onFirstRemoteVideoDecoded | 远端首帧视频接收解码回调 |
 | 
	
		
			
			|  | 109 | +| onJoinChannelSuccess      | 加入频道成功的回调    |
 | 
	
		
			
			|  | 110 | +| onUserOffline             | 其他用户离开当前频道   |
 | 
	
		
			
			|  | 111 | +| onUserJoined              | 其他用户加入当前频道   |
 | 
	
		
			
			|  | 112 | +| onError                   | 错误信息         |
 | 
	
		
			
			|  | 113 | +| onWarning                 | 警告           |
 | 
	
		
			
			|  | 114 | +| onLeaveChannel            | 退出频道         |
 | 
	
		
			
			|  | 115 | +
 | 
	
		
			
			|  | 116 | +##### AgoraView 组件
 | 
	
		
			
			|  | 117 | +
 | 
	
		
			
			|  | 118 | +| Name           | Description          |
 | 
	
		
			
			|  | 119 | +| -------------- | -------------------- |
 | 
	
		
			
			|  | 120 | +| showLocalVideo | 是否显示本地视频(bool)       |
 | 
	
		
			
			|  | 121 | +| remoteUid      | 显示远程视频(number 传入uid) |
 | 
	
		
			
			|  | 122 | +
 | 
	
		
			
			|  | 123 | +## Usage
 | 
	
		
			
			|  | 124 | +
 | 
	
		
			
			|  | 125 | +```
 | 
	
		
			
			|  | 126 | +import React, {Component} from 'react';
 | 
	
		
			
			|  | 127 | +import {
 | 
	
		
			
			|  | 128 | +    StyleSheet,
 | 
	
		
			
			|  | 129 | +    View,
 | 
	
		
			
			|  | 130 | +    Text,
 | 
	
		
			
			|  | 131 | +    TouchableOpacity,
 | 
	
		
			
			|  | 132 | +    Image,
 | 
	
		
			
			|  | 133 | +    Dimensions
 | 
	
		
			
			|  | 134 | +} from 'react-native';
 | 
	
		
			
			|  | 135 | +
 | 
	
		
			
			|  | 136 | +const {width, height} = Dimensions.get('window');
 | 
	
		
			
			|  | 137 | +
 | 
	
		
			
			|  | 138 | +import {RtcEngine, AgoraView} from 'react-native-agora'
 | 
	
		
			
			|  | 139 | +
 | 
	
		
			
			|  | 140 | +export default class Meeting extends Component {
 | 
	
		
			
			|  | 141 | +
 | 
	
		
			
			|  | 142 | +    constructor(props) {
 | 
	
		
			
			|  | 143 | +        super(props);
 | 
	
		
			
			|  | 144 | +        this.state = {
 | 
	
		
			
			|  | 145 | +            remotes: [],
 | 
	
		
			
			|  | 146 | +            isJoinSuccess: false,
 | 
	
		
			
			|  | 147 | +            isSpeaker: false,
 | 
	
		
			
			|  | 148 | +            isMute: false
 | 
	
		
			
			|  | 149 | +        };
 | 
	
		
			
			|  | 150 | +    }
 | 
	
		
			
			|  | 151 | +
 | 
	
		
			
			|  | 152 | +    componentWillMount() {
 | 
	
		
			
			|  | 153 | +
 | 
	
		
			
			|  | 154 | +        //初始化Agora
 | 
	
		
			
			|  | 155 | +        const options = {
 | 
	
		
			
			|  | 156 | +            appid: '',
 | 
	
		
			
			|  | 157 | +            channelProfile: 1,
 | 
	
		
			
			|  | 158 | +            videoProfile: 40,
 | 
	
		
			
			|  | 159 | +            clientRole: 1,
 | 
	
		
			
			|  | 160 | +        };
 | 
	
		
			
			|  | 161 | +        RtcEngine.init(options);
 | 
	
		
			
			|  | 162 | +    }
 | 
	
		
			
			|  | 163 | +
 | 
	
		
			
			|  | 164 | +    componentDidMount() {
 | 
	
		
			
			|  | 165 | +
 | 
	
		
			
			|  | 166 | +        //加入房间
 | 
	
		
			
			|  | 167 | +        RtcEngine.joinChannel();
 | 
	
		
			
			|  | 168 | +
 | 
	
		
			
			|  | 169 | +        //所以的原生通知统一管理
 | 
	
		
			
			|  | 170 | +        RtcEngine.eventEmitter({
 | 
	
		
			
			|  | 171 | +            onFirstRemoteVideoDecoded: (data) => {
 | 
	
		
			
			|  | 172 | +                console.log(data);
 | 
	
		
			
			|  | 173 | +                //有远程视频加入 返回重要的  uid  AgoraView 根据uid 来设置remoteUid值
 | 
	
		
			
			|  | 174 | +                const {remotes} = this.state;
 | 
	
		
			
			|  | 175 | +
 | 
	
		
			
			|  | 176 | +                let arr = [...remotes];
 | 
	
		
			
			|  | 177 | +                let sign = false;
 | 
	
		
			
			|  | 178 | +                arr.forEach(v => {
 | 
	
		
			
			|  | 179 | +                    sign = v === data.uid
 | 
	
		
			
			|  | 180 | +                });
 | 
	
		
			
			|  | 181 | +
 | 
	
		
			
			|  | 182 | +                if (!sign) {
 | 
	
		
			
			|  | 183 | +                    arr.push(data.uid)
 | 
	
		
			
			|  | 184 | +                }
 | 
	
		
			
			|  | 185 | +
 | 
	
		
			
			|  | 186 | +                this.setState({
 | 
	
		
			
			|  | 187 | +                    remotes: arr
 | 
	
		
			
			|  | 188 | +                })
 | 
	
		
			
			|  | 189 | +            },
 | 
	
		
			
			|  | 190 | +            onUserOffline: (data) => {
 | 
	
		
			
			|  | 191 | +             	console.log(data);
 | 
	
		
			
			|  | 192 | +              	//有人离开了!
 | 
	
		
			
			|  | 193 | +                const {remotes} = this.state;
 | 
	
		
			
			|  | 194 | +
 | 
	
		
			
			|  | 195 | +                let arr = [...remotes];
 | 
	
		
			
			|  | 196 | +
 | 
	
		
			
			|  | 197 | +                let newArr = [];
 | 
	
		
			
			|  | 198 | +                newArr = arr.filter(v => {
 | 
	
		
			
			|  | 199 | +                    return v !== data.uid
 | 
	
		
			
			|  | 200 | +                });
 | 
	
		
			
			|  | 201 | +
 | 
	
		
			
			|  | 202 | +                this.setState({
 | 
	
		
			
			|  | 203 | +                    remotes: newArr
 | 
	
		
			
			|  | 204 | +                });
 | 
	
		
			
			|  | 205 | +            },
 | 
	
		
			
			|  | 206 | +            onJoinChannelSuccess: (data) => {
 | 
	
		
			
			|  | 207 | +                console.log(data);
 | 
	
		
			
			|  | 208 | + 			   //加入房间成功!
 | 
	
		
			
			|  | 209 | +                this.setState({
 | 
	
		
			
			|  | 210 | +                    isJoinSuccess: true
 | 
	
		
			
			|  | 211 | +                });
 | 
	
		
			
			|  | 212 | +            },
 | 
	
		
			
			|  | 213 | +            onUserJoined: (data) => {
 | 
	
		
			
			|  | 214 | +                console.log(data);
 | 
	
		
			
			|  | 215 | +                //有人来了!
 | 
	
		
			
			|  | 216 | +            },
 | 
	
		
			
			|  | 217 | +            onError: (data) => {
 | 
	
		
			
			|  | 218 | +                console.log(data);
 | 
	
		
			
			|  | 219 | +                //错误!
 | 
	
		
			
			|  | 220 | +                RtcEngine.leaveChannel();
 | 
	
		
			
			|  | 221 | +            }
 | 
	
		
			
			|  | 222 | +        })
 | 
	
		
			
			|  | 223 | +    }
 | 
	
		
			
			|  | 224 | +
 | 
	
		
			
			|  | 225 | +    componentWillUnmount() {
 | 
	
		
			
			|  | 226 | +        RtcEngine.removeEmitter()
 | 
	
		
			
			|  | 227 | +    }
 | 
	
		
			
			|  | 228 | +
 | 
	
		
			
			|  | 229 | +    handlerCancel = () => {
 | 
	
		
			
			|  | 230 | +
 | 
	
		
			
			|  | 231 | +        RtcEngine.leaveChannel();
 | 
	
		
			
			|  | 232 | +
 | 
	
		
			
			|  | 233 | +        const {navigator} = this.props;
 | 
	
		
			
			|  | 234 | +        navigator.pop()
 | 
	
		
			
			|  | 235 | +    };
 | 
	
		
			
			|  | 236 | +
 | 
	
		
			
			|  | 237 | +    handlerSwitchCamera = () => {
 | 
	
		
			
			|  | 238 | +        RtcEngine.switchCamera();
 | 
	
		
			
			|  | 239 | +    };
 | 
	
		
			
			|  | 240 | +
 | 
	
		
			
			|  | 241 | +    handlerMuteAllRemoteAudioStreams = () => {
 | 
	
		
			
			|  | 242 | +        this.setState({
 | 
	
		
			
			|  | 243 | +            isMute: !this.state.isMute
 | 
	
		
			
			|  | 244 | +        }, () => {
 | 
	
		
			
			|  | 245 | +            RtcEngine.muteAllRemoteAudioStreams(this.state.isMute)
 | 
	
		
			
			|  | 246 | +        })
 | 
	
		
			
			|  | 247 | +    };
 | 
	
		
			
			|  | 248 | +
 | 
	
		
			
			|  | 249 | +    handlerSetEnableSpeakerphone = () => {
 | 
	
		
			
			|  | 250 | +
 | 
	
		
			
			|  | 251 | +        this.setState({
 | 
	
		
			
			|  | 252 | +            isSpeaker: !this.state.isSpeaker
 | 
	
		
			
			|  | 253 | +        }, () => {
 | 
	
		
			
			|  | 254 | +            RtcEngine.setEnableSpeakerphone(this.state.isSpeaker)
 | 
	
		
			
			|  | 255 | +        });
 | 
	
		
			
			|  | 256 | +
 | 
	
		
			
			|  | 257 | +    };
 | 
	
		
			
			|  | 258 | +
 | 
	
		
			
			|  | 259 | +    render() {
 | 
	
		
			
			|  | 260 | +
 | 
	
		
			
			|  | 261 | +        const {isMute, isSpeaker, remotes, isJoinSuccess} = this.state;
 | 
	
		
			
			|  | 262 | +
 | 
	
		
			
			|  | 263 | +        if (!isJoinSuccess) {
 | 
	
		
			
			|  | 264 | +            return(
 | 
	
		
			
			|  | 265 | +                <View style={{flex:1, backgroundColor:'#fff', justifyContent:'center', alignItems:'center'}}>
 | 
	
		
			
			|  | 266 | +                    <Text>正在创建视频会议...</Text>
 | 
	
		
			
			|  | 267 | +                </View>
 | 
	
		
			
			|  | 268 | +            )
 | 
	
		
			
			|  | 269 | +        }
 | 
	
		
			
			|  | 270 | +
 | 
	
		
			
			|  | 271 | +        return (
 | 
	
		
			
			|  | 272 | +            <View style={styles.container}>
 | 
	
		
			
			|  | 273 | +                <AgoraView style={styles.localView} showLocalVideo={true} />
 | 
	
		
			
			|  | 274 | +                <View style={styles.absView}>
 | 
	
		
			
			|  | 275 | +                    <View style={styles.videoView}>
 | 
	
		
			
			|  | 276 | +                        {remotes.map((v, k) => {
 | 
	
		
			
			|  | 277 | +                            return (
 | 
	
		
			
			|  | 278 | +                                <AgoraView
 | 
	
		
			
			|  | 279 | +                                    style={styles.remoteView}
 | 
	
		
			
			|  | 280 | +                                    key={k}
 | 
	
		
			
			|  | 281 | +                                    remoteUid={v}
 | 
	
		
			
			|  | 282 | +                                />
 | 
	
		
			
			|  | 283 | +                            )
 | 
	
		
			
			|  | 284 | +                        })}
 | 
	
		
			
			|  | 285 | +                    </View>
 | 
	
		
			
			|  | 286 | +                    <View>
 | 
	
		
			
			|  | 287 | +                        <TouchableOpacity
 | 
	
		
			
			|  | 288 | +                            style={{alignSelf: 'center'}}
 | 
	
		
			
			|  | 289 | +                            onPress={this.handlerCancel}>
 | 
	
		
			
			|  | 290 | +                            <Image
 | 
	
		
			
			|  | 291 | +                                style={{width: 60, height: 60}}
 | 
	
		
			
			|  | 292 | +                                source={require('../images/btn_endcall.png')}/>
 | 
	
		
			
			|  | 293 | +                        </TouchableOpacity>
 | 
	
		
			
			|  | 294 | +                        <View style={styles.bottomView}>
 | 
	
		
			
			|  | 295 | +                            <TouchableOpacity onPress={this.handlerMuteAllRemoteAudioStreams} activeOpacity={.7}>
 | 
	
		
			
			|  | 296 | +                                <Image
 | 
	
		
			
			|  | 297 | +                                    style={{width: 50, height: 50}}
 | 
	
		
			
			|  | 298 | +                                    source={ isMute ? require('../images/icon_muted.png') : require('../images/btn_mute.png')}/>
 | 
	
		
			
			|  | 299 | +                            </TouchableOpacity>
 | 
	
		
			
			|  | 300 | +
 | 
	
		
			
			|  | 301 | +                            <TouchableOpacity onPress={this.handlerSwitchCamera} activeOpacity={.7}>
 | 
	
		
			
			|  | 302 | +                                <Image
 | 
	
		
			
			|  | 303 | +                                    style={{width: 50, height: 50}}
 | 
	
		
			
			|  | 304 | +                                    source={ require('../images/btn_switch_camera.png')}/>
 | 
	
		
			
			|  | 305 | +                            </TouchableOpacity>
 | 
	
		
			
			|  | 306 | +
 | 
	
		
			
			|  | 307 | +                            <TouchableOpacity onPress={this.handlerSetEnableSpeakerphone} activeOpacity={.7}>
 | 
	
		
			
			|  | 308 | +                                <Image
 | 
	
		
			
			|  | 309 | +                                    style={{width: 50, height: 50}}
 | 
	
		
			
			|  | 310 | +                                    source={isSpeaker ? require('../images/icon_speaker.png') : require('../images/btn_speaker.png')}/>
 | 
	
		
			
			|  | 311 | +                            </TouchableOpacity>
 | 
	
		
			
			|  | 312 | +                        </View>
 | 
	
		
			
			|  | 313 | +                    </View>
 | 
	
		
			
			|  | 314 | +
 | 
	
		
			
			|  | 315 | +                </View>
 | 
	
		
			
			|  | 316 | +            </View>
 | 
	
		
			
			|  | 317 | +        );
 | 
	
		
			
			|  | 318 | +    }
 | 
	
		
			
			|  | 319 | +}
 | 
	
		
			
			|  | 320 | +
 | 
	
		
			
			|  | 321 | +const styles = StyleSheet.create({
 | 
	
		
			
			|  | 322 | +    container: {
 | 
	
		
			
			|  | 323 | +        flex: 1,
 | 
	
		
			
			|  | 324 | +        backgroundColor: '#F4F4F4'
 | 
	
		
			
			|  | 325 | +    },
 | 
	
		
			
			|  | 326 | +    absView: {
 | 
	
		
			
			|  | 327 | +        position: 'absolute',
 | 
	
		
			
			|  | 328 | +        top: 20,
 | 
	
		
			
			|  | 329 | +        left: 0,
 | 
	
		
			
			|  | 330 | +        right: 0,
 | 
	
		
			
			|  | 331 | +        bottom: 0,
 | 
	
		
			
			|  | 332 | +        justifyContent: 'space-between'
 | 
	
		
			
			|  | 333 | +    },
 | 
	
		
			
			|  | 334 | +    videoView: {
 | 
	
		
			
			|  | 335 | +        padding: 5,
 | 
	
		
			
			|  | 336 | +        flexWrap: 'wrap',
 | 
	
		
			
			|  | 337 | +        flexDirection: 'row',
 | 
	
		
			
			|  | 338 | +        zIndex: 100
 | 
	
		
			
			|  | 339 | +    },
 | 
	
		
			
			|  | 340 | +    localView: {
 | 
	
		
			
			|  | 341 | +        flex: 1
 | 
	
		
			
			|  | 342 | +    },
 | 
	
		
			
			|  | 343 | +    remoteView: {
 | 
	
		
			
			|  | 344 | +        width: (width - 40) / 3,
 | 
	
		
			
			|  | 345 | +        height: (width - 40) / 3,
 | 
	
		
			
			|  | 346 | +        margin: 5
 | 
	
		
			
			|  | 347 | +    },
 | 
	
		
			
			|  | 348 | +    bottomView: {
 | 
	
		
			
			|  | 349 | +        padding: 20,
 | 
	
		
			
			|  | 350 | +        flexDirection: 'row',
 | 
	
		
			
			|  | 351 | +        justifyContent: 'space-around'
 | 
	
		
			
			|  | 352 | +    }
 | 
	
		
			
			|  | 353 | +});
 | 
	
		
			
			|  | 354 | +```
 |