|
@@ -0,0 +1,140 @@
|
|
|
|
|
+<!-- rtc流组件 -->
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="webRTC">
|
|
|
|
|
+ <video
|
|
|
|
|
+ ref="videoElement"
|
|
|
|
|
+ autoplay
|
|
|
|
|
+ muted
|
|
|
|
|
+ playsinline
|
|
|
|
|
+ style="width:470px;height:290px;background-color: #000; display: block;"
|
|
|
|
|
+ ></video>
|
|
|
|
|
+ <p class="right-text">AI</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+<script>
|
|
|
|
|
+ const OFFER_URL = 'https://192.168.166.11/offer'
|
|
|
|
|
+ const RETRY_INTERVAL = 5000 // 失败后 5 秒重连
|
|
|
|
|
+ export default {
|
|
|
|
|
+ name: 'webRTC',
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ pc: null,
|
|
|
|
|
+ retryTimer: null
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ mounted() {
|
|
|
|
|
+ console.log('screenType',this.screenType);
|
|
|
|
|
+ this.connect()
|
|
|
|
|
+ },
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+ this._clearRetry()
|
|
|
|
|
+ this._cleanup()
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ async connect() {
|
|
|
|
|
+ this._cleanup()
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ this.pc = new RTCPeerConnection({
|
|
|
|
|
+ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
|
|
|
|
|
+ iceTransportPolicy: 'all'
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ this.pc.ontrack = (event) => {
|
|
|
|
|
+ this.$refs.videoElement.srcObject = event.streams[0]
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.pc.onconnectionstatechange = () => {
|
|
|
|
|
+ const state = this.pc && this.pc.connectionState
|
|
|
|
|
+ if (state === 'failed' || state === 'disconnected' || state === 'closed') {
|
|
|
|
|
+ this._scheduleRetry()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.pc.addTransceiver('video', { direction: 'recvonly' })
|
|
|
|
|
+ this.pc.addTransceiver('audio', { direction: 'recvonly' })
|
|
|
|
|
+
|
|
|
|
|
+ const offer = await this.pc.createOffer()
|
|
|
|
|
+ await this.pc.setLocalDescription(offer)
|
|
|
|
|
+ await this._waitIceGathering()
|
|
|
|
|
+
|
|
|
|
|
+ const response = await fetch(OFFER_URL, {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
|
|
|
+ body: JSON.stringify({
|
|
|
|
|
+ sdp: this.pc.localDescription.sdp,
|
|
|
|
|
+ type: this.pc.localDescription.type
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
|
|
|
|
+
|
|
|
|
|
+ const answer = await response.json()
|
|
|
|
|
+ await this.pc.setRemoteDescription(new RTCSessionDescription(answer))
|
|
|
|
|
+
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('[WebRTC] 连接失败,准备重连:', e)
|
|
|
|
|
+ this._scheduleRetry()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ _scheduleRetry() {
|
|
|
|
|
+ this._cleanup()
|
|
|
|
|
+ this._clearRetry()
|
|
|
|
|
+ this.retryTimer = setTimeout(() => this.connect(), RETRY_INTERVAL)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ _clearRetry() {
|
|
|
|
|
+ if (this.retryTimer) {
|
|
|
|
|
+ clearTimeout(this.retryTimer)
|
|
|
|
|
+ this.retryTimer = null
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ _cleanup() {
|
|
|
|
|
+ if (this.pc) {
|
|
|
|
|
+ this.pc.ontrack = null
|
|
|
|
|
+ this.pc.onconnectionstatechange = null
|
|
|
|
|
+ this.pc.close()
|
|
|
|
|
+ this.pc = null
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.$refs.videoElement) {
|
|
|
|
|
+ this.$refs.videoElement.srcObject = null
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ _waitIceGathering() {
|
|
|
|
|
+ return new Promise((resolve) => {
|
|
|
|
|
+ if (this.pc.iceGatheringState === 'complete') return resolve()
|
|
|
|
|
+ const check = () => {
|
|
|
|
|
+ if (this.pc && this.pc.iceGatheringState === 'complete') {
|
|
|
|
|
+ this.pc.removeEventListener('icegatheringstatechange', check)
|
|
|
|
|
+ resolve()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ this.pc.addEventListener('icegatheringstatechange', check)
|
|
|
|
|
+ setTimeout(resolve, 5000)
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+</script>
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+ .webRTC {
|
|
|
|
|
+ width:470px;
|
|
|
|
|
+ height:290px;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ .right-text{
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top:5px;
|
|
|
|
|
+ right:5px;
|
|
|
|
|
+ background: rgba(255, 240, 200, 0.9);
|
|
|
|
|
+ color: #d48806;
|
|
|
|
|
+ border:1px solid #ffd591;
|
|
|
|
|
+ padding:0 5px;
|
|
|
|
|
+ border-radius:4px;
|
|
|
|
|
+ font-size:12px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|