diff --git a/src/platform/camera/v4l2.cpp b/src/platform/camera/v4l2.cpp index 172bd4695..a442afad4 100644 --- a/src/platform/camera/v4l2.cpp +++ b/src/platform/camera/v4l2.cpp @@ -28,6 +28,7 @@ #include #include #include +#include /** * Most of this file is adapted from libavdevice's v4l2.c, @@ -35,6 +36,26 @@ * stdout and is not part of the public API for some reason. */ +static std::map createPixFmtToQuality() +{ + std::map m; + m[V4L2_PIX_FMT_H264] = 3; + m[V4L2_PIX_FMT_MJPEG] = 2; + m[V4L2_PIX_FMT_YUYV] = 1; + return m; +} +const std::map pixFmtToQuality = createPixFmtToQuality(); + +static std::map createPixFmtToName() +{ + std::map m; + m[V4L2_PIX_FMT_H264] = QString("h264"); + m[V4L2_PIX_FMT_MJPEG] = QString("mjpeg"); + m[V4L2_PIX_FMT_YUYV] = QString("yuyv422"); + return m; +} +const std::map pixFmtToName = createPixFmtToName(); + static int deviceOpen(QString devName) { struct v4l2_capability cap; @@ -113,6 +134,7 @@ QVector v4l2::getDeviceModes(QString devName) while(!ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &vfse)) { VideoMode mode; + mode.pixel_format = vfse.pixel_format; switch (vfse.type) { case V4L2_FRMSIZE_TYPE_DISCRETE: mode.width = vfse.discrete.width; @@ -169,3 +191,27 @@ QVector> v4l2::getDeviceList() } return devices; } + +QString v4l2::getPixelFormatString(uint32_t pixel_format) +{ + if (pixFmtToName.find(pixel_format) == pixFmtToName.end()) + { + printf("BAD!\n"); + return QString("unknown"); + } + return pixFmtToName.at(pixel_format); +} + +bool v4l2::betterPixelFormat(uint32_t a, uint32_t b) +{ + if (pixFmtToQuality.find(a) == pixFmtToQuality.end()) + { + return false; + } + else if (pixFmtToQuality.find(b) == pixFmtToQuality.end()) + { + return true; + } + return pixFmtToQuality.at(a) > pixFmtToQuality.at(b); +} + diff --git a/src/platform/camera/v4l2.h b/src/platform/camera/v4l2.h index a4a8292e3..c9556c84c 100644 --- a/src/platform/camera/v4l2.h +++ b/src/platform/camera/v4l2.h @@ -32,6 +32,8 @@ namespace v4l2 { QVector getDeviceModes(QString devName); QVector> getDeviceList(); + QString getPixelFormatString(uint32_t pixel_format); + bool betterPixelFormat(uint32_t a, uint32_t b); } #endif // V4L2_H diff --git a/src/video/cameradevice.cpp b/src/video/cameradevice.cpp index f774bfb12..113f26e96 100644 --- a/src/video/cameradevice.cpp +++ b/src/video/cameradevice.cpp @@ -17,7 +17,6 @@ along with qTox. If not, see . */ - #include #include #include @@ -105,7 +104,7 @@ out: CameraDevice* CameraDevice::open(QString devName) { - VideoMode mode{0,0,0}; + VideoMode mode{0,0,0,0}; return open(devName, mode); } @@ -164,7 +163,11 @@ CameraDevice* CameraDevice::open(QString devName, VideoMode mode) { av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0); av_dict_set(&options, "framerate", QString().setNum(mode.FPS).toStdString().c_str(), 0); - av_dict_set(&options, "pixel_format", "mjpeg", 0); + const char *pixel_format = v4l2::getPixelFormatString(mode.pixel_format).toStdString().c_str(); + if (strncmp(pixel_format, "unknown", 7) != 0) + { + av_dict_set(&options, "pixel_format", pixel_format, 0); + } } #endif #ifdef Q_OS_OSX @@ -360,6 +363,24 @@ QVector CameraDevice::getVideoModes(QString devName) return {}; } +QString CameraDevice::getPixelFormatString(uint32_t pixel_format) +{ +#ifdef Q_OS_LINUX + return v4l2::getPixelFormatString(pixel_format); +#else + return QString("unknown"); +#endif +} + +bool CameraDevice::betterPixelFormat(uint32_t a, uint32_t b) +{ +#ifdef Q_OS_LINUX + return v4l2::betterPixelFormat(a, b); +#else + return false; +#endif +} + bool CameraDevice::getDefaultInputFormat() { QMutexLocker locker(&iformatLock); diff --git a/src/video/cameradevice.h b/src/video/cameradevice.h index 83209aeeb..e6f304e35 100644 --- a/src/video/cameradevice.h +++ b/src/video/cameradevice.h @@ -59,6 +59,10 @@ public: /// Get the list of video modes for a device static QVector getVideoModes(QString devName); + /// Get the name of the pixel format of a video mode + static QString getPixelFormatString(uint32_t pixel_format); + /// Returns true if we prefer format a to b, false otherwise (such as if there's no preference) + static bool betterPixelFormat(uint32_t a, uint32_t b); /// Returns the short name of the default defice /// This is either the device in the settings diff --git a/src/video/camerasource.cpp b/src/video/camerasource.cpp index 25fd5473c..dd1beb143 100644 --- a/src/video/camerasource.cpp +++ b/src/video/camerasource.cpp @@ -35,7 +35,7 @@ extern "C" { CameraSource* CameraSource::instance{nullptr}; CameraSource::CameraSource() - : deviceName{"none"}, device{nullptr}, mode(VideoMode{0,0,0}), + : deviceName{"none"}, device{nullptr}, mode(VideoMode{0,0,0,0}), cctx{nullptr}, cctxOrig{nullptr}, videoStreamIndex{-1}, _isOpen{false}, streamBlocker{false}, subscriptions{0} { @@ -67,7 +67,7 @@ void CameraSource::open() void CameraSource::open(const QString deviceName) { - open(deviceName, VideoMode{0,0,0}); + open(deviceName, VideoMode{0,0,0,0}); } void CameraSource::open(const QString DeviceName, VideoMode Mode) diff --git a/src/video/videoframe.cpp b/src/video/videoframe.cpp index 427cfa8c8..c686b5fde 100644 --- a/src/video/videoframe.cpp +++ b/src/video/videoframe.cpp @@ -17,6 +17,8 @@ along with qTox. If not, see . */ +#include + #include #include #include @@ -48,12 +50,13 @@ VideoFrame::VideoFrame(AVFrame* frame, int w, int h, int fmt, std::functioncolor_range = AVCOL_RANGE_UNSPECIFIED; - if (pixFmt == AV_PIX_FMT_YUV420P) + if (pixFmt == AV_PIX_FMT_YUV420P) { frameYUV420 = frame; - else if (pixFmt == AV_PIX_FMT_RGB24) + } else if (pixFmt == AV_PIX_FMT_RGB24) { frameRGB24 = frame; - else + } else { frameOther = frame; + } } VideoFrame::VideoFrame(AVFrame* frame, std::function freelistCallback) @@ -126,6 +129,7 @@ bool VideoFrame::convertToRGB24(QSize size) qWarning() << "None of the frames are valid! Did someone release us?"; return false; } + //std::cout << "converting to RGB24" << std::endl; if (size.isEmpty()) { @@ -198,6 +202,7 @@ bool VideoFrame::convertToYUV420() qCritical() << "None of the frames are valid! Did someone release us?"; return false; } + //std::cout << "converting to YUV420" << std::endl; frameYUV420=av_frame_alloc(); if (!frameYUV420) diff --git a/src/video/videomode.h b/src/video/videomode.h index d3e17f98b..b3560409c 100644 --- a/src/video/videomode.h +++ b/src/video/videomode.h @@ -26,6 +26,7 @@ struct VideoMode { unsigned short width, height; ///< Displayed video resolution (NOT frame resolution) float FPS; ///< Max frames per second supported by the device at this resolution + uint32_t pixel_format; /// All zeros means a default/unspecified mode operator bool() const @@ -37,7 +38,13 @@ struct VideoMode { return width == other.width && height == other.height - && FPS == other.FPS; + && FPS == other.FPS + && pixel_format == other.pixel_format; + } + + uint32_t norm(const VideoMode& other) const + { + return std::abs(this->width-other.width) + std::abs(this->height-other.height); } }; diff --git a/src/widget/form/settings/avform.cpp b/src/widget/form/settings/avform.cpp index ab40173b4..96ba0a97f 100644 --- a/src/widget/form/settings/avform.cpp +++ b/src/widget/form/settings/avform.cpp @@ -30,6 +30,7 @@ #include #include +#include #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER ALC_DEVICE_SPECIFIER @@ -159,21 +160,65 @@ void AVForm::updateVideoModes(int curIndex) a.FPS>b.FPS;}); bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true); bodyUI->videoModescomboBox->clear(); + + // Identify the best resolutions available for the supposed XXXXp resolutions. + std::map idealModes; + idealModes[240] = {460,240,0,0}; idealModes[360] = {640,360,0,0}; + idealModes[480] = {854,480,0,0}; idealModes[720] = {1280,720,0,0}; + idealModes[1080] = {1920,1080,0,0}; + std::map bestModeInds; + for (int i=0; ifirst; + VideoMode idealMode = iter->second; + // don't take approximately correct resolutions unless they really + // are close + if (mode.norm(idealMode) > 300) continue; + if (bestModeInds.find(res) == bestModeInds.end()) + { + bestModeInds[res] = i; + continue; + } + int ind = bestModeInds[res]; + if (mode.norm(idealMode) < videoModes[ind].norm(idealMode)) + { + bestModeInds[res] = i; + } + else if (mode.norm(idealMode) == videoModes[ind].norm(idealMode)) + { + // prefer higher FPS and "better" pixel formats + if (mode.FPS > videoModes[ind].FPS) { + bestModeInds[res] = i; + } + else if (mode.FPS == videoModes[ind].FPS && + CameraDevice::betterPixelFormat(mode.pixel_format, videoModes[ind].pixel_format)) + { + bestModeInds[res] = i; + } + } + } + } + printf("=====\n"); int prefResIndex = -1; QSize prefRes = Settings::getInstance().getCamVideoRes(); unsigned short prefFPS = Settings::getInstance().getCamVideoFPS(); - for (int i=0; isecond; VideoMode mode = videoModes[i]; if (mode.width==prefRes.width() && mode.height==prefRes.height() && mode.FPS == prefFPS && prefResIndex==-1) prefResIndex = i; QString str; + printf("width: %d, height: %d, FPS: %f, pixel format: %s\n", mode.width, mode.height, mode.FPS, CameraDevice::getPixelFormatString(mode.pixel_format).toStdString().c_str()); if (mode.height && mode.width) - str += tr("%1x%2").arg(mode.width).arg(mode.height); + str += tr("%1p").arg(iter->first); else str += tr("Default resolution"); - if (mode.FPS) - str += tr(" at %1 FPS").arg(mode.FPS); bodyUI->videoModescomboBox->addItem(str); } if (videoModes.isEmpty())