microphone.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <div :class="sizeClass" >
  3. <div class="mic" @mousedown="mouseDownHandle" @mouseup="mouseUpHandle" :title="isRecording?'录音中...':'开始录音'">
  4. <i :class="`el-icon-${isRecording?'microphone':'turn-off-microphone'}`"></i>
  5. </div>
  6. </div>
  7. </template>
  8. <script>
  9. import handle from "@/until/handle";
  10. import {sleep} from "@/until/time";
  11. import ZLMRTCClient from "@/assets/ZLMRTCClient";
  12. import CryptoJS from "crypto-js"
  13. export default {
  14. name: "microphone.vue",
  15. props:{
  16. size: {default:"medium"},
  17. deviceId: {required:true},
  18. channelId: {required:true},
  19. httpsHook: {default:""},
  20. httpHook: {default:""},
  21. pushKey: {default:""},
  22. enableDebug: {default: false}
  23. },
  24. data(){
  25. return {
  26. iconStyle: "",
  27. // 音频流
  28. audioStream: null,
  29. // record
  30. mediaRecord: null,
  31. isRecording: false,
  32. player: null,
  33. mediaStream: null,
  34. audioConnected: false,
  35. }
  36. },
  37. computed: {
  38. sizeClass() {
  39. return {
  40. 'microPhone':true,
  41. 'large': this.size === 'large', 'medium': this.size === 'medium', 'small': this.size === 'small'}
  42. }
  43. },
  44. beforeMount() {
  45. this.initAudioApplications();
  46. },
  47. beforeDestroy() {
  48. if(this.player){
  49. this.player.close();
  50. this.player = null;
  51. this.mediaStream = null;
  52. }
  53. },
  54. methods: {
  55. /**
  56. * 初始化app应用
  57. */
  58. initAudioApplications(){
  59. this.player = null;
  60. this.mediaStream = null;
  61. this.audioConnected = false;
  62. },
  63. // 录音按钮按下
  64. mouseDownHandle() {
  65. if (this.audioConnected) {
  66. // 取消静音音频轨道
  67. if(this.mediaStream){
  68. let audioStream = this.mediaStream.getAudioTracks();
  69. audioStream.forEach(track=>{
  70. track.enabled = false;
  71. });
  72. }else{
  73. this.$message.error("无法获取音频流!");
  74. }
  75. }else{
  76. // 未创建webrtc 连接
  77. this.startRecordAudio();
  78. }
  79. },
  80. // 录音按钮抬起
  81. mouseUpHandle() {
  82. this.isRecording = false;
  83. // 静音音频轨道
  84. if (this.audioConnected && this.mediaStream) {
  85. let audioStream = this.mediaStream.getAudioTracks();
  86. audioStream.forEach(track => {
  87. track.enabled = false;
  88. });
  89. }
  90. },
  91. async startRecordAudio() {
  92. let err, stream, res;
  93. if (!this.mediaStream) {
  94. let httpsHook = this.httpsHook;
  95. let pushKey = this.pushKey;
  96. pushKey = CryptoJS.MD5(pushKey);
  97. let serverHost = `${httpsHook}`
  98. let app = "rtc";
  99. let stream = `audio${this.deviceId}`
  100. // webrtc 请求推流 url
  101. let zlmSdpUrl = `${serverHost}/index/api/webrtc?app=${app}&stream=${stream}&type=push&sign=${pushKey}`;
  102. // webrtc 拉流地址
  103. let playAudioStreamUrl = `${serverHost}/index/api/webrtc?app=${app}&stream=${stream}&type=play`;
  104. console.log(playAudioStreamUrl);
  105. let player = new ZLMRTCClient.Endpoint(
  106. {
  107. // element: document.getElementById('video'),// video 标签
  108. debug: true,// 是否打印日志
  109. zlmsdpUrl: zlmSdpUrl,//流地址
  110. simulcast: false,
  111. useCamera: false,
  112. audioEnable: true,
  113. videoEnable: false,
  114. recvOnly: false,
  115. resolution: {w: 0, h: 0},
  116. usedatachannel: false,
  117. }
  118. );
  119. this.player = player;
  120. // console.log(player);
  121. player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, async (state) => {// RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
  122. console.log('当前状态==>', state);
  123. if (state === 'connected') {
  124. // 等待1秒
  125. await sleep(1000);
  126. [err, res] = await handle(this.sendBroaderCast(stream));
  127. if (err) {
  128. this.audioStartFailed(err,`与设备交互错误信息失败${err.message}`)
  129. this.mediaStream = null;
  130. return player.close();
  131. }
  132. this.audioConnected = true;
  133. console.log('创建音视频通道成功');
  134. } else if(state === 'close'){
  135. this.$message.warning("webrtc连接断开");
  136. this.audioConnected = false;
  137. }
  138. });
  139. player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e)=> {
  140. this.audioStartFailed(e,"ICE 协商出错");
  141. });
  142. // player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) {//获取到了远端流,可以播放
  143. // console.log('播放成功', e.streams)
  144. // });
  145. player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e)=> {
  146. this.audioStartFailed(e,"offer anwser 交换失败");
  147. });
  148. player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s)=> {
  149. console.log("获取到了本地流");
  150. this.mediaStream = s;
  151. });
  152. player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, (s)=> {
  153. this.audioStartFailed(s,"获取音频信息失败,请检查是否有麦克风");
  154. });
  155. }
  156. this.isRecording = true;
  157. },
  158. async stopRecordAudio() {
  159. // todo 停止录音
  160. },
  161. audioStartFailed(e,msg){
  162. if(this.enableDebug){
  163. console.log(msg);
  164. console.log(e);
  165. console.log("-------");
  166. }
  167. this.$message.error(msg);
  168. },
  169. // 通知设备开启推流
  170. sendBroaderCast(stream) {
  171. let url = `/api/play/broadcast`
  172. url += `?deviceId=${this.deviceId}&stream=${stream}`;
  173. return this.$axios({
  174. method: 'get',
  175. url: url
  176. });
  177. }
  178. }
  179. }
  180. </script>
  181. <style scoped>
  182. .microPhone{
  183. display: flex;
  184. justify-content: center;
  185. align-items: center;
  186. }
  187. .microPhone .mic{
  188. width: 100%;
  189. height: 100%;
  190. display: flex;
  191. justify-content: center;
  192. align-items: center;
  193. }
  194. .small{width: 25px;height:25px;font-size: 1.8rem;}
  195. .medium{width: 40px;height:40px;font-size: 2.2rem;}
  196. .large{width: 60px;height:60px;font-size: 2.6rem;}
  197. .huge{width: 80px;height:80px;font-size: 2.8rem;}
  198. </style>