diff --git a/src/core/coreav.cpp b/src/core/coreav.cpp index 691c58796..529174f8b 100644 --- a/src/core/coreav.cpp +++ b/src/core/coreav.cpp @@ -295,7 +295,7 @@ void CoreAV::sendCallVideo(uint32_t callId, shared_ptr vframe) if (frame->fmt == VPX_IMG_FMT_NONE) { qWarning() << "Invalid frame"; - delete frame; + vpx_img_free(frame); return; } @@ -321,7 +321,7 @@ void CoreAV::sendCallVideo(uint32_t callId, shared_ptr vframe) if (err == TOXAV_ERR_SEND_FRAME_SYNC) qDebug() << "toxav_video_send_frame error: Lock busy, dropping frame"; - delete frame; + vpx_img_free(frame); } void CoreAV::micMuteToggle(uint32_t callId) diff --git a/src/core/toxcall.cpp b/src/core/toxcall.cpp index 390957f2b..8ed8d14a1 100644 --- a/src/core/toxcall.cpp +++ b/src/core/toxcall.cpp @@ -102,6 +102,7 @@ ToxFriendCall::ToxFriendCall(uint32_t FriendNum, bool VideoEnabled, CoreAV& av) { videoSource = new CoreVideoSource; CameraSource& source = CameraSource::getInstance(); + if (!source.isOpen()) source.open(); source.subscribe(); diff --git a/src/persistence/settings.cpp b/src/persistence/settings.cpp index a7e2049b3..61880d941 100644 --- a/src/persistence/settings.cpp +++ b/src/persistence/settings.cpp @@ -251,7 +251,9 @@ void Settings::loadGlobal() s.beginGroup("Video"); videoDev = s.value("videoDev", "").toString(); - camVideoRes = s.value("camVideoRes",QSize()).toSize(); + camVideoRes = s.value("camVideoRes", QRect()).toRect(); + screenRegion = s.value("screenRegion", QRect()).toRect(); + screenGrabbed = s.value("screenGrabbed", false).toBool(); camVideoFPS = s.value("camVideoFPS", 0).toUInt(); s.endGroup(); @@ -489,8 +491,10 @@ void Settings::saveGlobal() s.beginGroup("Video"); s.setValue("videoDev", videoDev); - s.setValue("camVideoRes",camVideoRes); - s.setValue("camVideoFPS",camVideoFPS); + s.setValue("camVideoRes", camVideoRes); + s.setValue("camVideoFPS", camVideoFPS); + s.setValue("screenRegion", screenRegion); + s.setValue("screenGrabbed", screenGrabbed); s.endGroup(); } @@ -1465,13 +1469,35 @@ void Settings::setOutVolume(int volume) outVolume = volume; } -QSize Settings::getCamVideoRes() const +QRect Settings::getScreenRegion() const +{ + return screenRegion; +} + +void Settings::setScreenRegion(const QRect &value) +{ + QMutexLocker locker{&bigLock}; + screenRegion = value; +} + +bool Settings::getScreenGrabbed() const +{ + return screenGrabbed; +} + +void Settings::setScreenGrabbed(bool value) +{ + QMutexLocker locker{&bigLock}; + screenGrabbed = value; +} + +QRect Settings::getCamVideoRes() const { QMutexLocker locker{&bigLock}; return camVideoRes; } -void Settings::setCamVideoRes(QSize newValue) +void Settings::setCamVideoRes(QRect newValue) { QMutexLocker locker{&bigLock}; camVideoRes = newValue; diff --git a/src/persistence/settings.h b/src/persistence/settings.h index 69c9abe98..a2cee2803 100644 --- a/src/persistence/settings.h +++ b/src/persistence/settings.h @@ -197,8 +197,14 @@ public: QString getVideoDev() const; void setVideoDev(const QString& deviceSpecifier); - QSize getCamVideoRes() const; - void setCamVideoRes(QSize newValue); + QRect getScreenRegion() const; + void setScreenRegion(const QRect &value); + + bool getScreenGrabbed() const; + void setScreenGrabbed(bool value); + + QRect getCamVideoRes() const; + void setCamVideoRes(QRect newValue); unsigned short getCamVideoFPS() const; void setCamVideoFPS(unsigned short newValue); @@ -447,7 +453,9 @@ private: // Video QString videoDev; - QSize camVideoRes; + QRect camVideoRes; + QRect screenRegion; + bool screenGrabbed; unsigned short camVideoFPS; struct friendProp diff --git a/src/platform/camera/directshow.cpp b/src/platform/camera/directshow.cpp index f0d8208f8..38287464c 100644 --- a/src/platform/camera/directshow.cpp +++ b/src/platform/camera/directshow.cpp @@ -187,6 +187,7 @@ QVector DirectShow::getDeviceModes(QString devName) IPin *pin; if (devFilter->EnumPins(&pins) != S_OK) return modes; + while (pins->Next(1, &pin, nullptr) == S_OK) { IKsPropertySet *p = nullptr; @@ -214,12 +215,14 @@ QVector DirectShow::getDeviceModes(QString devName) goto next; if (config->GetNumberOfCapabilities(&n, &size) != S_OK) goto pinend; + assert(size == sizeof(VIDEO_STREAM_CONFIG_CAPS)); vcaps = new VIDEO_STREAM_CONFIG_CAPS; - for (int i=0; iGetStreamCaps(i, &type, (BYTE*)vcaps) != S_OK) goto nextformat; @@ -227,7 +230,6 @@ QVector DirectShow::getDeviceModes(QString devName) && !IsEqualGUID(type->formattype, FORMAT_VideoInfo2)) goto nextformat; - VideoMode mode; mode.width = vcaps->MaxOutputSize.cx; mode.height = vcaps->MaxOutputSize.cy; mode.FPS = 1e7 / vcaps->MinFrameInterval; diff --git a/src/video/cameradevice.cpp b/src/video/cameradevice.cpp index ba0a7adc4..6a9c2c92c 100644 --- a/src/video/cameradevice.cpp +++ b/src/video/cameradevice.cpp @@ -73,7 +73,7 @@ CameraDevice* CameraDevice::open(QString devName, AVDictionary** options) format = iformat; } - if (avformat_open_input(&fctx, devName.toStdString().c_str(), format, options)<0) + if (avformat_open_input(&fctx, devName.toStdString().c_str(), format, options) < 0) goto out; // Fix avformat_find_stream_info hanging on garbage input @@ -103,12 +103,6 @@ out: return dev; } -CameraDevice* CameraDevice::open(QString devName) -{ - VideoMode mode{0,0,0,0}; - return open(devName, mode); -} - CameraDevice* CameraDevice::open(QString devName, VideoMode mode) { if (!getDefaultInputFormat()) @@ -149,26 +143,14 @@ CameraDevice* CameraDevice::open(QString devName, VideoMode mode) screen.setHeight((screen.height() * pixRatio)-2); } av_dict_set(&options, "video_size", QString("%1x%2").arg(screen.width()).arg(screen.height()).toStdString().c_str(), 0); + devName += QString("+%1,%2").arg(QString().setNum(mode.x), QString().setNum(mode.y)); + + int FPS = 5; if (mode.FPS) - av_dict_set(&options, "framerate", QString().setNum(mode.FPS).toStdString().c_str(), 0); - else - av_dict_set(&options, "framerate", QString().setNum(5).toStdString().c_str(), 0); + FPS = mode.FPS; + + av_dict_set(&options, "framerate", QString().setNum(FPS).toStdString().c_str(), 0); } -#endif -#ifdef Q_OS_WIN - else if (devName.startsWith("gdigrab#")) - { - av_dict_set(&options, "framerate", QString().setNum(5).toStdString().c_str(), 0); - } -#endif -#ifdef Q_OS_WIN - else if (iformat->name == QString("dshow") && 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); - } -#endif -#ifdef Q_OS_LINUX else if (iformat->name == QString("video4linux2,v4l2") && mode) { av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0); @@ -180,6 +162,20 @@ CameraDevice* CameraDevice::open(QString devName, VideoMode mode) } } #endif +#ifdef Q_OS_WIN + else if (devName.startsWith("gdigrab#")) + { + av_dict_set(&options, "framerate", QString().setNum(5).toStdString().c_str(), 0); + av_dict_set(&options, "offset_x", QString().setNum(mode.x).toStdString().c_str(), 0); + av_dict_set(&options, "offset_y", QString().setNum(mode.y).toStdString().c_str(), 0); + av_dict_set(&options, "video_size", QString("%1x%2").arg(mode.width).arg(mode.height).toStdString().c_str(), 0); + } + else if (iformat->name == QString("dshow") && 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); + } +#endif #ifdef Q_OS_OSX else if (iformat->name == QString("avfoundation")) { @@ -215,19 +211,15 @@ void CameraDevice::open() bool CameraDevice::close() { - if (--refcount <= 0) - { - openDeviceLock.lock(); - openDevices.remove(devName); - openDeviceLock.unlock(); - avformat_close_input(&context); - delete this; - return true; - } - else - { + if (--refcount > 0) return false; - } + + openDeviceLock.lock(); + openDevices.remove(devName); + openDeviceLock.unlock(); + avformat_close_input(&context); + delete this; + return true; } QVector> CameraDevice::getRawDeviceListGeneric() @@ -351,9 +343,33 @@ QString CameraDevice::getDefaultDeviceName() return devlist[0].first; } +bool CameraDevice::isScreen(const QString &devName) +{ + return devName.startsWith("x11grab") || devName.startsWith("gdigrab"); +} + +QVector CameraDevice::getScreenModes() +{ + QList screens = QApplication::screens(); + QVector result; + + std::for_each(screens.begin(), screens.end(), [&result](QScreen *s) + { + QRect rect = s->geometry(); + QPoint p = rect.topLeft(); + + VideoMode mode(rect.width(), rect.height(), p.x(), p.y()); + result.push_back(mode); + }); + + return result; +} + QVector CameraDevice::getVideoModes(QString devName) { if (!iformat); + else if (isScreen(devName)) + return getScreenModes(); #ifdef Q_OS_WIN else if (iformat->name == QString("dshow")) return DirectShow::getDeviceModes(devName); @@ -417,6 +433,7 @@ bool CameraDevice::getDefaultInputFormat() if ((iformat = av_find_input_format("dshow"))) return true; if ((iformat = av_find_input_format("vfwcap"))) + return true; #endif #ifdef Q_OS_OSX diff --git a/src/video/cameradevice.h b/src/video/cameradevice.h index e6f304e35..48981858d 100644 --- a/src/video/cameradevice.h +++ b/src/video/cameradevice.h @@ -41,15 +41,12 @@ struct AVDictionary; class CameraDevice { public: - /// Opens a device, creating a new one if needed - /// Returns a nullptr if the device couldn't be opened - static CameraDevice* open(QString devName); /// Opens a device, creating a new one if needed /// If the device is alreay open in another mode, the mode /// will be ignored and the existing device is used /// If the mode does not exist, a new device can't be opened /// Returns a nullptr if the device couldn't be opened - static CameraDevice* open(QString devName, VideoMode mode); + static CameraDevice* open(QString devName, VideoMode mode = VideoMode()); void open(); ///< Opens the device again. Never fails bool close(); ///< Closes the device. Never fails. If returns true, "this" becomes invalid @@ -69,11 +66,15 @@ public: /// or the system default. static QString getDefaultDeviceName(); + /// Checks if a device name specifies a display + static bool isScreen(const QString &devName); + private: CameraDevice(const QString &devName, AVFormatContext *context); static CameraDevice* open(QString devName, AVDictionary** options); static bool getDefaultInputFormat(); ///< Sets CameraDevice::iformat, returns success/failure static QVector > getRawDeviceListGeneric(); ///< Uses avdevice_list_devices + static QVector getScreenModes(); ///< Returns avaliable screen modes with offset public: const QString devName; ///< Short name of the device diff --git a/src/video/camerasource.cpp b/src/video/camerasource.cpp index bfb9646c1..1bcbcc296 100644 --- a/src/video/camerasource.cpp +++ b/src/video/camerasource.cpp @@ -28,6 +28,7 @@ extern "C" { #include #include #include +#include "src/persistence/settings.h" #include "camerasource.h" #include "cameradevice.h" #include "videoframe.h" @@ -35,7 +36,7 @@ extern "C" { CameraSource* CameraSource::instance{nullptr}; CameraSource::CameraSource() - : deviceName{"none"}, device{nullptr}, mode(VideoMode{0,0,0,0}), + : deviceName{"none"}, device{nullptr}, mode(VideoMode()), cctx{nullptr}, cctxOrig{nullptr}, videoStreamIndex{-1}, _isOpen{false}, streamBlocker{false}, subscriptions{0} { @@ -67,7 +68,15 @@ void CameraSource::open() void CameraSource::open(const QString& deviceName) { - open(deviceName, VideoMode{0,0,0,0}); + bool isScreen = CameraDevice::isScreen(deviceName); + VideoMode mode = VideoMode(Settings::getInstance().getScreenRegion()); + if (!isScreen) + { + mode = VideoMode(Settings::getInstance().getCamVideoRes()); + mode.FPS = Settings::getInstance().getCamVideoFPS(); + } + + open(deviceName, mode); } void CameraSource::open(const QString& DeviceName, VideoMode Mode) @@ -206,7 +215,7 @@ void CameraSource::unsubscribe() bool CameraSource::openDevice() { - qDebug() << "Opening device "<context->streams[videoStreamIndex]->codec; codec = avcodec_find_decoder(cctxOrig->codec_id); if(!codec) + { + qWarning() << "Codec not found"; return false; + } // Copy context, since we apparently aren't allowed to use the original cctx = avcodec_alloc_context3(codec); if(avcodec_copy_context(cctx, cctxOrig) != 0) + { + qWarning() << "Can't copy context"; return false; + } cctx->refcounted_frames = 1; // Open codec if(avcodec_open2(cctx, codec, nullptr)<0) { + qWarning() << "Can't open codec"; avcodec_free_context(&cctx); return false; } @@ -288,6 +306,7 @@ void CameraSource::closeDevice() std::shared_ptr vframe = freelist[i].lock(); if (!vframe) continue; + vframe->releaseFrame(); } freelist.clear(); @@ -311,14 +330,15 @@ void CameraSource::stream() AVFrame* frame = av_frame_alloc(); if (!frame) return; + frame->opaque = nullptr; AVPacket packet; - if (av_read_frame(device->context, &packet)<0) + if (av_read_frame(device->context, &packet) < 0) return; // Only keep packets from the right stream; - if (packet.stream_index==videoStreamIndex) + if (packet.stream_index == videoStreamIndex) { // Decode video frame int frameFinished; @@ -340,7 +360,8 @@ void CameraSource::stream() av_packet_unref(&packet); }; - forever { + forever + { biglock.lock(); // When a thread makes device null, it releases it, so we acquire here @@ -357,6 +378,7 @@ void CameraSource::stream() biglock.unlock(); while (streamBlocker) QThread::yieldCurrentThread(); + QThread::yieldCurrentThread(); } } diff --git a/src/video/corevideosource.cpp b/src/video/corevideosource.cpp index 0e76df947..cac6ce382 100644 --- a/src/video/corevideosource.cpp +++ b/src/video/corevideosource.cpp @@ -17,11 +17,11 @@ along with qTox. If not, see . */ - extern "C" { #include #include } + #include "corevideosource.h" #include "videoframe.h" @@ -39,15 +39,13 @@ void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe) QMutexLocker locker(&biglock); std::shared_ptr vframe; - AVFrame* avframe; - uint8_t* buf; - int width = vpxframe->d_w, height = vpxframe->d_h; - int dstStride, srcStride, minStride; + int width = vpxframe->d_w; + int height = vpxframe->d_h; if (subscribers <= 0) return; - avframe = av_frame_alloc(); + AVFrame* avframe = av_frame_alloc(); if (!avframe) return; avframe->width = width; @@ -55,29 +53,35 @@ void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe) avframe->format = AV_PIX_FMT_YUV420P; int imgBufferSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1); - buf = (uint8_t*)av_malloc(imgBufferSize); + uint8_t* buf = (uint8_t*)av_malloc(imgBufferSize); if (!buf) { av_frame_free(&avframe); return; } - avframe->opaque = buf; uint8_t** data = avframe->data; int* linesize = avframe->linesize; av_image_fill_arrays(data, linesize, buf, AV_PIX_FMT_YUV420P, width, height, 1); - dstStride=avframe->linesize[0], srcStride=vpxframe->stride[0], minStride=std::min(dstStride, srcStride); - for (int i=0; idata[0]+dstStride*i, vpxframe->planes[0]+srcStride*i, minStride); - dstStride=avframe->linesize[1], srcStride=vpxframe->stride[1], minStride=std::min(dstStride, srcStride); - for (int i=0; idata[1]+dstStride*i, vpxframe->planes[1]+srcStride*i, minStride); - dstStride=avframe->linesize[2], srcStride=vpxframe->stride[2], minStride=std::min(dstStride, srcStride); - for (int i=0; idata[2]+dstStride*i, vpxframe->planes[2]+srcStride*i, minStride); + for (int i = 0; i < 3; i++) + { + int dstStride = avframe->linesize[i]; + int srcStride = vpxframe->stride[i]; + int minStride = std::min(dstStride, srcStride); + int size = (i == 0) ? height : height / 2; + + for (int j = 0; j < size; j++) + { + uint8_t *dst = avframe->data[i] + dstStride * j; + uint8_t *src = vpxframe->planes[i] + srcStride * j; + memcpy(dst, src, minStride); + } + } vframe = std::make_shared(avframe); + + av_free(buf); emit frameAvailable(vframe); } diff --git a/src/video/netcamview.cpp b/src/video/netcamview.cpp index 997bbfe0a..74dd0bde1 100644 --- a/src/video/netcamview.cpp +++ b/src/video/netcamview.cpp @@ -84,12 +84,8 @@ NetCamView::NetCamView(int friendId, QWidget* parent) videoSurface->setAvatar(pixmap); }); - VideoMode videoMode; - QSize videoSize = Settings::getInstance().getCamVideoRes(); - videoMode.width = videoSize.width(); - videoMode.height = videoSize.height(); + QRect videoSize = Settings::getInstance().getCamVideoRes(); qDebug() << "SIZER" << videoSize; - videoMode.FPS = Settings::getInstance().getCamVideoFPS(); } NetCamView::~NetCamView() diff --git a/src/video/videoframe.cpp b/src/video/videoframe.cpp index 72ef4f5b4..4845a5264 100644 --- a/src/video/videoframe.cpp +++ b/src/video/videoframe.cpp @@ -90,25 +90,25 @@ QImage VideoFrame::toQImage(QSize size) vpx_image *VideoFrame::toVpxImage() { - // libvpx doesn't provide a clean way to reuse an existing external buffer - // so we'll manually fill-in the vpx_image fields and hope for the best. - vpx_image* img = new vpx_image; - memset(img, 0, sizeof(vpx_image)); + vpx_image* img = vpx_img_alloc(nullptr, VPX_IMG_FMT_I420, width, height, 0); if (!convertToYUV420()) return img; - img->w = img->d_w = width; - img->h = img->d_h = height; - img->fmt = VPX_IMG_FMT_I420; - img->planes[0] = frameYUV420->data[0]; - img->planes[1] = frameYUV420->data[1]; - img->planes[2] = frameYUV420->data[2]; - img->planes[3] = nullptr; - img->stride[0] = frameYUV420->linesize[0]; - img->stride[1] = frameYUV420->linesize[1]; - img->stride[2] = frameYUV420->linesize[2]; - img->stride[3] = frameYUV420->linesize[3]; + for (int i = 0; i < 3; i++) + { + int dstStride = img->stride[i]; + int srcStride = frameYUV420->linesize[i]; + int minStride = std::min(dstStride, srcStride); + int size = (i == 0) ? img->d_h : img->d_h / 2; + + for (int j = 0; j < size; j++) + { + uint8_t *dst = img->planes[i] + dstStride * j; + uint8_t *src = frameYUV420->data[i] + srcStride * j; + memcpy(dst, src, minStride); + } + } return img; } diff --git a/src/video/videomode.h b/src/video/videomode.h index 134650af5..0fc4fb05d 100644 --- a/src/video/videomode.h +++ b/src/video/videomode.h @@ -21,13 +21,36 @@ #ifndef VIDEOMODE_H #define VIDEOMODE_H +#include +#include + /// Describes a video mode supported by a device struct VideoMode { unsigned short width, height; ///< Displayed video resolution (NOT frame resolution) + unsigned short x, y; ///< Coordinates of upper-left corner float FPS; ///< Max frames per second supported by the device at this resolution uint32_t pixel_format; + VideoMode(int width = 0, int height = 0, int x = 0, int y = 0, + int FPS = 0, int format = 0) : + width(width), height(height), x(x), y(y), + FPS(FPS), pixel_format(format) + { + } + + VideoMode(QRect rect) : + width(rect.width()), height(rect.height()), + x(rect.x()), y(rect.y()), + FPS(0), pixel_format(0) + { + } + + QRect toRect() const + { + return QRect(x, y, width, height); + } + /// All zeros means a default/unspecified mode operator bool() const { @@ -38,6 +61,8 @@ struct VideoMode { return width == other.width && height == other.height + && x == other.x + && y == other.y && FPS == other.FPS && pixel_format == other.pixel_format; } diff --git a/src/widget/form/settings/avform.cpp b/src/widget/form/settings/avform.cpp index cbd720f2d..9a3caa1d7 100644 --- a/src/widget/form/settings/avform.cpp +++ b/src/widget/form/settings/avform.cpp @@ -25,6 +25,7 @@ #include "src/video/cameradevice.h" #include "src/video/videosurface.h" #include "src/widget/translator.h" +#include "src/widget/tool/screenshotgrabber.h" #include "src/core/core.h" #include "src/core/coreav.h" @@ -32,14 +33,15 @@ #include #include + #ifndef ALC_ALL_DEVICES_SPECIFIER #define ALC_ALL_DEVICES_SPECIFIER ALC_DEVICE_SPECIFIER #endif AVForm::AVForm() : GenericForm(QPixmap(":/img/settings/av.png")) - , subscribedToAudioIn{false} - , camVideoSurface{nullptr} + , subscribedToAudioIn(false) + , camVideoSurface(nullptr) , camera(CameraSource::getInstance()) { bodyUI = new Ui::AVSettings; @@ -134,58 +136,80 @@ void AVForm::showEvent(QShowEvent* event) GenericForm::showEvent(event); } +void AVForm::open(const QString &devName, const VideoMode &mode) +{ + QRect rect = mode.toRect(); + Settings::getInstance().setCamVideoRes(rect); + Settings::getInstance().setCamVideoFPS(mode.FPS); + camera.open(devName, mode); +} + void AVForm::onVideoModesIndexChanged(int index) { - if (index<0 || index>=videoModes.size()) + if (index < 0 || index >= videoModes.size()) { qWarning() << "Invalid mode index"; return; } int devIndex = bodyUI->videoDevCombobox->currentIndex(); - if (devIndex<0 || devIndex>=videoModes.size()) + if (devIndex < 0 || devIndex >= videoDeviceList.size()) { qWarning() << "Invalid device index"; return; } QString devName = videoDeviceList[devIndex].first; VideoMode mode = videoModes[index]; - Settings::getInstance().setCamVideoRes(QSize(mode.width, mode.height)); - Settings::getInstance().setCamVideoFPS(mode.FPS); - camera.open(devName, mode); -} -void AVForm::updateVideoModes(int curIndex) -{ - if (curIndex<0 || curIndex>=videoDeviceList.size()) + if (CameraDevice::isScreen(devName) && mode == VideoMode()) { - qWarning() << "Invalid index"; + if (Settings::getInstance().getScreenGrabbed()) + { + VideoMode mode(Settings::getInstance().getScreenRegion()); + open(devName, mode); + return; + } + + ScreenshotGrabber* screenshotGrabber = new ScreenshotGrabber(this); + + auto onGrabbed = [screenshotGrabber, devName, this] (QRect region) + { + VideoMode mode(region); + mode.width = mode.width / 2 * 2; + mode.height = mode.height / 2 * 2; + + Settings::getInstance().setScreenRegion(mode.toRect()); + Settings::getInstance().setScreenGrabbed(true); + + open(devName, mode); + delete screenshotGrabber; + }; + + connect(screenshotGrabber, &ScreenshotGrabber::regionChosen, this, onGrabbed, Qt::QueuedConnection); + screenshotGrabber->showGrabber(); return; } - QString devName = videoDeviceList[curIndex].first; - QVector allVideoModes = CameraDevice::getVideoModes(devName); - std::sort(allVideoModes.begin(), allVideoModes.end(), - [](const VideoMode& a, const VideoMode& b) - {return a.width!=b.width ? a.width>b.width : - a.height!=b.height ? a.height>b.height : - a.FPS>b.FPS;}); - bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true); - bodyUI->videoModescomboBox->clear(); + Settings::getInstance().setScreenGrabbed(false); + open(devName, mode); +} + +void AVForm::selectBestModes(QVector &allVideoModes) +{ // Identify the best resolutions available for the supposed XXXXp resolutions. std::map idealModes; - idealModes[120] = {160,120,0,0}; - 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; + idealModes[120] = VideoMode(160, 120); + idealModes[240] = VideoMode(460, 240); + idealModes[360] = VideoMode(640, 360); + idealModes[480] = VideoMode(854, 480); + idealModes[720] = VideoMode(1280, 720); + idealModes[1080] = VideoMode(1920, 1080); - qDebug("available Modes:"); - for (int i=0; i bestModeInds; + for (int i = 0; i < allVideoModes.size(); ++i) { VideoMode mode = allVideoModes[i]; - qDebug("width: %d, height: %d, FPS: %f, pixel format: %s", mode.width, mode.height, mode.FPS, CameraDevice::getPixelFormatString(mode.pixel_format).toStdString().c_str()); + QString pixelFormat = CameraDevice::getPixelFormatString(mode.pixel_format); + qDebug("width: %d, height: %d, FPS: %f, pixel format: %s", mode.width, mode.height, mode.FPS, pixelFormat.toStdString().c_str()); // PS3-Cam protection, everything above 60fps makes no sense if(mode.FPS > 60) @@ -205,120 +229,188 @@ void AVForm::updateVideoModes(int curIndex) bestModeInds[res] = i; continue; } - int ind = bestModeInds[res]; - if (mode.norm(idealMode) < allVideoModes[ind].norm(idealMode)) + + int index = bestModeInds[res]; + VideoMode best = allVideoModes[index]; + if (mode.norm(idealMode) < best.norm(idealMode)) { bestModeInds[res] = i; + continue; } - else if (mode.norm(idealMode) == allVideoModes[ind].norm(idealMode)) + + if (mode.norm(idealMode) == best.norm(idealMode)) { // prefer higher FPS and "better" pixel formats - if (mode.FPS > allVideoModes[ind].FPS) + if (mode.FPS > best.FPS) { bestModeInds[res] = i; + continue; } - else if (mode.FPS == allVideoModes[ind].FPS && - CameraDevice::betterPixelFormat(mode.pixel_format, allVideoModes[ind].pixel_format)) - { + + bool better = CameraDevice::betterPixelFormat(mode.pixel_format, best.pixel_format); + if (mode.FPS == best.FPS && better) bestModeInds[res] = i; - } } } } - qDebug("selected Modes:"); - int prefResIndex = -1; - QSize prefRes = Settings::getInstance().getCamVideoRes(); - unsigned short prefFPS = Settings::getInstance().getCamVideoFPS(); - // Iterate backwards to show higest resolution first. - videoModes.clear(); - for(auto iter = bestModeInds.rbegin(); iter != bestModeInds.rend(); ++iter) + + QVector newVideoModes; + for (auto it = bestModeInds.rbegin(); it != bestModeInds.rend(); ++it) { - int i = iter->second; - VideoMode mode = allVideoModes[i]; + VideoMode mode = allVideoModes[it->second]; + auto result = std::find(newVideoModes.begin(), newVideoModes.end(), mode); + if (result == newVideoModes.end()) + newVideoModes.push_back(mode); + } + allVideoModes = newVideoModes; +} - if (videoModes.contains(mode)) - continue; +void AVForm::fillCameraModesComboBox() +{ + bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true); + bodyUI->videoModescomboBox->clear(); + + for(int i = 0; i < videoModes.size(); i++) + { + VideoMode mode = videoModes[i]; - videoModes.append(mode); - if (mode.width==prefRes.width() && mode.height==prefRes.height() && mode.FPS == prefFPS && prefResIndex==-1) - prefResIndex = videoModes.size() - 1; QString str; - qDebug("width: %d, height: %d, FPS: %f, pixel format: %s\n", mode.width, mode.height, mode.FPS, CameraDevice::getPixelFormatString(mode.pixel_format).toStdString().c_str()); + QString pixelFormat = CameraDevice::getPixelFormatString(mode.pixel_format); + qDebug("width: %d, height: %d, FPS: %f, pixel format: %s\n", mode.width, mode.height, mode.FPS, pixelFormat.toStdString().c_str()); + if (mode.height && mode.width) - str += tr("%1p").arg(iter->first); + str += QString("%1p").arg(mode.height); else str += tr("Default resolution"); + bodyUI->videoModescomboBox->addItem(str); } + if (videoModes.isEmpty()) bodyUI->videoModescomboBox->addItem(tr("Default resolution")); + bodyUI->videoModescomboBox->blockSignals(previouslyBlocked); - if (prefResIndex != -1) +} + +int AVForm::searchPreferredIndex() +{ + QRect prefRes = Settings::getInstance().getCamVideoRes(); + unsigned short prefFPS = Settings::getInstance().getCamVideoFPS(); + + for (int i = 0; i < videoModes.size(); i++) { - bodyUI->videoModescomboBox->setCurrentIndex(prefResIndex); + VideoMode mode = videoModes[i]; + if (mode.width == prefRes.width() + && mode.height == prefRes.height() + && mode.FPS == prefFPS) + return i; + } + + return -1; +} + +void AVForm::fillScreenModesComboBox() +{ + bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true); + bodyUI->videoModescomboBox->clear(); + + for(int i = 0; i < videoModes.size(); i++) + { + VideoMode mode = videoModes[i]; + QString pixelFormat = CameraDevice::getPixelFormatString(mode.pixel_format); + qDebug("%dx%d+%d,%d FPS: %f, pixel format: %s\n", mode.width, mode.height, mode.x, mode.y, mode.FPS, pixelFormat.toStdString().c_str()); + + QString name; + if (mode.width && mode.height) + name = QString("Screen %1").arg(i + 1); + else + name = tr("Select region"); + + bodyUI->videoModescomboBox->addItem(name); + } + + bodyUI->videoModescomboBox->blockSignals(previouslyBlocked); +} + +void AVForm::updateVideoModes(int curIndex) +{ + if (curIndex < 0 || curIndex >= videoDeviceList.size()) + { + qWarning() << "Invalid index"; + return; + } + QString devName = videoDeviceList[curIndex].first; + QVector allVideoModes = CameraDevice::getVideoModes(devName); + + qDebug("available Modes:"); + bool isScreen = CameraDevice::isScreen(devName); + if (isScreen) + { + // Add extra video mode to region selection + allVideoModes.push_back(VideoMode()); + videoModes = allVideoModes; + fillScreenModesComboBox(); } else { - // If the user hasn't set a preffered resolution yet, - // we'll pick the resolution in the middle of the list, - // and the best FPS for that resolution. - // If we picked the lowest resolution, the quality would be awful - // but if we picked the largest, FPS would be bad and thus quality bad too. - int numRes=0; - QSize lastSize; - for (int i=0; ivideoModescomboBox->setCurrentIndex(i); - break; - } - } + selectBestModes(allVideoModes); + videoModes = allVideoModes; - if (videoModes.size()) - { - bodyUI->videoModescomboBox->setUpdatesEnabled(false); - bodyUI->videoModescomboBox->setCurrentIndex(-1); - bodyUI->videoModescomboBox->setUpdatesEnabled(true); - bodyUI->videoModescomboBox->setCurrentIndex(0); - } - else - { - // We don't have any video modes, open it with the default mode - camera.open(devName); - } + qDebug("selected Modes:"); + fillCameraModesComboBox(); } + + int preferedIndex = searchPreferredIndex(); + if (preferedIndex != -1) + { + bodyUI->videoModescomboBox->setCurrentIndex(preferedIndex); + return; + } + + if (isScreen) + { + QRect rect = Settings::getInstance().getScreenRegion(); + VideoMode mode(rect); + + Settings::getInstance().setScreenGrabbed(true); + bodyUI->videoModescomboBox->setCurrentIndex(videoModes.size() - 1); + open(devName, mode); + return; + } + + // If the user hasn't set a preferred resolution yet, + // we'll pick the resolution in the middle of the list, + // and the best FPS for that resolution. + // If we picked the lowest resolution, the quality would be awful + // but if we picked the largest, FPS would be bad and thus quality bad too. + int mid = (videoModes.size() - 1) / 2; + bodyUI->videoModescomboBox->setCurrentIndex(mid); } void AVForm::onVideoDevChanged(int index) { - if (index<0 || index>=videoDeviceList.size()) + if (index < 0 || index >= videoDeviceList.size()) { qWarning() << "Invalid index"; return; } + Settings::getInstance().setScreenGrabbed(false); QString dev = videoDeviceList[index].first; Settings::getInstance().setVideoDev(dev); bool previouslyBlocked = bodyUI->videoModescomboBox->blockSignals(true); updateVideoModes(index); bodyUI->videoModescomboBox->blockSignals(previouslyBlocked); - camera.open(dev); + + if (Settings::getInstance().getScreenGrabbed()) + return; + + int modeIndex = bodyUI->videoModescomboBox->currentIndex(); + VideoMode mode = VideoMode(); + if (0 < modeIndex && modeIndex < videoModes.size()) + mode = videoModes[modeIndex]; + + camera.open(dev, mode); if (dev == "none") Core::getInstance()->getAv()->sendNoVideo(); } diff --git a/src/widget/form/settings/avform.h b/src/widget/form/settings/avform.h index b1c2cc85d..7ec5a146e 100644 --- a/src/widget/form/settings/avform.h +++ b/src/widget/form/settings/avform.h @@ -46,6 +46,11 @@ private: void getAudioOutDevices(); void getVideoDevices(); + void selectBestModes(QVector &allVideoModes); + void fillCameraModesComboBox(); + void fillScreenModesComboBox(); + int searchPreferredIndex(); + void createVideoSurface(); void killVideoSurface(); @@ -70,9 +75,9 @@ protected: private: bool eventFilter(QObject *o, QEvent *e) final override; - void hideEvent(QHideEvent* event) final override; void showEvent(QShowEvent*event) final override; + void open(const QString &devName, const VideoMode &mode); private: Ui::AVSettings *bodyUI; diff --git a/src/widget/tool/screenshotgrabber.cpp b/src/widget/tool/screenshotgrabber.cpp index 36115d7ea..4d404927a 100644 --- a/src/widget/tool/screenshotgrabber.cpp +++ b/src/widget/tool/screenshotgrabber.cpp @@ -131,6 +131,7 @@ void ScreenshotGrabber::acceptRegion() if (rect.width() < 1 || rect.height() < 1) return; + emit regionChosen(rect); qDebug() << "Screenshot accepted, chosen region" << rect; QPixmap pixmap = this->screenGrab.copy(rect); this->window->close(); diff --git a/src/widget/tool/screenshotgrabber.h b/src/widget/tool/screenshotgrabber.h index 0a0a36b62..e983aba13 100644 --- a/src/widget/tool/screenshotgrabber.h +++ b/src/widget/tool/screenshotgrabber.h @@ -52,6 +52,7 @@ public slots: signals: void screenshotTaken(const QPixmap &pixmap); + void regionChosen(QRect region); void rejected(); private: