|
@@ -1,7 +1,7 @@
|
|
|
<template>
|
|
|
- <div class="microPhone">
|
|
|
- <div class="mic" @mousedown="mouseDownHandle" @mouseup="mouseUpHandle">
|
|
|
- {{isRecording?'录音中...':'开始录音'}}
|
|
|
+ <div :class="sizeClass" >
|
|
|
+ <div class="mic" @mousedown="mouseDownHandle" @mouseup="mouseUpHandle" :title="isRecording?'录音中...':'开始录音'">
|
|
|
+ <i :class="`el-icon-${isRecording?'microphone':'turn-off-microphone'}`"></i>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -11,231 +11,176 @@ import handle from "@/until/handle";
|
|
|
import {sleep} from "@/until/time";
|
|
|
import ZLMRTCClient from "@/assets/ZLMRTCClient";
|
|
|
import CryptoJS from "crypto-js"
|
|
|
+
|
|
|
export default {
|
|
|
name: "microphone.vue",
|
|
|
props:{
|
|
|
- size: "default",
|
|
|
+ size: {default:"medium"},
|
|
|
deviceId: {required:true},
|
|
|
channelId: {required:true},
|
|
|
- hookProtocol: {default:"https"},
|
|
|
- hookHost: {default:""}
|
|
|
+ httpsHook: {default:""},
|
|
|
+ httpHook: {default:""},
|
|
|
+ pushKey: {default:""},
|
|
|
+ enableDebug: {default: false}
|
|
|
},
|
|
|
data(){
|
|
|
return {
|
|
|
- // 是否已经请求到 audio
|
|
|
- isQueryAllowAudio:false,
|
|
|
+ iconStyle: "",
|
|
|
// 音频流
|
|
|
audioStream: null,
|
|
|
// record
|
|
|
mediaRecord: null,
|
|
|
isRecording: false,
|
|
|
- player: null
|
|
|
+ player: null,
|
|
|
+ mediaStream: null,
|
|
|
+ audioConnected: false,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ sizeClass() {
|
|
|
+ return {
|
|
|
+ 'microPhone':true,
|
|
|
+ 'large': this.size === 'large', 'medium': this.size === 'medium', 'small': this.size === 'small'}
|
|
|
}
|
|
|
},
|
|
|
+ beforeMount() {
|
|
|
+ this.initAudioApplications();
|
|
|
+ },
|
|
|
beforeDestroy() {
|
|
|
if(this.player){
|
|
|
this.player.close();
|
|
|
+ this.player = null;
|
|
|
+ this.mediaStream = null;
|
|
|
}
|
|
|
},
|
|
|
- methods:{
|
|
|
+ methods: {
|
|
|
+ /**
|
|
|
+ * 初始化app应用
|
|
|
+ */
|
|
|
+ initAudioApplications(){
|
|
|
+ this.player = null;
|
|
|
+ this.mediaStream = null;
|
|
|
+ this.audioConnected = false;
|
|
|
+ },
|
|
|
// 录音按钮按下
|
|
|
- mouseDownHandle(){
|
|
|
- this.startRecordAudio();
|
|
|
+ mouseDownHandle() {
|
|
|
+ if (this.audioConnected) {
|
|
|
+ // 取消静音音频轨道
|
|
|
+ if(this.mediaStream){
|
|
|
+ let audioStream = this.mediaStream.getAudioTracks();
|
|
|
+ audioStream.forEach(track=>{
|
|
|
+ track.enabled = false;
|
|
|
+ });
|
|
|
+ }else{
|
|
|
+ this.$message.error("无法获取音频流!");
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ // 未创建webrtc 连接
|
|
|
+ this.startRecordAudio();
|
|
|
+ }
|
|
|
},
|
|
|
// 录音按钮抬起
|
|
|
- mouseUpHandle(){
|
|
|
+ mouseUpHandle() {
|
|
|
this.isRecording = false;
|
|
|
- },
|
|
|
- queryMediaStream(){
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia
|
|
|
- navigator.getUserMedia({audio:true , video:false } ,
|
|
|
- (stream) => {
|
|
|
- resolve(stream);
|
|
|
- }, (error) => {
|
|
|
- reject(error);
|
|
|
+ // 静音音频轨道
|
|
|
+ if (this.audioConnected && this.mediaStream) {
|
|
|
+ let audioStream = this.mediaStream.getAudioTracks();
|
|
|
+ audioStream.forEach(track => {
|
|
|
+ track.enabled = false;
|
|
|
});
|
|
|
- })
|
|
|
+ }
|
|
|
},
|
|
|
- async startRecordAudio(){
|
|
|
- let err,stream,res;
|
|
|
|
|
|
- if(!this.mediaStream){
|
|
|
- [err,stream] = await handle(this.queryMediaStream());
|
|
|
- if(err){
|
|
|
- this.$message.error('获取音频设备失败!');
|
|
|
- // return;
|
|
|
- }
|
|
|
- this.isQueryAllowAudio = true
|
|
|
- let hookProtocol = this.hookProtocol;
|
|
|
- let hookHost = this.hookHost;
|
|
|
- let pushKey = "3e80d1762a324d5b0ff636e0bd16f1e3";
|
|
|
+ async startRecordAudio() {
|
|
|
+ let err, stream, res;
|
|
|
+ if (!this.mediaStream) {
|
|
|
+ let httpHook = this.hookProtocol;
|
|
|
+ let pushKey = this.pushKey;
|
|
|
pushKey = CryptoJS.MD5(pushKey);
|
|
|
- if(!hookProtocol){
|
|
|
- hookProtocol = "http"
|
|
|
- }
|
|
|
- if(!hookHost){
|
|
|
- hookHost = "szgpay.ticp.net:29010"
|
|
|
- }
|
|
|
+ let serverHost = `${hookProtocol}://${hookHost}`
|
|
|
let app = "rtc";
|
|
|
- let stream = `${this.deviceId}`
|
|
|
- let zlmSdpUrl = `/zlmServer/index/api/webrtc?app=${app}&stream=${stream}&type=push&sign=${pushKey}`;
|
|
|
+ let stream = `audio${this.deviceId}`
|
|
|
+ // webrtc 请求推流 url
|
|
|
+ let zlmSdpUrl = `${serverHost}/index/api/webrtc?app=${app}&stream=${stream}&type=push&sign=${pushKey}`;
|
|
|
+ // webrtc 拉流地址
|
|
|
+ let playAudioStreamUrl = `${serverHost}/index/api/webrtc?app=${app}&stream=${stream}&type=play`;
|
|
|
+ console.log(playAudioStreamUrl);
|
|
|
|
|
|
- let ffmpegStreamId = `ff_audio${this.deviceId}`
|
|
|
- let audioStreamUrl = `webrtc://${hookHost}/${app}/${stream}`
|
|
|
- let dstUrl = `rtsp://127.0.0.1:11554/${app}/${ffmpegStreamId}`;
|
|
|
- let playAudioStreamUrl = `https://192.168.1.203:29010/index/api/webrtc?app=${app}&stream=${stream}&type=play`;
|
|
|
- console.log(audioStreamUrl)
|
|
|
- console.log(playAudioStreamUrl)
|
|
|
-
|
|
|
- //secret Y api操作密钥(配置文件配置),如果操作ip是127.0.0.1,则不需要此参数
|
|
|
- // src_url Y FFmpeg拉流地址,支持任意协议或格式(只要FFmpeg支持即可)
|
|
|
- // dst_url Y FFmpeg rtmp推流地址,一般都是推给自己,例如rtmp://127.0.0.1/live/stream_form_ffmpeg
|
|
|
- // timeout_ms Y FFmpeg推流成功超时时间
|
|
|
- // enable_hls Y 是否开启hls录制
|
|
|
- // enable_mp4 Y 是否开启mp4录制
|
|
|
- // ffmpeg_cmd_key
|
|
|
- let addFFmpegSourceData = {
|
|
|
- secret:"035c73f7-bb6b-4889-a715-d9eb2d1925cc",
|
|
|
- src_url: audioStreamUrl,
|
|
|
- dst_url: dstUrl,
|
|
|
- timeout_ms: 15000,
|
|
|
- enable_hls: 0,
|
|
|
- enable_mp4: 0,
|
|
|
- }
|
|
|
- let ffmpegStreamProxyUrl = `/zlmServer/index/api/addFFmpegSource`
|
|
|
- ffmpegStreamProxyUrl += `?secret=${addFFmpegSourceData.secret}`
|
|
|
- // ffmpegStreamProxyUrl += `&src_url=${addFFmpegSourceData.src_url}`
|
|
|
- // ffmpegStreamProxyUrl += `&dst_url=${addFFmpegSourceData.dst_url}`
|
|
|
- // ffmpegStreamProxyUrl += `&timeout_ms=${addFFmpegSourceData.timeout_ms}`
|
|
|
- // ffmpegStreamProxyUrl += `&enable_hls=${addFFmpegSourceData.enable_hls}`
|
|
|
- // ffmpegStreamProxyUrl += `&enable_mp4=${addFFmpegSourceData.enable_mp4}`
|
|
|
let player = new ZLMRTCClient.Endpoint(
|
|
|
{
|
|
|
-
|
|
|
// element: document.getElementById('video'),// video 标签
|
|
|
debug: true,// 是否打印日志
|
|
|
- zlmsdpUrl:zlmSdpUrl,//流地址
|
|
|
- simulcast:false,
|
|
|
- useCamera:false,
|
|
|
- audioEnable:true,
|
|
|
- videoEnable:false,
|
|
|
- recvOnly:false,
|
|
|
- resolution:{w:0,h:0},
|
|
|
- usedatachannel:false,
|
|
|
+ zlmsdpUrl: zlmSdpUrl,//流地址
|
|
|
+ simulcast: false,
|
|
|
+ useCamera: false,
|
|
|
+ audioEnable: true,
|
|
|
+ videoEnable: false,
|
|
|
+ recvOnly: false,
|
|
|
+ resolution: {w: 0, h: 0},
|
|
|
+ usedatachannel: false,
|
|
|
}
|
|
|
);
|
|
|
this.player = player;
|
|
|
- console.log(player);
|
|
|
- player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,async (state)=>
|
|
|
- {// RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
|
|
|
- console.log('当前状态==>',state);
|
|
|
- console.log(ffmpegStreamProxyUrl);
|
|
|
- if(state === 'connected'){
|
|
|
- console.log(player);
|
|
|
- console.log(player._localStream.id);
|
|
|
- // if(player._localStream && player._localStream.id){
|
|
|
- // stream = player._localStream.id;
|
|
|
- // }
|
|
|
+ // console.log(player);
|
|
|
+ player.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, async (state) => {// RTC 状态变化 ,详情参考 https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
|
|
|
+ console.log('当前状态==>', state);
|
|
|
+ if (state === 'connected') {
|
|
|
+ // 等待1秒
|
|
|
await sleep(1000);
|
|
|
- [err,res] = await handle(this.sendBroaderCast(stream));
|
|
|
-
|
|
|
- if(err){
|
|
|
- this.$message.error(err.message);
|
|
|
- console.log("[发送broadCast信息] 失败")
|
|
|
- console.log(err);
|
|
|
- player.close();
|
|
|
+ [err, res] = await handle(this.sendBroaderCast(stream));
|
|
|
+ if (err) {
|
|
|
+ this.audioStartFailed(err,`与设备交互错误信息失败${err.message}`)
|
|
|
+ this.mediaStream = null;
|
|
|
+ return player.close();
|
|
|
}
|
|
|
-
|
|
|
- // 连接成功添加ffmpeg 推流代理
|
|
|
- // [err,res] = await handle(this.$axios({
|
|
|
- // baseURL: "/",
|
|
|
- // method: 'post',
|
|
|
- // url: ffmpegStreamProxyUrl,
|
|
|
- // data:addFFmpegSourceData
|
|
|
- // }));
|
|
|
- // if(err){
|
|
|
- // this.$message.error(err.message);
|
|
|
- // console.log("[创建拉流代理] ")
|
|
|
- // console.log(err);
|
|
|
- // }
|
|
|
- // console.log(res);
|
|
|
-
|
|
|
-
|
|
|
- }else{
|
|
|
-
|
|
|
+ this.audioConnected = true;
|
|
|
+ console.log('创建音视频通道成功');
|
|
|
+ } else if(state === 'close'){
|
|
|
+ this.$message.warning("webrtc连接断开");
|
|
|
+ this.audioConnected = false;
|
|
|
}
|
|
|
});
|
|
|
- player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,function(e)
|
|
|
- {// ICE 协商出错
|
|
|
- console.log('ICE 协商出错')
|
|
|
+ player.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e)=> {
|
|
|
+ this.audioStartFailed(e,"ICE 协商出错");
|
|
|
});
|
|
|
-
|
|
|
- player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,function(e)
|
|
|
- {//获取到了远端流,可以播放
|
|
|
- console.log('播放成功',e.streams)
|
|
|
+ // player.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) {//获取到了远端流,可以播放
|
|
|
+ // console.log('播放成功', e.streams)
|
|
|
+ // });
|
|
|
+ player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e)=> {
|
|
|
+ this.audioStartFailed(e,"offer anwser 交换失败");
|
|
|
});
|
|
|
-
|
|
|
- player.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,function(e)
|
|
|
- {// offer anwser 交换失败
|
|
|
- console.log('offer anwser 交换失败',e)
|
|
|
- stop();
|
|
|
+ player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM, (s)=> {
|
|
|
+ console.log("获取到了本地流");
|
|
|
+ this.mediaStream = s;
|
|
|
});
|
|
|
-
|
|
|
- player.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,function(s)
|
|
|
- {// 获取到了本地流
|
|
|
-
|
|
|
- // document.getElementById('selfVideo').srcObject=s;
|
|
|
- // document.getElementById('selfVideo').muted = true;
|
|
|
- //console.log('offer anwser 交换失败',e)
|
|
|
- });
|
|
|
-
|
|
|
- player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED,function(s)
|
|
|
- {// 获取本地流失败
|
|
|
- console.log('获取本地流失败')
|
|
|
+ player.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, (s)=> {
|
|
|
+ this.audioStartFailed(s,"获取音频信息失败,请检查是否有麦克风");
|
|
|
});
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
}
|
|
|
- if(!this.isQueryAllowAudio){
|
|
|
- return this.$message.error('无法获取音频!');
|
|
|
- }
|
|
|
-
|
|
|
this.isRecording = true;
|
|
|
- // 通知设备开始推流
|
|
|
- // 开始录音并推送音频至 wvp 服务器 || 或者推送至 zlm
|
|
|
- // 暂定wvp开启
|
|
|
- // todo 开始录音,并且每隔500ms发送音频文件给wvp
|
|
|
},
|
|
|
- async stopRecordAudio(){
|
|
|
- // todo 停止录音
|
|
|
+ async stopRecordAudio() {
|
|
|
+ // todo 停止录音
|
|
|
+ },
|
|
|
+ audioStartFailed(e,msg){
|
|
|
+ if(this.enableDebug){
|
|
|
+ console.log(msg);
|
|
|
+ console.log(e);
|
|
|
+ console.log("-------");
|
|
|
+ }
|
|
|
+ this.$message.error(msg);
|
|
|
},
|
|
|
// 通知设备开启推流
|
|
|
- async sendBroaderCast(stream){
|
|
|
+ sendBroaderCast(stream) {
|
|
|
let url = `/api/play/broadcast`
|
|
|
- url+=`?deviceId=${this.deviceId}&channel=${this.channelId}&stream=${stream}`;
|
|
|
- let [err,res] = await handle(this.$axios({
|
|
|
+ url += `?deviceId=${this.deviceId}&stream=${stream}`;
|
|
|
+ return this.$axios({
|
|
|
method: 'get',
|
|
|
url: url
|
|
|
- }));
|
|
|
- this.clickCount = 0;
|
|
|
- if(err){
|
|
|
- console.error(err)
|
|
|
- console.warn(err.message)
|
|
|
- }
|
|
|
- let response = res.data;
|
|
|
-
|
|
|
- console.log(res);
|
|
|
- if(response.code !== 0){
|
|
|
- this.player.close();
|
|
|
- return this.$message.error(response.msg);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
+ });
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
</script>
|
|
|
|
|
@@ -245,4 +190,16 @@ export default {
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
}
|
|
|
+.microPhone .mic{
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.small{width: 25px;height:25px;font-size: 1.8rem;}
|
|
|
+.medium{width: 40px;height:40px;font-size: 2.2rem;}
|
|
|
+.large{width: 60px;height:60px;font-size: 2.6rem;}
|
|
|
+.huge{width: 80px;height:80px;font-size: 2.8rem;}
|
|
|
</style>
|