Redesign UI and add color science / video mode controls
- Full UI rewrite: dark tabbed layout (Exposure, Color Science, Color Correction, Lens, System) replacing cramped two-panel layout - Exposure tab: large pill controls for ISO/Shutter/ND/Gain/WB with ◀ ▶ nudge buttons, WB presets, AE mode row - Color Science tab (new): gamma curve selector, color gamut selector, quick presets (BRAW Film/Video/Ext.Video/Rec.709), video format (codec + frame rate) controls via /system/format - Color Correction tab: cleaner LGGO rows with per-channel LRGB labels, contrast and hue/sat/luma-contribution sliders - Lens tab: large vertical sliders for Focus/Iris/Zoom with AF button - System tab: connection, presets, manual API, footer links - BMDevice.js: added setColorScience(gamut, gamma) and setVideoFormat() - Always-visible top bar: recording pulse animation, transport controls, timecode, format display, camera switcher Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6fcd7f0317
commit
e423bd4bf6
5 changed files with 1816 additions and 1280 deletions
12
BMDevice.js
12
BMDevice.js
|
|
@ -245,6 +245,18 @@ class BMCamera extends BMDevice {
|
||||||
doAutoWhitebalance() {
|
doAutoWhitebalance() {
|
||||||
this.PUTdata("/video/whiteBalance/doAuto");
|
this.PUTdata("/video/whiteBalance/doAuto");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gamut and gamma can each be null to leave the other unchanged
|
||||||
|
setColorScience(gamut, gamma) {
|
||||||
|
const data = {};
|
||||||
|
if (gamut !== null && gamut !== undefined) data.gamut = gamut;
|
||||||
|
if (gamma !== null && gamma !== undefined) data.gamma = gamma;
|
||||||
|
if (Object.keys(data).length) this.PUTdata("/video/colorScience", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
setVideoFormat(data) {
|
||||||
|
this.PUTdata("/system/format", data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper Functions */
|
/* Helper Functions */
|
||||||
|
|
|
||||||
22
PRODUCT.md
Normal file
22
PRODUCT.md
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# BM Camera Control WebUI
|
||||||
|
|
||||||
|
## Product Purpose
|
||||||
|
A browser-based remote control panel for Blackmagic Design cameras via the official REST API over a local network. Used on set to adjust camera parameters without touching the camera body.
|
||||||
|
|
||||||
|
## Users
|
||||||
|
- Camera operators: need one-tap access to recording state and exposure controls
|
||||||
|
- DITs (Digital Imaging Technicians): need color science selection, color correction, and format settings
|
||||||
|
- Directors / ACs: need timecode and recording status visible at a glance
|
||||||
|
- All users may be working in dim environments (video village, studio) and on tablets
|
||||||
|
|
||||||
|
## Register
|
||||||
|
product
|
||||||
|
|
||||||
|
## Tone
|
||||||
|
Clinical and fast. Every control earns its place. No decorative chrome. Quiet when nothing is happening; clear when something requires attention (especially recording state).
|
||||||
|
|
||||||
|
## Anti-references
|
||||||
|
- Neon SaaS dashboards
|
||||||
|
- Consumer camera apps (bright white, rounded, playful)
|
||||||
|
- Generic Bootstrap-dark or shadcn defaults
|
||||||
|
- Glassmorphism decoration
|
||||||
843
index.html
843
index.html
|
|
@ -1,349 +1,558 @@
|
||||||
<!-- (c) 2024 Dylan Speiser -->
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<!-- Page title and metadata -->
|
<meta charset="UTF-8">
|
||||||
<title>Camera Control WebUI for Blackmagic Cameras</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta charset="UTF-8">
|
<title>BM Camera Control</title>
|
||||||
<meta name="description" content="JS-based web interface for controlling Blackmagic Design cameras via the official REST API">
|
<link rel="stylesheet" href="style.css">
|
||||||
<meta name="author" content="Dylan Speiser">
|
</head>
|
||||||
|
<body onload="bodyOnLoad()">
|
||||||
|
<script src="BMDevice.js"></script>
|
||||||
|
<script src="web-ui.js"></script>
|
||||||
|
|
||||||
<!-- Linking the stylesheet -->
|
<!-- Top Bar -->
|
||||||
<link rel="stylesheet" href="style.css">
|
<header id="topBar">
|
||||||
</head>
|
<nav id="camSwitcher">
|
||||||
<body onload="bodyOnLoad()">
|
<button class="camBtn selected" onclick="switchCamera(0)">CAM1</button>
|
||||||
<!-- JavaScript Linking -->
|
<button class="camBtn" onclick="switchCamera(1)">CAM2</button>
|
||||||
<script src="BMDevice.js"></script>
|
<button class="camBtn" onclick="switchCamera(2)">CAM3</button>
|
||||||
<script src="web-ui.js"></script>
|
<button class="camBtn" onclick="switchCamera(3)">CAM4</button>
|
||||||
|
<button class="camBtn" onclick="switchCamera(4)">CAM5</button>
|
||||||
|
<button class="camBtn" onclick="switchCamera(5)">CAM6</button>
|
||||||
|
<button class="camBtn" onclick="switchCamera(6)">CAM7</button>
|
||||||
|
<button class="camBtn" onclick="switchCamera(7)">CAM8</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<!------ Page Content ------>
|
<div id="camInfo">
|
||||||
|
<span id="cameraName">NOT CONNECTED</span>
|
||||||
<!-- Header Div -->
|
<span id="timecodeLabel" class="timecode">--:--:--:--</span>
|
||||||
<div class="flexContainerH" id="headerContainer" onclick="document.getElementById('footerContainer').style.display='flex'">
|
|
||||||
<h1>Camera Control WebUI for Blackmagic Cameras</h1>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Camera Select Bar -->
|
<div id="formatDisplay">
|
||||||
<div class="flexContainerH" id="cameraSelectContainer">
|
<span id="formatCodec">—</span>
|
||||||
<span class="cameraSwitchLabel selectedCam"><a href="#" onclick="switchCamera(0)">CAM1</a></span>
|
<span id="formatResolution">—</span>
|
||||||
<span class="camSelectSeparator">|</span>
|
<span id="formatFPS">—</span>
|
||||||
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(1)">CAM2</a></span>
|
|
||||||
<span class="camSelectSeparator">|</span>
|
|
||||||
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(2)">CAM3</a></span>
|
|
||||||
<span class="camSelectSeparator">|</span>
|
|
||||||
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(3)">CAM4</a></span>
|
|
||||||
<span class="camSelectSeparator">|</span>
|
|
||||||
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(4)">CAM5</a></span>
|
|
||||||
<span class="camSelectSeparator">|</span>
|
|
||||||
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(5)">CAM6</a></span>
|
|
||||||
<span class="camSelectSeparator">|</span>
|
|
||||||
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(6)">CAM7</a></span>
|
|
||||||
<span class="camSelectSeparator">|</span>
|
|
||||||
<span class="cameraSwitchLabel"><a href="#" onclick="switchCamera(7)">CAM8</a></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Camera Controls Box -->
|
<div id="transportControls">
|
||||||
<div class="flexContainerH" id="allCamerasContainer">
|
<button class="iconBtn" onclick="loopHandler('Loop')" title="Loop" id="loopButton">↻</button>
|
||||||
<div class="flexContainerV" id="cameraControlsContainer">
|
<button class="iconBtn" onclick="loopHandler('Single Clip')" title="Single Clip" id="singleClipButton">S</button>
|
||||||
<div class="flexContainerH" id="cameraControlHeadContainer">
|
<button class="iconBtn" onclick="cameras[ci].seek(false)" title="Previous">⏴</button>
|
||||||
<h2 id="cameraNumberLabel">CAM1</h2>
|
<button class="iconBtn" onclick="cameras[ci].play()" title="Play">▶</button>
|
||||||
</div>
|
<button class="iconBtn" onclick="cameras[ci].stop()" title="Stop">⏹</button>
|
||||||
|
<button class="iconBtn" onclick="cameras[ci].seek(true)" title="Next">⏵</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionContainer">
|
<button id="recordButton" onclick="cameras[ci].toggleRecord()" title="Toggle Record">
|
||||||
<!-- <div class="flexContainerH" id="cameraControlLGGTabs">
|
<span id="recDot"></span>
|
||||||
<a href="#" class="ccTabLabel selectedTab" onclick="">Lift</a>
|
<span id="recLabel">REC</span>
|
||||||
<a href="#" class="ccTabLabel" onclick="">Gamma</a>
|
</button>
|
||||||
<a href="#" class="ccTabLabel" onclick="">Gain</a>
|
</header>
|
||||||
</div> -->
|
|
||||||
<span style="margin-top: 0.5em;">Lift</span>
|
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
|
|
||||||
<button class="CCResetButton circleButton" onclick="resetCC(0)" title="Reset Lift">⟳</button>
|
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
|
||||||
</div>
|
|
||||||
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(0)" title="Set Lift">➚</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span>Gamma</span>
|
<!-- Tab Bar -->
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
|
<nav id="tabBar">
|
||||||
<button class="CCResetButton circleButton" onclick="resetCC(1)" title="Reset Gamma">⟳</button>
|
<button class="tab active" onclick="switchTab('exposure')" id="tab-btn-exposure">Exposure</button>
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
|
<button class="tab" onclick="switchTab('colorscience')" id="tab-btn-colorscience">Color Science</button>
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
<button class="tab" onclick="switchTab('colorcorrection')" id="tab-btn-colorcorrection">Color Correction</button>
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
<button class="tab" onclick="switchTab('lens')" id="tab-btn-lens">Lens</button>
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
<button class="tab" onclick="switchTab('system')" id="tab-btn-system">System</button>
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
</nav>
|
||||||
</div>
|
|
||||||
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(1)" title="Set Gamma">➚</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span>Gain</span>
|
<!-- ======== MAIN CONTENT ======== -->
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
|
<main id="mainContent">
|
||||||
<button class="CCResetButton circleButton" onclick="resetCC(2)" title="Reset Gain">⟳</button>
|
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">0.00</span>
|
|
||||||
</div>
|
|
||||||
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(2)" title="Set Gain">➚</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<span>Offset</span>
|
<!-- ===== EXPOSURE PANEL ===== -->
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionBottomContainer">
|
<section id="tab-exposure" class="tabPanel active">
|
||||||
<button class="CCResetButton circleButton" onclick="resetCC(3)" title="Reset Offset">⟳</button>
|
<div class="exposureRow">
|
||||||
<div class="flexContainerH" id="cameraControlColorCorrectionNumbersContainer">
|
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #dbdbdb;" class="CClumaLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
<div class="expPill">
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #e64b3d;" class="CCredLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
<span class="expPillLabel">ISO</span>
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #00a841;" class="CCgreenLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
<div class="expPillValue">
|
||||||
<span contenteditable="plaintext-only" style="text-decoration-color: #2a78c8;" class="CCblueLabel" onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
<button class="adjBtn" onclick="adjustISO(-100)">◀</button>
|
||||||
</div>
|
<span id="ISODisplay" class="expValue" contenteditable="plaintext-only"
|
||||||
<button id="CCHamburgerButton" class="circleButton" onclick="setCCFromUI(4)" title="Set Offset">➚</button>
|
onkeydown="ISOKeyHandler(event)" onblur="ISOBlurHandler()">—</span>
|
||||||
|
<button class="adjBtn" onclick="adjustISO(100)">▶</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flexContainerH" id="cameraControlExposureContainer">
|
<div class="expPill">
|
||||||
<div class="ccExposureSettingContainer">
|
<span class="expPillLabel">SHUTTER</span>
|
||||||
<span class="exposureControlLabel">FILTER</span>
|
<div class="expPillValue">
|
||||||
<div class="ccExposureSettingValueContainer">
|
<button class="adjBtn" onclick="decreaseShutter()">◀</button>
|
||||||
<a class="expAdjArr" href="#" onclick="decreaseND()" id="NDL">◀</a>
|
<span id="shutterDisplay" class="expValue" contenteditable="plaintext-only"
|
||||||
<span id="ndFilterSpan" contenteditable="plaintext-only" onkeydown="NDFilterInputHandler()" onmousedown="NDFilterInputHandler()">0</span>
|
onkeydown="handleShutterInput()" onmousedown="handleShutterInput()">—</span>
|
||||||
<a class="expAdjArr" href="#" onclick="increaseND()" id="NDR">▶</a>
|
<button class="adjBtn" onclick="increaseShutter()">▶</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ccExposureSettingContainer">
|
|
||||||
<span class="exposureControlLabel">GAIN</span>
|
|
||||||
<div class="ccExposureSettingValueContainer">
|
|
||||||
<a class="expAdjArr" href="#" onclick="decreaseGain()" id="GAL">◀</a>
|
|
||||||
<span id="gainSpan" contenteditable="plaintext-only" onkeydown="GainInputHandler()" onmousedown="GainInputHandler()">+0db</span>
|
|
||||||
<a class="expAdjArr" href="#" onclick="increaseGain()" id="GAR">▶</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ccExposureSettingContainer">
|
|
||||||
<span class="exposureControlLabel">SHUTTER</span>
|
|
||||||
<div class="ccExposureSettingValueContainer">
|
|
||||||
<a class="expAdjArr" href="#" onclick="decreaseShutter()" id="SHL">◀</a>
|
|
||||||
<span id="shutterSpan" contenteditable="plaintext-only" onkeydown="handleShutterInput()" onmousedown="handleShutterInput()">1/50</span>
|
|
||||||
<a class="expAdjArr" href="#" onclick="increaseShutter()" id="SHR">▶</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ccExposureSettingContainer">
|
|
||||||
<span class="exposureControlLabel" onclick="swapWBMode()" title="Click here to swap between WB and Tint" id="WBLabel">BALANCE</span>
|
|
||||||
<div class="ccExposureSettingValueContainer" id="WBValueContainer">
|
|
||||||
<a class="expAdjArr" href="#" onclick="decreaseWhiteBalance()" id="WBL">◀</a>
|
|
||||||
<span id="whiteBalanceSpan" contenteditable="plaintext-only" onkeydown="WBInputHandler()" onmousedown="WBInputHandler()">5600K</span>
|
|
||||||
<a class="expAdjArr" href="#" onclick="increaseWhiteBalance()" id="WBR">▶</a>
|
|
||||||
</div>
|
|
||||||
<div class="ccExposureSettingValueContainer dNone" id="WBTintValueContainer">
|
|
||||||
<a class="expAdjArr" href="#" onclick="decreaseWhiteBalanceTint()" id="WBTL">◀</a>
|
|
||||||
<span id="whiteBalanceTintSpan" contenteditable="plaintext-only" onkeydown="WBTInputHandler()" onmousedown="WBTInputHandler()">0</span>
|
|
||||||
<a class="expAdjArr" href="#" onclick="increaseWhiteBalanceTint()" id="WBLR">▶</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="ccExposureSettingContainer">
|
|
||||||
<button id="AWBButton" class="circleButton" title="Make the camera do an Auto Whitebalance" onclick="cameras[ci].doAutoWhitebalance()">AW</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flexContainerH" id="cameraControlLensContainer">
|
<div class="expPill">
|
||||||
<div class="lensSliderContainer">
|
<span class="expPillLabel">ND FILTER</span>
|
||||||
<span>FOCUS</span>
|
<div class="expPillValue">
|
||||||
<input type="range" orient="vertical" max="1" min="0" step="0.001" id="focusRange" oninput="cameras[ci].PUTdata('/lens/focus', {normalised: parseFloat(this.value)})">
|
<button class="adjBtn" onclick="decreaseND()">◀</button>
|
||||||
<button id="AFButton" class="circleButton" onclick="cameras[ci].doAutoFocus()">AF</button>
|
<span id="ndFilterDisplay" class="expValue" contenteditable="plaintext-only"
|
||||||
</div>
|
onkeydown="NDFilterInputHandler()" onmousedown="NDFilterInputHandler()">—</span>
|
||||||
<div class="lensSliderContainer">
|
<button class="adjBtn" onclick="increaseND()">▶</button>
|
||||||
<span>IRIS</span>
|
|
||||||
<input type="range" orient="vertical" max="1" min="0" step="0.001" id="irisRange" oninput="cameras[ci].PUTdata('/lens/iris', {normalised: parseFloat(this.value)})">
|
|
||||||
<span id="apertureStopsLabel">X.X</span>
|
|
||||||
</div>
|
|
||||||
<div class="lensSliderContainer">
|
|
||||||
<span>ZOOM</span>
|
|
||||||
<input type="range" orient="vertical" max="1" min="0" step="0.001" id="zoomRange" oninput="cameras[ci].PUTdata('/lens/zoom', {normalised: parseFloat(this.value)})">
|
|
||||||
<span id="zoomMMLabel">XXmm</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="expPill">
|
||||||
|
<span class="expPillLabel">GAIN</span>
|
||||||
|
<div class="expPillValue">
|
||||||
|
<button class="adjBtn" onclick="decreaseGain()">◀</button>
|
||||||
|
<span id="gainDisplay" class="expValue" contenteditable="plaintext-only"
|
||||||
|
onkeydown="GainInputHandler()" onmousedown="GainInputHandler()">—</span>
|
||||||
|
<button class="adjBtn" onclick="increaseGain()">▶</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="expPill" id="wbPill">
|
||||||
|
<span class="expPillLabel" id="WBLabel" onclick="swapWBMode()" title="Click to toggle WB / Tint"
|
||||||
|
style="cursor:pointer">WB ↕</span>
|
||||||
|
<div class="expPillValue" id="WBValueContainer">
|
||||||
|
<button class="adjBtn" onclick="decreaseWhiteBalance()">◀</button>
|
||||||
|
<span id="whiteBalanceDisplay" class="expValue" contenteditable="plaintext-only"
|
||||||
|
onkeydown="WBInputHandler()" onmousedown="WBInputHandler()">—</span>
|
||||||
|
<button class="adjBtn" onclick="increaseWhiteBalance()">▶</button>
|
||||||
|
</div>
|
||||||
|
<div class="expPillValue hidden" id="WBTintValueContainer">
|
||||||
|
<button class="adjBtn" onclick="decreaseWhiteBalanceTint()">◀</button>
|
||||||
|
<span id="whiteBalanceTintDisplay" class="expValue" contenteditable="plaintext-only"
|
||||||
|
onkeydown="WBTInputHandler()" onmousedown="WBTInputHandler()">—</span>
|
||||||
|
<button class="adjBtn" onclick="increaseWhiteBalanceTint()">▶</button>
|
||||||
|
</div>
|
||||||
|
<button class="awbBtn" onclick="cameras[ci].doAutoWhitebalance()" title="Auto White Balance">AWB</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flexContainerV" id="cameraControlsContainerExpanded">
|
<!-- AE Mode row -->
|
||||||
<div class="flexContainerH" id="cameraControlExpandedHeadContainer">
|
<div class="settingsRow">
|
||||||
<h2 id="cameraName">CAMERA NAME</h2>
|
<div class="settingGroup">
|
||||||
<div id="formatDisplay">
|
<label class="settingLabel">AE MODE</label>
|
||||||
<span id="formatCodec">CODEC</span>
|
<select id="AEmodeDropDown" class="settingSelect" onmousedown="unsavedChanges.push('AutoExposure')">
|
||||||
<span id="formatResolution">RESOLUTION</span>
|
<option value="Off">Off</option>
|
||||||
<span id="formatFPS">FPS</span>
|
<option value="Continuous">Continuous</option>
|
||||||
</div>
|
<option value="OneShot">One-Shot</option>
|
||||||
<div id="transportControls">
|
</select>
|
||||||
<!-- These will be sticky buttons for loop, single clip, record -->
|
</div>
|
||||||
<button class="circleButton" onclick="loopHandler('Loop')" title="Loop" id="loopButton">↻</button>
|
<div class="settingGroup">
|
||||||
<button class="circleButton" onclick="loopHandler('Single Clip')" title="Single Clip" id="singleClipButton">S</button>
|
<label class="settingLabel">AE TYPE</label>
|
||||||
<button class="circleButton" onclick="cameras[ci].seek(false)" title="Back">⏴</button>
|
<select id="AEtypeDropDown" class="settingSelect" onmousedown="unsavedChanges.push('AutoExposure')">
|
||||||
<button class="circleButton" onclick="cameras[ci].seek(true)" title="Forward">⏵</button>
|
<option value="">None</option>
|
||||||
<button class="circleButton" onclick="cameras[ci].toggleRecord()" title="Record" style="color: red;">⏺</button>
|
<option value="Iris">Iris Only</option>
|
||||||
<button class="circleButton" onclick="cameras[ci].play()" title="Play">▶</button>
|
<option value="Shutter">Shutter Only</option>
|
||||||
<button class="circleButton" onclick="cameras[ci].stop()" title="Stop">⏹</button>
|
<option value="Shutter,Iris">Shutter + Iris</option>
|
||||||
</div>
|
<option value="Iris,Shutter">Iris + Shutter</option>
|
||||||
<h2 id="timecodeLabel">TIMECODE</h2>
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="actionBtn" onclick="AEmodeInputHandler()">Set AE Mode</button>
|
||||||
|
|
||||||
|
<!-- WB Presets -->
|
||||||
|
<div class="settingGroup" style="margin-left: auto;">
|
||||||
|
<label class="settingLabel">WB PRESETS</label>
|
||||||
|
<div class="presetBtnRow">
|
||||||
|
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(0)">Sun</button>
|
||||||
|
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(1)">Tungsten</button>
|
||||||
|
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(2)">Fluoro</button>
|
||||||
|
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(3)">Shade</button>
|
||||||
|
<button class="presetBtn" onclick="cameras[ci].setWhiteBalancePreset(4)">Cloud</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flexContainerV" id="cameraControlExpandedBodyContainer">
|
<!-- ISO direct input -->
|
||||||
<div class="tableControl">
|
<div class="settingsRow">
|
||||||
<h3>Connection</h3>
|
<div class="settingGroup">
|
||||||
<table>
|
<label class="settingLabel">ISO (direct)</label>
|
||||||
<tr>
|
<input type="number" id="ISOInput" class="settingInput" step="100"
|
||||||
<td>Hostname</td>
|
onkeydown="ISOInputHandler()" onmousedown="unsavedChanges.push('ISO')">
|
||||||
<td>
|
</div>
|
||||||
<input type="text" placeholder=" Camera-Name-Here.local" id="hostnameInput" onclick="hostnameInputHandler()" onkeydown="hostnameInputHandler()" style="text-align: left;">
|
</div>
|
||||||
<button onclick="initCamera()">Connect</button>
|
</section>
|
||||||
<input type="checkbox" id="secureCheckbox">
|
|
||||||
<span id="secureCheckboxLabel">Use HTTPS</span>
|
|
||||||
<span id="connectionErrorSpan"></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Send API Call</td>
|
|
||||||
<td>
|
|
||||||
<input type="radio" id="requestTypeGET" value="GET" name="manualRequestType" checked>
|
|
||||||
<label for="requestTypeGET">GET</label>
|
|
||||||
<input type="radio" id="requestTypePUT" value="PUT" name="manualRequestType">
|
|
||||||
<label for="requestTypePUT">PUT</label>
|
|
||||||
|
|
||||||
<input type="text" id="manualRequestEndpointLabel" placeholder="request endpoint">
|
<!-- ===== COLOR SCIENCE PANEL (NEW) ===== -->
|
||||||
<input type="text" id="manualRequestBodyLabel" placeholder="request body">
|
<section id="tab-colorscience" class="tabPanel">
|
||||||
|
|
||||||
<button onclick="manualAPICall()">Send API Request</button>
|
<div class="csGrid">
|
||||||
</td>
|
<div class="csCard">
|
||||||
</tr>
|
<h3 class="csCardTitle">Gamma Curve</h3>
|
||||||
<tr>
|
<div class="csOptions" id="gammaOptions">
|
||||||
<td colspan="2">
|
<button class="csOptionBtn" data-gamma="Blackmagic Design Film" onclick="setGamma(this)">Film</button>
|
||||||
<p id="manualRequestResponseP">Send manual API requests using the above controls. See <a href="https://documents.blackmagicdesign.com/DeveloperManuals/RESTAPIforBlackmagicCameras.pdf?_v=1696143610000" target="_blank" style="color: #6e6e6e;">documentation</a> for details.</p>
|
<button class="csOptionBtn" data-gamma="Blackmagic Design Video" onclick="setGamma(this)">Video</button>
|
||||||
</td>
|
<button class="csOptionBtn" data-gamma="Blackmagic Design Extended Video" onclick="setGamma(this)">Extended Video</button>
|
||||||
</tr>
|
<button class="csOptionBtn" data-gamma="Rec709" onclick="setGamma(this)">Rec.709</button>
|
||||||
</table>
|
<button class="csOptionBtn" data-gamma="sRGB" onclick="setGamma(this)">sRGB</button>
|
||||||
|
<button class="csOptionBtn" data-gamma="Linear" onclick="setGamma(this)">Linear</button>
|
||||||
|
<button class="csOptionBtn" data-gamma="DaVinci Intermediate" onclick="setGamma(this)">DaVinci Intermediate</button>
|
||||||
|
</div>
|
||||||
|
<div class="csCurrentRow">
|
||||||
|
<span class="csCurrentLabel">Current:</span>
|
||||||
|
<span id="currentGamma" class="csCurrentValue">—</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="csCard">
|
||||||
|
<h3 class="csCardTitle">Color Gamut</h3>
|
||||||
|
<div class="csOptions" id="gamutOptions">
|
||||||
|
<button class="csOptionBtn" data-gamut="Blackmagic Wide Gamut" onclick="setGamut(this)">BM Wide Gamut</button>
|
||||||
|
<button class="csOptionBtn" data-gamut="Blackmagic Design" onclick="setGamut(this)">BM Design</button>
|
||||||
|
<button class="csOptionBtn" data-gamut="Rec.2020" onclick="setGamut(this)">Rec.2020</button>
|
||||||
|
<button class="csOptionBtn" data-gamut="Rec.709" onclick="setGamut(this)">Rec.709</button>
|
||||||
|
<button class="csOptionBtn" data-gamut="sRGB" onclick="setGamut(this)">sRGB</button>
|
||||||
|
<button class="csOptionBtn" data-gamut="DCI-P3" onclick="setGamut(this)">DCI-P3</button>
|
||||||
|
<button class="csOptionBtn" data-gamut="Display P3" onclick="setGamut(this)">Display P3</button>
|
||||||
|
</div>
|
||||||
|
<div class="csCurrentRow">
|
||||||
|
<span class="csCurrentLabel">Current:</span>
|
||||||
|
<span id="currentGamut" class="csCurrentValue">—</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="csCard csCard--full">
|
||||||
|
<h3 class="csCardTitle">Quick Presets</h3>
|
||||||
|
<div class="csQuickPresets">
|
||||||
|
<button class="csQuickBtn" onclick="applyColorSciencePreset('braw-film')">
|
||||||
|
<span class="csQuickBtnName">BRAW Film</span>
|
||||||
|
<span class="csQuickBtnDetail">BM Wide Gamut + Film</span>
|
||||||
|
</button>
|
||||||
|
<button class="csQuickBtn" onclick="applyColorSciencePreset('braw-video')">
|
||||||
|
<span class="csQuickBtnName">BRAW Video</span>
|
||||||
|
<span class="csQuickBtnDetail">BM Design + Video</span>
|
||||||
|
</button>
|
||||||
|
<button class="csQuickBtn" onclick="applyColorSciencePreset('braw-ext')">
|
||||||
|
<span class="csQuickBtnName">BRAW Ext. Video</span>
|
||||||
|
<span class="csQuickBtnDetail">BM Design + Ext. Video</span>
|
||||||
|
</button>
|
||||||
|
<button class="csQuickBtn" onclick="applyColorSciencePreset('rec709')">
|
||||||
|
<span class="csQuickBtnName">Rec.709</span>
|
||||||
|
<span class="csQuickBtnDetail">Rec.709 + Rec709</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Video Format / Mode -->
|
||||||
|
<div class="sectionDivider">
|
||||||
|
<span>Video Format</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="formatGrid">
|
||||||
|
<div class="settingGroup">
|
||||||
|
<label class="settingLabel">CODEC</label>
|
||||||
|
<select id="codecSelect" class="settingSelect">
|
||||||
|
<option value="">— current —</option>
|
||||||
|
<option value="BRAW 12:1">BRAW 12:1</option>
|
||||||
|
<option value="BRAW 8:1">BRAW 8:1</option>
|
||||||
|
<option value="BRAW 5:1">BRAW 5:1</option>
|
||||||
|
<option value="BRAW 3:1">BRAW 3:1</option>
|
||||||
|
<option value="H.265 High">H.265 High</option>
|
||||||
|
<option value="H.265 Medium">H.265 Medium</option>
|
||||||
|
<option value="H.265 Low">H.265 Low</option>
|
||||||
|
<option value="H.264 High">H.264 High</option>
|
||||||
|
<option value="H.264 Medium">H.264 Medium</option>
|
||||||
|
<option value="H.264 Low">H.264 Low</option>
|
||||||
|
<option value="ProRes 422 HQ">ProRes 422 HQ</option>
|
||||||
|
<option value="ProRes 422">ProRes 422</option>
|
||||||
|
<option value="ProRes 422 LT">ProRes 422 LT</option>
|
||||||
|
<option value="ProRes 422 Proxy">ProRes 422 Proxy</option>
|
||||||
|
<option value="ProRes 4444 XQ">ProRes 4444 XQ</option>
|
||||||
|
<option value="DNxHR">DNxHR</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="settingGroup">
|
||||||
|
<label class="settingLabel">FRAME RATE</label>
|
||||||
|
<select id="frameRateSelect" class="settingSelect">
|
||||||
|
<option value="">— current —</option>
|
||||||
|
<option value="23.98">23.98</option>
|
||||||
|
<option value="24">24</option>
|
||||||
|
<option value="25">25</option>
|
||||||
|
<option value="29.97">29.97</option>
|
||||||
|
<option value="30">30</option>
|
||||||
|
<option value="47.95">47.95</option>
|
||||||
|
<option value="48">48</option>
|
||||||
|
<option value="50">50</option>
|
||||||
|
<option value="59.94">59.94</option>
|
||||||
|
<option value="60">60</option>
|
||||||
|
<option value="120">120</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="actionBtn" onclick="applyVideoFormat()">Set Format</button>
|
||||||
|
<span id="formatSetStatus" class="statusNote"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== COLOR CORRECTION PANEL ===== -->
|
||||||
|
<section id="tab-colorcorrection" class="tabPanel">
|
||||||
|
|
||||||
|
<div class="ccGrid">
|
||||||
|
<!-- Lift -->
|
||||||
|
<div class="ccRow">
|
||||||
|
<span class="ccRowLabel">Lift</span>
|
||||||
|
<div class="ccChannels">
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
|
||||||
|
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
<div class="tableControl">
|
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
|
||||||
<h3>Presets</h3>
|
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
|
||||||
<table>
|
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
||||||
<tr>
|
|
||||||
<td>Preset Select</td>
|
|
||||||
<td>
|
|
||||||
<select id="presetsDropDown" onmousedown="unsavedChanges.push('presets')" onchange="presetInputHandler()">
|
|
||||||
<!-- Auto-populated by updateUIPresets() -->
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<button onclick="presetInputHandler()">Restore from Preset</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
<div class="tableControl">
|
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
|
||||||
<h3>Exposure</h3>
|
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
|
||||||
<table>
|
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
||||||
<tr>
|
|
||||||
<td>ISO</td>
|
|
||||||
<td><input type="number" id="ISOInput" step="100" onkeydown="ISOInputHandler()" onmousedown="unsavedChanges.push('ISO')"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>AE Mode</td>
|
|
||||||
<td>
|
|
||||||
<select id="AEmodeDropDown" onmousedown="unsavedChanges.push('AutoExposure')">
|
|
||||||
<option value="Off">Off</option>
|
|
||||||
<option value="Continuous">Continuous</option>
|
|
||||||
<option value="OneShot">One-Shot</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>AE Type</td>
|
|
||||||
<td>
|
|
||||||
<select id="AEtypeDropDown" onmousedown="unsavedChanges.push('AutoExposure')">
|
|
||||||
<option value="">None</option>
|
|
||||||
<option value="Iris">Iris Only</option>
|
|
||||||
<option value="Shutter">Shutter Only</option>
|
|
||||||
<option value="Shutter,Iris">Shutter + Iris</option>
|
|
||||||
<option value="Iris,Shutter">Iris + Shutter</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<button style="margin: 2vh 0 0 3.5vw;" onclick="AEmodeInputHandler()">Set AE Mode</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
<div class="tableControl">
|
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
|
||||||
<h3>Contrast</h3>
|
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
|
||||||
<table>
|
onmousedown="CCInputHandler(0)" onkeydown="CCInputHandler(0)">0.00</span>
|
||||||
<tr>
|
|
||||||
<td>Pivot</td>
|
|
||||||
<td><input type="range" max="1" min="0" step="0.001" id="CCcontrastPivotRange" oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {pivot: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC4')});"></td>
|
|
||||||
<td>
|
|
||||||
<span id="CCcontrastPivotLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">0</span>
|
|
||||||
</td>
|
|
||||||
<td rowspan="2">
|
|
||||||
<button class="CCResetButton circleButton" onclick="resetCC(4)" title="Reset Contrast">⟳</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Adjust</td>
|
|
||||||
<td><input type="range" max="2" min="0" step="0.001" id="CCcontrastAdjustRange" oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {adjust: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC4')});"></td>
|
|
||||||
<td>
|
|
||||||
<span id="CCcontrastAdjustLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">0</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="tableControl">
|
|
||||||
<h3>Color</h3>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>Hue</td>
|
|
||||||
<td><input type="range" max="1" min="-1" step="0.001" id="CChueRange" oninput="cameras[ci].PUTdata('/colorCorrection/color', {hue: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC5')});"></td>
|
|
||||||
<td>
|
|
||||||
<span id="CCcolorHueLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">0</span>
|
|
||||||
</td>
|
|
||||||
<td rowspan="3">
|
|
||||||
<button class="CCResetButton circleButton" onclick="resetCC(5)" title="Reset Color">⟳</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Saturation</td>
|
|
||||||
<td><input type="range" max="2" min="0" step="0.001" id="CCsaturationRange" oninput="cameras[ci].PUTdata('/colorCorrection/color', {saturation: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC5')});"></td>
|
|
||||||
<td>
|
|
||||||
<span id="CCcolorSatLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">0</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Luma Contribution</td>
|
|
||||||
<td><input type="range" max="1" min="0" step="0.001" id="CClumaContributionRange" oninput="cameras[ci].PUTdata('/colorCorrection/lumaContribution', {lumaContribution: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter((e) => {return !e.includes('CC5')});"></td>
|
|
||||||
<td>
|
|
||||||
<span id="CCcolorLCLabel" contenteditable="plaintext-only" onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">0</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<button class="ccApplyBtn" onclick="setCCFromUI(0)" title="Apply">➚</button>
|
||||||
|
<button class="ccResetBtn" onclick="resetCC(0)" title="Reset">⟳</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Footer Div -->
|
<!-- Gamma -->
|
||||||
<div class="flexContainerH" id="footerContainer" onclick="this.style.display='none'">
|
<div class="ccRow">
|
||||||
<div id="footerLeft">
|
<span class="ccRowLabel">Gamma</span>
|
||||||
<span class="">(v 1.4.2)</span>
|
<div class="ccChannels">
|
||||||
<span id="activeElementSpan"></span>
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
|
||||||
|
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
|
||||||
|
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
|
||||||
|
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
|
||||||
|
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(1)" onkeydown="CCInputHandler(1)">0.00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="ccApplyBtn" onclick="setCCFromUI(1)" title="Apply">➚</button>
|
||||||
|
<button class="ccResetBtn" onclick="resetCC(1)" title="Reset">⟳</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Gain -->
|
||||||
|
<div class="ccRow">
|
||||||
|
<span class="ccRowLabel">Gain</span>
|
||||||
|
<div class="ccChannels">
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
|
||||||
|
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
|
||||||
|
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
|
||||||
|
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
|
||||||
|
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(2)" onkeydown="CCInputHandler(2)">1.00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="ccApplyBtn" onclick="setCCFromUI(2)" title="Apply">➚</button>
|
||||||
|
<button class="ccResetBtn" onclick="resetCC(2)" title="Reset">⟳</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Offset -->
|
||||||
|
<div class="ccRow">
|
||||||
|
<span class="ccRowLabel">Offset</span>
|
||||||
|
<div class="ccChannels">
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-luma)">L</span>
|
||||||
|
<span class="CClumaLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-red)">R</span>
|
||||||
|
<span class="CCredLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-green)">G</span>
|
||||||
|
<span class="CCgreenLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
||||||
|
</div>
|
||||||
|
<div class="ccChannel">
|
||||||
|
<span class="ccChLabel" style="color: var(--ch-blue)">B</span>
|
||||||
|
<span class="CCblueLabel ccVal" contenteditable="plaintext-only"
|
||||||
|
onmousedown="CCInputHandler(3)" onkeydown="CCInputHandler(3)">0.00</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="ccApplyBtn" onclick="setCCFromUI(3)" title="Apply">➚</button>
|
||||||
|
<button class="ccResetBtn" onclick="resetCC(3)" title="Reset">⟳</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footerLinks">
|
|
||||||
<span><a id="documentationLink" href="#" target="_blank">YAML Documentation</a></span>
|
<!-- Contrast -->
|
||||||
<span><a id="mediaManagerLink" href="#" target="_blank">Web Media Manager</a></span>
|
<div class="sectionDivider"><span>Contrast</span></div>
|
||||||
<span><a id="githubLink" href="https://github.com/DylanSpeiser/BM-Camera-Control-WebUI" target="_blank">GitHub</a></span>
|
<div class="sliderGroup">
|
||||||
|
<div class="sliderRow">
|
||||||
|
<label class="sliderLabel">Pivot</label>
|
||||||
|
<input type="range" class="slider" max="1" min="0" step="0.001" id="CCcontrastPivotRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {pivot: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC4'));">
|
||||||
|
<span id="CCcontrastPivotLabel" class="sliderVal" contenteditable="plaintext-only"
|
||||||
|
onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">0.50</span>
|
||||||
|
</div>
|
||||||
|
<div class="sliderRow">
|
||||||
|
<label class="sliderLabel">Adjust</label>
|
||||||
|
<input type="range" class="slider" max="2" min="0" step="0.001" id="CCcontrastAdjustRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/colorCorrection/contrast', {adjust: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC4'));">
|
||||||
|
<span id="CCcontrastAdjustLabel" class="sliderVal" contenteditable="plaintext-only"
|
||||||
|
onkeydown="CCInputHandler(4)" onmousedown="CCInputHandler(4)">50%</span>
|
||||||
|
</div>
|
||||||
|
<button class="ccResetBtn" onclick="resetCC(4)" title="Reset Contrast">⟳</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</body>
|
<!-- Hue / Sat / LC -->
|
||||||
</html>
|
<div class="sectionDivider"><span>Color</span></div>
|
||||||
|
<div class="sliderGroup">
|
||||||
|
<div class="sliderRow">
|
||||||
|
<label class="sliderLabel">Hue</label>
|
||||||
|
<input type="range" class="slider" max="1" min="-1" step="0.001" id="CChueRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/colorCorrection/color', {hue: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC5'));">
|
||||||
|
<span id="CCcolorHueLabel" class="sliderVal" contenteditable="plaintext-only"
|
||||||
|
onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">180°</span>
|
||||||
|
</div>
|
||||||
|
<div class="sliderRow">
|
||||||
|
<label class="sliderLabel">Saturation</label>
|
||||||
|
<input type="range" class="slider" max="2" min="0" step="0.001" id="CCsaturationRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/colorCorrection/color', {saturation: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC5'));">
|
||||||
|
<span id="CCcolorSatLabel" class="sliderVal" contenteditable="plaintext-only"
|
||||||
|
onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">50%</span>
|
||||||
|
</div>
|
||||||
|
<div class="sliderRow">
|
||||||
|
<label class="sliderLabel">Luma Contrib.</label>
|
||||||
|
<input type="range" class="slider" max="1" min="0" step="0.001" id="CClumaContributionRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/colorCorrection/lumaContribution', {lumaContribution: parseFloat(this.value)}); unsavedChanges = unsavedChanges.filter(e => !e.includes('CC5'));">
|
||||||
|
<span id="CCcolorLCLabel" class="sliderVal" contenteditable="plaintext-only"
|
||||||
|
onkeydown="CCInputHandler(5)" onmousedown="CCInputHandler(5)">100%</span>
|
||||||
|
</div>
|
||||||
|
<button class="ccResetBtn" onclick="resetCC(5)" title="Reset Color">⟳</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== LENS PANEL ===== -->
|
||||||
|
<section id="tab-lens" class="tabPanel">
|
||||||
|
<div class="lensGrid">
|
||||||
|
|
||||||
|
<div class="lensCard">
|
||||||
|
<span class="lensCardLabel">FOCUS</span>
|
||||||
|
<input type="range" class="vertSlider" orient="vertical" max="1" min="0" step="0.001"
|
||||||
|
id="focusRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/lens/focus', {normalised: parseFloat(this.value)})">
|
||||||
|
<button class="lensActionBtn" onclick="cameras[ci].doAutoFocus()">AF</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lensCard">
|
||||||
|
<span class="lensCardLabel">IRIS</span>
|
||||||
|
<input type="range" class="vertSlider" orient="vertical" max="1" min="0" step="0.001"
|
||||||
|
id="irisRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/lens/iris', {normalised: parseFloat(this.value)})">
|
||||||
|
<span id="apertureStopsLabel" class="lensValueLabel">f/—</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="lensCard">
|
||||||
|
<span class="lensCardLabel">ZOOM</span>
|
||||||
|
<input type="range" class="vertSlider" orient="vertical" max="1" min="0" step="0.001"
|
||||||
|
id="zoomRange"
|
||||||
|
oninput="cameras[ci].PUTdata('/lens/zoom', {normalised: parseFloat(this.value)})">
|
||||||
|
<span id="zoomMMLabel" class="lensValueLabel">—mm</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== SYSTEM PANEL ===== -->
|
||||||
|
<section id="tab-system" class="tabPanel">
|
||||||
|
|
||||||
|
<div class="sysGrid">
|
||||||
|
<div class="sysCard">
|
||||||
|
<h3 class="sysCardTitle">Connection</h3>
|
||||||
|
<div class="sysRow">
|
||||||
|
<label class="settingLabel">Hostname / IP</label>
|
||||||
|
<input type="text" id="hostnameInput" class="settingInput hostnameInput"
|
||||||
|
placeholder="Camera-Name.local"
|
||||||
|
onclick="hostnameInputHandler()" onkeydown="hostnameInputHandler()">
|
||||||
|
</div>
|
||||||
|
<div class="sysRow sysRow--inline">
|
||||||
|
<input type="checkbox" id="secureCheckbox">
|
||||||
|
<label for="secureCheckbox" class="settingLabel" style="cursor:pointer">Use HTTPS</label>
|
||||||
|
<button class="actionBtn" onclick="initCamera()">Connect</button>
|
||||||
|
<span id="connectionErrorSpan" class="statusNote"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sysCard">
|
||||||
|
<h3 class="sysCardTitle">Presets</h3>
|
||||||
|
<div class="sysRow sysRow--inline">
|
||||||
|
<select id="presetsDropDown" class="settingSelect" style="flex:1"
|
||||||
|
onmousedown="unsavedChanges.push('presets')" onchange="presetInputHandler()">
|
||||||
|
</select>
|
||||||
|
<button class="actionBtn" onclick="presetInputHandler()">Restore Preset</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sysCard sysCard--full">
|
||||||
|
<h3 class="sysCardTitle">Manual API</h3>
|
||||||
|
<div class="sysRow sysRow--inline">
|
||||||
|
<label class="settingLabel">
|
||||||
|
<input type="radio" id="requestTypeGET" value="GET" name="manualRequestType" checked>
|
||||||
|
GET
|
||||||
|
</label>
|
||||||
|
<label class="settingLabel">
|
||||||
|
<input type="radio" id="requestTypePUT" value="PUT" name="manualRequestType">
|
||||||
|
PUT
|
||||||
|
</label>
|
||||||
|
<input type="text" id="manualRequestEndpointLabel" class="settingInput" placeholder="/endpoint">
|
||||||
|
<input type="text" id="manualRequestBodyLabel" class="settingInput" placeholder="JSON body">
|
||||||
|
<button class="actionBtn" onclick="manualAPICall()">Send</button>
|
||||||
|
</div>
|
||||||
|
<p id="manualRequestResponseP" class="apiResponse">
|
||||||
|
See <a href="https://documents.blackmagicdesign.com/DeveloperManuals/RESTAPIforBlackmagicCameras.pdf?_v=1696143610000"
|
||||||
|
target="_blank">BM REST API docs</a> for available endpoints.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sysCard">
|
||||||
|
<h3 class="sysCardTitle">Links</h3>
|
||||||
|
<div class="linkRow">
|
||||||
|
<a id="documentationLink" href="#" target="_blank" class="sysLink">YAML Docs</a>
|
||||||
|
<a id="mediaManagerLink" href="#" target="_blank" class="sysLink">Web Media Manager</a>
|
||||||
|
<a href="https://github.com/DylanSpeiser/BM-Camera-Control-WebUI" target="_blank" class="sysLink">GitHub</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Status Bar -->
|
||||||
|
<footer id="statusBar">
|
||||||
|
<span id="activeElementSpan"></span>
|
||||||
|
<span class="version">v1.5.0</span>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue