Bladeren bron

feat: rts

lvkun 3 jaren geleden
bovenliggende
commit
e16b5cb704

+ 14 - 6
src/api/rts/stream.ts

@@ -29,7 +29,7 @@ export const closeStream = (streamPath: string) => {
 }
 
 export const getPullList = () => {
-  return request<RTS.STREAM.Detail[]>({
+  return request<RTS.PullStream.Detail[]>({
     url: '/api/list/pull',
     method: 'GET'
   })
@@ -42,7 +42,7 @@ export const stopPull = (url: string) => {
   })
 }
 
-export const createRtspPull = (params: {streamPath: string, target: string, save: number}) => {
+export const createPull = (params: {streamPath: string, target: string, save: number}) => {
   return request<RTS.STREAM.Detail[]>({
     url: '/rtsp/api/pull',
     method: 'GET',
@@ -59,7 +59,7 @@ export const createRtmpPull = (params: {streamPath: string, target: string, save
 }
 
 export const getPushList = () => {
-  return request<RTS.STREAM.Detail[]>({
+  return request<RTS.PullStream.Detail[]>({
     url: '/api/list/push',
     method: 'GET'
   })
@@ -72,8 +72,8 @@ export const stopPush = (url: string) => {
   })
 }
 
-export const createRtspPush = (params: {streamPath: string, target: string, save: number}) => {
-  return request<RTS.STREAM.Detail[]>({
+export const createPush = (params: {streamPath: string, target: string, save: number}) => {
+  return request<RTS.PullStream.Detail[]>({
     url: '/rtsp/api/push',
     method: 'GET',
     params
@@ -131,12 +131,20 @@ export const playRecord = (streamPath: 'flv'| 'mp4' | 'm3u8' | 'h264'| 'h265') =
 
 /** 获取配置 RTSP */
 export const getConfig = (name: 'RTSP' | 'RTMP' | 'HLS' | 'HDL' | 'GB28181') => {
-  return request<RTS.STREAM.Detail[]>({
+  return request<any>({
     url: `/api/getconfig?name=${name}`,
     method: 'GET'
   })
 }
 
+export const updateConfig = (name: 'RTSP' | 'RTMP' | 'HLS' | 'HDL' | 'GB28181', data: string) => {
+  return request<string>({
+    url: `/api/getconfig?name=${name}`,
+    method: 'POST',
+    data
+  })
+}
+
 /** 系统监控 */
 export const getSummary = () => {
   return request<RTS.STREAM.Detail[]>({

+ 11 - 3
src/components/CodeMirror/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue'
+import { defineComponent, nextTick, onMounted, reactive, ref, defineExpose } from 'vue'
 
 import { basicSetup } from 'codemirror'
 import { EditorView } from '@codemirror/view'
@@ -25,6 +25,8 @@ export const CodeMirrorTsx = defineComponent({
 
     const editorRef = ref()
 
+    const view = ref<EditorView>()
+
     const transDoc = () => {
       console.log(' props.bodyType:', props.bodyType)
 
@@ -36,11 +38,17 @@ export const CodeMirrorTsx = defineComponent({
         doc: transDoc(),
         extensions: [basicSetup, javascript()]
       })
-
-      const view = new EditorView({
+      // transDoc(),
+      view.value = new EditorView({
         state: editorState,
         parent: document.getElementById(`editorRef-${id}`)!
       })
+
+      setTimeout(() => {
+        console.dir(document.getElementById(`editorRef-${id}`))
+
+        // console.log(document.getElementById(`editorRef-${id}`)!.getValue())
+      }, 10000)
     }
 
     onMounted(() => {

+ 53 - 1
src/controller/rts/rts.ts

@@ -1,4 +1,5 @@
-import { getStreams } from '@/api/rts/stream'
+import { closeStream, createPull, createPush, getConfig, getPullList, getPushList, getRecording, getRecordList, getStreams, stopPull, stopPush, updateConfig } from '@/api/rts/stream'
+import { message } from 'ant-design-vue'
 
 export class RtsController {
   static StateMap = new Map([
@@ -8,7 +9,58 @@ export class RtsController {
     [3, { name: '已经关闭', key: 3 }]
   ])
 
+  static protocolList = ['RTSP', 'RTMP', 'HLS', 'HDL', 'GB28181']
+
   static async getStreams () {
     return getStreams()
   }
+
+  static async cloeStreams (streamPath: string) {
+    await closeStream(streamPath)
+    message.success('关闭成功')
+  }
+
+  static async listPullList () {
+    return await getPullList()
+  }
+
+  static async stopPull (remoteurl: string) {
+    await stopPull(remoteurl)
+    message.success('停止拉流成功')
+  }
+
+  static async createPull (params: {streamPath: string, target: string, save: number}) {
+    await createPull(params)
+    message.success('新增成功')
+  }
+
+  static async listPush () {
+    return await getPushList()
+  }
+
+  static async stopPush (remoteurl: string) {
+    await stopPush(remoteurl)
+  }
+
+  static async createPush (params: {streamPath: string, target: string, save: number}) {
+    await createPush(params)
+    message.success('新增成功')
+  }
+
+  static async listRecord (type: 'mp4' | 'ts' | 'flv') {
+    return await getRecordList(type)
+  }
+
+  static async listRecording () {
+    return await getRecording()
+  }
+
+  static async protocol (name: 'RTSP' | 'RTMP' | 'HLS' | 'HDL' | 'GB28181') {
+    return await getConfig(name)
+  }
+
+  static async updateProtocol (name: 'RTSP' | 'RTMP' | 'HLS' | 'HDL' | 'GB28181', data: string) {
+    await updateConfig(name, data)
+    message.success('修改协议成功')
+  }
 }

+ 71 - 2
src/pages/rts/protocol/index.vue

@@ -1,5 +1,74 @@
-<template><h1/></template>
+<template>
+  <a-spin :spinning="state.loading" >
+  <a-card
+    style="min-height: 600px;"
+  >
+    <a-row justify="space-between" style="margin-bottom: 20px;" >
+      <a-col span="12" >
+        <a-select placeholder="请选择协议" v-model:value="state.name" style="width: 170px;" >
+          <a-select-option
+            v-for="item in protocolList"
+            :key="item"
+          >
+            {{item}}
+          </a-select-option>
+        </a-select>
+      </a-col>
+      <a-col  >
+        <a-button type="primary" @click="saveProtocol"> 保存 </a-button>
+      </a-col>
+    </a-row>
+    <code-mirror-tsx
+      :key="state.name"
+      v-if="state.bodyJson"
+      :body-json="state.bodyJson"
+      body-type="json"
+    />
+    <a-empty  style="margin-top: 200px;" v-else :image="Empty.PRESENTED_IMAGE_SIMPLE" description="请在左上角选择协议"/>
+
+  </a-card>
+</a-spin>
+</template>
 <script lang="ts" setup >
+import { RtsController } from '@/controller/rts'
+import { reactive, ref, watch } from 'vue'
+import { CodeMirrorTsx } from '@/components/CodeMirror/index'
+import { useScheduler } from '@/hooks'
+import { Empty } from 'ant-design-vue'
+
+const protocolList = ['RTSP', 'RTMP', 'HLS', 'HDL', 'GB28181']
+
+const codeMirrorRef = ref()
+
+const state = reactive<{
+  name: RTS.protocol | '',
+  bodyJson: string
+  loading: boolean
+}>({
+  name: '',
+  bodyJson: '',
+  loading: false
+})
+
+watch(
+  () => state.name,
+  () => getProtocol()
+)
+const { start, stop } = useScheduler(() => state.loading = false, 2000)
+
+const saveProtocol = async () => {
+  console.log(codeMirrorRef.value.getValue)
+
+  // await RtsController.updateProtocol(state.name as RTS.protocol, state.bodyJson)
+}
+
+const getProtocol = async () => {
+  state.loading = true
+  const { data } = await RtsController.protocol(state.name as RTS.protocol)
+  start()
+  state.bodyJson = JSON.stringify(data)
+}
 </script>
 
-<style></style>
+<style lang="less" scoped >
+</style>

+ 150 - 2
src/pages/rts/pull/index.vue

@@ -1,5 +1,153 @@
-<template><h1/></template>
+<template>
+  <a-card>
+    <a-row justify="end" >
+      <a-col  >
+        <a-button type="primary" @click="openModal">创建拉流</a-button>
+      </a-col>
+    </a-row>
+    <a-table
+      style="margin-top: 20px;"
+      :columns="columns"
+      :data-source="state.dataSource"
+      :loading="state.loading"
+    >
+      <template #bodyCell="{column, record}" >
+        <template v-if="column.key === 'action'"  >
+          <a-space>
+            <a @click="stopPull(record.remoteurl)" >停止</a>
+          </a-space>
+        </template>
+      </template>
+    </a-table>
+  </a-card>
+
+  <modal-pro
+    label="创建拉流"
+    :visible="state.visible"
+    @cancel="state.visible = false "
+    @ok="ok"
+  >
+    <a-form :label-col="{span: 4}" >
+      <a-form-item label="拉流类型" >
+        <a-select v-model:value="bodyParamsState.target" >
+          <a-select-option
+            v-for="item in pullList"
+            :key="item.key"
+            :value="item.value"
+          >
+            {{item.value}}
+          </a-select-option>
+        </a-select>
+      </a-form-item>
+      <a-form-item label="拉流地址" v-bind='validateInfos.streamPath' >
+        <a-input v-model:value="bodyParamsState.streamPath" > </a-input>
+      </a-form-item>
+    </a-form>
+  </modal-pro>
+</template>
 <script lang="ts" setup >
+import { RtsController } from '@/controller/rts'
+import { onMounted, reactive } from 'vue'
+import { Form } from 'ant-design-vue'
+
+const columns = [
+  {
+    title: 'ID',
+    dataIndex: 'ID'
+  },
+  {
+    title: '拉流类型',
+    dataIndex: 'Type'
+  },
+  {
+    title: '流地址',
+    dataIndex: 'StreamPath'
+  },
+  {
+    title: '被拉流地址',
+    dataIndex: 'RemoteURL'
+  },
+  {
+    title: '重联次数',
+    dataIndex: 'ReConnectCount'
+  },
+  {
+    title: '拉流参数',
+    dataIndex: 'Args'
+  },
+  {
+    title: '开始时间',
+    dataIndex: 'StartTime'
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    key: 'action'
+  }
+]
+
+const useForm = Form.useForm
+
+const pullList = [
+  {
+    key: 'RTSP',
+    value: 'RTSP'
+  },
+  {
+    key: 'RTMP',
+    value: 'RTMP'
+  }
+]
+
+const bodyParamsState = reactive({
+  target: 'RTSP',
+  streamPath: '',
+  save: 2
+})
+
+const state = reactive<{
+  dataSource: RTS.PullStream.Detail[]
+  loading: boolean
+  visible: boolean
+  detail: Partial<RTS.PullStream.Detail>
+}>({
+  loading: false,
+  visible: false,
+  detail: {},
+  dataSource: []
+})
+
+const { resetFields, validate, validateInfos } = useForm(bodyParamsState, reactive({
+  streamPath: [{ required: 'true', message: '请填写流地址' }]
+}))
+
+const openModal = () => {
+  state.visible = true
+}
+
+const ok = () => {
+  validate().then(async () => {
+    await RtsController.createPull(bodyParamsState)
+    state.visible = false
+    getPullList()
+  })
+}
+
+const stopPull = async (remoteurl: string) => {
+  await RtsController.stopPull(remoteurl)
+}
+
+const getPullList = async () => {
+  const { data } = await RtsController.listPullList()
+  state.dataSource = data
+}
+
+onMounted(() => {
+  getPullList()
+})
+
 </script>
 
-<style></style>
+<style lang="less" scoped >
+
+</style>

+ 150 - 2
src/pages/rts/push/index.vue

@@ -1,5 +1,153 @@
-<template><h1/></template>
+<template>
+  <a-card>
+    <a-row justify="end" >
+      <a-col  >
+        <a-button type="primary" @click="openModal">创建推流</a-button>
+      </a-col>
+    </a-row>
+    <a-table
+      style="margin-top: 20px;"
+      :columns="columns"
+      :data-source="state.dataSource"
+      :loading="state.loading"
+    >
+      <template #bodyCell="{column, record}" >
+        <template v-if="column.key === 'action'"  >
+          <a-space>
+            <a @click="stopPush(record.remoteurl)" >停止</a>
+          </a-space>
+        </template>
+      </template>
+    </a-table>
+  </a-card>
+
+  <modal-pro
+    label="创建推流"
+    :visible="state.visible"
+    @cancel="state.visible = false "
+    @ok="ok"
+  >
+    <a-form :label-col="{span: 4}" >
+      <a-form-item label="推流类型" >
+        <a-select v-model:value="bodyParamsState.target" >
+          <a-select-option
+            v-for="item in pullList"
+            :key="item.key"
+            :value="item.value"
+          >
+            {{item.value}}
+          </a-select-option>
+        </a-select>
+      </a-form-item>
+      <a-form-item label="推流地址" v-bind='validateInfos.streamPath' >
+        <a-input v-model:value="bodyParamsState.streamPath" > </a-input>
+      </a-form-item>
+    </a-form>
+  </modal-pro>
+</template>
 <script lang="ts" setup >
+import { RtsController } from '@/controller/rts'
+import { onMounted, reactive } from 'vue'
+import { Form } from 'ant-design-vue'
+
+const columns = [
+  {
+    title: 'ID',
+    dataIndex: 'ID'
+  },
+  {
+    title: '推流类型',
+    dataIndex: 'Type'
+  },
+  {
+    title: '流地址',
+    dataIndex: 'StreamPath'
+  },
+  {
+    title: '流id',
+    dataIndex: 'StreamID'
+  },
+  {
+    title: '重连参数',
+    dataIndex: 'ReConnectCount'
+  },
+  {
+    title: '推流参数',
+    dataIndex: 'Args'
+  },
+  {
+    title: '开始时间',
+    dataIndex: 'StartTime'
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    key: 'action'
+  }
+]
+
+const useForm = Form.useForm
+
+const pullList = [
+  {
+    key: 'RTSP',
+    value: 'RTSP'
+  },
+  {
+    key: 'RTMP',
+    value: 'RTMP'
+  }
+]
+
+const bodyParamsState = reactive({
+  target: 'RTSP',
+  streamPath: '',
+  save: 2
+})
+
+const state = reactive<{
+  dataSource: RTS.PullStream.Detail[]
+  loading: boolean
+  visible: boolean
+  detail: Partial<RTS.PullStream.Detail>
+}>({
+  loading: false,
+  visible: false,
+  detail: {},
+  dataSource: []
+})
+
+const { resetFields, validate, validateInfos } = useForm(bodyParamsState, reactive({
+  streamPath: [{ required: 'true', message: '请填写流地址' }]
+}))
+
+const openModal = () => {
+  state.visible = true
+}
+
+const ok = () => {
+  validate().then(async () => {
+    await RtsController.createPush(bodyParamsState)
+    state.visible = false
+    getPullList()
+  })
+}
+
+const stopPush = async (remoteurl: string) => {
+  await RtsController.stopPull(remoteurl)
+}
+
+const getPullList = async () => {
+  const { data } = await RtsController.listPush()
+  state.dataSource = data
+}
+
+onMounted(() => {
+  getPullList()
+})
+
 </script>
 
-<style></style>
+<style lang="less" scoped >
+
+</style>

+ 109 - 1
src/pages/rts/record/index.vue

@@ -1,5 +1,113 @@
-<template><h1/></template>
+<template>
+
+  <a-card
+    title="存储列表"
+  >
+  <a-table
+      :column="recordColumns"
+      :data-source="state.recordingDataSource"
+      :loading="state.loading"
+    >
+    <template #bodyCell="{column, record}" >
+        <template v-if="column.key === 'action'"  >
+          <a-space>
+            <a @click="openModal(record)" >详情</a>
+          </a-space>
+        </template>
+      </template>
+    </a-table>
+  </a-card>
+  <a-card
+    title="正在存储的视频流"
+    style="margin-top: 20px;"
+  >
+    <a-table
+      :column="columns"
+      :data-source="state.recordingDataSource"
+      :loading="state.loading"
+    >
+    <template #bodyCell="{column, record}" >
+        <template v-if="column.key === 'action'"  >
+          <a-space>
+            <a @click="openModal(record)" >详情</a>
+          </a-space>
+        </template>
+      </template>
+    </a-table>
+  </a-card>
+
+  <modal-pro
+    label="视频流详情"
+    :visible="state.visible"
+    @cancel="state.visible = false"
+    @ok="state.visible = false"
+  >
+
+  </modal-pro>
+</template>
 <script lang="ts" setup >
+import { RtsController } from '@/controller/rts'
+import { onMounted, reactive } from 'vue'
+
+const recordColumns = [
+  {
+    title: '存储地址',
+    dataIndex: 'Path'
+  },
+  {
+    title: '文件大小',
+    dataIndex: 'Size'
+  },
+  {
+    title: '时长',
+    dataIndex: 'Duration'
+  }
+]
+
+const columns = [
+  {
+    title: 'id',
+    dataIndex: 'ID'
+  },
+  {
+    title: '录制类型',
+    dataIndex: 'Type'
+  },
+  {
+    title: '录制开始时间',
+    dataIndex: 'StartTime'
+  },
+  {
+    title: '录制参数',
+    dataIndex: 'Config'
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    key: 'action'
+  }
+]
+
+const state = reactive({
+  loading: false,
+  visible: false,
+  recordingDataSource: [],
+  detail: {}
+})
+
+const openModal = (record: RTS.STREAM.Detail) => {
+  state.detail = record
+  state.visible = false
+}
+
+const getRecordingList = async () => {
+  const { data } = await RtsController.listRecording()
+  state.recordingDataSource = state
+}
+
+onMounted(() => {
+  getRecordingList()
+})
 </script>
 
 <style></style>

+ 29 - 3
src/pages/rts/stream/index.vue

@@ -11,13 +11,23 @@
       <template #bodyCell="{column, record}" >
         <template v-if="column.key === 'action'"  >
           <a-space>
+            <a @click="openModal(record)" >详情</a>
             <a>编辑</a>
-            <a>关闭视频流</a>
+            <a @click="closeSteram(record.streamPath)" >关闭视频流</a>
           </a-space>
         </template>
       </template>
     </a-table>
   </a-card>
+
+  <modal-pro
+    label="视频流详情"
+    :visible="state.visible"
+    @cancel="state.visible = false"
+    @ok="state.visible = false"
+  >
+
+  </modal-pro>
 </template>
 
 <script lang="ts" setup >
@@ -70,11 +80,27 @@ const queryParamsState = reactive({
 
 })
 
-const state = reactive({
+const state = reactive<{
+  loading: boolean,
+  dataSource: RTS.STREAM.Detail[]
+  detail: Partial<RTS.STREAM.Detail>
+  visible: boolean
+}>({
   loading: false,
-  dataSource: []
+  dataSource: [],
+  detail: {},
+  visible: false
 })
 
+const openModal = (record: RTS.STREAM.Detail) => {
+  state.detail = record
+  state.visible = false
+}
+
+const closeSteram = (streamPath: string) => {
+  RtsController.cloeStreams(streamPath)
+}
+
 const getStreamList = async () => {
   const { data } = await RtsController.getStreams()
   state.dataSource = data

+ 15 - 0
src/type/rts.d.ts

@@ -26,6 +26,19 @@ declare namespace RTS {
     }
   }
 
+  namespace PullStream {
+    interface Detail {
+      'ID': string,
+      'Type': 'RTSPPuller', // 拉流类型
+      'StartTime': string, // 开始时间
+      'Args': Record<string, any>, // 拉流参数
+      'StreamPath': string, // 流地址
+      'RemoteURL': string, // 被拉流地址
+      'ReConnectCount': number, // 重联次数
+      'Transport': number
+    }
+  }
+
   namespace SUMMARY {
     interface Detail {
       'Address': string,
@@ -54,4 +67,6 @@ declare namespace RTS {
     }
   }
 
+  type protocol = 'RTSP' | 'RTMP' | 'HLS' | 'HDL' | 'GB28181'
+
 }