diff --git a/include/grabber/PixelFormat.h b/include/grabber/PixelFormat.h new file mode 100644 index 00000000..6afc7510 --- /dev/null +++ b/include/grabber/PixelFormat.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +/** + * Enumeration of the possible pixel formats the grabber can be set to + */ +enum PixelFormat { + PIXELFORMAT_YUYV, + PIXELFORMAT_UYVY, + PIXELFORMAT_RGB32, + PIXELFORMAT_NO_CHANGE +}; + +inline PixelFormat parsePixelFormat(std::string pixelFormat) +{ + // convert to lower case + std::transform(pixelFormat.begin(), pixelFormat.end(), pixelFormat.begin(), ::tolower); + + if (pixelFormat == "yuyv") + { + return PIXELFORMAT_YUYV; + } + else if (pixelFormat == "uyvy") + { + return PIXELFORMAT_UYVY; + } + else if (pixelFormat == "rgb32") + { + return PIXELFORMAT_RGB32; + } + + // return the default NO_CHANGE + return PIXELFORMAT_NO_CHANGE; +} diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index ff600108..05941fb7 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -15,6 +15,7 @@ // grabber includes #include +#include /// Capture class for V4L2 devices /// @@ -26,7 +27,7 @@ class V4L2Grabber : public QObject public: V4L2Grabber(const std::string & device, int input, - VideoStandard videoStandard, + VideoStandard videoStandard, PixelFormat pixelFormat, int width, int height, int frameDecimation, @@ -104,7 +105,7 @@ private: int _fileDescriptor; std::vector _buffers; - uint32_t _pixelFormat; + PixelFormat _pixelFormat; int _width; int _height; int _cropLeft; diff --git a/include/grabber/V4L2Wrapper.h b/include/grabber/V4L2Wrapper.h index 49958a15..2a9ece2c 100644 --- a/include/grabber/V4L2Wrapper.h +++ b/include/grabber/V4L2Wrapper.h @@ -18,6 +18,7 @@ public: V4L2Wrapper(const std::string & device, int input, VideoStandard videoStandard, + PixelFormat pixelFormat, int width, int height, int frameDecimation, diff --git a/libsrc/grabber/v4l2/CMakeLists.txt b/libsrc/grabber/v4l2/CMakeLists.txt index 5c7844e0..540a1217 100644 --- a/libsrc/grabber/v4l2/CMakeLists.txt +++ b/libsrc/grabber/v4l2/CMakeLists.txt @@ -9,6 +9,7 @@ SET(V4L2_QT_HEADERS SET(V4L2_HEADERS ${CURRENT_HEADER_DIR}/VideoStandard.h + ${CURRENT_HEADER_DIR}/PixelFormat.h ) SET(V4L2_SOURCES diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index aa9fa911..fc76c06e 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -39,6 +39,7 @@ static void yuv2rgb(uint8_t y, uint8_t u, uint8_t v, uint8_t & r, uint8_t & g, u V4L2Grabber::V4L2Grabber(const std::string & device, int input, VideoStandard videoStandard, + PixelFormat pixelFormat, int width, int height, int frameDecimation, @@ -48,7 +49,7 @@ V4L2Grabber::V4L2Grabber(const std::string & device, _ioMethod(IO_METHOD_MMAP), _fileDescriptor(-1), _buffers(), - _pixelFormat(0), + _pixelFormat(pixelFormat), _width(width), _height(height), _cropLeft(0), @@ -380,17 +381,25 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) throw_errno_exception("VIDIOC_G_FMT"); } - // check pixel format - switch (fmt.fmt.pix.pixelformat) + // set the requested pixel format + switch (_pixelFormat) { - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - _pixelFormat = fmt.fmt.pix.pixelformat; + case PIXELFORMAT_UYVY: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; break; + case PIXELFORMAT_YUYV: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case PIXELFORMAT_RGB32: + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; + break; + case PIXELFORMAT_NO_CHANGE: default: - throw_exception("Only pixel formats UYVY and YUYV are supported"); + // No change to device settings + break; } + // set the requested withd and height if (_width > 0 || _height > 0) { if (_width > 0) @@ -402,19 +411,38 @@ void V4L2Grabber::init_device(VideoStandard videoStandard, int input) { fmt.fmt.pix.height = _height; } + } - // set the settings - if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) - { - throw_errno_exception("VIDIOC_S_FMT"); - } + // set the settings + if (-1 == xioctl(VIDIOC_S_FMT, &fmt)) + { + throw_errno_exception("VIDIOC_S_FMT"); + } - // 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"); - } + // 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"); + } + + // check pixel format + switch (fmt.fmt.pix.pixelformat) + { + case V4L2_PIX_FMT_UYVY: + _pixelFormat = PIXELFORMAT_UYVY; + std::cout << "V4L2 pixel format=UYVY" << std::endl; + break; + case V4L2_PIX_FMT_YUYV: + _pixelFormat = PIXELFORMAT_YUYV; + std::cout << "V4L2 pixel format=YUYV" << std::endl; + break; + case V4L2_PIX_FMT_RGB32: + _pixelFormat = PIXELFORMAT_RGB32; + std::cout << "V4L2 pixel format=RGB32" << std::endl; + break; + default: + throw_exception("Only pixel formats UYVY, YUYV, and RGB32 are supported"); } // store width & height @@ -680,27 +708,40 @@ void V4L2Grabber::process_image(const uint8_t * data) { for (int xSource = _cropLeft + _horizontalPixelDecimation/2, xDest = 0; xSource < width - _cropRight; xSource += _horizontalPixelDecimation, ++xDest) { - int index = (_width * ySource + xSource) * 2; - uint8_t y = 0; - uint8_t u = 0; - uint8_t v = 0; + ColorRgb & rgb = image(xDest, yDest); switch (_pixelFormat) { - case V4L2_PIX_FMT_UYVY: - y = data[index+1]; - u = (xSource%2 == 0) ? data[index ] : data[index-2]; - v = (xSource%2 == 0) ? data[index+2] : data[index ]; + case PIXELFORMAT_UYVY: + { + int index = (_width * ySource + xSource) * 2; + uint8_t y = data[index+1]; + uint8_t u = (xSource%2 == 0) ? data[index ] : data[index-2]; + uint8_t v = (xSource%2 == 0) ? data[index+2] : data[index ]; + yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); + } break; - case V4L2_PIX_FMT_YUYV: - y = data[index]; - u = (xSource%2 == 0) ? data[index+1] : data[index-1]; - v = (xSource%2 == 0) ? data[index+3] : data[index+1]; + case PIXELFORMAT_YUYV: + { + int index = (_width * ySource + xSource) * 2; + uint8_t y = data[index]; + uint8_t u = (xSource%2 == 0) ? data[index+1] : data[index-1]; + uint8_t v = (xSource%2 == 0) ? data[index+3] : data[index+1]; + yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); + } + break; + case PIXELFORMAT_RGB32: + { + int index = (_width * ySource + xSource) * 4; + rgb.red = data[index+1]; + rgb.green = data[index+2]; + rgb.blue = data[index+3]; + } + break; + default: + // this should not be possible break; } - - ColorRgb & rgb = image(xDest, yDest); - yuv2rgb(y, u, v, rgb.red, rgb.green, rgb.blue); } } diff --git a/libsrc/grabber/v4l2/V4L2Wrapper.cpp b/libsrc/grabber/v4l2/V4L2Wrapper.cpp index 860cc555..76b16ab7 100644 --- a/libsrc/grabber/v4l2/V4L2Wrapper.cpp +++ b/libsrc/grabber/v4l2/V4L2Wrapper.cpp @@ -7,6 +7,7 @@ V4L2Wrapper::V4L2Wrapper(const std::string &device, int input, VideoStandard videoStandard, + PixelFormat pixelFormat, int width, int height, int frameDecimation, @@ -21,6 +22,7 @@ V4L2Wrapper::V4L2Wrapper(const std::string &device, _grabber(device, input, videoStandard, + pixelFormat, width, height, frameDecimation, diff --git a/src/hyperion-v4l2/CMakeLists.txt b/src/hyperion-v4l2/CMakeLists.txt index 0cf387af..7d13baf8 100644 --- a/src/hyperion-v4l2/CMakeLists.txt +++ b/src/hyperion-v4l2/CMakeLists.txt @@ -21,6 +21,7 @@ set(Hyperion_V4L2_QT_HEADERS set(Hyperion_V4L2_HEADERS VideoStandardParameter.h + PixelFormatParameter.h ProtoConnection.h ) diff --git a/src/hyperion-v4l2/PixelFormatParameter.h b/src/hyperion-v4l2/PixelFormatParameter.h new file mode 100644 index 00000000..f9dff93e --- /dev/null +++ b/src/hyperion-v4l2/PixelFormatParameter.h @@ -0,0 +1,43 @@ +// getoptPlusPLus includes +#include + +// grabber includes +#include + +using namespace vlofgren; + +/// Data parameter for the pixel format +typedef vlofgren::PODParameter PixelFormatParameter; + +namespace vlofgren { + /// Translates a string (as passed on the commandline) to a pixel format + /// + /// @param[in] s The string (as passed on the commandline) + /// @return The pixel format + /// @throws Parameter::ParameterRejected If the string did not result in a pixel format + template<> + PixelFormat PixelFormatParameter::validate(const std::string& s) throw (Parameter::ParameterRejected) + { + QString input = QString::fromStdString(s).toLower(); + + if (input == "yuyv") + { + return PIXELFORMAT_YUYV; + } + else if (input == "uyvy") + { + return PIXELFORMAT_UYVY; + } + else if (input == "rgb32") + { + return PIXELFORMAT_RGB32; + } + else if (input == "no-change") + { + return PIXELFORMAT_NO_CHANGE; + } + + throw Parameter::ParameterRejected("Invalid value for pixel format. Valid values are: YUYV, UYVY, RGB32, and NO-CHANGE"); + return PIXELFORMAT_NO_CHANGE; + } +} diff --git a/src/hyperion-v4l2/hyperion-v4l2.cpp b/src/hyperion-v4l2/hyperion-v4l2.cpp index 611abe3f..86c23781 100644 --- a/src/hyperion-v4l2/hyperion-v4l2.cpp +++ b/src/hyperion-v4l2/hyperion-v4l2.cpp @@ -18,6 +18,7 @@ // hyperion-v4l2 includes #include "ProtoConnection.h" #include "VideoStandardParameter.h" +#include "PixelFormatParameter.h" #include "ImageHandler.h" #include "ScreenshotHandler.h" @@ -50,6 +51,7 @@ int main(int argc, char** argv) StringParameter & argDevice = parameters.add ('d', "device", "The device to use [default=/dev/video0]"); VideoStandardParameter & argVideoStandard = parameters.add('v', "video-standard", "The used video standard. Valid values are PAL or NTSC (optional)"); + PixelFormatParameter & argPixelFormat = parameters.add (0x0, "pixel-format", "The use pixel format. Valid values are YUYV, UYVY, and RGB32 (optional)"); IntParameter & argInput = parameters.add (0x0, "input", "Input channel (optional)"); IntParameter & argWidth = parameters.add (0x0, "width", "Try to set the width of the video input (optional)"); IntParameter & argHeight = parameters.add (0x0, "height", "Try to set the height of the video input (optional)"); @@ -76,6 +78,7 @@ int main(int argc, char** argv) // set defaults argDevice.setDefault("/dev/video0"); argVideoStandard.setDefault(VIDEOSTANDARD_NO_CHANGE); + argPixelFormat.setDefault(PIXELFORMAT_NO_CHANGE); argInput.setDefault(-1); argWidth.setDefault(-1); argHeight.setDefault(-1); @@ -107,6 +110,7 @@ int main(int argc, char** argv) argDevice.getValue(), argInput.getValue(), argVideoStandard.getValue(), + argPixelFormat.getValue(), argWidth.getValue(), argHeight.getValue(), std::max(1, argFrameDecimation.getValue()), diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index d31e9f07..c6174fa9 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -180,6 +180,7 @@ int main(int argc, char** argv) grabberConfig.get("device", "/dev/video0").asString(), grabberConfig.get("input", 0).asInt(), parseVideoStandard(grabberConfig.get("standard", "no-change").asString()), + parsePixelFormat(grabberConfig.get("pixelFormat", "no-change").asString()), grabberConfig.get("width", -1).asInt(), grabberConfig.get("height", -1).asInt(), grabberConfig.get("frameDecimation", 2).asInt(),