mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Implement video modesetting for dshow
This commit is contained in:
parent
b463028536
commit
27bb71f195
1
qtox.pro
1
qtox.pro
|
@ -492,4 +492,5 @@ HEADERS += \
|
||||||
src/video/cameradevice.h \
|
src/video/cameradevice.h \
|
||||||
src/video/camerasource.h \
|
src/video/camerasource.h \
|
||||||
src/video/corevideosource.h \
|
src/video/corevideosource.h \
|
||||||
|
src/video/videomode.h \
|
||||||
src/core/toxid.h
|
src/core/toxid.h
|
||||||
|
|
|
@ -3,7 +3,11 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <Objbase.h>
|
#include <Objbase.h>
|
||||||
#include <Strmif.h>
|
#include <Strmif.h>
|
||||||
|
#include <Amvideo.h>
|
||||||
|
#include <Dvdmedia.h>
|
||||||
#include <uuids.h>
|
#include <uuids.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Most of this file is adapted from libavdevice's dshow.c,
|
* Most of this file is adapted from libavdevice's dshow.c,
|
||||||
|
@ -48,9 +52,10 @@ QVector<QPair<QString,QString>> DirectShow::getDeviceList()
|
||||||
goto fail;
|
goto fail;
|
||||||
if (CreateBindCtx(0, &bindCtx) != S_OK)
|
if (CreateBindCtx(0, &bindCtx) != S_OK)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
// Get an uuid for the device that we can pass to ffmpeg directly
|
||||||
if (m->GetDisplayName(bindCtx, nullptr, &olestr) != S_OK)
|
if (m->GetDisplayName(bindCtx, nullptr, &olestr) != S_OK)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
devIdString = wcharToUtf8(olestr);
|
devIdString = wcharToUtf8(olestr);
|
||||||
|
|
||||||
// replace ':' with '_' since FFmpeg uses : to delimitate sources
|
// replace ':' with '_' since FFmpeg uses : to delimitate sources
|
||||||
|
@ -58,6 +63,7 @@ QVector<QPair<QString,QString>> DirectShow::getDeviceList()
|
||||||
if (devIdString[i] == ':')
|
if (devIdString[i] == ':')
|
||||||
devIdString[i] = '_';
|
devIdString[i] = '_';
|
||||||
|
|
||||||
|
// Get a human friendly name/description
|
||||||
if (m->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void**)&bag) != S_OK)
|
if (m->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void**)&bag) != S_OK)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
@ -83,3 +89,144 @@ fail:
|
||||||
|
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used (by getDeviceModes) to select a device
|
||||||
|
// so we can list its properties
|
||||||
|
static IBaseFilter* getDevFilter(QString devName)
|
||||||
|
{
|
||||||
|
IBaseFilter* devFilter = nullptr;
|
||||||
|
devName = devName.mid(6); // Remove the "video="
|
||||||
|
IMoniker* m = nullptr;
|
||||||
|
|
||||||
|
ICreateDevEnum* devenum = nullptr;
|
||||||
|
if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER,
|
||||||
|
IID_ICreateDevEnum, (void**) &devenum) != S_OK)
|
||||||
|
return devFilter;
|
||||||
|
|
||||||
|
IEnumMoniker* classenum = nullptr;
|
||||||
|
if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
|
||||||
|
(IEnumMoniker**)&classenum, 0) != S_OK)
|
||||||
|
return devFilter;
|
||||||
|
|
||||||
|
while (classenum->Next(1, &m, nullptr) == S_OK)
|
||||||
|
{
|
||||||
|
LPMALLOC coMalloc = nullptr;
|
||||||
|
IBindCtx* bindCtx = nullptr;
|
||||||
|
LPOLESTR olestr = nullptr;
|
||||||
|
char* devIdString;
|
||||||
|
|
||||||
|
if (CoGetMalloc(1, &coMalloc) != S_OK)
|
||||||
|
goto fail;
|
||||||
|
if (CreateBindCtx(0, &bindCtx) != S_OK)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (m->GetDisplayName(bindCtx, nullptr, &olestr) != S_OK)
|
||||||
|
goto fail;
|
||||||
|
devIdString = wcharToUtf8(olestr);
|
||||||
|
|
||||||
|
// replace ':' with '_' since FFmpeg uses : to delimitate sources
|
||||||
|
for (unsigned i = 0; i < strlen(devIdString); i++)
|
||||||
|
if (devIdString[i] == ':')
|
||||||
|
devIdString[i] = '_';
|
||||||
|
|
||||||
|
if (devName != devIdString)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (m->BindToObject(0, 0, IID_IBaseFilter, (void**)&devFilter) != S_OK)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (olestr && coMalloc)
|
||||||
|
coMalloc->Free(olestr);
|
||||||
|
if (bindCtx)
|
||||||
|
bindCtx->Release();
|
||||||
|
delete[] devIdString;
|
||||||
|
m->Release();
|
||||||
|
}
|
||||||
|
classenum->Release();
|
||||||
|
|
||||||
|
if (!devFilter)
|
||||||
|
qWarning() << "Could't find the device "<<devName;
|
||||||
|
|
||||||
|
return devFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
|
||||||
|
{
|
||||||
|
QVector<VideoMode> modes;
|
||||||
|
|
||||||
|
IBaseFilter* devFilter = getDevFilter(devName);
|
||||||
|
if (!devFilter)
|
||||||
|
return modes;
|
||||||
|
|
||||||
|
// The outter loop tries to find a valid output pin
|
||||||
|
GUID category;
|
||||||
|
DWORD r2;
|
||||||
|
IEnumPins *pins = nullptr;
|
||||||
|
IPin *pin;
|
||||||
|
if (devFilter->EnumPins(&pins) != S_OK)
|
||||||
|
return modes;
|
||||||
|
while (pins->Next(1, &pin, nullptr) == S_OK)
|
||||||
|
{
|
||||||
|
IKsPropertySet *p = nullptr;
|
||||||
|
PIN_INFO info;
|
||||||
|
|
||||||
|
pin->QueryPinInfo(&info);
|
||||||
|
info.pFilter->Release();
|
||||||
|
if (info.dir != PINDIR_OUTPUT)
|
||||||
|
goto next;
|
||||||
|
if (pin->QueryInterface(IID_IKsPropertySet, (void**)&p) != S_OK)
|
||||||
|
goto next;
|
||||||
|
if (p->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY,
|
||||||
|
nullptr, 0, &category, sizeof(GUID), &r2) != S_OK)
|
||||||
|
goto next;
|
||||||
|
if (!IsEqualGUID(category, PIN_CATEGORY_CAPTURE))
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
// Now we can list the video modes for the current pin
|
||||||
|
// Prepare for another wall of spaghetti DIRECT SHOW QUALITY code
|
||||||
|
{
|
||||||
|
IAMStreamConfig *config = nullptr;
|
||||||
|
VIDEO_STREAM_CONFIG_CAPS *vcaps = nullptr;
|
||||||
|
int size, n;
|
||||||
|
if (pin->QueryInterface(IID_IAMStreamConfig, (void**)&config) != S_OK)
|
||||||
|
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; i<n; ++i)
|
||||||
|
{
|
||||||
|
AM_MEDIA_TYPE* type = nullptr;
|
||||||
|
if (config->GetStreamCaps(i, &type, (BYTE*)vcaps) != S_OK)
|
||||||
|
goto nextformat;
|
||||||
|
|
||||||
|
if (!IsEqualGUID(type->formattype, FORMAT_VideoInfo)
|
||||||
|
&& !IsEqualGUID(type->formattype, FORMAT_VideoInfo2))
|
||||||
|
goto nextformat;
|
||||||
|
|
||||||
|
VideoMode mode;
|
||||||
|
mode.width = vcaps->MaxOutputSize.cx;
|
||||||
|
mode.height = vcaps->MaxOutputSize.cy;
|
||||||
|
mode.FPS = 1e7 / vcaps->MinFrameInterval;
|
||||||
|
if (!modes.contains(mode))
|
||||||
|
modes.append(std::move(mode));
|
||||||
|
|
||||||
|
nextformat:
|
||||||
|
if (type->pbFormat)
|
||||||
|
CoTaskMemFree(type->pbFormat);
|
||||||
|
CoTaskMemFree(type);
|
||||||
|
}
|
||||||
|
pinend:
|
||||||
|
config->Release();
|
||||||
|
delete vcaps;
|
||||||
|
}
|
||||||
|
next:
|
||||||
|
if (p)
|
||||||
|
p->Release();
|
||||||
|
pin->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return modes;
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
|
#include "src/video/videomode.h"
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
#error "This file is only meant to be compiled for Windows targets"
|
#error "This file is only meant to be compiled for Windows targets"
|
||||||
|
@ -12,6 +13,7 @@
|
||||||
namespace DirectShow
|
namespace DirectShow
|
||||||
{
|
{
|
||||||
QVector<QPair<QString,QString>> getDeviceList();
|
QVector<QPair<QString,QString>> getDeviceList();
|
||||||
|
QVector<VideoMode> getDeviceModes(QString devName);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // DIRECTSHOW_H
|
#endif // DIRECTSHOW_H
|
||||||
|
|
|
@ -17,10 +17,9 @@ AVInputFormat* CameraDevice::iformat{nullptr};
|
||||||
CameraDevice::CameraDevice(const QString devName, AVFormatContext *context)
|
CameraDevice::CameraDevice(const QString devName, AVFormatContext *context)
|
||||||
: devName{devName}, context{context}, refcount{1}
|
: devName{devName}, context{context}, refcount{1}
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraDevice* CameraDevice::open(QString devName)
|
CameraDevice* CameraDevice::open(QString devName, AVDictionary** options)
|
||||||
{
|
{
|
||||||
openDeviceLock.lock();
|
openDeviceLock.lock();
|
||||||
AVFormatContext* fctx = nullptr;
|
AVFormatContext* fctx = nullptr;
|
||||||
|
@ -28,7 +27,7 @@ CameraDevice* CameraDevice::open(QString devName)
|
||||||
if (dev)
|
if (dev)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (avformat_open_input(&fctx, devName.toStdString().c_str(), iformat, nullptr)<0)
|
if (avformat_open_input(&fctx, devName.toStdString().c_str(), iformat, options)<0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (avformat_find_stream_info(fctx, NULL) < 0)
|
if (avformat_find_stream_info(fctx, NULL) < 0)
|
||||||
|
@ -45,6 +44,36 @@ out:
|
||||||
return dev;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CameraDevice* CameraDevice::open(QString devName)
|
||||||
|
{
|
||||||
|
return open(devName, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraDevice* CameraDevice::open(QString devName, VideoMode mode)
|
||||||
|
{
|
||||||
|
if (!getDefaultInputFormat())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
AVDictionary* options = nullptr;
|
||||||
|
if (false);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
else if (iformat->name == QString("dshow"))
|
||||||
|
{
|
||||||
|
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
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning() << "Video mode-setting not implemented for input "<<iformat->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraDevice* dev = open(devName, &options);
|
||||||
|
if (options)
|
||||||
|
av_dict_free(&options);
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
void CameraDevice::open()
|
void CameraDevice::open()
|
||||||
{
|
{
|
||||||
++refcount;
|
++refcount;
|
||||||
|
@ -130,8 +159,7 @@ QVector<QPair<QString, QString>> CameraDevice::getRawDeviceListGeneric()
|
||||||
|
|
||||||
QVector<QPair<QString, QString>> CameraDevice::getDeviceList()
|
QVector<QPair<QString, QString>> CameraDevice::getDeviceList()
|
||||||
{
|
{
|
||||||
if (!iformat)
|
if (!getDefaultInputFormat())
|
||||||
if (!getDefaultInputFormat())
|
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (iformat->name == QString("dshow"))
|
if (iformat->name == QString("dshow"))
|
||||||
|
@ -158,6 +186,19 @@ QString CameraDevice::getDefaultDeviceName()
|
||||||
return devlist[0].first;
|
return devlist[0].first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<VideoMode> CameraDevice::getVideoModes(QString devName)
|
||||||
|
{
|
||||||
|
if (false);
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
else if (iformat->name == QString("dshow"))
|
||||||
|
return DirectShow::getDeviceModes(devName);
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
qWarning() << "Video mode listing not implemented for input "<<iformat->name;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
bool CameraDevice::getDefaultInputFormat()
|
bool CameraDevice::getDefaultInputFormat()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&iformatLock);
|
QMutexLocker locker(&iformatLock);
|
||||||
|
|
|
@ -6,10 +6,12 @@
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include "videomode.h"
|
||||||
|
|
||||||
struct AVFormatContext;
|
struct AVFormatContext;
|
||||||
struct AVInputFormat;
|
struct AVInputFormat;
|
||||||
struct AVDeviceInfoList;
|
struct AVDeviceInfoList;
|
||||||
|
struct AVDictionary;
|
||||||
|
|
||||||
/// Maintains an FFmpeg context for open camera devices,
|
/// Maintains an FFmpeg context for open camera devices,
|
||||||
/// takes care of sharing the context accross users
|
/// takes care of sharing the context accross users
|
||||||
|
@ -22,12 +24,21 @@ public:
|
||||||
/// Opens a device, creating a new one if needed
|
/// Opens a device, creating a new one if needed
|
||||||
/// Returns a nullptr if the device couldn't be opened
|
/// Returns a nullptr if the device couldn't be opened
|
||||||
static CameraDevice* open(QString devName);
|
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);
|
||||||
void open(); ///< Opens the device again. Never fails
|
void open(); ///< Opens the device again. Never fails
|
||||||
bool close(); ///< Closes the device. Never fails. If returns true, "this" becomes invalid
|
bool close(); ///< Closes the device. Never fails. If returns true, "this" becomes invalid
|
||||||
|
|
||||||
/// Returns a list of device names and descriptions
|
/// Returns a list of device names and descriptions
|
||||||
/// The names are the first part of the pair and can be passed to open(QString)
|
/// The names are the first part of the pair and can be passed to open(QString)
|
||||||
static QVector<QPair<QString, QString> > getDeviceList();
|
static QVector<QPair<QString, QString>> getDeviceList();
|
||||||
|
|
||||||
|
/// Get the list of video modes for a device
|
||||||
|
static QVector<VideoMode> getVideoModes(QString devName);
|
||||||
|
|
||||||
/// Returns the short name of the default defice
|
/// Returns the short name of the default defice
|
||||||
/// This is either the device in the settings
|
/// This is either the device in the settings
|
||||||
|
@ -36,6 +47,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CameraDevice(const QString devName, AVFormatContext *context);
|
CameraDevice(const QString devName, AVFormatContext *context);
|
||||||
|
static CameraDevice* open(QString devName, AVDictionary** options);
|
||||||
static bool getDefaultInputFormat(); ///< Sets CameraDevice::iformat, returns success/failure
|
static bool getDefaultInputFormat(); ///< Sets CameraDevice::iformat, returns success/failure
|
||||||
static QVector<QPair<QString, QString> > getRawDeviceListGeneric(); ///< Uses avdevice_list_devices
|
static QVector<QPair<QString, QString> > getRawDeviceListGeneric(); ///< Uses avdevice_list_devices
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,12 @@ CameraSource::CameraSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraSource::CameraSource(const QString deviceName)
|
CameraSource::CameraSource(const QString deviceName)
|
||||||
: deviceName{deviceName}, device{nullptr},
|
: CameraSource{deviceName, VideoMode{0,0,0}}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraSource::CameraSource(const QString deviceName, VideoMode mode)
|
||||||
|
: deviceName{deviceName}, device{nullptr}, mode(mode),
|
||||||
cctx{nullptr}, videoStreamIndex{-1},
|
cctx{nullptr}, videoStreamIndex{-1},
|
||||||
biglock{false}, freelistLock{false}, subscriptions{0}
|
biglock{false}, freelistLock{false}, subscriptions{0}
|
||||||
{
|
{
|
||||||
|
@ -50,8 +55,20 @@ CameraSource::~CameraSource()
|
||||||
expected = false;
|
expected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Free all remaining VideoFrame
|
||||||
|
// Locking must be done precisely this way to avoid races
|
||||||
|
for (int i=0; i<freelist.size(); i++)
|
||||||
|
{
|
||||||
|
std::shared_ptr<VideoFrame> vframe = freelist[i].lock();
|
||||||
|
if (!vframe)
|
||||||
|
continue;
|
||||||
|
vframe->releaseFrame();
|
||||||
|
}
|
||||||
|
|
||||||
if (cctx)
|
if (cctx)
|
||||||
avcodec_free_context(&cctx);
|
avcodec_free_context(&cctx);
|
||||||
|
if (cctxOrig)
|
||||||
|
avcodec_close(cctxOrig);
|
||||||
|
|
||||||
for (int i=subscriptions; i; --i)
|
for (int i=subscriptions; i; --i)
|
||||||
device->close();
|
device->close();
|
||||||
|
@ -82,7 +99,10 @@ bool CameraSource::subscribe()
|
||||||
|
|
||||||
// We need to create a new CameraDevice
|
// We need to create a new CameraDevice
|
||||||
AVCodec* codec;
|
AVCodec* codec;
|
||||||
device = CameraDevice::open(deviceName);
|
if (mode)
|
||||||
|
device = CameraDevice::open(deviceName, mode);
|
||||||
|
else
|
||||||
|
device = CameraDevice::open(deviceName);
|
||||||
if (!device)
|
if (!device)
|
||||||
{
|
{
|
||||||
biglock = false;
|
biglock = false;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include "src/video/videosource.h"
|
#include "src/video/videosource.h"
|
||||||
|
#include "src/video/videomode.h"
|
||||||
|
|
||||||
class CameraDevice;
|
class CameraDevice;
|
||||||
struct AVCodecContext;
|
struct AVCodecContext;
|
||||||
|
@ -37,6 +38,7 @@ class CameraSource : public VideoSource
|
||||||
public:
|
public:
|
||||||
CameraSource(); ///< Opens the camera device in the settings, or the system default
|
CameraSource(); ///< Opens the camera device in the settings, or the system default
|
||||||
CameraSource(const QString deviceName);
|
CameraSource(const QString deviceName);
|
||||||
|
CameraSource(const QString deviceName, VideoMode mode);
|
||||||
~CameraSource();
|
~CameraSource();
|
||||||
|
|
||||||
// VideoSource interface
|
// VideoSource interface
|
||||||
|
@ -63,6 +65,7 @@ private:
|
||||||
QFuture<void> streamFuture; ///< Future of the streaming thread
|
QFuture<void> streamFuture; ///< Future of the streaming thread
|
||||||
const QString deviceName; ///< Short name of the device for CameraDevice's open(QString)
|
const QString deviceName; ///< Short name of the device for CameraDevice's open(QString)
|
||||||
CameraDevice* device; ///< Non-owning pointer to an open CameraDevice, or nullptr
|
CameraDevice* device; ///< Non-owning pointer to an open CameraDevice, or nullptr
|
||||||
|
VideoMode mode; ///< What mode we tried to open the device in, all zeros means default mode
|
||||||
AVCodecContext* cctx, *cctxOrig; ///< Codec context of the camera's selected video stream
|
AVCodecContext* cctx, *cctxOrig; ///< Codec context of the camera's selected video stream
|
||||||
int videoStreamIndex; ///< A camera can have multiple streams, this is the one we're decoding
|
int videoStreamIndex; ///< A camera can have multiple streams, this is the one we're decoding
|
||||||
std::atomic_bool biglock, freelistLock; ///< True when locked. Faster than mutexes for video decoding.
|
std::atomic_bool biglock, freelistLock; ///< True when locked. Faster than mutexes for video decoding.
|
||||||
|
|
25
src/video/videomode.h
Normal file
25
src/video/videomode.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef VIDEOMODE_H
|
||||||
|
#define VIDEOMODE_H
|
||||||
|
|
||||||
|
/// Describes a video mode supported by a device
|
||||||
|
struct VideoMode
|
||||||
|
{
|
||||||
|
short width, height; ///< Displayed video resolution (NOT frame resolution)
|
||||||
|
short FPS; ///< Max frames per second supported by the device at this resolution
|
||||||
|
|
||||||
|
/// All zeros means a default/unspecified mode
|
||||||
|
operator bool()
|
||||||
|
{
|
||||||
|
return width || height || FPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const VideoMode& other)
|
||||||
|
{
|
||||||
|
return width == other.width
|
||||||
|
&& height == other.height
|
||||||
|
&& FPS == other.FPS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VIDEOMODE_H
|
||||||
|
|
|
@ -78,18 +78,101 @@ void AVForm::present()
|
||||||
{
|
{
|
||||||
getAudioOutDevices();
|
getAudioOutDevices();
|
||||||
getAudioInDevices();
|
getAudioInDevices();
|
||||||
|
|
||||||
createVideoSurface();
|
createVideoSurface();
|
||||||
|
getVideoDevices();
|
||||||
bodyUI->videoModescomboBox->blockSignals(true);
|
|
||||||
bodyUI->videoModescomboBox->addItem(tr("Initializing Camera..."));
|
|
||||||
bodyUI->videoModescomboBox->blockSignals(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
|
void AVForm::on_videoModescomboBox_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
QSize res = bodyUI->videoModescomboBox->itemData(index).toSize();
|
if (index<0 || index>=videoModes.size())
|
||||||
Settings::getInstance().setCamVideoRes(res);
|
{
|
||||||
|
qWarning() << "Invalid mode index";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int devIndex = bodyUI->videoDevCombobox->currentIndex();
|
||||||
|
if (devIndex<0 || devIndex>=videoModes.size())
|
||||||
|
{
|
||||||
|
qWarning() << "Invalid device index";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString devName = videoDeviceList[devIndex].first;
|
||||||
|
VideoMode mode = videoModes[index];
|
||||||
|
Settings::getInstance().setCamVideoRes(QSize(mode.width, mode.height));
|
||||||
|
camVideoSurface->setSource(nullptr);
|
||||||
|
if (camera)
|
||||||
|
delete camera;
|
||||||
|
camera = new CameraSource(devName, mode);
|
||||||
|
camVideoSurface->setSource(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AVForm::updateVideoModes(int curIndex)
|
||||||
|
{
|
||||||
|
if (curIndex<0 || curIndex>=videoDeviceList.size())
|
||||||
|
{
|
||||||
|
qWarning() << "Invalid index";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString devName = videoDeviceList[curIndex].first;
|
||||||
|
videoModes = CameraDevice::getVideoModes(devName);
|
||||||
|
std::sort(videoModes.begin(), videoModes.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;});
|
||||||
|
bodyUI->videoModescomboBox->blockSignals(true);
|
||||||
|
bodyUI->videoModescomboBox->clear();
|
||||||
|
int prefResIndex = -1;
|
||||||
|
QSize prefRes = Settings::getInstance().getCamVideoRes();
|
||||||
|
for (int i=0; i<videoModes.size(); ++i)
|
||||||
|
{
|
||||||
|
VideoMode mode = videoModes[i];
|
||||||
|
if (mode.width==prefRes.width() && mode.height==prefRes.height() && prefResIndex==-1)
|
||||||
|
prefResIndex = i;
|
||||||
|
QString str = tr("%1x%2 at %3 FPS").arg(mode.width).arg(mode.height).arg(mode.FPS);
|
||||||
|
bodyUI->videoModescomboBox->addItem(str);
|
||||||
|
}
|
||||||
|
if (videoModes.isEmpty())
|
||||||
|
bodyUI->videoModescomboBox->addItem(tr("Default resolution"));
|
||||||
|
bodyUI->videoModescomboBox->blockSignals(false);
|
||||||
|
if (prefResIndex != -1)
|
||||||
|
{
|
||||||
|
bodyUI->videoModescomboBox->setCurrentIndex(prefResIndex);
|
||||||
|
}
|
||||||
|
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; i<videoModes.size(); i++)
|
||||||
|
{
|
||||||
|
if (lastSize != QSize{videoModes[i].width, videoModes[i].height})
|
||||||
|
{
|
||||||
|
numRes++;
|
||||||
|
lastSize = {videoModes[i].width, videoModes[i].height};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int target = numRes/2;
|
||||||
|
numRes=0;
|
||||||
|
for (int i=0; i<videoModes.size(); i++)
|
||||||
|
{
|
||||||
|
if (lastSize != QSize{videoModes[i].width, videoModes[i].height})
|
||||||
|
{
|
||||||
|
numRes++;
|
||||||
|
lastSize = {videoModes[i].width, videoModes[i].height};
|
||||||
|
}
|
||||||
|
if (numRes==target)
|
||||||
|
{
|
||||||
|
bodyUI->videoModescomboBox->setCurrentIndex(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVForm::onVideoDevChanged(int index)
|
void AVForm::onVideoDevChanged(int index)
|
||||||
|
@ -104,6 +187,7 @@ void AVForm::onVideoDevChanged(int index)
|
||||||
delete camera;
|
delete camera;
|
||||||
QString dev = videoDeviceList[index].first;
|
QString dev = videoDeviceList[index].first;
|
||||||
Settings::getInstance().setVideoDev(dev);
|
Settings::getInstance().setVideoDev(dev);
|
||||||
|
updateVideoModes(index);
|
||||||
camera = new CameraSource(dev);
|
camera = new CameraSource(dev);
|
||||||
camVideoSurface->setSource(camera);
|
camVideoSurface->setSource(camera);
|
||||||
}
|
}
|
||||||
|
@ -146,20 +230,14 @@ void AVForm::hideEvent(QHideEvent *)
|
||||||
videoDeviceList.clear();
|
videoDeviceList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVForm::showEvent(QShowEvent *)
|
|
||||||
{
|
|
||||||
createVideoSurface();
|
|
||||||
getVideoDevices();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AVForm::getVideoDevices()
|
void AVForm::getVideoDevices()
|
||||||
{
|
{
|
||||||
QString settingsInDev = Settings::getInstance().getVideoDev();
|
QString settingsInDev = Settings::getInstance().getVideoDev();
|
||||||
int videoDevIndex = 0;
|
int videoDevIndex = 0;
|
||||||
videoDeviceList = CameraDevice::getDeviceList();
|
videoDeviceList = CameraDevice::getDeviceList();
|
||||||
bodyUI->videoDevCombobox->clear();
|
|
||||||
//prevent currentIndexChanged to be fired while adding items
|
//prevent currentIndexChanged to be fired while adding items
|
||||||
bodyUI->videoDevCombobox->blockSignals(true);
|
bodyUI->videoDevCombobox->blockSignals(true);
|
||||||
|
bodyUI->videoDevCombobox->clear();
|
||||||
for (QPair<QString, QString> device : videoDeviceList)
|
for (QPair<QString, QString> device : videoDeviceList)
|
||||||
{
|
{
|
||||||
bodyUI->videoDevCombobox->addItem(device.second);
|
bodyUI->videoDevCombobox->addItem(device.second);
|
||||||
|
@ -170,6 +248,7 @@ void AVForm::getVideoDevices()
|
||||||
bodyUI->videoDevCombobox->setCurrentIndex(-1);
|
bodyUI->videoDevCombobox->setCurrentIndex(-1);
|
||||||
bodyUI->videoDevCombobox->blockSignals(false);
|
bodyUI->videoDevCombobox->blockSignals(false);
|
||||||
bodyUI->videoDevCombobox->setCurrentIndex(videoDevIndex);
|
bodyUI->videoDevCombobox->setCurrentIndex(videoDevIndex);
|
||||||
|
updateVideoModes(videoDevIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVForm::getAudioInDevices()
|
void AVForm::getAudioInDevices()
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include "genericsettings.h"
|
#include "genericsettings.h"
|
||||||
|
#include "src/video/videomode.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class AVSettings;
|
class AVSettings;
|
||||||
|
@ -57,16 +58,17 @@ private slots:
|
||||||
void onResProbingFinished(QList<QSize> res);
|
void onResProbingFinished(QList<QSize> res);
|
||||||
|
|
||||||
virtual void hideEvent(QHideEvent*);
|
virtual void hideEvent(QHideEvent*);
|
||||||
virtual void showEvent(QShowEvent*);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *o, QEvent *e);
|
bool eventFilter(QObject *o, QEvent *e);
|
||||||
|
void updateVideoModes(int curIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::AVSettings *bodyUI;
|
Ui::AVSettings *bodyUI;
|
||||||
VideoSurface* camVideoSurface;
|
VideoSurface* camVideoSurface;
|
||||||
CameraSource* camera;
|
CameraSource* camera;
|
||||||
QVector<QPair<QString, QString>> videoDeviceList;
|
QVector<QPair<QString, QString>> videoDeviceList;
|
||||||
|
QVector<VideoMode> videoModes;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue
Block a user