trtc-room.vue 79 KB

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