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