[no ci] Package: update wifibroadcast-ng (#1788)

pull/1789/head
viktorxda 2025-04-07 15:18:24 +02:00 committed by GitHub
parent accf5a91ba
commit cf526507ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 4 additions and 687 deletions

View File

@ -1,127 +0,0 @@
diff --git a/src/tx.cpp b/src/tx.cpp
index 4dcfd87..b1a9d94 100644
--- a/src/tx.cpp
+++ b/src/tx.cpp
@@ -1347,7 +1347,7 @@ int open_control_fd(int control_port)
void local_loop(int argc, char* const* argv, int optind, int srv_port, int rcv_buf, int log_interval,
int udp_port, int debug_port, int k, int n, const string &keypair, int fec_timeout,
uint64_t epoch, uint32_t channel_id, uint32_t fec_delay, bool use_qdisc, uint32_t fwmark,
- radiotap_header_t &radiotap_header, uint8_t frame_type, int control_port, bool mirror)
+ radiotap_header_t &radiotap_header, uint8_t frame_type, int control_port, int socket_type, bool mirror)
{
vector<int> rx_fd;
vector<string> wlans;
@@ -1357,7 +1357,7 @@ void local_loop(int argc, char* const* argv, int optind, int srv_port, int rcv_b
for(int i = 0; optind + i < argc; i++)
{
int bind_port = udp_port != 0 ? udp_port + i : 0;
- int fd = open_udp_socket_for_rx(bind_port, rcv_buf);
+ int fd = open_udp_socket_for_rx(bind_port, rcv_buf, INADDR_ANY, socket_type);
if (udp_port == 0)
{
@@ -1371,7 +1371,15 @@ void local_loop(int argc, char* const* argv, int optind, int srv_port, int rcv_b
bind_port = ntohs(saddr.sin_port);
IPC_MSG("%" PRIu64 "\tLISTEN_UDP\t%d:%x\n", get_time_ms(), bind_port, i);
}
- WFB_INFO("Listen on %d for %s\n", bind_port, argv[optind + i]);
+
+ if (socket_type == SOCK_SEQPACKET)
+ {
+ WFB_INFO("Listen on %s for %s\n", UNIX_SOCKET, argv[optind + i]);
+ }
+ else
+ {
+ WFB_INFO("Listen on %d for %s\n", bind_port, argv[optind + i]);
+ }
rx_fd.push_back(fd);
wlans.push_back(string(argv[optind + i]));
}
@@ -1499,8 +1507,9 @@ int main(int argc, char * const *argv)
bool use_qdisc = false;
uint32_t fwmark = 0;
tx_mode_t tx_mode = LOCAL;
+ int socket_type = SOCK_DGRAM;
- while ((opt = getopt(argc, argv, "dI:K:k:n:u:p:F:l:B:G:S:L:M:N:D:T:i:e:R:f:mVQP:C:")) != -1) {
+ while ((opt = getopt(argc, argv, "dI:K:k:n:u:p:F:l:B:G:S:L:M:N:D:T:i:e:R:f:mVQUP:C:")) != -1) {
switch (opt) {
case 'I':
tx_mode = INJECTOR;
@@ -1599,10 +1608,13 @@ int main(int argc, char * const *argv)
case 'C':
control_port = atoi(optarg);
break;
+ case 'U':
+ socket_type = SOCK_SEQPACKET;
+ break;
default: /* '?' */
show_usage:
WFB_INFO("Local TX: %s [-K tx_key] [-k RS_K] [-n RS_N] [-u udp_port] [-R rcv_buf] [-p radio_port] [-F fec_delay] [-B bandwidth] [-G guard_interval] [-S stbc] [-L ldpc] [-M mcs_index] [-N VHT_NSS]\n"
- " [-T fec_timeout] [-l log_interval] [-e epoch] [-i link_id] [-f { data | rts }] [-m] [-V] [-Q] [-P fwmark] [-C control_port] interface1 [interface2] ...\n",
+ " [-T fec_timeout] [-l log_interval] [-e epoch] [-i link_id] [-f { data | rts }] [-m] [-V] [-Q] [-U] [-P fwmark] [-C control_port] interface1 [interface2] ...\n",
argv[0]);
WFB_INFO("TX distributor: %s -d [-K tx_key] [-k RS_K] [-n RS_N] [-u udp_port] [-R rcv_buf] [-p radio_port] [-F fec_delay] [-B bandwidth] [-G guard_interval] [-S stbc] [-L ldpc] [-M mcs_index] [-N VHT_NSS]\n"
" [-T fec_timeout] [-l log_interval] [-e epoch] [-i link_id] [-f { data | rts }] [-m] [-V] [-Q] [-P fwmark] [-C control_port] host1:port1,port2,... [host2:port1,port2,...] ...\n",
@@ -1658,7 +1670,7 @@ int main(int argc, char * const *argv)
local_loop(argc, argv, optind, srv_port, rcv_buf, log_interval,
udp_port, debug_port, k, n, keypair, fec_timeout,
epoch, channel_id, fec_delay, use_qdisc, fwmark,
- radiotap_header, frame_type, control_port, mirror);
+ radiotap_header, frame_type, control_port, socket_type, mirror);
break;
diff --git a/src/wifibroadcast.cpp b/src/wifibroadcast.cpp
index d082958..a1f9f76 100644
--- a/src/wifibroadcast.cpp
+++ b/src/wifibroadcast.cpp
@@ -18,6 +18,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
@@ -65,9 +66,26 @@ uint64_t get_time_us(void) // in microseconds
int open_udp_socket_for_rx(int port, int rcv_buf_size, uint32_t bind_addr, int socket_type, int socket_protocol)
{
struct sockaddr_in saddr;
- int fd = socket(AF_INET, socket_type, socket_protocol);
+ struct sockaddr_un uaddr;
+
+ int socket_family = (socket_type == SOCK_SEQPACKET) ? AF_UNIX : AF_INET;
+ int fd = socket(socket_family, socket_type, socket_protocol);
if (fd < 0) throw runtime_error(string_format("Error opening socket: %s", strerror(errno)));
+ if (socket_family == AF_UNIX)
+ {
+ memset(&uaddr, '\0', sizeof(uaddr));
+ uaddr.sun_family = socket_family;
+ strcpy(uaddr.sun_path, UNIX_SOCKET);
+
+ if (::connect(fd, (struct sockaddr *) &uaddr, sizeof(uaddr)) < 0)
+ {
+ close(fd);
+ throw runtime_error(string_format("Unable to connect to %s : %s", uaddr.sun_path, strerror(errno)));
+ }
+ return fd;
+ }
+
const int optval = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(optval)) !=0)
{
diff --git a/src/wifibroadcast.hpp b/src/wifibroadcast.hpp
index 0947a76..9bc8e1d 100644
--- a/src/wifibroadcast.hpp
+++ b/src/wifibroadcast.hpp
@@ -286,6 +286,8 @@ typedef struct {
#define IPC_MSG_SEND() fflush(stdout)
#endif
+#define UNIX_SOCKET "/tmp/wfb_stream"
+
int open_udp_socket_for_rx(int port, int rcv_buf_size, uint32_t bind_addr = INADDR_ANY, int socket_type = SOCK_DGRAM, int socket_protocol = 0);
uint64_t get_time_ms(void);
uint64_t get_time_us(void);

View File

@ -1,17 +0,0 @@
wireless:
txpower: 1
channel: 161
width: 20
broadcast:
mcs_index: 2
tun_index: 1
fec_k: 8
fec_n: 12
stbc: 0
ldpc: 0
link_id: 7669206
jumbo_frames: false
telemetry:
router: msposd
serial: ttyS2
osd_fps: 20

View File

@ -1,162 +0,0 @@
#!/bin/sh
wfb_key=/etc/drone.key
wfb_dev=wlan0
echo_log() {
echo "$1" | logger -s -t wifibroadcast
}
wfb_yaml() {
if [ -e "$1" ]; then
while IFS=: read -r key value; do
key=$(eval echo "$key")
value=$(eval echo "$value")
export "$key=$value"
done < "$1"
fi
}
load_config() {
wfb_yaml /etc/wfb.yaml
[ ! -e "$wfb_key" ] && wfb_key=/rom/etc/drone.key
}
load_modules() {
for card in $(lsusb | awk '{print $6}' | uniq); do
case "$card" in
"0bda:8812" | "0bda:881a" | "0b05:17d2" | "2357:0101" | "2604:0012")
driver=88XXau
modprobe "$driver"
;;
"0bda:a81a")
driver=8812eu
modprobe "$driver" rtw_regd_src=1 rtw_tx_pwr_by_rate=0 rtw_tx_pwr_lmt_enable=0
;;
"0bda:f72b" | "0bda:b733")
driver=8733bu
modprobe "$driver" rtw_regd_src=1 rtw_tx_pwr_by_rate=0 rtw_tx_pwr_lmt_enable=0
;;
esac
done
if [ -z "$driver" ]; then
echo_log "Wireless module not detected!"
exit 1
fi
echo_log "Detected driver: $driver"
if ! ifconfig "$wfb_dev" up; then
echo_log "Wireless driver not found!"
exit 1
fi
}
load_interface() {
iw "$wfb_dev" set monitor none
[ "$width" = "40" ] && wfb_width=HT40+ || wfb_width=HT20
iw "$wfb_dev" set channel "$channel" "$wfb_width"
iw reg set 00
[ "$driver" = "88XXau" ] && wfb_power=$((txpower * -100)) || wfb_power=$((txpower * 50))
iw "$wfb_dev" set txpower fixed "$wfb_power"
if [ "$jumbo_frames" = "true" ]; then
ip link set dev "$wfb_dev" mtu 3994
fi
}
start_broadcast() {
echo_log "Starting wfb_tx"
wfb_tx -K "$wfb_key" -M "$mcs_index" -B "$width" -k "$fec_k" -n "$fec_n" \
-S "$stbc" -L "$ldpc" -i "$link_id" -C 8000 -G short "$wfb_dev" &> /dev/null &
}
start_tunnel() {
echo_log "Starting wfb_tun"
wfb_rx -K "$wfb_key" -i "$link_id" -p 160 -u 5800 "$wfb_dev" &> /dev/null &
wfb_tx -K "$wfb_key" -M "$tun_index" -B "$width" -k "$fec_k" -n "$fec_n" \
-S "$stbc" -L "$ldpc" -i "$link_id" -p 32 -u 5801 "$wfb_dev" &> /dev/null &
wfb_tun -a 10.5.0.10/24 > /dev/null &
}
start_telemetry() {
chipset=$(ipcinfo -c)
[ "$chipset" = "ssc33x" ] && devmem 0x1F207890 16 0x8
echo_log "Starting $router"
if [ "$router" = "mavfwd" ]; then
mavfwd -b 115200 -c 8 -p 100 -a 15 -t -m /dev/"$serial" \
-i 0.0.0.0:14550 -o 10.5.0.1:14551 > /dev/null &
else
if [ "$router" = "msposd" ]; then
size=$(curl -s localhost/api/v1/config.json | jsonfilter -e "@.video0.size")
fi
msposd -b 115200 -c 8 -r "$osd_fps" -m /dev/"$serial" \
-o 10.5.0.1:14551 -z "$size" > /dev/null &
fi
}
video_settings() {
sensor=$(ipcinfo -s)
cli -s .isp.sensorConfig /etc/sensors/"$sensor"_fpv.bin
cli -s .isp.exposure 5
cli -s .video0.fps 60
cli -s .video0.bitrate 8192
cli -s .video0.codec h265
cli -s .video0.rcMode cbr
cli -s .outgoing.enabled true
cli -s .outgoing.server udp://0.0.0.0:5600
cli -s .fpv.enabled true
cli -s .fpv.noiseLevel 1
}
start() {
load_config
load_modules
load_interface
if [ ! -e /etc/system.ok ]; then
sleep 1
video_settings
touch /etc/system.ok
killall -1 majestic
fi
stop
start_broadcast
start_tunnel
start_telemetry
}
stop() {
for process in wfb_rx wfb_tx wfb_tun msposd mavfwd; do
if [ -n "$(pidof $process)" ]; then
echo_log "Stopping $process"
killall -q "$process"
fi
done
}
case "$1" in
start|stop)
$1
;;
resetv)
cp -f /rom/etc/majestic.yaml /etc/majestic.yaml
video_settings
;;
resetw)
cp -f /rom/etc/wfb.yaml /etc/wfb.yaml
start
;;
*)
echo "Usage: $0 {start|stop|resetv|resetw}"
exit 1
;;
esac

View File

@ -5,18 +5,12 @@
################################################################################ ################################################################################
define WIFIBROADCAST_EXT_INSTALL_TARGET_CMDS define WIFIBROADCAST_EXT_INSTALL_TARGET_CMDS
$(INSTALL) -m 755 -d $(TARGET_DIR)/etc
$(INSTALL) -m 644 -t $(TARGET_DIR)/etc $(WIFIBROADCAST_NG_PKGDIR)/files/wfb.yaml
$(INSTALL) -m 755 -d $(TARGET_DIR)/etc/init.d $(INSTALL) -m 755 -d $(TARGET_DIR)/etc/init.d
$(INSTALL) -m 755 -t $(TARGET_DIR)/etc/init.d $(WIFIBROADCAST_EXT_PKGDIR)/files/S75generate_vtx_info $(INSTALL) -m 755 -t $(TARGET_DIR)/etc/init.d $(WIFIBROADCAST_EXT_PKGDIR)/files/S991temp_monitor
$(INSTALL) -m 755 -t $(TARGET_DIR)/etc/init.d $(WIFIBROADCAST_NG_PKGDIR)/files/S991temp_monitor
$(INSTALL) -m 755 -d $(TARGET_DIR)/usr/bin $(INSTALL) -m 755 -d $(TARGET_DIR)/usr/bin
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_EXT_PKGDIR)/files/generate_vtx_info $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_EXT_PKGDIR)/files/temp_monitor
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_NG_PKGDIR)/files/wifibroadcast $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_EXT_PKGDIR)/files/channels.sh
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_NG_PKGDIR)/files/temp_monitor
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_NG_PKGDIR)/files/channels.sh
endef endef
$(eval $(generic-package)) $(eval $(generic-package))

View File

@ -1,2 +0,0 @@
#!/bin/sh
yaml-cli -i /etc/wfb.yaml "$@"

View File

@ -4,7 +4,7 @@
# #
################################################################################ ################################################################################
WIFIBROADCAST_NG_VERSION = 6c5febf31c713beacd63221bfd74dfe30031c44d WIFIBROADCAST_NG_VERSION = fd3042d27d6f5ef9e1d4f4bf49a8efec307bc4d1
WIFIBROADCAST_NG_SITE = $(call github,svpcom,wfb-ng,$(WIFIBROADCAST_NG_VERSION)) WIFIBROADCAST_NG_SITE = $(call github,svpcom,wfb-ng,$(WIFIBROADCAST_NG_VERSION))
WIFIBROADCAST_NG_LICENSE = GPL-3.0 WIFIBROADCAST_NG_LICENSE = GPL-3.0
@ -30,11 +30,7 @@ define WIFIBROADCAST_NG_INSTALL_TARGET_CMDS
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(@D)/wfb_tx $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(@D)/wfb_tx
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(@D)/wfb_tx_cmd $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(@D)/wfb_tx_cmd
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(@D)/wfb_tun $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(@D)/wfb_tun
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_NG_PKGDIR)/files/wfb-cli
$(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_NG_PKGDIR)/files/wifibroadcast $(INSTALL) -m 755 -t $(TARGET_DIR)/usr/bin $(WIFIBROADCAST_NG_PKGDIR)/files/wifibroadcast
$(INSTALL) -m 755 -d $(TARGET_DIR)/var/www
$(INSTALL) -m 644 -t $(TARGET_DIR)/var/www $(WIFIBROADCAST_NG_PKGDIR)/www/*
endef endef
$(eval $(generic-package)) $(eval $(generic-package))

View File

@ -1,156 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>OpenIPC</title>
</head>
<body>
<header>
<a href="javascript:location.reload()"><img src="logo.webp" width="220dp"></a>
</header>
<section class="video-section">
<video id="preview" poster="/mjpeg"></video>
</section>
<div class="container">
<section class="form-section">
<form id="majestic-form">
<h3>Video</h3>
<div class="row">
<label for="fps">FPS:</label>
<select id="fps" name="majestic.video0.fps">
<option value="30">30</option>
<option value="60">60</option>
<option value="90">90</option>
<option value="120">120</option>
</select>
</div>
<div class="row">
<label for="codec">Codec:</label>
<select id="codec" name="majestic.video0.codec">
<option value="h264">H264</option>
<option value="h265">H265</option>
</select>
</div>
<div class="row">
<label for="bitrate">Bitrate:</label>
<select id="bitrate" name="majestic.video0.bitrate"></select>
</div>
<div class="row">
<label for="records">Record:</label>
<input type="checkbox" id="records" name="majestic.records.enabled">
</div>
<hr class="divider">
<h3>Audio</h3>
<div class="row">
<label for="records">Enabled:</label>
<input type="checkbox" id="audio" name="majestic.audio.enabled">
</div>
<div class="row">
<label for="srate">Sample:</label>
<select id="srate" name="majestic.audio.srate">
<option value="8000">8000</option>
<option value="48000">48000</option>
</select>
</div>
</form>
<hr class="divider">
<div class="button-container">
<button id="apply-video-button">Apply</button>
<button id="reset-video-button">Reset</button>
</div>
</section>
<section class="form-section">
<form id="wfb-form">
<h3>Wireless</h3>
<div class="row">
<label for="txpower">TX Power:</label>
<select id="txpower" name="wfb.wireless.txpower"></select>
</div>
<div class="row">
<label for="channel">Channel:</label>
<select id="channel" name="wfb.wireless.channel">
<option value="32">32</option>
<option value="36">36</option>
<option value="40">40</option>
<option value="44">44</option>
<option value="48">48</option>
<option value="52">52</option>
<option value="56">56</option>
<option value="60">60</option>
<option value="64">64</option>
<option value="68">68</option>
<option value="96">96</option>
<option value="100">100</option>
<option value="104">104</option>
<option value="108">108</option>
<option value="112">112</option>
<option value="116">116</option>
<option value="120">120</option>
<option value="124">124</option>
<option value="128">128</option>
<option value="132">132</option>
<option value="136">136</option>
<option value="140">140</option>
<option value="144">144</option>
<option value="149">149</option>
<option value="153">153</option>
<option value="157">157</option>
<option value="161">161</option>
<option value="165">165</option>
<option value="169">169</option>
<option value="173">173</option>
<option value="177">177</option>
</select>
</div>
<hr class="divider">
<h3>Broadcast</h3>
<div class="row">
<label for="mcs_index">MCS Index:</label>
<select id="mcs_index" name="wfb.broadcast.mcs_index"></select>
</div>
<hr class="divider">
<h3>Telemetry</h3>
<div class="row">
<label for="router">Router:</label>
<select id="router" name="wfb.telemetry.router">
<option value="msposd">msposd</option>
<option value="mavfwd">mavfwd</option>
</select>
</div>
<div class="row">
<label for="serial">Serial:</label>
<select id="serial" name="wfb.telemetry.serial">
<option value="ttyS0">ttyS0</option>
<option value="ttyS2">ttyS2</option>
</select>
</div>
</form>
<hr class="divider">
<div class="button-container">
<button id="apply-wfb-button">Apply</button>
<button id="reset-wfb-button">Reset</button>
</div>
</section>
</div>
<script src="js-yaml.min.js"></script>
<script src="script.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,96 +0,0 @@
let configData = { majestic: {}, wfb: {} };
async function loadYAML(url, setter) {
const response = await fetch(url);
const text = await response.text();
setter(jsyaml.load(text));
}
async function uploadYAML(data, location) {
const yamlData = jsyaml.dump(data);
await fetch('/upload', {
method: 'POST',
headers: { 'File-Location': location },
body: yamlData
});
}
async function runCommand(command) {
await fetch('/command', {
method: 'POST',
headers: { 'Run-Command': command }
});
}
if (navigator.userAgent.includes("Android")) {
document.querySelector('.video-section').style.display = 'none';
}
function syncForm(data, formPrefix, mode) {
Object.keys(data).forEach((section) => {
Object.keys(data[section]).forEach((key) => {
const field = document.getElementsByName(formPrefix + '.' + section + '.' + key)[0];
if (!field) {
return;
}
const isCheckbox = field.type === "checkbox";
if (mode === "setup") {
isCheckbox ?
field.checked = data[section][key] === true : field.value = data[section][key];
} else if (mode === "update") {
data[section][key] = isCheckbox ?
field.checked : (isNaN(field.value) ? field.value : Number(field.value));
}
});
});
}
function executeDelay(button, command) {
button.disabled = true;
runCommand(command);
setTimeout(() => {
button.disabled = false;
location.reload();
}, 1000);
}
function addClickListener(buttonId, command, configKey) {
document.getElementById(buttonId).addEventListener('click', function () {
if (configKey && configData[configKey]) {
syncForm(configData[configKey], configKey, "update");
var yamlPath = '/etc/' + configKey + '.yaml';
uploadYAML(configData[configKey], yamlPath);
}
executeDelay(this, command);
});
}
addClickListener('apply-video-button', 'killall -1 majestic', 'majestic');
addClickListener('apply-wfb-button', 'wifibroadcast start', 'wfb');
addClickListener('reset-video-button', 'wifibroadcast resetv');
addClickListener('reset-wfb-button', 'wifibroadcast resetw');
function populateSelect(id, start, end, step) {
const select = document.getElementById(id);
for (let i = start; i <= end; i += step) {
const option = document.createElement('option');
option.value = i;
option.textContent = i;
select.appendChild(option);
}
}
populateSelect('bitrate', 1024, 20480, 1024);
populateSelect('txpower', 1, 58, 1);
populateSelect('mcs_index', 0, 8, 1);
loadYAML('/etc/majestic.yaml', (data) => {
configData.majestic = data;
syncForm(configData.majestic, 'majestic', "setup");
});
loadYAML('/etc/wfb.yaml', (data) => {
configData.wfb = data;
syncForm(configData.wfb, 'wfb', "setup");
});

View File

@ -1,111 +0,0 @@
body {
font-family: Helvetica;
background-color: #1e1e1e;
color: #dcdcdc;
text-align: center;
}
header {
background-color: #252526;
border-radius: 4px;
box-shadow: 0 2px 10px #00000080;
color: white;
padding: 15px 0;
}
h3 {
font-size: 1.5rem;
margin-top: 1rem;
color: #dee2e6bf;
}
.container {
display: flex;
justify-content: space-evenly;
width: 100%;
}
.form-section .row {
display: flex;
justify-content: space-between;
margin: 10px 40px;
}
.form-section {
width: 45%;
margin: 20px 0;
padding-top: 20px;
background-color: #2d2d2d;
border-radius: 4px;
box-shadow: 0 2px 10px #00000080;
}
.form-section label {
width: 10vw;
margin: 10px 0 5px;
display: inline-block;
font-weight: bold;
color: #dee2e6;
font-size: 0.9rem;
}
.form-section select {
width: 16vw;
padding: 10px;
margin-bottom: 15px;
background-color: #3c3c3c;
border-radius: 4px;
border: 1px solid #555;
color: #dcdcdc;
font-size: 0.9rem;
}
.form-section input {
width: 16vw;
height: 26px;
margin-bottom: 25px;
}
.form-section button {
width: 20vw;
padding: 10px;
margin: 10px;
border: none;
cursor: pointer;
font-size: 1rem;
color: white;
border-radius: 4px;
box-shadow: 0 2px 10px #00000040;
}
.button-container {
display: flex;
flex-direction: column;
align-items: center;
margin: 25px;
}
#preview {
width: 100%;
max-width: 70%;
margin-top: 25px;
background-color: #000;
border-radius: 4px;
box-shadow: 0 2px 10px #00000080;
}
#apply-video-button, #apply-wfb-button {
background-color: #0d4aa5;
}
#apply-video-button:hover, #apply-wfb-button:hover {
background-color: #115fd4;
}
#reset-video-button, #reset-wfb-button {
background-color: #a11212;
}
#reset-video-button:hover, #reset-wfb-button:hover {
background-color: #cf1717;
}