TRTCCalling.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. import EventEmitter from './utils/event.js'
  2. import TSignaling from './utils/tsignaling-wx'
  3. import * as ENV from 'utils/environment.js'
  4. import MTA from 'libs/mta_analysis.js'
  5. import { EVENT, TRTC_EVENT } from './common/constants.js'
  6. import UserController from './controller/user-controller'
  7. const app = getApp()
  8. const TAG_NAME = 'TRTCCalling-Component'
  9. // 组件旨在跨终端维护一个通话状态管理机,以事件发布机制驱动上层进行管理,并通过API调用进行状态变更。
  10. // 组件设计思路将UI和状态管理分离。您可以通过修改`template`文件夹下的文件,适配您的业务场景,
  11. // 在UI展示上,您可以通过属性的方式,将上层的用户头像,名称等数据传入组件内部,`static`下的用户头像图片,
  12. // 只是为了展示基础的效果,您需要根据业务场景进行修改。
  13. Component({
  14. properties: {
  15. config: {
  16. type: Object,
  17. value: {
  18. sdkAppID: app.globalData.sdkAppID,
  19. userID: '',
  20. userSig: '',
  21. type: 0,
  22. },
  23. observer: function(newVal, oldVal) {
  24. this.setData({
  25. config: newVal,
  26. })
  27. },
  28. },
  29. pusherAvatar: {
  30. type: String,
  31. value: '',
  32. },
  33. remoteAvatar: {
  34. type: String,
  35. value: '',
  36. },
  37. },
  38. data: {
  39. soundMode: 'speaker', // 声音模式 听筒/扬声器
  40. callingFlag: false,
  41. active: false,
  42. pusherConfig: { // 本地上行状态管理
  43. pushUrl: '',
  44. frontCamera: 'front',
  45. enableMic: true,
  46. enableCamera: true,
  47. volume: 0,
  48. },
  49. playerList: [], // 通话成员列表
  50. streamList: [],
  51. invitation: { // 接收到的邀请
  52. inviteID: '',
  53. },
  54. invitationAccept: { // 发出的邀请,以及返回的状态
  55. inviteID: '',
  56. acceptFlag: false,
  57. rejectFlag: false,
  58. },
  59. _historyUserList: [], // 房间内进过房的成员列表,用于发送call_end
  60. },
  61. methods: {
  62. _initEventEmitter() {
  63. // 监听TSignaling事件
  64. this.tsignaling.on(TSignaling.EVENT.NEW_INVITATION_RECEIVED, (event) => {
  65. console.log(TAG_NAME, 'onNewInvitationReceived', `是否在通话:${this.data.callingFlag || this.data.invitationAccept.acceptFlag}, inviteID:${event.data.inviteID} inviter:${event.data.inviter} inviteeList:${event.data.inviteeList} data:${event.data.data}`)
  66. const data = JSON.parse(event.data.data)
  67. // 新的通话邀请
  68. // {
  69. // "version":0,
  70. // "call_type":1,
  71. // "room_id":"123"
  72. // }
  73. // 通话结束,发出的 call_end
  74. // {
  75. // "version":0,
  76. // "call_type":1,
  77. // "call_end":123123
  78. // }
  79. // 通话中,接收的新的邀请时,忙线拒绝
  80. if (this.data.callingFlag || this.data.invitationAccept.acceptFlag) {
  81. this.tsignaling.reject({
  82. inviteID: event.data.inviteID,
  83. data: JSON.stringify({
  84. version: 0,
  85. call_type: data.call_type,
  86. line_busy: '',
  87. }),
  88. })
  89. return
  90. }
  91. if (data.call_end) {
  92. // 小程序端对 call_end 的接收不做业务处理,这里只是向外抛出这个事件
  93. this._emitter.emit(EVENT.CALL_END, {
  94. call_end: data.call_end,
  95. })
  96. } else {
  97. this.data.invitation.inviteID = event.data.inviteID
  98. this.data.invitation.inviter = event.data.inviter
  99. this.data.invitation.type = data.call_type
  100. this.data.invitation.roomID = data.room_id
  101. this.setData({
  102. invitation: this.data.invitation,
  103. callingFlag: true, // 当前invitation未处理完成时,下一个invitation都将会忙线
  104. }, () => {
  105. console.log(`${TAG_NAME} NEW_INVITATION_RECEIVED invitation: `, this.data.callingFlag, this.data.invitation)
  106. this._emitter.emit(EVENT.INVITED, {
  107. inviter: this.data.invitation.inviter,
  108. type: this.data.invitation.type,
  109. })
  110. })
  111. }
  112. })
  113. this.tsignaling.on(TSignaling.EVENT.INVITEE_ACCEPTED, (event) => {
  114. // 发出的邀请收到接受的回调
  115. console.log(`${TAG_NAME} INVITEE_ACCEPTED inviteID:${event.data.inviteID} invitee:${event.data.invitee} data:${event.data.data}`)
  116. if (this.data.invitationAccept.inviteID === event.data.inviteID) {
  117. this.data.invitationAccept.acceptFlag = true
  118. this.setData({
  119. invitationAccept: this.data.invitationAccept,
  120. })
  121. }
  122. })
  123. this.tsignaling.on(TSignaling.EVENT.INVITEE_REJECTED, (event) => {
  124. // 发出的邀请收到拒绝的回调
  125. console.log(`${TAG_NAME} INVITEE_REJECTED inviteID:${event.data.inviteID} invitee:${event.data.invitee} data:${event.data.data}`)
  126. // 小程序使用双向绑定模式,这里只向外抛出拒绝的用户 userID 业务逻辑由业务层进行处理
  127. const data = JSON.parse(event.data.data)
  128. if (this.data.invitationAccept.inviteID === event.data.inviteID) {
  129. this.data.invitationAccept.rejectFlag = true
  130. this.setData({
  131. invitationAccept: this.data.invitationAccept,
  132. }, () => {
  133. if (data.line_busy === '') {
  134. this._emitter.emit(EVENT.LINE_BUSY, {
  135. inviteID: event.data.inviteID,
  136. invitee: event.data.invitee,
  137. reason: 'line busy',
  138. })
  139. } else {
  140. this._emitter.emit(EVENT.REJECT, {
  141. inviteID: event.data.inviteID,
  142. invitee: event.data.invitee,
  143. reason: 'reject',
  144. })
  145. }
  146. })
  147. }
  148. })
  149. this.tsignaling.on(TSignaling.EVENT.INVITATION_CANCELLED, (event) => {
  150. // 收到的邀请收到该邀请取消的回调
  151. console.log('demo | onInvitationCancelled', `inviteID:${event.data.inviteID} inviter:${event.data.invitee} data:${event.data.data}`)
  152. // invitation取消,收到此消息的时候应该还没有接通,为防止时序的问题,还是走一下挂断流程
  153. this.setData({
  154. callingFlag: false,
  155. })
  156. this._emitter.emit(EVENT.CALLING_CANCEL, {
  157. inviteID: event.data.inviteID,
  158. invitee: event.data.invitee,
  159. })
  160. })
  161. this.tsignaling.on(TSignaling.EVENT.INVITATION_TIMEOUT, (event) => {
  162. console.log(TAG_NAME, 'onInvitationTimeout 邀请超时', `inviteID:${event.data.inviteID} inviteeList:${event.data.inviteeList}`)
  163. // 邀请超时, 无人应答
  164. this._emitter.emit(EVENT.NO_RESP, {
  165. inviteID: event.data.inviteID,
  166. inviteeList: event.data.inviteeList,
  167. })
  168. })
  169. this.tsignaling.on(TSignaling.EVENT.SDK_READY, () => {
  170. console.log(TAG_NAME, 'TSignaling SDK ready')
  171. })
  172. this.tsignaling.on(TSignaling.EVENT.SDK_NOT_READY, () => {
  173. this._emitter.emit(EVENT.ERROR, {
  174. errorMsg: 'TSignaling SDK not ready !!! 如果想使用发送消息等功能,接入侧需驱动 SDK 进入 ready 状态',
  175. })
  176. })
  177. this.tsignaling.on(TSignaling.EVENT.TEXT_MESSAGE_RECEIVED, () => {
  178. })
  179. this.tsignaling.on(TSignaling.EVENT.CUSTOM_MESSAGE_RECEIVED, () => {
  180. })
  181. this.tsignaling.on(TSignaling.EVENT.REMOTE_USER_JOIN, () => {
  182. //
  183. })
  184. this.tsignaling.on(TSignaling.EVENT.REMOTE_USER_LEAVE, () => {
  185. // 离开
  186. })
  187. this.tsignaling.on(TSignaling.EVENT.KICKED_OUT, () => {
  188. // 被踢下线 TODO
  189. wx.showToast({
  190. title: '您已被踢下线',
  191. })
  192. this.hangup()
  193. })
  194. this.tsignaling.on(TSignaling.EVENT.NET_STATE_CHANGE, () => {
  195. })
  196. // 监听TRTC SDK抛出的事件
  197. this.userController.on(TRTC_EVENT.REMOTE_USER_JOIN, (event)=>{
  198. console.log(TAG_NAME, '远端用户进房', event, event.data.userID)
  199. this.setData({
  200. playerList: event.data.userList,
  201. }, () => {
  202. // this._emitter.emit(EVENT.REMOTE_USER_JOIN, { userID: event.data.userID })
  203. this._emitter.emit(EVENT.USER_ENTER, {
  204. userID: event.data.userID,
  205. })
  206. if (this.data._historyUserList.indexOf(event.data.userID) > -1) {
  207. this.data._historyUserList.push(event.data.userID)
  208. }
  209. })
  210. console.log(TAG_NAME, 'REMOTE_USER_JOIN', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  211. })
  212. // 远端用户离开
  213. this.userController.on(TRTC_EVENT.REMOTE_USER_LEAVE, (event)=>{
  214. console.log(TAG_NAME, '远端用户离开', event, event.data.userID)
  215. if (event.data.userID) {
  216. this.setData({
  217. playerList: event.data.userList,
  218. streamList: event.data.streamList,
  219. }, () => {
  220. // TODO: 房间内没有远端用户时就退房, 并且发出invite消息,带call_end信息
  221. this._emitter.emit(EVENT.USER_LEAVE, {
  222. userID: event.data.userID,
  223. })
  224. })
  225. }
  226. console.log(TAG_NAME, 'REMOTE_USER_LEAVE', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  227. })
  228. // 视频状态 true
  229. this.userController.on(TRTC_EVENT.REMOTE_VIDEO_ADD, (event)=>{
  230. console.log(TAG_NAME, '远端视频可用', event, event.data.stream.userID)
  231. const stream = event.data.stream
  232. this.setData({
  233. playerList: event.data.userList,
  234. streamList: event.data.streamList,
  235. }, () => {
  236. stream.playerContext = wx.createLivePlayerContext(stream.streamID, this)
  237. })
  238. console.log(TAG_NAME, 'REMOTE_VIDEO_ADD', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  239. })
  240. // 视频状态 false
  241. this.userController.on(TRTC_EVENT.REMOTE_VIDEO_REMOVE, (event)=>{
  242. console.log(TAG_NAME, '远端视频移除', event, event.data.stream.userID)
  243. const stream = event.data.stream
  244. this.setData({
  245. playerList: event.data.userList,
  246. streamList: event.data.streamList,
  247. }, () => {
  248. // 有可能先触发了退房事件,用户名下的所有stream都已清除
  249. if (stream.userID && stream.streamType) {
  250. // this._emitter.emit(EVENT.REMOTE_VIDEO_REMOVE, { userID: stream.userID, streamType: stream.streamType })
  251. }
  252. })
  253. console.log(TAG_NAME, 'REMOTE_VIDEO_REMOVE', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  254. })
  255. // 音频可用
  256. this.userController.on(TRTC_EVENT.REMOTE_AUDIO_ADD, (event)=>{
  257. console.log(TAG_NAME, '远端音频可用', event)
  258. const stream = event.data.stream
  259. this.setData({
  260. playerList: event.data.userList,
  261. streamList: event.data.streamList,
  262. }, () => {
  263. stream.playerContext = wx.createLivePlayerContext(stream.streamID, this)
  264. // 新增的需要触发一次play 默认属性才能生效
  265. // stream.playerContext.play()
  266. // console.log(TAG_NAME, 'REMOTE_AUDIO_ADD playerContext.play()', stream)
  267. // this._emitter.emit(EVENT.REMOTE_AUDIO_ADD, { userID: stream.userID, streamType: stream.streamType })
  268. })
  269. console.log(TAG_NAME, 'REMOTE_AUDIO_ADD', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  270. })
  271. // 音频不可用
  272. this.userController.on(TRTC_EVENT.REMOTE_AUDIO_REMOVE, (event)=>{
  273. console.log(TAG_NAME, '远端音频移除', event, event.data.stream.userID)
  274. const stream = event.data.stream
  275. this.setData({
  276. playerList: event.data.userList,
  277. streamList: event.data.streamList,
  278. }, () => {
  279. // 有可能先触发了退房事件,用户名下的所有stream都已清除
  280. if (stream.userID && stream.streamType) {
  281. // this._emitter.emit(EVENT.REMOTE_AUDIO_REMOVE, { userID: stream.userID, streamType: stream.streamType })
  282. }
  283. })
  284. console.log(TAG_NAME, 'REMOTE_AUDIO_REMOVE', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  285. })
  286. },
  287. /**
  288. * 登录IM接口,所有功能需要先进行登录后才能使用
  289. *
  290. */
  291. login() {
  292. return new Promise((resolve, reject) => {
  293. this.tsignaling.setLogLevel(0)
  294. MTA.Page.stat()
  295. this.tsignaling.login({
  296. userID: this.data.config.userID,
  297. userSig: this.data.config.userSig,
  298. }).then( () => {
  299. console.log(TAG_NAME, 'login', 'IM登入成功')
  300. this._initEventEmitter()
  301. resolve()
  302. })
  303. })
  304. },
  305. /**
  306. * 登出接口,登出后无法再进行拨打操作
  307. */
  308. logout() {
  309. this.tsignaling.logout({
  310. userID: this.data.config.userID,
  311. userSig: this.data.config.userSig,
  312. }).then( () => {
  313. console.log(TAG_NAME, 'login', 'IM登出成功')
  314. }).catch( () => {
  315. console.error(TAG_NAME, 'login', 'IM登出失败')
  316. })
  317. },
  318. /**
  319. * 监听事件
  320. *
  321. * @param eventCode 事件名
  322. * @param handler 事件响应回调
  323. */
  324. on(eventCode, handler, context) {
  325. this._emitter.on(eventCode, handler, context)
  326. },
  327. off(eventCode, handler) {
  328. this._emitter.off(eventCode, handler)
  329. },
  330. /**
  331. * C2C邀请通话,被邀请方会收到的回调
  332. * 如果当前处于通话中,可以调用该函数以邀请第三方进入通话
  333. *
  334. * @param userID 被邀请方
  335. * @param type 0-为之, 1-语音通话,2-视频通话
  336. */
  337. call({ userID, type }) {
  338. // 生成房间号,拼接URL地址
  339. const roomID = Math.floor(Math.random() * 100000000 + 1) // 随机生成房间号
  340. this._getPushUrl(roomID)
  341. this._enterTRTCRoom()
  342. this.tsignaling.invite({
  343. userID: userID,
  344. data: JSON.stringify({
  345. version: 0,
  346. call_type: type,
  347. room_id: roomID,
  348. }),
  349. timeout: 30,
  350. }).then( (res) => {
  351. console.log(`${TAG_NAME} call(userID: ${userID}, type: ${type}) success`)
  352. this.data.invitationAccept.inviteID = res.inviteID
  353. this.setData({
  354. invitationAccept: this.data.invitationAccept,
  355. callingFlag: true,
  356. })
  357. }).catch((error) => {
  358. console.log(`${TAG_NAME} call(userID:${userID},type:${type}) failed', error: ${error}`)
  359. })
  360. },
  361. /**
  362. * IM群组邀请通话,被邀请方会收到的回调
  363. * 如果当前处于通话中,可以继续调用该函数继续邀请他人进入通话,同时正在通话的用户会收到的回调
  364. *
  365. * @param userIDList 邀请列表
  366. * @param type 1-语音通话,2-视频通话
  367. * @param groupID IM群组ID
  368. */
  369. groupCall(params) {
  370. this.tsignaling.inviteInGroup({
  371. groupID: params.groupID,
  372. inviteeList: params.userIDList,
  373. timeout: 30,
  374. data: JSON.stringify({
  375. version: 0,
  376. call_type: params.type,
  377. room_id: Math.floor(Math.random() * 100000000 + 1),
  378. }),
  379. }).then(function(res) {
  380. console.log(TAG_NAME, 'inviteInGroup OK', res)
  381. }).catch(function(error) {
  382. console.log(TAG_NAME, 'inviteInGroup failed', error)
  383. })
  384. },
  385. /**
  386. * 当您作为被邀请方收到 {@link TRTCCallingDelegate#onInvited } 的回调时,可以调用该函数接听来电
  387. */
  388. accept() {
  389. // 拼接pusherURL进房
  390. console.log(TAG_NAME, 'accept() inviteID: ', this.data.invitation.inviteID)
  391. this.tsignaling.accept({
  392. inviteID: this.data.invitation.inviteID,
  393. data: JSON.stringify({
  394. version: 0,
  395. call_type: this.data.config.type,
  396. }),
  397. }).then( () => {
  398. console.log('接受成功')
  399. }).catch( () => {
  400. console.error('接受失败')
  401. })
  402. this._getPushUrl(this.data.invitation.roomID)
  403. this._enterTRTCRoom()
  404. },
  405. /**
  406. * 当您作为被邀请方收到的回调时,可以调用该函数拒绝来电
  407. */
  408. reject() {
  409. if (this.data.invitation.inviteID) {
  410. this.tsignaling.reject({
  411. inviteID: this.data.invitation.inviteID,
  412. data: JSON.stringify({
  413. version: 0,
  414. call_type: this.data.config.type,
  415. }),
  416. }).then( (res) => {
  417. console.log('demo reject OK', res)
  418. this._reset()
  419. }).catch( (error) => {
  420. console.log('demo reject failed', error)
  421. })
  422. } else {
  423. console.warn(`${TAG_NAME} 未收到邀请,无法拒绝`)
  424. return
  425. }
  426. },
  427. /**
  428. * 当您处于通话中,可以调用该函数结束通话
  429. */
  430. hangup() {
  431. const inviterFlag = !this.data.invitation.inviteID && this.data.invitationAccept.inviteID && true // 是否是邀请者
  432. if (inviterFlag && !this.data.invitationAccept.acceptFlag && !this.data.invitationAccept.rejectFlag) {
  433. console.log(TAG_NAME, 'cancel() inviteID: ', this.data.invitationAccept.inviteID)
  434. this.tsignaling.cancel({
  435. inviteID: this.data.invitationAccept.inviteID,
  436. })
  437. }
  438. if (this.data.playerList.length === 0 && this.data.invitationAccept.acceptFlag) {
  439. const currentTime = Date.parse(new Date())
  440. // console.log('发送call_end信息', currentTime)
  441. this.data._historyUserList.forEach( (user) => {
  442. this.tsignaling.invite({
  443. userID: user,
  444. data: JSON.stringify({
  445. version: 0,
  446. call_type: this.data.config.type,
  447. call_end: currentTime,
  448. }),
  449. })
  450. })
  451. this._emitter.emit(EVENT.CALL_END, {
  452. inviteID: this.data.invitationAccept.inviteID,
  453. call_end: currentTime,
  454. })
  455. }
  456. this._reset().then( () => {
  457. this._emitter.emit(EVENT.HANG_UP)
  458. console.log(TAG_NAME, 'hangup() pusherConfig: ', this.data.pusherConfig)
  459. })
  460. },
  461. _reset() {
  462. return new Promise( (resolve, reject) => {
  463. console.log(TAG_NAME, ' _reset()')
  464. const result = this.userController.reset()
  465. this.data.pusherConfig = {
  466. pushUrl: '',
  467. frontCamera: 'front',
  468. enableMic: true,
  469. enableCamera: true,
  470. volume: 0,
  471. },
  472. // 清空状态
  473. this.setData({
  474. pusherConfig: this.data.pusherConfig,
  475. soundMode: 'speaker',
  476. invitation: {
  477. inviteID: '',
  478. },
  479. playerList: result.userList,
  480. streamList: result.streamList,
  481. _historyUserList: [],
  482. active: false,
  483. callingFlag: false,
  484. invitationAccept: {
  485. inviteID: '',
  486. acceptFlag: false,
  487. rejectFlag: false,
  488. },
  489. }, () => {
  490. resolve()
  491. })
  492. })
  493. },
  494. /**
  495. *
  496. * @param userID 远端用户id
  497. */
  498. startRemoteView(userID) {
  499. this.data.streamList.forEach( (stream) => {
  500. if (stream.userID === userID) {
  501. stream.muteVideo = false
  502. this.setData({
  503. streamList: this.data.streamList,
  504. }, () => {
  505. console.log(`${TAG_NAME}, startRemoteView(${userID})`)
  506. })
  507. return
  508. }
  509. })
  510. },
  511. /**
  512. * 当您收到 onUserVideoAvailable 回调为false时,可以停止渲染数据
  513. *
  514. * @param userID 远端用户id
  515. */
  516. stopRemoteView(userID) {
  517. this.data.streamList.forEach( (stream) => {
  518. if (stream.userID === userID) {
  519. stream.muteVideo = true
  520. this.setData({
  521. streamList: this.data.streamList,
  522. }, () => {
  523. console.log(`${TAG_NAME}, stopRemoteView(${userID})`)
  524. })
  525. return
  526. }
  527. })
  528. },
  529. /**
  530. * 您可以调用该函数开启摄像头
  531. */
  532. openCamera() {
  533. this.data.pusherConfig.enableCamera = true
  534. this.setData({
  535. pusherConfig: this.data.pusherConfig,
  536. }, () => {
  537. console.log(`${TAG_NAME}, closeCamera() pusherConfig: ${this.data.pusherConfig}`)
  538. })
  539. },
  540. /**
  541. * 您可以调用该函数关闭摄像头
  542. * 处于通话中的用户会收到回调
  543. */
  544. closeCamera() {
  545. this.data.pusherConfig.enableCamera = false
  546. this.setData({
  547. pusherConfig: this.data.pusherConfig,
  548. }, () => {
  549. console.log(`${TAG_NAME}, closeCamera() pusherConfig: ${this.data.pusherConfig}`)
  550. })
  551. },
  552. /**
  553. * 是否静音mic
  554. *
  555. * @param isMute true:麦克风关闭 false:麦克风打开
  556. */
  557. // setMicMute(isMute) {
  558. // this.data.pusherConfig.enableMic = !isMute
  559. // this.setData({
  560. // pusherConfig: this.data.pusherConfig,
  561. // }, () => {
  562. // console.log(`${TAG_NAME}, setMicMute(${isMute}) enableMic: ${this.data.pusherConfig.enableMic}`)
  563. // })
  564. // },
  565. setMicMute(isMute) {
  566. this.data.pusherConfig.enableMic = !isMute
  567. this.setData({
  568. pusherConfig: this.data.pusherConfig,
  569. }, () => {
  570. console.log(`${TAG_NAME}, setMicMute(${isMute}) enableMic: ${this.data.pusherConfig.enableMic}`)
  571. wx.createLivePusherContext().setMICVolume({ volume: isMute ? 0 : 1 })
  572. })
  573. },
  574. switchCamera(isFrontCamera) {
  575. this.data.pusherConfig.frontCamera = isFrontCamera ? 'front' : 'back'
  576. this.setData({
  577. pusherConfig: this.data.pusherConfig,
  578. }, () => {
  579. console.log(`${TAG_NAME}, switchCamera(), frontCamera${this.data.pusherConfig.frontCamera}`)
  580. })
  581. },
  582. setHandsFree(isHandsFree) {
  583. this.data.soundMode = isHandsFree ? 'speaker' : 'ear'
  584. this.setData({
  585. soundMode: this.data.soundMode,
  586. }, () => {
  587. console.log(`${TAG_NAME}, setHandsFree() result: ${this.data.soundMode}`)
  588. })
  589. },
  590. _toggleAudio() {
  591. if (this.data.pusherConfig.enableMic) {
  592. this.setMicMute(true)
  593. } else {
  594. this.setMicMute(false)
  595. }
  596. },
  597. _toggleSoundMode() {
  598. if (this.data.soundMode === 'speaker') {
  599. this.setHandsFree(false)
  600. } else {
  601. this.setHandsFree(true)
  602. }
  603. },
  604. _getPushUrl(roomId) {
  605. // 拼接 puhser url rtmp 方案
  606. console.log(TAG_NAME, '_getPushUrl', roomId)
  607. // TODO: 解注释
  608. if (ENV.IS_TRTC) {
  609. // 版本高于7.0.8,基础库版本高于2.10.0 使用新的 url
  610. return new Promise((resolve, reject) => {
  611. this.setData({
  612. active: true,
  613. })
  614. let roomID = ''
  615. if (/^\d+$/.test(roomId)) {
  616. // 数字房间号
  617. roomID = '&roomid=' + roomId
  618. } else {
  619. // 字符串房间号
  620. roomID = '&strroomid=' + roomId
  621. }
  622. setTimeout(()=> {
  623. const pushUrl = 'room://cloud.tencent.com/rtc?sdkappid=' + this.data.config.sdkAppID +
  624. roomID +
  625. '&userid=' + this.data.config.userID +
  626. '&usersig=' + this.data.config.userSig +
  627. '&appscene=videocall' +
  628. '&cloudenv=PRO' // ios此参数必填
  629. console.warn(TAG_NAME, 'getPushUrl result:', pushUrl)
  630. this.data.pusherConfig.pushUrl = pushUrl
  631. this.setData({
  632. pusherConfig: this.data.pusherConfig,
  633. })
  634. resolve(pushUrl)
  635. }, 0)
  636. })
  637. }
  638. console.error(TAG_NAME, '组件仅支持微信 App iOS >=7.0.9, Android >= 7.0.8, 小程序基础库版 >= 2.10.0')
  639. console.error(TAG_NAME, '需要真机运行,开发工具不支持实时音视频')
  640. },
  641. _enterTRTCRoom() {
  642. // 开始推流
  643. wx.createLivePusherContext().start()
  644. },
  645. _hangUp() {
  646. this.hangup()
  647. },
  648. _pusherStateChangeHandler(event) {
  649. const code = event.detail.code
  650. const message = event.detail.message
  651. const TAG_NAME = 'TRTCCalling pusherStateChange: '
  652. switch (code) {
  653. case 0: // 未知状态码,不做处理
  654. console.log(TAG_NAME, message, code)
  655. break
  656. case 1001:
  657. console.log(TAG_NAME, '已经连接推流服务器', code)
  658. break
  659. case 1002:
  660. console.log(TAG_NAME, '已经与服务器握手完毕,开始推流', code)
  661. break
  662. case 1003:
  663. console.log(TAG_NAME, '打开摄像头成功', code)
  664. break
  665. case 1005:
  666. console.log(TAG_NAME, '推流动态调整分辨率', code)
  667. break
  668. case 1006:
  669. console.log(TAG_NAME, '推流动态调整码率', code)
  670. break
  671. case 1007:
  672. console.log(TAG_NAME, '首帧画面采集完成', code)
  673. break
  674. case 1008:
  675. console.log(TAG_NAME, '编码器启动', code)
  676. break
  677. case 1018:
  678. console.log(TAG_NAME, '进房成功', code)
  679. break
  680. case 1019:
  681. console.log(TAG_NAME, '退出房间', code)
  682. // 20200421 iOS 仍然没有1019事件通知退房,退房事件移动到 exitRoom 方法里,但不是后端通知的退房成功
  683. // this._emitter.emit(EVENT.LOCAL_LEAVE, { userID: this.data.pusher.userID })
  684. break
  685. case 2003:
  686. console.log(TAG_NAME, '渲染首帧视频', code)
  687. break
  688. case 1020:
  689. case 1031:
  690. case 1032:
  691. case 1033:
  692. case 1034:
  693. // 通过 userController 处理 1020 1031 1032 1033 1034
  694. this.userController.userEventHandler(event)
  695. break
  696. case -1301:
  697. console.error(TAG_NAME, '打开摄像头失败: ', code)
  698. this._emitter.emit(EVENT.ERROR, { code, message })
  699. break
  700. case -1302:
  701. console.error(TAG_NAME, '打开麦克风失败: ', code)
  702. this._emitter.emit(EVENT.ERROR, { code, message })
  703. break
  704. case -1303:
  705. console.error(TAG_NAME, '视频编码失败: ', code)
  706. this._emitter.emit(EVENT.ERROR, { code, message })
  707. break
  708. case -1304:
  709. console.error(TAG_NAME, '音频编码失败: ', code)
  710. this._emitter.emit(EVENT.ERROR, { code, message })
  711. break
  712. case -1307:
  713. console.error(TAG_NAME, '推流连接断开: ', code)
  714. this._emitter.emit(EVENT.ERROR, { code, message })
  715. break
  716. case -100018:
  717. console.error(TAG_NAME, '进房失败: userSig 校验失败,请检查 userSig 是否填写正确', code, message)
  718. this._emitter.emit(EVENT.ERROR, { code, message })
  719. break
  720. case 5000:
  721. console.log(TAG_NAME, '小程序被挂起: ', code)
  722. // 20200421 iOS 微信点击胶囊圆点会触发该事件
  723. // 触发 5000 后,底层SDK会退房,返回前台后会自动进房
  724. break
  725. case 5001:
  726. // 20200421 仅有 Android 微信会触发该事件
  727. console.log(TAG_NAME, '小程序悬浮窗被关闭: ', code)
  728. break
  729. case 1021:
  730. console.log(TAG_NAME, '网络类型发生变化,需要重新进房', code)
  731. break
  732. case 2007:
  733. console.log(TAG_NAME, '本地视频播放loading: ', code)
  734. break
  735. case 2004:
  736. console.log(TAG_NAME, '本地视频播放开始: ', code)
  737. break
  738. default:
  739. console.log(TAG_NAME, message, code)
  740. }
  741. },
  742. _playerStateChange(event) {
  743. // console.log(TAG_NAME, '_playerStateChange', event)
  744. this._emitter.emit(EVENT.REMOTE_STATE_UPDATE, event)
  745. },
  746. _playerAudioVolumeNotify(event) {
  747. const userID = event.target.dataset.userid
  748. const volume = event.detail.volume
  749. const stream = this.userController.getStream({
  750. userID: userID,
  751. streamType: 'main',
  752. })
  753. if (stream) {
  754. stream.volume = volume
  755. }
  756. this.setData({
  757. streamList: this.data.streamList,
  758. }, () => {
  759. this._emitter.emit(EVENT.USER_VOICE_VOLUME, {
  760. userID: userID,
  761. volume: volume,
  762. })
  763. })
  764. },
  765. _pusherAudioVolumeNotify(event) {
  766. this.data.pusherConfig.volume = event.detail.volume
  767. this._emitter.emit(EVENT.USER_VOICE_VOLUME, {
  768. userID: this.data.config.userID,
  769. volume: event.detail.volume,
  770. })
  771. this.setData({
  772. pusherConfig: this.data.pusherConfig,
  773. })
  774. },
  775. },
  776. /**
  777. * 生命周期方法
  778. */
  779. lifetimes: {
  780. created: function() {
  781. // 在组件实例刚刚被创建时执行
  782. console.log(TAG_NAME, 'created', ENV)
  783. this.tsignaling = new TSignaling({ SDKAppID: this.data.config.sdkAppID })
  784. wx.setKeepScreenOn({
  785. keepScreenOn: true,
  786. })
  787. MTA.App.init({
  788. 'appID': '500728728',
  789. 'eventID': '500730148',
  790. 'autoReport': true,
  791. 'statParam': true,
  792. 'ignoreParams': [],
  793. })
  794. },
  795. attached: function() {
  796. // 在组件实例进入页面节点树时执行
  797. console.log(TAG_NAME, 'attached')
  798. this.EVENT = EVENT
  799. this._emitter = new EventEmitter()
  800. this.userController = new UserController()
  801. MTA.Page.stat()
  802. },
  803. ready: function() {
  804. // 在组件在视图层布局完成后执行
  805. console.log(TAG_NAME, 'ready')
  806. },
  807. detached: function() {
  808. // 在组件实例被从页面节点树移除时执行
  809. console.log(TAG_NAME, 'detached')
  810. this._reset()
  811. },
  812. error: function(error) {
  813. // 每当组件方法抛出错误时执行
  814. console.log(TAG_NAME, 'error', error)
  815. },
  816. },
  817. pageLifetimes: {
  818. show: function() {
  819. },
  820. hide: function() {
  821. // 组件所在的页面被隐藏时执行
  822. console.log(TAG_NAME, 'hide')
  823. },
  824. resize: function(size) {
  825. // 组件所在的页面尺寸变化时执行
  826. console.log(TAG_NAME, 'resize', size)
  827. },
  828. },
  829. })