diff --git a/src/lib/storage/settings/OkSettings.cpp b/src/lib/storage/settings/OkSettings.cpp index 81b9b502..d1ca6c8a 100644 --- a/src/lib/storage/settings/OkSettings.cpp +++ b/src/lib/storage/settings/OkSettings.cpp @@ -61,6 +61,8 @@ OkSettings::OkSettings(QObject* parent) connect(this, &OkSettings::timestampFormatChanged, this, &OkSettings::saveGlobal); connect(this, &OkSettings::dateFormatChanged, this, &OkSettings::saveGlobal); connect(this, &OkSettings::outVolumeChanged, this, &OkSettings::saveGlobal); + connect(this, &OkSettings::camVideoResChanged, this, &OkSettings::saveGlobal); + connect(this, &OkSettings::camVideoFPSChanged, this, &OkSettings::saveGlobal); path = getGlobalSettingsFile(); qDebug() << "Settings file at:" << path; diff --git a/src/lib/video/cameradevice.cpp b/src/lib/video/cameradevice.cpp index 3fced25e..5b58617e 100644 --- a/src/lib/video/cameradevice.cpp +++ b/src/lib/video/cameradevice.cpp @@ -15,6 +15,8 @@ #include #include #include +#include + extern "C" { #include #include @@ -46,6 +48,9 @@ extern "C" { namespace lib::video { + +std::binary_semaphore sem(1); + /** * @class CameraDevice * @@ -74,7 +79,15 @@ CameraDevice::~CameraDevice() void CameraDevice::stop() { // QMutexLocker locker{&openDeviceLock}; + // sem.acquire(); run = false; + // sem.release(); +} + +bool CameraDevice::isOpened() +{ + QMutexLocker locker{&openDeviceLock}; + return context; } void CameraDevice::stream() { @@ -89,8 +102,15 @@ void CameraDevice::stream() { break; } + if(!isOpened()){ + return; + } + + // sem.acquire(); readFrame(); + // sem.release(); } + qDebug() << __func__ << "was Finished!"; if(handler){ handler->onCompleted(); @@ -103,7 +123,7 @@ void CameraDevice::readFrame() QMutexLocker locker{&openDeviceLock}; if (!context) { - qWarning() << __func__ << "Exited."; + // qWarning() << __func__ << "Exited."; return; } @@ -112,7 +132,7 @@ void CameraDevice::readFrame() return; } - // Forward packets to the decoder and grab the decoded frame + // Forward packets to the decoder and grab the decoded frame bool isVideo = packet.stream_index == videoStreamIndex; bool readyToRecive = isVideo && !avcodec_send_packet(cctx, &packet); @@ -139,8 +159,10 @@ void CameraDevice::readFrame() } bool CameraDevice::open(const VideoDevice& dev, AVDictionary** options, std::string &error) { - QMutexLocker locker(&openDeviceLock); - + if(isOpened()){ + qWarning() << "Was opened!"; + return false; + } qDebug() << __func__ << std::format("device:{} url:{}", dev.name.toStdString(), dev.url.toStdString()).c_str(); auto format = getDefaultInputFormat(dev.type); @@ -156,17 +178,17 @@ bool CameraDevice::open(const VideoDevice& dev, AVDictionary** options, std::str int aduration = context->max_analyze_duration = 0; if (avformat_find_stream_info(context, nullptr) < 0) { - avformat_close_input(&context); - avformat_free_context(context); error = "Unable to find stream info!"; qWarning() << error.c_str(); + avformat_close_input(&context); + avformat_free_context(context); return false; } context->max_analyze_duration = aduration; - // Find the first video stream, if any + // Find the first video stream, if any for (unsigned i = 0; i < context->nb_streams; ++i) { AVMediaType type = context->streams[i]->codecpar->codec_type; if (type == AVMEDIA_TYPE_VIDEO) { @@ -183,7 +205,7 @@ bool CameraDevice::open(const VideoDevice& dev, AVDictionary** options, std::str } - // Get the stream's codec's parameters and find a matching decoder + // Get the stream's codec's parameters and find a matching decoder AVCodecParameters* cparams = context->streams[videoStreamIndex]->codecpar; AVCodecID codecId = cparams->codec_id; qDebug() << "Codec id is:" << codecId; @@ -198,7 +220,7 @@ bool CameraDevice::open(const VideoDevice& dev, AVDictionary** options, std::str } - // Create a context for our codec, using the existing parameters + // Create a context for our codec, using the existing parameters cctx = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(cctx, cparams) < 0) { error = "Can't create AV context from parameters"; @@ -207,7 +229,7 @@ bool CameraDevice::open(const VideoDevice& dev, AVDictionary** options, std::str return false; } - // Open codec + // Open codec if (avcodec_open2(cctx, codec, nullptr) < 0) { error= "Can't open codec"; qWarning() << error.c_str(); @@ -220,106 +242,9 @@ bool CameraDevice::open(const VideoDevice& dev, AVDictionary** options, std::str } -bool CameraDevice::open(VideoMode mode) { +bool CameraDevice::open() { qDebug() << "Open device:" << videoDevice.name; - qDebug() << "Vide mode is:[" << mode.width << "x" << mode.height << "FPS" << mode.FPS << "]"; - - - float FPS = 30; - if (mode.FPS > 0.0f && mode.FPS <= 30) { - FPS = mode.FPS; - } else { - qWarning() << "Using default FPS:" << FPS; - } - - if (mode.width == 0) { - mode.width = 340; - } - - if (mode.height == 0) { - mode.height = 480; - } - options = nullptr; - //设置视频大小 - const std::string videoSize = QStringLiteral("%1x%2").arg(mode.width).arg(mode.height).toStdString(); - qDebug() << "videoSize: " << QString::fromStdString(videoSize); - av_dict_set(&options, "video_size", videoSize.c_str(), 0); - - //采样率 - const std::string framerate = std::format("{}", FPS); - qDebug() << "framerate: " << QString::fromStdString(framerate); - av_dict_set(&options, "framerate", framerate.c_str(), 0); - - - // auto devName = dev.name; - // if (!iformat) - // ; -// #if USING_V4L -// if (devName.startsWith("x11grab#")) { -// QSize screen; -// if (mode.width && mode.height) { -// screen.setWidth(mode.width); -// screen.setHeight(mode.height); -// } else { -// QScreen* defaultScreen = QApplication::primaryScreen(); -// qreal pixRatio = defaultScreen->devicePixelRatio(); - -// screen = defaultScreen->size(); -// // Workaround https://trac.ffmpeg.org/ticket/4574 by choping 1 px bottom and right -// // Actually, let's chop two pixels, toxav hates odd resolutions (off by one stride) -// screen.setWidth((screen.width() * pixRatio) - 2); -// screen.setHeight((screen.height() * pixRatio) - 2); -// } -// const std::string screenVideoSize = QStringLiteral("%1x%2").arg(screen.width()).arg(screen.height()).toStdString(); - -// devName += QString("+%1,%2").arg(QString().setNum(mode.x), QString().setNum(mode.y)); - - -// } -// if (format->name == QString("video4linux2,v4l2") && mode) { -// av_dict_set(&options, "video_size", videoSize.c_str(), 0); -// av_dict_set(&options, "framerate", framerate.c_str(), 0); -// const std::string pixelFormatStr = -// v4l2::getPixelFormatString(mode.pixel_format).toStdString(); -// // don't try to set a format string that doesn't exist -// if (pixelFormatStr != "unknown" && pixelFormatStr != "invalid") { -// const char* pixel_format = pixelFormatStr.c_str(); -// av_dict_set(&options, "pixel_format", pixel_format, 0); -// } -// } -// #endif -// #ifdef Q_OS_WIN -// else if (devName.startsWith("gdigrab#")) { -// const std::string offsetX = QString().setNum(mode.x).toStdString(); -// const std::string offsetY = QString().setNum(mode.y).toStdString(); -// av_dict_set(&options, "framerate", framerate.c_str(), 0); -// av_dict_set(&options, "video_size", videoSize.c_str(), 0); -// av_dict_set(&options, "offset_x", offsetX.c_str(), 0); -// av_dict_set(&options, "offset_y", offsetY.c_str(), 0); -// } else if (iformat->name == QString("dshow") && mode) { -// // 可能存在设置异常问题 -// // av_dict_set(&options, "video_size", videoSize.c_str(), 0); -// // av_dict_set(&options, "framerate", framerate.c_str(), 0); -// } -// #endif -// #ifdef Q_OS_OSX -// else if (iformat->name == QString("avfoundation")) { -// if (mode) { -// av_dict_set(&options, "video_size", videoSize.c_str(), 0); -// av_dict_set(&options, "framerate", framerate.c_str(), 0); -// } else if (devName.startsWith(avfoundation::CAPTURE_SCREEN)) { -// av_dict_set(&options, "framerate", framerate.c_str(), 0); -// av_dict_set_int(&options, "capture_cursor", 1, 0); -// av_dict_set_int(&options, "capture_mouse_clicks", 1, 0); -// } -// } -// #endif -// else if (mode) { -// qWarning() << "Video mode-setting not implemented for input " << iformat->name; -// Q_UNUSED(mode); -// } - // CameraDevice* dev0 = new CameraDevice(); std::string msg; bool opened = open(videoDevice, &options, msg); if(!opened){ @@ -340,7 +265,11 @@ bool CameraDevice::close() { QMutexLocker locker(&openDeviceLock); - handler = nullptr; + // handler = nullptr; + if(!isOpened()){ + qWarning() << "Was closed!"; + return false; + } avformat_close_input(&context); context = nullptr; @@ -348,13 +277,13 @@ bool CameraDevice::close() { avcodec_free_context(&cctx); cctx = nullptr; - av_dict_free(&options); - options = nullptr; + // av_dict_free(&options); + // options = nullptr; qDebug() << "Device: " << videoDevice.name << " closed."; - // delete this; + // delete this; return true; } @@ -467,7 +396,7 @@ QVector CameraDevice::getDeviceList() { } } - //屏幕 + //屏幕 if (iformat->name == QString("gdigrab")){ QString url = "gdigrab#desktop"; QString name = "Desktop"; @@ -476,12 +405,12 @@ QVector CameraDevice::getDeviceList() { #endif // Q_OS_WIN -// #ifdef Q_OS_OSX - // if (iformat->name == QString("avfoundation")) - // devices += avfoundation::getDeviceList(); -// #endif + // #ifdef Q_OS_OSX + // if (iformat->name == QString("avfoundation")) + // devices += avfoundation::getDeviceList(); + // #endif } - // devices += getRawDeviceListGeneric(); + // devices += getRawDeviceListGeneric(); return devices; } @@ -534,7 +463,7 @@ QVector CameraDevice::getScreenModes() { qreal pixRatio = s->devicePixelRatio(); VideoMode mode(rect.width() * pixRatio, rect.height() * pixRatio, p.x() * pixRatio, - p.y() * pixRatio); + p.y() * pixRatio, 30); result.push_back(mode); }); @@ -556,7 +485,7 @@ QVector CameraDevice::getVideoModes() const { qDebug() << "format name is" << iformat->name; - //如果是屏幕 + //如果是屏幕 if (isScreen(iformat->name)) return getScreenModes(); @@ -578,6 +507,48 @@ QVector CameraDevice::getVideoModes() const { return {}; } +bool CameraDevice::setVideoMode(const VideoMode &mode_) +{ + qDebug() << __func__ << mode_.toString(); + + QMutexLocker locker(&openDeviceLock); + + if (!(mode_.FPS > 0.0f && mode_.FPS <= 30)) { + qWarning() << "Invalid FPS:" << mode_.FPS; + return false; + } + + if(mode_.width <= 0){ + qWarning() << "Invalid video width:" << mode_.width; + return false; + } + + if(mode_.height <= 0 ){ + qWarning() << "Invalid video height:" << mode_.height; + return false; + } + + mode = mode_; + + //设置视频大小 + // options = nullptr; + auto videoSize = std::format("{}x{}", mode.width, mode.height); + // qDebug() << "videoSize: " << QString::fromStdString(videoSize); + if(av_dict_set(&options, "video_size", videoSize.c_str(), 0)){ + qWarning() << "av_dict_set error for video_size!"; + return false; + } + + //采样率 + auto framerate = std::format("{}", mode.FPS); + if(av_dict_set(&options, "framerate", framerate.c_str(), 0) ){ + qWarning() << "av_dict_set error for framerate!"; + return false; + } + + return true; +} + /** * @brief Get the name of the pixel format of a video mode. * @param pixel_format Pixel format to get the name from. @@ -617,38 +588,38 @@ const AVInputFormat* CameraDevice::getDefaultInputFormat(VideoType type) { // Webcam input formats case VideoType::Camera:{ #if USING_V4L - auto iformat = av_find_input_format("v4l2"); - if(iformat) return iformat; + auto iformat = av_find_input_format("v4l2"); + if(iformat) return iformat; #endif #ifdef Q_OS_WIN - auto iformat = av_find_input_format("dshow"); - if(iformat) return iformat; - iformat = av_find_input_format("vfwcap"); - if(iformat) return iformat; + auto iformat = av_find_input_format("dshow"); + if(iformat) return iformat; + iformat = av_find_input_format("vfwcap"); + if(iformat) return iformat; #endif #ifdef Q_OS_OSX - auto iformat = av_find_input_format("avfoundation"); - if(iformat) return iformat; - iformat = av_find_input_format("qtkit"); - if(iformat) return iformat; + auto iformat = av_find_input_format("avfoundation"); + if(iformat) return iformat; + iformat = av_find_input_format("qtkit"); + if(iformat) return iformat; #endif - break; + break; } - // Desktop capture input formats + // Desktop capture input formats case VideoType::Desktop:{ #if USING_V4L - auto iformat = av_find_input_format("x11grab"); - if(iformat) return iformat; + auto iformat = av_find_input_format("x11grab"); + if(iformat) return iformat; #endif #ifdef Q_OS_WIN - auto iformat = av_find_input_format("gdigrab"); - if(iformat) return iformat; + auto iformat = av_find_input_format("gdigrab"); + if(iformat) return iformat; #endif - break; + break; } case VideoType::Stream: diff --git a/src/lib/video/cameradevice.h b/src/lib/video/cameradevice.h index 65385674..3fee84e5 100644 --- a/src/lib/video/cameradevice.h +++ b/src/lib/video/cameradevice.h @@ -18,6 +18,7 @@ #include #include #include + #include #include "videoframe.h" #include "videomode.h" @@ -36,7 +37,13 @@ class CameraDevice { explicit CameraDevice(const VideoDevice &dev, FrameHandler* h); ~CameraDevice(); - bool open(VideoMode mode); + /** + * @brief Dynamic sets video mode + * @param mode + * @return + */ + bool setVideoMode(const VideoMode& mode); + bool open(); bool close(); static QVector getDeviceList(); @@ -54,7 +61,7 @@ class CameraDevice { void stream(); void stop(); - + bool isOpened(); private: void readFrame(); @@ -68,6 +75,7 @@ class CameraDevice { mutable CompatibleRecursiveMutex openDeviceLock; VideoDevice videoDevice; + VideoMode mode; int videoStreamIndex; AtomicIDType id; diff --git a/src/lib/video/camerasource.cpp b/src/lib/video/camerasource.cpp index 4587dd6b..1ab734dc 100644 --- a/src/lib/video/camerasource.cpp +++ b/src/lib/video/camerasource.cpp @@ -53,28 +53,15 @@ CameraSource::CameraSource(const VideoDevice &dev) CameraSource::~CameraSource() { closeDevice(); + delete device; qDebug() << __func__ << "Destroyed."; - - // deviceThread->exit(0); - // deviceThread->wait(); - - // QMutexLocker locker{&mutex}; - - // qDebug() << "untrackFrames" << id; - // Free all remaining VideoFrame - // VideoFrame::untrackFrames(id, true); - - // if (cctx) { - // avcodec_free_context(&cctx); - // } - // locker.unlock(); } /** * @brief Creates the instance. */ -std::unique_ptr CameraSource::CreateInstance(VideoDevice dev) { +std::unique_ptr CameraSource::CreateInstance(const VideoDevice &dev) { qDebug() << __func__ << dev.name; return std::make_unique(dev); } @@ -101,7 +88,20 @@ void CameraSource::setupDefault() { void CameraSource::setup(const VideoMode &vm) { + QMutexLocker locker{&mutex}; mode = vm; + + bool isOpened = device->isOpened(); + + auto y = device->setVideoMode(mode); + if(!y){ + return; + } + + if(isOpened){ + closeDevice(); + openDevice(); + } } void CameraSource::setupDevice(const QString& deviceName_, const VideoMode& Mode) { @@ -110,22 +110,15 @@ void CameraSource::setupDevice(const QString& deviceName_, const VideoMode& Mode QVector CameraSource::getVideoModes() { + QMutexLocker locker{&mutex}; return device->getVideoModes(); } -void CameraSource::subscribe() { - -} - -void CameraSource::unsubscribe() { - -} - void CameraSource::onCompleted() { if (device) { device->close(); - delete device; + emit sourceStopped(); } } @@ -142,8 +135,8 @@ void CameraSource::onFrame(std::shared_ptr vf) */ void CameraSource::openDevice() { - if(deviceThread){ - qWarning() << "Opened device!"; + if(device->isOpened()){ + qWarning() << "Was opened!"; return; } @@ -153,7 +146,7 @@ void CameraSource::openDevice() { qDebug() << "Opening device" << deviceName; // We need to create a new CameraDevice(Enable camera light) - bool opened = device->open(mode); + bool opened = device->open(); if (!opened) { qWarning() << "Failed to open device:" << deviceName; emit openFailed(); @@ -161,9 +154,11 @@ void CameraSource::openDevice() { } //Make device thread - deviceThread = std::make_unique([&](){ - device->stream(); - }); + if(!deviceThread){ + deviceThread = std::make_unique([&](){ + device->stream(); + }); + } // Device is opened emit deviceOpened(); @@ -175,8 +170,8 @@ void CameraSource::openDevice() { */ void CameraSource::closeDevice() { - if(!deviceThread){ - qWarning() << "Is not opened!"; + if(!device->isOpened()){ + qWarning() << "Was closed!"; return; } diff --git a/src/lib/video/camerasource.h b/src/lib/video/camerasource.h index 20b711d7..ff6b8eb2 100644 --- a/src/lib/video/camerasource.h +++ b/src/lib/video/camerasource.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "lib/video/videomode.h" #include "lib/video/videosource.h" @@ -31,7 +32,7 @@ namespace lib::video { class CameraSource : public VideoSource, public FrameHandler { Q_OBJECT public: - static std::unique_ptr CreateInstance(VideoDevice dev); + static std::unique_ptr CreateInstance(const VideoDevice &dev); static void destroyInstance(); explicit CameraSource(const VideoDevice &dev); @@ -43,16 +44,13 @@ class CameraSource : public VideoSource, public FrameHandler { void setupDefault(); void setup(const VideoMode& mode); - // VideoSource interface - void subscribe() override; - void unsubscribe() override; protected: void onCompleted() override; void onFrame(std::shared_ptr frm) override; private: - QRecursiveMutex mutex; + CompatibleRecursiveMutex mutex; VideoDevice dev; VideoMode mode; diff --git a/src/lib/video/videoframe.cpp b/src/lib/video/videoframe.cpp index d52365ba..67cc07ee 100644 --- a/src/lib/video/videoframe.cpp +++ b/src/lib/video/videoframe.cpp @@ -281,13 +281,6 @@ QImage VideoFrame::toQImage(QSize frameSize) { /** * @brief Converts this VideoFrame to a ToxAVFrame that shares this VideoFrame's buffer. * - * The given ToxAVFrame will be frame aligned under a pixel format of planar YUV with a chroma - * subsampling format of 4:2:0 (i.e. AV_PIX_FMT_YUV420P). - * - * @param frameSize the given frame size of ToxAVFrame to generate. Defaults to source frame size - * if frameSize is invalid. - * @return a ToxAVFrame structure that represents this VideoFrame, sharing it's buffers or an - * empty structure if this VideoFrame is no longer valid. */ ToxYUVFrame VideoFrame::toToxYUVFrame(QSize frameSize) { if (!frameSize.isValid()) { @@ -299,7 +292,6 @@ ToxYUVFrame VideoFrame::toToxYUVFrame(QSize frameSize) { ToxYUVFrame ret{static_cast(frameSize.width()), static_cast(frameSize.height()), frame->data[0], frame->data[1], frame->data[2]}; - return ret; }; diff --git a/src/lib/video/videomode.cpp b/src/lib/video/videomode.cpp index 754987b4..6485e76a 100644 --- a/src/lib/video/videomode.cpp +++ b/src/lib/video/videomode.cpp @@ -55,11 +55,11 @@ uint32_t VideoMode::tolerance() const { return std::max((width + height) / toleranceFactor, minTolerance); } -std::string VideoMode::toString() +QString VideoMode::toString() const { - return std::format("w:{} h:{}, x:{} y:{} FPS:{} format:{}", - width, height, x, y, FPS, - CameraDevice::getPixelFormatString(pixel_format).toStdString()); + return QString("{w:%1 h:%2, x:%3 y:%4, FPS:%5, format:%6}")// + .arg(width).arg( height).arg(x).arg(y).arg(FPS)// + .arg(CameraDevice::getPixelFormatString(pixel_format)); } /** diff --git a/src/lib/video/videomode.h b/src/lib/video/videomode.h index d94a8eab..1f64f4b8 100644 --- a/src/lib/video/videomode.h +++ b/src/lib/video/videomode.h @@ -33,10 +33,10 @@ enum class VideoType { }; struct VideoMode { - int width; - int height; - int x; - int y; + int width = 0; + int height = 0; + int x = 0; + int y = 0; float FPS = -1.0f; uint32_t pixel_format = 0; @@ -44,6 +44,10 @@ struct VideoMode { explicit VideoMode(QRect rect); + inline bool isValid() const { + return width > 0 && height > 0; + } + QRect toRect() const; operator bool() const; @@ -51,7 +55,7 @@ struct VideoMode { uint32_t norm(const VideoMode& other) const; uint32_t tolerance() const; - std::string toString(); + QString toString() const; }; diff --git a/src/lib/video/videosource.h b/src/lib/video/videosource.h index 038ca5fb..640991c6 100644 --- a/src/lib/video/videosource.h +++ b/src/lib/video/videosource.h @@ -33,15 +33,6 @@ class VideoSource : public QObject { VideoSource() : id(sourceIDs++) {} virtual ~VideoSource() = default; - /** - * @brief If subscribe sucessfully opens the source, it will start emitting frameAvailable - * signals. - */ - virtual void subscribe() = 0; - /** - * @brief Stop emitting frameAvailable signals, and free associated resources if necessary. - */ - virtual void unsubscribe() = 0; /// ID of this VideoSource const IDType id; diff --git a/src/modules/im/src/video/corevideosource.cpp b/src/modules/im/src/video/corevideosource.cpp index 97ad3c6c..ee964adb 100644 --- a/src/modules/im/src/video/corevideosource.cpp +++ b/src/modules/im/src/video/corevideosource.cpp @@ -41,24 +41,6 @@ void CoreVideoSource::pushFrame(std::unique_ptr vpxfram emit frameAvailable(std::shared_ptr(vframe.release())); } -void CoreVideoSource::subscribe() { - QMutexLocker locker(&biglock); - ++subscribers; -} - -void CoreVideoSource::unsubscribe() { - biglock.lock(); - if (--subscribers == 0) { - if (deleteOnClose) { - biglock.unlock(); - // DANGEROUS: No member access after this point, that's why we manually unlock - delete this; - return; - } - } - biglock.unlock(); -} - /** * @brief Setup delete on close * @param If true, self-delete after the last suscriber is gone diff --git a/src/modules/im/src/video/corevideosource.h b/src/modules/im/src/video/corevideosource.h index 4293ec87..32c23d12 100644 --- a/src/modules/im/src/video/corevideosource.h +++ b/src/modules/im/src/video/corevideosource.h @@ -25,10 +25,6 @@ class CoreVideoSource : public lib::video::VideoSource { public: CoreVideoSource(); - // VideoSource interface - virtual void subscribe() override; - virtual void unsubscribe() override; - private: void pushFrame(std::unique_ptr frame); void setDeleteOnClose(bool newstate); diff --git a/src/modules/im/src/video/videosurface.cpp b/src/modules/im/src/video/videosurface.cpp index 323ce2f4..e68f04ce 100644 --- a/src/modules/im/src/video/videosurface.cpp +++ b/src/modules/im/src/video/videosurface.cpp @@ -38,7 +38,6 @@ VideoSurface::VideoSurface(const QPixmap& avatar, QWidget* parent, bool expandin : QWidget{parent} , source{nullptr} , frameLock{false} - , hasSubscribed{0} , avatar{avatar} , ratio{1.0f} , expanding{expanding} { @@ -65,10 +64,10 @@ bool VideoSurface::isExpanding() const { * * Unsubscribe from old source and subscribe to new. */ -void VideoSurface::setSource(lib::video::VideoSource* src) { +void VideoSurface::setSource(const lib::video::VideoSource* src) { if (source == src) return; - unsubscribe(); + // unsubscribe(); source = src; subscribe(); } @@ -93,19 +92,19 @@ QPixmap VideoSurface::getAvatar() const { } void VideoSurface::subscribe() { - if (source && hasSubscribed++ == 0) { - source->subscribe(); - connect(source, &lib::video::VideoSource::frameAvailable, this, - &VideoSurface::onNewFrameAvailable); - connect(source, &lib::video::VideoSource::sourceStopped, this, - &VideoSurface::onSourceStopped); + if (!source) { + return; } + + connect(source, &lib::video::VideoSource::frameAvailable, this, + &VideoSurface::onNewFrameAvailable); + connect(source, &lib::video::VideoSource::sourceStopped, this, + &VideoSurface::onSourceStopped); + } void VideoSurface::unsubscribe() { - if (!source || hasSubscribed == 0) return; - - if (--hasSubscribed != 0) return; + if (!source) return; lock(); lastFrame.reset(); @@ -120,7 +119,7 @@ void VideoSurface::unsubscribe() { &VideoSurface::onNewFrameAvailable); disconnect(source, &lib::video::VideoSource::sourceStopped, this, &VideoSurface::onSourceStopped); - source->unsubscribe(); + } void VideoSurface::onNewFrameAvailable(const std::shared_ptr& newFrame) { @@ -157,8 +156,8 @@ void VideoSurface::paintEvent(QPaintEvent*) { // QImage frame = lastFrame->toQImage(rect().size()); // if (frame.isNull()) lastFrame.reset(); // auto& img = lastFrame->image; - - painter.drawImage(boundingRect, lastFrame->getImage(), rect(), Qt::NoFormatConversion); + // auto rec = rect(); + painter.drawImage(boundingRect, lastFrame->getImage(), lastFrame->getImage().rect(), Qt::NoFormatConversion); } else { painter.fillRect(boundingRect, Qt::white); QPixmap drawnAvatar = avatar; diff --git a/src/modules/im/src/video/videosurface.h b/src/modules/im/src/video/videosurface.h index 98136f74..aea6650f 100644 --- a/src/modules/im/src/video/videosurface.h +++ b/src/modules/im/src/video/videosurface.h @@ -28,7 +28,7 @@ class VideoSurface : public QWidget { ~VideoSurface(); bool isExpanding() const; - void setSource(lib::video::VideoSource* src); + void setSource(const lib::video::VideoSource* src); QRect getBoundingRect() const; float getRatio() const; void setAvatar(const QPixmap& pixmap); @@ -56,10 +56,9 @@ private slots: void unlock(); QRect boundingRect; - lib::video::VideoSource* source; + const lib::video::VideoSource* source; std::shared_ptr lastFrame; std::atomic_bool frameLock; - uint8_t hasSubscribed; QPixmap avatar; volatile float ratio; bool expanding; diff --git a/src/modules/im/src/widget/form/settings/avform.cpp b/src/modules/im/src/widget/form/settings/avform.cpp index 191f507f..667b4b9a 100644 --- a/src/modules/im/src/widget/form/settings/avform.cpp +++ b/src/modules/im/src/widget/form/settings/avform.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include "lib/audio/audio.h" #include "lib/audio/iaudiosettings.h" @@ -28,12 +29,13 @@ #include "lib/video/cameradevice.h" #include "lib/video/camerasource.h" #include "lib/video/ivideosettings.h" +#include "lib/video/videomode.h" +#include "src/video/videosurface.h" #include "src/Bus.h" #include "src/application.h" #include "src/core/coreav.h" #include "src/persistence/profile.h" #include "src/persistence/settings.h" -#include "src/video/videosurface.h" #include "src/widget/tool/screenshotgrabber.h" #ifndef ALC_ALL_DEVICES_SPECIFIER @@ -130,33 +132,15 @@ void AVForm::hideEvent(QHideEvent* event) { } videoDeviceList.clear(); - // camera->destroyInstance(); - // camera = nullptr; camera.reset(); - GenericForm::hideEvent(event); } void AVForm::showEvent(QShowEvent* event) { - auto ds = lib::video::CameraDevice::getDeviceList(); - if(ds.empty()){ - return; - } - - if (camera) { - return; - } - camera = lib::video::CameraSource::CreateInstance(ds.at(0)); getAudioOutDevices(); getAudioInDevices(); - - getVideoDevices(); - - auto surface = createVideoSurface(); - gridLayout->addWidget(surface, 0, 0, 1, 1); - if (audioSrc == nullptr) { audioSrc = audio->makeSource(); connect(audioSrc.get(), &lib::audio::IAudioSource::volumeAvailable, this, @@ -167,15 +151,25 @@ void AVForm::showEvent(QShowEvent* event) { audioSink = audio->makeSink(); } + auto source = initVideoDevices(); + auto surface = createVideoSurface(source); + gridLayout->addWidget(surface, 0, 0, 1, 1); + + GenericForm::showEvent(event); } -void AVForm::open(const QString& devName, const lib::video::VideoMode& mode) { +void AVForm::open(const lib::video::VideoMode& mode) { + // qDebug() << __func__ << qstring(mode.toString()); + QRect rect = mode.toRect(); videoSettings->setCamVideoRes(rect); videoSettings->setCamVideoFPS(static_cast(mode.FPS)); + camera->setup(mode); camera->openDevice(); + if(camVideoSurface) + camVideoSurface->setSource(camera.get()); } void AVForm::trackNewScreenGeometry(QScreen* qScreen) { @@ -187,7 +181,7 @@ void AVForm::onProfileChanged(Profile* profile) {} void AVForm::rescanDevices() { getAudioInDevices(); getAudioOutDevices(); - getVideoDevices(); + initVideoDevices(); } void AVForm::setVolume(float value) { @@ -205,7 +199,7 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index) { if (lib::video::CameraDevice::isScreen(devName) && mode == lib::video::VideoMode()) { if (videoSettings->getScreenGrabbed()) { lib::video::VideoMode mode(videoSettings->getScreenRegion()); - open(devName, mode); + open(mode); return; } @@ -221,7 +215,7 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index) { videoSettings->setScreenRegion(mode.toRect()); videoSettings->setScreenGrabbed(true); - open(devName, mode); + open(mode); }; // note: grabber is self-managed and will destroy itself when done @@ -234,7 +228,7 @@ void AVForm::on_videoModescomboBox_currentIndexChanged(int index) { } videoSettings->setScreenGrabbed(false); - open(devName, mode); + open(mode); } void AVForm::selectBestModes(QVector& allVideoModes) { @@ -314,18 +308,20 @@ void AVForm::selectBestModes(QVector& allVideoModes) { allVideoModes = newVideoModes; } -void AVForm::fillCameraModesComboBox() { - qDebug() << "selected Modes"; +void AVForm::fillCameraModesComboBox( const QVector &modes) { + qDebug() << __func__ ; + + videoModes = modes; + bool previouslyBlocked = videoModescomboBox->blockSignals(true); videoModescomboBox->clear(); for (int i = 0; i < videoModes.size(); ++i) { - lib::video::VideoMode mode = videoModes[i]; - qDebug() << mode.toString().c_str(); - + auto mode = videoModes[i]; + // qDebug() << mode.toString().c_str(); QString str; if (mode.height && mode.width) { - str += QString("%1x%2p").arg(mode.width).arg(mode.height); + str += QString("%1x%2 FPS:%3").arg(mode.width).arg(mode.height).arg(mode.FPS); } else { str += tr("Default resolution"); } @@ -338,22 +334,10 @@ void AVForm::fillCameraModesComboBox() { videoModescomboBox->blockSignals(previouslyBlocked); } -int AVForm::searchPreferredIndex() { - QRect prefRes = videoSettings->getCamVideoRes(); - float prefFPS = videoSettings->getCamVideoFPS(); - for (int i = 0; i < videoModes.size(); ++i) { - lib::video::VideoMode mode = videoModes[i]; - if (mode.width == prefRes.width() && mode.height == prefRes.height() && - (qAbs(mode.FPS - prefFPS) < 0.0001f)) { - return i; - } - } +void AVForm::fillScreenModesComboBox(const QVector &modes) { + videoModes = modes; - return -1; -} - -void AVForm::fillScreenModesComboBox() { bool previouslyBlocked = videoModescomboBox->blockSignals(true); videoModescomboBox->clear(); @@ -376,6 +360,22 @@ void AVForm::fillScreenModesComboBox() { videoModescomboBox->blockSignals(previouslyBlocked); } +int AVForm::searchPreferredIndex() { + QRect prefRes = videoSettings->getCamVideoRes(); + float prefFPS = videoSettings->getCamVideoFPS(); + + for (int i = 0; i < videoModes.size(); ++i) { + lib::video::VideoMode mode = videoModes[i]; + if (mode.width == prefRes.width() && mode.height == prefRes.height() && + (qAbs(mode.FPS - prefFPS) < 0.0001f)) { + return i; + } + } + + return -1; +} + + void AVForm::fillAudioQualityComboBox() { const bool previouslyBlocked = audioQualityComboBox->blockSignals(true); @@ -391,29 +391,33 @@ void AVForm::fillAudioQualityComboBox() { audioQualityComboBox->blockSignals(previouslyBlocked); } -void AVForm::updateVideoModes(int curIndex) { +lib::video::CameraSource* AVForm::initVideoModes(int curIndex) { if (curIndex < 0 || curIndex >= videoDeviceList.size()) { qWarning() << "Invalid index:" << curIndex; - return; + return nullptr; } auto dev = videoDeviceList[curIndex]; - auto devName = dev.name; - - QVector allVideoModes = camera->getVideoModes(); + // auto devName = dev.name; + if(!camera){ + camera = lib::video::CameraSource::CreateInstance(dev); + } + auto modes = camera->getVideoModes(); - qDebug("available Modes:"); - bool isScreen = lib::video::CameraDevice::isScreen(devName); - if (isScreen) { + // bool isScreen = lib::video::CameraDevice::isScreen(devName); + // if (isScreen) { // Add extra video mode to region selection - allVideoModes.push_back(lib::video::VideoMode()); - videoModes = allVideoModes; - fillScreenModesComboBox(); - } else { - selectBestModes(allVideoModes); - videoModes = allVideoModes; - fillCameraModesComboBox(); - } + // allVideoModes.push_back(lib::video::VideoMode()); + // videoModes = allVideoModes; + // fillScreenModesComboBox(modes); + // } else { + // selectBestModes(allVideoModes); + // videoModes = allVideoModes; + fillCameraModesComboBox(modes); + // } + + + // QVector allVideoModes = camera->getVideoModes(); int preferedIndex = searchPreferredIndex(); if (preferedIndex != -1) { @@ -421,18 +425,18 @@ void AVForm::updateVideoModes(int curIndex) { videoModescomboBox->blockSignals(true); videoModescomboBox->setCurrentIndex(preferedIndex); videoModescomboBox->blockSignals(false); - open(devName, videoModes[preferedIndex]); - return; + open(videoModes[preferedIndex]); + return camera.get(); } - if (isScreen) { - QRect rect = videoSettings->getScreenRegion(); - lib::video::VideoMode mode(rect); - videoSettings->setScreenGrabbed(true); - videoModescomboBox->setCurrentIndex(videoModes.size() - 1); - open(devName, mode); - return; - } + // if (isScreen) { + // QRect rect = videoSettings->getScreenRegion(); + // lib::video::VideoMode mode(rect); + // videoSettings->setScreenGrabbed(true); + // videoModescomboBox->setCurrentIndex(videoModes.size() - 1); + // open(mode); + // return; + // } // If the user hasn't set a preferred resolution yet, // we'll pick the resolution in the middle of the list, @@ -441,53 +445,61 @@ void AVForm::updateVideoModes(int curIndex) { // but if we picked the largest, FPS would be bad and thus quality bad too. int mid = (videoModes.size() - 1) / 2; videoModescomboBox->setCurrentIndex(mid); + + return camera.get(); } void AVForm::on_videoDevCombobox_currentIndexChanged(int index) { assert(0 <= index && index < videoDeviceList.size()); - videoSettings->setScreenGrabbed(false); + auto dev = videoDeviceList[index]; videoSettings->setVideoDev(dev.name); - bool previouslyBlocked = videoModescomboBox->blockSignals(true); - updateVideoModes(index); - videoModescomboBox->blockSignals(previouslyBlocked); + + videoSettings->setScreenGrabbed(false); + + // bool previouslyBlocked = videoModescomboBox->blockSignals(true); + // updateVideoModes(index); + // videoModescomboBox->blockSignals(previouslyBlocked); if (videoSettings->getScreenGrabbed()) { return; } + + camera = lib::video::CameraSource::CreateInstance(videoDeviceList.at(index)); + videoModes = camera->getVideoModes(); + fillCameraModesComboBox( camera->getVideoModes()); + int modeIndex = videoModescomboBox->currentIndex(); - lib::video::VideoMode mode = lib::video::VideoMode(); if (0 <= modeIndex && modeIndex < videoModes.size()) { - mode = videoModes[modeIndex]; + auto mode = videoModes[modeIndex]; + open(mode); } - - camera->setupDevice(dev.name, mode); - // if (dev == "none") { - // TODO: Use injected `coreAv` currently injected `nullptr` - // Core::getInstance()->getAv()->sendNoVideo(); - // } } void AVForm::on_audioQualityComboBox_currentIndexChanged(int index) { audioSettings->setAudioBitrate(audioQualityComboBox->currentData().toInt()); } -void AVForm::getVideoDevices() { +lib::video::VideoSource* AVForm::initVideoDevices() { + qDebug() << __func__; + QString settingsInDev = videoSettings->getVideoDev(); - int videoDevIndex = 0; + videoDeviceList = lib::video::CameraDevice::getDeviceList(); // prevent currentIndexChanged to be fired while adding items videoDevCombobox->blockSignals(true); videoDevCombobox->clear(); + + int videoDevIndex = 0; for (auto& device : videoDeviceList) { videoDevCombobox->addItem(device.name); if (device.url == settingsInDev) videoDevIndex = videoDevCombobox->count() - 1; } videoDevCombobox->setCurrentIndex(videoDevIndex); videoDevCombobox->blockSignals(false); - updateVideoModes(videoDevIndex); + return initVideoModes(videoDevIndex); } int AVForm::getModeSize(lib::video::VideoMode mode) { @@ -618,11 +630,11 @@ void AVForm::on_audioThresholdSlider_valueChanged(int sliderSteps) { audio->setInputThreshold(normThreshold); } -VideoSurface* AVForm::createVideoSurface() { +VideoSurface* AVForm::createVideoSurface(const lib::video::VideoSource* const source) { camVideoSurface = std::make_unique(QPixmap()); camVideoSurface->setObjectName(QStringLiteral("CamVideoSurface")); camVideoSurface->setMinimumSize(QSize(160, 120)); - camVideoSurface->setSource(camera.get()); + camVideoSurface->setSource(source); return camVideoSurface.get(); } diff --git a/src/modules/im/src/widget/form/settings/avform.h b/src/modules/im/src/widget/form/settings/avform.h index 56386a5a..478024e4 100644 --- a/src/modules/im/src/widget/form/settings/avform.h +++ b/src/modules/im/src/widget/form/settings/avform.h @@ -18,7 +18,6 @@ #include #include "genericsettings.h" -#include "lib/video/videomode.h" #include "ui_avform.h" #include @@ -28,6 +27,8 @@ namespace lib::video { class CameraSource; class IVideoSettings; +class VideoMode; +class VideoDevice; } // namespace lib::video namespace lib::audio { @@ -53,16 +54,16 @@ class AVForm : public GenericForm, private Ui::AVForm { private: void getAudioInDevices(); void getAudioOutDevices(); - void getVideoDevices(); + lib::video::VideoSource* initVideoDevices(); static int getModeSize(lib::video::VideoMode mode); void selectBestModes(QVector& allVideoModes); - void fillCameraModesComboBox(); - void fillScreenModesComboBox(); + void fillCameraModesComboBox(const QVector &); + void fillScreenModesComboBox(const QVector &); void fillAudioQualityComboBox(); int searchPreferredIndex(); - VideoSurface* createVideoSurface(); + VideoSurface* createVideoSurface(const lib::video::VideoSource* const source); void retranslateUi(); @@ -76,7 +77,7 @@ private slots: void on_audioThresholdSlider_valueChanged(int sliderSteps); void on_audioQualityComboBox_currentIndexChanged(int index); - // camera + // camera void on_videoDevCombobox_currentIndexChanged(int index); void on_videoModescomboBox_currentIndexChanged(int index); @@ -84,12 +85,12 @@ private slots: void setVolume(float value); protected: - void updateVideoModes(int curIndex); + lib::video::CameraSource* initVideoModes(int curIndex); private: void hideEvent(QHideEvent* event) final override; void showEvent(QShowEvent* event) final override; - void open(const QString& devName, const lib::video::VideoMode& mode); + void open(const lib::video::VideoMode& mode); int getStepsFromValue(qreal val, qreal valMin, qreal valMax); qreal getValueFromSteps(int steps, qreal valMin, qreal valMax); void trackNewScreenGeometry(QScreen* qScreen); diff --git a/src/modules/meet/src/MeetingOptionWidget.cpp b/src/modules/meet/src/MeetingOptionWidget.cpp index 836ffa39..98587e2f 100644 --- a/src/modules/meet/src/MeetingOptionWidget.cpp +++ b/src/modules/meet/src/MeetingOptionWidget.cpp @@ -141,7 +141,7 @@ MeetingOptionWidget::MeetingOptionWidget(QWidget* parent) : QWidget(parent) { cameraSetting->setMenu(videoMenu); connect(vGroup, &QActionGroup::triggered, this, &MeetingOptionWidget::videoSelected); - ctrlState = {true, false, true}; + ctrlState = {false, false, true}; updateAudioVideoIcon(true, true, true); QTimer::singleShot(100, this, [this]() { @@ -292,7 +292,7 @@ void MeetingOptionWidget::doOpenVideo() { return; } - auto dev = vDeviceList.at(std::distance(vDeviceList.begin(), it)); + auto& dev = vDeviceList.at(std::distance(vDeviceList.begin(), it)); selectedVideo = dev.name; cameraOutput->render(dev); videoOutLayout->setCurrentWidget(cameraOutput); @@ -344,7 +344,7 @@ void CameraVideoOutputWidget::stopRender() { } disconnect(_camera.get()); - _camera->unsubscribe(); + _camera->closeDevice(); _camera.reset();