index.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import {
  2. Component,
  3. OnInit,
  4. AfterContentInit,
  5. AfterViewInit,
  6. OnDestroy,
  7. } from '@angular/core';
  8. import sdk, { Client } from 'urtc-sdk';
  9. import { Stream } from 'urtc-sdk/types';
  10. // 注:实际使用时,请自行在 config 目录下创建 index.ts 配置文件
  11. import config from '../../config';
  12. const { AppId, AppKey } = config;
  13. // 此处使用固定的房间号的随机的用户ID,请自行替换
  14. const RoomId = 'ssss02';
  15. const UserId = Math.floor(Math.random() * 1000000).toString();
  16. console.log('UCloudRTC sdk version: ', sdk.version);
  17. @Component({
  18. selector: 'app-room-page',
  19. templateUrl: './index.html',
  20. styleUrls: ['./index.css']
  21. })
  22. export class RoomComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
  23. private client: Client;
  24. private roomId: string = RoomId;
  25. private userId: string = UserId;
  26. private isJoinedRoom = false;
  27. private selectedStream: Stream | null = null;
  28. private localStreams: Stream[] = [];
  29. private remoteStreams: Stream[] = [];
  30. get roomStatus() {
  31. return this.isJoinedRoom ? '已加入' : '未加入';
  32. }
  33. get selectedStreamStatus() {
  34. return this.selectedStream ? this.selectedStream.sid : '未选择';
  35. }
  36. private leaveRoom: () => void;
  37. private onSelectStream: () => void;
  38. ngOnInit() {
  39. if (!AppId || !AppKey) {
  40. alert('请先设置 AppId 和 AppKey');
  41. return;
  42. }
  43. if (!RoomId) {
  44. alert('请先设置 RoomId');
  45. return;
  46. }
  47. if (!UserId) {
  48. alert('请先设置 UserId');
  49. }
  50. this.leaveRoom = this.handleLeaveRoom.bind(this);
  51. this.onSelectStream = this.handleSelectStream.bind(this);
  52. }
  53. ngAfterContentInit() {
  54. const token = sdk.generateToken(AppId, AppKey, RoomId, UserId);
  55. this.client = new Client(AppId, token);
  56. this.client.on('stream-published', (localStream) => {
  57. console.info('stream-published: ', localStream);
  58. const { localStreams } = this;
  59. localStreams.push(localStream);
  60. setTimeout(() => {
  61. this.client.play({
  62. streamId: localStream.sid,
  63. container: localStream.sid
  64. });
  65. }, 0);
  66. });
  67. this.client.on('stream-added', (remoteStream) => {
  68. console.info('stream-added: ', remoteStream);
  69. const { remoteStreams } = this;
  70. remoteStreams.push(remoteStream);
  71. // 自动订阅
  72. this.client.subscribe(remoteStream.sid, (err) => {
  73. console.error('自动订阅失败:', err);
  74. });
  75. });
  76. this.client.on('stream-subscribed', (remoteStream) => {
  77. console.info('stream-subscribed: ', remoteStream);
  78. const { remoteStreams } = this;
  79. const idx = remoteStreams.findIndex(item => item.sid === remoteStream.sid);
  80. if (idx >= 0) {
  81. remoteStreams.splice(idx, 1, remoteStream);
  82. }
  83. setTimeout(() => {
  84. this.client.play({
  85. streamId: remoteStream.sid,
  86. container: remoteStream.sid
  87. });
  88. }, 0);
  89. });
  90. this.client.on('stream-removed', (remoteStream) => {
  91. console.info('stream-removed: ', remoteStream);
  92. const { remoteStreams } = this;
  93. const idx = remoteStreams.findIndex(item => item.sid === remoteStream.sid);
  94. if (idx >= 0) {
  95. remoteStreams.splice(idx, 1);
  96. }
  97. });
  98. this.client.on('connection-state-change', ({ previous, current }) => {
  99. console.log(`连接状态 ${previous} -> ${current}`);
  100. });
  101. this.client.on('stream-reconnected', ({ previous, current }) => {
  102. console.log(`流已断开重连`);
  103. const isLocalStream = previous.type === 'publish';
  104. const streams = isLocalStream ? this.localStreams : this.remoteStreams;
  105. const idx = streams.findIndex(item => item.sid === previous.sid);
  106. if (idx >= 0) {
  107. streams.splice(idx, 1, current);
  108. }
  109. setTimeout(() => {
  110. this.client.play({
  111. streamId: current.sid,
  112. container: current.sid
  113. });
  114. }, 0);
  115. });
  116. window.addEventListener('beforeunload', this.leaveRoom);
  117. }
  118. ngAfterViewInit() {
  119. console.log('view inited, todo something');
  120. }
  121. ngOnDestroy() {
  122. console.info('component will destroy');
  123. window.removeEventListener('beforeunload', this.leaveRoom);
  124. this.handleLeaveRoom();
  125. }
  126. handleJoinRoom() {
  127. const { roomId, userId, isJoinedRoom } = this;
  128. if (isJoinedRoom) {
  129. alert('已经加入了房间');
  130. return;
  131. }
  132. if (!roomId) {
  133. alert('请先填写房间号');
  134. return;
  135. }
  136. this.client.joinRoom(roomId, userId, () => {
  137. console.info('加入房间成功');
  138. this.isJoinedRoom = true;
  139. }, (err) => {
  140. console.error('加入房间失败: ', err);
  141. });
  142. }
  143. handlePublish() {
  144. this.client.publish(err => {
  145. console.error(`发布失败:错误码 - ${err.name},错误信息 - ${err.message}`);
  146. });
  147. }
  148. handlePublishScreen() {
  149. this.client.publish({ audio: false, video: false, screen: true }, (err) => {
  150. console.error(`发布失败:错误码 - ${err.name},错误信息 - ${err.message}`);
  151. });
  152. }
  153. handleUnpublish() {
  154. const { selectedStream } = this;
  155. if (!selectedStream) {
  156. alert('未选择需要取消发布的本地流');
  157. return;
  158. }
  159. this.client.unpublish(selectedStream.sid, (stream) => {
  160. console.info('取消发布本地流成功:', stream);
  161. const { localStreams } = this;
  162. const idx = localStreams.findIndex(item => item.sid === stream.sid);
  163. if (idx >= 0) {
  164. localStreams.splice(idx, 1);
  165. }
  166. this.selectedStream = null;
  167. }, (err) => {
  168. console.error('取消发布本地流失败:', err);
  169. });
  170. }
  171. handleSubscribe() {
  172. const { selectedStream } = this;
  173. if (!selectedStream) {
  174. alert('未选择需要订阅的远端流');
  175. return;
  176. }
  177. this.client.subscribe(selectedStream.sid, (err) => {
  178. console.error('订阅失败:', err);
  179. });
  180. }
  181. handleUnsubscribe() {
  182. const { selectedStream } = this;
  183. if (!selectedStream) {
  184. alert('未选择需要取消订阅的远端流');
  185. return;
  186. }
  187. this.client.unsubscribe(selectedStream.sid, (stream) => {
  188. console.info('取消订阅成功:', stream);
  189. const { remoteStreams } = this;
  190. const idx = remoteStreams.findIndex(item => item.sid === stream.sid);
  191. if (idx >= 0) {
  192. remoteStreams.splice(idx, 1, stream);
  193. }
  194. }, (err) => {
  195. console.error('订阅失败:', err);
  196. });
  197. }
  198. handleLeaveRoom() {
  199. const { isJoinedRoom } = this;
  200. if (!isJoinedRoom) {
  201. return;
  202. }
  203. this.client.leaveRoom(() => {
  204. console.info('离开房间成功');
  205. this.selectedStream = null;
  206. this.localStreams = [];
  207. this.remoteStreams = [];
  208. this.isJoinedRoom = false;
  209. }, (err) => {
  210. console.error('离开房间失败:', err);
  211. });
  212. }
  213. handleSelectStream(stream) {
  214. console.log('select stream: ', stream);
  215. this.selectedStream = stream;
  216. }
  217. trackBySId(index, stream) {
  218. return stream.sid;
  219. }
  220. }