(1)
html部分
<div class="container">
<video id="localVideo" playsinline autoplay muted></video>
<video id="remoteVideo" playsinline autoplay></video>
<div class="box">
<button @click="start" :disabled="startdisabled">
开始
</button>
<button @click="call" :disabled="calldisabled">
呼叫
</button>
<button class="hangupButton" @click="hangup" :disabled="disabled">
离开
</button>
</div>
<div class="box">
<span>SDP Semantics:</span>
<select id="sdpSemantics">
<option selected value="">Default</option>
<option value="unified-plan"> Plan</option>
<option value="plan-b">Plan B</option>
</select>
</div>
</div>
js部分
export default{
data(){
calldisabled: true,
disabled: true,
startdisabled: false,
startTime: null,
pc1: null,
pc2: null,
localStream: null,
sdpSemanticsSelect: null,
offerOptions: {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1,
},
},
mounted(){
const localVideo = document.getElementById('localVideo')
const remoteVideo = document.getElementById('remoteVideo')
this.sdpSemanticsSelect = document.querySelector('#sdpSemantics')
},
methods:{
getName(pc) {
return pc === this.pc1 ? this.pc1 : this.pc2
},
getOtherPc(pc) {
return pc === this.pc1 ? this.pc2 : this.pc1
},
async start(){
this.startdisabled = true //开始按钮禁用
try{
const stream = await navigator.mediaDevices.getUserMedia({audio: true,video: true,})
//点击开始打开摄像头和音频
this.calldisabled = false //呼叫按钮取消禁用
localVideo.srcObject = stream //视频流给到第一个视频对象
this.localStream = stream
}catch (e) {
alert(`getUserMedia() error: ${e.name}`)
}
},
getSelectedSdpSemantics() {
const option = this.sdpSemanticsSelect.options[
this.sdpSemanticsSelect.selectedIndex
]
return option
},
async call(){
this.calldisabled = true
this.disabled = false
//开始按钮禁用 离开按钮取消禁用
this.startTime = window.performance.now()//获取精确的时间
//localStream 视频轨道的序列
const videoTracks = this.localStream.getVideoTracks()
//音频轨道的对象序列
const audioTracks = this.localStream.getAudioTracks()
const configuration = this.getSelectedSdpSemantics()
//建立数据通信
this.pc1 = new RTCPeerConnection(configuration)
this.pc1.addEventListener('icecandidate', (e) =>
this.onIceCandidate(this.pc1, e)
)
//IceCandidate是一个模板类,里面主要包含着会话描述协议。
this.pc2 = new RTCPeerConnection(configuration)
this.pc2.addEventListener('icecandidate', (e) =>
this.onIceCandidate(this.pc2, e)
)
//iceConnectionState 是一个只读属性,用于描述连接的ICE连接状态。
this.pc1.addEventListener('iceconnectionstatechange', (e) =>
this.onIceStateChange(this.pc1, e)
)
this.pc2.addEventListener('iceconnectionstatechange', (e) =>
this.onIceStateChange(this.pc2, e)
)
//track视频轨道
this.pc2.addEventListener('track', (e) => this.gotRemoteStream(e))
try {
let offerOptions = this.offerOptions
const offer = await this.pc1.createOffer(offerOptions)
await this.onCreateOfferSuccess(offer)
} catch (e) {
console.log(`Failed to set session description: ${e.toString()}`)
}
},
async onCreateOfferSuccess(desc) {
try {
await this.pc1.setLocalDescription(desc) //本地描述
console.log(`${this.getName(this.pc1)} setLocalDescription complete`)
} catch (e) {
console.log(`Failed to set session description: ${e.toString()}`)
}
try {
await this.pc2.setRemoteDescription(desc) //远程描述
console.log(`${this.getName(this.pc2)} setLocalDescription complete`)
} catch (e) {
console.log(`Failed to set session description: ${e.toString()}`)
}
try {
const answer = await this.pc2.createAnswer()
await this.onCreateAnswerSuccess(answer)
} catch (e) {
console.log(`Failed to set session description: ${e.toString()}`)
}
},
async onCreateAnswerSuccess(desc) {
try {
await this.pc2.setLocalDescription(desc)
console.log(`${this.getName(this.pc2)} setLocalDescription complete`)
} catch (e) {
console.log(`Failed to set session description: ${e.toString()}`)
}
try {
await this.pc1.setRemoteDescription(desc)
console.log(`${this.getName(pc1)} setRemoteDescription complete`)
} catch (e) {
console.log(`Failed to set session description: ${e.toString()}`)
}
},
async onIceCandidate(pc, event) {
try {
await this.getOtherPc(pc).addIceCandidate(event.candidate)
console.log(`${this.getName(pc)} addIceCandidate success`)
} catch (e) {
console.log(`${this.getName(pc)} failed to add ICE Candidate: ${e.toString()}`)
}
console.log(`${this.getName(pc)} ICE candidate:\n${
event.candidate ? event.candidate.candidate : '(null)'}`)
},
hangup() {
this.pc1.close()
this.pc2.close()
this.pc1 = null
this.pc2 = null
this.calldisabled = true
this.disabled = false
},
}
}
效果大概就是这样,一边呼叫另一端加入视频聊天, 后台还在写。。。
