直播:后台 JWT 推流、前台画中画;WebRTC 服务与 Nginx WebSocket 代理

Made-with: Cursor
This commit is contained in:
whm
2026-03-25 15:00:14 +08:00
parent b83ec91b1a
commit 7811adca66
1050 changed files with 146524 additions and 37 deletions

View File

@@ -0,0 +1,99 @@
<template>
<div class="live-broadcast">
<el-card>
<template #header>
<span>官网视频直播WebRTC</span>
</template>
<p class="tip">
仅后台可开播推流后官网首页左上角以<strong>画中画</strong>自动展示用户也可打开官网直播全屏页观看需站点使用
<strong>HTTPS</strong>公网复杂网络请在服务端配置 <code>LIVE_ICE_SERVERS</code> TURN
</p>
<p class="status">{{ status }}</p>
<div class="actions">
<el-button v-if="!session" type="primary" :disabled="!token" @click="start">开始直播</el-button>
<el-button v-else type="danger" @click="stop">结束直播</el-button>
</div>
<video ref="previewRef" class="preview" playsinline muted autoplay></video>
</el-card>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useAuthStore } from '../../stores/auth'
import { startPublishing } from '../../utils/liveWebRTC'
const authStore = useAuthStore()
const token = computed(() => authStore.getToken() || '')
const previewRef = ref(null)
const status = ref('就绪')
const session = ref(null)
function start() {
if (!token.value) {
status.value = '请先登录'
return
}
status.value = '正在连接…'
const { stop } = startPublishing({
token: token.value,
onStatus: (s) => {
status.value = s
},
onLocalStream: (stream) => {
if (previewRef.value) previewRef.value.srcObject = stream
}
})
session.value = { stop }
}
function stop() {
session.value?.stop()
session.value = null
if (previewRef.value) previewRef.value.srcObject = null
status.value = '已停止'
}
onMounted(() => {
document.title = '视频直播开播 - 管理后台'
})
onUnmounted(() => {
stop()
})
</script>
<style scoped>
.live-broadcast {
max-width: 720px;
}
.tip {
font-size: 13px;
line-height: 1.7;
color: #606266;
margin-bottom: 16px;
}
.tip code {
font-size: 12px;
background: #f4f4f5;
padding: 2px 6px;
border-radius: 4px;
}
.status {
color: #409eff;
margin-bottom: 12px;
min-height: 1.5em;
}
.actions {
margin-bottom: 16px;
}
.preview {
width: 100%;
max-width: 480px;
border-radius: 8px;
background: #000;
aspect-ratio: 4 / 3;
object-fit: cover;
display: block;
}
</style>