index.jsx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import React, { Component } from 'react';
  2. import sdk, { Client } from 'urtc-sdk';
  3. import config from '../../config';
  4. import StreamInfo from '../../components/StreamInfo';
  5. import './index.css';
  6. const { AppId, AppKey } = config;
  7. // 此处使用固定的房间号的随机的用户ID,请自行替换
  8. const RoomId = "ssss022";
  9. const UserId = Math.floor(Math.random() * 1000000).toString();
  10. console.log('UCloudRTC sdk version: ', sdk.version);
  11. export default class Room extends Component {
  12. constructor() {
  13. super();
  14. this.state = {
  15. roomId: RoomId,
  16. userId: UserId,
  17. isJoinedRoom: false,
  18. selectedStream: null,
  19. localStreams: [],
  20. remoteStreams: [],
  21. }
  22. }
  23. componentDidMount() {
  24. if (!AppId || !AppKey) {
  25. alert('请先设置 AppId 和 AppKey');
  26. return;
  27. }
  28. if (!RoomId) {
  29. alert('请先设置 RoomId');
  30. return;
  31. }
  32. if (!UserId) {
  33. alert('请先设置 UserId');
  34. return;
  35. }
  36. const token = sdk.generateToken(AppId, AppKey, RoomId, UserId);
  37. this.client = new Client(AppId, token);
  38. this.client.on('stream-published', (localStream) => {
  39. console.info('stream-published: ', localStream);
  40. const { localStreams } = this.state;
  41. localStreams.push(localStream);
  42. this.setState({ localStreams }, () => {
  43. this.client.play({
  44. streamId: localStream.sid,
  45. container: localStream.sid,
  46. });
  47. });
  48. });
  49. this.client.on('stream-added', (remoteStream) => {
  50. console.info('stream-added: ', remoteStream);
  51. const { remoteStreams } = this.state;
  52. remoteStreams.push(remoteStream);
  53. // 自动订阅
  54. this.client.subscribe(remoteStream.sid, (err) => {
  55. console.error('自动订阅失败:', err);
  56. });
  57. this.setState({ remoteStreams });
  58. });
  59. this.client.on('stream-subscribed', (remoteStream) => {
  60. console.info('stream-subscribed: ', remoteStream);
  61. const { remoteStreams } = this.state;
  62. const idx = remoteStreams.findIndex(item => item.sid === remoteStream.sid);
  63. if (idx >= 0 ) {
  64. remoteStreams.splice(idx, 1, remoteStream);
  65. }
  66. this.setState({ remoteStreams }, () => {
  67. this.client.play({
  68. streamId: remoteStream.sid,
  69. container: remoteStream.sid,
  70. });
  71. });
  72. });
  73. this.client.on('stream-removed', (remoteStream) => {
  74. console.info('stream-removed: ', remoteStream);
  75. const { remoteStreams } = this.state;
  76. const idx = remoteStreams.findIndex(item => item.sid === remoteStream.sid);
  77. if (idx >= 0 ) {
  78. remoteStreams.splice(idx, 1);
  79. }
  80. this.setState({ remoteStreams });
  81. });
  82. this.client.on('connection-state-change', ({ previous, current }) => {
  83. console.log(`连接状态 ${previous} -> ${current}`);
  84. });
  85. this.client.on('stream-reconnected', ({previous, current}) => {
  86. console.log(`流已断开重连`);
  87. const isLocalStream = previous.type === 'publish';
  88. const streams = isLocalStream ? this.state.localStreams : this.state.remoteStreams;
  89. const idx = streams.findIndex(item => item.sid === previous.sid);
  90. if (idx >= 0) {
  91. // 更新流的信息
  92. streams.splice(idx, 1, current);
  93. }
  94. const playFunc = () => {
  95. this.client.play({
  96. streamId: current.sid,
  97. container: current.sid,
  98. });
  99. }
  100. if (isLocalStream) {
  101. this.setState({ localStreams: streams }, playFunc);
  102. } else {
  103. this.setState({ remoteStreams: streams }, playFunc);
  104. }
  105. });
  106. window.addEventListener('beforeunload', this.handleLeaveRoom);
  107. }
  108. componentWillUnmount() {
  109. console.info('component will unmout');
  110. window.removeEventListener('beforeunload', this.handleLeaveRoom);
  111. this.handleLeaveRoom();
  112. }
  113. handleJoinRoom = () => {
  114. const { roomId, userId, isJoinedRoom } = this.state;
  115. if (isJoinedRoom) {
  116. alert('已经加入了房间');
  117. return;
  118. }
  119. if (!roomId) {
  120. alert('请先填写房间号');
  121. return;
  122. }
  123. this.client.joinRoom(roomId, userId, () => {
  124. console.info('加入房间成功');
  125. this.setState({ isJoinedRoom: true });
  126. }, (err) => {
  127. console.error('加入房间失败: ', err);
  128. });
  129. }
  130. handlePublish = () => {
  131. this.client.publish(err => {
  132. console.error(`发布失败:错误码 - ${err.name},错误信息 - ${err.message}`);
  133. });
  134. }
  135. handlePublishScreen = () => {
  136. this.client.publish({ audio: false, video: false, screen: true }, err => {
  137. console.error(`发布失败:错误码 - ${err.name},错误信息 - ${err.message}`);
  138. });
  139. }
  140. handleUnpublish = () => {
  141. const { selectedStream } = this.state;
  142. if (!selectedStream) {
  143. alert('未选择需要取消发布的本地流');
  144. return;
  145. }
  146. this.client.unpublish(selectedStream.sid, (stream) => {
  147. console.info('取消发布本地流成功:', stream);
  148. const { localStreams } = this.state;
  149. const idx = localStreams.findIndex(item => item.sid === stream.sid);
  150. if (idx >=0) {
  151. localStreams.splice(idx, 1);
  152. }
  153. this.setState({
  154. localStreams,
  155. selectedStream: null
  156. });
  157. }, (err) => {
  158. console.error('取消发布本地流失败:', err);
  159. })
  160. }
  161. handleSubscribe = () => {
  162. const { selectedStream } = this.state;
  163. if (!selectedStream) {
  164. alert('未选择需要订阅的远端流');
  165. return;
  166. }
  167. this.client.subscribe(selectedStream.sid, (err) => {
  168. console.error('订阅失败:', err);
  169. });
  170. }
  171. handleUnsubscribe = () => {
  172. const { selectedStream } = this.state;
  173. if (!selectedStream) {
  174. alert('未选择需要取消订阅的远端流');
  175. return;
  176. }
  177. this.client.unsubscribe(selectedStream.sid, (stream) => {
  178. console.info('取消订阅成功:', stream);
  179. const { remoteStreams } = this.state;
  180. const idx = remoteStreams.findIndex(item => item.sid === stream.sid);
  181. if (idx >=0) {
  182. remoteStreams.splice(idx, 1, stream);
  183. }
  184. this.setState({
  185. remoteStreams,
  186. });
  187. }, (err) => {
  188. console.error('订阅失败:', err);
  189. });
  190. }
  191. handleLeaveRoom = () => {
  192. const { isJoinedRoom } = this.state;
  193. if (!isJoinedRoom) {
  194. return;
  195. }
  196. this.client.leaveRoom(() => {
  197. console.info('离开房间成功');
  198. this.setState({
  199. selectedStream: null,
  200. localStreams: [],
  201. remoteStreams: [],
  202. isJoinedRoom: false,
  203. });
  204. }, (err) => {
  205. console.error('离开房间失败:', err);
  206. });
  207. }
  208. handleSelectStream = (stream) => {
  209. console.log('select stream: ', stream);
  210. this.setState({ selectedStream: stream });
  211. }
  212. renderLocalStream() {
  213. const { localStreams } = this.state;
  214. return localStreams.map(stream => {
  215. return stream.mediaStream ?
  216. <div className="stream-container" key={stream.sid} onClick={() => this.handleSelectStream(stream)}>
  217. <StreamInfo client={this.client} stream={stream}></StreamInfo>
  218. <div className="video-container" id={stream.sid}></div>
  219. </div> :
  220. null;
  221. });
  222. }
  223. renderRemoteStream() {
  224. const { remoteStreams } = this.state;
  225. return remoteStreams.map(stream => {
  226. return stream.mediaStream ?
  227. <div className="stream-container" key={stream.sid} onClick={() => this.handleSelectStream(stream)}>
  228. <StreamInfo client={this.client} stream={stream}></StreamInfo>
  229. <div className="video-container" id={stream.sid}></div>
  230. </div> :
  231. null;
  232. });
  233. }
  234. handleRecord = () => {
  235. console.log('handleRecord')
  236. }
  237. handleStopRecord = () => {
  238. console.log('handleStopRecord')
  239. }
  240. handleRelay = () => {
  241. console.log('handleRelay')
  242. }
  243. handleStopRelay = () => {
  244. console.log('handleStopRelay')
  245. }
  246. render() {
  247. const { selectedStream, isJoinedRoom } = this.state;
  248. return (
  249. <div className="room">
  250. <label>房间号:{RoomId}({isJoinedRoom ? '已加入' : '未加入'})</label>
  251. <p>当前选中的流:{selectedStream ? selectedStream.sid : '未选择'}</p>
  252. <h3>本地(发布)流</h3>
  253. {
  254. this.renderLocalStream()
  255. }
  256. <h3>远端(订阅)流</h3>
  257. {
  258. this.renderRemoteStream()
  259. }
  260. <h3>操作</h3>
  261. <button onClick={this.handleJoinRoom}>加入房间</button>
  262. <button onClick={this.handlePublish}>发布</button>
  263. <button onClick={this.handlePublishScreen}>屏幕共享</button>
  264. <button onClick={this.handleUnpublish}>取消发布/屏幕共享</button>
  265. <button onClick={this.handleSubscribe}>订阅</button>
  266. <button onClick={this.handleUnsubscribe}>取消订阅</button>
  267. <button onClick={this.handleRecord}>录制</button>
  268. <button onClick={this.handleStopRecord}>停止录制</button>
  269. <button onClick={this.handleRelay}>转推</button>
  270. <button onClick={this.handleStopRelay}>停止转推</button>
  271. <button onClick={this.handleLeaveRoom}>离开房间</button>
  272. </div>
  273. )
  274. }
  275. }