trtc-room.js 91 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660
  1. import UserController from 'controller/user-controller.js'
  2. import Pusher from 'model/pusher.js'
  3. import {
  4. EVENT,
  5. DEFAULT_COMPONENT_CONFIG
  6. } from 'common/constants.js'
  7. import Event from 'utils/event.js'
  8. import * as ENV from 'utils/environment.js'
  9. import TIM from 'libs/tim-wx.js'
  10. import MTA from 'libs/mta_analysis.js'
  11. const TAG_NAME = 'TRTC-ROOM'
  12. const IM_GROUP_TYPE = TIM.TYPES.GRP_CHATROOM // TIM.TYPES.GRP_CHATROOM 体验版IM无数量限制,成员20个, TIM.TYPES.GRP_AVCHATROOM IM体验版最多10个,升级后无限制
  13. let touchX = 0
  14. let touchY = 0
  15. Component({
  16. /**
  17. * 组件的属性列表
  18. */
  19. properties: {
  20. // 必要的初始化参数
  21. config: {
  22. type: Object,
  23. value: {
  24. sdkAppID: '',
  25. userID: '',
  26. userSig: '',
  27. template: '',
  28. debugMode: false, // 是否开启调试模式
  29. enableIM: true // 是否开启 IM
  30. },
  31. observer: function (newVal, oldVal) {
  32. this._propertyObserver({
  33. 'name': 'config',
  34. newVal,
  35. oldVal,
  36. })
  37. },
  38. },
  39. },
  40. /**
  41. * 组件的初始数据
  42. */
  43. data: {
  44. // orientation:'horizontal',
  45. fullScreenFlag: 0, //默认为0不全屏 全屏为1
  46. pusher: null,
  47. debugPanel: true, // 是否打开组件调试面板
  48. debug: false, // 是否打开player pusher 的调试信息
  49. streamList: [], // 用于渲染player列表,存储stram
  50. visibleStreamList: [], // 有音频或者视频的StreamList
  51. userList: [], // 扁平化的数据用来返回给用户
  52. template: '', // 不能设置默认值,当默认值和传入组件的值不一致时,iOS渲染失败
  53. cameraPosition: '', // 摄像头位置,用于debug
  54. panelName: '', // 控制面板名称,包括 setting-panel memberlist-panel
  55. localVolume: 0,
  56. remoteVolumeList: [],
  57. enableIM: true, // 用于组件内渲染
  58. showIMPanel: false,
  59. exitIMThrottle: false,
  60. messageContent: '',
  61. messageList: [], // 仅保留10条消息
  62. maxMessageListLength: 10,
  63. messageListScrollTop: 0,
  64. appVersion: ENV.APP_VERSION,
  65. libVersion: ENV.LIB_VERSION,
  66. hasGridPageTipsShow: false,
  67. gridPageCount: 0, // grid 布局 player 分页的总页数
  68. gridCurrentPage: 1, // grid 布局 当前页码
  69. gridPlayerPerPage: 4, // grid 布局每页 player的数量, 如果大于3,在逻辑里第一页需要减1。等于3 pusher 在每一页都出现。可选值: 3,4
  70. gridPagePlaceholderStreamList: [], // 占位数量
  71. isFullscreenDevice: ENV.IS_FULLSCREEN_DEVICE,
  72. isShowMoreMenu: false,
  73. MICVolume: 50,
  74. BGMVolume: 50,
  75. BGMProgress: 0,
  76. beautyStyle: 'smooth',
  77. beautyStyleArray: [{
  78. value: 'smooth',
  79. label: '光滑',
  80. checked: true
  81. },
  82. {
  83. value: 'nature',
  84. label: '自然',
  85. checked: false
  86. },
  87. {
  88. value: 'close',
  89. label: '关闭',
  90. checked: false
  91. },
  92. ],
  93. filterIndex: 0,
  94. filterArray: [{
  95. value: 'standard',
  96. label: '标准'
  97. },
  98. {
  99. value: 'pink',
  100. label: '粉嫩'
  101. },
  102. {
  103. value: 'nostalgia',
  104. label: '怀旧'
  105. },
  106. {
  107. value: 'blues',
  108. label: '蓝调'
  109. },
  110. {
  111. value: 'romantic',
  112. label: '浪漫'
  113. },
  114. {
  115. value: 'cool',
  116. label: '清凉'
  117. },
  118. {
  119. value: 'fresher',
  120. label: '清新'
  121. },
  122. {
  123. value: 'solor',
  124. label: '日系'
  125. },
  126. {
  127. value: 'aestheticism',
  128. label: '唯美'
  129. },
  130. {
  131. value: 'whitening',
  132. label: '美白'
  133. },
  134. {
  135. value: 'cerisered',
  136. label: '樱红'
  137. },
  138. ],
  139. audioReverbType: 0,
  140. audioReverbTypeArray: ['关闭', 'KTV', '小房间', '大会堂', '低沉', '洪亮', '金属声', '磁性'],
  141. },
  142. /**
  143. * 生命周期方法
  144. */
  145. lifetimes: {
  146. created: function () {
  147. // 在组件实例刚刚被创建时执行
  148. console.log(TAG_NAME, 'created', ENV)
  149. MTA.App.init({
  150. appID: '500710685',
  151. eventID: '500710697',
  152. autoReport: true,
  153. statParam: true,
  154. })
  155. },
  156. attached: function () {
  157. // 在组件实例进入页面节点树时执行
  158. console.log(TAG_NAME, 'attached')
  159. this._init()
  160. MTA.Page.stat()
  161. },
  162. ready: function () {
  163. // 在组件在视图层布局完成后执行
  164. console.log(TAG_NAME, 'ready')
  165. },
  166. detached: function () {
  167. // 在组件实例被从页面节点树移除时执行
  168. console.log(TAG_NAME, 'detached')
  169. // 停止所有拉流,并重置数据
  170. this.exitRoom()
  171. },
  172. error: function (error) {
  173. // 每当组件方法抛出错误时执行
  174. console.log(TAG_NAME, 'error', error)
  175. },
  176. },
  177. pageLifetimes: {
  178. show: function () {
  179. // 组件所在的页面被展示时执行
  180. console.log(TAG_NAME, 'show status:', this.status)
  181. if (this.status.isPending) {
  182. // 经历了 5000 挂起事件
  183. this.status.isPending = false
  184. // 修复iOS 最小化触发5000事件后,音频推流失败的问题
  185. // if (ENV.IS_IOS && this.data.pusher.enableMic) {
  186. // this.unpublishLocalAudio().then(()=>{
  187. // this.publishLocalAudio()
  188. // })
  189. // }
  190. // 经历了 5001 浮窗关闭事件,小程序底层会自动退房,恢复小程序时组件需要重新进房
  191. // 重新进房
  192. this.enterRoom({
  193. roomID: this.data.config.roomID
  194. }).then(() => {
  195. // 进房后开始推送视频或音频
  196. // setTimeout(()=>{
  197. // this.publishLocalVideo()
  198. // this.publishLocalAudio()
  199. // }, 2000)
  200. })
  201. } else if (ENV.IS_ANDROID && this.status.pageLife === 'hide' && this.status.isOnHideAddStream && this.data.streamList.length > 0) {
  202. // 微信没有提供明确的最小化事件,onHide事件,不一定是最小化
  203. // 获取所有的player 清空 src 重新赋值 验证无效
  204. // 清空 visibleStreamList 重新赋值, 验证无效
  205. // 退房重新进房,有效但是成本比较高
  206. // 将标记了 isOnHideAdd 的 stream 的 palyer 销毁并重新渲染
  207. const streamList = this.data.streamList
  208. let tempStreamList = []
  209. // 过滤 onHide 时新增的 stream
  210. for (let i = 0; i < streamList.length; i++) {
  211. if (streamList[i].isOnHideAdd && streamList[i].playerContext) {
  212. const stream = streamList[i]
  213. tempStreamList.push(stream)
  214. stream.playerContext = undefined
  215. streamList.splice(i, 1)
  216. }
  217. }
  218. // 设置渲染,销毁onHide 时新增的 player
  219. this._setList({
  220. streamList: streamList,
  221. }).then(() => {
  222. for (let i = 0; i < tempStreamList.length; i++) {
  223. streamList.push(tempStreamList[i])
  224. }
  225. // 设置渲染,重新创建 onHide 时新增的 player
  226. // setTimeout(()=>{
  227. this._setList({
  228. streamList: streamList,
  229. }).then(() => {
  230. for (let i = 0; i < tempStreamList.length; i++) {
  231. tempStreamList[i] = wx.createLivePlayerContext(tempStreamList[i].streamID, this)
  232. }
  233. tempStreamList = []
  234. })
  235. // }, 500)
  236. })
  237. this.status.isOnHideAddStream = false
  238. }
  239. this.status.pageLife = 'show'
  240. },
  241. hide: function () {
  242. // 组件所在的页面被隐藏时执行
  243. console.log(TAG_NAME, 'hide')
  244. this.status.pageLife = 'hide'
  245. },
  246. resize: function (size) {
  247. // 组件所在的页面尺寸变化时执行
  248. console.log(TAG_NAME, 'resize', size)
  249. },
  250. },
  251. /**
  252. * 组件的方法列表
  253. */
  254. methods: {
  255. fullScreen() {
  256. console.log(this.data.streamList,'我是全屏时player的数量');
  257. for (let i = 0; i < this.data.streamList.length; i++) {
  258. console.log(this.data.streamList[i])
  259. if(this.data.streamList[i].userID=="0"||this.data.streamList[i].userID=="share-userId"){
  260. let params = {
  261. userID: this.data.streamList[i].userID,
  262. streamType: this.data.streamList[i].streamType,
  263. direction: 0
  264. }
  265. let params1 = {
  266. userID: this.data.streamList[i].userID,
  267. streamType: this.data.streamList[i].streamType,
  268. orientation: 'horizontal'
  269. }
  270. this.enterFullscreen(params);
  271. this.setRemoteOrientation(params1)
  272. }
  273. }
  274. },
  275. exitFullScreen() {
  276. for (let i = 0; i < this.data.streamList.length; i++) {
  277. console.log(this.data.streamList[i])
  278. let params = {
  279. userID: this.data.streamList[i].userID,
  280. streamType: this.data.streamList[i].streamType,
  281. direction: 0
  282. }
  283. let params1 = {
  284. userID: this.data.streamList[i].userID,
  285. streamType: this.data.streamList[i].streamType,
  286. orientation: 'vertical'
  287. }
  288. this.exitFullscreen(params);
  289. this.setRemoteOrientation(params1)
  290. }
  291. },
  292. /**
  293. * 初始化各项参数和用户控制模块,在组件实例触发 attached 时调用,此时不建议对View进行变更渲染(调用setData方法)
  294. */
  295. _init() {
  296. console.log(TAG_NAME, '_init')
  297. this.userController = new UserController(this)
  298. this._emitter = new Event()
  299. this.EVENT = EVENT
  300. this._initStatus()
  301. this._bindEvent()
  302. this._gridBindEvent()
  303. this._keepScreenOn()
  304. console.log(TAG_NAME, '_init success component:', this)
  305. },
  306. _initStatus() {
  307. this.status = {
  308. isPush: false, // 推流状态
  309. isPending: false, // 挂起状态,触发5000事件标记为true,onShow后标记为false
  310. pageLife: '', // 页面生命周期 hide, show
  311. isOnHideAddStream: false, // onHide后有新增Stream
  312. }
  313. this._lastTapTime = 0 // 点击时间戳 用于判断双击事件
  314. this._beforeLastTapTime = 0 // 点击时间戳 用于判断双击事件
  315. this._lastTapCoordinate = {
  316. x: 0,
  317. y: 0
  318. }, // 点击时的坐标
  319. this._isFullscreen = false // 是否进入全屏状态
  320. },
  321. /**
  322. * 监听组件属性变更,外部变更组件属性时触发该监听
  323. * @param {Object} data newVal,oldVal
  324. */
  325. _propertyObserver(data) {
  326. console.log(TAG_NAME, '_propertyObserver', data, this.data.config)
  327. if (data.name === 'config') {
  328. const config = Object.assign({}, DEFAULT_COMPONENT_CONFIG, data.newVal)
  329. console.log(TAG_NAME, '_propertyObserver config:', config)
  330. // 由于 querystring 只支持 String 类型,做一个类型防御
  331. if (typeof config.debugMode === 'string') {
  332. config.debugMode = config.debugMode === 'true' ? true : false
  333. }
  334. // 初始化IM
  335. if (config.enableIM && config.sdkAppID) {
  336. // this._initIM(config)
  337. }
  338. if (config.sdkAppID && data.oldVal.sdkAppID !== config.sdkAppID && MTA) {
  339. MTA.Event.stat('sdkAppID', {
  340. 'value': config.sdkAppID
  341. })
  342. }
  343. // 独立设置与pusher无关的配置
  344. this.setData({
  345. enableIM: config.enableIM,
  346. template: config.template,
  347. debugMode: config.debugMode || false,
  348. debug: config.debugMode || false,
  349. })
  350. this._setPusherConfig(config)
  351. }
  352. },
  353. // _______ __ __ __
  354. // | \ | \ | \| \
  355. // | $$$$$$$\ __ __ | $$____ | $$ \$$ _______
  356. // | $$__/ $$| \ | \| $$ \ | $$| \ / \
  357. // | $$ $$| $$ | $$| $$$$$$$\| $$| $$| $$$$$$$
  358. // | $$$$$$$ | $$ | $$| $$ | $$| $$| $$| $$
  359. // | $$ | $$__/ $$| $$__/ $$| $$| $$| $$_____
  360. // | $$ \$$ $$| $$ $$| $$| $$ \$$ \
  361. // \$$ \$$$$$$ \$$$$$$$ \$$ \$$ \$$$$$$$
  362. /**
  363. * 进房
  364. * @param {Object} params 必传 roomID 取值范围 1 ~ 4294967295
  365. * @returns {Promise}
  366. */
  367. enterRoom(params) {
  368. console.log(params)
  369. return new Promise((resolve, reject) => {
  370. console.log(TAG_NAME, 'enterRoom')
  371. console.log(TAG_NAME, 'params', params)
  372. console.log(TAG_NAME, 'config', this.data.config)
  373. console.log(TAG_NAME, 'pusher', this.data.pusher)
  374. // 1. 补齐进房参数,校验必要参数是否齐全
  375. if (params) {
  376. Object.assign(this.data.pusher, params)
  377. Object.assign(this.data.config, params)
  378. }
  379. console.log(this.data.config)
  380. if (!this._checkParam(this.data.config)) {
  381. reject(new Error('缺少必要参数'))
  382. return
  383. }
  384. // 2. 根据参数拼接 push url,赋值给 live-pusher,
  385. this._getPushUrl(this.data.config).then((pushUrl) => {
  386. this.data.pusher.url = pushUrl
  387. this.setData({
  388. pusher: this.data.pusher,
  389. }, () => {
  390. // 真正进房成功需要通过 1018 事件通知
  391. console.log(TAG_NAME, 'enterRoom', this.data.pusher)
  392. // view 渲染成功回调后,开始推流
  393. this.data.pusher.getPusherContext().start()
  394. this.status.isPush = true
  395. resolve()
  396. })
  397. }).catch((res) => {
  398. // 进房失败需要通过 pusher state 事件通知,目前还没有准确的事件通知
  399. console.error(TAG_NAME, 'enterRoom error', res)
  400. reject(res)
  401. })
  402. // 初始化 IM SDK
  403. // this._initIM(this.data.config)
  404. // 登录IM
  405. this._loginIM({
  406. ...this.data.config,
  407. roomID: params.roomID
  408. })
  409. })
  410. },
  411. /**
  412. * 退房,停止推流和拉流,并重置数据
  413. * @returns {Promise}
  414. */
  415. exitRoom() {
  416. if (this.status.pageLife === 'hide') {
  417. // 如果是退后台触发 onHide,不能调用 pusher API
  418. console.warn(TAG_NAME, '小程序最小化时不能调用 exitRoom,如果不想听到远端声音,可以调用取消订阅,如果不想远端听到声音,可以调用取消发布')
  419. }
  420. return new Promise((resolve, reject) => {
  421. console.log(TAG_NAME, 'exitRoom')
  422. this._exitIM()
  423. this.data.pusher.reset()
  424. this.status.isPush = false
  425. const result = this.userController.reset()
  426. this.setData({
  427. pusher: this.data.pusher,
  428. userList: result.userList,
  429. streamList: result.streamList,
  430. visibleStreamList: this._filterVisibleStream(result.streamList),
  431. }, () => {
  432. // 在销毁页面时调用exitRoom时,不会走到这里
  433. resolve({
  434. userList: this.data.userList,
  435. streamList: this.data.streamList
  436. })
  437. console.log(TAG_NAME, 'exitRoom success', this.data.pusher, this.data.streamList, this.data.userList)
  438. // 20200421 iOS 仍然没有1019事件通知退房,退房事件移动到 exitRoom 方法里,但不是后端通知的退房成功
  439. this._emitter.emit(EVENT.LOCAL_LEAVE, {
  440. userID: this.data.pusher.userID
  441. })
  442. })
  443. })
  444. },
  445. /**
  446. * 开启摄像头
  447. * @returns {Promise}
  448. */
  449. publishLocalVideo() {
  450. console.log(TAG_NAME, 'publishLocalVideo 开启摄像头')
  451. return this._setPusherConfig({
  452. enableCamera: true
  453. })
  454. },
  455. /**
  456. * 关闭摄像头
  457. * @returns {Promise}
  458. */
  459. unpublishLocalVideo() {
  460. // 设置 pusher enableCamera
  461. console.log(TAG_NAME, 'unpublshLocalVideo 关闭摄像头')
  462. return this._setPusherConfig({
  463. enableCamera: false
  464. })
  465. },
  466. /**
  467. * 开启麦克风
  468. * @returns {Promise}
  469. */
  470. publishLocalAudio() {
  471. // 设置 pusher enableCamera
  472. console.log(TAG_NAME, 'publishLocalAudio 开启麦克风')
  473. return this._setPusherConfig({
  474. enableMic: true
  475. })
  476. },
  477. /**
  478. * 关闭麦克风
  479. * @returns {Promise}
  480. */
  481. unpublishLocalAudio() {
  482. // 设置 pusher enableCamera
  483. console.log(TAG_NAME, 'unpublshLocalAudio 关闭麦克风')
  484. return this._setPusherConfig({
  485. enableMic: false
  486. })
  487. },
  488. /**
  489. * 订阅远端视频 主流 小画面 辅流
  490. * @param {Object} params {userID,streamType} streamType 传入 small 时修改对应的主流 url 的 _definitionType 参数为 small, stream.streamType 仍为 main
  491. * @returns {Promise}
  492. */
  493. subscribeRemoteVideo(params) {
  494. console.log(TAG_NAME, '订阅远端视频 主流 小画面 辅流', params)
  495. // 设置指定 user streamType 的 muteVideo 为 false
  496. const config = {
  497. muteVideo: false,
  498. }
  499. // 本地数据结构里的 streamType 只支持 main 和 aux ,订阅 small 也是对 main 进行处理
  500. const streamType = params.streamType === 'small' ? 'main' : params.streamType
  501. const stream = this.userController.getStream({
  502. userID: params.userID,
  503. streamType: streamType,
  504. })
  505. stream.muteVideoPrev = false // 用于分页切换时保留player当前的订阅状态
  506. if (params.streamType === 'small' || params.streamType === 'main') {
  507. if (stream && stream.streamType === 'main') {
  508. console.log(TAG_NAME, 'subscribeRemoteVideo switch small', stream.src)
  509. if (params.streamType === 'small') {
  510. config.src = stream.src.replace('main', 'small')
  511. config._definitionType = 'small' // 用于设置面板的渲染
  512. } else if (params.streamType === 'main') {
  513. stream.src = stream.src.replace('small', 'main')
  514. config._definitionType = 'main'
  515. }
  516. console.log(TAG_NAME, 'subscribeRemoteVideo', stream.src)
  517. }
  518. }
  519. return this._setPlayerConfig({
  520. userID: params.userID,
  521. streamType: streamType,
  522. config: config,
  523. })
  524. },
  525. /**
  526. * 取消订阅远端视频
  527. * @param {Object} params {userID,streamType}
  528. * @returns {Promise}
  529. */
  530. unsubscribeRemoteVideo(params) {
  531. console.log(TAG_NAME, '取消订阅远端视频', params)
  532. const stream = this.userController.getStream({
  533. userID: params.userID,
  534. streamType: params.streamType,
  535. })
  536. stream.muteVideoPrev = true // 用于分页切换时保留player当前的订阅状态
  537. // 设置指定 user streamType 的 muteVideo 为 true
  538. return this._setPlayerConfig({
  539. userID: params.userID,
  540. streamType: params.streamType,
  541. config: {
  542. muteVideo: true,
  543. },
  544. })
  545. },
  546. /**
  547. * 订阅远端音频
  548. * @param {Object} params userID 用户ID
  549. * @returns {Promise}
  550. */
  551. subscribeRemoteAudio(params) {
  552. console.log(TAG_NAME, 'subscribeRemoteAudio', params)
  553. // if (params.userID == "0" || params.userID == "share-userId") {
  554. return this._setPlayerConfig({
  555. userID: params.userID,
  556. streamType: 'main',
  557. config: {
  558. muteAudio: false,
  559. },
  560. })
  561. // }
  562. },
  563. /**
  564. * 取消订阅远端音频
  565. * @param {Object} params userID 用户ID
  566. * @returns {Promise}
  567. */
  568. unsubscribeRemoteAudio(params) {
  569. console.log(TAG_NAME, 'unsubscribeRemoteAudio', params)
  570. return this._setPlayerConfig({
  571. userID: params.userID,
  572. streamType: 'main',
  573. config: {
  574. muteAudio: true,
  575. },
  576. })
  577. },
  578. on(eventCode, handler, context) {
  579. this._emitter.on(eventCode, handler, context)
  580. },
  581. off(eventCode, handler) {
  582. this._emitter.off(eventCode, handler)
  583. },
  584. getRemoteUserList() {
  585. return this.data.userList
  586. },
  587. /**
  588. * 切换前后摄像头
  589. */
  590. switchCamera() {
  591. if (!this.data.cameraPosition) {
  592. // this.data.pusher.cameraPosition 是初始值,不支持动态设置
  593. this.data.cameraPosition = this.data.pusher.frontCamera
  594. }
  595. console.log(TAG_NAME, 'switchCamera', this.data.cameraPosition)
  596. this.data.cameraPosition = this.data.cameraPosition === 'front' ? 'back' : 'front'
  597. this.setData({
  598. cameraPosition: this.data.cameraPosition,
  599. }, () => {
  600. console.log(TAG_NAME, 'switchCamera success', this.data.cameraPosition)
  601. })
  602. // wx 7.0.9 不支持动态设置 pusher.frontCamera ,只支持调用 API switchCamer() 设置,这里修改 cameraPosition 是为了记录状态
  603. this.data.pusher.getPusherContext().switchCamera()
  604. },
  605. /**
  606. * 设置指定player view的渲染坐标和尺寸
  607. * @param {object} params
  608. * userID: string
  609. * streamType: string
  610. * xAxis: number
  611. * yAxis: number
  612. * width: number
  613. * height: number
  614. * @returns {Promise}
  615. */
  616. setViewRect(params) {
  617. console.log(TAG_NAME, 'setViewRect', params)
  618. if (this.data.template !== 'custom') {
  619. console.warn(`如需使用setViewRect方法,请初始化时设置template:"custom", 当前 template:"${this.data.template}"`)
  620. }
  621. console.info(`不建议使用该方法动态修改样式,避免引起微信小程序渲染问题,建议直接修改 wxml wxss 进行样式定制化`)
  622. if (this.data.pusher.userID === params.userID) {
  623. return this._setPusherConfig({
  624. xAxis: params.xAxis,
  625. yAxis: params.yAxis,
  626. width: params.width,
  627. height: params.height,
  628. })
  629. }
  630. return this._setPlayerConfig({
  631. userID: params.userID,
  632. streamType: params.streamType,
  633. config: {
  634. xAxis: params.xAxis,
  635. yAxis: params.yAxis,
  636. width: params.width,
  637. height: params.height,
  638. },
  639. })
  640. },
  641. /**
  642. * 设置指定 player 或者 pusher view 是否可见
  643. * @param {object} params
  644. * userID: string
  645. * streamType: string
  646. * isVisible:boolean
  647. * @returns {Promise}
  648. */
  649. setViewVisible(params) {
  650. console.log(TAG_NAME, '设置指定 player 或者 pusher view 是否可见', params)
  651. if (this.data.template !== 'custom') {
  652. console.warn(`如需使用setViewVisible方法,请初始化时设置template:"custom", 当前 template:"${this.data.template}"`)
  653. }
  654. console.info(`不建议使用该方法动态修改样式,避免引起微信小程序渲染问题,建议直接修改 wxml wxss 进行样式定制化`)
  655. if (this.data.pusher.userID === params.userID) {
  656. return this._setPusherConfig({
  657. isVisible: params.isVisible,
  658. })
  659. }
  660. return this._setPlayerConfig({
  661. userID: params.userID,
  662. streamType: params.streamType,
  663. config: {
  664. isVisible: params.isVisible,
  665. },
  666. })
  667. },
  668. /**
  669. * 设置指定player view的层级
  670. * @param {Object} params
  671. * userID: string
  672. * streamType: string
  673. * zIndex: number
  674. * @returns {Promise}
  675. */
  676. setViewZIndex(params) {
  677. console.log(TAG_NAME, 'setViewZIndex', params)
  678. if (this.data.template !== 'custom') {
  679. console.warn(`如需使用setViewZIndex方法,请初始化时设置template:"custom", 当前 template:"${this.data.template}"`)
  680. }
  681. console.info(`不建议使用该方法动态修改样式,避免引起微信小程序渲染问题,建议直接修改 wxml wxss 进行样式定制化`)
  682. if (this.data.pusher.userID === params.userID) {
  683. return this._setPusherConfig({
  684. zIndex: params.zindex || params.zIndex,
  685. })
  686. }
  687. return this._setPlayerConfig({
  688. userID: params.userID,
  689. streamType: params.streamType,
  690. config: {
  691. zIndex: params.zindex || params.zIndex,
  692. },
  693. })
  694. },
  695. /**
  696. * 播放背景音
  697. * @param {Object} params url
  698. * @returns {Promise}
  699. */
  700. playBGM(params) {
  701. return new Promise((resolve, reject) => {
  702. this.data.pusher.getPusherContext().playBGM({
  703. url: params.url,
  704. // 已经有相关事件不需要在这里监听,目前用于测试
  705. success: () => {
  706. console.log(TAG_NAME, '播放背景音成功')
  707. // this._emitter.emit(EVENT.BGM_PLAY_START)
  708. resolve()
  709. },
  710. fail: () => {
  711. console.log(TAG_NAME, '播放背景音失败')
  712. this._emitter.emit(EVENT.BGM_PLAY_FAIL)
  713. reject(new Error('播放背景音失败'))
  714. },
  715. // complete: () => {
  716. // console.log(TAG_NAME, '背景完成')
  717. // this._emitter.emit(EVENT.BGM_PLAY_COMPLETE)
  718. // },
  719. })
  720. })
  721. },
  722. stopBGM() {
  723. this.data.pusher.getPusherContext().stopBGM()
  724. },
  725. pauseBGM() {
  726. this.data.pusher.getPusherContext().pauseBGM()
  727. },
  728. resumeBGM() {
  729. this.data.pusher.getPusherContext().resumeBGM()
  730. },
  731. /**
  732. * 设置背景音音量
  733. * @param {Object} params volume
  734. */
  735. setBGMVolume(params) {
  736. console.log(TAG_NAME, 'setBGMVolume', params)
  737. this.data.pusher.getPusherContext().setBGMVolume({
  738. volume: params.volume
  739. })
  740. },
  741. /**
  742. * 设置麦克风音量
  743. * @param {Object} params volume
  744. */
  745. setMICVolume(params) {
  746. console.log(TAG_NAME, 'setMICVolume', params)
  747. this.data.pusher.getPusherContext().setMICVolume({
  748. volume: params.volume
  749. })
  750. },
  751. /**
  752. * 发送SEI消息
  753. * @param {Object} params message
  754. * @returns {Promise}
  755. */
  756. sendSEI(params) {
  757. return new Promise((resolve, reject) => {
  758. this.data.pusher.getPusherContext().sendMessage({
  759. msg: params.message,
  760. success: function (result) {
  761. resolve(result)
  762. },
  763. })
  764. })
  765. },
  766. /**
  767. * pusher 和 player 的截图并保存
  768. * @param {Object} params userID streamType
  769. * @returns {Promise}
  770. */
  771. snapshot(params) {
  772. console.log(TAG_NAME, 'snapshot', params)
  773. return new Promise((resolve, reject) => {
  774. this.captureSnapshot(params).then((result) => {
  775. wx.saveImageToPhotosAlbum({
  776. filePath: result.tempImagePath,
  777. success(res) {
  778. wx.showToast({
  779. title: '已保存到相册',
  780. })
  781. console.log('save photo is success', res)
  782. resolve(result)
  783. },
  784. fail: function (error) {
  785. wx.showToast({
  786. icon: 'none',
  787. title: '保存失败',
  788. })
  789. console.log('save photo is fail', error)
  790. reject(error)
  791. },
  792. })
  793. }).catch((error) => {
  794. reject(error)
  795. })
  796. })
  797. },
  798. /**
  799. * 获取pusher 和 player 的截图
  800. * @param {Object} params userID streamType
  801. * @returns {Promise}
  802. */
  803. captureSnapshot(params) {
  804. return new Promise((resolve, reject) => {
  805. if (params.userID === this.data.pusher.userID) {
  806. // pusher
  807. this.data.pusher.getPusherContext().snapshot({
  808. quality: 'raw',
  809. complete: (result) => {
  810. console.log(TAG_NAME, 'snapshot pusher', result)
  811. if (result.tempImagePath) {
  812. resolve(result)
  813. } else {
  814. console.log('snapShot 回调失败', result)
  815. reject(new Error('截图失败'))
  816. }
  817. },
  818. })
  819. } else {
  820. // player
  821. this.userController.getStream(params).playerContext.snapshot({
  822. quality: 'raw',
  823. complete: (result) => {
  824. console.log(TAG_NAME, 'snapshot player', result)
  825. if (result.tempImagePath) {
  826. resolve(result)
  827. } else {
  828. console.log('snapShot 回调失败', result)
  829. reject(new Error('截图失败'))
  830. }
  831. },
  832. })
  833. }
  834. })
  835. },
  836. /**
  837. * 将远端视频全屏
  838. * @param {Object} params userID streamType direction
  839. * @returns {Promise}
  840. */
  841. enterFullscreen(params) {
  842. console.log(TAG_NAME, 'enterFullscreen', params)
  843. return new Promise((resolve, reject) => {
  844. this.userController.getStream(params).playerContext.requestFullScreen({
  845. direction: params.direction || 0,
  846. success: (event) => {
  847. console.log(TAG_NAME, 'enterFullscreen success', event)
  848. this.setData({
  849. fullScreenFlag: 1
  850. })
  851. resolve(event)
  852. },
  853. fail: (event) => {
  854. console.log(TAG_NAME, 'enterFullscreen fail', event)
  855. reject(event)
  856. },
  857. })
  858. })
  859. },
  860. /**
  861. * 将远端视频取消全屏
  862. * @param {Object} params userID streamType
  863. * @returns {Promise}
  864. */
  865. exitFullscreen(params) {
  866. console.log(TAG_NAME, 'exitFullscreen', params)
  867. return new Promise((resolve, reject) => {
  868. this.userController.getStream(params).playerContext.exitFullScreen({
  869. success: (event) => {
  870. console.log(TAG_NAME, 'exitFullScreen success', event)
  871. this.setData({
  872. fullScreenFlag: 0
  873. })
  874. resolve(event)
  875. },
  876. fail: (event) => {
  877. console.log(TAG_NAME, 'exitFullScreen fail', event)
  878. reject(event)
  879. },
  880. })
  881. })
  882. },
  883. /**
  884. * 设置 player 视图的横竖屏显示
  885. * @param {Object} params userID streamType orientation: vertical, horizontal
  886. * @returns {Promise}
  887. */
  888. setRemoteOrientation(params) {
  889. console.log(params)
  890. return this._setPlayerConfig({
  891. userID: params.userID,
  892. streamType: params.streamType,
  893. config: {
  894. orientation: params.orientation,
  895. },
  896. })
  897. },
  898. // 改为:
  899. setViewOrientation(params) {
  900. return this._setPlayerConfig({
  901. userID: params.userID,
  902. streamType: params.streamType,
  903. config: {
  904. orientation: params.orientation,
  905. },
  906. })
  907. },
  908. /**
  909. * 设置 player 视图的填充模式
  910. * @param {Object} params userID streamType fillMode: contain,fillCrop
  911. * @returns {Promise}
  912. */
  913. setRemoteFillMode(params) {
  914. return this._setPlayerConfig({
  915. userID: params.userID,
  916. streamType: params.streamType,
  917. config: {
  918. objectFit: params.fillMode,
  919. },
  920. })
  921. },
  922. // 改为:
  923. setViewFillMode(params) {
  924. return this._setPlayerConfig({
  925. userID: params.userID,
  926. streamType: params.streamType,
  927. config: {
  928. objectFit: params.fillMode,
  929. },
  930. })
  931. },
  932. /**
  933. * 发送C2C文本消息
  934. * @param {*} params userID,message
  935. * @returns {Promise}
  936. */
  937. sendC2CTextMessage(params) {
  938. if (!this.tim) {
  939. console.warn(TAG_NAME, '未开启IM功能,该方法无法使用', params)
  940. return
  941. }
  942. console.log(TAG_NAME, 'sendC2CTextMessage', params)
  943. const message = this.tim.createTextMessage({
  944. to: params.userID + '',
  945. conversationType: TIM.TYPES.CONV_C2C,
  946. payload: {
  947. text: params.message,
  948. },
  949. })
  950. const promise = this.tim.sendMessage(message)
  951. promise.then(function (imResponse) {
  952. // 发送成功
  953. console.log(TAG_NAME, 'sendC2CTextMessage success', imResponse)
  954. }).catch(function (imError) {
  955. // 发送失败
  956. console.warn(TAG_NAME, 'sendC2CTextMessage error:', imError)
  957. })
  958. return promise
  959. },
  960. /**
  961. * 发送C2C自定义消息
  962. * @param {*} params: userID payload
  963. * @returns {Promise}
  964. *
  965. */
  966. sendC2CCustomMessage(params) {
  967. if (!this.tim) {
  968. console.warn(TAG_NAME, '未开启IM功能,该方法无法使用', params)
  969. return
  970. }
  971. console.log(TAG_NAME, 'sendC2CCustomMessage', params)
  972. const message = this.tim.createCustomMessage({
  973. to: params.userID + '',
  974. conversationType: TIM.TYPES.CONV_C2C,
  975. payload: params.payload,
  976. })
  977. const promise = this.tim.sendMessage(message)
  978. promise.then(function (imResponse) {
  979. // 发送成功
  980. console.log(TAG_NAME, 'sendMessage success', imResponse)
  981. }).catch(function (imError) {
  982. // 发送失败
  983. console.warn(TAG_NAME, 'sendMessage error:', imError)
  984. })
  985. return promise
  986. },
  987. /**
  988. * 发送群组文本消息
  989. * @param {*} params roomID message
  990. * @returns {Promise}
  991. *
  992. */
  993. sendGroupTextMessage(params) {
  994. if (!this.tim) {
  995. console.warn(TAG_NAME, '未开启IM功能,该方法无法使用', params)
  996. return
  997. }
  998. console.log(TAG_NAME, 'sendGroupTextMessage', params)
  999. const message = this.tim.createTextMessage({
  1000. to: params.roomID + '',
  1001. conversationType: TIM.TYPES.CONV_GROUP,
  1002. payload: {
  1003. text: params.message,
  1004. },
  1005. })
  1006. const promise = this.tim.sendMessage(message)
  1007. promise.then(function (imResponse) {
  1008. // 发送成功
  1009. console.log(TAG_NAME, 'sendGroupTextMessage success', imResponse)
  1010. }).catch(function (imError) {
  1011. // 发送失败
  1012. console.warn(TAG_NAME, 'sendGroupTextMessage error:', imError)
  1013. })
  1014. return promise
  1015. },
  1016. /**
  1017. * 发送群组自定义消息
  1018. * @param {*} params roomID payload
  1019. * @returns {Promise}
  1020. *
  1021. */
  1022. sendGroupCustomMessage(params) {
  1023. if (!this.tim) {
  1024. console.warn(TAG_NAME, '未开启IM功能,该方法无法使用', params)
  1025. return
  1026. }
  1027. console.log(TAG_NAME, 'sendGroupCustomMessage', params)
  1028. const message = this.tim.createCustomMessage({
  1029. to: params.roomID + '',
  1030. conversationType: TIM.TYPES.CONV_GROUP,
  1031. payload: params.payload,
  1032. })
  1033. const promise = this.tim.sendMessage(message)
  1034. promise.then(function (imResponse) {
  1035. // 发送成功
  1036. console.log(TAG_NAME, 'sendMessage success', imResponse)
  1037. }).catch(function (imError) {
  1038. // 发送失败
  1039. console.warn(TAG_NAME, 'sendMessage error:', imError)
  1040. })
  1041. return promise
  1042. },
  1043. // ______ __ __
  1044. // | \ | \ | \
  1045. // \$$$$$$ _______ _| $$_ ______ ______ _______ ______ | $$
  1046. // | $$ | \| $$ \ / \ / \ | \ | \ | $$
  1047. // | $$ | $$$$$$$\\$$$$$$ | $$$$$$\| $$$$$$\| $$$$$$$\ \$$$$$$\| $$
  1048. // | $$ | $$ | $$ | $$ __ | $$ $$| $$ \$$| $$ | $$ / $$| $$
  1049. // _| $$_ | $$ | $$ | $$| \| $$$$$$$$| $$ | $$ | $$| $$$$$$$| $$
  1050. // | $$ \| $$ | $$ \$$ $$ \$$ \| $$ | $$ | $$ \$$ $$| $$
  1051. // \$$$$$$ \$$ \$$ \$$$$ \$$$$$$$ \$$ \$$ \$$ \$$$$$$$ \$$
  1052. /**
  1053. * 设置推流参数并触发页面渲染更新
  1054. * @param {Object} config live-pusher 的配置
  1055. * @returns {Promise}
  1056. */
  1057. _setPusherConfig(config, skipLog = false) {
  1058. if (!skipLog) {
  1059. console.log(TAG_NAME, '设置推流参数并触发页面渲染更新', config, this.data.pusher)
  1060. }
  1061. return new Promise((resolve, reject) => {
  1062. if (!this.data.pusher) {
  1063. this.data.pusher = new Pusher(config)
  1064. } else {
  1065. Object.assign(this.data.pusher, config)
  1066. }
  1067. this.setData({
  1068. pusher: this.data.pusher,
  1069. }, () => {
  1070. if (!skipLog) {
  1071. console.log(TAG_NAME, '_setPusherConfig setData compelete', 'config:', config, 'pusher:', this.data.pusher)
  1072. }
  1073. resolve(config)
  1074. })
  1075. })
  1076. },
  1077. /**
  1078. * 设置指定 player 属性并触发页面渲染
  1079. * @param {Object} params include userID,streamType,config
  1080. * @returns {Promise}
  1081. */
  1082. _setPlayerConfig(params) {
  1083. console.log(params,"000000000000000");
  1084. const userID = params.userID
  1085. const streamType = params.streamType
  1086. const config = params.config
  1087. console.log(TAG_NAME, '设置指定 player 属性并触发页面渲染', params)
  1088. return new Promise((resolve, reject) => {
  1089. // 获取指定的userID streamType 的 stream
  1090. const user = this.userController.getUser(userID)
  1091. if (user && user.streams[streamType]) {
  1092. Object.assign(user.streams[streamType], config)
  1093. // user.streams引用的对象和 streamList 里的是同一个
  1094. this.setData({
  1095. streamList: this.data.streamList,
  1096. visibleStreamList: this._filterVisibleStream(this.data.streamList, true),
  1097. }, () => {
  1098. // console.log(TAG_NAME, '_setPlayerConfig complete', params, 'streamList:', this.data.streamList)
  1099. resolve(params)
  1100. })
  1101. } else {
  1102. // 不需要reject,静默处理
  1103. console.warn(TAG_NAME, '指定 userID 或者 streamType 不存在')
  1104. // reject(new Error('指定 userID 或者 streamType 不存在'))
  1105. }
  1106. })
  1107. },
  1108. /**
  1109. * 设置列表数据,并触发页面渲染
  1110. * @param {Object} params include userList, stramList
  1111. * @returns {Promise}
  1112. */
  1113. _setList(params) {
  1114. console.log(TAG_NAME, '_setList', params, this.data.template)
  1115. const {
  1116. userList,
  1117. streamList
  1118. } = params
  1119. return new Promise((resolve, reject) => {
  1120. let visibleStreamList = []
  1121. const data = {
  1122. userList: userList || this.data.userList,
  1123. streamList: streamList || this.data.streamList,
  1124. }
  1125. if (this.data.template === 'grid') {
  1126. visibleStreamList = this._filterVisibleStream(streamList)
  1127. data.visibleStreamList = visibleStreamList || this.data.visibleStreamList
  1128. data.gridPagePlaceholderStreamList = this.data.gridPagePlaceholderStreamList
  1129. data.gridCurrentPage = this.data.gridCurrentPage
  1130. data.gridPageCount = this.data.gridPageCount
  1131. }
  1132. this.setData(data, () => {
  1133. resolve(params)
  1134. })
  1135. })
  1136. },
  1137. /**
  1138. * 必选参数检测
  1139. * @param {Object} rtcConfig rtc参数
  1140. * @returns {Boolean}
  1141. */
  1142. _checkParam(rtcConfig) {
  1143. console.log(TAG_NAME, 'checkParam config:', rtcConfig)
  1144. if (!rtcConfig.sdkAppID) {
  1145. console.error('未设置 sdkAppID')
  1146. return false
  1147. }
  1148. if (rtcConfig.roomID === undefined) {
  1149. console.error('未设置 roomID')
  1150. return false
  1151. }
  1152. if (rtcConfig.roomID < 1 || rtcConfig.roomID > 4294967296) {
  1153. console.error('roomID 超出取值范围 1 ~ 4294967295')
  1154. return false
  1155. }
  1156. if (!rtcConfig.userID) {
  1157. console.error('未设置 userID')
  1158. return false
  1159. }
  1160. if (!rtcConfig.userSig) {
  1161. console.error('未设置 userSig')
  1162. return false
  1163. }
  1164. if (!rtcConfig.template) {
  1165. console.error('未设置 template')
  1166. return false
  1167. }
  1168. return true
  1169. },
  1170. _getPushUrl(rtcConfig) {
  1171. // 拼接 puhser url rtmp 方案
  1172. console.log(TAG_NAME, '_getPushUrl', rtcConfig)
  1173. if (ENV.IS_TRTC) {
  1174. // 版本高于7.0.8,基础库版本高于2.10.0 使用新的 url
  1175. return new Promise((resolve, reject) => {
  1176. // appscene videocall live
  1177. // cloudenv PRO CCC DEV UAT
  1178. // encsmall 0
  1179. // 对外的默认值是rtc ,对内的默认值是videocall
  1180. rtcConfig.scene = !rtcConfig.scene || rtcConfig.scene === 'rtc' ? 'videocall' : rtcConfig.scene
  1181. rtcConfig.enableBlackStream = rtcConfig.enableBlackStream || '' // 是否支持在纯音频下推送SEI消息,注意:在关闭enable-recv-message后还是无法接收
  1182. rtcConfig.encsmall = rtcConfig.encsmall || 0 // 是否编小画面,这个特性不建议学生默认开启,只有老师端才比较有意义
  1183. rtcConfig.cloudenv = rtcConfig.cloudenv || 'PRO'
  1184. rtcConfig.streamID = rtcConfig.streamID || '' // 指定旁边路直播的流ID
  1185. rtcConfig.userDefineRecordID = rtcConfig.userDefineRecordID || '' // 指定录制文件的recordid
  1186. rtcConfig.privateMapKey = rtcConfig.privateMapKey || '' // 字符串房间号
  1187. rtcConfig.pureAudioMode = rtcConfig.pureAudioMode || '' // 指定是否纯音频推流及录制,默认不填,值为1 或 2,其他值非法不处理
  1188. rtcConfig.recvMode = rtcConfig.recvMode || 1 // 1. 自动接收音视频 2. 仅自动接收音频 3. 仅自动接收视频 4. 音视频都不自动接收, 不能绑定player
  1189. let roomID = ''
  1190. if (/^\d+$/.test(rtcConfig.roomID)) {
  1191. // 数字房间号
  1192. roomID = '&roomid=' + rtcConfig.roomID
  1193. } else {
  1194. // 字符串房间号
  1195. roomID = '&strroomid=' + rtcConfig.roomID
  1196. }
  1197. setTimeout(() => {
  1198. const pushUrl = 'room://cloud.tencent.com/rtc?sdkappid=' + rtcConfig.sdkAppID +
  1199. roomID +
  1200. '&userid=' + rtcConfig.userID +
  1201. '&usersig=' + rtcConfig.userSig +
  1202. '&appscene=' + rtcConfig.scene +
  1203. '&encsmall=' + rtcConfig.encsmall +
  1204. '&cloudenv=' + rtcConfig.cloudenv +
  1205. '&enableBlackStream=' + rtcConfig.enableBlackStream +
  1206. '&streamid=' + rtcConfig.streamID +
  1207. '&userdefinerecordid=' + rtcConfig.userDefineRecordID +
  1208. '&privatemapkey=' + rtcConfig.privateMapKey +
  1209. '&pureaudiomode=' + rtcConfig.pureAudioMode +
  1210. '&recvmode=' + rtcConfig.recvMode
  1211. console.warn(TAG_NAME, 'getPushUrl result:', pushUrl)
  1212. resolve(pushUrl)
  1213. }, 0)
  1214. })
  1215. }
  1216. console.error(TAG_NAME, '组件仅支持微信 App iOS >=7.0.9, Android >= 7.0.8, 小程序基础库版 >= 2.10.0')
  1217. console.error(TAG_NAME, '需要真机运行,开发工具不支持实时音视频')
  1218. },
  1219. /**
  1220. * 获取签名和推流地址
  1221. * @param {Object} rtcConfig 进房参数配置
  1222. * @returns {Promise}
  1223. */
  1224. _requestSigServer(rtcConfig) {
  1225. console.log(TAG_NAME, '_requestSigServer:', rtcConfig)
  1226. const sdkAppID = rtcConfig.sdkAppID
  1227. const userID = rtcConfig.userID
  1228. const userSig = rtcConfig.userSig
  1229. const roomID = rtcConfig.roomID
  1230. const privateMapKey = rtcConfig.privateMapKey
  1231. rtcConfig.useCloud = rtcConfig.useCloud === undefined ? true : rtcConfig.useCloud
  1232. let url = rtcConfig.useCloud ? 'https://official.opensso.tencent-cloud.com/v4/openim/jsonvideoapp' : 'https://yun.tim.qq.com/v4/openim/jsonvideoapp'
  1233. url += '?sdkappid=' + sdkAppID + '&identifier=' + userID + '&usersig=' + userSig + '&random=' + Date.now() + '&contenttype=json'
  1234. const reqHead = {
  1235. 'Cmd': 1,
  1236. 'SeqNo': 1,
  1237. 'BusType': 7,
  1238. 'GroupId': roomID,
  1239. }
  1240. const reqBody = {
  1241. 'PrivMapEncrypt': privateMapKey,
  1242. 'TerminalType': 1,
  1243. 'FromType': 3,
  1244. 'SdkVersion': 26280566,
  1245. }
  1246. console.log(TAG_NAME, '_requestSigServer:', url, reqHead, reqBody)
  1247. return new Promise((resolve, reject) => {
  1248. wx.request({
  1249. url: url,
  1250. data: {
  1251. 'ReqHead': reqHead,
  1252. 'ReqBody': reqBody,
  1253. },
  1254. method: 'POST',
  1255. success: (res) => {
  1256. console.log('_requestSigServer success:', res)
  1257. if (res.data['ErrorCode'] || res.data['RspHead']['ErrorCode'] !== 0) {
  1258. // console.error(res.data['ErrorInfo'] || res.data['RspHead']['ErrorInfo'])
  1259. console.error('获取roomsig失败')
  1260. reject(res)
  1261. }
  1262. const roomSig = JSON.stringify(res.data['RspBody'])
  1263. let pushUrl = 'room://cloud.tencent.com?sdkappid=' + sdkAppID + '&roomid=' + roomID + '&userid=' + userID + '&roomsig=' + encodeURIComponent(roomSig)
  1264. // TODO 需要重新整理的逻辑 TRTC尚未支持 20200213
  1265. // 如果有配置纯音频推流或者recordId参数
  1266. if (rtcConfig.pureAudioPushMod || rtcConfig.recordId) {
  1267. const bizbuf = {
  1268. Str_uc_params: {
  1269. pure_audio_push_mod: 0,
  1270. record_id: 0,
  1271. },
  1272. }
  1273. // 纯音频推流
  1274. if (rtcConfig.pureAudioPushMod) {
  1275. bizbuf.Str_uc_params.pure_audio_push_mod = rtcConfig.pureAudioPushMod
  1276. } else {
  1277. delete bizbuf.Str_uc_params.pure_audio_push_mod
  1278. }
  1279. // 自动录制时业务自定义id
  1280. if (rtcConfig.recordId) {
  1281. bizbuf.Str_uc_params.record_id = rtcConfig.recordId
  1282. } else {
  1283. delete bizbuf.Str_uc_params.record_id
  1284. }
  1285. pushUrl += '&bizbuf=' + encodeURIComponent(JSON.stringify(bizbuf))
  1286. }
  1287. console.log('roomSigInfo', pushUrl)
  1288. resolve(pushUrl)
  1289. },
  1290. fail: (res) => {
  1291. console.log(TAG_NAME, 'requestSigServer fail:', res)
  1292. reject(res)
  1293. },
  1294. })
  1295. })
  1296. },
  1297. _doubleTabToggleFullscreen(event) {
  1298. const curTime = event.timeStamp
  1299. const lastTime = this._lastTapTime
  1300. const lastTapCoordinate = this._lastTapCoordinate
  1301. const currentTapCoordinate = event.detail
  1302. // 计算两次点击的距离
  1303. const distence = Math.sqrt(Math.pow(Math.abs(currentTapCoordinate.x - lastTapCoordinate.x), 2) + Math.pow(Math.abs(currentTapCoordinate.y - lastTapCoordinate.y), 2))
  1304. this._lastTapCoordinate = currentTapCoordinate
  1305. // 已知问题:上次全屏操作后,必须等待1.5s后才能再次进行全屏操作,否则引发SDK全屏异常,因此增加节流逻辑
  1306. const beforeLastTime = this._beforeLastTapTime
  1307. console.log(TAG_NAME, '_doubleTabToggleFullscreen', event, lastTime, beforeLastTime, distence)
  1308. if (curTime - lastTime > 0 && curTime - lastTime < 300 && lastTime - beforeLastTime > 1500 && distence < 20) {
  1309. const userID = event.currentTarget.dataset.userid
  1310. const streamType = event.currentTarget.dataset.streamtype
  1311. if (this._isFullscreen) {
  1312. this.exitFullscreen({
  1313. userID,
  1314. streamType
  1315. }).then(() => {
  1316. this._isFullscreen = false
  1317. }).catch(() => {})
  1318. } else {
  1319. // const stream = this.userController.getStream({ userID, streamType })
  1320. let direction
  1321. // // 已知问题:视频的尺寸需要等待player触发NetStatus事件才能获取到,如果进房就双击全屏,全屏后的方向有可能不对。
  1322. // if (stream && stream.videoWidth && stream.videoHeight) {
  1323. // // 如果是横视频,全屏时进行横屏处理。如果是竖视频,则为0
  1324. // direction = stream.videoWidth > stream.videoHeight ? 90 : 0
  1325. // }
  1326. this.enterFullscreen({
  1327. userID,
  1328. streamType,
  1329. direction
  1330. }).then(() => {
  1331. this._isFullscreen = true
  1332. }).catch(() => {})
  1333. }
  1334. this._beforeLastTapTime = lastTime
  1335. }
  1336. this._lastTapTime = curTime
  1337. },
  1338. /**
  1339. * TRTC-room 远端用户和音视频状态处理
  1340. */
  1341. _bindEvent() {
  1342. // 远端用户进房
  1343. this.userController.on(EVENT.REMOTE_USER_JOIN, (event) => {
  1344. console.log(TAG_NAME, '远端用户进房', event, event.data.userID)
  1345. this.setData({
  1346. userList: event.data.userList,
  1347. }, () => {
  1348. this._emitter.emit(EVENT.REMOTE_USER_JOIN, {
  1349. userID: event.data.userID
  1350. })
  1351. })
  1352. console.log(TAG_NAME, 'REMOTE_USER_JOIN', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  1353. })
  1354. // 远端用户离开
  1355. this.userController.on(EVENT.REMOTE_USER_LEAVE, (event) => {
  1356. console.log(TAG_NAME, '远端用户离开', event, event.data.userID)
  1357. if (event.data.userID) {
  1358. this._setList({
  1359. userList: event.data.userList,
  1360. streamList: event.data.streamList,
  1361. }).then(() => {
  1362. this._emitter.emit(EVENT.REMOTE_USER_LEAVE, {
  1363. userID: event.data.userID
  1364. })
  1365. })
  1366. }
  1367. console.log(TAG_NAME, 'REMOTE_USER_LEAVE', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  1368. })
  1369. // 视频状态 true
  1370. this.userController.on(EVENT.REMOTE_VIDEO_ADD, (event) => {
  1371. // if (event.data.stream.userID == "0" || event.data.stream.userID == "share-userId") {
  1372. console.log(TAG_NAME, '远端视频可用', event, event.data.stream.userID)
  1373. const stream = event.data.stream
  1374. // 如果Android onHide 时,新增的player 无法播放 记录标识位
  1375. if (this.status.pageLife === 'hide') {
  1376. this.status.isOnHideAddStream = true
  1377. stream.isOnHideAdd = true
  1378. }
  1379. this._setList({
  1380. userList: event.data.userList,
  1381. streamList: event.data.streamList,
  1382. }).then(() => {
  1383. // 完善 的stream 的 playerContext
  1384. stream.playerContext = wx.createLivePlayerContext(stream.streamID, this)
  1385. // 新增的需要触发一次play 默认属性才能生效
  1386. // stream.playerContext.play()
  1387. // console.log(TAG_NAME, 'REMOTE_VIDEO_ADD playerContext.play()', stream)
  1388. this._emitter.emit(EVENT.REMOTE_VIDEO_ADD, {
  1389. userID: stream.userID,
  1390. streamType: stream.streamType
  1391. })
  1392. })
  1393. console.log(TAG_NAME, 'REMOTE_VIDEO_ADD', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  1394. // }
  1395. })
  1396. // 视频状态 false
  1397. this.userController.on(EVENT.REMOTE_VIDEO_REMOVE, (event) => {
  1398. console.log(TAG_NAME, '远端视频移除', event, event.data.stream.userID)
  1399. const stream = event.data.stream
  1400. this._setList({
  1401. userList: event.data.userList,
  1402. streamList: event.data.streamList,
  1403. }).then(() => {
  1404. // 有可能先触发了退房事件,用户名下的所有stream都已清除
  1405. if (stream.userID && stream.streamType) {
  1406. this._emitter.emit(EVENT.REMOTE_VIDEO_REMOVE, {
  1407. userID: stream.userID,
  1408. streamType: stream.streamType
  1409. })
  1410. }
  1411. })
  1412. console.log(TAG_NAME, 'REMOTE_VIDEO_REMOVE', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  1413. })
  1414. // 音频可用
  1415. this.userController.on(EVENT.REMOTE_AUDIO_ADD, (event) => {
  1416. console.log(TAG_NAME, '远端音频可用', event)
  1417. // if (event.data.stream.userID == "0" || event.data.stream.userID == "share-userId") {
  1418. const stream = event.data.stream
  1419. this._setList({
  1420. userList: event.data.userList,
  1421. streamList: event.data.streamList,
  1422. }).then(() => {
  1423. stream.playerContext = wx.createLivePlayerContext(stream.streamID, this)
  1424. // 新增的需要触发一次play 默认属性才能生效
  1425. // stream.playerContext.play()
  1426. // console.log(TAG_NAME, 'REMOTE_AUDIO_ADD playerContext.play()', stream)
  1427. this._emitter.emit(EVENT.REMOTE_AUDIO_ADD, {
  1428. userID: stream.userID,
  1429. streamType: stream.streamType
  1430. })
  1431. })
  1432. console.log(TAG_NAME, 'REMOTE_AUDIO_ADD', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  1433. // }
  1434. })
  1435. // 音频不可用
  1436. this.userController.on(EVENT.REMOTE_AUDIO_REMOVE, (event) => {
  1437. console.log(TAG_NAME, '远端音频移除', event, event.data.stream.userID)
  1438. const stream = event.data.stream
  1439. this._setList({
  1440. userList: event.data.userList,
  1441. streamList: event.data.streamList,
  1442. }).then(() => {
  1443. // 有可能先触发了退房事件,用户名下的所有stream都已清除
  1444. if (stream.userID && stream.streamType) {
  1445. this._emitter.emit(EVENT.REMOTE_AUDIO_REMOVE, {
  1446. userID: stream.userID,
  1447. streamType: stream.streamType
  1448. })
  1449. }
  1450. })
  1451. console.log(TAG_NAME, 'REMOTE_AUDIO_REMOVE', 'streamList:', this.data.streamList, 'userList:', this.data.userList)
  1452. })
  1453. },
  1454. /**
  1455. * pusher event handler
  1456. * @param {*} event 事件实例
  1457. */
  1458. _pusherStateChangeHandler(event) {
  1459. const code = event.detail.code
  1460. const message = event.detail.message
  1461. console.log(TAG_NAME, 'pusherStateChange:', code, event)
  1462. switch (code) {
  1463. case 0: // 未知状态码,不做处理
  1464. console.log(TAG_NAME, message, code)
  1465. break
  1466. case 1001:
  1467. console.log(TAG_NAME, '已经连接推流服务器', code)
  1468. break
  1469. case 1002:
  1470. console.log(TAG_NAME, '已经与服务器握手完毕,开始推流', code)
  1471. break
  1472. case 1003:
  1473. console.log(TAG_NAME, '打开摄像头成功', code)
  1474. break
  1475. case 1004:
  1476. console.log(TAG_NAME, '录屏启动成功', code)
  1477. break
  1478. case 1005:
  1479. console.log(TAG_NAME, '推流动态调整分辨率', code)
  1480. break
  1481. case 1006:
  1482. console.log(TAG_NAME, '推流动态调整码率', code)
  1483. break
  1484. case 1007:
  1485. console.log(TAG_NAME, '首帧画面采集完成', code)
  1486. break
  1487. case 1008:
  1488. console.log(TAG_NAME, '编码器启动', code)
  1489. break
  1490. case 1018:
  1491. console.log(TAG_NAME, '进房成功', code)
  1492. this._emitter.emit(EVENT.LOCAL_JOIN, {
  1493. userID: this.data.pusher.userID
  1494. })
  1495. break
  1496. case 1019:
  1497. console.log(TAG_NAME, '退出房间', code)
  1498. // 20200421 iOS 仍然没有1019事件通知退房,退房事件移动到 exitRoom 方法里,但不是后端通知的退房成功
  1499. // this._emitter.emit(EVENT.LOCAL_LEAVE, { userID: this.data.pusher.userID })
  1500. break
  1501. case 2003:
  1502. console.log(TAG_NAME, '渲染首帧视频', code)
  1503. break
  1504. case 1020:
  1505. case 1031:
  1506. case 1032:
  1507. case 1033:
  1508. case 1034:
  1509. // 通过 userController 处理 1020 1031 1032 1033 1034
  1510. this.userController.userEventHandler(event)
  1511. break
  1512. case -1301:
  1513. console.error(TAG_NAME, '打开摄像头失败: ', code)
  1514. this._emitter.emit(EVENT.ERROR, {
  1515. code,
  1516. message
  1517. })
  1518. break
  1519. case -1302:
  1520. console.error(TAG_NAME, '打开麦克风失败: ', code)
  1521. this._emitter.emit(EVENT.ERROR, {
  1522. code,
  1523. message
  1524. })
  1525. break
  1526. case -1303:
  1527. console.error(TAG_NAME, '视频编码失败: ', code)
  1528. this._emitter.emit(EVENT.ERROR, {
  1529. code,
  1530. message
  1531. })
  1532. break
  1533. case -1304:
  1534. console.error(TAG_NAME, '音频编码失败: ', code)
  1535. this._emitter.emit(EVENT.ERROR, {
  1536. code,
  1537. message
  1538. })
  1539. break
  1540. case -1307:
  1541. console.error(TAG_NAME, '推流连接断开: ', code)
  1542. this._emitter.emit(EVENT.ERROR, {
  1543. code,
  1544. message
  1545. })
  1546. break
  1547. case -100018:
  1548. console.error(TAG_NAME, '进房失败: userSig 校验失败,请检查 userSig 是否填写正确', code, message)
  1549. this._emitter.emit(EVENT.ERROR, {
  1550. code,
  1551. message
  1552. })
  1553. break
  1554. case 5000:
  1555. console.log(TAG_NAME, '小程序被挂起: ', code)
  1556. // 20200421 iOS 微信点击胶囊圆点会触发该事件
  1557. // 触发 5000 后,底层SDK会退房,返回前台后会自动进房
  1558. break
  1559. case 5001:
  1560. // 20200421 仅有 Android 微信会触发该事件
  1561. console.log(TAG_NAME, '小程序悬浮窗被关闭: ', code)
  1562. this.status.isPending = true
  1563. if (this.status.isPush) {
  1564. this.exitRoom()
  1565. }
  1566. break
  1567. case 1021:
  1568. console.log(TAG_NAME, '网络类型发生变化,需要重新进房', code)
  1569. break
  1570. case 2007:
  1571. console.log(TAG_NAME, '本地视频播放loading: ', code)
  1572. break
  1573. case 2004:
  1574. console.log(TAG_NAME, '本地视频播放开始: ', code)
  1575. break
  1576. default:
  1577. console.log(TAG_NAME, message, code)
  1578. }
  1579. },
  1580. _pusherNetStatusHandler(event) {
  1581. // 触发 LOCAL_NET_STATE_UPDATE
  1582. this._emitter.emit(EVENT.LOCAL_NET_STATE_UPDATE, event)
  1583. },
  1584. _pusherErrorHandler(event) {
  1585. // 触发 ERROR
  1586. console.warn(TAG_NAME, 'pusher error', event)
  1587. try {
  1588. const code = event.detail.errCode
  1589. const message = event.detail.errMsg
  1590. this._emitter.emit(EVENT.ERROR, {
  1591. code,
  1592. message
  1593. })
  1594. } catch (exception) {
  1595. console.error(TAG_NAME, 'pusher error data parser exception', event, exception)
  1596. }
  1597. },
  1598. _pusherBGMStartHandler(event) {
  1599. // 触发 BGM_START 已经在playBGM方法中进行处理
  1600. // this._emitter.emit(EVENT.BGM_PLAY_START, { data: event })
  1601. },
  1602. _pusherBGMProgressHandler(event) {
  1603. // BGM_PROGRESS
  1604. this._emitter.emit(EVENT.BGM_PLAY_PROGRESS, event)
  1605. },
  1606. _pusherBGMCompleteHandler(event) {
  1607. // BGM_COMPLETE
  1608. this._emitter.emit(EVENT.BGM_PLAY_COMPLETE, event)
  1609. },
  1610. _pusherAudioVolumeNotify: function (event) {
  1611. // console.log(TAG_NAME, '_pusherAudioVolumeNotify', event)
  1612. this._emitter.emit(EVENT.LOCAL_AUDIO_VOLUME_UPDATE, event)
  1613. },
  1614. // player event handler
  1615. // 获取 player ID 再进行触发
  1616. _playerStateChange(event) {
  1617. // console.log(TAG_NAME, '_playerStateChange', event)
  1618. this._emitter.emit(EVENT.REMOTE_STATE_UPDATE, event)
  1619. },
  1620. _playerFullscreenChange(event) {
  1621. console.log(event, "ooooooooooooooooooooooooooooo")
  1622. this._emitter.emit(EVENT.REMOTE_FULLSCREEN_UPDATE, event)
  1623. this._emitter.emit(EVENT.VIDEO_FULLSCREEN_UPDATE, event)
  1624. },
  1625. _playerNetStatus(event) {
  1626. console.log(TAG_NAME, 'player变化了', event)
  1627. // 获取player 视频的宽高
  1628. const stream = this.userController.getStream({
  1629. userID: event.currentTarget.dataset.userid,
  1630. streamType: event.currentTarget.dataset.streamtype,
  1631. })
  1632. if (stream && (stream.videoWidth !== event.detail.info.videoWidth || stream.videoHeight !== event.detail.info.videoHeight)) {
  1633. console.log(TAG_NAME, '_playerNetStatus update video size', event)
  1634. stream.videoWidth = event.detail.info.videoWidth
  1635. stream.videoHeight = event.detail.info.videoHeight
  1636. }
  1637. this._emitter.emit(EVENT.REMOTE_NET_STATE_UPDATE, event)
  1638. },
  1639. _playerAudioVolumeNotify(event) {
  1640. // console.log(TAG_NAME, '_playerAudioVolumeNotify', event)
  1641. this._emitter.emit(EVENT.REMOTE_AUDIO_VOLUME_UPDATE, event)
  1642. },
  1643. _filterVisibleStream(streamList, skipPagination) {
  1644. const list = streamList.filter((item) => {
  1645. // 全部显示
  1646. // return true
  1647. // 只显示有视频或者有音频的 stream
  1648. return (item.hasVideo || item.hasAudio)
  1649. })
  1650. // 按 userID 进行排序
  1651. list.sort((item1, item2) => {
  1652. const id1 = item1.userID.toUpperCase()
  1653. const id2 = item2.userID.toUpperCase()
  1654. if (id1 < id2) {
  1655. return -1
  1656. }
  1657. if (id1 > id2) {
  1658. return 1
  1659. }
  1660. return 0
  1661. })
  1662. if (this.data.template === 'grid' && !skipPagination) {
  1663. this._filterGridPageVisibleStream(list)
  1664. // console.log(TAG_NAME, '_filterVisibleStream gridPagePlaceholderStreamList:', this.data.gridPagePlaceholderStreamList)
  1665. if ( // list.length > this.data.gridPlayerPerPage - 2 &&
  1666. this.data.gridCurrentPage > 1 &&
  1667. this.data.gridPagePlaceholderStreamList.length === this.data.gridPlayerPerPage) {
  1668. // 如果stream 数量大于每页可显示数量,当前页面已经没有可显示的stream(占位数量==3) 回到上一个页面。
  1669. this._gridPageToPrev(list)
  1670. }
  1671. }
  1672. // console.log(TAG_NAME, '_filterVisibleStream list:', list)
  1673. return list
  1674. },
  1675. _filterGridPageVisibleStream(list) {
  1676. // 最多只显示 gridPlayerPerPage 个stream
  1677. const length = list.length
  1678. // +1 pusher
  1679. this.data.gridPageCount = Math.ceil((length + 1) / this.data.gridPlayerPerPage)
  1680. this.data.gridPagePlaceholderStreamList = []
  1681. let visibleCount = 0
  1682. // 需要显示的player区间
  1683. let interval
  1684. if (this.data.gridPlayerPerPage > 3) {
  1685. if (this.data.gridCurrentPage === 1) {
  1686. interval = [-1, this.data.gridPlayerPerPage - 1]
  1687. } else {
  1688. // 每页显示4个时,第一页显示3个,pusher只在第一页
  1689. // -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
  1690. // 1 2 3 4
  1691. // -1 3
  1692. // 2 7
  1693. // 6 11
  1694. interval = [this.data.gridCurrentPage * this.data.gridPlayerPerPage - (this.data.gridPlayerPerPage + 2), this.data.gridCurrentPage * this.data.gridPlayerPerPage - 1]
  1695. }
  1696. } else {
  1697. // 每页显示3个,每页都有pusher
  1698. interval = [this.data.gridCurrentPage * this.data.gridPlayerPerPage - (this.data.gridPlayerPerPage + 1), this.data.gridCurrentPage * this.data.gridPlayerPerPage]
  1699. }
  1700. for (let i = 0; i < length; i++) {
  1701. if (i > interval[0] && i < interval[1]) {
  1702. list[i].isVisible = true
  1703. list[i].muteVideo = list[i].muteVideoPrev === undefined ? list[i].muteVideo : list[i].muteVideoPrev
  1704. visibleCount++
  1705. } else {
  1706. list[i].isVisible = false
  1707. list[i].muteVideo = true
  1708. }
  1709. }
  1710. // 第一页,不需要占位
  1711. if (this.data.gridCurrentPage !== 1) {
  1712. for (let i = 0; i < this.data.gridPlayerPerPage - visibleCount; i++) {
  1713. this.data.gridPagePlaceholderStreamList.push({
  1714. id: 'holder-' + i
  1715. })
  1716. }
  1717. }
  1718. return list
  1719. },
  1720. /**
  1721. * 保持屏幕常亮
  1722. */
  1723. _keepScreenOn() {
  1724. setInterval(() => {
  1725. wx.setKeepScreenOn({
  1726. keepScreenOn: true,
  1727. })
  1728. }, 20000)
  1729. },
  1730. // ______ __ __ ______ __ __
  1731. // | \| \ / \ | \ | \ | \
  1732. // \$$$$$$| $$\ / $$ \$$$$$$ _______ _| $$_ ______ ______ _______ ______ | $$
  1733. // | $$ | $$$\ / $$$ | $$ | \| $$ \ / \ / \ | \ | \ | $$
  1734. // | $$ | $$$$\ $$$$ | $$ | $$$$$$$\\$$$$$$ | $$$$$$\| $$$$$$\| $$$$$$$\ \$$$$$$\| $$
  1735. // | $$ | $$\$$ $$ $$ | $$ | $$ | $$ | $$ __ | $$ $$| $$ \$$| $$ | $$ / $$| $$
  1736. // _| $$_ | $$ \$$$| $$ _| $$_ | $$ | $$ | $$| \| $$$$$$$$| $$ | $$ | $$| $$$$$$$| $$
  1737. // | $$ \| $$ \$ | $$ | $$ \| $$ | $$ \$$ $$ \$$ \| $$ | $$ | $$ \$$ $$| $$
  1738. // \$$$$$$ \$$ \$$ \$$$$$$ \$$ \$$ \$$$$ \$$$$$$$ \$$ \$$ \$$ \$$$$$$$ \$$
  1739. /**
  1740. * 初始化 IM SDK
  1741. * @param {Object} config sdkAppID
  1742. */
  1743. _initIM(config) {
  1744. if (!config.enableIM || !config.sdkAppID || this.tim) {
  1745. return
  1746. }
  1747. console.log(TAG_NAME, '_initIM', config)
  1748. // 初始化 sdk 实例
  1749. const tim = TIM.create({
  1750. SDKAppID: config.sdkAppID,
  1751. })
  1752. // 0 普通级别,日志量较多,接入时建议使用
  1753. // 1 release级别,SDK 输出关键信息,生产环境时建议使用
  1754. // 2 告警级别,SDK 只输出告警和错误级别的日志
  1755. // 3 错误级别,SDK 只输出错误级别的日志
  1756. // 4 无日志级别,SDK 将不打印任何日志
  1757. if (config.debugMode) {
  1758. tim.setLogLevel(1)
  1759. } else {
  1760. tim.setLogLevel(1)
  1761. }
  1762. // 取消监听
  1763. tim.off(TIM.EVENT.SDK_READY, this._onIMReady)
  1764. tim.off(TIM.EVENT.MESSAGE_RECEIVED, this._onIMMessageReceived)
  1765. tim.off(TIM.EVENT.SDK_NOT_READY, this._onIMNotReady)
  1766. tim.off(TIM.EVENT.KICKED_OUT, this._onIMKickedOut)
  1767. tim.off(TIM.EVENT.ERROR, this._onIMError)
  1768. // 监听事件
  1769. tim.on(TIM.EVENT.SDK_READY, this._onIMReady, this)
  1770. tim.on(TIM.EVENT.MESSAGE_RECEIVED, this._onIMMessageReceived, this)
  1771. tim.on(TIM.EVENT.SDK_NOT_READY, this._onIMNotReady, this)
  1772. tim.on(TIM.EVENT.KICKED_OUT, this._onIMKickedOut, this)
  1773. tim.on(TIM.EVENT.ERROR, this._onIMError, this)
  1774. this.tim = tim
  1775. wx.tim = tim
  1776. },
  1777. _loginIM(params) {
  1778. if (!this.tim) {
  1779. return
  1780. }
  1781. console.log(TAG_NAME, '_loginIM', params)
  1782. return this.tim.login({
  1783. userID: params.userID,
  1784. userSig: params.userSig,
  1785. })
  1786. },
  1787. _logoutIM() {
  1788. if (!this.tim) {
  1789. return
  1790. }
  1791. console.log(TAG_NAME, '_logoutIM')
  1792. return this.tim.logout()
  1793. },
  1794. _exitIM() {
  1795. // 方法需要调用限制,否则重复解散群 退群会有warn
  1796. if (this.data.exitIMThrottle || !this.tim) {
  1797. return
  1798. }
  1799. this.data.exitIMThrottle = true
  1800. const userList = this.getRemoteUserList()
  1801. const roomID = this.data.config.roomID
  1802. const userID = this.data.config.userID
  1803. this._searchGroup({
  1804. roomID
  1805. }).then((imResponse) => {
  1806. // 查询群资料,判断是否为群主
  1807. if (imResponse.data.group.ownerID === userID && userList.length === 0) {
  1808. // 如果 userList 为 0 群主可以解散群,并登出IM
  1809. this._dismissGroup({
  1810. roomID
  1811. }).then(() => {
  1812. this.data.exitIMThrottle = false
  1813. this._logoutIM()
  1814. }).catch((imError) => {
  1815. this.data.exitIMThrottle = false
  1816. this._logoutIM()
  1817. })
  1818. } else if (imResponse.data.group.ownerID === userID) {
  1819. this.data.exitIMThrottle = false
  1820. // 群主不能退群只能登出
  1821. this._logoutIM()
  1822. } else {
  1823. // 普通成员退群并登出IM
  1824. this._quitGroup({
  1825. roomID
  1826. }).then(() => {
  1827. this.data.exitIMThrottle = false
  1828. this._logoutIM()
  1829. }).catch((imError) => {
  1830. this.data.exitIMThrottle = false
  1831. this._logoutIM()
  1832. })
  1833. }
  1834. }).catch((imError) => {
  1835. this.data.exitIMThrottle = false
  1836. // 查询异常直接登出
  1837. this._logoutIM()
  1838. })
  1839. },
  1840. _searchGroup(params) {
  1841. if (!this.tim) {
  1842. return
  1843. }
  1844. console.log(TAG_NAME, '_searchGroup', params)
  1845. const tim = this.tim
  1846. const promise = tim.searchGroupByID(params.roomID + '')
  1847. promise.then(function (imResponse) {
  1848. // const group = imResponse.data.group // 群组信息
  1849. console.log(TAG_NAME, '_searchGroup success', imResponse)
  1850. }).catch(function (imError) {
  1851. console.warn(TAG_NAME, '_searchGroup fail,TIM 报错信息不影响后续逻辑,可以忽略', imError) // 搜素群组失败的相关信息
  1852. })
  1853. return promise
  1854. },
  1855. /**
  1856. * 创建 AVchatroom
  1857. * @param {*} params roomID
  1858. * @returns {Promise}
  1859. */
  1860. _createGroup(params) {
  1861. if (!this.tim) {
  1862. return
  1863. }
  1864. console.log(TAG_NAME, '_createGroup', params)
  1865. const promise = this.tim.createGroup({
  1866. groupID: params.roomID + '',
  1867. name: params.roomID + '',
  1868. type: IM_GROUP_TYPE,
  1869. })
  1870. promise.then((imResponse) => { // 创建成功
  1871. console.log(TAG_NAME, '_createGroup success', imResponse.data.group) // 创建的群的资料
  1872. }).catch((imError) => {
  1873. console.warn(TAG_NAME, '_createGroup error', imError) // 创建群组失败的相关信息
  1874. })
  1875. return promise
  1876. },
  1877. /**
  1878. * 进入 AVchatroom
  1879. * @param {*} params roomID
  1880. * @returns {Promise}
  1881. */
  1882. _joinGroup(params) {
  1883. if (!this.tim) {
  1884. return
  1885. }
  1886. console.log(TAG_NAME, '_joinGroup', params)
  1887. const promise = this.tim.joinGroup({
  1888. groupID: params.roomID + '',
  1889. type: IM_GROUP_TYPE
  1890. })
  1891. promise.then((imResponse) => {
  1892. switch (imResponse.data.status) {
  1893. case TIM.TYPES.JOIN_STATUS_WAIT_APPROVAL: // 等待管理员同意
  1894. break
  1895. case TIM.TYPES.JOIN_STATUS_SUCCESS: // 加群成功
  1896. case TIM.TYPES.JOIN_STATUS_ALREADY_IN_GROUP: // 已经在群中
  1897. // console.log(imResponse.data.group) // 加入的群组资料
  1898. // wx.showToast({
  1899. // title: '进群成功',
  1900. // })
  1901. console.log(TAG_NAME, '_joinGroup success', imResponse)
  1902. break
  1903. default:
  1904. break
  1905. }
  1906. }).catch((imError) => {
  1907. console.warn(TAG_NAME, 'joinGroup error', imError) // 申请加群失败的相关信息
  1908. })
  1909. return promise
  1910. },
  1911. _quitGroup(params) {
  1912. if (!this.tim) {
  1913. return
  1914. }
  1915. console.log(TAG_NAME, '_quitGroup', params)
  1916. const promise = this.tim.quitGroup(params.roomID + '')
  1917. promise.then((imResponse) => {
  1918. console.log(TAG_NAME, '_quitGroup success', imResponse)
  1919. }).catch((imError) => {
  1920. console.warn(TAG_NAME, 'quitGroup error', imError)
  1921. })
  1922. return promise
  1923. },
  1924. _dismissGroup(params) {
  1925. if (!this.tim) {
  1926. return
  1927. }
  1928. console.log(TAG_NAME, '_dismissGroup', params)
  1929. const promise = this.tim.dismissGroup(params.roomID + '')
  1930. promise.then((imResponse) => {
  1931. console.log(TAG_NAME, '_dismissGroup success', imResponse)
  1932. }).catch((imError) => {
  1933. console.warn(TAG_NAME, '_dismissGroup error', imError)
  1934. })
  1935. return promise
  1936. },
  1937. _onIMReady(event) {
  1938. console.log(TAG_NAME, 'IM.READY', event)
  1939. this._emitter.emit(EVENT.IM_READY, event)
  1940. const roomID = this.data.config.roomID
  1941. // 查询群组是否存在
  1942. this._searchGroup({
  1943. roomID
  1944. }).then((res) => {
  1945. // console.log(TAG_NAME, 'searchGroup', res)
  1946. // 存在直接进群
  1947. this._joinGroup({
  1948. roomID
  1949. })
  1950. }).catch(() => {
  1951. // 不存在则创建,如果是avchatroom 创建后进群
  1952. this._createGroup({
  1953. roomID
  1954. }).then((res) => {
  1955. // 进群
  1956. this._joinGroup({
  1957. roomID
  1958. })
  1959. }).catch((imError) => {
  1960. if (imError.code === 10021) {
  1961. console.log(TAG_NAME, '群已存在,直接进群', event)
  1962. this._joinGroup({
  1963. roomID
  1964. })
  1965. }
  1966. })
  1967. })
  1968. // 收到离线消息和会话列表同步完毕通知,接入侧可以调用 sendMessage 等需要鉴权的接口
  1969. // event.name - TIM.EVENT.IM_READY
  1970. },
  1971. _onIMMessageReceived(event) {
  1972. // 收到推送的单聊、群聊、群提示、群系统通知的新消息,可通过遍历 event.data 获取消息列表数据并渲染到页面
  1973. console.log(TAG_NAME, 'IM.MESSAGE_RECEIVED', event)
  1974. // messageList 仅保留10条消息
  1975. const messageData = event.data
  1976. const roomID = this.data.config.roomID + ''
  1977. const userID = this.data.config.userID + ''
  1978. for (let i = 0; i < messageData.length; i++) {
  1979. const message = messageData[i]
  1980. // console.log(TAG_NAME, 'IM.MESSAGE_RECEIVED', message, this.data.config, TIM.TYPES.MSG_TEXT)
  1981. if (message.to === roomID + '' || message.to === userID) {
  1982. // 遍历messageData 获取当前room 或者当前user的消息
  1983. console.log(TAG_NAME, 'IM.MESSAGE_RECEIVED', message, message.type, TIM.TYPES.MSG_TEXT)
  1984. if (message.type === TIM.TYPES.MSG_TEXT) {
  1985. this._pushMessageList({
  1986. name: message.from,
  1987. message: message.payload.text,
  1988. })
  1989. } else {
  1990. if (message.type === TIM.TYPES.MSG_GRP_SYS_NOTICE && message.payload.operationType === 2) {
  1991. // 群系统通知
  1992. this._pushMessageList({
  1993. name: '系统通知',
  1994. message: `欢迎 ${userID}`,
  1995. })
  1996. }
  1997. // 其他消息暂不处理
  1998. }
  1999. }
  2000. }
  2001. this._emitter.emit(EVENT.IM_MESSAGE_RECEIVED, event)
  2002. },
  2003. _onIMNotReady(event) {
  2004. console.log(TAG_NAME, 'IM.NOT_READY', event)
  2005. this._emitter.emit(EVENT.IM_NOT_READY, event)
  2006. // 收到 SDK 进入 not ready 状态通知,此时 SDK 无法正常工作
  2007. // event.name - TIM.EVENT.IM_NOT_READY
  2008. },
  2009. _onIMKickedOut(event) {
  2010. console.log(TAG_NAME, 'IM.KICKED_OUT', event)
  2011. this._emitter.emit(EVENT.IM_KICKED_OUT, event)
  2012. // 收到被踢下线通知
  2013. // event.name - TIM.EVENT.KICKED_OUT
  2014. // event.data.type - 被踢下线的原因,例如 :
  2015. // - TIM.TYPES.KICKED_OUT_MULT_ACCOUNT 多实例登录被踢
  2016. // - TIM.TYPES.KICKED_OUT_MULT_DEVICE 多终端登录被踢
  2017. // - TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED 签名过期被踢。使用前需要将SDK版本升级至v2.4.0或以上。
  2018. },
  2019. _onIMError(event) {
  2020. console.log(TAG_NAME, 'IM.ERROR', event)
  2021. this._emitter.emit(EVENT.IM_ERROR, event)
  2022. // 收到 SDK 发生错误通知,可以获取错误码和错误信息
  2023. // event.name - TIM.EVENT.ERROR
  2024. // event.data.code - 错误码
  2025. // event.data.message - 错误信息
  2026. },
  2027. // ________ __ __
  2028. // | \ | \ | \
  2029. // \$$$$$$$$______ ______ ____ ______ | $$ ______ _| $$_ ______
  2030. // | $$ / \ | \ \ / \ | $$ | \| $$ \ / \
  2031. // | $$ | $$$$$$\| $$$$$$\$$$$\| $$$$$$\| $$ \$$$$$$\\$$$$$$ | $$$$$$\
  2032. // | $$ | $$ $$| $$ | $$ | $$| $$ | $$| $$ / $$ | $$ __ | $$ $$
  2033. // | $$ | $$$$$$$$| $$ | $$ | $$| $$__/ $$| $$| $$$$$$$ | $$| \| $$$$$$$$
  2034. // | $$ \$$ \| $$ | $$ | $$| $$ $$| $$ \$$ $$ \$$ $$ \$$ \
  2035. // \$$ \$$$$$$$ \$$ \$$ \$$| $$$$$$$ \$$ \$$$$$$$ \$$$$ \$$$$$$$
  2036. // | $$
  2037. // | $$
  2038. // \$$
  2039. // 以下为 debug & template 相关函数
  2040. _toggleVideo() {
  2041. if (this.data.pusher.enableCamera) {
  2042. this.unpublishLocalVideo()
  2043. } else {
  2044. this.publishLocalVideo()
  2045. }
  2046. },
  2047. _toggleAudio() {
  2048. if (this.data.pusher.enableMic) {
  2049. this.unpublishLocalAudio()
  2050. } else {
  2051. this.publishLocalAudio()
  2052. }
  2053. },
  2054. _debugToggleRemoteVideo(event) {
  2055. console.log(TAG_NAME, '_debugToggleRemoteVideo', event.currentTarget.dataset)
  2056. const userID = event.currentTarget.dataset.userID
  2057. const streamType = event.currentTarget.dataset.streamType
  2058. const stream = this.data.streamList.find((item) => {
  2059. return item.userID === userID && item.streamType === streamType
  2060. })
  2061. if (stream.muteVideo) {
  2062. this.subscribeRemoteVideo({
  2063. userID,
  2064. streamType
  2065. })
  2066. this._setPusherConfig({
  2067. isVisible: true,
  2068. })
  2069. // this.setViewVisible({ userID, streamType, isVisible: true })
  2070. } else {
  2071. this.unsubscribeRemoteVideo({
  2072. userID,
  2073. streamType
  2074. })
  2075. this._setPusherConfig({
  2076. isVisible: false,
  2077. })
  2078. // this.setViewVisible({ userID, streamType, isVisible: false })
  2079. }
  2080. },
  2081. _debugToggleRemoteAudio(event) {
  2082. console.log(TAG_NAME, '_debugToggleRemoteAudio', event.currentTarget.dataset)
  2083. const userID = event.currentTarget.dataset.userID
  2084. const streamType = event.currentTarget.dataset.streamType
  2085. const stream = this.data.streamList.find((item) => {
  2086. return item.userID === userID && item.streamType === streamType
  2087. })
  2088. if (stream.muteAudio) {
  2089. this.subscribeRemoteAudio({
  2090. userID
  2091. })
  2092. } else {
  2093. this.unsubscribeRemoteAudio({
  2094. userID
  2095. })
  2096. }
  2097. },
  2098. _debugToggleVideoDebug() {
  2099. this.setData({
  2100. debug: !this.data.debug,
  2101. })
  2102. },
  2103. _debugExitRoom() {
  2104. this.exitRoom()
  2105. },
  2106. _debugEnterRoom() {
  2107. Object.assign(this.data.pusher, this.data.config)
  2108. this.enterRoom({
  2109. roomID: this.data.config.roomID
  2110. }).then(() => {
  2111. setTimeout(() => {
  2112. this.publishLocalVideo()
  2113. this.publishLocalAudio()
  2114. }, 2000)
  2115. // 进房后开始推送视频或音频
  2116. })
  2117. },
  2118. _debugGoBack() {
  2119. wx.navigateBack({
  2120. delta: 1,
  2121. })
  2122. },
  2123. _debugTogglePanel() {
  2124. this.setData({
  2125. debugPanel: !this.data.debugPanel,
  2126. })
  2127. },
  2128. _debugSendRandomMessage() {
  2129. const userList = this.getRemoteUserList()
  2130. if (userList.length === 0 || !this.tim) {
  2131. return false
  2132. }
  2133. const roomID = this.data.config.roomID
  2134. const message = `Hello! ${userList[0].userID} ${9999 * Math.random()}`
  2135. const userID = userList[0].userID
  2136. this.sendC2CTextMessage({
  2137. userID: userID,
  2138. message: message,
  2139. })
  2140. const promise = this.sendGroupTextMessage({
  2141. roomID: roomID,
  2142. message: message,
  2143. })
  2144. // 消息上屏
  2145. this._pushMessageList({
  2146. name: userID,
  2147. message: message,
  2148. })
  2149. promise.then(function (imResponse) {
  2150. // 发送成功
  2151. console.log(TAG_NAME, '_debugSendRandomMessage success', imResponse)
  2152. wx.showToast({
  2153. title: '发送成功',
  2154. icon: 'success',
  2155. duration: 1000,
  2156. })
  2157. }).catch(function (imError) {
  2158. // 发送失败
  2159. console.warn(TAG_NAME, '_debugSendRandomMessage error', imError)
  2160. wx.showToast({
  2161. title: '发送失败',
  2162. icon: 'none',
  2163. duration: 1000,
  2164. })
  2165. })
  2166. },
  2167. _toggleAudioVolumeType() {
  2168. if (this.data.pusher.audioVolumeType === 'voicecall') {
  2169. this._setPusherConfig({
  2170. audioVolumeType: 'media',
  2171. })
  2172. } else {
  2173. this._setPusherConfig({
  2174. audioVolumeType: 'voicecall',
  2175. })
  2176. }
  2177. },
  2178. _toggleSoundMode() {
  2179. if (this.data.userList.length === 0) {
  2180. return
  2181. }
  2182. const stream = this.userController.getStream({
  2183. userID: this.data.userList[0].userID,
  2184. streamType: 'main',
  2185. })
  2186. if (stream) {
  2187. if (stream.soundMode === 'speaker') {
  2188. stream['soundMode'] = 'ear'
  2189. } else {
  2190. stream['soundMode'] = 'speaker'
  2191. }
  2192. this._setPlayerConfig({
  2193. userID: stream.userID,
  2194. streamType: 'main',
  2195. config: {
  2196. soundMode: stream['soundMode'],
  2197. },
  2198. })
  2199. }
  2200. },
  2201. /**
  2202. * 退出通话
  2203. */
  2204. _hangUp() {
  2205. this.exitRoom()
  2206. wx.navigateBack({
  2207. delta: 1,
  2208. })
  2209. },
  2210. /**
  2211. * 切换订阅音频状态
  2212. */
  2213. handleSubscribeAudio() {
  2214. if (this.data.pusher.enableMic) {
  2215. this.unpublishLocalAudio()
  2216. } else {
  2217. this.publishLocalAudio()
  2218. }
  2219. },
  2220. /**
  2221. * 切换订阅远端视频状态
  2222. * @param {Object} event native 事件对象
  2223. */
  2224. _handleSubscribeRemoteVideo(event) {
  2225. console.log(event,"切换订阅远端视频状态");
  2226. const userID = event.currentTarget.dataset.userID
  2227. const streamType = event.currentTarget.dataset.streamType
  2228. const stream = this.data.streamList.find((item) => {
  2229. return item.userID === userID && item.streamType === streamType
  2230. })
  2231. if (stream.muteVideo) {
  2232. this.subscribeRemoteVideo({
  2233. userID,
  2234. streamType
  2235. })
  2236. } else {
  2237. console.log("jddddddddddhuiafyefieygf")
  2238. this.unsubscribeRemoteVideo({
  2239. userID,
  2240. streamType
  2241. })
  2242. }
  2243. },
  2244. /**
  2245. *
  2246. * @param {Object} event native 事件对象
  2247. */
  2248. _handleSubscribeRemoteAudio(event) {
  2249. const userID = event.currentTarget.dataset.userID
  2250. const streamType = event.currentTarget.dataset.streamType
  2251. const stream = this.data.streamList.find((item) => {
  2252. return item.userID === userID && item.streamType === streamType
  2253. })
  2254. if (stream.muteAudio) {
  2255. this.subscribeRemoteAudio({
  2256. userID
  2257. })
  2258. } else {
  2259. this.unsubscribeRemoteAudio({
  2260. userID
  2261. })
  2262. }
  2263. },
  2264. /**
  2265. * grid布局, 唤起 memberlist-panel
  2266. */
  2267. _switchMemberListPanel() {
  2268. this.setData({
  2269. panelName: this.data.panelName !== 'memberlist-panel' ? 'memberlist-panel' : '',
  2270. })
  2271. },
  2272. /**
  2273. * grid布局, 唤起 setting-panel
  2274. */
  2275. _switchSettingPanel() {
  2276. this.setData({
  2277. panelName: this.data.panelName !== 'setting-panel' ? 'setting-panel' : '',
  2278. })
  2279. },
  2280. _switchBGMPanel() {
  2281. this.setData({
  2282. panelName: this.data.panelName !== 'bgm-panel' ? 'bgm-panel' : '',
  2283. })
  2284. },
  2285. _handleMaskerClick() {
  2286. this.setData({
  2287. panelName: '',
  2288. })
  2289. },
  2290. _setPuserProperty(event) {
  2291. console.log(TAG_NAME, '_setPuserProperty', event)
  2292. const key = event.currentTarget.dataset.key
  2293. const valueType = event.currentTarget.dataset.valueType
  2294. let value = event.currentTarget.dataset.value
  2295. const config = {}
  2296. if (valueType === 'boolean') {
  2297. value = value === 'true' ? true : false
  2298. config[key] = !this.data.pusher[key]
  2299. }
  2300. if (valueType === 'number' && value.indexOf('|') > 0) {
  2301. value = value.split('|')
  2302. // console.log(this.data.pusher, this.data.pusher[key], key, value)
  2303. if (this.data.pusher[key] === Number(value[0])) {
  2304. config[key] = Number(value[1])
  2305. } else {
  2306. config[key] = Number(value[0])
  2307. }
  2308. }
  2309. if (valueType === 'string' && value.indexOf('|') > 0) {
  2310. value = value.split('|')
  2311. if (this.data.pusher[key] === value[0]) {
  2312. config[key] = value[1]
  2313. } else {
  2314. config[key] = value[0]
  2315. }
  2316. }
  2317. this._setPusherConfig(config)
  2318. },
  2319. _setPlayerProperty(event) {
  2320. console.log(TAG_NAME, '设置player播放属性', event)
  2321. const userID = event.currentTarget.dataset.userid
  2322. const streamType = event.currentTarget.dataset.streamtype
  2323. const key = event.currentTarget.dataset.key
  2324. let value = event.currentTarget.dataset.value
  2325. const stream = this.userController.getStream({
  2326. userID: userID,
  2327. streamType: streamType,
  2328. })
  2329. if (!stream) {
  2330. return
  2331. }
  2332. const config = {}
  2333. if (value === 'true') {
  2334. value = true
  2335. } else if (value === 'false') {
  2336. value = false
  2337. }
  2338. if (typeof value === 'boolean') {
  2339. config[key] = !stream[key]
  2340. } else if (typeof value === 'string' && value.indexOf('|') > 0) {
  2341. value = value.split('|')
  2342. if (stream[key] === value[0]) {
  2343. config[key] = value[1]
  2344. } else {
  2345. config[key] = value[0]
  2346. }
  2347. }
  2348. console.log(TAG_NAME, '设置player播放属性', config)
  2349. this._setPlayerConfig({
  2350. userID,
  2351. streamType,
  2352. config
  2353. })
  2354. },
  2355. _changeProperty(event) {
  2356. const propertyName = event.currentTarget.dataset.propertyName
  2357. const newData = {}
  2358. newData[propertyName] = event.detail.value
  2359. this.setData(newData)
  2360. const volume = newData[propertyName] / 100
  2361. switch (propertyName) {
  2362. case 'MICVolume':
  2363. this.setMICVolume({
  2364. volume
  2365. })
  2366. break
  2367. case 'BGMVolume':
  2368. this.setBGMVolume({
  2369. volume
  2370. })
  2371. break
  2372. }
  2373. },
  2374. _switchStreamType(event) {
  2375. const userID = event.currentTarget.dataset.userid
  2376. const streamType = event.currentTarget.dataset.streamtype
  2377. const stream = this.userController.getStream({
  2378. userID: userID,
  2379. streamType: streamType,
  2380. })
  2381. if (stream && stream.streamType === 'main') {
  2382. if (stream._definitionType === 'small') {
  2383. this.subscribeRemoteVideo({
  2384. userID,
  2385. streamType: 'main'
  2386. })
  2387. } else {
  2388. this.subscribeRemoteVideo({
  2389. userID,
  2390. streamType: 'small'
  2391. })
  2392. }
  2393. }
  2394. },
  2395. _handleSnapshotClick(event) {
  2396. wx.showToast({
  2397. title: '开始截屏',
  2398. icon: 'none',
  2399. duration: 1000,
  2400. })
  2401. const userID = event.currentTarget.dataset.userid
  2402. const streamType = event.currentTarget.dataset.streamtype
  2403. this.snapshot({
  2404. userID,
  2405. streamType
  2406. })
  2407. },
  2408. /**
  2409. * grid布局, 绑定事件
  2410. */
  2411. _gridBindEvent() {
  2412. // 远端音量变更
  2413. this.on(EVENT.REMOTE_AUDIO_VOLUME_UPDATE, (event) => {
  2414. const data = event.data
  2415. const userID = data.currentTarget.dataset.userid
  2416. const streamType = data.currentTarget.dataset.streamtype
  2417. const volume = data.detail.volume
  2418. // console.log(TAG_NAME, '远端音量变更', userID, streamType, volume, event)
  2419. const stream = this.userController.getStream({
  2420. userID: userID,
  2421. streamType: streamType === 'aux' ? 'main' : streamType, // 远端推辅流后,音量回调会从辅流的 player 返回,而不是主流player 返回。需要等 native SDK修复。
  2422. })
  2423. if (stream) {
  2424. stream.volume = volume
  2425. }
  2426. this.setData({
  2427. streamList: this.data.streamList,
  2428. visibleStreamList: this._filterVisibleStream(this.data.streamList, true),
  2429. }, () => {})
  2430. })
  2431. this.on(EVENT.BGM_PLAY_PROGRESS, (event) => {
  2432. // console.log(TAG_NAME, '_gridBindEvent on BGM_PLAY_PROGRESS', event)
  2433. const BGMProgress = event.data.detail.progress / event.data.detail.duration * 100
  2434. this.setData({
  2435. BGMProgress
  2436. })
  2437. })
  2438. this.on(EVENT.LOCAL_AUDIO_VOLUME_UPDATE, (event) => {
  2439. // console.log(TAG_NAME, '_gridBindEvent on LOCAL_AUDIO_VOLUME_UPDATE', event)
  2440. // const data = event.data
  2441. const volume = event.data.detail.volume
  2442. // 避免频繁输出log
  2443. this._setPusherConfig({
  2444. volume
  2445. }, true)
  2446. })
  2447. },
  2448. _handleGridTouchStart(event) {
  2449. touchX = event.changedTouches[0].clientX
  2450. touchY = event.changedTouches[0].clientY
  2451. },
  2452. _handleGridTouchEnd(event) {
  2453. const x = event.changedTouches[0].clientX
  2454. const y = event.changedTouches[0].clientY
  2455. if (x - touchX > 50 && Math.abs(y - touchY) < 50) {
  2456. // console.log(TAG_NAME, '向右滑 当前页面', this.data.gridCurrentPage, this.data.gridPageCount)
  2457. this._gridPagePrev()
  2458. } else if (x - touchX < -50 && Math.abs(y - touchY) < 50) {
  2459. // console.log(TAG_NAME, '向左滑 当前页面', this.data.gridCurrentPage, this.data.gridPageCount)
  2460. this._gridPageNext()
  2461. }
  2462. },
  2463. _gridPageToPrev(streamList) {
  2464. const visibleStreamList = this._filterGridPageVisibleStream(streamList)
  2465. if (this.data.gridPagePlaceholderStreamList.length === this.data.gridPlayerPerPage) {
  2466. this.data.gridCurrentPage--
  2467. this._gridPageToPrev(streamList)
  2468. } else {
  2469. return visibleStreamList
  2470. }
  2471. },
  2472. _gridPageNext() {
  2473. this.data.gridCurrentPage++
  2474. if (this.data.gridCurrentPage > this.data.gridPageCount) {
  2475. this.data.gridCurrentPage = 1
  2476. }
  2477. this._gridPageSetData()
  2478. },
  2479. _gridPagePrev() {
  2480. this.data.gridCurrentPage--
  2481. if (this.data.gridCurrentPage < 1) {
  2482. this.data.gridCurrentPage = this.data.gridPageCount
  2483. }
  2484. this._gridPageSetData()
  2485. },
  2486. _gridPageSetData() {
  2487. this._gridShowPageTips()
  2488. const visibleStreamList = this._filterVisibleStream(this.data.streamList)
  2489. this.setData({
  2490. gridCurrentPage: this.data.gridCurrentPage,
  2491. gridPageCount: this.data.gridPageCount,
  2492. visibleStreamList: visibleStreamList,
  2493. streamList: this.data.streamList,
  2494. gridPagePlaceholderStreamList: this.data.gridPagePlaceholderStreamList,
  2495. }, () => {
  2496. })
  2497. },
  2498. _gridShowPageTips(event) {
  2499. if (this.data.gridPageCount < 2) {
  2500. return
  2501. }
  2502. console.log(TAG_NAME, '_gridShowPageTips', this.data)
  2503. if (this.data.hasGridPageTipsShow) {
  2504. clearTimeout(this.data.hasGridPageTipsShow)
  2505. }
  2506. this.animate('.pages-container', [{
  2507. opacity: 1
  2508. }, ], 100, () => {
  2509. })
  2510. this.data.hasGridPageTipsShow = setTimeout(() => {
  2511. this.animate('.pages-container', [{
  2512. opacity: 1
  2513. },
  2514. {
  2515. opacity: 0.3
  2516. },
  2517. ], 600, () => {
  2518. })
  2519. }, 3000)
  2520. },
  2521. _toggleFullscreen(event) {
  2522. console.log(TAG_NAME, '_toggleFullscreen', event)
  2523. const userID = event.currentTarget.dataset.userID
  2524. const streamType = event.currentTarget.dataset.streamType
  2525. if (this._isFullscreen) {
  2526. this.exitFullscreen({
  2527. userID,
  2528. streamType
  2529. }).then(() => {
  2530. this._isFullscreen = false
  2531. }).catch(() => {})
  2532. } else {
  2533. // const stream = this.userController.getStream({ userID, streamType })
  2534. const direction = 0
  2535. // 已知问题:视频的尺寸需要等待player触发NetStatus事件才能获取到,如果进房就双击全屏,全屏后的方向有可能不对。
  2536. // if (stream && stream.videoWidth && stream.videoHeight) {
  2537. // // 如果是横视频,全屏时进行横屏处理。如果是竖视频,则为0
  2538. // direction = stream.videoWidth > stream.videoHeight ? 90 : 0
  2539. // }
  2540. this.enterFullscreen({
  2541. userID,
  2542. streamType,
  2543. direction
  2544. }).then(() => {
  2545. this._isFullscreen = true
  2546. }).catch(() => {})
  2547. }
  2548. },
  2549. _toggleMoreMenu() {
  2550. this.setData({
  2551. isShowMoreMenu: !this.data.isShowMoreMenu,
  2552. })
  2553. },
  2554. _toggleIMPanel() {
  2555. if (!this.data.enableIM) {
  2556. wx.showToast({
  2557. icon: 'none',
  2558. title: '当前没有开启IM功能,请设置 enableIM:true',
  2559. })
  2560. }
  2561. this.setData({
  2562. showIMPanel: !this.data.showIMPanel,
  2563. })
  2564. },
  2565. _handleBGMOperation(event) {
  2566. const operationName = event.currentTarget.dataset.operationName
  2567. if (this[operationName]) {
  2568. this[operationName]({
  2569. url: 'https://trtc-1252463788.cos.ap-guangzhou.myqcloud.com/web/assets/bgm-test.mp3'
  2570. })
  2571. }
  2572. },
  2573. _selectBeautyStyle: function (event) {
  2574. console.log(TAG_NAME, '_selectBeautyStyle', event)
  2575. // this.data.beauty = (event.detail.value === 'close' ? 0 : 9)
  2576. const value = event.detail.value
  2577. this.setData({
  2578. // beauty: (value === 'close' ? 0 : 9),
  2579. beautyStyle: value,
  2580. }, () => {
  2581. this._setPusherConfig({
  2582. beautyLevel: value === 'close' ? 0 : 9,
  2583. beautyStyle: value === 'close' ? 'smooth' : value,
  2584. })
  2585. })
  2586. },
  2587. _selectFilter: function (event) {
  2588. console.log(TAG_NAME, '_selectFilter', event)
  2589. const index = parseInt(event.detail.value)
  2590. this.setData({
  2591. filterIndex: index
  2592. }, () => {
  2593. this._setPusherConfig({
  2594. filter: this.data.filterArray[index].value,
  2595. })
  2596. })
  2597. },
  2598. _selectAudioReverbType: function (event) {
  2599. console.log(TAG_NAME, '_selectAudioReverbType', event)
  2600. const audioReverbType = parseInt(event.detail.value)
  2601. this._setPusherConfig({
  2602. audioReverbType
  2603. })
  2604. },
  2605. _sendIMMessage(event) {
  2606. console.log(TAG_NAME, '_sendIMMessage', event)
  2607. if (!this.data.messageContent) {
  2608. return
  2609. }
  2610. const roomID = this.data.config.roomID
  2611. const message = this.data.messageContent
  2612. const userID = this.data.config.userID
  2613. this.sendGroupTextMessage({
  2614. roomID,
  2615. message
  2616. })
  2617. // 消息上屏
  2618. this._pushMessageList({
  2619. name: userID,
  2620. message: message,
  2621. })
  2622. this.setData({
  2623. messageContent: '',
  2624. })
  2625. },
  2626. _inputIMMessage(event) {
  2627. // console.log(TAG_NAME, '_inputIMMessage', event)
  2628. this.setData({
  2629. messageContent: event.detail.value,
  2630. })
  2631. },
  2632. _pushMessageList(params) {
  2633. if (this.data.messageList.length === this.data.maxMessageListLength) {
  2634. this.data.messageList.shift()
  2635. }
  2636. this.data.messageList.push(params)
  2637. this.setData({
  2638. messageList: this.data.messageList,
  2639. messageListScrollTop: this.data.messageList.length * 100,
  2640. }, () => {})
  2641. },
  2642. },
  2643. })