diff --git a/CMakeLists.txt b/CMakeLists.txt index 18d7cb66..d580e6d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,18 @@ find_package(libusb-1.0 REQUIRED) find_package(Threads REQUIRED) add_definitions(${QT_DEFINITIONS}) +# Add jpeg library +if (ENABLE_V4L2) + find_package(JPEG) + if (JPEG_FOUND) + add_definitions(-DHAVE_JPEG) + message( STATUS "Using JPEG library: ${JPEG_LIBRARIES}") + include_directories(${JPEG_INCLUDE_DIR}) + else() + message( STATUS "JPEG library not found, MJPEG camera format won't work in V4L2 grabber.") + endif() +endif() + # TODO[TvdZ]: This linking directory should only be added if we are cross compiling #if(NOT APPLE) # link_directories(${CMAKE_FIND_ROOT_PATH}/lib/arm-linux-gnueabihf) diff --git a/CompileHowto.md b/CompileHowto.md index 1ea16a6a..b181d055 100644 --- a/CompileHowto.md +++ b/CompileHowto.md @@ -26,7 +26,7 @@ Note: call the script with `./docker-compile.sh -h` for more options ``` sudo apt-get update -sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev +sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev ``` **on RPI you need the videocore IV headers** diff --git a/assets/webconfig/js/content_grabber.js b/assets/webconfig/js/content_grabber.js index eedd1d89..ddf21fac 100644 --- a/assets/webconfig/js/content_grabber.js +++ b/assets/webconfig/js/content_grabber.js @@ -27,7 +27,7 @@ $(document).ready( function() { //v4l $('#conf_cont').append(createRow('conf_cont_v4l')) $('#conf_cont_v4l').append(createOptPanel('fa-camera', $.i18n("edt_conf_v4l2_heading_title"), 'editor_container_v4l2', 'btn_submit_v4l2')); - $('#conf_cont_v4l').append(createHelpTable(schema.grabberV4L2.items.properties, $.i18n("edt_conf_v4l2_heading_title"))); + $('#conf_cont_v4l').append(createHelpTable(schema.grabberV4L2.properties, $.i18n("edt_conf_v4l2_heading_title"))); } else { diff --git a/config/hyperion.config.json.commented b/config/hyperion.config.json.commented index 3c9d4fa2..001c62c5 100644 --- a/config/hyperion.config.json.commented +++ b/config/hyperion.config.json.commented @@ -114,26 +114,24 @@ /// * sDHOffsetMax : area for signal detection - horizontal maximum offset value. Values between 0.0 and 1.0 /// * sDVOffsetMax : area for signal detection - vertical maximum offset value. Values between 0.0 and 1.0 "grabberV4L2" : - [ - { - "device" : "auto", - "standard" : "NO_CHANGE", - "sizeDecimation" : 8, - "priority" : 240, - "cropLeft" : 0, - "cropRight" : 0, - "cropTop" : 0, - "cropBottom" : 0, - "redSignalThreshold" : 5, - "greenSignalThreshold" : 5, - "blueSignalThreshold" : 5, - "signalDetection" : false, - "sDVOffsetMin" : 0.25, - "sDHOffsetMin" : 0.25, - "sDVOffsetMax" : 0.75, - "sDHOffsetMax" : 0.75 - } - ], + { + "device" : "auto", + "standard" : "NO_CHANGE", + "sizeDecimation" : 8, + "priority" : 240, + "cropLeft" : 0, + "cropRight" : 0, + "cropTop" : 0, + "cropBottom" : 0, + "redSignalThreshold" : 5, + "greenSignalThreshold" : 5, + "blueSignalThreshold" : 5, + "signalDetection" : false, + "sDVOffsetMin" : 0.25, + "sDHOffsetMin" : 0.25, + "sDVOffsetMax" : 0.75, + "sDHOffsetMax" : 0.75 + }, /// The configuration for the frame-grabber, contains the following items: /// * type : type of grabber. (auto|osx|dispmanx|amlogic|x11|framebuffer|qt) [auto] diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 6e360981..3a9c67c5 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -57,25 +57,23 @@ }, "grabberV4L2" : - [ - { - "device" : "auto", - "standard" : "NO_CHANGE", - "sizeDecimation" : 8, - "cropLeft" : 0, - "cropRight" : 0, - "cropTop" : 0, - "cropBottom" : 0, - "redSignalThreshold" : 5, - "greenSignalThreshold" : 5, - "blueSignalThreshold" : 5, - "signalDetection" : false, - "sDVOffsetMin" : 0.25, - "sDHOffsetMin" : 0.25, - "sDVOffsetMax" : 0.75, - "sDHOffsetMax" : 0.75 - } - ], + { + "device" : "auto", + "standard" : "NO_CHANGE", + "sizeDecimation" : 8, + "cropLeft" : 0, + "cropRight" : 0, + "cropTop" : 0, + "cropBottom" : 0, + "redSignalThreshold" : 5, + "greenSignalThreshold" : 5, + "blueSignalThreshold" : 5, + "signalDetection" : false, + "sDVOffsetMin" : 0.25, + "sDHOffsetMin" : 0.25, + "sDVOffsetMax" : 0.75, + "sDHOffsetMax" : 0.75 + }, "framegrabber" : { diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index 0d9409a3..9294ba74 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -13,6 +13,13 @@ #include #include #include +#include + +#ifdef HAVE_JPEG + #include + #include + #include +#endif /// Capture class for V4L2 devices /// @@ -78,6 +85,8 @@ public slots: void stop(); + void componentStateChanged(const hyperion::Components component, bool enable); + signals: void newFrame(const Image & image); void readError(const char* err); @@ -111,7 +120,7 @@ private: bool process_image(const void *p, int size); - void process_image(const uint8_t *p); + void process_image(const uint8_t *p, int size); int xioctl(int request, void *arg); @@ -120,17 +129,41 @@ private: void throw_errno_exception(const QString &error); private: - enum io_method { + enum io_method + { IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR }; - struct buffer { + struct buffer + { void *start; size_t length; }; +#ifdef HAVE_JPEG + struct errorManager + { + jpeg_error_mgr pub; + jmp_buf setjmp_buffer; + }; + + static void errorHandler(j_common_ptr cInfo) + { + errorManager* mgr = reinterpret_cast(cInfo->err); + longjmp(mgr->setjmp_buffer, 1); + } + + static void outputHandler(j_common_ptr cInfo) + { + // Suppress fprintf warnings. + } + + jpeg_decompress_struct* _decompress; + errorManager* _error; +#endif + private: QString _deviceName; std::map _v4lDevices; @@ -156,7 +189,7 @@ private: double _x_frac_max; double _y_frac_max; - QSocketNotifier * _streamNotifier; + QSocketNotifier *_streamNotifier; bool _initialized; bool _deviceAutoDiscoverEnabled; diff --git a/include/utils/PixelFormat.h b/include/utils/PixelFormat.h index cade1817..a3b1fca3 100644 --- a/include/utils/PixelFormat.h +++ b/include/utils/PixelFormat.h @@ -12,6 +12,9 @@ enum PixelFormat { PIXELFORMAT_BGR24, PIXELFORMAT_RGB32, PIXELFORMAT_BGR32, +#ifdef HAVE_JPEG + PIXELFORMAT_MJPEG, +#endif PIXELFORMAT_NO_CHANGE }; @@ -44,6 +47,12 @@ inline PixelFormat parsePixelFormat(QString pixelFormat) { return PIXELFORMAT_BGR32; } +#ifdef HAVE_JPEG + else if (pixelFormat == "mjpeg") + { + return PIXELFORMAT_MJPEG; + } +#endif // return the default NO_CHANGE return PIXELFORMAT_NO_CHANGE; diff --git a/libsrc/grabber/v4l2/CMakeLists.txt b/libsrc/grabber/v4l2/CMakeLists.txt index fd1fe93c..07801299 100644 --- a/libsrc/grabber/v4l2/CMakeLists.txt +++ b/libsrc/grabber/v4l2/CMakeLists.txt @@ -10,3 +10,7 @@ target_link_libraries(v4l2-grabber hyperion ${QT_LIBRARIES} ) + +if (JPEG_FOUND) + target_link_libraries(v4l2-grabber ${JPEG_LIBRARY}) +endif() diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 2f17f238..b6ba9f83 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -16,11 +16,15 @@ #include #include +#include + #include #include #include "grabber/V4L2Grabber.h" +using namespace hyperion; + #define CLEAR(x) memset(&(x), 0, sizeof(x)) V4L2Grabber::V4L2Grabber(const QString & device @@ -55,6 +59,10 @@ V4L2Grabber::V4L2Grabber(const QString & device setPixelDecimation(pixelDecimation); getV4Ldevices(); + // listen for component change for build-in grabber only + if (Hyperion::_hyperion) + connect(Hyperion::getInstance(), &Hyperion::componentStateChanged, this, &V4L2Grabber::componentStateChanged); + // init setDeviceVideoStandard(device, videoStandard); } @@ -70,11 +78,7 @@ void V4L2Grabber::uninit() if (_initialized) { Debug(_log,"uninit grabber: %s", QSTRING_CSTR(_deviceName)); - stop(); - uninit_device(); - close_device(); - _initialized = false; } } @@ -233,6 +237,9 @@ void V4L2Grabber::stop() { stop_capturing(); _streamNotifier->setEnabled(false); + uninit_device(); + close_device(); + _initialized = false; Info(_log, "Stopped"); } } @@ -518,74 +525,109 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) // set the requested pixel format switch (_pixelFormat) { - case PIXELFORMAT_UYVY: - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + case PIXELFORMAT_UYVY: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; break; - case PIXELFORMAT_YUYV: - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + case PIXELFORMAT_YUYV: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; break; - case PIXELFORMAT_RGB32: - fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; + case PIXELFORMAT_RGB32: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; break; - case PIXELFORMAT_NO_CHANGE: - default: - // No change to device settings +#ifdef HAVE_JPEG + case PIXELFORMAT_MJPEG: + { + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + fmt.fmt.pix.field = V4L2_FIELD_ANY; + } + break; +#endif + case PIXELFORMAT_NO_CHANGE: + default: + // No change to device settings break; } -// TODO Does never accept own sizes? use always _imageResampler instead -/* - - // calc the size based on pixelDecimation - fmt.fmt.pix.width = fmt.fmt.pix.width / _pixelDecimation; - fmt.fmt.pix.height = fmt.fmt.pix.height / _pixelDecimation; - // set the settings + fmt.fmt.pix.width = _width; + fmt.fmt.pix.height = _height; + if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) { throw_errno_exception("VIDIOC_S_FMT"); return; } - // get the format settings again - // (the size may not have been accepted without an error) - if (-1 == xioctl(VIDIOC_G_FMT, &fmt)) - { - throw_errno_exception("VIDIOC_G_FMT"); - return; - } -*/ - // set the line length - _lineLength = fmt.fmt.pix.bytesperline; - - // store width & height + // initialize current width and height _width = fmt.fmt.pix.width; _height = fmt.fmt.pix.height; // display the used width and height Debug(_log, "width=%d height=%d", _width, _height ); + // Trying to set frame rate + struct v4l2_streamparm streamparms; + CLEAR(streamparms); + streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(VIDIOC_G_PARM, &streamparms)) + { + throw_errno_exception("VIDIOC_G_PARM"); + // continue + } + else + { + // Check the capability flag is set to V4L2_CAP_TIMEPERFRAME + if (streamparms.parm.capture.capability == V4L2_CAP_TIMEPERFRAME) + { + // Driver supports the feature. Set required framerate + streamparms.parm.capture.capturemode = V4L2_MODE_HIGHQUALITY; + streamparms.parm.capture.timeperframe.numerator = 1; + streamparms.parm.capture.timeperframe.denominator = 30; + if(-1 == xioctl(VIDIOC_S_PARM, &streamparms)) + { + throw_errno_exception("VIDIOC_S_PARM"); + return; + } + } + } + + // set the line length + _lineLength = fmt.fmt.pix.bytesperline; // check pixel format and frame size switch (fmt.fmt.pix.pixelformat) { - case V4L2_PIX_FMT_UYVY: - _pixelFormat = PIXELFORMAT_UYVY; - _frameByteSize = _width * _height * 2; - Debug(_log, "Pixel format=UYVY"); + case V4L2_PIX_FMT_UYVY: + { + _pixelFormat = PIXELFORMAT_UYVY; + _frameByteSize = _width * _height * 2; + Debug(_log, "Pixel format=UYVY"); + } break; - case V4L2_PIX_FMT_YUYV: - _pixelFormat = PIXELFORMAT_YUYV; - _frameByteSize = _width * _height * 2; - Debug(_log, "Pixel format=YUYV"); + case V4L2_PIX_FMT_YUYV: + { + _pixelFormat = PIXELFORMAT_YUYV; + _frameByteSize = _width * _height * 2; + Debug(_log, "Pixel format=YUYV"); + } break; - case V4L2_PIX_FMT_RGB32: - _pixelFormat = PIXELFORMAT_RGB32; - _frameByteSize = _width * _height * 4; - Debug(_log, "Pixel format=RGB32"); + case V4L2_PIX_FMT_RGB32: + { + _pixelFormat = PIXELFORMAT_RGB32; + _frameByteSize = _width * _height * 4; + Debug(_log, "Pixel format=RGB32"); + } break; - default: - throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported"); +#ifdef HAVE_JPEG + case V4L2_PIX_FMT_MJPEG: + { + _pixelFormat = PIXELFORMAT_MJPEG; + Debug(_log, "Pixel format=MJPEG"); + } + break; +#endif + default: + throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported"); return; } @@ -714,107 +756,108 @@ int V4L2Grabber::read_frame() { struct v4l2_buffer buf; - switch (_ioMethod) { - case IO_METHOD_READ: - int size; - if ((size = read(_fileDescriptor, _buffers[0].start, _buffers[0].length)) == -1) - { - switch (errno) + switch (_ioMethod) + { + case IO_METHOD_READ: + int size; + if ((size = read(_fileDescriptor, _buffers[0].start, _buffers[0].length)) == -1) { - case EAGAIN: - return 0; + switch (errno) + { + case EAGAIN: + return 0; - case EIO: - /* Could ignore EIO, see spec. */ + case EIO: + /* Could ignore EIO, see spec. */ - /* fall through */ + /* fall through */ - default: - throw_errno_exception("read"); + default: + throw_errno_exception("read"); + return 0; + } + } + + rc = process_image(_buffers[0].start, size); + break; + + case IO_METHOD_MMAP: + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(VIDIOC_DQBUF, &buf)) + { + switch (errno) + { + case EAGAIN: + return 0; + + case EIO: + /* Could ignore EIO, see spec. */ + + /* fall through */ + + default: + throw_errno_exception("VIDIOC_DQBUF"); + return 0; + } + } + + assert(buf.index < _buffers.size()); + + rc = process_image(_buffers[buf.index].start, buf.bytesused); + + if (-1 == xioctl(VIDIOC_QBUF, &buf)) + { + throw_errno_exception("VIDIOC_QBUF"); return 0; } - } - rc = process_image(_buffers[0].start, size); - break; + break; - case IO_METHOD_MMAP: - CLEAR(buf); + case IO_METHOD_USERPTR: + CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_USERPTR; - if (-1 == xioctl(VIDIOC_DQBUF, &buf)) - { - switch (errno) + if (-1 == xioctl(VIDIOC_DQBUF, &buf)) { - case EAGAIN: - return 0; + switch (errno) + { + case EAGAIN: + return 0; - case EIO: - /* Could ignore EIO, see spec. */ + case EIO: + /* Could ignore EIO, see spec. */ - /* fall through */ + /* fall through */ - default: - throw_errno_exception("VIDIOC_DQBUF"); + default: + throw_errno_exception("VIDIOC_DQBUF"); + return 0; + } + } + + for (size_t i = 0; i < _buffers.size(); ++i) + { + if (buf.m.userptr == (unsigned long)_buffers[i].start && buf.length == _buffers[i].length) + { + break; + } + } + + rc = process_image((void *)buf.m.userptr, buf.bytesused); + + if (-1 == xioctl(VIDIOC_QBUF, &buf)) + { + throw_errno_exception("VIDIOC_QBUF"); return 0; } + break; } - - assert(buf.index < _buffers.size()); - - rc = process_image(_buffers[buf.index].start, buf.bytesused); - - if (-1 == xioctl(VIDIOC_QBUF, &buf)) - { - throw_errno_exception("VIDIOC_QBUF"); - return 0; - } - - break; - - case IO_METHOD_USERPTR: - CLEAR(buf); - - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_USERPTR; - - if (-1 == xioctl(VIDIOC_DQBUF, &buf)) - { - switch (errno) - { - case EAGAIN: - return 0; - - case EIO: - /* Could ignore EIO, see spec. */ - - /* fall through */ - - default: - throw_errno_exception("VIDIOC_DQBUF"); - return 0; - } - } - - for (size_t i = 0; i < _buffers.size(); ++i) - { - if (buf.m.userptr == (unsigned long)_buffers[i].start && buf.length == _buffers[i].length) - { - break; - } - } - - rc = process_image((void *)buf.m.userptr, buf.bytesused); - - if (-1 == xioctl(VIDIOC_QBUF, &buf)) - { - throw_errno_exception("VIDIOC_QBUF"); - return 0; - } - break; - } } catch (std::exception& e) { @@ -828,23 +871,101 @@ int V4L2Grabber::read_frame() bool V4L2Grabber::process_image(const void *p, int size) { // We do want a new frame... +#ifdef HAVE_JPEG + if (size != _frameByteSize && _pixelFormat != PIXELFORMAT_MJPEG) +#else if (size != _frameByteSize) +#endif { Error(_log, "Frame too small: %d != %d", size, _frameByteSize); } else { - process_image(reinterpret_cast(p)); + process_image(reinterpret_cast(p), size); return true; } return false; } -void V4L2Grabber::process_image(const uint8_t * data) +void V4L2Grabber::process_image(const uint8_t * data, int size) { - Image image(0, 0); - _imageResampler.processImage(data, _width, _height, _lineLength, _pixelFormat, image); + Image image(_width, _height); + +#ifdef HAVE_JPEG + if (_pixelFormat == PIXELFORMAT_MJPEG) + { + _decompress = new jpeg_decompress_struct; + _error = new errorManager; + + _decompress->err = jpeg_std_error(&_error->pub); + _error->pub.error_exit = &errorHandler; + _error->pub.output_message = &outputHandler; + + jpeg_create_decompress(_decompress); + + if (setjmp(_error->setjmp_buffer)) + { + jpeg_abort_decompress(_decompress); + jpeg_destroy_decompress(_decompress); + delete _decompress; + delete _error; + return; + } + + jpeg_mem_src(_decompress, const_cast(data), size); + + if (jpeg_read_header(_decompress, (bool) TRUE) != JPEG_HEADER_OK) + { + jpeg_abort_decompress(_decompress); + jpeg_destroy_decompress(_decompress); + delete _decompress; + delete _error; + return; + } + + jpeg_start_decompress(_decompress); + + QImage imageFrame = QImage(_decompress->output_width, _decompress->output_height, QImage::Format_RGB888); + + int y = 0; + while (_decompress->output_scanline < _decompress->output_height) + { + uchar *row = imageFrame.scanLine(_decompress->output_scanline); + jpeg_read_scanlines(_decompress, &row, 1); + y++; + } + + jpeg_finish_decompress(_decompress); + jpeg_destroy_decompress(_decompress); + delete _decompress; + delete _error; + + if (imageFrame.isNull()) + return; + + QRect rect(_cropLeft, _cropTop, imageFrame.width() - _cropLeft - _cropRight, imageFrame.height() - _cropTop - _cropBottom); + imageFrame = imageFrame.copy(rect); + imageFrame = imageFrame.scaled(imageFrame.width() / _pixelDecimation, imageFrame.height() / _pixelDecimation,Qt::KeepAspectRatio); + + if ((image.width() != unsigned(imageFrame.width())) && (image.height() != unsigned(imageFrame.height()))) + image.resize(imageFrame.width(), imageFrame.height()); + + for (int y=0; y> 16; + outPixel.green = (inPixel & 0xff00) >> 8; + outPixel.blue = (inPixel & 0xff); + } + } + } + else +#endif + _imageResampler.processImage(data, _width, _height, _lineLength, _pixelFormat, image); if (_signalDetectionEnabled) { @@ -963,3 +1084,19 @@ void V4L2Grabber::setDeviceVideoStandard(QString device, VideoStandard videoStan start(); } } + +void V4L2Grabber::componentStateChanged(const hyperion::Components component, bool enable) +{ + if (component == COMP_V4L) + { + if (_initialized != enable) + { + if (enable) + { + if(init()) start(); + } + else + uninit(); + } + } +} diff --git a/libsrc/hyperion/PriorityMuxer.cpp b/libsrc/hyperion/PriorityMuxer.cpp index b22a1800..6d5473c4 100644 --- a/libsrc/hyperion/PriorityMuxer.cpp +++ b/libsrc/hyperion/PriorityMuxer.cpp @@ -294,7 +294,7 @@ void PriorityMuxer::setCurrentTime(void) else { // timeoutTime of -100 is awaiting data (inactive); skip - if(infoIt->timeoutTime_ms >= -100) + if(infoIt->timeoutTime_ms > -100) newPriority = qMin(newPriority, infoIt->priority); // call timeTrigger when effect or color is running with timeout > 0, blacklist prio 255 diff --git a/libsrc/hyperion/schema/schema-grabberV4L2.json b/libsrc/hyperion/schema/schema-grabberV4L2.json index 01df9d8d..1808f04d 100644 --- a/libsrc/hyperion/schema/schema-grabberV4L2.json +++ b/libsrc/hyperion/schema/schema-grabberV4L2.json @@ -1,208 +1,200 @@ { - "type":"array", + "type" : "object", "required" : true, "title" : "edt_conf_v4l2_heading_title", - "minItems": 1, - "maxItems": 1, - "items": + "properties" : { - "type" : "object", - "required" : true, - "title" : "edt_conf_v4l2_heading_title", - "properties" : + "device" : { - "device" : - { - "type" : "string", - "title" : "edt_conf_v4l2_device_title", - "default" : "auto", - "minLength" : 4, - "required" : true, - "propertyOrder" : 1 - }, - "standard" : - { - "type" : "string", - "title" : "edt_conf_v4l2_standard_title", - "enum" : ["PAL","NTSC","SECAM","NO_CHANGE"], - "default" : "NO_CHANGE", - "options" : { - "enum_titles" : ["edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM", "edt_conf_enum_NO_CHANGE"] - }, - "required" : true, - "propertyOrder" : 2 - }, - "sizeDecimation" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_sizeDecimation_title", - "minimum" : 1, - "maximum" : 30, - "default" : 6, - "required" : true, - "propertyOrder" : 3 - }, - "cropLeft" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_cropLeft_title", - "minimum" : 0, - "default" : 0, - "append" : "edt_append_pixel", - "required" : true, - "propertyOrder" : 4 - }, - "cropRight" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_cropRight_title", - "minimum" : 0, - "default" : 0, - "append" : "edt_append_pixel", - "required" : true, - "propertyOrder" : 5 - }, - "cropTop" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_cropTop_title", - "minimum" : 0, - "default" : 0, - "append" : "edt_append_pixel", - "required" : true, - "propertyOrder" : 6 - }, - "cropBottom" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_cropBottom_title", - "minimum" : 0, - "default" : 0, - "append" : "edt_append_pixel", - "required" : true, - "propertyOrder" : 7 - }, - "signalDetection" : - { - "type" : "boolean", - "title" : "edt_conf_v4l2_signalDetection_title", - "default" : false, - "required" : true, - "propertyOrder" : 8 - }, - "redSignalThreshold" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_redSignalThreshold_title", - "minimum" : 0, - "maximum" : 100, - "default" : 5, - "append" : "edt_append_percent", - "options": { - "dependencies": { - "signalDetection": true - } - }, - "required" : true, - "propertyOrder" : 9 - }, - "greenSignalThreshold" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_greenSignalThreshold_title", - "minimum" : 0, - "maximum" : 100, - "default" : 5, - "append" : "edt_append_percent", - "options": { - "dependencies": { - "signalDetection": true - } - }, - "required" : true, - "propertyOrder" : 10 - }, - "blueSignalThreshold" : - { - "type" : "integer", - "title" : "edt_conf_v4l2_blueSignalThreshold_title", - "minimum" : 0, - "maximum" : 100, - "default" : 5, - "append" : "edt_append_percent", - "options": { - "dependencies": { - "signalDetection": true - } - }, - "required" : true, - "propertyOrder" : 11 - }, - "sDVOffsetMin" : - { - "type" : "number", - "title" : "edt_conf_v4l2_sDVOffsetMin_title", - "minimum" : 0.0, - "maximum" : 1.0, - "default" : 0.25, - "step" : 0.01, - "options": { - "dependencies": { - "signalDetection": true - } - }, - "required" : true, - "propertyOrder" : 12 - }, - "sDVOffsetMax" : - { - "type" : "number", - "title" : "edt_conf_v4l2_sDVOffsetMax_title", - "minimum" : 0.0, - "maximum" : 1.0, - "default" : 0.75, - "step" : 0.01, - "options": { - "dependencies": { - "signalDetection": true - } - }, - "required" : true, - "propertyOrder" : 13 - }, - "sDHOffsetMin" : - { - "type" : "number", - "title" : "edt_conf_v4l2_sDHOffsetMin_title", - "minimum" : 0.0, - "maximum" : 1.0, - "default" : 0.25, - "step" : 0.01, - "options": { - "dependencies": { - "signalDetection": true - } - }, - "required" : true, - "propertyOrder" : 14 - }, - "sDHOffsetMax" : - { - "type" : "number", - "title" : "edt_conf_v4l2_sDHOffsetMax_title", - "minimum" : 0.0, - "maximum" : 1.0, - "default" : 0.75, - "step" : 0.01, - "options": { - "dependencies": { - "signalDetection": true - } - }, - "required" : true, - "propertyOrder" : 15 - } + "type" : "string", + "title" : "edt_conf_v4l2_device_title", + "default" : "auto", + "minLength" : 4, + "required" : true, + "propertyOrder" : 1 }, + "standard" : + { + "type" : "string", + "title" : "edt_conf_v4l2_standard_title", + "enum" : ["PAL","NTSC","SECAM","NO_CHANGE"], + "default" : "NO_CHANGE", + "options" : { + "enum_titles" : ["edt_conf_enum_PAL", "edt_conf_enum_NTSC", "edt_conf_enum_SECAM", "edt_conf_enum_NO_CHANGE"] + }, + "required" : true, + "propertyOrder" : 2 + }, + "sizeDecimation" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_sizeDecimation_title", + "minimum" : 1, + "maximum" : 30, + "default" : 6, + "required" : true, + "propertyOrder" : 3 + }, + "cropLeft" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_cropLeft_title", + "minimum" : 0, + "default" : 0, + "append" : "edt_append_pixel", + "required" : true, + "propertyOrder" : 4 + }, + "cropRight" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_cropRight_title", + "minimum" : 0, + "default" : 0, + "append" : "edt_append_pixel", + "required" : true, + "propertyOrder" : 5 + }, + "cropTop" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_cropTop_title", + "minimum" : 0, + "default" : 0, + "append" : "edt_append_pixel", + "required" : true, + "propertyOrder" : 6 + }, + "cropBottom" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_cropBottom_title", + "minimum" : 0, + "default" : 0, + "append" : "edt_append_pixel", + "required" : true, + "propertyOrder" : 7 + }, + "signalDetection" : + { + "type" : "boolean", + "title" : "edt_conf_v4l2_signalDetection_title", + "default" : false, + "required" : true, + "propertyOrder" : 8 + }, + "redSignalThreshold" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_redSignalThreshold_title", + "minimum" : 0, + "maximum" : 100, + "default" : 5, + "append" : "edt_append_percent", + "options": { + "dependencies": { + "signalDetection": true + } + }, + "required" : true, + "propertyOrder" : 9 + }, + "greenSignalThreshold" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_greenSignalThreshold_title", + "minimum" : 0, + "maximum" : 100, + "default" : 5, + "append" : "edt_append_percent", + "options": { + "dependencies": { + "signalDetection": true + } + }, + "required" : true, + "propertyOrder" : 10 + }, + "blueSignalThreshold" : + { + "type" : "integer", + "title" : "edt_conf_v4l2_blueSignalThreshold_title", + "minimum" : 0, + "maximum" : 100, + "default" : 5, + "append" : "edt_append_percent", + "options": { + "dependencies": { + "signalDetection": true + } + }, + "required" : true, + "propertyOrder" : 11 + }, + "sDVOffsetMin" : + { + "type" : "number", + "title" : "edt_conf_v4l2_sDVOffsetMin_title", + "minimum" : 0.0, + "maximum" : 1.0, + "default" : 0.25, + "step" : 0.01, + "options": { + "dependencies": { + "signalDetection": true + } + }, + "required" : true, + "propertyOrder" : 12 + }, + "sDVOffsetMax" : + { + "type" : "number", + "title" : "edt_conf_v4l2_sDVOffsetMax_title", + "minimum" : 0.0, + "maximum" : 1.0, + "default" : 0.75, + "step" : 0.01, + "options": { + "dependencies": { + "signalDetection": true + } + }, + "required" : true, + "propertyOrder" : 13 + }, + "sDHOffsetMin" : + { + "type" : "number", + "title" : "edt_conf_v4l2_sDHOffsetMin_title", + "minimum" : 0.0, + "maximum" : 1.0, + "default" : 0.25, + "step" : 0.01, + "options": { + "dependencies": { + "signalDetection": true + } + }, + "required" : true, + "propertyOrder" : 14 + }, + "sDHOffsetMax" : + { + "type" : "number", + "title" : "edt_conf_v4l2_sDHOffsetMax_title", + "minimum" : 0.0, + "maximum" : 1.0, + "default" : 0.75, + "step" : 0.01, + "options": { + "dependencies": { + "signalDetection": true + } + }, + "required" : true, + "propertyOrder" : 15 + } + }, "additionalProperties" : false - } } diff --git a/libsrc/utils/ImageResampler.cpp b/libsrc/utils/ImageResampler.cpp index b0fccdf9..017712fb 100644 --- a/libsrc/utils/ImageResampler.cpp +++ b/libsrc/utils/ImageResampler.cpp @@ -121,6 +121,10 @@ void ImageResampler::processImage(const uint8_t * data, int width, int height, i rgb.red = data[index+2]; } break; +#ifdef HAVE_JPEG + case PIXELFORMAT_MJPEG: + break; +#endif case PIXELFORMAT_NO_CHANGE: Error(Logger::getInstance("ImageResampler"), "Invalid pixel format given"); break; diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index 4621a5a9..54c507f3 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -60,7 +60,7 @@ int main(int argc, char** argv) Option & argDevice = parser.add