live.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. import { Message } from 'element-ui';
  2. import store from '@/store';
  3. import { rtc, updateMcResult, getDevice, createData } from '@/views/live/common';
  4. export {
  5. initSDK,
  6. downloadWebSDK,
  7. createScript,
  8. getLiveStat,
  9. roomUpdate,
  10. sendMsg,
  11. drawChange,
  12. publishStream,
  13. handsDown,
  14. sendPublishMessage,
  15. closeVideo,
  16. chatRoll,
  17. updateMcResult,
  18. reconnection,
  19. getNetPoint,
  20. pauseAudio,
  21. playAudio,
  22. pauseVideo,
  23. playVideo
  24. } from '@/views/live/common';
  25. /**
  26. * 推送本地流
  27. */
  28. function publishStream(streamName) {
  29. rtc.publish({
  30. streamName,
  31. // 推流成功,更新上麦结果
  32. success: stream => {
  33. console.log('推流成功', stream);
  34. updateMcResult(stream.id(), 1);
  35. },
  36. fail: str => {
  37. // 推流失败,更新上麦结果
  38. console.log('推流失败,更新上麦结果', str);
  39. updateMcResult('', 0);
  40. }
  41. });
  42. }
  43. /**
  44. * 创建本地流
  45. */
  46. export function createLocalStream(vue) {
  47. rtc.createLocalStream({
  48. streamName: 'main',
  49. createData: createData(),
  50. success(stream) {
  51. console.log('创建本地流成功', stream);
  52. vue.hasAudio = stream.hasAudio();
  53. vue.hasVideo = stream.hasVideo();
  54. // 创建本地流成功,将流展示到id为 live 的 dom 元素盒子中
  55. stream.show('live');
  56. publishStream('main'); // 如果需要立即推流,执行 publish 方法
  57. },
  58. fail(data) {
  59. console.log('创建本地流失败:', data);
  60. // 创建本地流失败,应用层处理
  61. Message({
  62. type: 'error',
  63. message: '创建本地流失败:' + data.err
  64. });
  65. }
  66. });
  67. }
  68. /**
  69. * 结束本地流
  70. */
  71. export function closeVideoTeacher(options) {
  72. rtc.closeVideo(options);
  73. }
  74. /**
  75. * 初始化监听事件
  76. */
  77. export function initListener(vue) {
  78. rtc.on('login_success', data => {
  79. console.log('登录成功', data);
  80. Message({
  81. message: '登录成功',
  82. type: 'success'
  83. });
  84. vue.roomData = data;
  85. // 初始化画板需要的数据
  86. let canvasInitData = {
  87. allowDraw: true, // 是否具有书写画笔权限(讲师权限) true / false
  88. id: 'draw-parent',
  89. pptDisplay: 1, // 文档展示方式。默认0,按窗口 1,按宽度
  90. liveId: data.live.status === 1 ? data.live.id : '' // 如果直播已经开始,需将直播 id 传入 sdk 中
  91. };
  92. // 初始化画板
  93. rtc.canvasInit(canvasInitData);
  94. });
  95. rtc.on('login_failed', data => {
  96. console.log('登录失败', data);
  97. Message({
  98. message: '登录失败:' + JSON.stringify(data),
  99. type: 'warning'
  100. });
  101. });
  102. // 教师 必须在加入房间成功的事件回调里创建本地流
  103. rtc.on('conference_join', () => {
  104. console.log('加入房间成功');
  105. getDevice({
  106. success(data) {
  107. console.log('获取音视频设备成功', data);
  108. let device = store.state.app.liveDevice;
  109. vue.device = data;
  110. if (data.video.length === 0 && data.audio.length === 0) {
  111. Message({
  112. type: 'warning',
  113. message: '无音视频设备,无法创建本地流'
  114. });
  115. }
  116. let isVideo =
  117. data.video.some(item => item.deviceId === device.video) ||
  118. (data.video.length === 0 && device.video.length === 0);
  119. let isAudio =
  120. data.audio.some(item => item.deviceId === device.audio) ||
  121. (data.audio.length === 0 && device.audio.length === 0);
  122. if (!isVideo || !isAudio) {
  123. vue.setDevice(false);
  124. } else {
  125. createLocalStream(vue);
  126. }
  127. },
  128. fail(str) {
  129. console.log('直播关闭状态或查询直播失败: ', str);
  130. Message({
  131. type: 'error',
  132. message: `直播关闭状态或查询直播失败: ${str}`
  133. });
  134. }
  135. });
  136. });
  137. rtc.on('conference_join_failed', err => {
  138. // 加入房间失败 err为错误原因
  139. console.log('加入房间失败', err);
  140. Message({
  141. message: `加入房间失败:${err}`,
  142. type: 'warning'
  143. });
  144. });
  145. // 新增订阅流事件
  146. rtc.on('allow_sub', tryStream => {
  147. if (tryStream.isMixed()) {
  148. console.log('是混合流,不订阅');
  149. } else {
  150. // 订阅远程流
  151. rtc.trySubscribeStream({
  152. tryStream,
  153. success(stream) {
  154. // 订阅流成功
  155. let streamType = stream.streamType();
  156. vue.remoteStreamType = streamType;
  157. console.log('订阅流成功', streamType);
  158. stream.show('student', 'contain'); // 将流显示到指定 id 的盒子中
  159. if (streamType === 1 || streamType === 10) {
  160. vue.connect = true;
  161. vue.callLoading = false;
  162. }
  163. if (streamType === 10) {
  164. vue.dealStudentConnection(vue.connectUid, 2, vue.roomInfo.video_mode);
  165. }
  166. },
  167. fail(err) {
  168. console.log('订阅流失败', err);
  169. }
  170. });
  171. }
  172. });
  173. // 直播未开始,不能推流
  174. rtc.on('not_live', function () {
  175. console.log('直播未开始,不能推流');
  176. Message({
  177. message: '直播未开始,不能推流',
  178. type: 'warning'
  179. });
  180. });
  181. // 推流前查询直播状态失败,导致没有推流
  182. rtc.on('local_stream_publish_failed', function () {
  183. console.log('推流前查询直播状态失败,导致没有推流');
  184. Message({
  185. message: '推流前查询直播状态失败,导致没有推流',
  186. type: 'warning'
  187. });
  188. });
  189. // 房间全量信息事件(人员进出时广播)
  190. rtc.on('room_context', roomData => {
  191. vue.roomContext = JSON.parse(roomData);
  192. vue.getLiveRoomStudentList();
  193. console.log('房间全量信息事件(人员进出时广播)', JSON.parse(roomData));
  194. });
  195. rtc.on('publish_stream', str => {
  196. console.log('直播已开启', str);
  197. vue.liveStat = true;
  198. });
  199. rtc.on('end_stream', str => {
  200. console.log('直播已关闭', str);
  201. vue.liveStat = false;
  202. });
  203. rtc.on('switch_user_settings', settingData => {
  204. // 单个用户配置监听
  205. console.log(settingData);
  206. });
  207. // 人员列表事件(人员麦序变化时广播)
  208. rtc.on('speak_context', speakData => {
  209. vue.speakData = JSON.parse(speakData);
  210. console.log('人员列表事件(人员麦序变化时广播)', JSON.parse(speakData));
  211. });
  212. rtc.on('switch_settings', data => {
  213. console.log('房间设置事件', data); // 房间设置事件
  214. });
  215. // 网络整体状态事件
  216. rtc.on('netStatus', data => {
  217. vue.netStatus = data.netStatus;
  218. });
  219. // 单条流状态通知事件
  220. rtc.on('streamStatus', data => {
  221. console.log(data);
  222. });
  223. // 推流异常断开事件
  224. rtc.on('publishStreamErr', data => {
  225. // 直播开启状态下,尝试重推这条流
  226. console.log('推流意外终止:' + data.streamName);
  227. publishStream('main');
  228. });
  229. // 视频无法自动播放
  230. rtc.on('playError', data => {
  231. console.log('视频无法自动播放', data);
  232. });
  233. // 监听通知移除流事件
  234. rtc.on('stream_removed', stream => {
  235. console.log('监听通知移除流事件', stream);
  236. if ('room_user_id' in vue.connectStudent && stream.streamType() === 10 && vue.connect) {
  237. vue.handsDown();
  238. }
  239. vue.connect = false;
  240. vue.remoteStreamType = -1;
  241. });
  242. // 停止订阅流
  243. rtc.on('unSub', unSubStream => {
  244. console.log('停止订阅流', unSubStream);
  245. rtc.unSubscribeStream({
  246. unSubStream,
  247. success(stream) {
  248. console.log('取消订阅流成功', stream.id());
  249. },
  250. fail(str) {
  251. console.log(str);
  252. }
  253. });
  254. });
  255. // 用户退出房间通知其他人员事件
  256. rtc.on('exit_room_user', data => {
  257. console.log('用户退出房间通知其他人员事件', data);
  258. vue.studentExitLiveRoom(data.id, false);
  259. });
  260. /**
  261. * 排麦监听事件
  262. */
  263. // 监听自己被邀请事件
  264. rtc.on('inviteUp', uid => {
  265. console.log('监听自己被邀请事件', uid);
  266. rtc.inviteAccept({
  267. success(str) {
  268. console.log('接受邀请成功', str);
  269. },
  270. fail(data) {
  271. console.log('接受邀请失败', data);
  272. }
  273. });
  274. });
  275. /**
  276. * 监听聊天事件
  277. */
  278. rtc.on('chat_message', data => {
  279. let dat = JSON.parse(data);
  280. console.log(dat);
  281. // 敏感词过滤:如果发送的聊天消息被系统判定包含敏感词,则只有发送者能收到本条消息,房间内其他人都不会收到这条聊天消息。
  282. // 如果返回消息中有 isFilterChat 字段(消息不包含敏感词返回数据中无isFilterChat字段),且isFilterChat的值为1,则说明该消息包含敏感字,除发送者外其他人不会收到这条消息。
  283. if (dat.isFilterChat && dat.isFilterChat === 1) {
  284. return;
  285. }
  286. vue.chatList.push(dat);
  287. });
  288. rtc.on('allowChatChange', function (data) {
  289. let msg = data.settings.allow_chat ? '开言' : '禁言';
  290. Message({
  291. type: 'success',
  292. message: `全体${msg}成功`
  293. });
  294. });
  295. }
  296. /**
  297. * 开启直播
  298. */
  299. export function startLive() {
  300. rtc.startLive({
  301. success(data) {
  302. console.log(data);
  303. publishStream('main'); // 如果需要立即推流,执行 publish 方法
  304. Message({
  305. message: '开启直播成功',
  306. type: 'success'
  307. });
  308. },
  309. fail(data) {
  310. Message({
  311. message: `开启直播失败:${data}`,
  312. type: 'warning'
  313. });
  314. }
  315. });
  316. }
  317. /**
  318. * 结束直播
  319. */
  320. export function stopLive() {
  321. rtc.stopLive({
  322. success() {
  323. Message({
  324. type: 'success',
  325. message: '直播已结束'
  326. });
  327. },
  328. fail(data) {
  329. Message({
  330. type: 'error',
  331. message: '结束直播失败:' + JSON.stringify(data)
  332. });
  333. }
  334. });
  335. }
  336. /**
  337. * 推送桌面共享
  338. */
  339. export function publishShareStream() {
  340. rtc.publishShareStream({
  341. success: stream => {
  342. console.log('推送桌面共享成功', stream);
  343. },
  344. fail: str => {
  345. console.log(str);
  346. }
  347. });
  348. }
  349. /**
  350. * 关闭桌面共享
  351. */
  352. export function unPubShareStream() {
  353. rtc.unPubShareStream();
  354. }
  355. /**
  356. * 开启、结束、暂停、恢复录制
  357. * @param { String } status: 'start' 开启, 'end' 结束, 'pause' 暂停, 'resume' 恢复
  358. */
  359. export function liveRecord(status) {
  360. rtc.liveRecord({
  361. status: status,
  362. success(data) {
  363. console.log('成功', data);
  364. },
  365. fail(str) {
  366. console.log(str);
  367. }
  368. });
  369. }
  370. // 连麦
  371. /**
  372. * 老师端发起邀请,邀请学生上麦。(举手模式)
  373. * @param {Object} object
  374. */
  375. export function invite(object) {
  376. rtc.invite(object);
  377. }