diff --git a/.vscode/launch.json b/.vscode/launch.json index a2ec8824..f1eb3533 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "(Linux) hyperiond", "type": "cppdbg", "request": "launch", - "program": "${command:cmake.launchTargetDirectory}/hyperiond", + "program": "${workspaceFolder}/build/bin/hyperiond", "args": ["-d"], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/CMakeLists.txt b/CMakeLists.txt index e34e2c72..5805d190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ SET ( DEFAULT_EXPERIMENTAL OFF ) SET ( DEFAULT_MF OFF ) IF ( ${CMAKE_SYSTEM} MATCHES "Linux" ) - SET ( DEFAULT_V4L2 OFF ) # TODO: Reactivate when refactor of V4L2 grabber is finished. + SET ( DEFAULT_V4L2 ON ) SET ( DEFAULT_SPIDEV ON ) SET ( DEFAULT_TINKERFORGE ON ) SET ( DEFAULT_FB ON ) diff --git a/include/grabber/MFGrabber.h b/include/grabber/MFGrabber.h index 8f51d52d..7ad2094b 100644 --- a/include/grabber/MFGrabber.h +++ b/include/grabber/MFGrabber.h @@ -61,6 +61,7 @@ public: bool getCecDetectionEnabled() const { return _cecDetectionEnabled; } QStringList getDevices() const override; QString getDeviceName(const QString& devicePath) const override { return devicePath; } + QMultiMap getDeviceInputs(const QString& devicePath) const override { return {{ devicePath, 0}}; } QStringList getAvailableEncodingFormats(const QString& devicePath, const int& /*deviceInput*/) const override; QStringList getAvailableDeviceResolutions(const QString& devicePath, const int& /*deviceInput*/, const PixelFormat& encFormat) const override; QStringList getAvailableDeviceFramerates(const QString& devicePath, const int& /*deviceInput*/, const PixelFormat& encFormat, const unsigned width, const unsigned height) const override; diff --git a/include/grabber/V4L2Grabber.h b/include/grabber/V4L2Grabber.h index 7dcc367e..7dc61b92 100644 --- a/include/grabber/V4L2Grabber.h +++ b/include/grabber/V4L2Grabber.h @@ -35,9 +35,10 @@ #include #endif +/// /// Capture class for V4L2 devices /// -/// @see http://linuxtv.org/downloads/v4l-dvb-apis/capture-example.html + class V4L2Grabber : public Grabber { Q_OBJECT @@ -45,10 +46,19 @@ class V4L2Grabber : public Grabber public: struct DeviceProperties { - QString name = QString(); - QMultiMap inputs = QMultiMap(); - QStringList resolutions = QStringList(); - QStringList framerates = QStringList(); + QString name = QString(); + struct InputProperties + { + QString inputName = QString(); + struct EncodingProperties + { + unsigned int width = 0; + unsigned int height = 0; + QList framerates = QList(); + }; + QMultiMap encodingFormats = QMultiMap(); + }; + QMap inputs = QMap(); }; V4L2Grabber(const QString & device, const unsigned width, const unsigned height, const unsigned fps, const unsigned input, VideoStandard videoStandard, PixelFormat pixelFormat, int pixelDecimation); @@ -120,27 +130,33 @@ public: /// /// @brief overwrite Grabber.h implementation /// - QStringList getV4L2devices() const override; + QStringList getDevices() const override; /// /// @brief overwrite Grabber.h implementation /// - QString getV4L2deviceName(const QString& devicePath) const override; + QString getDeviceName(const QString& devicePath) const override; /// /// @brief overwrite Grabber.h implementation /// - QMultiMap getV4L2deviceInputs(const QString& devicePath) const override; + QMultiMap getDeviceInputs(const QString& devicePath) const override; /// /// @brief overwrite Grabber.h implementation /// - QStringList getResolutions(const QString& devicePath) const override; + QStringList getAvailableEncodingFormats(const QString& devicePath, const int& deviceInput) const override; /// /// @brief overwrite Grabber.h implementation /// - QStringList getFramerates(const QString& devicePath) const override; + QMultiMap getAvailableDeviceResolutions(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat) const override; + + /// + /// @brief overwrite Grabber.h implementation + /// + QStringList getAvailableDeviceFramerates(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat, const unsigned width, const unsigned height) const override; + public slots: @@ -275,5 +291,5 @@ private: bool _deviceAutoDiscoverEnabled; protected: - void enumFrameIntervals(QStringList &framerates, int fileDescriptor, int pixelformat, int width, int height); + void enumFrameIntervals(QList &framerates, int fileDescriptor, int pixelformat, int width, int height); }; diff --git a/include/hyperion/Grabber.h b/include/hyperion/Grabber.h index e1e1e979..90937297 100644 --- a/include/hyperion/Grabber.h +++ b/include/hyperion/Grabber.h @@ -143,7 +143,7 @@ public: /// @param devicePath The device path /// @return multi pair of name/index on success else empty pair /// - virtual QMultiMap getDeviceInputs(const QString& /*devicePath*/) const { return {{ "", 0}}; } + virtual QMultiMap getDeviceInputs(const QString& /*devicePath*/) const { return QMultiMap(); } /// /// @brief Get a list of all available device encoding formats depends on device input @@ -154,13 +154,13 @@ public: virtual QStringList getAvailableEncodingFormats(const QString& /*devicePath*/, const int& /*deviceInput*/) const { return QStringList(); } /// - /// @brief Get a list of available device resolutions depends on device input and encoding format + /// @brief Get a map of available device resolutions (width/heigth) depends on device input and encoding format /// @param devicePath The device path /// @param inputIndex The device input index /// @param encFormat The device encoding format - /// @return List of resolutions on success else empty List + /// @return Map of resolutions (width/heigth) on success else empty List /// - virtual QStringList getAvailableDeviceResolutions(const QString& /*devicePath*/, const int& /*deviceInput*/, const PixelFormat& /*encFormat*/) const { return QStringList(); } + virtual QMultiMap getAvailableDeviceResolutions(const QString& /*devicePath*/, const int& /*deviceInput*/, const PixelFormat& /*encFormat*/) const { return QMultiMap(); } /// /// @brief Get a list of available device framerates depends on device input, encoding format and resolution diff --git a/include/hyperion/GrabberWrapper.h b/include/hyperion/GrabberWrapper.h index 121a0ee4..5120ce0a 100644 --- a/include/hyperion/GrabberWrapper.h +++ b/include/hyperion/GrabberWrapper.h @@ -71,7 +71,7 @@ public: virtual QString getDeviceName(const QString& devicePath) const; /// - /// @brief Get a name/index pair of supported device inputs + /// @brief Get a map of name/index pair of supported device inputs /// @param devicePath The device path /// @return multi pair of name/index on success else empty pair /// @@ -86,13 +86,13 @@ public: virtual QStringList getAvailableEncodingFormats(const QString& devicePath, const int& deviceInput) const; /// - /// @brief Get a list of available device resolutions depends on device input and encoding format + /// @brief Get a map of available device resolutions (width/heigth) depends on device input and encoding format /// @param devicePath The device path /// @param inputIndex The device input index /// @param encFormat The device encoding format - /// @return List of resolutions on success else empty List + /// @return Map of resolutions (width/heigth) on success else empty List /// - virtual QStringList getAvailableDeviceResolutions(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat) const; + virtual QMultiMap getAvailableDeviceResolutions(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat) const; /// /// @brief Get a list of available device framerates depends on encoding format and resolution diff --git a/libsrc/api/JsonAPI.cpp b/libsrc/api/JsonAPI.cpp index 9304a734..7e824650 100644 --- a/libsrc/api/JsonAPI.cpp +++ b/libsrc/api/JsonAPI.cpp @@ -496,48 +496,57 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString { QJsonObject device; device["device"] = devicePath; - device["name"] = GrabberWrapper::getInstance()->getDeviceName(devicePath); + device["device_name"] = GrabberWrapper::getInstance()->getDeviceName(devicePath); + device["type"] = "v4l2"; + + QJsonArray video_inputs; - QJsonObject availableInputs; QMultiMap inputs = GrabberWrapper::getInstance()->getDeviceInputs(devicePath); for (auto input = inputs.begin(); input != inputs.end(); input++) { - QJsonObject availableEncodingFormats; - availableInputs["inputName"] = input.key(); - availableInputs["inputIndex"] = input.value(); + QJsonObject in; + in["name"] = input.key(); + in["inputIdx"] = input.value(); + QJsonArray formats; QStringList encodingFormats = GrabberWrapper::getInstance()->getAvailableEncodingFormats(devicePath, input.value()); for (auto encodingFormat : encodingFormats) { - QJsonArray formats; - QStringList resolutions = GrabberWrapper::getInstance()->getAvailableDeviceResolutions(devicePath, input.value(), parsePixelFormat(encodingFormat)); - for (auto resolution : resolutions) - { - QJsonObject format; - format["resolution"] = resolution; + QJsonObject format; + format["format"] = encodingFormat; - QJsonArray availableFramerates; - QStringList framerates = GrabberWrapper::getInstance()->getAvailableDeviceFramerates(devicePath, input.value(), parsePixelFormat(encodingFormat), resolution.split("x")[0].toInt(), resolution.split("x")[1].toInt()); + QJsonArray resolutionArray; + QMultiMap deviceResolutions = GrabberWrapper::getInstance()->getAvailableDeviceResolutions(devicePath, input.value(), parsePixelFormat(encodingFormat)); + for (auto width_height = deviceResolutions.begin(); width_height != deviceResolutions.end(); width_height++) + { + QJsonObject resolution; + resolution["width"] = width_height.key(); + resolution["heigth"] = width_height.value(); + + QJsonArray fps; + QStringList framerates = GrabberWrapper::getInstance()->getAvailableDeviceFramerates(devicePath, input.value(), parsePixelFormat(encodingFormat), width_height.key(), width_height.value()); for (auto framerate : framerates) { - availableFramerates.append(framerate); + fps.append(framerate); } - format["framerates"] = availableFramerates; - formats.append(format); + resolution["fps"] = fps; + resolutionArray.append(resolution); } - availableEncodingFormats[encodingFormat] = formats; + format["resolutions"] = resolutionArray; + formats.append(format); } - availableInputs["encoding_formats"] = availableEncodingFormats; + in["formats"] = formats; + video_inputs.append(in); } - device["device_inputs"] = availableInputs; + device["video_inputs"] = video_inputs; availableDevices.append(device); } - grabbers["v4l2_properties"] = availableDevices; + grabbers["video_sources"] = availableDevices; #endif diff --git a/libsrc/grabber/v4l2/V4L2Grabber.cpp b/libsrc/grabber/v4l2/V4L2Grabber.cpp index 8c6e0338..2b8009e1 100644 --- a/libsrc/grabber/v4l2/V4L2Grabber.cpp +++ b/libsrc/grabber/v4l2/V4L2Grabber.cpp @@ -30,6 +30,18 @@ #define V4L2_CAP_META_CAPTURE 0x00800000 // Specified in kernel header v4.16. Required for backward compatibility. #endif +static PixelFormat GetPixelFormat(const unsigned int format) +{ + if (format == V4L2_PIX_FMT_RGB32) return PixelFormat::RGB32; + if (format == V4L2_PIX_FMT_RGB24) return PixelFormat::BGR24; + if (format == V4L2_PIX_FMT_YUYV) return PixelFormat::YUYV; + if (format == V4L2_PIX_FMT_UYVY) return PixelFormat::UYVY; + if (format == V4L2_PIX_FMT_MJPEG) return PixelFormat::MJPEG; + if (format == V4L2_PIX_FMT_NV12) return PixelFormat::NV12; + if (format == V4L2_PIX_FMT_YUV420) return PixelFormat::I420; + return PixelFormat::NO_CHANGE; +}; + V4L2Grabber::V4L2Grabber(const QString & device, unsigned width, unsigned height, unsigned fps, unsigned input, VideoStandard videoStandard, PixelFormat pixelFormat, int pixelDecimation) : Grabber("V4L2:"+device) , _deviceName() @@ -64,6 +76,7 @@ V4L2Grabber::V4L2Grabber(const QString & device, unsigned width, unsigned height setWidthHeight(width, height); setFramerate(fps); setDeviceVideoStandard(device, videoStandard); + Debug(_log,"Init pixel format: %i", static_cast(_pixelFormat)); } V4L2Grabber::~V4L2Grabber() @@ -210,42 +223,69 @@ void V4L2Grabber::getV4Ldevices() V4L2Grabber::DeviceProperties properties; // collect available device inputs (index & name) - int inputIndex; - if (xioctl(fd, VIDIOC_G_INPUT, &inputIndex) == 0) + struct v4l2_input input; + CLEAR(input); + + input.index = 0; + while (xioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0) { - struct v4l2_input input; - CLEAR(input); + V4L2Grabber::DeviceProperties::InputProperties inputProperties; + inputProperties.inputName = QString((char*)input.name); - input.index = 0; - while (xioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0) + // Enumerate pixel formats + struct v4l2_fmtdesc desc; + CLEAR(desc); + + desc.index = 0; + desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + while (xioctl(fd, VIDIOC_ENUM_FMT, &desc) == 0) { - properties.inputs.insert(QString((char*)input.name), input.index); - input.index++; - } - } - - // collect available device resolutions & frame rates - struct v4l2_frmsizeenum frmsizeenum; - CLEAR(frmsizeenum); - - frmsizeenum.index = 0; - frmsizeenum.pixel_format = fmt.fmt.pix.pixelformat; - while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) >= 0) - { - switch (frmsizeenum.type) - { - case V4L2_FRMSIZE_TYPE_DISCRETE: + PixelFormat encodingFormat = GetPixelFormat(desc.pixelformat); + if (encodingFormat != PixelFormat::NO_CHANGE) { - properties.resolutions << QString::number(frmsizeenum.discrete.width) + "x" + QString::number(frmsizeenum.discrete.height); - enumFrameIntervals(properties.framerates, fd, fmt.fmt.pix.pixelformat, frmsizeenum.discrete.width, frmsizeenum.discrete.height); + V4L2Grabber::DeviceProperties::InputProperties::EncodingProperties encodingProperties; + + // Enumerate frame sizes and frame rates + struct v4l2_frmsizeenum frmsizeenum; + CLEAR(frmsizeenum); + + frmsizeenum.index = 0; + frmsizeenum.pixel_format = desc.pixelformat; + while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) >= 0) + { + switch (frmsizeenum.type) + { + case V4L2_FRMSIZE_TYPE_DISCRETE: + { + encodingProperties.width = frmsizeenum.discrete.width; + encodingProperties.height = frmsizeenum.discrete.height; + enumFrameIntervals(encodingProperties.framerates, fd, desc.pixelformat, frmsizeenum.discrete.width, frmsizeenum.discrete.height); + } + break; + case V4L2_FRMSIZE_TYPE_CONTINUOUS: + case V4L2_FRMSIZE_TYPE_STEPWISE: // We do not take care of V4L2_FRMSIZE_TYPE_CONTINUOUS or V4L2_FRMSIZE_TYPE_STEPWISE + break; + } + + inputProperties.encodingFormats.insert(encodingFormat, encodingProperties); + frmsizeenum.index++; + } + + // Failsafe: In case VIDIOC_ENUM_FRAMESIZES fails, insert current heigth, width and fps. + if (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == -1) + { + encodingProperties.width = fmt.fmt.pix.width; + encodingProperties.height = fmt.fmt.pix.height; + enumFrameIntervals(encodingProperties.framerates, fd, desc.pixelformat, encodingProperties.width, encodingProperties.height); + inputProperties.encodingFormats.insert(encodingFormat, encodingProperties); + } } - break; - case V4L2_FRMSIZE_TYPE_CONTINUOUS: - case V4L2_FRMSIZE_TYPE_STEPWISE: - // We do not take care of V4L2_FRMSIZE_TYPE_CONTINUOUS or V4L2_FRMSIZE_TYPE_STEPWISE - break; + + desc.index++; } - frmsizeenum.index++; + + properties.inputs.insert(input.index, inputProperties); + input.index++; } if (close(fd) < 0) continue; @@ -1238,7 +1278,7 @@ int V4L2Grabber::xioctl(int fileDescriptor, int request, void *arg) return r; } -void V4L2Grabber::enumFrameIntervals(QStringList &framerates, int fileDescriptor, int pixelformat, int width, int height) +void V4L2Grabber::enumFrameIntervals(QList &framerates, int fileDescriptor, int pixelformat, int width, int height) { // collect available frame rates struct v4l2_frmivalenum frmivalenum; @@ -1259,8 +1299,8 @@ void V4L2Grabber::enumFrameIntervals(QStringList &framerates, int fileDescriptor if (frmivalenum.discrete.numerator != 0) { rate = frmivalenum.discrete.denominator / frmivalenum.discrete.numerator; - if (!framerates.contains(QString::number(rate))) - framerates.append(QString::number(rate)); + if (!framerates.contains(rate)) + framerates.append(rate); } } break; @@ -1270,13 +1310,24 @@ void V4L2Grabber::enumFrameIntervals(QStringList &framerates, int fileDescriptor if (frmivalenum.stepwise.min.denominator != 0) { rate = frmivalenum.stepwise.min.denominator / frmivalenum.stepwise.min.numerator; - if (!framerates.contains(QString::number(rate))) - framerates.append(QString::number(rate)); + if (!framerates.contains(rate)) + framerates.append(rate); } } } frmivalenum.index++; } + + // If VIDIOC_ENUM_FRAMEINTERVALS fails, try to read the current fps via VIDIOC_G_PARM if possible and insert it into 'framerates'. + if (xioctl(fileDescriptor, VIDIOC_ENUM_FRAMESIZES, &frmivalenum) == -1) + { + struct v4l2_streamparm streamparms; + CLEAR(streamparms); + streamparms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (xioctl(fileDescriptor, VIDIOC_G_PARM, &streamparms) >= 0) + framerates.append(streamparms.parm.capture.timeperframe.denominator / streamparms.parm.capture.timeperframe.numerator); + } } void V4L2Grabber::setSignalDetectionEnable(bool enable) @@ -1360,34 +1411,77 @@ bool V4L2Grabber::setFramerate(int fps) return false; } -QStringList V4L2Grabber::getV4L2devices() const +QStringList V4L2Grabber::getDevices() const { QStringList result = QStringList(); - for (auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) - { + for(auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) result << it.key(); - } + return result; } -QString V4L2Grabber::getV4L2deviceName(const QString& devicePath) const +QString V4L2Grabber::getDeviceName(const QString& devicePath) const { return _deviceProperties.value(devicePath).name; } -QMultiMap V4L2Grabber::getV4L2deviceInputs(const QString& devicePath) const +QMultiMap V4L2Grabber::getDeviceInputs(const QString& devicePath) const { - return _deviceProperties.value(devicePath).inputs; + QMultiMap result = QMultiMap(); + for(auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) + if (it.key() == devicePath) + for (auto input = it.value().inputs.begin(); input != it.value().inputs.end(); input++) + if (!result.contains(input.value().inputName, input.key())) + result.insert(input.value().inputName, input.key()); + + return result; } -QStringList V4L2Grabber::getResolutions(const QString& devicePath) const +QStringList V4L2Grabber::getAvailableEncodingFormats(const QString& devicePath, const int& deviceInput) const { - return _deviceProperties.value(devicePath).resolutions; + QStringList result = QStringList(); + + for(auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) + if (it.key() == devicePath) + for (auto input = it.value().inputs.begin(); input != it.value().inputs.end(); input++) + if (input.key() == deviceInput) + for (auto enc = input.value().encodingFormats.begin(); enc != input.value().encodingFormats.end(); enc++) + if (!result.contains(pixelFormatToString(enc.key()).toLower(), Qt::CaseInsensitive)) + result << pixelFormatToString(enc.key()).toLower(); + + return result; } -QStringList V4L2Grabber::getFramerates(const QString& devicePath) const +QMultiMap V4L2Grabber::getAvailableDeviceResolutions(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat) const { - return _deviceProperties.value(devicePath).framerates; + QMultiMap result = QMultiMap(); + + for(auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) + if (it.key() == devicePath) + for (auto input = it.value().inputs.begin(); input != it.value().inputs.end(); input++) + if (input.key() == deviceInput) + for (auto enc = input.value().encodingFormats.begin(); enc != input.value().encodingFormats.end(); enc++) + if (!result.contains(enc.value().width, enc.value().height)) + result.insert(enc.value().width, enc.value().height); + + return result; +} + +QStringList V4L2Grabber::getAvailableDeviceFramerates(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat, const unsigned width, const unsigned height) const +{ + QStringList result = QStringList(); + + for(auto it = _deviceProperties.begin(); it != _deviceProperties.end(); ++it) + if (it.key() == devicePath) + for (auto input = it.value().inputs.begin(); input != it.value().inputs.end(); input++) + if (input.key() == deviceInput) + for (auto enc = input.value().encodingFormats.begin(); enc != input.value().encodingFormats.end(); enc++) + if(enc.key() == encFormat && enc.value().width == width && enc.value().height == height) + for (auto fps = enc.value().framerates.begin(); fps != enc.value().framerates.end(); fps++) + if(!result.contains(QString::number(*fps))) + result << QString::number(*fps); + + return result; } void V4L2Grabber::handleCecEvent(CECEvent event) diff --git a/libsrc/grabber/v4l2/V4L2Wrapper.cpp b/libsrc/grabber/v4l2/V4L2Wrapper.cpp index 8322053c..8a463ba3 100644 --- a/libsrc/grabber/v4l2/V4L2Wrapper.cpp +++ b/libsrc/grabber/v4l2/V4L2Wrapper.cpp @@ -128,7 +128,7 @@ void V4L2Wrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& obj["cropBottom"].toInt(0)); // device input - _grabber.setInput(obj["input"].toInt(-1)); + _grabber.setInput(obj["input"].toInt(0)); // device resolution _grabber.setWidthHeight(obj["width"].toInt(0), obj["height"].toInt(0)); diff --git a/libsrc/hyperion/GrabberWrapper.cpp b/libsrc/hyperion/GrabberWrapper.cpp index 3270a67d..b95a891d 100644 --- a/libsrc/hyperion/GrabberWrapper.cpp +++ b/libsrc/hyperion/GrabberWrapper.cpp @@ -243,7 +243,7 @@ QMultiMap GrabberWrapper::getDeviceInputs(const QString& devicePat if(_grabberName.startsWith("V4L")) return _ggrabber->getDeviceInputs(devicePath); - return {{ "", 0}}; + return QMultiMap(); } QStringList GrabberWrapper::getAvailableEncodingFormats(const QString& devicePath, const int& deviceInput) const @@ -254,12 +254,12 @@ QStringList GrabberWrapper::getAvailableEncodingFormats(const QString& devicePat return QStringList(); } -QStringList GrabberWrapper::getAvailableDeviceResolutions(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat) const +QMultiMap GrabberWrapper::getAvailableDeviceResolutions(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat) const { if(_grabberName.startsWith("V4L")) return _ggrabber->getAvailableDeviceResolutions(devicePath, deviceInput, encFormat); - return QStringList(); + return QMultiMap(); } QStringList GrabberWrapper::getAvailableDeviceFramerates(const QString& devicePath, const int& deviceInput, const PixelFormat& encFormat, const unsigned width, const unsigned height) const