Explorar o código

feat: implement Cloudflare Stream and WebRTC Stream components

- Add Cloudflare Stream component with configuration and playback features
- Update WebRTC Stream component to improve UI and functionality
- Refactor routing and layout for better navigation consistency
- Remove deprecated elements and enhance user experience with new controls
yb hai 2 semanas
pai
achega
8e9b444b3b

+ 1 - 1
src/assets/styles/index.scss

@@ -171,7 +171,7 @@ ol {
   // padding: var(--content-padding);
   background-color: var(--bg-container);
   border-radius: var(--radius-lg);
-  box-shadow: var(--shadow-sm);
+  // box-shadow: var(--shadow-sm);
 }
 
 // 搜索表单

+ 2 - 2
src/layout/index.vue

@@ -32,12 +32,12 @@
           <template #title>{{ t('用户管理') }}</template>
         </el-menu-item>
 
-        <el-menu-item index="/demo/cc">
+        <el-menu-item index="/cc">
           <el-icon><VideoCamera /></el-icon>
           <template #title>{{ t('Cloudflare Stream') }}</template>
         </el-menu-item>
 
-        <el-menu-item index="/demo/webrtc">
+        <el-menu-item index="/webrtc">
           <el-icon><Connection /></el-icon>
           <template #title>{{ t('WebRTC 流') }}</template>
         </el-menu-item>

+ 3 - 3
src/router/index.ts

@@ -81,13 +81,13 @@ const routes: RouteRecordRaw[] = [
         meta: { title: '用户管理', icon: 'User' }
       },
       {
-        path: 'demo/cc',
+        path: 'cc',
         name: 'CloudflareStream',
-        component: () => import('@/views/demo/cloudflare-stream.vue'),
+        component: () => import('@/views/demo/cloudflareStream.vue'),
         meta: { title: 'Cloudflare Stream', icon: 'VideoCamera' }
       },
       {
-        path: 'demo/webrtc',
+        path: 'webrtc',
         name: 'WebrtcStream',
         component: () => import('@/views/demo/webrtc-stream.vue'),
         meta: { title: 'WebRTC 流', icon: 'Connection' }

+ 15 - 34
src/views/demo/cloudflare-stream.vue → src/views/demo/cloudflareStream.vue

@@ -17,12 +17,6 @@
             style="width: 400px"
           />
         </el-form-item>
-        <el-form-item label="播放方式">
-          <el-select v-model="cfConfig.playMode" style="width: 200px">
-            <el-option label="iframe 嵌入" value="iframe" />
-            <el-option label="HLS 播放" value="hls" />
-          </el-select>
-        </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="playCloudflare">播放</el-button>
           <el-button @click="generateCfUrl">生成地址</el-button>
@@ -95,7 +89,12 @@
           <div class="ptz-btn" @mousedown="handlePTZ('DOWN')" @mouseup="handlePTZStop" @mouseleave="handlePTZStop">
             <el-icon><Bottom /></el-icon>
           </div>
-          <div class="ptz-btn" @mousedown="handlePTZ('DOWN_RIGHT')" @mouseup="handlePTZStop" @mouseleave="handlePTZStop">
+          <div
+            class="ptz-btn"
+            @mousedown="handlePTZ('DOWN_RIGHT')"
+            @mouseup="handlePTZStop"
+            @mouseleave="handlePTZStop"
+          >
             <el-icon><BottomRight /></el-icon>
           </div>
         </div>
@@ -105,13 +104,7 @@
           <div class="control-label">
             <span>速度: {{ ptzSpeed }}</span>
           </div>
-          <el-slider
-            v-model="ptzSpeed"
-            :min="10"
-            :max="100"
-            :step="10"
-            :show-tooltip="true"
-          />
+          <el-slider v-model="ptzSpeed" :min="10" :max="100" :step="10" :show-tooltip="true" />
         </div>
 
         <!-- 缩放控制 -->
@@ -152,7 +145,7 @@
     </div>
 
     <!-- 当前状态 -->
-    <div class="status-section">
+    <!-- <div class="status-section">
       <el-descriptions title="当前状态" :column="3" border>
         <el-descriptions-item label="播放器类型">{{ currentPlayerType }}</el-descriptions-item>
         <el-descriptions-item label="iframe 模式">{{ useIframe ? '是' : '否' }}</el-descriptions-item>
@@ -161,7 +154,7 @@
           <el-text truncated style="max-width: 800px">{{ currentSrc || '-' }}</el-text>
         </el-descriptions-item>
       </el-descriptions>
-    </div>
+    </div> -->
 
     <!-- 日志区域 -->
     <div class="log-section">
@@ -205,8 +198,7 @@ const playerRef = ref<InstanceType<typeof VideoPlayer>>()
 // Cloudflare Stream 配置
 const cfConfig = reactive({
   videoId: '3c1ae1949e76f200feef94b8f7d093ca',
-  customerDomain: 'customer-pj89kn2ke2tcuh19.cloudflarestream.com',
-  playMode: 'iframe' as 'iframe' | 'hls'
+  customerDomain: 'customer-pj89kn2ke2tcuh19.cloudflarestream.com'
 })
 const cfGeneratedUrl = ref('')
 
@@ -257,18 +249,11 @@ function playCloudflare() {
   }
 
   currentVideoId.value = cfConfig.videoId
-  useIframe.value = cfConfig.playMode === 'iframe'
-
-  if (cfConfig.playMode === 'hls') {
-    const domain = cfConfig.customerDomain || 'customer-xxx.cloudflarestream.com'
-    currentSrc.value = `https://${domain}/${cfConfig.videoId}/manifest/video.m3u8`
-    currentPlayerType.value = 'hls'
-  } else {
-    currentSrc.value = ''
-    currentPlayerType.value = 'cloudflare'
-  }
+  useIframe.value = true
+  currentSrc.value = ''
+  currentPlayerType.value = 'cloudflare'
 
-  addLog(`播放 Cloudflare Stream: ${cfConfig.videoId} (${cfConfig.playMode})`, 'success')
+  addLog(`播放 Cloudflare Stream: ${cfConfig.videoId} (iframe)`, 'success')
 }
 
 // 生成 Cloudflare URL
@@ -278,11 +263,7 @@ function generateCfUrl() {
     return
   }
   const domain = cfConfig.customerDomain || 'customer-xxx.cloudflarestream.com'
-  if (cfConfig.playMode === 'iframe') {
-    cfGeneratedUrl.value = `https://${domain}/${cfConfig.videoId}/iframe`
-  } else {
-    cfGeneratedUrl.value = `https://${domain}/${cfConfig.videoId}/manifest/video.m3u8`
-  }
+  cfGeneratedUrl.value = `https://${domain}/${cfConfig.videoId}/iframe`
 }
 
 // 复制 URL

+ 14 - 62
src/views/demo/webrtc-stream.vue

@@ -1,15 +1,15 @@
 <template>
   <div class="page-container">
     <div class="page-header">
-      <span class="title">WebRTC 低延迟播放</span>
-      <el-tag type="success" size="small" style="margin-left: 10px">延迟 &lt; 2s</el-tag>
+      <span class="title">WebRTC 播放 (暂时使用go2rtc)</span>
+      <!-- <el-tag type="success" size="small" style="margin-left: 10px">延迟 &lt; 2s</el-tag> -->
     </div>
 
     <!-- 配置区域 -->
     <div class="config-section">
       <el-form label-width="120px">
-        <el-form-item label="go2rtc 地址">
-          <el-input v-model="config.go2rtcUrl" placeholder="go2rtc 服务地址" style="width: 400px">
+        <el-form-item label="本地RTC地址">
+          <el-input v-model="config.go2rtcUrl" placeholder="服务地址" style="width: 400px">
             <template #prepend>http://</template>
           </el-input>
           <el-text type="info" style="margin-left: 10px">默认端口 1984</el-text>
@@ -17,13 +17,17 @@
         <el-form-item label="流名称">
           <el-input v-model="config.streamName" placeholder="摄像头流名称,如 camera1" style="width: 300px" />
         </el-form-item>
-        <el-form-item label="生成的 URL">
+        <!-- http://localhost:1984/api/webrtc?src=camera1 -->
+        <el-form-item label="完整URL">
+          <el-text>{{ fullGo2rtcUrl }}/api/webrtc?src={{ config.streamName }}</el-text>
+        </el-form-item>
+        <!-- <el-form-item label="生成的 URL">
           <el-input :value="generatedUrl" readonly style="width: 600px">
             <template #append>
               <el-button @click="copyUrl">复制</el-button>
             </template>
           </el-input>
-        </el-form-item>
+        </el-form-item> -->
         <el-form-item>
           <el-button type="primary" @click="startPlay">播放</el-button>
           <el-button @click="handleReconnect">重连</el-button>
@@ -102,13 +106,7 @@
           <div class="control-label">
             <span>速度: {{ ptzSpeed }}</span>
           </div>
-          <el-slider
-            v-model="ptzSpeed"
-            :min="10"
-            :max="100"
-            :step="10"
-            :show-tooltip="true"
-          />
+          <el-slider v-model="ptzSpeed" :min="10" :max="100" :step="10" :show-tooltip="true" />
         </div>
 
         <!-- 缩放控制 -->
@@ -147,55 +145,6 @@
       </el-space>
     </div>
 
-    <!-- 当前状态 -->
-    <div class="status-section">
-      <el-descriptions title="当前状态" :column="3" border>
-        <el-descriptions-item label="连接状态">
-          <el-tag :type="statusTagType">{{ statusText }}</el-tag>
-        </el-descriptions-item>
-        <el-descriptions-item label="go2rtc 地址">{{ fullGo2rtcUrl || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="流名称">{{ config.streamName || '-' }}</el-descriptions-item>
-      </el-descriptions>
-    </div>
-
-    <!-- 说明区域 -->
-    <div class="info-section">
-      <el-collapse>
-        <el-collapse-item title="go2rtc 配置说明" name="1">
-          <div class="info-content">
-            <h4>1. 下载 go2rtc</h4>
-            <p>
-              访问
-              <el-link type="primary" href="https://github.com/AlexxIT/go2rtc/releases" target="_blank">
-                GitHub Releases
-              </el-link>
-              下载对应系统版本
-            </p>
-
-            <h4>2. 创建配置文件 go2rtc.yaml</h4>
-            <pre class="code-block">
-streams:
-  camera1: rtsp://admin:password@192.168.0.64:554/Streaming/Channels/101
-
-webrtc:
-  candidates:
-    - stun:stun.l.google.com:19302</pre
-            >
-
-            <h4>3. 启动服务</h4>
-            <pre class="code-block">./go2rtc -config go2rtc.yaml</pre>
-
-            <h4>4. 验证</h4>
-            <p>
-              访问
-              <el-link type="primary" href="http://localhost:1984" target="_blank">http://localhost:1984</el-link>
-              查看管理界面
-            </p>
-          </div>
-        </el-collapse-item>
-      </el-collapse>
-    </div>
-
     <!-- 日志区域 -->
     <div class="log-section">
       <div class="log-header">
@@ -489,12 +438,15 @@ async function handleZoomRelease() {
 }
 
 .config-section {
+  padding: 20px;
   margin-bottom: 20px;
   background-color: var(--bg-container);
   border-radius: var(--radius-base);
 }
 
 .player-ptz-container {
+  padding: 20px;
+  background-color: var(--bg-container);
   display: flex;
   gap: 20px;
   margin-bottom: 20px;