trtc-room.vue 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311
  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 Event from './utils/event';
  481. import * as ENV from './utils/environment';
  482. const TAG_NAME = 'TRTC-ROOM';
  483. export default {
  484. data() {
  485. return {
  486. pusher: null,
  487. // debugMode: false, // 是否开启调试模式
  488. debugPanel: true,
  489. // 是否打开组件调试面板
  490. debug: false,
  491. // 是否打开player pusher 的调试信息
  492. streamList: [],
  493. // 用于渲染player列表,存储stram
  494. userList: [],
  495. // 扁平化的数据用来返回给用户
  496. template: '',
  497. // 不能设置默认值,当默认值和传入组件的值不一致时,iOS渲染失败
  498. cameraPosition: '',
  499. panelName: '',
  500. // 控制面板名称,包括 setting-panel memberlist-panel
  501. localVolume: 0,
  502. remoteVolumeList: [],
  503. appVersion: ENV.APP_VERSION,
  504. libVersion: ENV.LIB_VERSION,
  505. debugMode: ''
  506. };
  507. },
  508. components: {},
  509. props: {
  510. // 必要的初始化参数
  511. config: {
  512. type: Object,
  513. default: () => ({
  514. sdkAppID: '',
  515. userID: '',
  516. userSig: '',
  517. template: '',
  518. debugMode: ''
  519. })
  520. }
  521. },
  522. watch: {
  523. streamList(newVal, oldVal){
  524. console.log('streamList::::::::',newVal, oldVal)
  525. this.streamList = newVal
  526. },
  527. config: {
  528. handler: function(newVal, oldVal) {
  529. console.log('watch config');
  530. this.propertyObserverFun({
  531. name: 'config',
  532. newVal,
  533. oldVal
  534. });
  535. },
  536. deep: true
  537. }
  538. },
  539. created: function() {
  540. // 在组件实例刚刚被创建时执行
  541. console.log(TAG_NAME, 'created', ENV);
  542. },
  543. beforeMount: function() {
  544. // 在组件实例进入页面节点树时执行
  545. console.log(TAG_NAME, 'attached');
  546. this.initFun();
  547. },
  548. mounted: function() {
  549. // 在组件在视图层布局完成后执行
  550. console.log(TAG_NAME, 'ready');
  551. },
  552. destroyed: function() {
  553. // 在组件实例被从页面节点树移除时执行
  554. console.log(TAG_NAME, 'detached'); // 停止所有拉流,并重置数据
  555. this.exitRoom();
  556. },
  557. error: function(error) {
  558. // 每当组件方法抛出错误时执行
  559. console.log(TAG_NAME, 'error', error);
  560. },
  561. onPageShow: function() {
  562. // 组件所在的页面被展示时执行
  563. console.log(TAG_NAME, 'show status:', this.status);
  564. if (this.status.isPending) {
  565. // 经历了 5000 挂起事件
  566. this.status.isPending = false;
  567. }
  568. if (this.status.isPush) {
  569. // 小程序hide - show 有一定概率本地黑屏或静止,远端正常,或者远端和本地同时黑屏或静止,需要手动启动预览,非必现
  570. // this.data.pusher.getPusherContext().startPreview()
  571. // this.data.pusher.getPusherContext().resume()
  572. }
  573. },
  574. onPageHide: function() {
  575. // 组件所在的页面被隐藏时执行
  576. console.log(TAG_NAME, 'hide');
  577. },
  578. onPageResize: function(size) {
  579. // 组件所在的页面尺寸变化时执行
  580. console.log(TAG_NAME, 'resize', size);
  581. },
  582. methods: {
  583. setData,
  584. /**
  585. * 初始化各项参数和用户控制模块,在组件实例触发 attached 时调用,此时不建议对View进行变更渲染(调用setData方法)
  586. */
  587. initFun: function() {
  588. console.log(TAG_NAME, '_init');
  589. this.userController = new UserController(this);
  590. this._emitter = new Event();
  591. this.EVENT = EVENT;
  592. this.initStatusFun();
  593. this.bindEventFun();
  594. this.bindEventGridFun();
  595. console.log(TAG_NAME, '_init success component:', this);
  596. },
  597. /**
  598. * 进房
  599. * @param {Object} params 必传 roomID 取值范围 1 ~ 4294967295
  600. * @returns {Promise}
  601. */
  602. enterRoom: function(params) {
  603. return new Promise((resolve, reject) => {
  604. console.log(TAG_NAME, 'enterRoom');
  605. console.log(TAG_NAME, 'params', params);
  606. console.log(TAG_NAME, 'config', this.config);
  607. console.log(TAG_NAME, 'pusher', this.pusher); // 1. 补齐进房参数,校验必要参数是否齐全
  608. console.log('进房......', params, this.config, this.pusher);
  609. if (params) {
  610. Object.assign(this.pusher, params);
  611. Object.assign(this.config, params);
  612. }
  613. if (!this.checkParamFun(this.config)) {
  614. reject(new Error('缺少必要参数'));
  615. return;
  616. } // 2. 根据参数拼接 push url,赋值给 live-pusher,
  617. this.getPushUrlFun(this.config)
  618. .then(pushUrl => {
  619. this.pusher.url = pushUrl;
  620. this.setData(
  621. {
  622. pusher: this.pusher
  623. },
  624. () => {
  625. console.log(TAG_NAME, 'enterRoom success', this.pusher); // view 渲染成功回调后,开始推流
  626. this.pusher.getPusherContext().start();
  627. this.status.isPush = true;
  628. resolve();
  629. }
  630. );
  631. })
  632. .catch(res => {
  633. // 获取 room sig 失败, 进房失败需要通过 pusher state 事件通知
  634. console.error(TAG_NAME, 'enterRoom fail', res);
  635. reject(res);
  636. });
  637. });
  638. },
  639. /**
  640. * 退房,停止推流和拉流,并重置数据
  641. * @returns {Promise}
  642. */
  643. exitRoom: function() {
  644. return new Promise((resolve, reject) => {
  645. console.log(TAG_NAME, 'exitRoom');
  646. this.pusher.reset();
  647. this.status.isPush = false;
  648. const result = this.userController.reset();
  649. this.setData(
  650. {
  651. pusher: this.pusher,
  652. userList: result.userList,
  653. streamList: result.streamList
  654. },
  655. () => {
  656. // 在销毁页面时调用,不会走到这里
  657. resolve({
  658. userList: this.userList,
  659. streamList: this.streamList
  660. });
  661. console.log(TAG_NAME, 'exitRoom success', this.pusher, this.streamList, this.userList);
  662. }
  663. );
  664. });
  665. },
  666. /**
  667. * 开启摄像头
  668. * @returns {Promise}
  669. */
  670. publishLocalVideo: function() {
  671. // 设置 pusher enableCamera
  672. console.log(TAG_NAME, 'publishLocalVideo 开启摄像头');
  673. return this.setPusherConfigFun({
  674. enableCamera: true
  675. });
  676. },
  677. /**
  678. * 关闭摄像头
  679. * @returns {Promise}
  680. */
  681. unpublishLocalVideo: function() {
  682. // 设置 pusher enableCamera
  683. console.log(TAG_NAME, 'unpublshLocalVideo 关闭摄像头');
  684. return this.setPusherConfigFun({
  685. enableCamera: false
  686. });
  687. },
  688. /**
  689. * 开启麦克风
  690. * @returns {Promise}
  691. */
  692. publishLocalAudio: function() {
  693. // 设置 pusher enableCamera
  694. console.log(TAG_NAME, 'publishLocalAudio 开启麦克风');
  695. return this.setPusherConfigFun({
  696. enableMic: true
  697. });
  698. },
  699. /**
  700. * 关闭麦克风
  701. * @returns {Promise}
  702. */
  703. unpublishLocalAudio: function() {
  704. // 设置 pusher enableCamera
  705. console.log(TAG_NAME, 'unpublshLocalAudio 关闭麦克风');
  706. return this.setPusherConfigFun({
  707. enableMic: false
  708. });
  709. },
  710. /**
  711. * 订阅远端视频 主流 小画面 辅流
  712. * @param {Object} params {userID,streamType} streamType 传入 small 时修改对应的主流url的 streamtype 参数为small
  713. * @returns {Promise}
  714. */
  715. subscribeRemoteVideo(params) {
  716. console.log(TAG_NAME, 'subscribeRemoteVideo', params); // 设置指定 user streamType 的 muteVideo 为 false
  717. const config = {
  718. muteVideo: false
  719. }; // 本地数据结构里的 streamType 只支持 main 和 aux ,订阅small 也是对main进行处理
  720. const streamType = params.streamType === 'small' ? 'main' : params.streamType;
  721. if (params.streamType === 'small' || params.streamType === 'main') {
  722. const stream = this.userController.getStream({
  723. userID: params.userID,
  724. streamType: streamType
  725. });
  726. if (stream && stream.streamType === 'main') {
  727. console.log(TAG_NAME, 'subscribeRemoteVideo switch small', stream.src);
  728. if (params.streamType === 'small') {
  729. config.src = stream.src.replace('main', 'small');
  730. config._definitionType = 'small'; // 用于设置面板的渲染
  731. } else if (params.streamType === 'main') {
  732. stream.src = stream.src.replace('small', 'main');
  733. config._definitionType = 'main';
  734. }
  735. console.log(TAG_NAME, 'subscribeRemoteVideo', stream.src);
  736. }
  737. }
  738. return this.setPlayerConfigFun({
  739. userID: params.userID,
  740. streamType: streamType,
  741. config: config
  742. });
  743. },
  744. /**
  745. * 取消订阅远端视频
  746. * @param {Object} params {userID,streamType}
  747. * @returns {Promise}
  748. */
  749. unsubscribeRemoteVideo(params) {
  750. console.log(TAG_NAME, 'unsubscribeRemoteVideo', params); // 设置指定 user streamType 的 muteVideo 为 true
  751. return this.setPlayerConfigFun({
  752. userID: params.userID,
  753. streamType: params.streamType,
  754. config: {
  755. muteVideo: true
  756. }
  757. });
  758. },
  759. /**
  760. * 订阅远端音频
  761. * @param {Object} params userID 用户ID
  762. * @returns {Promise}
  763. */
  764. subscribeRemoteAudio(params) {
  765. console.log(TAG_NAME, 'subscribeRemoteAudio', params);
  766. return this.setPlayerConfigFun({
  767. userID: params.userID,
  768. streamType: 'main',
  769. config: {
  770. muteAudio: false
  771. }
  772. });
  773. },
  774. /**
  775. * 取消订阅远端音频
  776. * @param {Object} params userID 用户ID
  777. * @returns {Promise}
  778. */
  779. unsubscribeRemoteAudio(params) {
  780. console.log(TAG_NAME, 'unsubscribeRemoteAudio', params);
  781. return this.setPlayerConfigFun({
  782. userID: params.userID,
  783. streamType: 'main',
  784. config: {
  785. muteAudio: true
  786. }
  787. });
  788. },
  789. on: function(eventCode, handler, context) {
  790. this._emitter.on(eventCode, handler, context);
  791. },
  792. off: function(eventCode, handler) {
  793. this._emitter.off(eventCode, handler);
  794. },
  795. getRemoteUserList: function() {
  796. return this.userList;
  797. },
  798. /**
  799. * 切换前后摄像头
  800. */
  801. switchCamera: function() {
  802. if (!this.cameraPosition) {
  803. // this.data.pusher.cameraPosition 是初始值,不支持动态设置
  804. this.cameraPosition = this.pusher.frontCamera;
  805. }
  806. console.log(TAG_NAME, 'switchCamera', this.cameraPosition);
  807. this.cameraPosition = this.cameraPosition === 'front' ? 'back' : 'front';
  808. this.setData(
  809. {
  810. cameraPosition: this.cameraPosition
  811. },
  812. () => {
  813. console.log(TAG_NAME, 'switchCamera success', this.cameraPosition);
  814. }
  815. ); // wx 7.0.9 不支持动态设置 pusher.devicePosition ,需要调用api设置,这里修改cameraPosition是为了记录状态
  816. this.pusher.getPusherContext().switchCamera();
  817. },
  818. /**
  819. * 设置指定player view的渲染坐标和尺寸
  820. * @param {object} params
  821. * userID: string
  822. * streamType: string
  823. * xAxis: number
  824. * yAxis: number
  825. * width: number
  826. * height: number
  827. * @returns {Promise}
  828. */
  829. setViewRect: function(params) {
  830. console.log(TAG_NAME, 'setViewRect', params);
  831. if (this.pusher.template !== 'custom') {
  832. console.warn(`如需使用setViewRect方法,请设置template:"custom", 当前 template:"${this.pusher.template}"`);
  833. }
  834. if (this.pusher.userID === params.userID) {
  835. return this.setPusherConfigFun({
  836. xAxis: params.xAxis,
  837. yAxis: params.yAxis,
  838. width: params.width,
  839. height: params.height
  840. });
  841. }
  842. return this.setPlayerConfigFun({
  843. userID: params.userID,
  844. streamType: params.streamType,
  845. config: {
  846. xAxis: params.xAxis,
  847. yAxis: params.yAxis,
  848. width: params.width,
  849. height: params.height
  850. }
  851. });
  852. },
  853. /**
  854. * 设置指定 player 或者 pusher view 是否可见
  855. * @param {object} params
  856. * userID: string
  857. * streamType: string
  858. * isVisible:boolean
  859. * @returns {Promise}
  860. */
  861. setViewVisible: function(params) {
  862. console.log(TAG_NAME, 'setViewVisible', params); // if (this.data.pusher.template !== 'custom') {
  863. // console.warn(`如需使用setViewVisible方法,请设置template:"custom", 当前 template:"${this.data.pusher.template}"`)
  864. // }
  865. if (this.pusher.userID === params.userID) {
  866. return this.setPusherConfigFun({
  867. isVisible: params.isVisible
  868. });
  869. }
  870. return this.setPlayerConfigFun({
  871. userID: params.userID,
  872. streamType: params.streamType,
  873. config: {
  874. isVisible: params.isVisible
  875. }
  876. });
  877. },
  878. /**
  879. * 设置指定player view的层级
  880. * @param {Object} params
  881. * userID: string
  882. * streamType: string
  883. * zindex: number
  884. * @returns {Promise}
  885. */
  886. setViewZIndex: function(params) {
  887. console.log(TAG_NAME, 'setViewZIndex', params);
  888. if (this.pusher.template !== 'custom') {
  889. console.warn(`如需使用setViewZIndex方法,请设置template:"custom", 当前 template:"${this.pusher.template}"`);
  890. }
  891. if (this.pusher.userID === params.userID) {
  892. return this.setPusherConfigFun({
  893. zindex: params.zindex
  894. });
  895. }
  896. return this.setPlayerConfigFun({
  897. userID: params.userID,
  898. streamType: params.streamType,
  899. config: {
  900. zindex: params.zindex
  901. }
  902. });
  903. },
  904. /**
  905. * 播放背景音
  906. * @param {Object} params url
  907. * @returns {Promise}
  908. */
  909. playBGM: function(params) {
  910. return new Promise((resolve, reject) => {
  911. this.pusher.getPusherContext().playBGM({
  912. url: params.url,
  913. // 已经有相关事件不需要在这里监听,目前用于测试
  914. success: () => {
  915. console.log(TAG_NAME, '播放背景音成功'); // this._emitter.emit(EVENT.BGM_PLAY_START)
  916. resolve();
  917. },
  918. fail: () => {
  919. console.log(TAG_NAME, '播放背景音失败');
  920. this._emitter.emit(EVENT.BGM_PLAY_FAIL);
  921. reject(new Error('播放背景音失败'));
  922. } // complete: () => {
  923. // console.log(TAG_NAME, '背景完成')
  924. // this._emitter.emit(EVENT.BGM_PLAY_COMPLETE)
  925. // },
  926. });
  927. });
  928. },
  929. stopBGM: function() {
  930. this.pusher.getPusherContext().stopBGM();
  931. },
  932. pauseBGM: function() {
  933. this.pusher.getPusherContext().pauseBGM();
  934. },
  935. resumeBGM: function() {
  936. this.pusher.getPusherContext().resumeBGM();
  937. },
  938. /**
  939. * 设置背景音音量
  940. * @param {Object} params volume
  941. */
  942. setBGMVolume: function(params) {
  943. this.pusher.getPusherContext().setBGMVolume({
  944. volume: params.volume
  945. });
  946. },
  947. /**
  948. * 设置麦克风音量
  949. * @param {Object} params volume
  950. */
  951. setMICVolume: function(params) {
  952. this.pusher.getPusherContext().setMICVolume({
  953. volume: params.volume
  954. });
  955. },
  956. /**
  957. * 发送SEI消息
  958. * @param {Object} params message
  959. * @returns {Promise}
  960. */
  961. sendSEI: function(params) {
  962. return new Promise((resolve, reject) => {
  963. this.pusher.getPusherContext().sendMessage({
  964. msg: params.message,
  965. success: function(result) {
  966. resolve(result);
  967. }
  968. });
  969. });
  970. },
  971. /**
  972. * pusher 和 player 的截图并保存
  973. * @param {Object} params userID streamType
  974. * @returns {Promise}
  975. */
  976. snapshot: function(params) {
  977. console.log(TAG_NAME, 'snapshot', params);
  978. return new Promise((resolve, reject) => {
  979. this.captureSnapshot(params)
  980. .then(result => {
  981. wx.saveImageToPhotosAlbum({
  982. filePath: result.tempImagePath,
  983. success(res) {
  984. wx.showToast({
  985. title: '已保存到相册'
  986. });
  987. console.log('save photo is success', res);
  988. resolve(result);
  989. },
  990. fail: function(error) {
  991. wx.showToast({
  992. icon: 'none',
  993. title: '保存失败'
  994. });
  995. console.log('save photo is fail', error);
  996. reject(error);
  997. }
  998. });
  999. })
  1000. .catch(error => {
  1001. reject(error);
  1002. });
  1003. });
  1004. },
  1005. /**
  1006. * 获取pusher 和 player 的截图
  1007. * @param {Object} params userID streamType
  1008. * @returns {Promise}
  1009. */
  1010. captureSnapshot: function(params) {
  1011. return new Promise((resolve, reject) => {
  1012. if (params.userID === this.pusher.userID) {
  1013. // pusher
  1014. this.pusher.getPusherContext().snapshot({
  1015. quality: 'raw',
  1016. complete: result => {
  1017. console.log(TAG_NAME, 'snapshot pusher', result);
  1018. if (result.tempImagePath) {
  1019. resolve(result);
  1020. } else {
  1021. console.log('snapShot 回调失败', result);
  1022. reject(new Error('截图失败'));
  1023. }
  1024. }
  1025. });
  1026. } else {
  1027. // player
  1028. this.userController.getStream(params).playerContext.snapshot({
  1029. quality: 'raw',
  1030. complete: result => {
  1031. console.log(TAG_NAME, 'snapshot player', result);
  1032. if (result.tempImagePath) {
  1033. resolve(result);
  1034. } else {
  1035. console.log('snapShot 回调失败', result);
  1036. reject(new Error('截图失败'));
  1037. }
  1038. }
  1039. });
  1040. }
  1041. });
  1042. },
  1043. /**
  1044. * 将远端视频全屏
  1045. * @param {Object} params userID streamType direction
  1046. * @returns {Promise}
  1047. */
  1048. enterFullscreen: function(params) {
  1049. console.log(TAG_NAME, 'enterFullscreen', params);
  1050. return new Promise((resolve, reject) => {
  1051. this.userController.getStream(params).playerContext.requestFullScreen({
  1052. direction: params.direction || 0,
  1053. success: event => {
  1054. console.log(TAG_NAME, 'enterFullscreen success', event);
  1055. resolve(event);
  1056. },
  1057. fail: event => {
  1058. console.log(TAG_NAME, 'enterFullscreen fail', event);
  1059. reject(event);
  1060. }
  1061. });
  1062. });
  1063. },
  1064. /**
  1065. * 将远端视频取消全屏
  1066. * @param {Object} params userID streamType
  1067. * @returns {Promise}
  1068. */
  1069. exitFullscreen: function(params) {
  1070. console.log(TAG_NAME, 'exitFullscreen', params);
  1071. return new Promise((resolve, reject) => {
  1072. this.userController.getStream(params).playerContext.exitFullScreen({
  1073. success: event => {
  1074. console.log(TAG_NAME, 'exitFullScreen success', event);
  1075. resolve(event);
  1076. },
  1077. fail: event => {
  1078. console.log(TAG_NAME, 'exitFullScreen fail', event);
  1079. reject(event);
  1080. }
  1081. });
  1082. });
  1083. },
  1084. /**
  1085. * 设置 player 视图的横竖屏显示
  1086. * @param {Object} params userID streamType orientation: vertical, horizontal
  1087. * @returns {Promise}
  1088. */
  1089. setRemoteOrientation: function(params) {
  1090. return this.setPlayerConfigFun({
  1091. userID: params.userID,
  1092. streamType: params.streamType,
  1093. config: {
  1094. orientation: params.orientation
  1095. }
  1096. });
  1097. },
  1098. // 改为:
  1099. setViewOrientation: function(params) {
  1100. return this.setPlayerConfigFun({
  1101. userID: params.userID,
  1102. streamType: params.streamType,
  1103. config: {
  1104. orientation: params.orientation
  1105. }
  1106. });
  1107. },
  1108. /**
  1109. * 设置 player 视图的填充模式
  1110. * @param {Object} params userID streamType fillMode: contain,fillCrop
  1111. * @returns {Promise}
  1112. */
  1113. setRemoteFillMode: function(params) {
  1114. return this.setPlayerConfigFun({
  1115. userID: params.userID,
  1116. streamType: params.streamType,
  1117. config: {
  1118. objectFit: params.fillMode
  1119. }
  1120. });
  1121. },
  1122. // 改为:
  1123. setViewFillMode: function(params) {
  1124. return this.setPlayerConfigFun({
  1125. userID: params.userID,
  1126. streamType: params.streamType,
  1127. config: {
  1128. objectFit: params.fillMode
  1129. }
  1130. });
  1131. },
  1132. /**
  1133. * 切换 player 大小画面
  1134. * @param {Object} params userID streamType definition: HD SD
  1135. * @returns {Promise}
  1136. */
  1137. setRemoteDefinitionFun: function(params) {
  1138. params.streamType = 'main';
  1139. return new Promise((resolve, reject) => {
  1140. const stream = this.userController.getStream({
  1141. userID: params.userID,
  1142. streamType: params.streamType
  1143. });
  1144. if (stream && stream.streamType === 'main') {
  1145. console.log(TAG_NAME, '_switchStreamType', stream.src); // stream.volume = volume
  1146. if (stream.src.indexOf('main') > -1) {
  1147. stream.src = stream.src.replace('main', 'small');
  1148. stream._streamType = 'small'; // 用于设置面板的渲染
  1149. } else if (stream.src.indexOf('small') > -1) {
  1150. stream.src = stream.src.replace('small', 'main');
  1151. stream._streamType = 'main';
  1152. }
  1153. console.log(TAG_NAME, '_switchStreamType', stream.src);
  1154. this.setData(
  1155. {
  1156. streamList: this.streamList
  1157. },
  1158. () => {}
  1159. );
  1160. }
  1161. });
  1162. },
  1163. initStatusFun() {
  1164. this.status = {
  1165. isPush: false,
  1166. // 推流状态
  1167. isPending: false // 挂起状态,触发5000事件标记为true,onShow后标记为false
  1168. };
  1169. this._lastTapTime = 0;
  1170. this._beforeLastTapTime = 0;
  1171. this._isFullscreen = false;
  1172. },
  1173. /**
  1174. * 设置推流参数并触发页面渲染更新
  1175. * @param {Object} config live-pusher 的配置
  1176. * @returns {Promise}
  1177. */
  1178. setPusherConfigFun(config) {
  1179. console.log(TAG_NAME, '_setPusherConfig', config, this.pusher);
  1180. return new Promise((resolve, reject) => {
  1181. if (!this.pusher) {
  1182. this.pusher = new Pusher(config);
  1183. } else {
  1184. Object.assign(this.pusher, config);
  1185. }
  1186. this.setData(
  1187. {
  1188. pusher: this.pusher
  1189. },
  1190. () => {
  1191. // console.log(TAG_NAME, '_setPusherConfig setData compelete', 'config:', config, 'pusher:', this.data.pusher)
  1192. resolve(config);
  1193. }
  1194. );
  1195. });
  1196. },
  1197. /**
  1198. *
  1199. * @param {Object} params include userID,streamType,config
  1200. * @returns {Promise}
  1201. */
  1202. setPlayerConfigFun(params) {
  1203. const userID = params.userID;
  1204. const streamType = params.streamType;
  1205. const config = params.config;
  1206. console.log(TAG_NAME, '_setPlayerConfig', params);
  1207. return new Promise((resolve, reject) => {
  1208. // 获取指定的userID streamType 的 stream
  1209. const user = this.userController.getUser(userID);
  1210. if (user && user.streams[streamType]) {
  1211. user.streams[streamType] = Object.assign(user.streams[streamType], config); // user.streams引用的对象和 streamList 里的是同一个
  1212. this.setData(
  1213. {
  1214. streamList: this.streamList
  1215. },
  1216. () => {
  1217. // console.log(TAG_NAME, '_setPlayerConfig complete', params, 'streamList:', this.data.streamList)
  1218. resolve(params);
  1219. }
  1220. );
  1221. } else {
  1222. // 不需要reject,静默处理
  1223. console.warn(TAG_NAME, '指定 userID 或者 streamType 不存在'); // reject(new Error('指定 userID 或者 streamType 不存在'))
  1224. }
  1225. });
  1226. },
  1227. /**
  1228. * 必选参数检测
  1229. * @param {Object} rtcConfig rtc参数
  1230. * @returns {Boolean}
  1231. */
  1232. checkParamFun: function(rtcConfig) {
  1233. console.log(TAG_NAME, 'checkParam config:', rtcConfig);
  1234. if (!rtcConfig.sdkAppID) {
  1235. console.error('未设置 sdkAppID');
  1236. return false;
  1237. }
  1238. if (rtcConfig.roomID === undefined) {
  1239. console.error('未设置 roomID');
  1240. return false;
  1241. }
  1242. if (rtcConfig.roomID < 1 || rtcConfig.roomID > 4294967296) {
  1243. console.error('roomID 超出取值范围 1 ~ 4294967295');
  1244. return false;
  1245. }
  1246. if (!rtcConfig.userID) {
  1247. console.error('未设置 userID');
  1248. return false;
  1249. }
  1250. if (!rtcConfig.userSig) {
  1251. console.error('未设置 userSig');
  1252. return false;
  1253. }
  1254. if (!rtcConfig.template) {
  1255. console.error('未设置 template');
  1256. return false;
  1257. }
  1258. return true;
  1259. },
  1260. getPushUrlFun: function(rtcConfig) {
  1261. // 拼接 puhser url rtmp 方案
  1262. console.log(TAG_NAME, 'getPushUrl', rtcConfig);
  1263. if (ENV.IS_TRTC) {
  1264. // 版本高于7.0.8,基础库版本高于2.10.0 使用新的 url
  1265. return new Promise((resolve, reject) => {
  1266. // appscene videocall live
  1267. // cloudenv PRO CCC DEV UAT
  1268. // encsmall 0
  1269. // 对外的默认值是rtc ,对内的默认值是videocall
  1270. rtcConfig.scene = !rtcConfig.scene || rtcConfig.scene === 'rtc' ? 'videocall' : 'live';
  1271. rtcConfig.enableBlackStream = rtcConfig.enableBlackStream || 1;
  1272. rtcConfig.encsmall = rtcConfig.encsmall || 0;
  1273. rtcConfig.cloudenv = rtcConfig.cloudenv || 'PRO';
  1274. setTimeout(() => {
  1275. const pushUrl =
  1276. 'room://cloud.tencent.com/rtc?sdkappid=' +
  1277. rtcConfig.sdkAppID +
  1278. '&roomid=' +
  1279. rtcConfig.roomID +
  1280. '&userid=' +
  1281. rtcConfig.userID +
  1282. '&usersig=' +
  1283. rtcConfig.userSig +
  1284. '&appscene=' +
  1285. rtcConfig.scene +
  1286. '&encsmall=' +
  1287. rtcConfig.encsmall +
  1288. '&cloudenv=' +
  1289. rtcConfig.cloudenv;
  1290. console.log(TAG_NAME, 'getPushUrl result:', pushUrl);
  1291. resolve(pushUrl);
  1292. }, 0);
  1293. });
  1294. }
  1295. return this.requestSigServerFun(rtcConfig);
  1296. },
  1297. /**
  1298. * 获取签名和推流地址
  1299. * @param {Object} rtcConfig 进房参数配置
  1300. * @returns {Promise}
  1301. */
  1302. requestSigServerFun: function(rtcConfig) {
  1303. console.log('requestSigServer:', rtcConfig);
  1304. const sdkAppID = rtcConfig.sdkAppID;
  1305. const userID = rtcConfig.userID;
  1306. const userSig = rtcConfig.userSig;
  1307. const roomID = rtcConfig.roomID;
  1308. const privateMapKey = rtcConfig.privateMapKey;
  1309. rtcConfig.useCloud = rtcConfig.useCloud === undefined ? true : rtcConfig.useCloud;
  1310. let url = rtcConfig.useCloud ? 'https://official.opensso.tencent-cloud.com/v4/openim/jsonvideoapp' : 'https://yun.tim.qq.com/v4/openim/jsonvideoapp';
  1311. url += '?sdkappid=' + sdkAppID + '&identifier=' + userID + '&usersig=' + userSig + '&random=' + Date.now() + '&contenttype=json';
  1312. const reqHead = {
  1313. Cmd: 1,
  1314. SeqNo: 1,
  1315. BusType: 7,
  1316. GroupId: roomID
  1317. };
  1318. const reqBody = {
  1319. PrivMapEncrypt: privateMapKey,
  1320. TerminalType: 1,
  1321. FromType: 3,
  1322. SdkVersion: 26280566
  1323. };
  1324. console.log('requestSigServer:', url, reqHead, reqBody);
  1325. return new Promise((resolve, reject) => {
  1326. wx.request({
  1327. url: url,
  1328. data: {
  1329. ReqHead: reqHead,
  1330. ReqBody: reqBody
  1331. },
  1332. method: 'POST',
  1333. success: res => {
  1334. console.log('requestSigServer success:', res);
  1335. if (res.data['ErrorCode'] || res.data['RspHead']['ErrorCode'] !== 0) {
  1336. // console.error(res.data['ErrorInfo'] || res.data['RspHead']['ErrorInfo'])
  1337. console.error('获取roomsig失败');
  1338. reject(res);
  1339. }
  1340. const roomSig = JSON.stringify(res.data['RspBody']);
  1341. let pushUrl = 'room://cloud.tencent.com?sdkappid=' + sdkAppID + '&roomid=' + roomID + '&userid=' + userID + '&roomsig=' + encodeURIComponent(roomSig); // TODO 需要重新整理的逻辑
  1342. // 如果有配置纯音频推流或者recordId参数
  1343. if (rtcConfig.pureAudioPushMod || rtcConfig.recordId) {
  1344. const bizbuf = {
  1345. Str_uc_params: {
  1346. pure_audio_push_mod: 0,
  1347. record_id: 0
  1348. }
  1349. }; // 纯音频推流
  1350. if (rtcConfig.pureAudioPushMod) {
  1351. bizbuf.Str_uc_params.pure_audio_push_mod = rtcConfig.pureAudioPushMod;
  1352. } else {
  1353. delete bizbuf.Str_uc_params.pure_audio_push_mod;
  1354. } // 自动录制时业务自定义id
  1355. if (rtcConfig.recordId) {
  1356. bizbuf.Str_uc_params.record_id = rtcConfig.recordId;
  1357. } else {
  1358. delete bizbuf.Str_uc_params.record_id;
  1359. }
  1360. pushUrl += '&bizbuf=' + encodeURIComponent(JSON.stringify(bizbuf));
  1361. }
  1362. console.log('roomSigInfo', pushUrl);
  1363. resolve(pushUrl);
  1364. },
  1365. fail: res => {
  1366. console.log('requestSigServer fail:', res);
  1367. reject(res);
  1368. }
  1369. });
  1370. });
  1371. },
  1372. doubleTabToggleFullscreenFun: function(event) {
  1373. const curTime = event.timeStamp;
  1374. const lastTime = this._lastTapTime; // 已知问题:上次全屏操作后,必须等待1.5s后才能再次进行全屏操作,否则引发SDK全屏异常,因此增加节流逻辑
  1375. const beforeLastTime = this._beforeLastTapTime;
  1376. console.log(TAG_NAME, 'doubleTabToggleFullscreenFun', event, lastTime, beforeLastTime);
  1377. if (curTime - lastTime > 0 && curTime - lastTime < 300 && lastTime - beforeLastTime > 1500) {
  1378. const userID = event.currentTarget.dataset.userid;
  1379. const streamType = event.currentTarget.dataset.streamtype;
  1380. if (this._isFullscreen) {
  1381. this.exitFullscreen({
  1382. userID,
  1383. streamType
  1384. })
  1385. .then(() => {
  1386. this._isFullscreen = false;
  1387. })
  1388. .catch(() => {});
  1389. } else {
  1390. // const stream = this.userController.getStream({ userID, streamType })
  1391. let direction; // // 已知问题:视频的尺寸需要等待player触发NetStatus事件才能获取到,如果进房就双击全屏,全屏后的方向有可能不对。
  1392. // if (stream && stream.videoWidth && stream.videoHeight) {
  1393. // // 如果是横视频,全屏时进行横屏处理。如果是竖视频,则为0
  1394. // direction = stream.videoWidth > stream.videoHeight ? 90 : 0
  1395. // }
  1396. this.enterFullscreen({
  1397. userID,
  1398. streamType,
  1399. direction
  1400. })
  1401. .then(() => {
  1402. this._isFullscreen = true;
  1403. })
  1404. .catch(() => {});
  1405. }
  1406. this._beforeLastTapTime = lastTime;
  1407. }
  1408. this._lastTapTime = curTime;
  1409. },
  1410. /**
  1411. * TRTC-room 远端用户和音视频状态处理
  1412. */
  1413. bindEventFun: function() {
  1414. // 远端用户进房
  1415. this.userController.on(EVENT.REMOTE_USER_JOIN, event => {
  1416. console.log(TAG_NAME, '远端用户进房', event, event.data.userID);
  1417. this.setData(
  1418. {
  1419. userList: event.data.userList
  1420. },
  1421. () => {
  1422. this._emitter.emit(EVENT.REMOTE_USER_JOIN, {
  1423. userID: event.data.userID
  1424. });
  1425. }
  1426. );
  1427. console.log(TAG_NAME, 'REMOTE_USER_JOIN', 'streamList:', this.streamList, 'userList:', this.userList);
  1428. }); // 远端用户离开
  1429. this.userController.on(EVENT.REMOTE_USER_LEAVE, event => {
  1430. console.log(TAG_NAME, '远端用户离开', event, event.data.userID);
  1431. if (event.data.userID) {
  1432. this.setData(
  1433. {
  1434. userList: event.data.userList,
  1435. streamList: event.data.streamList
  1436. },
  1437. () => {
  1438. this._emitter.emit(EVENT.REMOTE_USER_LEAVE, {
  1439. userID: event.data.userID
  1440. });
  1441. }
  1442. );
  1443. }
  1444. console.log(TAG_NAME, 'REMOTE_USER_LEAVE', 'streamList:', this.streamList, 'userList:', this.userList);
  1445. }); // 视频状态 true
  1446. this.userController.on(EVENT.REMOTE_VIDEO_ADD, event => {
  1447. console.log(TAG_NAME, '远端视频可用', event, event.data.stream.userID);
  1448. const stream = event.data.stream;
  1449. this.setData(
  1450. {
  1451. userList: event.data.userList,
  1452. streamList: event.data.streamList
  1453. },
  1454. () => {
  1455. // 完善 的stream 的 playerContext
  1456. stream.playerContext = wx.createLivePlayerContext(stream.streamID, this); // 新增的需要触发一次play 默认属性才能生效
  1457. // stream.playerContext.play()
  1458. // console.log(TAG_NAME, 'REMOTE_VIDEO_ADD playerContext.play()', stream)
  1459. // TODO 视频通话模版默认订阅且显示
  1460. this._emitter.emit(EVENT.REMOTE_VIDEO_ADD, {
  1461. userID: stream.userID,
  1462. streamType: stream.streamType
  1463. });
  1464. }
  1465. );
  1466. console.log(TAG_NAME, 'REMOTE_VIDEO_ADD', 'streamList:', this.streamList, 'userList:', this.userList);
  1467. }); // 视频状态 false
  1468. this.userController.on(EVENT.REMOTE_VIDEO_REMOVE, event => {
  1469. console.log(TAG_NAME, '远端视频移除', event, event.data.stream.userID);
  1470. const stream = event.data.stream;
  1471. this.setData(
  1472. {
  1473. userList: event.data.userList,
  1474. streamList: event.data.streamList
  1475. },
  1476. () => {
  1477. // 有可能先触发了退房事件,用户名下的所有stream都已清除
  1478. if (stream.userID && stream.streamType) {
  1479. this._emitter.emit(EVENT.REMOTE_VIDEO_REMOVE, {
  1480. userID: stream.userID,
  1481. streamType: stream.streamType
  1482. });
  1483. }
  1484. }
  1485. );
  1486. console.log(TAG_NAME, 'REMOTE_VIDEO_REMOVE', 'streamList:', this.streamList, 'userList:', this.userList);
  1487. }); // 音频可用
  1488. this.userController.on(EVENT.REMOTE_AUDIO_ADD, event => {
  1489. console.log(TAG_NAME, '远端音频可用', event);
  1490. const stream = event.data.stream;
  1491. this.setData(
  1492. {
  1493. userList: event.data.userList,
  1494. streamList: event.data.streamList
  1495. },
  1496. () => {
  1497. stream.playerContext = wx.createLivePlayerContext(stream.streamID, this); // 新增的需要触发一次play 默认属性才能生效
  1498. // stream.playerContext.play()
  1499. // console.log(TAG_NAME, 'REMOTE_AUDIO_ADD playerContext.play()', stream)
  1500. this._emitter.emit(EVENT.REMOTE_AUDIO_ADD, {
  1501. userID: stream.userID,
  1502. streamType: stream.streamType
  1503. });
  1504. }
  1505. );
  1506. console.log(TAG_NAME, 'REMOTE_AUDIO_ADD', 'streamList:', this.streamList, 'userList:', this.userList);
  1507. }); // 音频不可用
  1508. this.userController.on(EVENT.REMOTE_AUDIO_REMOVE, event => {
  1509. console.log(TAG_NAME, '远端音频移除', event, event.data.stream.userID);
  1510. const stream = event.data.stream;
  1511. this.setData(
  1512. {
  1513. userList: event.data.userList,
  1514. streamList: event.data.streamList
  1515. },
  1516. () => {
  1517. // 有可能先触发了退房事件,用户名下的所有stream都已清除
  1518. if (stream.userID && stream.streamType) {
  1519. this._emitter.emit(EVENT.REMOTE_AUDIO_REMOVE, {
  1520. userID: stream.userID,
  1521. streamType: stream.streamType
  1522. });
  1523. }
  1524. }
  1525. );
  1526. console.log(TAG_NAME, 'REMOTE_AUDIO_REMOVE', 'streamList:', this.streamList, 'userList:', this.userList);
  1527. });
  1528. },
  1529. /**
  1530. * pusher event handler
  1531. * @param {*} event 事件实例
  1532. */
  1533. pusherStateChangeHandlerFun: function(event) {
  1534. const code = event.detail.code;
  1535. const message = event.detail.message;
  1536. console.log(TAG_NAME, 'pusherStateChange:', code, event);
  1537. switch (code) {
  1538. case 0:
  1539. console.log(message, code);
  1540. break;
  1541. case 1001:
  1542. console.log('已经连接推流服务器', code);
  1543. break;
  1544. case 1002:
  1545. console.log('已经与服务器握手完毕,开始推流', code);
  1546. break;
  1547. case 1003:
  1548. console.log('打开摄像头成功', code);
  1549. break;
  1550. case 1004:
  1551. console.log('录屏启动成功', code);
  1552. break;
  1553. case 1005:
  1554. console.log('推流动态调整分辨率', code);
  1555. break;
  1556. case 1006:
  1557. console.log('推流动态调整码率', code);
  1558. break;
  1559. case 1007:
  1560. console.log('首帧画面采集完成', code);
  1561. break;
  1562. case 1008:
  1563. console.log('编码器启动', code);
  1564. break;
  1565. case 1018:
  1566. console.log('进房成功', code);
  1567. this._emitter.emit(EVENT.LOCAL_JOIN, {
  1568. userID: this.pusher.userID
  1569. });
  1570. break;
  1571. case 1019:
  1572. console.log('退出房间', code);
  1573. this._emitter.emit(EVENT.LOCAL_LEAVE, {
  1574. userID: this.pusher.userID
  1575. });
  1576. break;
  1577. case 2003:
  1578. console.log('渲染首帧视频', code);
  1579. break;
  1580. case 1020:
  1581. case 1031:
  1582. case 1032:
  1583. case 1033:
  1584. case 1034:
  1585. // 通过 userController 处理 1020 1031 1032 1033 1034
  1586. this.userController.userEventHandler(event);
  1587. break;
  1588. case -1301:
  1589. console.error('打开摄像头失败: ', code);
  1590. this._emitter.emit(EVENT.ERROR, {
  1591. code,
  1592. message
  1593. });
  1594. break;
  1595. case -1302:
  1596. console.error('打开麦克风失败: ', code);
  1597. this._emitter.emit(EVENT.ERROR, {
  1598. code,
  1599. message
  1600. });
  1601. break;
  1602. case -1303:
  1603. console.error('视频编码失败: ', code);
  1604. this._emitter.emit(EVENT.ERROR, {
  1605. code,
  1606. message
  1607. });
  1608. break;
  1609. case -1304:
  1610. console.error('音频编码失败: ', code);
  1611. this._emitter.emit(EVENT.ERROR, {
  1612. code,
  1613. message
  1614. });
  1615. break;
  1616. case -1307:
  1617. console.error('推流连接断开: ', code);
  1618. this._emitter.emit(EVENT.ERROR, {
  1619. code,
  1620. message
  1621. });
  1622. break;
  1623. case -100018:
  1624. console.error('进房失败: ', code, message);
  1625. this._emitter.emit(EVENT.ERROR, {
  1626. code,
  1627. message
  1628. });
  1629. break;
  1630. case 5000:
  1631. console.log('小程序被挂起: ', code); // 终端 sdk 建议执行退房操作,唤起时重新进房,临时解决方案,待小程序SDK完全实现自动重新推流后可以去掉
  1632. this.status.isPending = true;
  1633. if (this.status.isPush) {
  1634. // this.exitRoom()
  1635. const tempUrl = this.pusher.url;
  1636. this.pusher.url = ''; // console.log('5000 小程序被挂起后更换pusher', this.data.pusher.getPusherContext().webviewId)
  1637. this.setData(
  1638. {
  1639. pusher: this.pusher
  1640. },
  1641. () => {
  1642. this.pusher.url = tempUrl;
  1643. this.setData(
  1644. {
  1645. pusher: this.pusher
  1646. },
  1647. () => {
  1648. this.pusher.getPusherContext().start();
  1649. console.log('5000 小程序被挂起后更换pusher', this.pusher);
  1650. }
  1651. );
  1652. }
  1653. );
  1654. }
  1655. break;
  1656. case 1021:
  1657. console.log('网络类型发生变化,需要重新进房', code);
  1658. break;
  1659. case 2007:
  1660. console.log('本地视频播放loading: ', code);
  1661. break;
  1662. case 2004:
  1663. console.log('本地视频播放开始: ', code);
  1664. break;
  1665. default:
  1666. console.log(message, code);
  1667. }
  1668. this._emitter.emit(EVENT.LOCAL_STATE_UPDATE, {
  1669. data: event
  1670. });
  1671. },
  1672. pusherNetStatusHandlerFun: function(event) {
  1673. // 触发 LOCAL_NET_STATE_UPDATE
  1674. this._emitter.emit(EVENT.LOCAL_NET_STATE_UPDATE, event);
  1675. },
  1676. pusherErrorHandlerFun: function(event) {
  1677. // 触发 ERROR
  1678. console.warn(TAG_NAME, 'pusher error', event);
  1679. try {
  1680. const code = event.detail.errCode;
  1681. const message = event.detail.errMsg;
  1682. this._emitter.emit(EVENT.ERROR, {
  1683. code,
  1684. message
  1685. });
  1686. } catch (exception) {
  1687. console.error(TAG_NAME, 'pusher error data parser exception', event, exception);
  1688. }
  1689. },
  1690. pusherBGMStartHandlerFun: function(event) {
  1691. // 触发 BGM_START 已经在playBGM方法中进行处理
  1692. // this._emitter.emit(EVENT.BGM_PLAY_START, { data: event })
  1693. },
  1694. pusherBGMProgressHandlerFun: function(event) {
  1695. // BGM_PROGRESS
  1696. this._emitter.emit(EVENT.BGM_PLAY_PROGRESS, event);
  1697. },
  1698. pusherBGMCompleteHandlerFun: function(event) {
  1699. // BGM_COMPLETE
  1700. this._emitter.emit(EVENT.BGM_PLAY_COMPLETE, event);
  1701. },
  1702. // player event handler
  1703. // 获取 player ID 再进行触发
  1704. playerStateChangeFun: function(event) {
  1705. // console.log(TAG_NAME, 'playerStateChangeFun', event)
  1706. this._emitter.emit(EVENT.REMOTE_STATE_UPDATE, event);
  1707. },
  1708. playerFullscreenChangeFun: function(event) {
  1709. // console.log(TAG_NAME, '_playerFullscreenChange', event)
  1710. this._emitter.emit(EVENT.REMOTE_NET_STATE_UPDATE, event);
  1711. },
  1712. playerNetStatusFun: function(event) {
  1713. // console.log(TAG_NAME, 'playerNetStatusFun', event)
  1714. // 获取player 视频的宽高
  1715. const stream = this.userController.getStream({
  1716. userID: event.currentTarget.dataset.userid,
  1717. streamType: event.currentTarget.dataset.streamtype
  1718. });
  1719. if (stream && (stream.videoWidth !== event.detail.info.videoWidth || stream.videoHeight !== event.detail.info.videoHeight)) {
  1720. console.log(TAG_NAME, 'playerNetStatusFun update video size', event);
  1721. stream.videoWidth = event.detail.info.videoWidth;
  1722. stream.videoHeight = event.detail.info.videoHeight;
  1723. }
  1724. this._emitter.emit(EVENT.REMOTE_FULLSCREEN_UPDATE, event);
  1725. },
  1726. playerAudioVolumeNotifyFun: function(event) {
  1727. // console.log(TAG_NAME, 'playerAudioVolumeNotifyFun', event)
  1728. this._emitter.emit(EVENT.REMOTE_AUDIO_VOLUME_UPDATE, event);
  1729. },
  1730. /**
  1731. * 监听组件属性变更,外部变更组件属性时触发该监听,用于检查属性设置是否正常
  1732. * @param {Object} data 变更数据
  1733. */
  1734. propertyObserverFun: function(data) {
  1735. console.log(TAG_NAME, '_propertyObserver', data, this.config);
  1736. if (data.name === 'config') {
  1737. // const config = Object.assign(DEFAULT_PUSHER_CONFIG, data.newVal)
  1738. const config = data.newVal; // querystring 只支持String类型,做一个类型防御
  1739. if (typeof config.debugMode === 'string') {
  1740. config.debugMode === 'true' ? true : false;
  1741. } // 独立设置与pusher无关的配置
  1742. this.setData({
  1743. template: config.template,
  1744. debugMode: config.debugMode || false,
  1745. debug: config.debugMode || false
  1746. });
  1747. this.setPusherConfigFun(config);
  1748. }
  1749. },
  1750. toggleVideoFun() {
  1751. if (this.pusher.enableCamera) {
  1752. this.unpublishLocalVideo();
  1753. } else {
  1754. this.publishLocalVideo();
  1755. }
  1756. },
  1757. toggleAudioFun() {
  1758. if (this.pusher.enableMic) {
  1759. this.unpublishLocalAudio();
  1760. } else {
  1761. this.publishLocalAudio();
  1762. }
  1763. },
  1764. debugToggleRemoteVideoFun(event) {
  1765. console.log(TAG_NAME, '_debugToggleRemoteVideo', event.currentTarget.dataset);
  1766. const userID = event.currentTarget.dataset.userID;
  1767. const streamType = event.currentTarget.dataset.streamType;
  1768. const stream = this.streamList.find(item => {
  1769. return item.userID === userID && item.streamType === streamType;
  1770. });
  1771. if (stream.muteVideo) {
  1772. this.subscribeRemoteVideo({
  1773. userID,
  1774. streamType
  1775. });
  1776. this.setViewVisible({
  1777. userID,
  1778. streamType,
  1779. isVisible: true
  1780. });
  1781. } else {
  1782. this.unsubscribeRemoteVideo({
  1783. userID,
  1784. streamType
  1785. });
  1786. this.setViewVisible({
  1787. userID,
  1788. streamType,
  1789. isVisible: false
  1790. });
  1791. }
  1792. },
  1793. debugToggleRemoteAudioFun(event) {
  1794. console.log(TAG_NAME, '_debugToggleRemoteAudio', event.currentTarget.dataset);
  1795. const userID = event.currentTarget.dataset.userID;
  1796. const streamType = event.currentTarget.dataset.streamType;
  1797. const stream = this.streamList.find(item => {
  1798. return item.userID === userID && item.streamType === streamType;
  1799. });
  1800. if (stream.muteAudio) {
  1801. this.subscribeRemoteAudio({
  1802. userID
  1803. });
  1804. } else {
  1805. this.unsubscribeRemoteAudio({
  1806. userID
  1807. });
  1808. }
  1809. },
  1810. debugToggleVideoDebugFun() {
  1811. this.setData({
  1812. debug: !this.debug
  1813. });
  1814. },
  1815. debugExitRoomFun() {
  1816. this.exitRoom();
  1817. },
  1818. debugEnterRoomFun() {
  1819. this.publishLocalVideo();
  1820. this.publishLocalAudio();
  1821. this.enterRoom({
  1822. roomID: this.config.roomID
  1823. }).then(() => {
  1824. // 进房后开始推送视频或音频
  1825. });
  1826. },
  1827. debugGoBackFun() {
  1828. wx.navigateBack({
  1829. delta: 1
  1830. });
  1831. },
  1832. debugTogglePanelFun() {
  1833. this.setData({
  1834. debugPanel: !this.debugPanel
  1835. });
  1836. },
  1837. toggleAudioVolumeTypeFun() {
  1838. if (this.pusher.audioVolumeType === 'voicecall') {
  1839. this.setPusherConfigFun({
  1840. audioVolumeType: 'media'
  1841. });
  1842. } else {
  1843. this.setPusherConfigFun({
  1844. audioVolumeType: 'voicecall'
  1845. });
  1846. }
  1847. },
  1848. toggleSoundModeFun() {
  1849. if (this.userList.length === 0) {
  1850. return;
  1851. }
  1852. const stream = this.userController.getStream({
  1853. userID: this.userList[0].userID,
  1854. streamType: 'main'
  1855. });
  1856. if (stream) {
  1857. if (stream.soundMode === 'speaker') {
  1858. stream['soundMode'] = 'ear';
  1859. } else {
  1860. stream['soundMode'] = 'speaker';
  1861. }
  1862. this.setPlayerConfigFun({
  1863. userID: stream.userID,
  1864. streamType: 'main',
  1865. config: {
  1866. soundMode: stream['soundMode']
  1867. }
  1868. });
  1869. }
  1870. },
  1871. /**
  1872. * 退出通话
  1873. */
  1874. hangUpFun: function() {
  1875. this.exitRoom();
  1876. wx.navigateBack({
  1877. delta: 1
  1878. });
  1879. },
  1880. /**
  1881. * 切换订阅音频状态
  1882. */
  1883. handleSubscribeAudio: function() {
  1884. if (this.pusher.enableMic) {
  1885. this.unpublishLocalAudio();
  1886. } else {
  1887. this.publishLocalAudio();
  1888. }
  1889. },
  1890. /**
  1891. * 切换订阅远端视频状态
  1892. * @param event
  1893. */
  1894. handleSubscribeRemoteVideoFun: function(event) {
  1895. const userID = event.currentTarget.dataset.userID;
  1896. const streamType = event.currentTarget.dataset.streamType;
  1897. const stream = this.streamList.find(item => {
  1898. return item.userID === userID && item.streamType === streamType;
  1899. });
  1900. if (stream.muteVideo) {
  1901. this.subscribeRemoteVideo({
  1902. userID,
  1903. streamType
  1904. });
  1905. } else {
  1906. this.unsubscribeRemoteVideo({
  1907. userID,
  1908. streamType
  1909. });
  1910. }
  1911. },
  1912. /**
  1913. * 将远端视频取消全屏
  1914. * @param event
  1915. */
  1916. handleSubscribeRemoteAudioFun: function(event) {
  1917. const userID = event.currentTarget.dataset.userID;
  1918. const streamType = event.currentTarget.dataset.streamType;
  1919. const stream = this.streamList.find(item => {
  1920. return item.userID === userID && item.streamType === streamType;
  1921. });
  1922. if (stream.muteAudio) {
  1923. this.subscribeRemoteAudio({
  1924. userID
  1925. });
  1926. } else {
  1927. this.unsubscribeRemoteAudio({
  1928. userID
  1929. });
  1930. }
  1931. },
  1932. /**
  1933. * grid布局, 唤起 memberlist-panel
  1934. */
  1935. switchMemberListPanelFun() {
  1936. this.setData({
  1937. panelName: this.panelName !== 'memberlist-panel' ? 'memberlist-panel' : ''
  1938. });
  1939. },
  1940. /**
  1941. * grid布局, 唤起setting-panel
  1942. */
  1943. switchSettingPanelFun() {
  1944. this.setData({
  1945. panelName: this.panelName !== 'setting-panel' ? 'setting-panel' : ''
  1946. });
  1947. },
  1948. handleMaskerClickFun() {
  1949. this.setData({
  1950. panelName: ''
  1951. });
  1952. },
  1953. setPuserPropertyFun(event) {
  1954. // console.log(TAG_NAME, '_setPuserProperty', event)
  1955. const key = event.currentTarget.dataset.key;
  1956. let value = event.currentTarget.dataset.value;
  1957. const config = {};
  1958. if (value === 'true') {
  1959. value = true;
  1960. } else if (value === 'false') {
  1961. value = false;
  1962. }
  1963. if (typeof value === 'boolean') {
  1964. config[key] = !this.pusher[key];
  1965. } else if (typeof value === 'string' && value.indexOf('|') > 0) {
  1966. value = value.split('|');
  1967. if (this.pusher[key] === value[0]) {
  1968. config[key] = value[1];
  1969. } else {
  1970. config[key] = value[0];
  1971. }
  1972. } // console.log(TAG_NAME, '_setPuserProperty', config)
  1973. this.setPusherConfigFun(config);
  1974. },
  1975. setPlayerPropertyFun(event) {
  1976. console.log(TAG_NAME, '_setPlayerProperty', event);
  1977. const userID = event.currentTarget.dataset.userid;
  1978. const streamType = event.currentTarget.dataset.streamtype;
  1979. const key = event.currentTarget.dataset.key;
  1980. let value = event.currentTarget.dataset.value;
  1981. const stream = this.userController.getStream({
  1982. userID: userID,
  1983. streamType: streamType
  1984. });
  1985. if (!stream) {
  1986. return;
  1987. }
  1988. const config = {};
  1989. if (value === 'true') {
  1990. value = true;
  1991. } else if (value === 'false') {
  1992. value = false;
  1993. }
  1994. if (typeof value === 'boolean') {
  1995. config[key] = !stream[key];
  1996. } else if (typeof value === 'string' && value.indexOf('|') > 0) {
  1997. value = value.split('|');
  1998. if (stream[key] === value[0]) {
  1999. config[key] = value[1];
  2000. } else {
  2001. config[key] = value[0];
  2002. }
  2003. }
  2004. console.log(TAG_NAME, '_setPlayerProperty', config);
  2005. this.setPlayerConfigFun({
  2006. userID,
  2007. streamType,
  2008. config
  2009. });
  2010. },
  2011. switchStreamTypeFun(event) {
  2012. const userID = event.currentTarget.dataset.userid;
  2013. const streamType = event.currentTarget.dataset.streamtype;
  2014. const stream = this.userController.getStream({
  2015. userID: userID,
  2016. streamType: streamType
  2017. });
  2018. if (stream && stream.streamType === 'main') {
  2019. if (stream._definitionType === 'small') {
  2020. this.subscribeRemoteVideo({
  2021. userID,
  2022. streamType: 'main'
  2023. });
  2024. } else {
  2025. this.subscribeRemoteVideo({
  2026. userID,
  2027. streamType: 'small'
  2028. });
  2029. }
  2030. }
  2031. },
  2032. handleSnapshotClickFun(event) {
  2033. wx.showToast({
  2034. title: '开始截屏',
  2035. icon: 'none',
  2036. duration: 1000
  2037. });
  2038. const userID = event.currentTarget.dataset.userid;
  2039. const streamType = event.currentTarget.dataset.streamtype;
  2040. this.snapshot({
  2041. userID,
  2042. streamType
  2043. });
  2044. },
  2045. /**
  2046. * grid布局, 绑定事件
  2047. */
  2048. bindEventGridFun() {
  2049. // 远端音量变更
  2050. this.on(EVENT.REMOTE_AUDIO_VOLUME_UPDATE, event => {
  2051. const data = event.data;
  2052. const userID = data.currentTarget.dataset.userid;
  2053. const streamType = data.currentTarget.dataset.streamtype;
  2054. const volume = data.detail.volume; // console.log(TAG_NAME, '远端音量变更', userID, streamType, volume)
  2055. const stream = this.userController.getStream({
  2056. userID: userID,
  2057. streamType: streamType
  2058. });
  2059. if (stream) {
  2060. stream.volume = volume;
  2061. }
  2062. this.setData(
  2063. {
  2064. streamList: this.streamList
  2065. },
  2066. () => {}
  2067. );
  2068. });
  2069. },
  2070. toggleFullscreenFun(event) {
  2071. console.log(TAG_NAME, '_toggleFullscreen', event);
  2072. const userID = event.currentTarget.dataset.userID;
  2073. const streamType = event.currentTarget.dataset.streamType;
  2074. if (this._isFullscreen) {
  2075. this.exitFullscreen({
  2076. userID,
  2077. streamType
  2078. })
  2079. .then(() => {
  2080. this._isFullscreen = false;
  2081. })
  2082. .catch(() => {});
  2083. } else {
  2084. // const stream = this.userController.getStream({ userID, streamType })
  2085. const direction = 0; // 已知问题:视频的尺寸需要等待player触发NetStatus事件才能获取到,如果进房就双击全屏,全屏后的方向有可能不对。
  2086. // if (stream && stream.videoWidth && stream.videoHeight) {
  2087. // // 如果是横视频,全屏时进行横屏处理。如果是竖视频,则为0
  2088. // direction = stream.videoWidth > stream.videoHeight ? 90 : 0
  2089. // }
  2090. this.enterFullscreen({
  2091. userID,
  2092. streamType,
  2093. direction
  2094. })
  2095. .then(() => {
  2096. this._isFullscreen = true;
  2097. })
  2098. .catch(() => {});
  2099. }
  2100. }
  2101. }
  2102. };
  2103. </script>
  2104. <style>
  2105. @import './trtc-room.css';
  2106. </style>