mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-03-15 07:26:44 +08:00
feat: 支持 rtsp 功能
This commit is contained in:
@@ -330,6 +330,49 @@ export const rustdeskConfigApi = {
|
||||
}),
|
||||
}
|
||||
|
||||
// ===== RTSP 配置 API =====
|
||||
|
||||
export type RtspCodec = 'h264' | 'h265'
|
||||
|
||||
export interface RtspConfigResponse {
|
||||
enabled: boolean
|
||||
bind: string
|
||||
port: number
|
||||
path: string
|
||||
allow_one_client: boolean
|
||||
codec: RtspCodec
|
||||
username?: string | null
|
||||
has_password: boolean
|
||||
}
|
||||
|
||||
export interface RtspConfigUpdate {
|
||||
enabled?: boolean
|
||||
bind?: string
|
||||
port?: number
|
||||
path?: string
|
||||
allow_one_client?: boolean
|
||||
codec?: RtspCodec
|
||||
username?: string
|
||||
password?: string
|
||||
}
|
||||
|
||||
export interface RtspStatusResponse {
|
||||
config: RtspConfigResponse
|
||||
service_status: string
|
||||
}
|
||||
|
||||
export const rtspConfigApi = {
|
||||
get: () => request<RtspConfigResponse>('/config/rtsp'),
|
||||
|
||||
update: (config: RtspConfigUpdate) =>
|
||||
request<RtspConfigResponse>('/config/rtsp', {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(config),
|
||||
}),
|
||||
|
||||
getStatus: () => request<RtspStatusResponse>('/config/rtsp/status'),
|
||||
}
|
||||
|
||||
// ===== Web 服务器配置 API =====
|
||||
|
||||
/** Web 服务器配置 */
|
||||
|
||||
@@ -124,6 +124,19 @@ export interface AvailableCodecsResponse {
|
||||
codecs: VideoCodecInfo[]
|
||||
}
|
||||
|
||||
export interface StreamConstraintsResponse {
|
||||
success: boolean
|
||||
allowed_codecs: string[]
|
||||
locked_codec: string | null
|
||||
disallow_mjpeg: boolean
|
||||
sources: {
|
||||
rustdesk: boolean
|
||||
rtsp: boolean
|
||||
}
|
||||
reason: string
|
||||
current_mode: string
|
||||
}
|
||||
|
||||
export const streamApi = {
|
||||
status: () =>
|
||||
request<{
|
||||
@@ -161,6 +174,9 @@ export const streamApi = {
|
||||
getCodecs: () =>
|
||||
request<AvailableCodecsResponse>('/stream/codecs'),
|
||||
|
||||
getConstraints: () =>
|
||||
request<StreamConstraintsResponse>('/stream/constraints'),
|
||||
|
||||
setBitratePreset: (bitrate_preset: import('@/types/generated').BitratePreset) =>
|
||||
request<{ success: boolean; message?: string }>('/stream/bitrate', {
|
||||
method: 'POST',
|
||||
@@ -536,11 +552,15 @@ export {
|
||||
audioConfigApi,
|
||||
extensionsApi,
|
||||
rustdeskConfigApi,
|
||||
rtspConfigApi,
|
||||
webConfigApi,
|
||||
type RustDeskConfigResponse,
|
||||
type RustDeskStatusResponse,
|
||||
type RustDeskConfigUpdate,
|
||||
type RustDeskPasswordResponse,
|
||||
type RtspConfigResponse,
|
||||
type RtspConfigUpdate,
|
||||
type RtspStatusResponse,
|
||||
type WebConfig,
|
||||
} from './config'
|
||||
|
||||
|
||||
@@ -19,7 +19,14 @@ import {
|
||||
} from '@/components/ui/select'
|
||||
import { Monitor, RefreshCw, Loader2, Settings, Zap, Scale, Image } from 'lucide-vue-next'
|
||||
import HelpTooltip from '@/components/HelpTooltip.vue'
|
||||
import { configApi, streamApi, type VideoCodecInfo, type EncoderBackendInfo, type BitratePreset } from '@/api'
|
||||
import {
|
||||
configApi,
|
||||
streamApi,
|
||||
type VideoCodecInfo,
|
||||
type EncoderBackendInfo,
|
||||
type BitratePreset,
|
||||
type StreamConstraintsResponse,
|
||||
} from '@/api'
|
||||
import { useConfigStore } from '@/stores/config'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
@@ -64,6 +71,7 @@ const loadingCodecs = ref(false)
|
||||
|
||||
// Backend list
|
||||
const backends = ref<EncoderBackendInfo[]>([])
|
||||
const constraints = ref<StreamConstraintsResponse | null>(null)
|
||||
const currentEncoderBackend = computed(() => configStore.stream?.encoder || 'auto')
|
||||
|
||||
// Browser supported codecs (WebRTC receive capabilities)
|
||||
@@ -220,7 +228,7 @@ const availableCodecs = computed(() => {
|
||||
const backend = backends.value.find(b => b.id === currentEncoderBackend.value)
|
||||
if (!backend) return allAvailable
|
||||
|
||||
return allAvailable
|
||||
const backendFiltered = allAvailable
|
||||
.filter(codec => {
|
||||
// MJPEG is always available (doesn't require encoder)
|
||||
if (codec.id === 'mjpeg') return true
|
||||
@@ -238,6 +246,13 @@ const availableCodecs = computed(() => {
|
||||
backend: backend.name,
|
||||
}
|
||||
})
|
||||
|
||||
const allowed = constraints.value?.allowed_codecs
|
||||
if (!allowed || allowed.length === 0) {
|
||||
return backendFiltered
|
||||
}
|
||||
|
||||
return backendFiltered.filter(codec => allowed.includes(codec.id))
|
||||
})
|
||||
|
||||
// Cascading filters
|
||||
@@ -303,6 +318,14 @@ async function loadCodecs() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadConstraints() {
|
||||
try {
|
||||
constraints.value = await streamApi.getConstraints()
|
||||
} catch {
|
||||
constraints.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to settings page (video tab)
|
||||
function goToSettings() {
|
||||
router.push('/settings?tab=video')
|
||||
@@ -339,6 +362,12 @@ function syncFromCurrentIfChanged() {
|
||||
// Handle video mode change
|
||||
function handleVideoModeChange(mode: unknown) {
|
||||
if (typeof mode !== 'string') return
|
||||
|
||||
if (constraints.value?.allowed_codecs?.length && !constraints.value.allowed_codecs.includes(mode)) {
|
||||
toast.error(constraints.value.reason || t('actionbar.selectMode'))
|
||||
return
|
||||
}
|
||||
|
||||
emit('update:videoMode', mode as VideoMode)
|
||||
}
|
||||
|
||||
@@ -466,6 +495,8 @@ watch(() => props.open, (isOpen) => {
|
||||
loadCodecs()
|
||||
}
|
||||
|
||||
loadConstraints()
|
||||
|
||||
Promise.all([
|
||||
configStore.refreshVideo(),
|
||||
configStore.refreshStream(),
|
||||
|
||||
@@ -357,6 +357,30 @@ export interface RustDeskConfig {
|
||||
device_id: string;
|
||||
}
|
||||
|
||||
/** RTSP output codec */
|
||||
export enum RtspCodec {
|
||||
H264 = "h264",
|
||||
H265 = "h265",
|
||||
}
|
||||
|
||||
/** RTSP configuration */
|
||||
export interface RtspConfig {
|
||||
/** Enable RTSP output */
|
||||
enabled: boolean;
|
||||
/** Bind IP address */
|
||||
bind: string;
|
||||
/** RTSP TCP listen port */
|
||||
port: number;
|
||||
/** Stream path (without leading slash) */
|
||||
path: string;
|
||||
/** Allow only one client connection at a time */
|
||||
allow_one_client: boolean;
|
||||
/** Output codec (H264/H265) */
|
||||
codec: RtspCodec;
|
||||
/** Optional username for authentication */
|
||||
username?: string;
|
||||
}
|
||||
|
||||
/** Main application configuration */
|
||||
export interface AppConfig {
|
||||
/** Whether initial setup has been completed */
|
||||
@@ -381,6 +405,8 @@ export interface AppConfig {
|
||||
extensions: ExtensionsConfig;
|
||||
/** RustDesk remote access settings */
|
||||
rustdesk: RustDeskConfig;
|
||||
/** RTSP streaming settings */
|
||||
rtsp: RtspConfig;
|
||||
}
|
||||
|
||||
/** Update for a single ATX key configuration */
|
||||
@@ -557,6 +583,33 @@ export interface MsdConfigUpdate {
|
||||
msd_dir?: string;
|
||||
}
|
||||
|
||||
export interface RtspConfigResponse {
|
||||
enabled: boolean;
|
||||
bind: string;
|
||||
port: number;
|
||||
path: string;
|
||||
allow_one_client: boolean;
|
||||
codec: RtspCodec;
|
||||
username?: string;
|
||||
has_password: boolean;
|
||||
}
|
||||
|
||||
export interface RtspConfigUpdate {
|
||||
enabled?: boolean;
|
||||
bind?: string;
|
||||
port?: number;
|
||||
path?: string;
|
||||
allow_one_client?: boolean;
|
||||
codec?: RtspCodec;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export interface RtspStatusResponse {
|
||||
config: RtspConfigResponse;
|
||||
service_status: string;
|
||||
}
|
||||
|
||||
export interface RustDeskConfigUpdate {
|
||||
enabled?: boolean;
|
||||
rendezvous_server?: string;
|
||||
|
||||
Reference in New Issue
Block a user