From: Joseph Coffland Date: Wed, 19 Sep 2018 11:26:59 +0000 (-0700) Subject: Much improved camera support. X-Git-Url: https://git.buildbotics.com/?a=commitdiff_plain;h=7e61e89d2879592c7eb7abd2285d223c0b1f64a9;p=bbctrl-firmware Much improved camera support. --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 96c5c1f..8da888b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Buildbotics CNC Controller Firmware Changelog - Added support for 256 microstepping. - Smoother operation at 250k step rate by doubling clock as needed. - Indicators tab improvements. + - Much improved camera support. + - Camera hotpluging. ## v0.3.28 - Show step rate on motor configuration page. diff --git a/scripts/install.sh b/scripts/install.sh index c11b480..51f11a4 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -2,6 +2,7 @@ UPDATE_AVR=true UPDATE_PY=true +REBOOT=false while [ $# -gt 0 ]; do case "$1" in @@ -30,6 +31,21 @@ if [ $? -ne 0 ]; then mount -o remount,ro /boot fi + +# Fix camera +grep dwc_otg.fiq_fsm_mask /boot/cmdline.txt >/dev/null +if [ $? -ne 0 ]; then + mount -o remount,rw /boot && + sed -i 's/\(.*\)/\1 dwc_otg.fiq_fsm_mask=0x3/' /boot/cmdline.txt + mount -o remount,ro /boot + REBOOT=true +fi + +# Remove Hawkeye +if [ -e /etc/init.d/hawkeye ]; then + apt-get remove --purge -y hawkeye +fi + # Decrease boot delay sed -i 's/^TimeoutStartSec=.*$/TimeoutStartSec=1/' \ /etc/systemd/system/network-online.target.wants/networking.service @@ -45,3 +61,7 @@ if $UPDATE_PY; then fi sync + +if $REBOOT; then + reboot +fi diff --git a/src/jade/templates/control-view.jade b/src/jade/templates/control-view.jade index 9eae06f..899d754 100644 --- a/src/jade/templates/control-view.jade +++ b/src/jade/templates/control-view.jade @@ -191,7 +191,7 @@ script#control-view-template(type="text/x-template") input#tab5(type="radio", name="tabs") label(for="tab5") Indicators - input#tab6(type="radio", name="tabs", @click="load_video") + input#tab6(type="radio", name="tabs") label(for="tab6") Video section#content1.tab-content.pure-form @@ -294,13 +294,11 @@ script#control-view-template(type="text/x-template") section#content6.tab-content .video - img.reload(src="/images/reload.png", @click="reload_video", - title="Reload video") - img.mjpeg(:src="video_url", alt="Video camera not found.") - - p(style="padding:0 1em") - | Plug in a USB video camera to monitor your machine remotely. - | If it's not working, try clicking the reload button or unplugging - | and replugging the the camera. - | Here - | is a list of USB cameras that should work. + img(:src="video_url", alt="Video camera not found.") + + p + | Plug in a USB video camera to monitor your machine remotely. + + p + | Here + | is a list of USB cameras that should work. diff --git a/src/jade/templates/indicators.jade b/src/jade/templates/indicators.jade index 7081d2d..f9650c4 100644 --- a/src/jade/templates/indicators.jade +++ b/src/jade/templates/indicators.jade @@ -132,7 +132,7 @@ script#indicators-template(type="text/x-template") th Load 2 tr - td {{state['pd'] | percent 0}} + td {{state.pd | percent 0}} td 17 th Tool PWM th.separator @@ -144,63 +144,73 @@ script#indicators-template(type="text/x-template") table.pwr_fault tr - th.header(colspan=5) Power Faults + th.header(colspan=5) + | Power Faults + span(v-if="state.pwr_version")  (Version {{state.pwr_version}}) tr - th(:class="{error: state['under_voltage']}") Under voltage - td(:class="{error: state['under_voltage']}") - | {{state['under_voltage'] ? 'True' : 'False'}} + th(:class="{error: state.under_voltage}") Under voltage + td(:class="{error: state.under_voltage}") + | {{state.under_voltage ? 'True' : 'False'}} th.separator - th(:class="{error: state['over_voltage']}") Over voltage - td(:class="{error: state['over_voltage']}") - | {{state['over_voltage'] ? 'True' : 'False'}} + th(:class="{error: state.over_voltage}") Over voltage + td(:class="{error: state.over_voltage}") + | {{state.over_voltage ? 'True' : 'False'}} tr - th(:class="{error: state['over_current']}") Over current - td(:class="{error: state['over_current']}") - | {{state['over_current'] ? 'True' : 'False'}} + th(:class="{error: state.over_current}") Over current + td(:class="{error: state.over_current}") + | {{state.over_current ? 'True' : 'False'}} th.separator - th(:class="{error: state['sense_error']}") Sense error - td(:class="{error: state['sense_error']}") - | {{state['sense_error'] ? 'True' : 'False'}} + th(:class="{error: state.sense_error}", :title="sense_error") + | Sense error + td(:class="{error: state.sense_error}") + | {{state.sense_error ? 'True' : 'False'}} tr - th(:class="{error: state['shunt_overload']}") Shunt overload - td(:class="{error: state['shunt_overload']}") - | {{state['shunt_overload'] ? 'True' : 'False'}} + th(:class="{error: state.shunt_overload}") Shunt overload + td(:class="{error: state.shunt_overload}") + | {{state.shunt_overload ? 'True' : 'False'}} th.separator - th(:class="{error: state['motor_overload']}") Motor overload - td(:class="{error: state['motor_overload']}") - | {{state['motor_overload'] ? 'True' : 'False'}} + th(:class="{error: state.motor_overload}") Motor overload + td(:class="{error: state.motor_overload}") + | {{state.motor_overload ? 'True' : 'False'}} tr - th(:class="{error: state['load1_shutdown']}") Load 1 shutdown - td(:class="{error: state['load1_shutdown']}") - | {{state['load1_shutdown'] ? 'True' : 'False'}} + th(:class="{error: state.load1_shutdown}") Load 1 shutdown + td(:class="{error: state.load1_shutdown}") + | {{state.load1_shutdown ? 'True' : 'False'}} th.separator - th(:class="{error: state['load2_shutdown']}") Load 2 shutdown - td(:class="{error: state['load2_shutdown']}") - | {{state['load2_shutdown'] ? 'True' : 'False'}} + th(:class="{error: state.load2_shutdown}") Load 2 shutdown + td(:class="{error: state.load2_shutdown}") + | {{state.load2_shutdown ? 'True' : 'False'}} + tr + th(:class="{error: state.motor_under_voltage}") Motor under volt + td(:class="{error: state.motor_under_voltage}") + | {{state.motor_under_voltage ? 'True' : 'False'}} + th.separator + th + td table.measurements tr th.header(colspan=5) Measurements tr - td {{state['vin'] | fixed 1}} V + td {{state.vin | fixed 1}} V th Input th.separator - td {{state['vout'] | fixed 1}} V + td {{state.vout | fixed 1}} V th Output tr - td {{state['motor'] | fixed 2}} A + td {{state.motor | fixed 2}} A th Motor th.separator - td {{state['temp'] | fixed 0}} ℃ + td {{state.temp | fixed 0}} ℃ th Temp tr - td {{state['load1'] | fixed 2}} A + td {{state.load1 | fixed 2}} A th Load 1 th.separator - td {{state['load2'] | fixed 2}} A + td {{state.load2 | fixed 2}} A th Load 2 tr @@ -218,14 +228,14 @@ script#indicators-template(type="text/x-template") td {{modbus_status}} th Status th.separator - td {{state['hz']}} Hz + td {{state.hz}} Hz th Frequency tr - td {{state['s']}} RPM + td {{state.s}} RPM th Speed th.separator - td {{state['hc']}} A + td {{state.hc}} A th Current h2 DB25 breakout box diff --git a/src/js/control-view.js b/src/js/control-view.js index 0ebbb84..36d4ef2 100644 --- a/src/js/control-view.js +++ b/src/js/control-view.js @@ -60,8 +60,8 @@ module.exports = { {x: false, y: false, z: false, a: false, b: false, c: false}, axis_position: 0, jog_adjust: 100, - video_url: '', - deleteGCode: false + video_url: '/api/video?nocache=' + Math.random(), + deleteGCode: false, } }, @@ -364,22 +364,6 @@ module.exports = { var data = {}; data[axis + 'pl'] = x; this.send(JSON.stringify(data)); - }, - - - load_video: function () { - this.video_url = '//' + document.location.hostname + ':8000/stream/0?=' + - Math.random(); - }, - - - reload_video: function () { - if (typeof this.lastVideoReset != 'undefined' && - Date.now() - this.lastVideoReset < 15000) return; - - this.lastVideoReset = Date.now(); - api.put('video/reload'); - setTimeout(this.load_video, 4000); } } } diff --git a/src/js/indicators.js b/src/js/indicators.js index af34033..f116335 100644 --- a/src/js/indicators.js +++ b/src/js/indicators.js @@ -36,7 +36,20 @@ module.exports = { computed: { - modbus_status: function () {return modbus.status_to_string(this.state.mx)} + modbus_status: function () {return modbus.status_to_string(this.state.mx)}, + + + sense_error: function () { + var error = ''; + + if (this.state.motor_voltage_sense_error) error += 'Motor voltage\n'; + if (this.state.motor_current_sense_error) error += 'Motor current\n'; + if (this.state.load1_sense_error) error += 'Load 1\n'; + if (this.state.load2_sense_error) error += 'Load 2\n'; + if (this.state.vdd_current_sense_error) error += 'Vdd current\n'; + + return error; + } }, @@ -143,6 +156,6 @@ module.exports = { var state = this.state[output + 'os']; return this.get_tooltip(mode, active, state); - }, + } } } diff --git a/src/pwr/Makefile b/src/pwr/Makefile index b3c9ce2..4fed54b 100644 --- a/src/pwr/Makefile +++ b/src/pwr/Makefile @@ -2,7 +2,6 @@ PROJECT = bbctrl-pwr-firmware MCU = attiny1634 CLOCK = 8000000 -VERSION = 0.0.1 # Compile flags CC = avr-gcc @@ -13,7 +12,7 @@ CFLAGS += -Wall -Werror CFLAGS += -std=gnu99 -DF_CPU=$(CLOCK)UL -O3 CFLAGS += -funsigned-bitfields -fpack-struct -fshort-enums -funsigned-char CFLAGS += -MD -MP -MT $@ -MF build/dep/$(@F).d -CFLAGS += -I. -DVERSION=\"$(VERSION)\" +CFLAGS += -I. # Linker flags LDFLAGS += $(COMMON) -Wl,-u,vfprintf -lprintf_flt -lm diff --git a/src/pwr/config.h b/src/pwr/config.h index 759ef9c..695df79 100644 --- a/src/pwr/config.h +++ b/src/pwr/config.h @@ -30,6 +30,9 @@ #include "pins.h" +#define VERSION 1 + + // Pins enum { AREF_PIN = PORT_A << 3, @@ -107,7 +110,7 @@ enum { // Addresses 0x60 to 0x67 #define I2C_ADDR 0x60 -#define I2C_MASK 0b00000111 +#define I2C_MASK 0b00001111 #define I2C_ERROR_BM (1 << TWBE) #define I2C_DATA_INT_BM (1 << TWDIF) @@ -125,11 +128,13 @@ typedef enum { LOAD2_REG, VDD_REG, FLAGS_REG, + VERSION_REG, NUM_REGS } regs_t; enum { + // Fatal UNDER_VOLTAGE_FLAG = 1 << 0, OVER_VOLTAGE_FLAG = 1 << 1, OVER_CURRENT_FLAG = 1 << 2, @@ -138,8 +143,16 @@ enum { MOTOR_OVERLOAD_FLAG = 1 << 5, // Non fatal - LOAD1_SHUTDOWN_FLAG = 1 << 6, - LOAD2_SHUTDOWN_FLAG = 1 << 7, + LOAD1_SHUTDOWN_FLAG = 1 << 6, + LOAD2_SHUTDOWN_FLAG = 1 << 7, + MOTOR_UNDER_VOLTAGE_FLAG = 1 << 8, + + // Sense errors + MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9, + MOTOR_CURRENT_SENSE_ERROR_FLAG = 1 << 10, + LOAD1_SENSE_ERROR_FLAG = 1 << 11, + LOAD2_SENSE_ERROR_FLAG = 1 << 12, + VDD_CURRENT_SENSE_ERROR_FLAG = 1 << 13, }; diff --git a/src/pwr/main.c b/src/pwr/main.c index b68f26a..f48c377 100644 --- a/src/pwr/main.c +++ b/src/pwr/main.c @@ -386,29 +386,31 @@ static void shutdown() { } -static void validate_measurements() { +static uint16_t validate_measurements() { const float max_voltage = 0.99 * convert_voltage(0x3ff); const float max_current = 0.99 * convert_current(0x3ff); + uint16_t flags = 0; - if (max_voltage < regs[VOUT_REG] || - max_current < regs[MOTOR_REG] || - max_current < regs[LOAD1_REG] || - max_current < regs[LOAD2_REG] || - max_current < regs[VDD_REG]) { - regs[FLAGS_REG] |= SENSE_ERROR_FLAG; - shutdown(); - } + if (max_voltage < regs[VOUT_REG]) flags |= MOTOR_VOLTAGE_SENSE_ERROR_FLAG; + if (max_current < regs[MOTOR_REG]) flags |= MOTOR_CURRENT_SENSE_ERROR_FLAG; + if (max_current < regs[LOAD1_REG]) flags |= LOAD1_SENSE_ERROR_FLAG; + if (max_current < regs[LOAD2_REG]) flags |= LOAD2_SENSE_ERROR_FLAG; + if (max_current < regs[VDD_REG]) flags |= VDD_CURRENT_SENSE_ERROR_FLAG; + + return flags ? SENSE_ERROR_FLAG | flags : 0; } int main() { wdt_enable(WDTO_8S); + regs[VERSION_REG] = VERSION; + init(); adc_conversion(); // Start ADC validate_input_voltage(); charge_caps(); - validate_measurements(); + uint16_t fatal = validate_measurements(); while (true) { wdt_reset(); @@ -418,18 +420,22 @@ int main() { update_shunt_power(vout, vnom); - // Check fault conditions - uint16_t flags = 0; - if (vin < VOLTAGE_MIN) flags |= UNDER_VOLTAGE_FLAG; - if (VOLTAGE_MAX < vin || VOLTAGE_MAX < vout) flags |= OVER_VOLTAGE_FLAG; - if (CURRENT_MAX < get_total_current()) flags |= OVER_CURRENT_FLAG; - if (shunt_overload) flags |= SHUNT_OVERLOAD_FLAG; - if (MOTOR_SHUTDOWN_THRESH <= motor_overload) flags |= MOTOR_OVERLOAD_FLAG; - if (loads[0].shutdown) flags |= LOAD1_SHUTDOWN_FLAG; - if (loads[1].shutdown) flags |= LOAD2_SHUTDOWN_FLAG; - - regs[FLAGS_REG] = flags; - if (flags & FATAL_FLAG_MASK) shutdown(); + // Fatal conditions + if (vin < VOLTAGE_MIN) fatal |= UNDER_VOLTAGE_FLAG; + if (VOLTAGE_MAX < vin || VOLTAGE_MAX < vout) fatal |= OVER_VOLTAGE_FLAG; + if (CURRENT_MAX < get_total_current()) fatal |= OVER_CURRENT_FLAG; + if (shunt_overload) fatal |= SHUNT_OVERLOAD_FLAG; + if (MOTOR_SHUTDOWN_THRESH <= motor_overload) fatal |= MOTOR_OVERLOAD_FLAG; + if (fatal) shutdown(); + + // Nonfatal conditions + uint16_t nonfatal = 0; + if (loads[0].shutdown) nonfatal |= LOAD1_SHUTDOWN_FLAG; + if (loads[1].shutdown) nonfatal |= LOAD2_SHUTDOWN_FLAG; + if (vout < VOLTAGE_MIN) nonfatal |= MOTOR_UNDER_VOLTAGE_FLAG; + + // Update flags + regs[FLAGS_REG] = fatal | nonfatal; } return 0; diff --git a/src/py/bbctrl/Camera.py b/src/py/bbctrl/Camera.py new file mode 100755 index 0000000..5814927 --- /dev/null +++ b/src/py/bbctrl/Camera.py @@ -0,0 +1,511 @@ +#!/usr/bin/env python3 +################################################################################ +# # +# This file is part of the Buildbotics firmware. # +# # +# Copyright (c) 2015 - 2018, Buildbotics LLC # +# All rights reserved. # +# # +# This file ("the software") is free software: you can redistribute it # +# and/or modify it under the terms of the GNU General Public License, # +# version 2 as published by the Free Software Foundation. You should # +# have received a copy of the GNU General Public License, version 2 # +# along with the software. If not, see . # +# # +# The software is distributed in the hope that it will be useful, but # +# WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # +# Lesser General Public License for more details. # +# # +# You should have received a copy of the GNU Lesser General Public # +# License along with the software. If not, see # +# . # +# # +# For information regarding this software email: # +# "Joseph Coffland" # +# # +################################################################################ + +import os +import fcntl +import logging +import select +import struct +import mmap +import pyudev +import base64 +from tornado import gen, web + +try: + import v4l2 +except: + import bbctrl.v4l2 as v4l2 + +log = logging.getLogger('Camera') + + +offline_jpg = ''' +/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsN +DhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQU +FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wgARCAHgAoADASEAAhEBAxEB/8QA +HQABAAMAAwEBAQAAAAAAAAAAAAYHCAMEBQkCAf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhAD +EAAAAcqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJl7hB/JAAAAAAAAA +BzFg9Qgn8Fgk6oYuP2z80CT+OHhgAAAAAAAAA/e1jwIYWtksiwAAAAAAAACQbeKX9UnuHju7nMryAsSn +TW/zzLUiJGQAAAAAAAAAXfY5kkP2W7eJzZbIZpo5LBM72+SXJpFrlL06JlDkNASkpWaFg+eZL88DYtPF +NjVMWJ9cRWk3PMzUaSwSXTBTt28TbgMhdU0BbJ4mTjpAAAAAADWlZFMAPX8g97YZh76DGMPAPov85ya3 +aZs2AYdmppHM5tv58nB655F0ktzSB9CcEnmC2brMz6eMa6TIdTpvbBJqmoj0dCGH9MkSjZfmOy4p6ZfA +AAAAAGpoeUSA03ZB0ZifPnexgkb2wSSXVhSWqyCDx86mk8dH72kc3oEPyIBvvE5HBdNmmddPGNdJkOp0 +3tgk1TUR6M8MzXger2i8IocsDMxAAAAAACd67MI9ckHVNz4CJPuU+ee9jBI3tgkkurDMOrzD38OaWGkc +eEo1cYpvcsTIgGjJEZR5DdGXDwtPGNdJkOp03tgk1TUR6M8MzXgerFi3sijmOEAAAAAALlL/AOgfnIJf +VqEYGQ9cGRxrjI57mgDMehS3OiVBWJdmbT9bMOz4BE85gfrUZOeUpOiCQ3sZou8jVamuMjl/1yd2WlGW +oehTZqScnUocqUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +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 +xeI9Jg+f7jZh7bBexx7LX6DbhkYLqCA+ayQvwO9SvCKmS8AWXJjO1CZIc4dfxpnZmy3pbdXtekqYRHBP +8ULiJeIHu/FICM5RdSDyKs9BKgpgp4li9Espl7pRMp8Lci/7vKnkovQxOF3d0aZ80t0WS7g1h1wyj5tk +hBmjXHOerGkSW064WFiIsoUF2xWUjdpEmTMxgWtu9AHI8nWwNGLrHdzXmmwKXQfKVbmkpeSByL9OKudb +FBVYLvatawr4k2sHbKkDCNZMTmHKW0E6llP0dDvtO0U0ph+MyiJjdielZFXWZEZXoRM6UQjTiYE4D3+G +e2tK7sgmbpm81JmS4eMvHkq4yo7e0PATO+mF1zlJaUF2xpWmWYyJMnqmGS8QiTi9WavokiNgOeGwBT+b +FMk1w/3j/wDUdTesjJlUA2A5jUXDVgwlguaVm9eakJlYCOoXywgrpWU3VhMiCchWVH6SkR8p4Su2tLLp +iJPIu9qdQNBkdJtqANOhkmjJbf8Age//2gAMAwEAAgADAAAAEMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMxMzMzMzMzMzMhMSEzMzMzMzMzMyMjMzMzMzMzMiMCEyMzMzMzMzMzM +zITMyMAMxExMBAxMjEzMzMzMzMjMSIjAAMCMTMwAzEAMSEzMzMzMxMwAzMiExEBMzADMQEgATMzMzMzE +xECMSITMDMzIwMxACATMzMzMzMxEhMzEyAwAzIBEhMAIRIzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM +zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzP/EABQRAQAAAAAAAAAAAAAAAAAAALD/ +2gAIAQMBAT8QFg//xAAUEQEAAAAAAAAAAAAAAAAAAACw/9oACAECAQE/EBYP/8QAJxABAQABBAIBBAMB +AQEAAAAAAREhADFBURBhcSBAgaEwkfCxsNH/2gAIAQEAAT8Q/wDAsP8AcRp7cb2LpQBmH9ob+JqqmIB7 +MAPafv8A0tsUcJV9BohH0Pyc9+TQYx7HpUAduNOQRFER8ujwZQMcqQ0UgiLcaKIGawFQ23Hejc5TLMAG +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 +''' + +offline_jpg = base64.b64decode(offline_jpg) + + +class VideoDevice(object): + def __init__(self, path = '/dev/video0'): + self.fd = os.open(path, os.O_RDWR | os.O_NONBLOCK | os.O_CLOEXEC) + self.buffers = [] + + + def fileno(self): return self.fd + + + def set_format(self, width = 640, height = 480, + fourcc = v4l2.V4L2_PIX_FMT_MJPEG): + fmt = v4l2.v4l2_format() + fmt.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + fcntl.ioctl(self, v4l2.VIDIOC_G_FMT, fmt) + + fmt.fmt.pix.width = width + fmt.fmt.pix.height = height + fmt.fmt.pix.pixelformat = fourcc + + fcntl.ioctl(self, v4l2.VIDIOC_S_FMT, fmt) + + + def create_buffers(self, count): + rbuf = v4l2.v4l2_requestbuffers() + rbuf.count = count; + rbuf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE; + rbuf.memory = v4l2.V4L2_MEMORY_MMAP; + + fcntl.ioctl(self, v4l2.VIDIOC_REQBUFS, rbuf) + + for i in range(rbuf.count): + buf = v4l2.v4l2_buffer() + buf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + buf.memory = v4l2.V4L2_MEMORY_MMAP + buf.index = i + fcntl.ioctl(self, v4l2.VIDIOC_QUERYBUF, buf) + + mm = mmap.mmap(self.fileno(), buf.length, mmap.MAP_SHARED, + mmap.PROT_READ | mmap.PROT_WRITE, + offset = buf.m.offset) + self.buffers.append(mm) + + # Queue the buffer for capture + fcntl.ioctl(self, v4l2.VIDIOC_QBUF, buf) + + + def read(self): + if not len(self.buffers): + raise Exception('Buffers have not been created.') + + buf = v4l2.v4l2_buffer() + buf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE + buf.memory = v4l2.V4L2_MEMORY_MMAP + fcntl.ioctl(self, v4l2.VIDIOC_DQBUF, buf) + + mm = self.buffers[buf.index] + frame = mm.read() + mm.seek(0) + + fcntl.ioctl(self, v4l2.VIDIOC_QBUF, buf) + + return frame + + + def get_info(self): + caps = v4l2.v4l2_capability() + fcntl.ioctl(self, v4l2.VIDIOC_QUERYCAP, caps) + + caps._driver = ''.join([chr(i) for i in caps.driver]) + caps._card = ''.join([chr(i) for i in caps.card]) + caps._bus_info = ''.join([chr(i) for i in caps.bus_info]) + + return caps + + + def start(self): + buf_type = v4l2.v4l2_buf_type(v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE) + fcntl.ioctl(self, v4l2.VIDIOC_STREAMON, buf_type) + + + def stop(self): + buf_type = v4l2.v4l2_buf_type(v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE) + fcntl.ioctl(self, v4l2.VIDIOC_STREAMOFF, buf_type) + + + def close(self): + if self.fd is None: return + os.close(self.fd) + self.fd = None + + +class Camera(object): + def __init__(self, ctrl): + self.ctrl = ctrl + self.dev = None + self.clients = [] + self.path = None + self.frames = 0 + + for i in range(4): + path = '/dev/video%d' % i + if os.path.exists(path): + self.open(path) + break + + self.udevCtx = pyudev.Context() + self.udevMon = pyudev.Monitor.from_netlink(self.udevCtx) + self.udevMon.filter_by(subsystem = 'video4linux') + ctrl.ioloop.add_handler(self.udevMon, self._udev_handler, + ctrl.ioloop.READ) + self.udevMon.start() + + + def _udev_handler(self, fd, events): + action, device = self.udevMon.receive_device() + if device is None or self.dev is not None: return + + path = str(device.device_node) + + if action == 'add': self.open(path) + if action == 'remove' and path == self.path: self.close() + + + def open(self, path): + self.path = path + + try: + self.dev = VideoDevice(path) + + caps = self.dev.get_info() + log.info('%s, %s, %s', caps._driver, caps._card, caps._bus_info) + + if caps.capabilities & v4l2.V4L2_CAP_VIDEO_CAPTURE == 0: + raise Exception('Video capture not supported.') + + self.dev.set_format(640, 480, fourcc = v4l2.V4L2_PIX_FMT_MJPEG) + self.dev.create_buffers(30) + self.dev.start() + + self.ctrl.ioloop.add_handler(self.dev, self._handler, + self.ctrl.ioloop.READ) + + log.info('Opened camera ' + path) + + + except Exception as e: + log.warning('While loading camera') + if not self.dev is None: + self.dev.close() + self.dev = None + + + def close(self): + if self.dev is None: return + try: + self.ctrl.ioloop.remove_handler(self.dev) + try: + self.dev.stop() + except: pass + self.dev.close() + + for client in self.clients: + client.write_frame(offline_jpg) + client.write_frame(offline_jpg) + + log.info('Closed camera %s' % self.path) + + except: log.warning('Closing camera') + finally: self.dev = None + + + def _handler(self, fd, events): + try: + frame = self.dev.read() + + except: + log.warning('Failed to read from camera.') + self.ctrl.ioloop.remove_handler(fd) + self.close() + return + + if frame is not None: + if self.frames < 10: + with open('frame%d.jpg' % self.frames, 'wb') as f: + f.write(frame) + + self.frames += 1 + + for client in self.clients: + client.write_frame(frame) + + + def add_client(self, client): + log.info('Adding camera client: %d' % len(self.clients)) + self.clients.append(client) + if self.dev is None: + client.write_frame(offline_jpg) + client.write_frame(offline_jpg) + + + def remove_client(self, client): + log.info('Removing camera client') + try: + self.clients.remove(client) + except: pass + + + +class VideoHandler(web.RequestHandler): + def __init__(self, app, request, **kwargs): + super().__init__(app, request, **kwargs) + self.camera = app.ctrl.camera + self.boundary = '---boundary---' + + + @web.asynchronous + def get(self): + self.set_header('Cache-Control', 'no-store, no-cache, ' + + 'must-revalidate, pre-check=0, post-check=0, ' + + 'max-age=0') + self.set_header('Connection', 'close') + self.set_header('Content-Type', + 'multipart/x-mixed-replace;boundary=' + + self.boundary) + self.set_header('Expires', 'Mon, 3 Jan 2000 12:34:56 GMT') + self.set_header('Pragma', 'no-cache') + + self.camera.add_client(self) + + + def write_frame(self, frame): + self.write("Content-type: image/jpeg\r\n") + self.write("Content-length: %s\r\n\r\n" % len(frame)) + self.write(frame) + self.write(self.boundary + '\n') + self.flush() + + + def on_connection_close(self): + self.camera.remove_client(self) + + + +if __name__ == '__main__': + class Ctrl(object): + def __init__(self): + from tornado import ioloop + self.ioloop = ioloop.IOLoop.current() + + + class RootHandler(web.RequestHandler): + def get(self): + self.set_header('Content-Type', 'text/html') + self.write('') + + + class Web(web.Application): + def __init__(self, ctrl): + self.ctrl = ctrl + + handlers = [ + (r'/', RootHandler), + (r'/video', VideoHandler) + ] + + web.Application.__init__(self, handlers) + self.listen(9000, address = '127.0.0.1') + + + + root = logging.getLogger() + root.setLevel(logging.INFO) + + ctrl = Ctrl() + ctrl.camera = Camera(ctrl) + server = Web(ctrl) + ctrl.ioloop.start() diff --git a/src/py/bbctrl/Ctrl.py b/src/py/bbctrl/Ctrl.py index 2b0ef52..c3bf7c0 100644 --- a/src/py/bbctrl/Ctrl.py +++ b/src/py/bbctrl/Ctrl.py @@ -55,4 +55,10 @@ class Ctrl(object): self.lcd.add_new_page(bbctrl.MainLCDPage(self)) self.lcd.add_new_page(bbctrl.IPLCDPage(self.lcd)) + self.camera = bbctrl.Camera(self) + except Exception as e: log.exception(e) + + + def close(self): + self.camera.close() diff --git a/src/py/bbctrl/Pwr.py b/src/py/bbctrl/Pwr.py index c980c5b..04d9af0 100644 --- a/src/py/bbctrl/Pwr.py +++ b/src/py/bbctrl/Pwr.py @@ -42,18 +42,25 @@ LOAD1_REG = 4 LOAD2_REG = 5 VDD_REG = 6 FLAGS_REG = 7 +VERSION_REG = 8 # Must be kept in sync with pwr firmware -UNDER_VOLTAGE_FLAG = 1 << 0 -OVER_VOLTAGE_FLAG = 1 << 1 -OVER_CURRENT_FLAG = 1 << 2 -SENSE_ERROR_FLAG = 1 << 3 -SHUNT_OVERLOAD_FLAG = 1 << 4 -MOTOR_OVERLOAD_FLAG = 1 << 5 -LOAD1_SHUTDOWN_FLAG = 1 << 6 -LOAD2_SHUTDOWN_FLAG = 1 << 7 - -reg_names = 'temp vin vout motor load1 load2 vdd pwr_flags'.split() +UNDER_VOLTAGE_FLAG = 1 << 0 +OVER_VOLTAGE_FLAG = 1 << 1 +OVER_CURRENT_FLAG = 1 << 2 +SENSE_ERROR_FLAG = 1 << 3 +SHUNT_OVERLOAD_FLAG = 1 << 4 +MOTOR_OVERLOAD_FLAG = 1 << 5 +LOAD1_SHUTDOWN_FLAG = 1 << 6 +LOAD2_SHUTDOWN_FLAG = 1 << 7 +MOTOR_UNDER_VOLTAGE_FLAG = 1 << 8 +MOTOR_VOLTAGE_SENSE_ERROR_FLAG = 1 << 9 +MOTOR_CURRENT_SENSE_ERROR_FLAG = 1 << 10 +LOAD1_SENSE_ERROR_FLAG = 1 << 11 +LOAD2_SENSE_ERROR_FLAG = 1 << 12 +VDD_CURRENT_SENSE_ERROR_FLAG = 1 << 13 + +reg_names = 'temp vin vout motor load1 load2 vdd pwr_flags pwr_version'.split() class Pwr(): @@ -61,16 +68,13 @@ class Pwr(): self.ctrl = ctrl self.i2c_addr = ctrl.args.pwr_addr - self.regs = [-1] * 8 + self.regs = [-1] * 9 self.lcd_page = ctrl.lcd.add_new_page() self.failures = 0 PeriodicCallback(self._update, 1000, ctrl.ioloop).start() - def get_reg(self, i): return self.regs[i] - - def check_fault(self, var, status): status = bool(status) @@ -108,6 +112,30 @@ class Pwr(): if self.check_fault('load2_shutdown', flags & LOAD2_SHUTDOWN_FLAG): log.error('Load 2 over temperature shutdown') + if self.check_fault('motor_under_voltage', + flags & MOTOR_UNDER_VOLTAGE_FLAG): + log.error('Motor under voltage') + + if self.check_fault('motor_voltage_sense_error', + flags & MOTOR_VOLTAGE_SENSE_ERROR_FLAG): + log.error('Motor voltage sense error') + + if self.check_fault('motor_current_sense_error', + flags & MOTOR_CURRENT_SENSE_ERROR_FLAG): + log.error('Motor current sense error') + + if self.check_fault('load1_sense_error', + flags & LOAD1_SENSE_ERROR_FLAG): + log.error('Load1 sense error') + + if self.check_fault('load2_sense_error', + flags & LOAD2_SENSE_ERROR_FLAG): + log.error('Load2 sense error') + + if self.check_fault('vdd_current_sense_error', + flags & VDD_CURRENT_SENSE_ERROR_FLAG): + log.error('Vdd current sense error') + def _update(self): update = {} @@ -117,7 +145,7 @@ class Pwr(): value = self.ctrl.i2c.read_word(self.i2c_addr + i) if i == TEMP_REG: value -= 273 - elif i == FLAGS_REG: pass + elif i == FLAGS_REG or i == VERSION_REG: pass else: value /= 100.0 key = reg_names[i] @@ -132,7 +160,7 @@ class Pwr(): except Exception as e: if i < 6: # Older pwr firmware does not have regs > 5 self.failures += 1 - msg = 'Pwr communication failed: %s' % e + msg = 'Pwr communication failed at reg %d: %s' % (i, e) if self.failures != 5: log.info(msg) else: log.warning(msg) diff --git a/src/py/bbctrl/Web.py b/src/py/bbctrl/Web.py index 8e213cb..787c09e 100644 --- a/src/py/bbctrl/Web.py +++ b/src/py/bbctrl/Web.py @@ -38,6 +38,7 @@ import subprocess import socket import time from tornado.web import HTTPError +from tornado import web, gen import bbctrl @@ -305,7 +306,7 @@ class JogHandler(bbctrl.APIHandler): class VideoReloadHandler(bbctrl.APIHandler): - def put_ok(self): subprocess.Popen('reset-video').wait() + def put_ok(self): pass #subprocess.Popen('reset-video').wait() # Base class for Web Socket connections @@ -402,6 +403,7 @@ class Web(tornado.web.Application): (r'/api/modbus/read', ModbusReadHandler), (r'/api/modbus/write', ModbusWriteHandler), (r'/api/jog', JogHandler), + (r'/api/video', bbctrl.VideoHandler), (r'/api/video/reload', VideoReloadHandler), (r'/(.*)', StaticFileHandler, {'path': bbctrl.get_resource('http/'), diff --git a/src/py/bbctrl/__init__.py b/src/py/bbctrl/__init__.py index 0298099..94bf4ad 100644 --- a/src/py/bbctrl/__init__.py +++ b/src/py/bbctrl/__init__.py @@ -55,7 +55,12 @@ from bbctrl.Comm import Comm from bbctrl.CommandQueue import CommandQueue from bbctrl.MainLCDPage import MainLCDPage from bbctrl.IPLCDPage import IPLCDPage +from bbctrl.Camera import Camera, VideoHandler import bbctrl.Cmd as Cmd +import bbctrl.v4l2 as v4l2 + + +ctrl = None def get_resource(path): @@ -63,7 +68,14 @@ def get_resource(path): def on_exit(sig = 0, func = None): + global ctrl + logging.info('Exit handler triggered: signal = %d', sig) + + if ctrl is not None: + ctrl.close() + ctrl = None + sys.exit(1) @@ -96,6 +108,8 @@ def parse_args(): def run(): + global ctrl + args = parse_args() # Init logging diff --git a/src/py/bbctrl/v4l2.py b/src/py/bbctrl/v4l2.py new file mode 100644 index 0000000..5d8ca83 --- /dev/null +++ b/src/py/bbctrl/v4l2.py @@ -0,0 +1,1941 @@ +# Python bindings for the v4l2 userspace api + +# Copyright (C) 1999-2009 the contributors + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# Alternatively you can redistribute this file under the terms of the +# BSD license as stated below: + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. The names of its contributors may not be used to endorse or promote +# products derived from this software without specific prior written +# permission. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +Python bindings for the v4l2 userspace api in Linux 2.6.34 +""" + +# see linux/videodev2.h + +import ctypes +import platform + + +# See ioctl.h of your architecture for appropriate constants here. +# This has been tested only on x86 and MIPS +_IOC_NRBITS = 8 +_IOC_TYPEBITS = 8 + +_IOC_NRSHIFT = 0 +_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS +_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS + +if (platform.machine() == "mips"): + _IOC_NONE = 1 + _IOC_READ = 2 + _IOC_WRITE = 4 + _IOC_SIZEBITS = 13 + _IOC_DIRBITS = 3 +else: + _IOC_NONE = 0 + _IOC_WRITE = 1 + _IOC_READ = 2 + _IOC_SIZEBITS = 14 + _IOC_DIRBITS = 2 + +_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS + +def _IOC(dir_, type_, nr, size): + return ( + ctypes.c_int32(dir_ << _IOC_DIRSHIFT).value | + ctypes.c_int32(ord(type_) << _IOC_TYPESHIFT).value | + ctypes.c_int32(nr << _IOC_NRSHIFT).value | + ctypes.c_int32(size << _IOC_SIZESHIFT).value) + +def _IOC_TYPECHECK(t): + return ctypes.sizeof(t) + + +def _IO(type_, nr): + return _IOC(_IOC_NONE, type_, nr, 0) + + +def _IOW(type_, nr, size): + return _IOC(_IOC_WRITE, type_, nr, _IOC_TYPECHECK(size)) + + +def _IOR(type_, nr, size): + return _IOC(_IOC_READ, type_, nr, _IOC_TYPECHECK(size)) + +def _IOWR(type_, nr, size): + return _IOC(_IOC_READ | _IOC_WRITE, type_, nr, _IOC_TYPECHECK(size)) + + +# +# type alias +# + +enum = ctypes.c_uint +c_int = ctypes.c_int + + +# +# time +# + +class timeval(ctypes.Structure): + _fields_ = [ + ('secs', ctypes.c_long), + ('usecs', ctypes.c_long), + ] + + +# +# v4l2 +# + + +VIDEO_MAX_FRAME = 32 + + +VID_TYPE_CAPTURE = 1 +VID_TYPE_TUNER = 2 +VID_TYPE_TELETEXT = 4 +VID_TYPE_OVERLAY = 8 +VID_TYPE_CHROMAKEY = 16 +VID_TYPE_CLIPPING = 32 +VID_TYPE_FRAMERAM = 64 +VID_TYPE_SCALES = 128 +VID_TYPE_MONOCHROME = 256 +VID_TYPE_SUBCAPTURE = 512 +VID_TYPE_MPEG_DECODER = 1024 +VID_TYPE_MPEG_ENCODER = 2048 +VID_TYPE_MJPEG_DECODER = 4096 +VID_TYPE_MJPEG_ENCODER = 8192 + + +def v4l2_fourcc(a, b, c, d): + return ord(a) | (ord(b) << 8) | (ord(c) << 16) | (ord(d) << 24) + + +v4l2_field = enum +( + V4L2_FIELD_ANY, + V4L2_FIELD_NONE, + V4L2_FIELD_TOP, + V4L2_FIELD_BOTTOM, + V4L2_FIELD_INTERLACED, + V4L2_FIELD_SEQ_TB, + V4L2_FIELD_SEQ_BT, + V4L2_FIELD_ALTERNATE, + V4L2_FIELD_INTERLACED_TB, + V4L2_FIELD_INTERLACED_BT, +) = range(10) + + +def V4L2_FIELD_HAS_TOP(field): + return ( + field == V4L2_FIELD_TOP or + field == V4L2_FIELD_INTERLACED or + field == V4L2_FIELD_INTERLACED_TB or + field == V4L2_FIELD_INTERLACED_BT or + field == V4L2_FIELD_SEQ_TB or + field == V4L2_FIELD_SEQ_BT) + + +def V4L2_FIELD_HAS_BOTTOM(field): + return ( + field == V4L2_FIELD_BOTTOM or + field == V4L2_FIELD_INTERLACED or + field == V4L2_FIELD_INTERLACED_TB or + field == V4L2_FIELD_INTERLACED_BT or + field == V4L2_FIELD_SEQ_TB or + field == V4L2_FIELD_SEQ_BT) + + +def V4L2_FIELD_HAS_BOTH(field): + return ( + field == V4L2_FIELD_INTERLACED or + field == V4L2_FIELD_INTERLACED_TB or + field == V4L2_FIELD_INTERLACED_BT or + field == V4L2_FIELD_SEQ_TB or + field == V4L2_FIELD_SEQ_BT) + + +v4l2_buf_type = enum +( + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_BUF_TYPE_VIDEO_OUTPUT, + V4L2_BUF_TYPE_VIDEO_OVERLAY, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_BUF_TYPE_VBI_OUTPUT, + V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, + V4L2_BUF_TYPE_SLICED_VBI_OUTPUT, + V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + V4L2_BUF_TYPE_PRIVATE, +) = list(range(1, 11)) + [0x80] + + +v4l2_ctrl_type = enum +( + V4L2_CTRL_TYPE_INTEGER, + V4L2_CTRL_TYPE_BOOLEAN, + V4L2_CTRL_TYPE_MENU, + V4L2_CTRL_TYPE_BUTTON, + V4L2_CTRL_TYPE_INTEGER64, + V4L2_CTRL_TYPE_CTRL_CLASS, + V4L2_CTRL_TYPE_STRING, +) = range(1, 8) + + +v4l2_tuner_type = enum +( + V4L2_TUNER_RADIO, + V4L2_TUNER_ANALOG_TV, + V4L2_TUNER_DIGITAL_TV, +) = range(1, 4) + + +v4l2_memory = enum +( + V4L2_MEMORY_MMAP, + V4L2_MEMORY_USERPTR, + V4L2_MEMORY_OVERLAY, +) = range(1, 4) + + +v4l2_colorspace = enum +( + V4L2_COLORSPACE_SMPTE170M, + V4L2_COLORSPACE_SMPTE240M, + V4L2_COLORSPACE_REC709, + V4L2_COLORSPACE_BT878, + V4L2_COLORSPACE_470_SYSTEM_M, + V4L2_COLORSPACE_470_SYSTEM_BG, + V4L2_COLORSPACE_JPEG, + V4L2_COLORSPACE_SRGB, +) = range(1, 9) + + +v4l2_priority = enum +( + V4L2_PRIORITY_UNSET, + V4L2_PRIORITY_BACKGROUND, + V4L2_PRIORITY_INTERACTIVE, + V4L2_PRIORITY_RECORD, + V4L2_PRIORITY_DEFAULT, +) = list(range(0, 4)) + [2] + + +class v4l2_rect(ctypes.Structure): + _fields_ = [ + ('left', ctypes.c_int32), + ('top', ctypes.c_int32), + ('width', ctypes.c_int32), + ('height', ctypes.c_int32), + ] + + +class v4l2_fract(ctypes.Structure): + _fields_ = [ + ('numerator', ctypes.c_uint32), + ('denominator', ctypes.c_uint32), + ] + + +# +# Driver capabilities +# + +class v4l2_capability(ctypes.Structure): + _fields_ = [ + ('driver', ctypes.c_uint8 * 16), + ('card', ctypes.c_uint8 * 32), + ('bus_info', ctypes.c_uint8 * 32), + ('version', ctypes.c_uint32), + ('capabilities', ctypes.c_uint32), + ('device_caps', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 3), + ] + + +# +# Values for 'capabilities' field +# + +V4L2_CAP_VIDEO_CAPTURE = 0x00000001 +V4L2_CAP_VIDEO_OUTPUT = 0x00000002 +V4L2_CAP_VIDEO_OVERLAY = 0x00000004 +V4L2_CAP_VBI_CAPTURE = 0x00000010 +V4L2_CAP_VBI_OUTPUT = 0x00000020 +V4L2_CAP_SLICED_VBI_CAPTURE = 0x00000040 +V4L2_CAP_SLICED_VBI_OUTPUT = 0x00000080 +V4L2_CAP_RDS_CAPTURE = 0x00000100 +V4L2_CAP_VIDEO_OUTPUT_OVERLAY = 0x00000200 +V4L2_CAP_HW_FREQ_SEEK = 0x00000400 +V4L2_CAP_RDS_OUTPUT = 0x00000800 + +V4L2_CAP_TUNER = 0x00010000 +V4L2_CAP_AUDIO = 0x00020000 +V4L2_CAP_RADIO = 0x00040000 +V4L2_CAP_MODULATOR = 0x00080000 + +V4L2_CAP_READWRITE = 0x01000000 +V4L2_CAP_ASYNCIO = 0x02000000 +V4L2_CAP_STREAMING = 0x04000000 + + +# +# Video image format +# + +class v4l2_pix_format(ctypes.Structure): + _fields_ = [ + ('width', ctypes.c_uint32), + ('height', ctypes.c_uint32), + ('pixelformat', ctypes.c_uint32), + ('field', v4l2_field), + ('bytesperline', ctypes.c_uint32), + ('sizeimage', ctypes.c_uint32), + ('colorspace', v4l2_colorspace), + ('priv', ctypes.c_uint32), + ] + +# RGB formats +V4L2_PIX_FMT_RGB332 = v4l2_fourcc('R', 'G', 'B', '1') +V4L2_PIX_FMT_RGB444 = v4l2_fourcc('R', '4', '4', '4') +V4L2_PIX_FMT_RGB555 = v4l2_fourcc('R', 'G', 'B', 'O') +V4L2_PIX_FMT_RGB565 = v4l2_fourcc('R', 'G', 'B', 'P') +V4L2_PIX_FMT_RGB555X = v4l2_fourcc('R', 'G', 'B', 'Q') +V4L2_PIX_FMT_RGB565X = v4l2_fourcc('R', 'G', 'B', 'R') +V4L2_PIX_FMT_BGR24 = v4l2_fourcc('B', 'G', 'R', '3') +V4L2_PIX_FMT_RGB24 = v4l2_fourcc('R', 'G', 'B', '3') +V4L2_PIX_FMT_BGR32 = v4l2_fourcc('B', 'G', 'R', '4') +V4L2_PIX_FMT_RGB32 = v4l2_fourcc('R', 'G', 'B', '4') + +# Grey formats +V4L2_PIX_FMT_GREY = v4l2_fourcc('G', 'R', 'E', 'Y') +V4L2_PIX_FMT_Y10 = v4l2_fourcc('Y', '1', '0', ' ') +V4L2_PIX_FMT_Y16 = v4l2_fourcc('Y', '1', '6', ' ') + +# Palette formats +V4L2_PIX_FMT_PAL8 = v4l2_fourcc('P', 'A', 'L', '8') + +# Luminance+Chrominance formats +V4L2_PIX_FMT_YVU410 = v4l2_fourcc('Y', 'V', 'U', '9') +V4L2_PIX_FMT_YVU420 = v4l2_fourcc('Y', 'V', '1', '2') +V4L2_PIX_FMT_YUYV = v4l2_fourcc('Y', 'U', 'Y', 'V') +V4L2_PIX_FMT_YYUV = v4l2_fourcc('Y', 'Y', 'U', 'V') +V4L2_PIX_FMT_YVYU = v4l2_fourcc('Y', 'V', 'Y', 'U') +V4L2_PIX_FMT_UYVY = v4l2_fourcc('U', 'Y', 'V', 'Y') +V4L2_PIX_FMT_VYUY = v4l2_fourcc('V', 'Y', 'U', 'Y') +V4L2_PIX_FMT_YUV422P = v4l2_fourcc('4', '2', '2', 'P') +V4L2_PIX_FMT_YUV411P = v4l2_fourcc('4', '1', '1', 'P') +V4L2_PIX_FMT_Y41P = v4l2_fourcc('Y', '4', '1', 'P') +V4L2_PIX_FMT_YUV444 = v4l2_fourcc('Y', '4', '4', '4') +V4L2_PIX_FMT_YUV555 = v4l2_fourcc('Y', 'U', 'V', 'O') +V4L2_PIX_FMT_YUV565 = v4l2_fourcc('Y', 'U', 'V', 'P') +V4L2_PIX_FMT_YUV32 = v4l2_fourcc('Y', 'U', 'V', '4') +V4L2_PIX_FMT_YUV410 = v4l2_fourcc('Y', 'U', 'V', '9') +V4L2_PIX_FMT_YUV420 = v4l2_fourcc('Y', 'U', '1', '2') +V4L2_PIX_FMT_HI240 = v4l2_fourcc('H', 'I', '2', '4') +V4L2_PIX_FMT_HM12 = v4l2_fourcc('H', 'M', '1', '2') + +# two planes -- one Y, one Cr + Cb interleaved +V4L2_PIX_FMT_NV12 = v4l2_fourcc('N', 'V', '1', '2') +V4L2_PIX_FMT_NV21 = v4l2_fourcc('N', 'V', '2', '1') +V4L2_PIX_FMT_NV16 = v4l2_fourcc('N', 'V', '1', '6') +V4L2_PIX_FMT_NV61 = v4l2_fourcc('N', 'V', '6', '1') + +# Bayer formats - see http://www.siliconimaging.com/RGB%20Bayer.htm +V4L2_PIX_FMT_SBGGR8 = v4l2_fourcc('B', 'A', '8', '1') +V4L2_PIX_FMT_SGBRG8 = v4l2_fourcc('G', 'B', 'R', 'G') +V4L2_PIX_FMT_SGRBG8 = v4l2_fourcc('G', 'R', 'B', 'G') +V4L2_PIX_FMT_SRGGB8 = v4l2_fourcc('R', 'G', 'G', 'B') +V4L2_PIX_FMT_SBGGR10 = v4l2_fourcc('B', 'G', '1', '0') +V4L2_PIX_FMT_SGBRG10 = v4l2_fourcc('G', 'B', '1', '0') +V4L2_PIX_FMT_SGRBG10 = v4l2_fourcc('B', 'A', '1', '0') +V4L2_PIX_FMT_SRGGB10 = v4l2_fourcc('R', 'G', '1', '0') +V4L2_PIX_FMT_SGRBG10DPCM8 = v4l2_fourcc('B', 'D', '1', '0') +V4L2_PIX_FMT_SBGGR16 = v4l2_fourcc('B', 'Y', 'R', '2') + +# compressed formats +V4L2_PIX_FMT_MJPEG = v4l2_fourcc('M', 'J', 'P', 'G') +V4L2_PIX_FMT_JPEG = v4l2_fourcc('J', 'P', 'E', 'G') +V4L2_PIX_FMT_DV = v4l2_fourcc('d', 'v', 's', 'd') +V4L2_PIX_FMT_MPEG = v4l2_fourcc('M', 'P', 'E', 'G') + +# Vendor-specific formats +V4L2_PIX_FMT_CPIA1 = v4l2_fourcc('C', 'P', 'I', 'A') +V4L2_PIX_FMT_WNVA = v4l2_fourcc('W', 'N', 'V', 'A') +V4L2_PIX_FMT_SN9C10X = v4l2_fourcc('S', '9', '1', '0') +V4L2_PIX_FMT_SN9C20X_I420 = v4l2_fourcc('S', '9', '2', '0') +V4L2_PIX_FMT_PWC1 = v4l2_fourcc('P', 'W', 'C', '1') +V4L2_PIX_FMT_PWC2 = v4l2_fourcc('P', 'W', 'C', '2') +V4L2_PIX_FMT_ET61X251 = v4l2_fourcc('E', '6', '2', '5') +V4L2_PIX_FMT_SPCA501 = v4l2_fourcc('S', '5', '0', '1') +V4L2_PIX_FMT_SPCA505 = v4l2_fourcc('S', '5', '0', '5') +V4L2_PIX_FMT_SPCA508 = v4l2_fourcc('S', '5', '0', '8') +V4L2_PIX_FMT_SPCA561 = v4l2_fourcc('S', '5', '6', '1') +V4L2_PIX_FMT_PAC207 = v4l2_fourcc('P', '2', '0', '7') +V4L2_PIX_FMT_MR97310A = v4l2_fourcc('M', '3', '1', '0') +V4L2_PIX_FMT_SN9C2028 = v4l2_fourcc('S', 'O', 'N', 'X') +V4L2_PIX_FMT_SQ905C = v4l2_fourcc('9', '0', '5', 'C') +V4L2_PIX_FMT_PJPG = v4l2_fourcc('P', 'J', 'P', 'G') +V4L2_PIX_FMT_OV511 = v4l2_fourcc('O', '5', '1', '1') +V4L2_PIX_FMT_OV518 = v4l2_fourcc('O', '5', '1', '8') +V4L2_PIX_FMT_STV0680 = v4l2_fourcc('S', '6', '8', '0') + + +# +# Format enumeration +# + +class v4l2_fmtdesc(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('type', ctypes.c_int), + ('flags', ctypes.c_uint32), + ('description', ctypes.c_char * 32), + ('pixelformat', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + +V4L2_FMT_FLAG_COMPRESSED = 0x0001 +V4L2_FMT_FLAG_EMULATED = 0x0002 + + +# +# Experimental frame size and frame rate enumeration +# + +v4l2_frmsizetypes = enum +( + V4L2_FRMSIZE_TYPE_DISCRETE, + V4L2_FRMSIZE_TYPE_CONTINUOUS, + V4L2_FRMSIZE_TYPE_STEPWISE, +) = range(1, 4) + + +class v4l2_frmsize_discrete(ctypes.Structure): + _fields_ = [ + ('width', ctypes.c_uint32), + ('height', ctypes.c_uint32), + ] + + +class v4l2_frmsize_stepwise(ctypes.Structure): + _fields_ = [ + ('min_width', ctypes.c_uint32), + ('min_height', ctypes.c_uint32), + ('step_width', ctypes.c_uint32), + ('min_height', ctypes.c_uint32), + ('max_height', ctypes.c_uint32), + ('step_height', ctypes.c_uint32), + ] + + +class v4l2_frmsizeenum(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('discrete', v4l2_frmsize_discrete), + ('stepwise', v4l2_frmsize_stepwise), + ] + + _fields_ = [ + ('index', ctypes.c_uint32), + ('pixel_format', ctypes.c_uint32), + ('type', ctypes.c_uint32), + ('_u', _u), + ('reserved', ctypes.c_uint32 * 2) + ] + + _anonymous_ = ('_u',) + + +# +# Frame rate enumeration +# + +v4l2_frmivaltypes = enum +( + V4L2_FRMIVAL_TYPE_DISCRETE, + V4L2_FRMIVAL_TYPE_CONTINUOUS, + V4L2_FRMIVAL_TYPE_STEPWISE, +) = range(1, 4) + + +class v4l2_frmival_stepwise(ctypes.Structure): + _fields_ = [ + ('min', v4l2_fract), + ('max', v4l2_fract), + ('step', v4l2_fract), + ] + + +class v4l2_frmivalenum(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('discrete', v4l2_fract), + ('stepwise', v4l2_frmival_stepwise), + ] + + _fields_ = [ + ('index', ctypes.c_uint32), + ('pixel_format', ctypes.c_uint32), + ('width', ctypes.c_uint32), + ('height', ctypes.c_uint32), + ('type', ctypes.c_uint32), + ('_u', _u), + ('reserved', ctypes.c_uint32 * 2), + ] + + _anonymous_ = ('_u',) + + +# +# Timecode +# + +class v4l2_timecode(ctypes.Structure): + _fields_ = [ + ('type', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('frames', ctypes.c_uint8), + ('seconds', ctypes.c_uint8), + ('minutes', ctypes.c_uint8), + ('hours', ctypes.c_uint8), + ('userbits', ctypes.c_uint8 * 4), + ] + + +V4L2_TC_TYPE_24FPS = 1 +V4L2_TC_TYPE_25FPS = 2 +V4L2_TC_TYPE_30FPS = 3 +V4L2_TC_TYPE_50FPS = 4 +V4L2_TC_TYPE_60FPS = 5 + +V4L2_TC_FLAG_DROPFRAME = 0x0001 +V4L2_TC_FLAG_COLORFRAME = 0x0002 +V4L2_TC_USERBITS_field = 0x000C +V4L2_TC_USERBITS_USERDEFINED = 0x0000 +V4L2_TC_USERBITS_8BITCHARS = 0x0008 + + +class v4l2_jpegcompression(ctypes.Structure): + _fields_ = [ + ('quality', ctypes.c_int), + ('APPn', ctypes.c_int), + ('APP_len', ctypes.c_int), + ('APP_data', ctypes.c_char * 60), + ('COM_len', ctypes.c_int), + ('COM_data', ctypes.c_char * 60), + ('jpeg_markers', ctypes.c_uint32), + ] + + +V4L2_JPEG_MARKER_DHT = 1 << 3 +V4L2_JPEG_MARKER_DQT = 1 << 4 +V4L2_JPEG_MARKER_DRI = 1 << 5 +V4L2_JPEG_MARKER_COM = 1 << 6 +V4L2_JPEG_MARKER_APP = 1 << 7 + + +# +# Memory-mapping buffers +# + +class v4l2_requestbuffers(ctypes.Structure): + _fields_ = [ + ('count', ctypes.c_uint32), + ('type', v4l2_buf_type), + ('memory', v4l2_memory), + ('reserved', ctypes.c_uint32 * 2), + ] + + +class v4l2_plane(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('mem_offset', ctypes.c_uint32), + ('userptr', ctypes.c_ulong), + ] + + _fields_ = [ + ('bytesused', ctypes.c_uint32), + ('length', ctypes.c_uint32), + ('m', _u), + ('data_offset', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 11), + ] + +class v4l2_buffer(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('offset', ctypes.c_uint32), + ('userptr', ctypes.c_ulong), + ('planes', ctypes.POINTER(v4l2_plane)) + ] + + _fields_ = [ + ('index', ctypes.c_uint32), + ('type', v4l2_buf_type), + ('bytesused', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('field', v4l2_field), + ('timestamp', timeval), + ('timecode', v4l2_timecode), + ('sequence', ctypes.c_uint32), + ('memory', v4l2_memory), + ('m', _u), + ('length', ctypes.c_uint32), + ('input', ctypes.c_uint32), + ('reserved', ctypes.c_uint32), + ] + + +V4L2_BUF_FLAG_MAPPED = 0x0001 +V4L2_BUF_FLAG_QUEUED = 0x0002 +V4L2_BUF_FLAG_DONE = 0x0004 +V4L2_BUF_FLAG_KEYFRAME = 0x0008 +V4L2_BUF_FLAG_PFRAME = 0x0010 +V4L2_BUF_FLAG_BFRAME = 0x0020 +V4L2_BUF_FLAG_TIMECODE = 0x0100 +V4L2_BUF_FLAG_INPUT = 0x0200 + + +# +# Overlay preview +# + +class v4l2_framebuffer(ctypes.Structure): + _fields_ = [ + ('capability', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('base', ctypes.c_void_p), + ('fmt', v4l2_pix_format), + ] + +V4L2_FBUF_CAP_EXTERNOVERLAY = 0x0001 +V4L2_FBUF_CAP_CHROMAKEY = 0x0002 +V4L2_FBUF_CAP_LIST_CLIPPING = 0x0004 +V4L2_FBUF_CAP_BITMAP_CLIPPING = 0x0008 +V4L2_FBUF_CAP_LOCAL_ALPHA = 0x0010 +V4L2_FBUF_CAP_GLOBAL_ALPHA = 0x0020 +V4L2_FBUF_CAP_LOCAL_INV_ALPHA = 0x0040 +V4L2_FBUF_CAP_SRC_CHROMAKEY = 0x0080 + +V4L2_FBUF_FLAG_PRIMARY = 0x0001 +V4L2_FBUF_FLAG_OVERLAY = 0x0002 +V4L2_FBUF_FLAG_CHROMAKEY = 0x0004 +V4L2_FBUF_FLAG_LOCAL_ALPHA = 0x0008 +V4L2_FBUF_FLAG_GLOBAL_ALPHA = 0x0010 +V4L2_FBUF_FLAG_LOCAL_INV_ALPHA = 0x0020 +V4L2_FBUF_FLAG_SRC_CHROMAKEY = 0x0040 + + +class v4l2_clip(ctypes.Structure): + pass +v4l2_clip._fields_ = [ + ('c', v4l2_rect), + ('next', ctypes.POINTER(v4l2_clip)), +] + + +class v4l2_window(ctypes.Structure): + _fields_ = [ + ('w', v4l2_rect), + ('field', v4l2_field), + ('chromakey', ctypes.c_uint32), + ('clips', ctypes.POINTER(v4l2_clip)), + ('clipcount', ctypes.c_uint32), + ('bitmap', ctypes.c_void_p), + ('global_alpha', ctypes.c_uint8), + ] + + +# +# Capture parameters +# + +class v4l2_captureparm(ctypes.Structure): + _fields_ = [ + ('capability', ctypes.c_uint32), + ('capturemode', ctypes.c_uint32), + ('timeperframe', v4l2_fract), + ('extendedmode', ctypes.c_uint32), + ('readbuffers', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +V4L2_MODE_HIGHQUALITY = 0x0001 +V4L2_CAP_TIMEPERFRAME = 0x1000 + + +class v4l2_outputparm(ctypes.Structure): + _fields_ = [ + ('capability', ctypes.c_uint32), + ('outputmode', ctypes.c_uint32), + ('timeperframe', v4l2_fract), + ('extendedmode', ctypes.c_uint32), + ('writebuffers', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +# +# Input image cropping +# + +class v4l2_cropcap(ctypes.Structure): + _fields_ = [ + ('type', v4l2_buf_type), + ('bounds', v4l2_rect), + ('defrect', v4l2_rect), + ('pixelaspect', v4l2_fract), + ] + + +class v4l2_crop(ctypes.Structure): + _fields_ = [ + ('type', ctypes.c_int), + ('c', v4l2_rect), + ] + + +# +# Analog video standard +# + +v4l2_std_id = ctypes.c_uint64 + + +V4L2_STD_PAL_B = 0x00000001 +V4L2_STD_PAL_B1 = 0x00000002 +V4L2_STD_PAL_G = 0x00000004 +V4L2_STD_PAL_H = 0x00000008 +V4L2_STD_PAL_I = 0x00000010 +V4L2_STD_PAL_D = 0x00000020 +V4L2_STD_PAL_D1 = 0x00000040 +V4L2_STD_PAL_K = 0x00000080 + +V4L2_STD_PAL_M = 0x00000100 +V4L2_STD_PAL_N = 0x00000200 +V4L2_STD_PAL_Nc = 0x00000400 +V4L2_STD_PAL_60 = 0x00000800 + +V4L2_STD_NTSC_M = 0x00001000 +V4L2_STD_NTSC_M_JP = 0x00002000 +V4L2_STD_NTSC_443 = 0x00004000 +V4L2_STD_NTSC_M_KR = 0x00008000 + +V4L2_STD_SECAM_B = 0x00010000 +V4L2_STD_SECAM_D = 0x00020000 +V4L2_STD_SECAM_G = 0x00040000 +V4L2_STD_SECAM_H = 0x00080000 +V4L2_STD_SECAM_K = 0x00100000 +V4L2_STD_SECAM_K1 = 0x00200000 +V4L2_STD_SECAM_L = 0x00400000 +V4L2_STD_SECAM_LC = 0x00800000 + +V4L2_STD_ATSC_8_VSB = 0x01000000 +V4L2_STD_ATSC_16_VSB = 0x02000000 + + +# some common needed stuff +V4L2_STD_PAL_BG = (V4L2_STD_PAL_B | V4L2_STD_PAL_B1 | V4L2_STD_PAL_G) +V4L2_STD_PAL_DK = (V4L2_STD_PAL_D | V4L2_STD_PAL_D1 | V4L2_STD_PAL_K) +V4L2_STD_PAL = (V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_H | V4L2_STD_PAL_I) +V4L2_STD_NTSC = (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR) +V4L2_STD_SECAM_DK = (V4L2_STD_SECAM_D | V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1) +V4L2_STD_SECAM = (V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H | V4L2_STD_SECAM_DK | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC) + +V4L2_STD_525_60 = (V4L2_STD_PAL_M | V4L2_STD_PAL_60 | V4L2_STD_NTSC | V4L2_STD_NTSC_443) +V4L2_STD_625_50 = (V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | V4L2_STD_SECAM) +V4L2_STD_ATSC = (V4L2_STD_ATSC_8_VSB | V4L2_STD_ATSC_16_VSB) + +V4L2_STD_UNKNOWN = 0 +V4L2_STD_ALL = (V4L2_STD_525_60 | V4L2_STD_625_50) + +# some merged standards +V4L2_STD_MN = (V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | V4L2_STD_NTSC) +V4L2_STD_B = (V4L2_STD_PAL_B | V4L2_STD_PAL_B1 | V4L2_STD_SECAM_B) +V4L2_STD_GH = (V4L2_STD_PAL_G | V4L2_STD_PAL_H|V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) +V4L2_STD_DK = (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK) + + +class v4l2_standard(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('id', v4l2_std_id), + ('name', ctypes.c_char * 24), + ('frameperiod', v4l2_fract), + ('framelines', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +# +# Video timings dv preset +# + +class v4l2_dv_preset(ctypes.Structure): + _fields_ = [ + ('preset', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4) + ] + + +# +# DV preset enumeration +# + +class v4l2_dv_enum_preset(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('preset', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('width', ctypes.c_uint32), + ('height', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + +# +# DV preset values +# + +V4L2_DV_INVALID = 0 +V4L2_DV_480P59_94 = 1 +V4L2_DV_576P50 = 2 +V4L2_DV_720P24 = 3 +V4L2_DV_720P25 = 4 +V4L2_DV_720P30 = 5 +V4L2_DV_720P50 = 6 +V4L2_DV_720P59_94 = 7 +V4L2_DV_720P60 = 8 +V4L2_DV_1080I29_97 = 9 +V4L2_DV_1080I30 = 10 +V4L2_DV_1080I25 = 11 +V4L2_DV_1080I50 = 12 +V4L2_DV_1080I60 = 13 +V4L2_DV_1080P24 = 14 +V4L2_DV_1080P25 = 15 +V4L2_DV_1080P30 = 16 +V4L2_DV_1080P50 = 17 +V4L2_DV_1080P60 = 18 + + +# +# DV BT timings +# + +class v4l2_bt_timings(ctypes.Structure): + _fields_ = [ + ('width', ctypes.c_uint32), + ('height', ctypes.c_uint32), + ('interlaced', ctypes.c_uint32), + ('polarities', ctypes.c_uint32), + ('pixelclock', ctypes.c_uint64), + ('hfrontporch', ctypes.c_uint32), + ('hsync', ctypes.c_uint32), + ('hbackporch', ctypes.c_uint32), + ('vfrontporch', ctypes.c_uint32), + ('vsync', ctypes.c_uint32), + ('vbackporch', ctypes.c_uint32), + ('il_vfrontporch', ctypes.c_uint32), + ('il_vsync', ctypes.c_uint32), + ('il_vbackporch', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 16), + ] + + _pack_ = True + +# Interlaced or progressive format +V4L2_DV_PROGRESSIVE = 0 +V4L2_DV_INTERLACED = 1 + +# Polarities. If bit is not set, it is assumed to be negative polarity +V4L2_DV_VSYNC_POS_POL = 0x00000001 +V4L2_DV_HSYNC_POS_POL = 0x00000002 + + +class v4l2_dv_timings(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('bt', v4l2_bt_timings), + ('reserved', ctypes.c_uint32 * 32), + ] + + _fields_ = [ + ('type', ctypes.c_uint32), + ('_u', _u), + ] + + _anonymous_ = ('_u',) + _pack_ = True + + +# Values for the type field +V4L2_DV_BT_656_1120 = 0 + + +# +# Video inputs +# + +class v4l2_input(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('type', ctypes.c_uint32), + ('audioset', ctypes.c_uint32), + ('tuner', ctypes.c_uint32), + ('std', v4l2_std_id), + ('status', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +V4L2_INPUT_TYPE_TUNER = 1 +V4L2_INPUT_TYPE_CAMERA = 2 + +V4L2_IN_ST_NO_POWER = 0x00000001 +V4L2_IN_ST_NO_SIGNAL = 0x00000002 +V4L2_IN_ST_NO_COLOR = 0x00000004 + +V4L2_IN_ST_HFLIP = 0x00000010 +V4L2_IN_ST_VFLIP = 0x00000020 + +V4L2_IN_ST_NO_H_LOCK = 0x00000100 +V4L2_IN_ST_COLOR_KILL = 0x00000200 + +V4L2_IN_ST_NO_SYNC = 0x00010000 +V4L2_IN_ST_NO_EQU = 0x00020000 +V4L2_IN_ST_NO_CARRIER = 0x00040000 + +V4L2_IN_ST_MACROVISION = 0x01000000 +V4L2_IN_ST_NO_ACCESS = 0x02000000 +V4L2_IN_ST_VTR = 0x04000000 + +V4L2_IN_CAP_PRESETS = 0x00000001 +V4L2_IN_CAP_CUSTOM_TIMINGS = 0x00000002 +V4L2_IN_CAP_STD = 0x00000004 + +# +# Video outputs +# + +class v4l2_output(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('type', ctypes.c_uint32), + ('audioset', ctypes.c_uint32), + ('modulator', ctypes.c_uint32), + ('std', v4l2_std_id), + ('reserved', ctypes.c_uint32 * 4), + ] + + +V4L2_OUTPUT_TYPE_MODULATOR = 1 +V4L2_OUTPUT_TYPE_ANALOG = 2 +V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY = 3 + +V4L2_OUT_CAP_PRESETS = 0x00000001 +V4L2_OUT_CAP_CUSTOM_TIMINGS = 0x00000002 +V4L2_OUT_CAP_STD = 0x00000004 + +# +# Controls +# + +class v4l2_control(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_uint32), + ('value', ctypes.c_int32), + ] + + +class v4l2_ext_control(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('value', ctypes.c_int32), + ('value64', ctypes.c_int64), + ('reserved', ctypes.c_void_p), + ] + + _fields_ = [ + ('id', ctypes.c_uint32), + ('reserved2', ctypes.c_uint32 * 2), + ('_u', _u) + ] + + _anonymous_ = ('_u',) + _pack_ = True + + +class v4l2_ext_controls(ctypes.Structure): + _fields_ = [ + ('ctrl_class', ctypes.c_uint32), + ('count', ctypes.c_uint32), + ('error_idx', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ('controls', ctypes.POINTER(v4l2_ext_control)), + ] + + +V4L2_CTRL_CLASS_USER = 0x00980000 +V4L2_CTRL_CLASS_MPEG = 0x00990000 +V4L2_CTRL_CLASS_CAMERA = 0x009a0000 +V4L2_CTRL_CLASS_FM_TX = 0x009b0000 + + +def V4L2_CTRL_ID_MASK(): + return 0x0fffffff + + +def V4L2_CTRL_ID2CLASS(id_): + return id_ & 0x0fff0000 # unsigned long + + +def V4L2_CTRL_DRIVER_PRIV(id_): + return (id_ & 0xffff) >= 0x1000 + + +class v4l2_queryctrl(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_uint32), + ('type', v4l2_ctrl_type), + ('name', ctypes.c_char * 32), + ('minimum', ctypes.c_int32), + ('maximum', ctypes.c_int32), + ('step', ctypes.c_int32), + ('default', ctypes.c_int32), + ('flags', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +class v4l2_querymenu(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_uint32), + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('reserved', ctypes.c_uint32), + ] + + +V4L2_CTRL_FLAG_DISABLED = 0x0001 +V4L2_CTRL_FLAG_GRABBED = 0x0002 +V4L2_CTRL_FLAG_READ_ONLY = 0x0004 +V4L2_CTRL_FLAG_UPDATE = 0x0008 +V4L2_CTRL_FLAG_INACTIVE = 0x0010 +V4L2_CTRL_FLAG_SLIDER = 0x0020 +V4L2_CTRL_FLAG_WRITE_ONLY = 0x0040 + +V4L2_CTRL_FLAG_NEXT_CTRL = 0x80000000 + +V4L2_CID_BASE = V4L2_CTRL_CLASS_USER | 0x900 +V4L2_CID_USER_BASE = V4L2_CID_BASE +V4L2_CID_PRIVATE_BASE = 0x08000000 + +V4L2_CID_USER_CLASS = V4L2_CTRL_CLASS_USER | 1 +V4L2_CID_BRIGHTNESS = V4L2_CID_BASE + 0 +V4L2_CID_CONTRAST = V4L2_CID_BASE + 1 +V4L2_CID_SATURATION = V4L2_CID_BASE + 2 +V4L2_CID_HUE = V4L2_CID_BASE + 3 +V4L2_CID_AUDIO_VOLUME = V4L2_CID_BASE + 5 +V4L2_CID_AUDIO_BALANCE = V4L2_CID_BASE + 6 +V4L2_CID_AUDIO_BASS = V4L2_CID_BASE + 7 +V4L2_CID_AUDIO_TREBLE = V4L2_CID_BASE + 8 +V4L2_CID_AUDIO_MUTE = V4L2_CID_BASE + 9 +V4L2_CID_AUDIO_LOUDNESS = V4L2_CID_BASE + 10 +V4L2_CID_BLACK_LEVEL = V4L2_CID_BASE + 11 # Deprecated +V4L2_CID_AUTO_WHITE_BALANCE = V4L2_CID_BASE + 12 +V4L2_CID_DO_WHITE_BALANCE = V4L2_CID_BASE + 13 +V4L2_CID_RED_BALANCE = V4L2_CID_BASE + 14 +V4L2_CID_BLUE_BALANCE = V4L2_CID_BASE + 15 +V4L2_CID_GAMMA = V4L2_CID_BASE + 16 +V4L2_CID_WHITENESS = V4L2_CID_GAMMA # Deprecated +V4L2_CID_EXPOSURE = V4L2_CID_BASE + 17 +V4L2_CID_AUTOGAIN = V4L2_CID_BASE + 18 +V4L2_CID_GAIN = V4L2_CID_BASE + 19 +V4L2_CID_HFLIP = V4L2_CID_BASE + 20 +V4L2_CID_VFLIP = V4L2_CID_BASE + 21 + +# Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET +V4L2_CID_HCENTER = V4L2_CID_BASE + 22 +V4L2_CID_VCENTER = V4L2_CID_BASE + 23 + +V4L2_CID_POWER_LINE_FREQUENCY = V4L2_CID_BASE + 24 + +v4l2_power_line_frequency = enum +( + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, +) = range(3) + +V4L2_CID_HUE_AUTO = V4L2_CID_BASE + 25 +V4L2_CID_WHITE_BALANCE_TEMPERATURE = V4L2_CID_BASE + 26 +V4L2_CID_SHARPNESS = V4L2_CID_BASE + 27 +V4L2_CID_BACKLIGHT_COMPENSATION = V4L2_CID_BASE + 28 +V4L2_CID_CHROMA_AGC = V4L2_CID_BASE + 29 +V4L2_CID_COLOR_KILLER = V4L2_CID_BASE + 30 +V4L2_CID_COLORFX = V4L2_CID_BASE + 31 + +v4l2_colorfx = enum +( + V4L2_COLORFX_NONE, + V4L2_COLORFX_BW, + V4L2_COLORFX_SEPIA, +) = range(3) + +V4L2_CID_AUTOBRIGHTNESS = V4L2_CID_BASE + 32 +V4L2_CID_BAND_STOP_FILTER = V4L2_CID_BASE + 33 + +V4L2_CID_ROTATE = V4L2_CID_BASE + 34 +V4L2_CID_BG_COLOR = V4L2_CID_BASE + 35 +V4L2_CID_LASTP1 = V4L2_CID_BASE + 36 + +V4L2_CID_MPEG_BASE = V4L2_CTRL_CLASS_MPEG | 0x900 +V4L2_CID_MPEG_CLASS = V4L2_CTRL_CLASS_MPEG | 1 + +# MPEG streams +V4L2_CID_MPEG_STREAM_TYPE = V4L2_CID_MPEG_BASE + 0 + +v4l2_mpeg_stream_type = enum +( + V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, + V4L2_MPEG_STREAM_TYPE_MPEG1_SS, + V4L2_MPEG_STREAM_TYPE_MPEG2_DVD, + V4L2_MPEG_STREAM_TYPE_MPEG1_VCD, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, +) = range(6) + +V4L2_CID_MPEG_STREAM_PID_PMT = V4L2_CID_MPEG_BASE + 1 +V4L2_CID_MPEG_STREAM_PID_AUDIO = V4L2_CID_MPEG_BASE + 2 +V4L2_CID_MPEG_STREAM_PID_VIDEO = V4L2_CID_MPEG_BASE + 3 +V4L2_CID_MPEG_STREAM_PID_PCR = V4L2_CID_MPEG_BASE + 4 +V4L2_CID_MPEG_STREAM_PES_ID_AUDIO = V4L2_CID_MPEG_BASE + 5 +V4L2_CID_MPEG_STREAM_PES_ID_VIDEO = V4L2_CID_MPEG_BASE + 6 +V4L2_CID_MPEG_STREAM_VBI_FMT = V4L2_CID_MPEG_BASE + 7 + +v4l2_mpeg_stream_vbi_fmt = enum +( + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, +) = range(2) + +V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ = V4L2_CID_MPEG_BASE + 100 + +v4l2_mpeg_audio_sampling_freq = enum +( + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, +) = range(3) + +V4L2_CID_MPEG_AUDIO_ENCODING = V4L2_CID_MPEG_BASE + 101 + +v4l2_mpeg_audio_encoding = enum +( + V4L2_MPEG_AUDIO_ENCODING_LAYER_1, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_3, + V4L2_MPEG_AUDIO_ENCODING_AAC, + V4L2_MPEG_AUDIO_ENCODING_AC3, +) = range(5) + +V4L2_CID_MPEG_AUDIO_L1_BITRATE = V4L2_CID_MPEG_BASE + 102 + +v4l2_mpeg_audio_l1_bitrate = enum +( + V4L2_MPEG_AUDIO_L1_BITRATE_32K, + V4L2_MPEG_AUDIO_L1_BITRATE_64K, + V4L2_MPEG_AUDIO_L1_BITRATE_96K, + V4L2_MPEG_AUDIO_L1_BITRATE_128K, + V4L2_MPEG_AUDIO_L1_BITRATE_160K, + V4L2_MPEG_AUDIO_L1_BITRATE_192K, + V4L2_MPEG_AUDIO_L1_BITRATE_224K, + V4L2_MPEG_AUDIO_L1_BITRATE_256K, + V4L2_MPEG_AUDIO_L1_BITRATE_288K, + V4L2_MPEG_AUDIO_L1_BITRATE_320K, + V4L2_MPEG_AUDIO_L1_BITRATE_352K, + V4L2_MPEG_AUDIO_L1_BITRATE_384K, + V4L2_MPEG_AUDIO_L1_BITRATE_416K, + V4L2_MPEG_AUDIO_L1_BITRATE_448K, +) = range(14) + +V4L2_CID_MPEG_AUDIO_L2_BITRATE = V4L2_CID_MPEG_BASE + 103 + +v4l2_mpeg_audio_l2_bitrate = enum +( + V4L2_MPEG_AUDIO_L2_BITRATE_32K, + V4L2_MPEG_AUDIO_L2_BITRATE_48K, + V4L2_MPEG_AUDIO_L2_BITRATE_56K, + V4L2_MPEG_AUDIO_L2_BITRATE_64K, + V4L2_MPEG_AUDIO_L2_BITRATE_80K, + V4L2_MPEG_AUDIO_L2_BITRATE_96K, + V4L2_MPEG_AUDIO_L2_BITRATE_112K, + V4L2_MPEG_AUDIO_L2_BITRATE_128K, + V4L2_MPEG_AUDIO_L2_BITRATE_160K, + V4L2_MPEG_AUDIO_L2_BITRATE_192K, + V4L2_MPEG_AUDIO_L2_BITRATE_224K, + V4L2_MPEG_AUDIO_L2_BITRATE_256K, + V4L2_MPEG_AUDIO_L2_BITRATE_320K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, +) = range(14) + +V4L2_CID_MPEG_AUDIO_L3_BITRATE = V4L2_CID_MPEG_BASE + 104 + +v4l2_mpeg_audio_l3_bitrate = enum +( + V4L2_MPEG_AUDIO_L3_BITRATE_32K, + V4L2_MPEG_AUDIO_L3_BITRATE_40K, + V4L2_MPEG_AUDIO_L3_BITRATE_48K, + V4L2_MPEG_AUDIO_L3_BITRATE_56K, + V4L2_MPEG_AUDIO_L3_BITRATE_64K, + V4L2_MPEG_AUDIO_L3_BITRATE_80K, + V4L2_MPEG_AUDIO_L3_BITRATE_96K, + V4L2_MPEG_AUDIO_L3_BITRATE_112K, + V4L2_MPEG_AUDIO_L3_BITRATE_128K, + V4L2_MPEG_AUDIO_L3_BITRATE_160K, + V4L2_MPEG_AUDIO_L3_BITRATE_192K, + V4L2_MPEG_AUDIO_L3_BITRATE_224K, + V4L2_MPEG_AUDIO_L3_BITRATE_256K, + V4L2_MPEG_AUDIO_L3_BITRATE_320K, +) = range(14) + +V4L2_CID_MPEG_AUDIO_MODE = V4L2_CID_MPEG_BASE + 105 + +v4l2_mpeg_audio_mode = enum +( + V4L2_MPEG_AUDIO_MODE_STEREO, + V4L2_MPEG_AUDIO_MODE_JOINT_STEREO, + V4L2_MPEG_AUDIO_MODE_DUAL, + V4L2_MPEG_AUDIO_MODE_MONO, +) = range(4) + +V4L2_CID_MPEG_AUDIO_MODE_EXTENSION = V4L2_CID_MPEG_BASE + 106 + +v4l2_mpeg_audio_mode_extension = enum +( + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, +) = range(4) + +V4L2_CID_MPEG_AUDIO_EMPHASIS = V4L2_CID_MPEG_BASE + 107 + +v4l2_mpeg_audio_emphasis = enum +( + V4L2_MPEG_AUDIO_EMPHASIS_NONE, + V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, +) = range(3) + +V4L2_CID_MPEG_AUDIO_CRC = V4L2_CID_MPEG_BASE + 108 + +v4l2_mpeg_audio_crc = enum +( + V4L2_MPEG_AUDIO_CRC_NONE, + V4L2_MPEG_AUDIO_CRC_CRC16, +) = range(2) + +V4L2_CID_MPEG_AUDIO_MUTE = V4L2_CID_MPEG_BASE + 109 +V4L2_CID_MPEG_AUDIO_AAC_BITRATE = V4L2_CID_MPEG_BASE + 110 +V4L2_CID_MPEG_AUDIO_AC3_BITRATE = V4L2_CID_MPEG_BASE + 111 + +v4l2_mpeg_audio_ac3_bitrate = enum +( + V4L2_MPEG_AUDIO_AC3_BITRATE_32K, + V4L2_MPEG_AUDIO_AC3_BITRATE_40K, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K, + V4L2_MPEG_AUDIO_AC3_BITRATE_56K, + V4L2_MPEG_AUDIO_AC3_BITRATE_64K, + V4L2_MPEG_AUDIO_AC3_BITRATE_80K, + V4L2_MPEG_AUDIO_AC3_BITRATE_96K, + V4L2_MPEG_AUDIO_AC3_BITRATE_112K, + V4L2_MPEG_AUDIO_AC3_BITRATE_128K, + V4L2_MPEG_AUDIO_AC3_BITRATE_160K, + V4L2_MPEG_AUDIO_AC3_BITRATE_192K, + V4L2_MPEG_AUDIO_AC3_BITRATE_224K, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_320K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, + V4L2_MPEG_AUDIO_AC3_BITRATE_512K, + V4L2_MPEG_AUDIO_AC3_BITRATE_576K, + V4L2_MPEG_AUDIO_AC3_BITRATE_640K, +) = range(19) + +V4L2_CID_MPEG_VIDEO_ENCODING = V4L2_CID_MPEG_BASE + 200 + +v4l2_mpeg_video_encoding = enum +( + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, +) = range(3) + +V4L2_CID_MPEG_VIDEO_ASPECT = V4L2_CID_MPEG_BASE + 201 + +v4l2_mpeg_video_aspect = enum +( + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_4x3, + V4L2_MPEG_VIDEO_ASPECT_16x9, + V4L2_MPEG_VIDEO_ASPECT_221x100, +) = range(4) + +V4L2_CID_MPEG_VIDEO_B_FRAMES = V4L2_CID_MPEG_BASE + 202 +V4L2_CID_MPEG_VIDEO_GOP_SIZE = V4L2_CID_MPEG_BASE + 203 +V4L2_CID_MPEG_VIDEO_GOP_CLOSURE = V4L2_CID_MPEG_BASE + 204 +V4L2_CID_MPEG_VIDEO_PULLDOWN = V4L2_CID_MPEG_BASE + 205 +V4L2_CID_MPEG_VIDEO_BITRATE_MODE = V4L2_CID_MPEG_BASE + 206 + +v4l2_mpeg_video_bitrate_mode = enum +( + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, +) = range(2) + +V4L2_CID_MPEG_VIDEO_BITRATE = V4L2_CID_MPEG_BASE + 207 +V4L2_CID_MPEG_VIDEO_BITRATE_PEAK = V4L2_CID_MPEG_BASE + 208 +V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION = V4L2_CID_MPEG_BASE + 209 +V4L2_CID_MPEG_VIDEO_MUTE = V4L2_CID_MPEG_BASE + 210 +V4L2_CID_MPEG_VIDEO_MUTE_YUV = V4L2_CID_MPEG_BASE + 211 + +V4L2_CID_MPEG_CX2341X_BASE = V4L2_CTRL_CLASS_MPEG | 0x1000 +V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 0 + +v4l2_mpeg_cx2341x_video_spatial_filter_mode = enum +( + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, +) = range(2) + +V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 1 +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 2 + +v4l2_mpeg_cx2341x_video_luma_spatial_filter_type = enum +( + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, +) = range(5) + +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 3 + +v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type = enum +( + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, +) = range(2) + +V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE = V4L2_CID_MPEG_CX2341X_BASE + 4 + +v4l2_mpeg_cx2341x_video_temporal_filter_mode = enum +( + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, +) = range(2) + +V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER = V4L2_CID_MPEG_CX2341X_BASE + 5 +V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE = V4L2_CID_MPEG_CX2341X_BASE + 6 + +v4l2_mpeg_cx2341x_video_median_filter_type = enum +( + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, +) = range(5) + +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 7 +V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 8 +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM = V4L2_CID_MPEG_CX2341X_BASE + 9 +V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP = V4L2_CID_MPEG_CX2341X_BASE + 10 +V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS = V4L2_CID_MPEG_CX2341X_BASE + 11 + +V4L2_CID_CAMERA_CLASS_BASE = V4L2_CTRL_CLASS_CAMERA | 0x900 +V4L2_CID_CAMERA_CLASS = V4L2_CTRL_CLASS_CAMERA | 1 + +V4L2_CID_EXPOSURE_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 1 + +v4l2_exposure_auto_type = enum +( + V4L2_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, + V4L2_EXPOSURE_SHUTTER_PRIORITY, + V4L2_EXPOSURE_APERTURE_PRIORITY, +) = range(4) + +V4L2_CID_EXPOSURE_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 2 +V4L2_CID_EXPOSURE_AUTO_PRIORITY = V4L2_CID_CAMERA_CLASS_BASE + 3 + +V4L2_CID_PAN_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 4 +V4L2_CID_TILT_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 5 +V4L2_CID_PAN_RESET = V4L2_CID_CAMERA_CLASS_BASE + 6 +V4L2_CID_TILT_RESET = V4L2_CID_CAMERA_CLASS_BASE + 7 + +V4L2_CID_PAN_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 8 +V4L2_CID_TILT_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 9 + +V4L2_CID_FOCUS_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 10 +V4L2_CID_FOCUS_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 11 +V4L2_CID_FOCUS_AUTO = V4L2_CID_CAMERA_CLASS_BASE + 12 + +V4L2_CID_ZOOM_ABSOLUTE = V4L2_CID_CAMERA_CLASS_BASE + 13 +V4L2_CID_ZOOM_RELATIVE = V4L2_CID_CAMERA_CLASS_BASE + 14 +V4L2_CID_ZOOM_CONTINUOUS = V4L2_CID_CAMERA_CLASS_BASE + 15 + +V4L2_CID_PRIVACY = V4L2_CID_CAMERA_CLASS_BASE + 16 + +V4L2_CID_FM_TX_CLASS_BASE = V4L2_CTRL_CLASS_FM_TX | 0x900 +V4L2_CID_FM_TX_CLASS = V4L2_CTRL_CLASS_FM_TX | 1 + +V4L2_CID_RDS_TX_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 1 +V4L2_CID_RDS_TX_PI = V4L2_CID_FM_TX_CLASS_BASE + 2 +V4L2_CID_RDS_TX_PTY = V4L2_CID_FM_TX_CLASS_BASE + 3 +V4L2_CID_RDS_TX_PS_NAME = V4L2_CID_FM_TX_CLASS_BASE + 5 +V4L2_CID_RDS_TX_RADIO_TEXT = V4L2_CID_FM_TX_CLASS_BASE + 6 + +V4L2_CID_AUDIO_LIMITER_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 64 +V4L2_CID_AUDIO_LIMITER_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 65 +V4L2_CID_AUDIO_LIMITER_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 66 + +V4L2_CID_AUDIO_COMPRESSION_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 80 +V4L2_CID_AUDIO_COMPRESSION_GAIN = V4L2_CID_FM_TX_CLASS_BASE + 81 +V4L2_CID_AUDIO_COMPRESSION_THRESHOLD = V4L2_CID_FM_TX_CLASS_BASE + 82 +V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME = V4L2_CID_FM_TX_CLASS_BASE + 83 +V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME = V4L2_CID_FM_TX_CLASS_BASE + 84 + +V4L2_CID_PILOT_TONE_ENABLED = V4L2_CID_FM_TX_CLASS_BASE + 96 +V4L2_CID_PILOT_TONE_DEVIATION = V4L2_CID_FM_TX_CLASS_BASE + 97 +V4L2_CID_PILOT_TONE_FREQUENCY = V4L2_CID_FM_TX_CLASS_BASE + 98 + +V4L2_CID_TUNE_PREEMPHASIS = V4L2_CID_FM_TX_CLASS_BASE + 112 + +v4l2_preemphasis = enum +( + V4L2_PREEMPHASIS_DISABLED, + V4L2_PREEMPHASIS_50_uS, + V4L2_PREEMPHASIS_75_uS, +) = range(3) + +V4L2_CID_TUNE_POWER_LEVEL = V4L2_CID_FM_TX_CLASS_BASE + 113 +V4L2_CID_TUNE_ANTENNA_CAPACITOR = V4L2_CID_FM_TX_CLASS_BASE + 114 + + +# +# Tuning +# + +class v4l2_tuner(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('type', v4l2_tuner_type), + ('capability', ctypes.c_uint32), + ('rangelow', ctypes.c_uint32), + ('rangehigh', ctypes.c_uint32), + ('rxsubchans', ctypes.c_uint32), + ('audmode', ctypes.c_uint32), + ('signal', ctypes.c_int32), + ('afc', ctypes.c_int32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +class v4l2_modulator(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('capability', ctypes.c_uint32), + ('rangelow', ctypes.c_uint32), + ('rangehigh', ctypes.c_uint32), + ('txsubchans', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ] + + +V4L2_TUNER_CAP_LOW = 0x0001 +V4L2_TUNER_CAP_NORM = 0x0002 +V4L2_TUNER_CAP_STEREO = 0x0010 +V4L2_TUNER_CAP_LANG2 = 0x0020 +V4L2_TUNER_CAP_SAP = 0x0020 +V4L2_TUNER_CAP_LANG1 = 0x0040 +V4L2_TUNER_CAP_RDS = 0x0080 + +V4L2_TUNER_SUB_MONO = 0x0001 +V4L2_TUNER_SUB_STEREO = 0x0002 +V4L2_TUNER_SUB_LANG2 = 0x0004 +V4L2_TUNER_SUB_SAP = 0x0004 +V4L2_TUNER_SUB_LANG1 = 0x0008 +V4L2_TUNER_SUB_RDS = 0x0010 + +V4L2_TUNER_MODE_MONO = 0x0000 +V4L2_TUNER_MODE_STEREO = 0x0001 +V4L2_TUNER_MODE_LANG2 = 0x0002 +V4L2_TUNER_MODE_SAP = 0x0002 +V4L2_TUNER_MODE_LANG1 = 0x0003 +V4L2_TUNER_MODE_LANG1_LANG2 = 0x0004 + + +class v4l2_frequency(ctypes.Structure): + _fields_ = [ + ('tuner', ctypes.c_uint32), + ('type', v4l2_tuner_type), + ('frequency', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 8), + ] + + +class v4l2_hw_freq_seek(ctypes.Structure): + _fields_ = [ + ('tuner', ctypes.c_uint32), + ('type', v4l2_tuner_type), + ('seek_upward', ctypes.c_uint32), + ('wrap_around', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 8), + ] + + +# +# RDS +# + +class v4l2_rds_data(ctypes.Structure): + _fields_ = [ + ('lsb', ctypes.c_char), + ('msb', ctypes.c_char), + ('block', ctypes.c_char), + ] + + _pack_ = True + + +V4L2_RDS_BLOCK_MSK = 0x7 +V4L2_RDS_BLOCK_A = 0 +V4L2_RDS_BLOCK_B = 1 +V4L2_RDS_BLOCK_C = 2 +V4L2_RDS_BLOCK_D = 3 +V4L2_RDS_BLOCK_C_ALT = 4 +V4L2_RDS_BLOCK_INVALID = 7 + +V4L2_RDS_BLOCK_CORRECTED = 0x40 +V4L2_RDS_BLOCK_ERROR = 0x80 + + +# +# Audio +# + +class v4l2_audio(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('capability', ctypes.c_uint32), + ('mode', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_AUDCAP_STEREO = 0x00001 +V4L2_AUDCAP_AVL = 0x00002 + +V4L2_AUDMODE_AVL = 0x00001 + + +class v4l2_audioout(ctypes.Structure): + _fields_ = [ + ('index', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ('capability', ctypes.c_uint32), + ('mode', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +# +# Mpeg services (experimental) +# + +V4L2_ENC_IDX_FRAME_I = 0 +V4L2_ENC_IDX_FRAME_P = 1 +V4L2_ENC_IDX_FRAME_B = 2 +V4L2_ENC_IDX_FRAME_MASK = 0xf + + +class v4l2_enc_idx_entry(ctypes.Structure): + _fields_ = [ + ('offset', ctypes.c_uint64), + ('pts', ctypes.c_uint64), + ('length', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_ENC_IDX_ENTRIES = 64 + + +class v4l2_enc_idx(ctypes.Structure): + _fields_ = [ + ('entries', ctypes.c_uint32), + ('entries_cap', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 4), + ('entry', v4l2_enc_idx_entry * V4L2_ENC_IDX_ENTRIES), + ] + + +V4L2_ENC_CMD_START = 0 +V4L2_ENC_CMD_STOP = 1 +V4L2_ENC_CMD_PAUSE = 2 +V4L2_ENC_CMD_RESUME = 3 + +V4L2_ENC_CMD_STOP_AT_GOP_END = 1 << 0 + + +class v4l2_encoder_cmd(ctypes.Structure): + class _u(ctypes.Union): + class _s(ctypes.Structure): + _fields_ = [ + ('data', ctypes.c_uint32 * 8), + ] + + _fields_ = [ + ('raw', _s), + ] + + _fields_ = [ + ('cmd', ctypes.c_uint32), + ('flags', ctypes.c_uint32), + ('_u', _u), + ] + + _anonymous_ = ('_u',) + + +# +# Data services (VBI) +# + +class v4l2_vbi_format(ctypes.Structure): + _fields_ = [ + ('sampling_rate', ctypes.c_uint32), + ('offset', ctypes.c_uint32), + ('samples_per_line', ctypes.c_uint32), + ('sample_format', ctypes.c_uint32), + ('start', ctypes.c_int32 * 2), + ('count', ctypes.c_uint32 * 2), + ('flags', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_VBI_UNSYNC = 1 << 0 +V4L2_VBI_INTERLACED = 1 << 1 + + +class v4l2_sliced_vbi_format(ctypes.Structure): + _fields_ = [ + ('service_set', ctypes.c_uint16), + ('service_lines', ctypes.c_uint16 * 2 * 24), + ('io_size', ctypes.c_uint32), + ('reserved', ctypes.c_uint32 * 2), + ] + + +V4L2_SLICED_TELETEXT_B = 0x0001 +V4L2_SLICED_VPS = 0x0400 +V4L2_SLICED_CAPTION_525 = 0x1000 +V4L2_SLICED_WSS_625 = 0x4000 +V4L2_SLICED_VBI_525 = V4L2_SLICED_CAPTION_525 +V4L2_SLICED_VBI_625 = ( + V4L2_SLICED_TELETEXT_B | V4L2_SLICED_VPS | V4L2_SLICED_WSS_625) + + +class v4l2_sliced_vbi_cap(ctypes.Structure): + _fields_ = [ + ('service_set', ctypes.c_uint16), + ('service_lines', ctypes.c_uint16 * 2 * 24), + ('type', v4l2_buf_type), + ('reserved', ctypes.c_uint32 * 3), + ] + + +class v4l2_sliced_vbi_data(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_uint32), + ('field', ctypes.c_uint32), + ('line', ctypes.c_uint32), + ('reserved', ctypes.c_uint32), + ('data', ctypes.c_char * 48), + ] + + +# +# Sliced VBI data inserted into MPEG Streams +# + + +V4L2_MPEG_VBI_IVTV_TELETEXT_B = 1 +V4L2_MPEG_VBI_IVTV_CAPTION_525 = 4 +V4L2_MPEG_VBI_IVTV_WSS_625 = 5 +V4L2_MPEG_VBI_IVTV_VPS = 7 + + +class v4l2_mpeg_vbi_itv0_line(ctypes.Structure): + _fields_ = [ + ('id', ctypes.c_char), + ('data', ctypes.c_char * 42), + ] + + _pack_ = True + + +class v4l2_mpeg_vbi_itv0(ctypes.Structure): + _fields_ = [ + ('linemask', ctypes.c_uint32 * 2), # how to define __le32 in ctypes? + ('line', v4l2_mpeg_vbi_itv0_line * 35), + ] + + _pack_ = True + + +class v4l2_mpeg_vbi_ITV0(ctypes.Structure): + _fields_ = [ + ('line', v4l2_mpeg_vbi_itv0_line * 36), + ] + + _pack_ = True + + +V4L2_MPEG_VBI_IVTV_MAGIC0 = "itv0" +V4L2_MPEG_VBI_IVTV_MAGIC1 = "ITV0" + + +class v4l2_mpeg_vbi_fmt_ivtv(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('itv0', v4l2_mpeg_vbi_itv0), + ('ITV0', v4l2_mpeg_vbi_ITV0), + ] + + _fields_ = [ + ('magic', ctypes.c_char * 4), + ('_u', _u) + ] + + _anonymous_ = ('_u',) + _pack_ = True + + +# +# Aggregate structures +# + +class v4l2_format(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('pix', v4l2_pix_format), + ('win', v4l2_window), + ('vbi', v4l2_vbi_format), + ('sliced', v4l2_sliced_vbi_format), + ('raw_data', ctypes.c_char * 200), + ] + + _fields_ = [ + ('type', v4l2_buf_type), + ('fmt', _u), + ] + + +class v4l2_streamparm(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('capture', v4l2_captureparm), + ('output', v4l2_outputparm), + ('raw_data', ctypes.c_char * 200), + ] + + _fields_ = [ + ('type', v4l2_buf_type), + ('parm', _u) + ] + + +# +# Advanced debugging +# + +V4L2_CHIP_MATCH_HOST = 0 +V4L2_CHIP_MATCH_I2C_DRIVER = 1 +V4L2_CHIP_MATCH_I2C_ADDR = 2 +V4L2_CHIP_MATCH_AC97 = 3 + + +class v4l2_dbg_match(ctypes.Structure): + class _u(ctypes.Union): + _fields_ = [ + ('addr', ctypes.c_uint32), + ('name', ctypes.c_char * 32), + ] + + _fields_ = [ + ('type', ctypes.c_uint32), + ('_u', _u), + ] + + _anonymous_ = ('_u',) + _pack_ = True + + +class v4l2_dbg_register(ctypes.Structure): + _fields_ = [ + ('match', v4l2_dbg_match), + ('size', ctypes.c_uint32), + ('reg', ctypes.c_uint64), + ('val', ctypes.c_uint64), + ] + + _pack_ = True + + +class v4l2_dbg_chip_ident(ctypes.Structure): + _fields_ = [ + ('match', v4l2_dbg_match), + ('ident', ctypes.c_uint32), + ('revision', ctypes.c_uint32), + ] + + _pack_ = True + + +# +# ioctl codes for video devices +# + +VIDIOC_QUERYCAP = _IOR('V', 0, v4l2_capability) +VIDIOC_RESERVED = _IO('V', 1) +VIDIOC_ENUM_FMT = _IOWR('V', 2, v4l2_fmtdesc) +VIDIOC_G_FMT = _IOWR('V', 4, v4l2_format) +VIDIOC_S_FMT = _IOWR('V', 5, v4l2_format) +VIDIOC_REQBUFS = _IOWR('V', 8, v4l2_requestbuffers) +VIDIOC_QUERYBUF = _IOWR('V', 9, v4l2_buffer) +VIDIOC_G_FBUF = _IOR('V', 10, v4l2_framebuffer) +VIDIOC_S_FBUF = _IOW('V', 11, v4l2_framebuffer) +VIDIOC_OVERLAY = _IOW('V', 14, ctypes.c_int) +VIDIOC_QBUF = _IOWR('V', 15, v4l2_buffer) +VIDIOC_DQBUF = _IOWR('V', 17, v4l2_buffer) +VIDIOC_STREAMON = _IOW('V', 18, ctypes.c_int) +VIDIOC_STREAMOFF = _IOW('V', 19, ctypes.c_int) +VIDIOC_G_PARM = _IOWR('V', 21, v4l2_streamparm) +VIDIOC_S_PARM = _IOWR('V', 22, v4l2_streamparm) +VIDIOC_G_STD = _IOR('V', 23, v4l2_std_id) +VIDIOC_S_STD = _IOW('V', 24, v4l2_std_id) +VIDIOC_ENUMSTD = _IOWR('V', 25, v4l2_standard) +VIDIOC_ENUMINPUT = _IOWR('V', 26, v4l2_input) +VIDIOC_G_CTRL = _IOWR('V', 27, v4l2_control) +VIDIOC_S_CTRL = _IOWR('V', 28, v4l2_control) +VIDIOC_G_TUNER = _IOWR('V', 29, v4l2_tuner) +VIDIOC_S_TUNER = _IOW('V', 30, v4l2_tuner) +VIDIOC_G_AUDIO = _IOR('V', 33, v4l2_audio) +VIDIOC_S_AUDIO = _IOW('V', 34, v4l2_audio) +VIDIOC_QUERYCTRL = _IOWR('V', 36, v4l2_queryctrl) +VIDIOC_QUERYMENU = _IOWR('V', 37, v4l2_querymenu) +VIDIOC_G_INPUT = _IOR('V', 38, ctypes.c_int) +VIDIOC_S_INPUT = _IOWR('V', 39, ctypes.c_int) +VIDIOC_G_OUTPUT = _IOR('V', 46, ctypes.c_int) +VIDIOC_S_OUTPUT = _IOWR('V', 47, ctypes.c_int) +VIDIOC_ENUMOUTPUT = _IOWR('V', 48, v4l2_output) +VIDIOC_G_AUDOUT = _IOR('V', 49, v4l2_audioout) +VIDIOC_S_AUDOUT = _IOW('V', 50, v4l2_audioout) +VIDIOC_G_MODULATOR = _IOWR('V', 54, v4l2_modulator) +VIDIOC_S_MODULATOR = _IOW('V', 55, v4l2_modulator) +VIDIOC_G_FREQUENCY = _IOWR('V', 56, v4l2_frequency) +VIDIOC_S_FREQUENCY = _IOW('V', 57, v4l2_frequency) +VIDIOC_CROPCAP = _IOWR('V', 58, v4l2_cropcap) +VIDIOC_G_CROP = _IOWR('V', 59, v4l2_crop) +VIDIOC_S_CROP = _IOW('V', 60, v4l2_crop) +VIDIOC_G_JPEGCOMP = _IOR('V', 61, v4l2_jpegcompression) +VIDIOC_S_JPEGCOMP = _IOW('V', 62, v4l2_jpegcompression) +VIDIOC_QUERYSTD = _IOR('V', 63, v4l2_std_id) +VIDIOC_TRY_FMT = _IOWR('V', 64, v4l2_format) +VIDIOC_ENUMAUDIO = _IOWR('V', 65, v4l2_audio) +VIDIOC_ENUMAUDOUT = _IOWR('V', 66, v4l2_audioout) +VIDIOC_G_PRIORITY = _IOR('V', 67, v4l2_priority) +VIDIOC_S_PRIORITY = _IOW('V', 68, v4l2_priority) +VIDIOC_G_SLICED_VBI_CAP = _IOWR('V', 69, v4l2_sliced_vbi_cap) +VIDIOC_LOG_STATUS = _IO('V', 70) +VIDIOC_G_EXT_CTRLS = _IOWR('V', 71, v4l2_ext_controls) +VIDIOC_S_EXT_CTRLS = _IOWR('V', 72, v4l2_ext_controls) +VIDIOC_TRY_EXT_CTRLS = _IOWR('V', 73, v4l2_ext_controls) + +VIDIOC_ENUM_FRAMESIZES = _IOWR('V', 74, v4l2_frmsizeenum) +VIDIOC_ENUM_FRAMEINTERVALS = _IOWR('V', 75, v4l2_frmivalenum) +VIDIOC_G_ENC_INDEX = _IOR('V', 76, v4l2_enc_idx) +VIDIOC_ENCODER_CMD = _IOWR('V', 77, v4l2_encoder_cmd) +VIDIOC_TRY_ENCODER_CMD = _IOWR('V', 78, v4l2_encoder_cmd) + +VIDIOC_DBG_S_REGISTER = _IOW('V', 79, v4l2_dbg_register) +VIDIOC_DBG_G_REGISTER = _IOWR('V', 80, v4l2_dbg_register) + +VIDIOC_DBG_G_CHIP_IDENT = _IOWR('V', 81, v4l2_dbg_chip_ident) + +VIDIOC_S_HW_FREQ_SEEK = _IOW('V', 82, v4l2_hw_freq_seek) +VIDIOC_ENUM_DV_PRESETS = _IOWR('V', 83, v4l2_dv_enum_preset) +VIDIOC_S_DV_PRESET = _IOWR('V', 84, v4l2_dv_preset) +VIDIOC_G_DV_PRESET = _IOWR('V', 85, v4l2_dv_preset) +VIDIOC_QUERY_DV_PRESET = _IOR('V', 86, v4l2_dv_preset) +VIDIOC_S_DV_TIMINGS = _IOWR('V', 87, v4l2_dv_timings) +VIDIOC_G_DV_TIMINGS = _IOWR('V', 88, v4l2_dv_timings) + +VIDIOC_OVERLAY_OLD = _IOWR('V', 14, ctypes.c_int) +VIDIOC_S_PARM_OLD = _IOW('V', 22, v4l2_streamparm) +VIDIOC_S_CTRL_OLD = _IOW('V', 28, v4l2_control) +VIDIOC_G_AUDIO_OLD = _IOWR('V', 33, v4l2_audio) +VIDIOC_G_AUDOUT_OLD = _IOWR('V', 49, v4l2_audioout) +VIDIOC_CROPCAP_OLD = _IOR('V', 58, v4l2_cropcap) + +BASE_VIDIOC_PRIVATE = 192 diff --git a/src/stylus/style.styl b/src/stylus/style.styl index ed3dd11..03a25f4 100644 --- a/src/stylus/style.styl +++ b/src/stylus/style.styl @@ -493,18 +493,10 @@ span.unit .video text-align center - line-height 0 - min-height 200px + min-height 300px .mjpeg - line-height 10 - - .reload - float left - margin-right -48px - - &:hover - opacity 0.5 + margin-bottom 1em tt.save display inline-block