From bdd139c9ea98ad47cac80c7deb70050760b4848f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com>
Date: Wed, 17 Feb 2016 13:52:44 -0500
Subject: [PATCH] video_device: add default settings selection logic

* use default settings if no preference exists
* logic requires unformated parameters (added related methods)
* current logic: largest resolution <= 720P, framerate >= 10 FPS
* support new pixel format
* report pixel format to libav/ffmpeg for decoding

Tuleap: #294
Change-Id: Ia9b61814b49e7057ae4eb5fef9d0b814706cafff
---
 src/media/media_decoder.cpp                   |   7 +-
 src/media/media_device.h                      |   3 +
 src/media/video/osxvideo/video_device_impl.mm | 157 ++---
 src/media/video/v4l2/video_device_impl.cpp    | 549 ++++++++++--------
 src/media/video/video_base.cpp                |  17 +-
 src/media/video/video_base.h                  |   4 +-
 src/media/video/video_device.h                | 140 ++++-
 src/media/video/video_device_monitor.cpp      |  20 +-
 src/media/video/video_input.cpp               |   5 +-
 .../video/winvideo/video_device_impl.cpp      | 143 ++---
 src/string_utils.cpp                          |  12 +
 src/string_utils.h                            |   2 +
 12 files changed, 593 insertions(+), 466 deletions(-)

diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index 738dfb5d4a..cf842b3ebd 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -90,8 +90,11 @@ int MediaDecoder::openInput(const DeviceParams& params)
     av_dict_set(&options_, "reorder_queue_size",ring::to_string(jitterBufferMaxSize_).c_str(), 0);
     av_dict_set(&options_, "max_delay",ring::to_string(jitterBufferMaxDelay_).c_str(), 0);
 
-    RING_DBG("Trying to open device %s with format %s", params.input.c_str(),
-                                                        params.format.c_str());
+    if(!params.pixel_format.empty()){
+        av_dict_set(&options_, "pixel_format", params.pixel_format.c_str(), 0);
+    }
+    RING_DBG("Trying to open device %s with format %s, pixel format %s, size %dx%d, rate %lf", params.input.c_str(),
+                                                        params.format.c_str(), params.pixel_format.c_str(), params.width, params.height, params.framerate.real());
     int ret = avformat_open_input(
         &inputCtx_,
         params.input.c_str(),
diff --git a/src/media/media_device.h b/src/media/media_device.h
index 1cf48f5376..851335119e 100644
--- a/src/media/media_device.h
+++ b/src/media/media_device.h
@@ -33,10 +33,13 @@ namespace ring {
  * to open a LibAV device/stream
  */
 struct DeviceParams {
+    std::string name {};
     std::string input {}; // Device path (e.g. /dev/video0)
     std::string format {};
     unsigned width {}, height {};
     rational<double> framerate {};
+    std::string pixel_format {};
+    std::string channel_name {};
     unsigned channel {}; // Channel number
     std::string loop {};
     std::string sdp_flags {};
diff --git a/src/media/video/osxvideo/video_device_impl.mm b/src/media/video/osxvideo/video_device_impl.mm
index 5795fb999b..3f05fa9742 100644
--- a/src/media/video/osxvideo/video_device_impl.mm
+++ b/src/media/video/osxvideo/video_device_impl.mm
@@ -35,13 +35,6 @@
 
 namespace ring { namespace video {
 
-class OSXVideoSize {
-    public:
-        OSXVideoSize(const unsigned width, const unsigned height);
-        unsigned width;
-        unsigned height;
-};
-
 class VideoDeviceImpl {
     public:
         /**
@@ -53,22 +46,19 @@ class VideoDeviceImpl {
         std::string name;
 
         std::vector<std::string> getChannelList() const;
-        std::vector<std::string> getSizeList(const std::string& channel) const;
-        std::vector<std::string> getSizeList() const;
-        std::vector<std::string> getRateList(const std::string& channel, const std::string& size) const;
-        float getRate(unsigned rate) const;
-
-        VideoSettings getSettings() const;
-        void applySettings(VideoSettings settings);
+        std::vector<VideoSize> getSizeList(const std::string& channel) const;
+        std::vector<VideoSize> getSizeList() const;
+        std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
 
         DeviceParams getDeviceParams() const;
+        void setDeviceParams(const DeviceParams&);
 
     private:
-        const OSXVideoSize extractSize(const std::string &name) const;
+        VideoSize extractSize(VideoSize) const;
 
         AVCaptureDevice* avDevice_;
-        std::vector<OSXVideoSize> available_sizes_;
-        OSXVideoSize current_size_;
+        std::vector<VideoSize> available_sizes_;
+        VideoSize current_size_;
 };
 
 VideoDeviceImpl::VideoDeviceImpl(const std::string& uniqueID)
@@ -79,34 +69,18 @@ VideoDeviceImpl::VideoDeviceImpl(const std::string& uniqueID)
 {
     name = [[avDevice_ localizedName] UTF8String];
 
+    available_sizes_.reserve(avDevice_.formats.count);
     for (AVCaptureDeviceFormat* format in avDevice_.formats) {
-        std::stringstream ss;
         auto dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
-        OSXVideoSize size(dimensions.width, dimensions.height);
-        available_sizes_.push_back(size);
+        available_sizes_.emplace_back(dimensions.width, dimensions.height);
     }
-    // Set default settings
-    applySettings(VideoSettings());
 }
 
-OSXVideoSize::OSXVideoSize(const unsigned width, const unsigned height) :
-    width(width), height(height) {}
-
-void
-VideoDeviceImpl::applySettings(VideoSettings settings)
-{
-//TODO: add framerate
-//    rate_ = size_.getRate(settings["rate"]);
-    current_size_ = extractSize(settings.video_size);
-}
-
-const OSXVideoSize
-VideoDeviceImpl::extractSize(const std::string &name) const
+VideoSize
+VideoDeviceImpl::extractSize(VideoSize size) const
 {
     for (const auto item : available_sizes_) {
-        std::stringstream ss;
-        ss << item.width << "x" << item.height;
-        if (ss.str() == name)
+        if (item.first == size.first && item.second == size.second)
             return item;
     }
 
@@ -114,19 +88,19 @@ VideoDeviceImpl::extractSize(const std::string &name) const
     if (!available_sizes_.empty()) {
         return available_sizes_.back();
     }
-    return OSXVideoSize(-1, -1);
+    return VideoSize(0, 0);
 }
 
-
 DeviceParams
 VideoDeviceImpl::getDeviceParams() const
 {
     DeviceParams params;
+    params.name = [[avDevice_ localizedName] UTF8String];
     params.input = "[" + device + "]";
     params.format = "avfoundation";
 
-    params.width = current_size_.width;
-    params.height = current_size_.height;
+    params.width = current_size_.first;
+    params.height = current_size_.second;
 
     auto format = [avDevice_ activeFormat];
     auto frameRate = (AVFrameRateRange*)
@@ -135,21 +109,41 @@ VideoDeviceImpl::getDeviceParams() const
     return params;
 }
 
-VideoSettings
-VideoDeviceImpl::getSettings() const
+void
+VideoDeviceImpl::setDeviceParams(const DeviceParams& params)
 {
-    VideoSettings settings;
+//TODO: add framerate
+//    rate_ = size_.getRate(settings["rate"]);
+    current_size_ = extractSize({params.width, params.height});
+}
 
-    settings.name = [[avDevice_ localizedName] UTF8String];
+std::vector<VideoSize>
+VideoDeviceImpl::getSizeList() const
+{
+    return getSizeList("default");
+}
 
+std::vector<FrameRate>
+VideoDeviceImpl::getRateList(const std::string& channel, VideoSize size) const
+{
     auto format = [avDevice_ activeFormat];
-    auto frameRate = (AVFrameRateRange*)
-                    [format.videoSupportedFrameRateRanges objectAtIndex:0];
-    settings.framerate = frameRate.maxFrameRate;
-    settings.video_size = std::to_string(current_size_.width) +
-        "x" + std::to_string(current_size_.height);
+    std::vector<FrameRate> v;
+    v.reserve(format.videoSupportedFrameRateRanges.count);
+    for (AVFrameRateRange* frameRateRange in format.videoSupportedFrameRateRanges)
+        v.emplace_back(frameRateRange.maxFrameRate);
+    return v;
+}
 
-    return settings;
+std::vector<VideoSize>
+VideoDeviceImpl::getSizeList(const std::string& channel) const
+{
+    return available_sizes_;
+}
+
+std::vector<std::string>
+VideoDeviceImpl::getChannelList() const
+{
+    return {"default"};
 }
 
 VideoDevice::VideoDevice(const std::string& path) :
@@ -166,66 +160,27 @@ VideoDevice::getDeviceParams() const
 }
 
 void
-VideoDevice::applySettings(VideoSettings settings)
-{
-    deviceImpl_->applySettings(settings);
-}
-
-VideoSettings
-VideoDevice::getSettings() const
-{
-    return deviceImpl_->getSettings();
-}
-
-std::vector<std::string>
-VideoDeviceImpl::getSizeList() const
-{
-    return getSizeList("default");
-}
-
-std::vector<std::string>
-VideoDeviceImpl::getRateList(const std::string& channel, const std::string& size) const
+VideoDevice::setDeviceParams(const DeviceParams& params)
 {
-    auto format = [avDevice_ activeFormat];
-    std::vector<std::string> v;
-
-    for (AVFrameRateRange* frameRateRange in format.videoSupportedFrameRateRanges) {
-      std::stringstream ss;
-        ss << frameRateRange.maxFrameRate;
-        v.push_back(ss.str());
-    }
-    return v;
+    return deviceImpl_->setDeviceParams(params);
 }
 
 std::vector<std::string>
-VideoDeviceImpl::getSizeList(const std::string& channel) const
+VideoDevice::getChannelList() const
 {
-    std::vector<std::string> v;
-
-    for (const auto &item : available_sizes_) {
-        std::stringstream ss;
-        ss << item.width << "x" << item.height;
-        v.push_back(ss.str());
-    }
-
-    return v;
+    return deviceImpl_->getChannelList();
 }
 
-std::vector<std::string> VideoDeviceImpl::getChannelList() const
+std::vector<VideoSize>
+VideoDevice::getSizeList(const std::string& channel) const
 {
-    return {"default"};
+    return deviceImpl_->getSizeList(channel);
 }
 
-DRing::VideoCapabilities
-VideoDevice::getCapabilities() const
+std::vector<FrameRate>
+VideoDevice::getRateList(const std::string& channel, VideoSize size) const
 {
-    DRing::VideoCapabilities cap;
-
-    for (const auto& chan : deviceImpl_->getChannelList())
-        for (const auto& size : deviceImpl_->getSizeList(chan))
-            cap[chan][size] = deviceImpl_->getRateList(chan, size);
-
-    return cap;
+    return deviceImpl_->getRateList(channel, size);
 }
 
 VideoDevice::~VideoDevice()
diff --git a/src/media/video/v4l2/video_device_impl.cpp b/src/media/video/v4l2/video_device_impl.cpp
index defaa6706a..8e0fd180ee 100644
--- a/src/media/video/v4l2/video_device_impl.cpp
+++ b/src/media/video/v4l2/video_device_impl.cpp
@@ -3,6 +3,7 @@
  *
  *  Author: Rafaël Carré <rafael.carre@savoirfairelinux.com>
  *  Author: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
  *
  *  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
@@ -42,84 +43,106 @@ extern "C" {
 
 #include "logger.h"
 #include "../video_device.h"
+#include "string_utils.h"
+#include "array_size.h"
 
-#define ZEROVAR(x) memset(&(x), 0, sizeof(x))
+#define ZEROVAR(x) std::memset(&(x), 0, sizeof(x))
 
 namespace ring { namespace video {
 
-class VideoV4l2Size {
-    public:
-        VideoV4l2Size(const unsigned width, const unsigned height);
-
-        /**
-         * @throw std::runtime_error
-         */
-        void getFrameRates(int fd, unsigned int pixel_format);
-        std::vector<std::string> getRateList() const;
-        float getRate(unsigned rate) const;
-
-        unsigned width;
-        unsigned height;
-
-    private:
-        std::vector<float> rates_;
+class VideoV4l2Rate
+{
+public:
+    VideoV4l2Rate(unsigned rate_numerator = 0,
+                  unsigned rate_denominator = 0,
+                  unsigned format = 0)
+        : frame_rate(rate_denominator , rate_numerator), pixel_format(format) {}
+
+    FrameRate frame_rate;
+    unsigned pixel_format;
+    std::string libAvPixelformat() const;
 };
 
-class VideoV4l2Channel {
-    public:
-        VideoV4l2Channel(unsigned idx, const char *s);
-
-        /**
-         * @throw std::runtime_error
-         */
-        void getFormat(int fd);
-        /**
-         * @throw std::runtime_error
-         */
-        unsigned int getSizes(int fd, unsigned int pixel_format);
+class VideoV4l2Size
+{
+public:
+    VideoV4l2Size(const unsigned width, const unsigned height)
+        : width(width), height(height), rates_() {}
 
-        void setFourcc(unsigned code);
-        const char * getFourcc() const;
+    /**
+     * @throw std::runtime_error
+     */
+    void readFrameRates(int fd, unsigned int pixel_format);
 
-        std::vector<std::string> getSizeList() const;
-        const VideoV4l2Size& getSize(const std::string &name) const;
+    std::vector<FrameRate> getRateList() const;
+    VideoV4l2Rate getRate(FrameRate rate) const;
 
-        unsigned idx;
-        std::string name;
+    unsigned width;
+    unsigned height;
 
-    private:
-        void putCIFFirst();
-        std::vector<VideoV4l2Size> sizes_;
-        char fourcc_[5];
+private:
+    void addRate(VideoV4l2Rate proposed_rate);
+    std::vector<VideoV4l2Rate> rates_;
 };
 
-class VideoDeviceImpl {
-    public:
-        /**
-         * @throw std::runtime_error
-         */
-        VideoDeviceImpl(const std::string& path);
+bool
+operator==(VideoV4l2Size& a, VideoV4l2Size& b)
+{
+    return a.height == b.height && a.width == b.width;
+}
+
+class VideoV4l2Channel
+{
+public:
+    VideoV4l2Channel(unsigned idx, const char *s);
 
-        std::string device;
-        std::string name;
+    /**
+     * @throw std::runtime_error
+     */
+    void readFormats(int fd);
 
-        std::vector<std::string> getChannelList() const;
-        std::vector<std::string> getSizeList(const std::string& channel) const;
-        std::vector<std::string> getRateList(const std::string& channel, const std::string& size) const;
+    /**
+     * @throw std::runtime_error
+     */
+    unsigned int readSizes(int fd, unsigned int pixel_format);
 
-        VideoSettings getSettings() const;
-        void applySettings(VideoSettings settings);
+    std::vector<VideoSize> getSizeList() const;
+    const VideoV4l2Size& getSize(VideoSize name) const;
 
-        DeviceParams getDeviceParams() const;
+    unsigned idx;
+    std::string name;
 
-    private:
-        std::vector<VideoV4l2Channel> channels_;
-        const VideoV4l2Channel& getChannel(const std::string& name) const;
+private:
+    void putCIFFirst();
+    std::vector<VideoV4l2Size> sizes_;
+};
 
-        /* Preferences */
-        VideoV4l2Channel channel_;
-        VideoV4l2Size size_;
-        float rate_;
+class VideoDeviceImpl
+{
+public:
+    /**
+     * @throw std::runtime_error
+     */
+    VideoDeviceImpl(const std::string& path);
+
+    std::string device;
+    std::string name;
+
+    std::vector<std::string> getChannelList() const;
+    std::vector<VideoSize> getSizeList(const std::string& channel) const;
+    std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
+
+    DeviceParams getDeviceParams() const;
+    void setDeviceParams(const DeviceParams&);
+
+private:
+    std::vector<VideoV4l2Channel> channels_;
+    const VideoV4l2Channel& getChannel(const std::string& name) const;
+
+    /* Preferences */
+    VideoV4l2Channel channel_;
+    VideoV4l2Size size_;
+    VideoV4l2Rate rate_;
 };
 
 static const unsigned pixelformats_supported[] = {
@@ -153,6 +176,23 @@ static const unsigned pixelformats_supported[] = {
     V4L2_PIX_FMT_NV16,     /* 16  Y/CbCr 4:2:2  */
     V4L2_PIX_FMT_NV61,     /* 16  Y/CrCb 4:2:2  */
 
+    /* Compressed formats */
+    V4L2_PIX_FMT_MJPEG,
+    V4L2_PIX_FMT_JPEG,
+    V4L2_PIX_FMT_DV,
+    V4L2_PIX_FMT_MPEG,
+    V4L2_PIX_FMT_H264,
+    V4L2_PIX_FMT_H264_NO_SC,
+    V4L2_PIX_FMT_H264_MVC,
+    V4L2_PIX_FMT_H263,
+    V4L2_PIX_FMT_MPEG1,
+    V4L2_PIX_FMT_MPEG2,
+    V4L2_PIX_FMT_MPEG4,
+    V4L2_PIX_FMT_XVID,
+    V4L2_PIX_FMT_VC1_ANNEX_G,
+    V4L2_PIX_FMT_VC1_ANNEX_L,
+    V4L2_PIX_FMT_VP8,
+
 #if 0
     /* RGB formats */
     V4L2_PIX_FMT_RGB332,   /*  8  RGB-3-3-2     */
@@ -184,19 +224,16 @@ static const unsigned pixelformats_supported[] = {
  * Lowest score is the best, the first entries in the array are the formats
  * supported as an input for the video encoders.
  *
- * Other entries in the array are YUV formats
- *
- * RGB / grey / palette formats are not supported because most cameras support
- * YUV input
- *
+ * See pixelformats_supported array for the support list.
  */
-
-static unsigned int pixelformat_score(unsigned pixelformat)
+static unsigned int
+pixelformat_score(unsigned pixelformat)
 {
-    for (const auto &item : pixelformats_supported)
-        if (item == pixelformat)
-            return item;
-
+    unsigned int formats_count = arraySize(pixelformats_supported);
+    for (unsigned int i = 0; i < formats_count; ++i) {
+        if (pixelformats_supported[i] == pixelformat)
+            return i;
+    }
     return UINT_MAX - 1;
 }
 
@@ -204,24 +241,21 @@ using std::vector;
 using std::string;
 using std::stringstream;
 
-VideoV4l2Size::VideoV4l2Size(const unsigned width, const unsigned height) :
-    width(width), height(height), rates_() {}
-
-vector<string> VideoV4l2Size::getRateList() const
+vector<FrameRate>
+VideoV4l2Size::getRateList() const
 {
-    vector<string> v;
-
-    for (const auto &item : rates_) {
-        stringstream ss;
-        ss << item;
-        v.push_back(ss.str());
-    }
-
-    return v;
+    vector<FrameRate> rates;
+    rates.reserve(rates_.size());
+    for (const auto& r : rates_)
+        rates.emplace_back(r.frame_rate);
+    return rates;
 }
 
-void VideoV4l2Size::getFrameRates(int fd, unsigned int pixel_format)
+void
+VideoV4l2Size::readFrameRates(int fd, unsigned int pixel_format)
 {
+    VideoV4l2Rate fallback_rate {1, 25, pixel_format};
+
     v4l2_frmivalenum frmival;
     ZEROVAR(frmival);
     frmival.pixel_format = pixel_format;
@@ -229,191 +263,181 @@ void VideoV4l2Size::getFrameRates(int fd, unsigned int pixel_format)
     frmival.height = height;
 
     if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival)) {
-        rates_.push_back(25);
+        addRate(fallback_rate);
         RING_ERR("could not query frame interval for size");
         return;
     }
 
-    switch(frmival.type) {
-        case V4L2_FRMIVAL_TYPE_DISCRETE:
-            do {
-                const float rate = static_cast<float>(frmival.discrete.denominator)
-                    / frmival.discrete.numerator;
-                rates_.push_back(rate);
-                ++frmival.index;
-            } while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival));
-            break;
-        case V4L2_FRMIVAL_TYPE_CONTINUOUS:
-            rates_.push_back(25);
-            // TODO
-            RING_ERR("Continuous Frame Intervals not supported");
-            break;
-        case V4L2_FRMIVAL_TYPE_STEPWISE:
-            rates_.push_back(25);
-            // TODO
-            RING_ERR("Stepwise Frame Intervals not supported");
-            break;
+    if (frmival.type != V4L2_FRMIVAL_TYPE_DISCRETE) {
+        addRate(fallback_rate);
+        RING_ERR("Continuous and stepwise Frame Intervals are not supported");
+        return;
     }
+
+    do {
+        addRate({frmival.discrete.numerator, frmival.discrete.denominator, pixel_format});
+        ++frmival.index;
+    } while (!ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival));
 }
 
-float
-VideoV4l2Size::getRate(unsigned rate) const
+VideoV4l2Rate
+VideoV4l2Size::getRate(FrameRate rate) const
 {
+    double r = rate.real();
     for (const auto& item : rates_) {
-        if (item == rate)
+        if (std::fabs(item.frame_rate.real() - r) < 0.0001d)
             return item;
     }
-
-    // fallback to last size
-    assert(not rates_.empty());
     return rates_.back();
 }
 
-VideoV4l2Channel::VideoV4l2Channel(unsigned idx, const char *s) :
-    idx(idx), name(s), sizes_(), fourcc_() {}
-
-void VideoV4l2Channel::setFourcc(unsigned code)
+void
+VideoV4l2Size::addRate(VideoV4l2Rate new_rate)
 {
-    fourcc_[0] = code;
-    fourcc_[1] = code >> 8;
-    fourcc_[2] = code >> 16;
-    fourcc_[3] = code >> 24;
-    fourcc_[4] = '\0';
-}
+    bool rate_found = false;
+    for (auto& item : rates_) {
+        if (item.frame_rate == new_rate.frame_rate) {
+            if (pixelformat_score(item.pixel_format) > pixelformat_score(new_rate.pixel_format)) {
+                 // Make sure we will use the prefered pixelformat (lower score means prefered format)
+                item.pixel_format = new_rate.pixel_format;
+            }
+            rate_found = true;
+        }
+    }
 
-const char *
-VideoV4l2Channel::getFourcc() const
-{
-    return fourcc_;
+    if (!rate_found)
+        rates_.push_back(new_rate);
 }
 
-vector<string> VideoV4l2Channel::getSizeList() const
-{
-    vector<string> v;
+VideoV4l2Channel::VideoV4l2Channel(unsigned idx, const char *s)
+    : idx(idx)
+    , name(s)
+    , sizes_()
+{}
 
-    for (const auto &item : sizes_) {
-        stringstream ss;
-        ss << item.width << "x" << item.height;
-        v.push_back(ss.str());
-    }
+std::vector<VideoSize>
+VideoV4l2Channel::getSizeList() const
+{
+    vector<VideoSize> v;
+    v.reserve(sizes_.size());
+    for (const auto &item : sizes_)
+        v.emplace_back(item.width, item.height);
 
     return v;
 }
 
 unsigned int
-VideoV4l2Channel::getSizes(int fd, unsigned int pixelformat)
+VideoV4l2Channel::readSizes(int fd, unsigned int pixelformat)
 {
     v4l2_frmsizeenum frmsize;
     ZEROVAR(frmsize);
+
     frmsize.index = 0;
     frmsize.pixel_format = pixelformat;
-    if (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize)) {
-
-        switch (frmsize.type) {
-            case V4L2_FRMSIZE_TYPE_DISCRETE:
-                do {
-                    VideoV4l2Size size(frmsize.discrete.width, frmsize.discrete.height);
-                    size.getFrameRates(fd, frmsize.pixel_format);
-                    sizes_.push_back(size);
-                    ++frmsize.index;
-                } while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize));
-                return pixelformat;
-
-                // TODO, we dont want to display a list of 2000x2000
-                // resolutions if the camera supports continuous framesizes
-                // from 1x1 to 2000x2000
-                // We should limit to a list of known standard sizes
-            case V4L2_FRMSIZE_TYPE_CONTINUOUS:
-                RING_ERR("Continuous Frame sizes not supported");
-                break;
-            case V4L2_FRMSIZE_TYPE_STEPWISE:
-                RING_ERR("Stepwise Frame sizes not supported");
-                break;
-        }
+
+    if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) < 0) {
+        v4l2_format fmt;
+        ZEROVAR(fmt);
+
+        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+        if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0)
+            throw std::runtime_error("Could not get format");
+
+        VideoV4l2Size size(fmt.fmt.pix.width, fmt.fmt.pix.height);
+        size.readFrameRates(fd, fmt.fmt.pix.pixelformat);
+        sizes_.push_back(size);
+
+        return fmt.fmt.pix.pixelformat;
     }
 
-    v4l2_format fmt;
-    ZEROVAR(fmt);
-    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-    if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0)
-        throw std::runtime_error("Could not get format");
+    if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE) {
+        // We do not take care of V4L2_FRMSIZE_TYPE_CONTINUOUS or V4L2_FRMSIZE_TYPE_STEPWISE
+        RING_ERR("Continuous Frame sizes not supported");
+        return pixelformat;
+    }
 
-    VideoV4l2Size size(fmt.fmt.pix.width, fmt.fmt.pix.height);
-    size.getFrameRates(fd, fmt.fmt.pix.pixelformat);
-    sizes_.push_back(size);
+    // Real work starts here: attach framerates to sizes and update pixelformat information
+    do {
+        bool size_exists = false;
+        VideoV4l2Size size(frmsize.discrete.width, frmsize.discrete.height);
+
+        for (auto &item : sizes_) {
+            if (item == size) {
+                size_exists = true;
+                // If a size already exist we add frame rates since there may be some
+                // frame rates available in one format that are not availabe in another.
+                item.readFrameRates(fd, frmsize.pixel_format);
+            }
+        }
 
-    return fmt.fmt.pix.pixelformat;
+        if (!size_exists) {
+            size.readFrameRates(fd, frmsize.pixel_format);
+            sizes_.push_back(size);
+        }
+
+        ++frmsize.index;
+    } while (!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize));
+
+    return pixelformat;
 }
 
 // Put CIF resolution (352x288) first in the list since it is more prevalent in
 // VoIP
-void VideoV4l2Channel::putCIFFirst()
+void
+VideoV4l2Channel::putCIFFirst()
 {
     const auto iter = std::find_if(sizes_.begin(), sizes_.end(),
-    [] (const VideoV4l2Size &size)
-    {
-        return size.width == 352 and size.height == 258;
-    });
+                                   [] (const VideoV4l2Size& size) {
+                                       return size.width == 352 and size.height == 258;
+                                   });
 
     if (iter != sizes_.end() and iter != sizes_.begin())
         std::swap(*iter, *sizes_.begin());
 }
 
-void VideoV4l2Channel::getFormat(int fd)
+void
+VideoV4l2Channel::readFormats(int fd)
 {
     if (ioctl(fd, VIDIOC_S_INPUT, &idx))
         throw std::runtime_error("VIDIOC_S_INPUT failed");
 
     v4l2_fmtdesc fmt;
     ZEROVAR(fmt);
-    unsigned fmt_index;
-    fmt.index = fmt_index = 0;
-    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+    unsigned fmt_index = 0;
 
-    unsigned int best_score = UINT_MAX;
-    unsigned int best_idx = 0;
-    unsigned int pixelformat = 0;
+    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmt)) {
         if (fmt_index != fmt.index)
             break;
-
-        unsigned int score = pixelformat_score(fmt.pixelformat);
-        if (score < best_score) {
-            pixelformat = fmt.pixelformat;
-            best_idx = fmt_index;
-            best_score = score;
-        }
-
+        readSizes(fd, fmt.pixelformat);
         fmt.index = ++fmt_index;
     }
+
     if (fmt_index == 0)
         throw std::runtime_error("Could not enumerate formats");
 
-    fmt.index = best_idx;
-    pixelformat = getSizes(fd, pixelformat);
     putCIFFirst();
-
-    setFourcc(pixelformat);
 }
 
 const VideoV4l2Size&
-VideoV4l2Channel::getSize(const string &name) const
+VideoV4l2Channel::getSize(VideoSize s) const
 {
     for (const auto &item : sizes_) {
-        stringstream ss;
-        ss << item.width << "x" << item.height;
-        if (ss.str() == name)
+        if (item.width == s.first && item.height == s.second)
             return item;
     }
 
-    // fallback to last size
     assert(not sizes_.empty());
-    return sizes_.back();
+    return sizes_.front();
 }
 
-VideoDeviceImpl::VideoDeviceImpl(const string& path) :
-    device(path), name(), channels_(),
-    channel_(-1, ""), size_(-1, -1), rate_(-1)
+VideoDeviceImpl::VideoDeviceImpl(const string& path)
+    : device(path)
+    , name()
+    , channels_()
+    , channel_(-1, "")
+    , size_(-1, -1)
+    , rate_(-1, 1, 0)
 {
     int fd = open(device.c_str(), O_RDWR);
     if (fd == -1)
@@ -438,7 +462,7 @@ VideoDeviceImpl::VideoDeviceImpl(const string& path) :
 
         if (input.type & V4L2_INPUT_TYPE_CAMERA) {
             VideoV4l2Channel channel(idx, (const char*) input.name);
-            channel.getFormat(fd);
+            channel.readFormats(fd);
             channels_.push_back(channel);
         }
 
@@ -446,29 +470,60 @@ VideoDeviceImpl::VideoDeviceImpl(const string& path) :
     }
 
     ::close(fd);
+}
 
-    // Set default settings
-    applySettings(VideoSettings());
+string
+VideoV4l2Rate::libAvPixelformat() const
+{
+    switch (pixel_format) {
+        // Set codec name for those pixelformats.
+        // Those  names can be found in libavcodec/codec_desc.c
+        case V4L2_PIX_FMT_MJPEG:
+            return "mjpeg";
+        case V4L2_PIX_FMT_DV:
+            return "dvvideo";
+        case V4L2_PIX_FMT_MPEG:
+        case V4L2_PIX_FMT_MPEG1:
+            return "mpeg1video";
+        case V4L2_PIX_FMT_H264:
+        case V4L2_PIX_FMT_H264_NO_SC:
+        case V4L2_PIX_FMT_H264_MVC:
+            return "h264";
+        case V4L2_PIX_FMT_H263:
+            return "h263";
+        case V4L2_PIX_FMT_MPEG2:
+            return "mpeg2video";
+        case V4L2_PIX_FMT_MPEG4:
+            return "mpeg4";
+        case V4L2_PIX_FMT_VC1_ANNEX_G:
+        case V4L2_PIX_FMT_VC1_ANNEX_L:
+            return "vc1";
+        case V4L2_PIX_FMT_VP8:
+            return "vp8";
+        default: // Most pixel formats do not need any codec
+            return "";
+    }
 }
 
-vector<string> VideoDeviceImpl::getChannelList() const
+vector<string>
+VideoDeviceImpl::getChannelList() const
 {
     vector<string> v;
-
+    v.reserve(channels_.size());
     for (const auto &itr : channels_)
         v.push_back(itr.name);
 
     return v;
 }
 
-vector<string>
+vector<VideoSize>
 VideoDeviceImpl::getSizeList(const string& channel) const
 {
     return getChannel(channel).getSizeList();
 }
 
-vector<string>
-VideoDeviceImpl::getRateList(const string& channel, const string& size) const
+vector<FrameRate>
+VideoDeviceImpl::getRateList(const string& channel, VideoSize size) const
 {
     return getChannel(channel).getSize(size).getRateList();
 }
@@ -481,61 +536,43 @@ VideoDeviceImpl::getChannel(const string &name) const
             return item;
 
     assert(not channels_.empty());
-    return channels_.back();
-}
-
-void
-VideoDeviceImpl::applySettings(VideoSettings settings)
-{
-    // Set preferences or fallback to defaults.
-    channel_ = getChannel(settings.channel);
-    size_ = channel_.getSize(settings.video_size);
-    rate_ = size_.getRate(settings.framerate);
-}
-
-VideoSettings
-VideoDeviceImpl::getSettings() const
-{
-    VideoSettings settings;
-    settings.name = name;
-    settings.channel = channel_.name;
-    stringstream video_size;
-    video_size << size_.width << "x" << size_.height;
-    settings.video_size = video_size.str();
-    settings.framerate = rate_;
-    return settings;
+    return channels_.front();
 }
 
 DeviceParams
 VideoDeviceImpl::getDeviceParams() const
 {
     DeviceParams params;
+    params.name = name;
     params.input = device;
     params.format = "video4linux2";
+    params.channel_name = channel_.name;
     params.channel = channel_.idx;
     params.width = size_.width;
     params.height = size_.height;
-    params.framerate = rate_;
+    params.framerate = rate_.frame_rate;
+    params.pixel_format = rate_.libAvPixelformat();
     return params;
 }
 
-VideoDevice::VideoDevice(const string& path) :
-    deviceImpl_(new VideoDeviceImpl(path))
-{
-    node_ = path;
-    name = deviceImpl_->name;
-}
-
 void
-VideoDevice::applySettings(VideoSettings settings)
+VideoDeviceImpl::setDeviceParams(const DeviceParams& params)
 {
-    deviceImpl_->applySettings(settings);
+    // Set preferences or fallback to defaults.
+    channel_ = getChannel(params.channel_name);
+    size_ = channel_.getSize({params.width, params.height});
+    try {
+        rate_ = size_.getRate(params.framerate);
+    } catch (...) {
+        rate_ = {};
+    }
 }
 
-VideoSettings
-VideoDevice::getSettings() const
+VideoDevice::VideoDevice(const string& path)
+    : deviceImpl_(new VideoDeviceImpl(path))
 {
-    return deviceImpl_->getSettings();
+    node_ = path;
+    name = deviceImpl_->name;
 }
 
 DeviceParams
@@ -544,16 +581,28 @@ VideoDevice::getDeviceParams() const
     return deviceImpl_->getDeviceParams();
 }
 
-DRing::VideoCapabilities
-VideoDevice::getCapabilities() const
+void
+VideoDevice::setDeviceParams(const DeviceParams& params)
+{
+    return deviceImpl_->setDeviceParams(params);
+}
+
+std::vector<std::string>
+VideoDevice::getChannelList() const
 {
-    DRing::VideoCapabilities cap;
+    return deviceImpl_->getChannelList();
+}
 
-    for (const auto& chan : deviceImpl_->getChannelList())
-        for (const auto& size : deviceImpl_->getSizeList(chan))
-            cap[chan][size] = deviceImpl_->getRateList(chan, size);
+std::vector<VideoSize>
+VideoDevice::getSizeList(const std::string& channel) const
+{
+    return deviceImpl_->getSizeList(channel);
+}
 
-    return cap;
+std::vector<FrameRate>
+VideoDevice::getRateList(const std::string& channel, VideoSize size) const
+{
+    return deviceImpl_->getRateList(channel, size);
 }
 
 VideoDevice::~VideoDevice()
diff --git a/src/media/video/video_base.cpp b/src/media/video/video_base.cpp
index 9345bce08d..7e078668fe 100644
--- a/src/media/video/video_base.cpp
+++ b/src/media/video/video_base.cpp
@@ -78,8 +78,13 @@ extractString(const std::map<std::string, std::string>& settings, const std::str
 static unsigned
 extractInt(const std::map<std::string, std::string>& settings, const std::string& key) {
     auto i = settings.find(key);
-    if (i != settings.cend())
-        return ring::stoi(i->second);
+    if (i != settings.cend()) {
+        try {
+            return ring::stoi(i->second);
+        } catch(...) {
+            RING_ERR("Can't parse int: %s", i->second.c_str());
+        }
+    }
     return 0;
 }
 
@@ -88,7 +93,7 @@ VideoSettings::VideoSettings(const std::map<std::string, std::string>& settings)
     name = extractString(settings, "name");
     channel = extractString(settings, "channel");
     video_size = extractString(settings, "size");
-    framerate = extractInt(settings, "rate");
+    framerate = extractString(settings, "rate");
 }
 
 std::map<std::string, std::string>
@@ -98,7 +103,7 @@ VideoSettings::to_map() const
         {"name", name},
         {"size", video_size},
         {"channel", channel},
-        {"rate", ring::to_string(framerate)}
+        {"rate", framerate}
     };
 }
 
@@ -112,7 +117,7 @@ convert<ring::video::VideoSettings>::encode(const ring::video::VideoSettings& rh
     node["name"] = rhs.name;
     node["video_size"] = rhs.video_size;
     node["channel"] = rhs.channel;
-    node["framerate"] = ring::to_string(rhs.framerate);
+    node["framerate"] = rhs.framerate;
     return node;
 }
 
@@ -125,7 +130,7 @@ convert<ring::video::VideoSettings>::decode(const Node& node, ring::video::Video
     rhs.name = node["name"].as<std::string>();
     rhs.video_size = node["video_size"].as<std::string>();
     rhs.channel = node["channel"].as<std::string>();
-    rhs.framerate = node["framerate"].as<unsigned>();
+    rhs.framerate = node["framerate"].as<std::string>();
     return true;
 }
 
diff --git a/src/media/video/video_base.h b/src/media/video/video_base.h
index 1ccbeb27f5..ff1a776e5a 100644
--- a/src/media/video/video_base.h
+++ b/src/media/video/video_base.h
@@ -154,9 +154,9 @@ struct VideoSettings
     std::map<std::string, std::string> to_map() const;
 
     std::string name {};
-    std::string video_size {};
     std::string channel {};
-    unsigned framerate {};
+    std::string video_size {};
+    std::string framerate {};
 };
 
 }} // namespace ring::video
diff --git a/src/media/video/video_device.h b/src/media/video/video_device.h
index 2b51c98b5e..bad3c218ce 100644
--- a/src/media/video/video_device.h
+++ b/src/media/video/video_device.h
@@ -2,6 +2,7 @@
  *  Copyright (C) 2014-2015 Savoir-faire Linux Inc.
  *
  *  Author: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
  *
  *  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
@@ -18,20 +19,27 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#ifndef __VIDEO_DEVICE_H__
-#define __VIDEO_DEVICE_H__
+#pragma once
 
 #include "media/media_device.h"
 #include "video_base.h"
+#include "rational.h"
 
 #include <map>
 #include <memory>
 #include <string>
 #include <vector>
+#include <algorithm>
+
 #include "videomanager_interface.h"
+#include "string_utils.h"
+#include "logger.h"
 
 namespace ring { namespace video {
 
+using VideoSize = std::pair<unsigned, unsigned>;
+using FrameRate = rational<double>;
+
 class VideoDeviceImpl;
 
 class VideoDevice {
@@ -44,7 +52,7 @@ public:
      * The device name, e.g. "Integrated Camera",
      * actually used as the identifier.
      */
-    std::string name = "";
+    std::string name {};
 
     const std::string& getNode() const { return node_; }
 
@@ -63,12 +71,74 @@ public:
      *                 '800x448': ['15'],
      *                 '960x540': ['10']}}
      */
-    DRing::VideoCapabilities getCapabilities() const;
+    DRing::VideoCapabilities getCapabilities() const {
+        DRing::VideoCapabilities cap;
+
+        for (const auto& chan : getChannelList())
+            for (const auto& size : getSizeList(chan)) {
+                std::stringstream sz;
+                sz << size.first << "x" << size.second;
+                auto rates = getRateList(chan, size);
+                std::vector<std::string> rates_str {rates.size()};
+                std::transform(rates.begin(), rates.end(), rates_str.begin(),
+                               [](FrameRate r) { return ring::to_string(r.real()); });
+                cap[chan][sz.str()] = std::move(rates_str);
+            }
+
+        return cap;
+    }
+
+    /* Default setting is found by using following rules:
+     * - frame height <= 720 pixels
+     * - frame rate >= 10 fps
+     */
+    VideoSettings getDefaultSettings() const {
+        auto settings = getSettings();
+        settings.channel = getChannelList().front();
+
+        VideoSize max_size {0, 0};
+        FrameRate max_size_rate {0};
+
+        auto sizes = getSizeList(settings.channel);
+        for (auto& s : sizes) {
+            if (s.second > 720)
+                continue;
+            auto rates = getRateList(settings.channel, s);
+            if (rates.empty())
+                continue;
+            auto max_rate = *std::max_element(rates.begin(), rates.end());
+            if (max_rate < 10)
+                continue;
+            if (s.second > max_size.second ||
+                (s.second == max_size.second && s.first > max_size.first)) {
+                max_size = s;
+                max_size_rate = max_rate;
+            }
+        }
+        if (max_size.second > 0) {
+            std::stringstream video_size;
+            video_size << max_size.first << "x" << max_size.second;
+            settings.video_size = video_size.str();
+            settings.framerate = ring::to_string(max_size_rate.real());
+            RING_WARN("Default video settings: %s, %s FPS", settings.video_size.c_str(),
+                      settings.framerate.c_str());
+        }
+
+        return settings;
+    }
 
     /*
      * Get the settings for the device.
      */
-    VideoSettings getSettings() const;
+    VideoSettings getSettings() const {
+        auto params = getDeviceParams();
+        VideoSettings settings;
+        settings.name = params.name;
+        settings.channel = params.channel_name;
+        settings.video_size = sizeToString(params.width, params.height);
+        settings.framerate = ring::to_string(params.framerate.real());
+        return settings;
+    }
 
     /*
      * Setup the device with the preferences listed in the "settings" map.
@@ -77,7 +147,16 @@ public:
      * If a key is missing, a valid default value is choosen. Thus, calling
      * this function with an empty map will reset the device to default.
      */
-    void applySettings(VideoSettings settings);
+    void applySettings(VideoSettings settings) {
+        DeviceParams params {};
+        params.name = settings.name;
+        params.channel_name = settings.channel;
+        auto size = sizeFromString(settings.channel, settings.video_size);
+        params.width = size.first;
+        params.height = size.second;
+        params.framerate = rateFromString(settings.channel, size, settings.framerate);
+        setDeviceParams(params);
+    }
 
     /**
      * Returns the parameters needed for actual use of the device
@@ -86,10 +165,55 @@ public:
 
 private:
 
+    std::vector<std::string> getChannelList() const;
+    std::vector<VideoSize> getSizeList(const std::string& channel) const;
+    std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
+
+    VideoSize sizeFromString(const std::string& channel, const std::string& size) const {
+        auto size_list = getSizeList(channel);
+        for (const auto& s : size_list) {
+            if (sizeToString(s.first, s.second) == size)
+                return s;
+        }
+        return {0, 0};
+    }
+
+    std::string sizeToString(unsigned w, unsigned h) const {
+        std::stringstream video_size;
+        video_size << w << "x" << h;
+        return video_size.str();
+    }
+
+    FrameRate rateFromString(const std::string& channel, VideoSize size,
+                             const std::string& rate) const {
+        FrameRate closest {0};
+        double rate_val = 0;
+        try {
+            rate_val = rate.empty() ? 0 : std::stod(rate);
+        } catch (...) {
+            RING_WARN("Can't read framerate \"%s\"", rate.c_str());
+        }
+        // fallback to framerate closest to 30 FPS
+        if (rate_val == 0)
+            rate_val = 30;
+        double closest_dist = std::numeric_limits<double>::max();
+        auto rate_list = getRateList(channel, size);
+        for (const auto& r : rate_list) {
+            double dist = std::fabs(r.real() - rate_val);
+            if (dist < closest_dist) {
+                closest = r;
+                closest_dist = dist;
+            }
+        }
+        return closest;
+    }
+
+    void setDeviceParams(const DeviceParams&);
+
     /*
      * The device node, e.g. "/dev/video0".
      */
-    std::string node_ = "";
+    std::string node_ {};
 
     /*
      * Device specific implementation.
@@ -102,5 +226,3 @@ private:
 };
 
 }} // namespace ring::video
-
-#endif // __VIDEO_DEVICE_H__
diff --git a/src/media/video/video_device_monitor.cpp b/src/media/video/video_device_monitor.cpp
index 5c98632bc4..e22f38a278 100644
--- a/src/media/video/video_device_monitor.cpp
+++ b/src/media/video/video_device_monitor.cpp
@@ -70,12 +70,12 @@ VideoDeviceMonitor::getCapabilities(const string& name) const
 VideoSettings
 VideoDeviceMonitor::getSettings(const string& name)
 {
-    const auto itd = findDeviceByName(name);
+    const auto itd = findPreferencesByName(name);
 
-    if (itd == devices_.end())
+    if (itd == preferences_.end())
         return VideoSettings();
 
-    return itd->getSettings();
+    return *itd;
 }
 
 void
@@ -182,24 +182,26 @@ VideoDeviceMonitor::addDevice(const string& node)
 
     // instantiate a new unique device
     try {
-        VideoDevice dev(node);
+        VideoDevice dev {node};
         giveUniqueName(dev, devices_);
 
         // restore its preferences if any, or store the defaults
         auto it = findPreferencesByName(dev.name);
         if (it != preferences_.end())
             dev.applySettings(*it);
-        else
-            preferences_.push_back(dev.getSettings());
+        else {
+            dev.applySettings(dev.getDefaultSettings());
+            preferences_.emplace_back(dev.getSettings());
+        }
 
         // in case there is no default device on a fresh run
         if (defaultDevice_.empty())
             defaultDevice_ = dev.name;
 
-        devices_.push_back(dev);
+        devices_.emplace_back(std::move(dev));
         notify();
-    } catch (const std::runtime_error& e) {
-        RING_ERR("%s", e.what());
+    } catch (const std::exception& e) {
+        RING_ERR("Failed to add device %s: %s", node.c_str(), e.what());
         return;
     }
 }
diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp
index ab0cc2a5d0..69f3629a54 100644
--- a/src/media/video/video_input.cpp
+++ b/src/media/video/video_input.cpp
@@ -113,9 +113,12 @@ bool VideoInput::captureFrame()
 
     switch (ret) {
         case MediaDecoder::Status::ReadError:
-        case MediaDecoder::Status::DecodeError:
             return false;
 
+        // try to keep decoding
+        case MediaDecoder::Status::DecodeError:
+            return true;
+
         // End of streamed file
         case MediaDecoder::Status::EOFError:
             createDecoder();
diff --git a/src/media/video/winvideo/video_device_impl.cpp b/src/media/video/winvideo/video_device_impl.cpp
index 0702d422a6..a2d7eae30f 100644
--- a/src/media/video/winvideo/video_device_impl.cpp
+++ b/src/media/video/winvideo/video_device_impl.cpp
@@ -48,24 +48,22 @@ class VideoDeviceImpl {
         unsigned int id;
 
         std::vector<std::string> getChannelList() const;
-        std::vector<std::string> getSizeList(const std::string& channel) const;
-        std::vector<std::string> getSizeList() const;
-        std::vector<std::string> getRateList(const std::string& channel,
-            const std::string& size) const;
-        float getRate(unsigned rate) const;
-
-        VideoSettings getSettings() const;
-        void applySettings(VideoSettings settings);
+        std::vector<VideoSize> getSizeList(const std::string& channel) const;
+        std::vector<VideoSize> getSizeList() const;
+        std::vector<FrameRate> getRateList(const std::string& channel, VideoSize size) const;
 
         DeviceParams getDeviceParams() const;
+        void setDeviceParams(const DeviceParams&);
 
     private:
         std::unique_ptr<CaptureGraphInterfaces> cInterface;
 
         void setup();
-        std::vector<std::string> sizeList_;
-        std::map<std::string, std::vector<std::string> > rateList_;
-        std::map<std::string, AM_MEDIA_TYPE*> capMap_;
+        std::vector<VideoSize> sizeList_;
+        std::map<VideoSize, std::vector<FrameRate> > rateList_;
+        std::map<VideoSize, AM_MEDIA_TYPE*> capMap_;
+
+        //AM_MEDIA_TYPE* findCap(const std::string& size);
         void fail(const std::string& error);
 };
 
@@ -74,8 +72,6 @@ VideoDeviceImpl::VideoDeviceImpl(const std::string& id)
     , cInterface(new CaptureGraphInterfaces())
 {
     setup();
-
-    applySettings(VideoSettings());
 }
 
 void
@@ -207,13 +203,9 @@ VideoDeviceImpl::setup()
                 cInterface->streamConf_->GetStreamCaps(i, &pmt, (BYTE*)&pSCC);
                 if (pmt->formattype == FORMAT_VideoInfo) {
                     auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat;
-                    sizeList_.push_back(
-                        std::to_string(videoInfo->bmiHeader.biWidth) + "x" +
-                        std::to_string(videoInfo->bmiHeader.biHeight));
-                    rateList_[sizeList_.back()].push_back(
-                        std::to_string(1e7 / pSCC.MinFrameInterval));
-                    rateList_[sizeList_.back()].push_back(
-                        std::to_string(1e7 / pSCC.MaxFrameInterval));
+                    sizeList_.emplace_back(videoInfo->bmiHeader.biWidth, videoInfo->bmiHeader.biHeight);
+                    rateList_[sizeList_.back()].emplace_back(1e7, pSCC.MinFrameInterval);
+                    rateList_[sizeList_.back()].emplace_back(1e7, pSCC.MaxFrameInterval);
                     capMap_[sizeList_.back()] = pmt;
                 }
             }
@@ -229,25 +221,12 @@ VideoDeviceImpl::fail(const std::string& error)
     throw std::runtime_error(error);
 }
 
-void
-VideoDeviceImpl::applySettings(VideoSettings settings)
-{
-    if (!settings.video_size.empty()) {
-        auto pmt = capMap_[settings.video_size];
-        if (pmt != nullptr) {
-            ((VIDEOINFOHEADER*) pmt->pbFormat)->AvgTimePerFrame = settings.framerate;
-            if (FAILED(cInterface->streamConf_->SetFormat(capMap_[settings.video_size]))) {
-                RING_ERR("Could not set settings.");
-            }
-        }
-    }
-}
-
 DeviceParams
 VideoDeviceImpl::getDeviceParams() const
 {
     DeviceParams params;
 
+    params.name = name;
     params.input = device;
     params.format = "dshow";
 
@@ -258,95 +237,87 @@ VideoDeviceImpl::getDeviceParams() const
             auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat;
             params.width = videoInfo->bmiHeader.biWidth;
             params.height = videoInfo->bmiHeader.biHeight;
-            params.framerate = 1e7 /  videoInfo->AvgTimePerFrame;
+            params.framerate = {1e7,  videoInfo->AvgTimePerFrame};
         }
     }
     return params;
 }
 
-VideoSettings
-VideoDeviceImpl::getSettings() const
+void
+VideoDeviceImpl::setDeviceParams(const DeviceParams& params)
 {
-    VideoSettings settings;
-
-    settings.name = name;
-
-    AM_MEDIA_TYPE *pmt;
-    HRESULT hr = cInterface->streamConf_->GetFormat(&pmt);
-    if (SUCCEEDED(hr)) {
-        if (pmt->formattype == FORMAT_VideoInfo) {
-            auto videoInfo = (VIDEOINFOHEADER*) pmt->pbFormat;
-            settings.video_size = std::to_string(videoInfo->bmiHeader.biWidth) +
-                "x" + std::to_string(videoInfo->bmiHeader.biHeight);
-            settings.framerate = 1e7 / videoInfo->AvgTimePerFrame;
+    if (params.width and params.height) {
+        auto pmt = capMap_.at(std::make_pair(params.width, params.height));
+        if (pmt != nullptr) {
+            ((VIDEOINFOHEADER*) pmt->pbFormat)->AvgTimePerFrame = (FrameRate(1e7) / params.framerate).real();
+            if (FAILED(cInterface->streamConf_->SetFormat(pmt))) {
+                RING_ERR("Could not set settings.");
+            }
         }
     }
-    return settings;
 }
 
-VideoDevice::VideoDevice(const std::string& path)
-    : deviceImpl_(new VideoDeviceImpl(path))
+std::vector<VideoSize>
+VideoDeviceImpl::getSizeList() const
 {
-    node_ = path;
-    name = deviceImpl_->name;
+    return sizeList_;
 }
 
-DeviceParams
-VideoDevice::getDeviceParams() const
+std::vector<FrameRate>
+VideoDeviceImpl::getRateList(const std::string& channel, VideoSize size) const
 {
-    return deviceImpl_->getDeviceParams();
+    (void) channel;
+    return rateList_.at(size);
 }
 
-void
-VideoDevice::applySettings(VideoSettings settings)
+std::vector<VideoSize>
+VideoDeviceImpl::getSizeList(const std::string& channel) const
 {
-    deviceImpl_->applySettings(settings);
+    (void) channel;
+    return sizeList_;
 }
 
-VideoSettings
-VideoDevice::getSettings() const
+std::vector<std::string>
+VideoDeviceImpl::getChannelList() const
 {
-    return deviceImpl_->getSettings();
+    return {"default"};
 }
 
-std::vector<std::string>
-VideoDeviceImpl::getSizeList() const
+VideoDevice::VideoDevice(const std::string& path)
+    : deviceImpl_(new VideoDeviceImpl(path))
 {
-    return sizeList_;
+    node_ = path;
+    name = deviceImpl_->name;
 }
 
-std::vector<std::string>
-VideoDeviceImpl::getRateList(const std::string& channel,
-    const std::string& size) const
+DeviceParams
+VideoDevice::getDeviceParams() const
 {
-    (void) channel;
-    return rateList_.at(size);
+    return deviceImpl_->getDeviceParams();
 }
 
-std::vector<std::string>
-VideoDeviceImpl::getSizeList(const std::string& channel) const
+void
+VideoDevice::setDeviceParams(const DeviceParams& params)
 {
-    (void) channel;
-    return sizeList_;
+    return deviceImpl_->setDeviceParams(params);
 }
 
 std::vector<std::string>
-VideoDeviceImpl::getChannelList() const
+VideoDevice::getChannelList() const
 {
-    return {"default"};
+    return deviceImpl_->getChannelList();
 }
 
-DRing::VideoCapabilities
-VideoDevice::getCapabilities() const
+std::vector<VideoSize>
+VideoDevice::getSizeList(const std::string& channel) const
 {
-    DRing::VideoCapabilities cap;
+    return deviceImpl_->getSizeList(channel);
+}
 
-    for (const auto& chan : deviceImpl_->getChannelList()) {
-        for (const auto& size : deviceImpl_->getSizeList(chan)) {
-            cap[chan][size] = deviceImpl_->getRateList(chan, size);
-        }
-    }
-    return cap;
+std::vector<FrameRate>
+VideoDevice::getRateList(const std::string& channel, VideoSize size) const
+{
+    return deviceImpl_->getRateList(channel, size);
 }
 
 VideoDevice::~VideoDevice()
diff --git a/src/string_utils.cpp b/src/string_utils.cpp
index a172f2b86c..1ee6fd63cb 100644
--- a/src/string_utils.cpp
+++ b/src/string_utils.cpp
@@ -23,9 +23,21 @@
 #include <sstream>
 #include <cctype>
 #include <algorithm>
+#include <ostream>
+#include <stdexcept>
 
 namespace ring {
 
+std::string
+to_string(double value)
+{
+    char buf[64];
+    int len = snprintf(buf, sizeof(buf), "%-.*G", 16, value);
+    if (len <= 0)
+        throw std::invalid_argument{"can't parse double"};
+    return {buf, (size_t)len};
+}
+
 std::string
 trim(const std::string &s)
 {
diff --git a/src/string_utils.h b/src/string_utils.h
index 3fd19d3336..4072bb10fd 100644
--- a/src/string_utils.h
+++ b/src/string_utils.h
@@ -40,6 +40,8 @@ bool_to_str(bool b) noexcept
     return b ? TRUE_STR : FALSE_STR;
 }
 
+std::string to_string(double value);
+
 #ifdef __ANDROID__
 
 // Rationale:
-- 
GitLab