live-wechat.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. <template>
  2. <view class="container" data-type="template" data-is="1v1" data-attr="pusher, streamList, debug">
  3. <view v-for="item in streams.slice(0,1)" :key="item.streamID" class="player-container" :style="'display:'+playerStyle">
  4. <template v-if="item.src && (item.hasVideo || item.hasAudio)">
  5. <live-player
  6. class="player"
  7. :data-userid="item.userID"
  8. :data-streamid="item.streamID"
  9. :data-streamtype="item.streamType"
  10. :src="item.src"
  11. mode="RTC"
  12. :autoplay="item.autoplay"
  13. :mute-audio="item.muteAudio"
  14. :mute-video="item.muteVideo"
  15. :orientation="item.orientation"
  16. :object-fit="item.objectFit"
  17. :background-mute="item.enableBackgroundMute"
  18. :min-cache="item.minCache"
  19. :max-cache="item.maxCache"
  20. :sound-mode="item.soundMode"
  21. :enable-recv-message="item.enableRecvMessage"
  22. :auto-pause-if-navigate="item.autoPauseIfNavigate"
  23. :auto-pause-if-open-native="item.autoPauseIfOpenNative"
  24. :debug="debug"
  25. @statechange="playerStateChangeFun"
  26. @fullscreenchange="playerFullscreenChangeFun"
  27. @netstatus="playerNetStatusFun"
  28. @audiovolumenotify="playerAudioVolumeNotifyFun"
  29. :idAttr="item.streamID" />
  30. </template>
  31. </view>
  32. <view class="pusher-container" :style="'display:'+pusherStyle">
  33. <live-pusher
  34. class="pusher"
  35. :url="pusher.url"
  36. :mode="pusher.mode"
  37. :autopush="pusher.autopush"
  38. :enable-camera="pusher.enableCamera"
  39. :enable-mic="pusher.enableMic"
  40. :enable-agc="pusher.enableAgc"
  41. :enable-ans="pusher.enableAns"
  42. :enable-ear-monitor="pusher.enableEarMonitor"
  43. :auto-focus="pusher.enableAutoFocus"
  44. :zoom="pusher.enableZoom"
  45. :min-bitrate="pusher.minBitrate"
  46. :max-bitrate="pusher.maxBitrate"
  47. :video-width="pusher.videoWidth"
  48. :video-height="pusher.videoHeight"
  49. :beauty="pusher.beautyLevel"
  50. :whiteness="pusher.whitenessLevel"
  51. :orientation="pusher.videoOrientation"
  52. :aspect="pusher.videoAspect"
  53. :device-position="pusher.frontCamera"
  54. :remote-mirror="pusher.enableRemoteMirror"
  55. :local-mirror="pusher.localMirror"
  56. :background-mute="pusher.enableBackgroundMute"
  57. :audio-quality="pusher.audioQuality"
  58. :audio-volume-type="pusher.audioVolumeType"
  59. :audio-reverb-type="pusher.audioReverbType"
  60. :waiting-image="pusher.waitingImage"
  61. :debug="debug"
  62. @statechange="pusherStateChangeHandlerFun"
  63. @netstatus="pusherNetStatusHandlerFun"
  64. @error="pusherErrorHandlerFun"
  65. @bgmstart="pusherBGMStartHandlerFun"
  66. @bgmprogress="pusherBGMProgressHandlerFun"
  67. @bgmcomplete="pusherBGMCompleteHandlerFun" />
  68. </view>
  69. </view>
  70. </template>
  71. <script>
  72. export default {
  73. name: 'jhlive-wechat',
  74. data() {
  75. return {
  76. pusher: null,
  77. users: [],
  78. };
  79. },
  80. props: {
  81. sdkAppID: Number,
  82. userSig: String,
  83. userid: String,
  84. roomid: String,
  85. isAuthor: Boolean,
  86. linkMic: Boolean
  87. },
  88. computed: {
  89. streams() {
  90. console.log('--users', this.users);
  91. let streams = [];
  92. this.users.forEach(v=>{
  93. let userStreams = Object.entries(v.streams);
  94. streams = streams.concat(userStreams.map(([kk,vv])=>vv).filter(vv=>vv.hasVideo));
  95. });
  96. console.log('--streams', streams);
  97. return streams;
  98. },
  99. playerStyle() {
  100. return (!this.isAuthor)?'block':'none';
  101. },
  102. pusherStyle() {
  103. return this.isAuthor?'block':'none';
  104. }
  105. },
  106. methods: {
  107. enterRoom() {
  108. this.pusher = {
  109. url: 'room://cloud.tencent.com/rtc?sdkappid=' +
  110. this.sdkAppID +
  111. '&roomid=' +
  112. this.roomid +
  113. '&userid=' +
  114. this.userid +
  115. '&usersig=' +
  116. this.userSig +
  117. '&appscene=' +
  118. 'live' +
  119. '&encsmall=' +
  120. '1' +
  121. '&cloudenv=' +
  122. 'PRO',
  123. mode: 'RTC', // RTC:实时通话(trtc sdk) live:直播模式(liteav sdk)
  124. autopush: this.isAuthor||this.linkMic, // 自动推送
  125. enableCamera: this.isAuthor, // 是否开启摄像头
  126. enableMic: this.isAuthor||this.linkMic, // 是否开启麦克风
  127. enableAgc: false, // 是否开启音频自动增益
  128. enableAns: false, // 是否开启音频噪声抑制
  129. enableEarMonitor: false, // 是否开启耳返(目前只在iOS平台有效)
  130. enableAutoFocus: true, // 是否自动对焦
  131. enableZoom: false, // 是否支持调整焦距
  132. minBitrate: 200, // 最小码率
  133. maxBitrate: 1000, // 最大码率
  134. videoWidth: 360, // 视频宽(若设置了视频宽高就会忽略aspect)
  135. videoHeight: 640, // 视频高(若设置了视频宽高就会忽略aspect)
  136. beautyLevel: 0, // 美颜,取值范围 0-9 ,0 表示关闭
  137. whitenessLevel: 0, // 美白,取值范围 0-9 ,0 表示关闭
  138. videoOrientation: 'vertical', // vertical horizontal
  139. videoAspect: '9:16', // 宽高比,可选值有 3:4,9:16
  140. frontCamera: 'front', // 前置或后置摄像头,可选值:front,back
  141. enableRemoteMirror: false, // 设置推流画面是否镜像,产生的效果会表现在 live-player
  142. localMirror: 'auto', // auto:前置摄像头镜像,后置摄像头不镜像(系统相机的表现)enable:前置摄像头和后置摄像头都镜像 disable: 前置摄像头和后置摄像头都不镜像
  143. enableBackgroundMute: false, // 进入后台时是否静音
  144. audioQuality: 'high', // 高音质(48KHz)或低音质(16KHz),可选值:high,low
  145. audioVolumeType: 'voicecall', // 声音类型 可选值: media: 媒体音量,voicecall: 通话音量
  146. audioReverbType: 0, // 音频混响类型 0: 关闭 1: KTV 2: 小房间 3:大会堂 4:低沉 5:洪亮 6:金属声 7:磁性
  147. waitingImage: '', // 当微信切到后台时的垫片图片 trtc暂不支持
  148. waitingImageHash: ''
  149. };
  150. console.log('JHLIVE-PUSHER CONFIG', this.pusher);
  151. setTimeout(()=>{
  152. this.pusherContext = wx.createLivePusherContext();
  153. this.pusherContext.start();
  154. console.log('JHLIVE-PUSHER pusherContext', this.pusherContext);
  155. },1000);
  156. },
  157. exitRoom: function() {
  158. if (this.pusherContext) {
  159. this.pusherContext.stop();
  160. this.pusherContext = null;
  161. }
  162. },
  163. switchCarema() {
  164. this.pusher.frontCamera = this.pusher.frontCamera=='front'?'back':'front';
  165. this.$forceUpdate();
  166. },
  167. setBeauty(blv, wlv) {
  168. this.pusher.beautyLevel = blv;
  169. this.pusher.whitenessLevel = wlv;
  170. this.$forceUpdate();
  171. },
  172. /** --- pusher event handler --- **/
  173. pusherStateChangeHandlerFun: function(event) {
  174. const code = event.detail.code;
  175. const message = event.detail.message;
  176. switch (code) {
  177. case 0:
  178. console.log(message, code);
  179. break;
  180. case 1001:
  181. console.log('已经连接推流服务器', code);
  182. break;
  183. case 1002:
  184. console.log('已经与服务器握手完毕,开始推流', code);
  185. break;
  186. case 1003:
  187. console.log('打开摄像头成功', code);
  188. break;
  189. case 1004:
  190. console.log('录屏启动成功', code);
  191. break;
  192. case 1005:
  193. console.log('推流动态调整分辨率', code);
  194. break;
  195. case 1006:
  196. console.log('推流动态调整码率', code);
  197. break;
  198. case 1007:
  199. console.log('首帧画面采集完成', code);
  200. break;
  201. case 1008:
  202. console.log('编码器启动', code);
  203. break;
  204. case 1018:
  205. console.log('进房成功', code);
  206. break;
  207. case 1019:
  208. console.log('退出房间', code);
  209. break;
  210. case 2003:
  211. console.log('渲染首帧视频', code);
  212. break;
  213. case 1020: // 远端用户全量列表更新
  214. break;
  215. case 1031: {// 远端用户进房通知
  216. const data = JSON.parse(event.detail.message)||{};
  217. console.log('远端用户进房通知', data);
  218. (data.userlist||[]).forEach( v=> {
  219. if(!this.users.find(vv => vv.userid == v.userid)) {
  220. this.users.push({
  221. userid: v.userid,
  222. streams: {},
  223. });
  224. }
  225. })
  226. }break;
  227. case 1032: {// 远端用户退房通知
  228. console.log('远端用户退房通知', data);
  229. const data = JSON.parse(event.detail.message)||{};
  230. (data.userlist||[]).forEach( v=> {
  231. let user = this.users.find(v => v.userid == data.userid);
  232. if(!user) return;
  233. user.streams.forEach(vv=>{
  234. if(vv.playerContext) {
  235. vv.playerContext.stop;
  236. vv.playerContext = null;
  237. }
  238. });
  239. user.streams = [];
  240. })
  241. this.users = this.users.filter(v => (data.userlist||[]).find(vv=>vv.userid!=v.userid));
  242. }break;
  243. case 1033: {// 用户视频状态变化,新增stream或者更新stream 状态
  244. console.log('用户视频状态变化,新增stream或者更新stream', data);
  245. const data = JSON.parse(event.detail.message)||{};
  246. (data.userlist||[]).forEach( v=> {
  247. let user = this.users.find(vv => v.userid == v.userid);
  248. if(!user) return;
  249. let stream = user.streams[v.streamtype] = user.streams[v.streamtype] || {
  250. streamType: v.streamtype,
  251. src: '',
  252. mode: 'RTC',
  253. autoplay: true, // 7.0.9 必须设置为true,否则 Android 有概率调用play()失败
  254. muteAudio: false, // 默认不拉取音频,需要手动订阅
  255. muteVideo: false, // 默认不拉取视频,需要手动订阅
  256. orientation: 'vertical', // 画面方向 vertical horizontal
  257. objectFit: 'fillCrop', // 填充模式,可选值有 contain,fillCrop
  258. enableBackgroundMute: false, // 进入后台时是否静音(已废弃,默认退台静音)
  259. minCache: 1, // 最小缓冲区,单位s(RTC 模式推荐 0.2s)
  260. maxCache: 2, // 最大缓冲区,单位s(RTC 模式推荐 0.8s)
  261. soundMode: 'speaker', // 声音输出方式 ear speaker
  262. enableRecvMessage: 'false', // 是否接收SEI消息
  263. autoPauseIfNavigate: true, // 当跳转到其它小程序页面时,是否自动暂停本页面的实时音视频播放
  264. autoPauseIfOpenNative: true // 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
  265. }
  266. stream.userID = v.userid;
  267. stream.streamID = v.userid + '_' + stream.streamType;
  268. stream.hasVideo = v.hasvideo;
  269. stream[`has${stream.streamType}Video`] = stream.hasVideo;
  270. stream.src = v.playurl;
  271. // stream.objectFit = stream.hasVideo && stream.streamType === 'aux' && 'contain';
  272. if(stream.hasVideo) stream.playerContext = wx.createLivePlayerContext(stream.streamID, this);
  273. })
  274. this.users = [...this.users];
  275. }break;
  276. case 1034: {// 远端用户音频状态位变化通知
  277. console.log('远端用户音频状态位变化通知', data);
  278. const data = JSON.parse(event.detail.message)||{};
  279. (data.userlist||[]).forEach( v=> {
  280. let user = this.users.find(vv => v.userid == vv.userid);
  281. if(!user) return;
  282. v.streamtype = 'main';
  283. let stream = user.streams[v.streamtype] = user.streams[v.streamtype] || {
  284. streamType: v.streamtype,
  285. src: '',
  286. mode: 'RTC',
  287. autoplay: true, // 7.0.9 必须设置为true,否则 Android 有概率调用play()失败
  288. muteAudio: false, // 默认不拉取音频,需要手动订阅
  289. muteVideo: false, // 默认不拉取视频,需要手动订阅
  290. orientation: 'vertical', // 画面方向 vertical horizontal
  291. objectFit: 'fillCrop', // 填充模式,可选值有 contain,fillCrop
  292. enableBackgroundMute: false, // 进入后台时是否静音(已废弃,默认退台静音)
  293. minCache: 1, // 最小缓冲区,单位s(RTC 模式推荐 0.2s)
  294. maxCache: 2, // 最大缓冲区,单位s(RTC 模式推荐 0.8s)
  295. soundMode: 'speaker', // 声音输出方式 ear speaker
  296. enableRecvMessage: 'false', // 是否接收SEI消息
  297. autoPauseIfNavigate: true, // 当跳转到其它小程序页面时,是否自动暂停本页面的实时音视频播放
  298. autoPauseIfOpenNative: true // 当跳转到其它微信原生页面时,是否自动暂停本页面的实时音视频播放
  299. }
  300. stream.userID = v.userid;
  301. stream.streamID = v.userid + '_' + stream.streamType;
  302. stream.hasAudio = v.hasaudio;
  303. stream[`has${stream.streamType}Audio`] = stream.hasAudio;
  304. stream.src = v.playurl;
  305. if(stream.hasAudio) stream.playerContext = wx.createLivePlayerContext(stream.streamID, this);
  306. })
  307. this.users = [...this.users];
  308. }break;
  309. case -1301:
  310. console.error('打开摄像头失败: ', code);
  311. break;
  312. case -1302:
  313. console.error('打开麦克风失败: ', code);
  314. break;
  315. case -1303:
  316. console.error('视频编码失败: ', code);
  317. break;
  318. case -1304:
  319. console.error('音频编码失败: ', code);
  320. break;
  321. case -1307:
  322. console.error('推流连接断开: ', code);
  323. break;
  324. case -100018:
  325. console.error('进房失败: ', code, message);
  326. break;
  327. case 5000:
  328. console.log('小程序被挂起: ', code);
  329. this.exitRoom()
  330. this.enterRoom()
  331. break;
  332. case 1021:
  333. console.log('网络类型发生变化,需要重新进房', code);
  334. break;
  335. case 2007:
  336. console.log('本地视频播放loading: ', code);
  337. break;
  338. case 2004:
  339. console.log('本地视频播放开始: ', code);
  340. break;
  341. default:
  342. console.log(message, code);
  343. }
  344. },
  345. pusherNetStatusHandlerFun: function(event) {
  346. },
  347. pusherErrorHandlerFun: function(event) {
  348. try {
  349. console.error('pusher error: ');
  350. const code = event.detail.errCode;
  351. const message = event.detail.errMsg;
  352. console.error(code, message);
  353. } catch (exception) {}
  354. },
  355. pusherBGMStartHandlerFun: function(event) {
  356. },
  357. pusherBGMProgressHandlerFun: function(event) {
  358. },
  359. pusherBGMCompleteHandlerFun: function(event) {
  360. },
  361. /** --- player event handler --- **/
  362. playerStateChangeFun: function(event) {
  363. console.log('playerStateChangeFun');
  364. },
  365. playerFullscreenChangeFun: function(event) {
  366. console.log('playerFullscreenChangeFun');
  367. },
  368. playerNetStatusFun: function(event) {
  369. console.log('playerNetStatusFun');
  370. let dataset = event.currentTarget.dataset;
  371. let info = event.detail.info;
  372. let user = this.users.find(v => v.userid == dataset.userid);
  373. if(!user) return;
  374. let stream = user.streams[dataset.streamtype];
  375. if(!stream) return;
  376. stream.videoWidth = info.videoWidth;
  377. stream.videoHeight = info.videoHeight;
  378. },
  379. playerAudioVolumeNotifyFun: function(event) {
  380. console.log('playerAudioVolumeNotifyFun');
  381. },
  382. }
  383. };
  384. </script>
  385. <style lang="less" scoped>
  386. .container {
  387. width: 100%;
  388. height: 100%;
  389. }
  390. .pusher-container {
  391. width: 100%;
  392. height: 100%;
  393. }
  394. .player-container{
  395. width: 100%;
  396. height: 100%;
  397. }
  398. .pusher {
  399. width: 100%;
  400. height: 100%;
  401. }
  402. .player {
  403. width: 100%;
  404. height: 100%;
  405. }
  406. </style>