Convert to user selected camera matching (#1556)

This commit is contained in:
oh-yes-0-fps
2025-01-01 03:04:20 -05:00
committed by GitHub
parent b2e70a7257
commit 418eada0b5
67 changed files with 2710 additions and 1948 deletions

View File

@@ -1,20 +1,20 @@
<script setup lang="ts">
import { computed, inject, ref, onBeforeUnmount } from "vue";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { useStateStore } from "@/stores/StateStore";
import loadingImage from "@/assets/images/loading.svg";
import type { StyleValue } from "vue/types/jsx";
import PvIcon from "@/components/common/pv-icon.vue";
import type { UiCameraConfiguration } from "@/types/SettingTypes";
const props = defineProps<{
streamType: "Raw" | "Processed";
id: string;
cameraSettings: UiCameraConfiguration;
}>();
const emptyStreamSrc = "//:0";
const streamSrc = computed<string>(() => {
const port =
useCameraSettingsStore().currentCameraSettings.stream[props.streamType === "Raw" ? "inputPort" : "outputPort"];
const port = props.cameraSettings.stream[props.streamType === "Raw" ? "inputPort" : "outputPort"];
if (!useStateStore().backendConnected || port === 0) {
return emptyStreamSrc;
@@ -32,8 +32,12 @@ const streamStyle = computed<StyleValue>(() => {
});
const containerStyle = computed<StyleValue>(() => {
const resolution = useCameraSettingsStore().currentVideoFormat.resolution;
const rotation = useCameraSettingsStore().currentPipelineSettings.inputImageRotationMode;
if (props.cameraSettings.validVideoFormats.length === 0) {
return { aspectRatio: "1/1" };
}
const resolution =
props.cameraSettings.validVideoFormats[props.cameraSettings.pipelineSettings.cameraVideoModeIndex].resolution;
const rotation = props.cameraSettings.pipelineSettings.inputImageRotationMode;
if (rotation === 1 || rotation === 3) {
return {
aspectRatio: `${resolution.height}/${resolution.width}`
@@ -54,9 +58,9 @@ const overlayStyle = computed<StyleValue>(() => {
const handleCaptureClick = () => {
if (props.streamType === "Raw") {
useCameraSettingsStore().saveInputSnapshot();
props.cameraSettings.pipelineSettings[props.cameraSettings.currentPipelineIndex].saveInputSnapshot();
} else {
useCameraSettingsStore().saveOutputSnapshot();
props.cameraSettings.pipelineSettings[props.cameraSettings.currentPipelineIndex].saveOutputSnapshot();
}
};
const handlePopoutClick = () => {
@@ -69,6 +73,16 @@ const handleFullscreenRequest = () => {
};
const mjpgStream: any = ref(null);
const handleStreamError = () => {
if (streamSrc.value && streamSrc.value !== emptyStreamSrc) {
console.error("Error loading stream:", streamSrc.value, " Trying again.");
setTimeout(() => {
mjpgStream.value.src = streamSrc.value;
}, 100);
}
};
onBeforeUnmount(() => {
if (!mjpgStream.value) return;
mjpgStream.value["src"] = emptyStreamSrc;
@@ -79,7 +93,6 @@ onBeforeUnmount(() => {
<div class="stream-container" :style="containerStyle">
<img :src="loadingImage" class="stream-loading" />
<img
v-show="streamSrc !== emptyStreamSrc"
:id="id"
ref="mjpgStream"
class="stream-video"
@@ -87,6 +100,7 @@ onBeforeUnmount(() => {
:src="streamSrc"
:alt="streamDesc"
:style="streamStyle"
@error="handleStreamError"
/>
<div class="stream-overlay" :style="overlayStyle">
<pv-icon

View File

@@ -2,6 +2,9 @@
import { computed, getCurrentInstance } from "vue";
import { useSettingsStore } from "@/stores/settings/GeneralSettingsStore";
import { useStateStore } from "@/stores/StateStore";
import { useCameraSettingsStore } from "@/stores/settings/CameraSettingsStore";
import { PlaceholderCameraSettings } from "@/types/SettingTypes";
import { useRoute } from "vue2-helpers/vue-router";
const compact = computed<boolean>({
get: () => {
@@ -14,6 +17,12 @@ const compact = computed<boolean>({
// Vuetify2 doesn't yet support the useDisplay API so this is required to access the prop when using the Composition API
const mdAndUp = computed<boolean>(() => getCurrentInstance()?.proxy.$vuetify.breakpoint.mdAndUp || false);
const needsCamerasConfigured = computed<boolean>(() => {
return (
useCameraSettingsStore().cameras.length === 0 || useCameraSettingsStore().cameras[0] === PlaceholderCameraSettings
);
});
</script>
<template>
@@ -35,14 +44,6 @@ const mdAndUp = computed<boolean>(() => getCurrentInstance()?.proxy.$vuetify.bre
<v-list-item-title>Dashboard</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item ref="camerasTabOpener" link to="/cameras">
<v-list-item-icon>
<v-icon>mdi-camera</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Cameras</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item link to="/settings">
<v-list-item-icon>
<v-icon>mdi-cog</v-icon>
@@ -51,6 +52,26 @@ const mdAndUp = computed<boolean>(() => getCurrentInstance()?.proxy.$vuetify.bre
<v-list-item-title>Settings</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item ref="camerasTabOpener" link to="/cameras">
<v-list-item-icon>
<v-icon>mdi-camera</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Camera</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item
link
to="/cameraConfigs"
:class="{ cameraicon: needsCamerasConfigured && useRoute().path !== '/cameraConfigs' }"
>
<v-list-item-icon>
<v-icon :class="{ 'red--text': needsCamerasConfigured }">mdi-swap-horizontal-bold</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title :class="{ 'red--text': needsCamerasConfigured }">Camera Matching</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item link to="/docs">
<v-list-item-icon>
<v-icon>mdi-bookshelf</v-icon>
@@ -119,4 +140,18 @@ const mdAndUp = computed<boolean>(() => getCurrentInstance()?.proxy.$vuetify.bre
height: 70px;
object-fit: contain;
}
.cameraicon {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%,
100% {
transform: scale(0.95);
}
50% {
transform: scale(1.05);
}
}
</style>