mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Implement DirectShow video dev listing
We can now get the list of video devices with the dshow ffmpeg device (so, on Windows). Most of this patch is adapted from libavdevice's dshow private internal interface, which retrieves useful info but is only designed to log it to stdout. We reimplement that to get an actual list of devices names/descriptions, that we can then open with ffmpeg the refular way.
This commit is contained in:
parent
e5684a4bbf
commit
f0f069114c
13
qtox.pro
13
qtox.pro
|
@ -133,8 +133,9 @@ win32 {
|
|||
RC_FILE = windows/qtox.rc
|
||||
LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -lavformat -lavdevice -lavcodec -lavutil -lswscale -lOpenAL32 -lopus
|
||||
LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -lws2_32 -liphlpapi -lz
|
||||
LIBS += -lopengl32 -lole32 -loleaut32 -lvfw32 -lws2_32 -liphlpapi -lz -luuid
|
||||
LIBS += -lqrencode
|
||||
LIBS += -lstrmiids # For DirectShow
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) {
|
||||
contains(STATICPKG, YES) {
|
||||
LIBS += -Wl,-Bstatic -lfilteraudio
|
||||
|
@ -359,7 +360,7 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
|
|||
src/misc/qrwidget.h \
|
||||
src/widget/systemtrayicon_private.h
|
||||
|
||||
SOURCES += \
|
||||
SOURCES += \
|
||||
src/widget/form/addfriendform.cpp \
|
||||
src/widget/form/settingswidget.cpp \
|
||||
src/widget/form/settings/generalform.cpp \
|
||||
|
@ -420,6 +421,14 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
|
|||
src/widget/genericchatroomwidget.cpp
|
||||
}
|
||||
|
||||
win32 {
|
||||
HEADERS += \
|
||||
src/platform/camera/directshow.h
|
||||
|
||||
SOURCES += \
|
||||
src/platform/camera/directshow.cpp
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
src/audio.cpp \
|
||||
src/historykeeper.cpp \
|
||||
|
|
85
src/platform/camera/directshow.cpp
Normal file
85
src/platform/camera/directshow.cpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
#include "directshow.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <Objbase.h>
|
||||
#include <Strmif.h>
|
||||
#include <uuids.h>
|
||||
|
||||
/**
|
||||
* Most of this file is adapted from libavdevice's dshow.c,
|
||||
* which retrieves useful information but only exposes it to
|
||||
* stdout and is not part of the public API for some reason.
|
||||
*/
|
||||
|
||||
static char *wcharToUtf8(wchar_t *w)
|
||||
{
|
||||
int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
|
||||
char *s = new char[l];
|
||||
if (s)
|
||||
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
|
||||
return s;
|
||||
}
|
||||
|
||||
QVector<QPair<QString,QString>> DirectShow::getDeviceList()
|
||||
{
|
||||
IMoniker* m = nullptr;
|
||||
QVector<QPair<QString,QString>> devices;
|
||||
|
||||
ICreateDevEnum* devenum = nullptr;
|
||||
if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_ICreateDevEnum, (void**) &devenum) != S_OK)
|
||||
return devices;
|
||||
|
||||
IEnumMoniker* classenum = nullptr;
|
||||
if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
|
||||
(IEnumMoniker**)&classenum, 0) != S_OK)
|
||||
return devices;
|
||||
|
||||
while (classenum->Next(1, &m, nullptr) == S_OK)
|
||||
{
|
||||
VARIANT var;
|
||||
IPropertyBag* bag = nullptr;
|
||||
LPMALLOC coMalloc = nullptr;
|
||||
IBindCtx* bindCtx = nullptr;
|
||||
LPOLESTR olestr = nullptr;
|
||||
char *devIdString=nullptr, *devHumanName=nullptr;
|
||||
|
||||
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 (m->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void**)&bag) != S_OK)
|
||||
goto fail;
|
||||
|
||||
var.vt = VT_BSTR;
|
||||
if (bag->Read(L"FriendlyName", &var, nullptr) != S_OK)
|
||||
goto fail;
|
||||
devHumanName = wcharToUtf8(var.bstrVal);
|
||||
|
||||
devices += {QString("video=")+devIdString, devHumanName};
|
||||
|
||||
fail:
|
||||
if (olestr && coMalloc)
|
||||
coMalloc->Free(olestr);
|
||||
if (bindCtx)
|
||||
bindCtx->Release();
|
||||
delete[] devIdString;
|
||||
delete[] devHumanName;
|
||||
if (bag)
|
||||
bag->Release();
|
||||
m->Release();
|
||||
}
|
||||
classenum->Release();
|
||||
|
||||
return devices;
|
||||
}
|
21
src/platform/camera/directshow.h
Normal file
21
src/platform/camera/directshow.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef DIRECTSHOW_H
|
||||
#define DIRECTSHOW_H
|
||||
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QPair>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#error "This file is only meant to be compiled for Windows targets"
|
||||
#endif
|
||||
|
||||
class DirectShow
|
||||
{
|
||||
public:
|
||||
static QVector<QPair<QString,QString>> getDeviceList();
|
||||
|
||||
private:
|
||||
DirectShow()=delete;
|
||||
};
|
||||
|
||||
#endif // DIRECTSHOW_H
|
|
@ -6,6 +6,10 @@ extern "C" {
|
|||
#include "cameradevice.h"
|
||||
#include "src/misc/settings.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include "src/platform/camera/directshow.h"
|
||||
#endif
|
||||
|
||||
QHash<QString, CameraDevice*> CameraDevice::openDevices;
|
||||
QMutex CameraDevice::openDeviceLock, CameraDevice::iformatLock;
|
||||
AVInputFormat* CameraDevice::iformat{nullptr};
|
||||
|
@ -24,7 +28,7 @@ CameraDevice* CameraDevice::open(QString devName)
|
|||
if (dev)
|
||||
goto out;
|
||||
|
||||
if (avformat_open_input(&fctx, devName.toStdString().c_str(), nullptr, nullptr)<0)
|
||||
if (avformat_open_input(&fctx, devName.toStdString().c_str(), iformat, nullptr)<0)
|
||||
goto out;
|
||||
|
||||
if (avformat_find_stream_info(fctx, NULL) < 0)
|
||||
|
@ -63,21 +67,21 @@ bool CameraDevice::close()
|
|||
}
|
||||
}
|
||||
|
||||
AVDeviceInfoList* CameraDevice::getRawDeviceList()
|
||||
QVector<QPair<QString, QString>> CameraDevice::getRawDeviceListGeneric()
|
||||
{
|
||||
AVDeviceInfoList* devlist = nullptr;
|
||||
QVector<QPair<QString, QString>> devices;
|
||||
|
||||
if (!getDefaultInputFormat())
|
||||
return devlist;
|
||||
return devices;
|
||||
|
||||
// Alloc an input device context
|
||||
AVFormatContext *s;
|
||||
if (!(s = avformat_alloc_context()))
|
||||
return devlist;
|
||||
return devices;
|
||||
if (!iformat->priv_class || !AV_IS_INPUT_DEVICE(iformat->priv_class->category))
|
||||
{
|
||||
avformat_free_context(s);
|
||||
return devlist;
|
||||
return devices;
|
||||
}
|
||||
s->iformat = iformat;
|
||||
if (s->iformat->priv_data_size > 0)
|
||||
|
@ -86,7 +90,7 @@ AVDeviceInfoList* CameraDevice::getRawDeviceList()
|
|||
if (!s->priv_data)
|
||||
{
|
||||
avformat_free_context(s);
|
||||
return devlist;
|
||||
return devices;
|
||||
}
|
||||
if (s->iformat->priv_class)
|
||||
{
|
||||
|
@ -100,6 +104,7 @@ AVDeviceInfoList* CameraDevice::getRawDeviceList()
|
|||
}
|
||||
|
||||
// List the devices for this context
|
||||
AVDeviceInfoList* devlist = nullptr;
|
||||
AVDictionary *tmp = nullptr;
|
||||
av_dict_copy(&tmp, nullptr, 0);
|
||||
if (av_opt_set_dict2(s, &tmp, AV_OPT_SEARCH_CHILDREN) < 0)
|
||||
|
@ -108,16 +113,8 @@ AVDeviceInfoList* CameraDevice::getRawDeviceList()
|
|||
avformat_free_context(s);
|
||||
}
|
||||
avdevice_list_devices(s, &devlist);
|
||||
return devlist;
|
||||
}
|
||||
|
||||
QVector<QPair<QString, QString>> CameraDevice::getDeviceList()
|
||||
{
|
||||
QVector<QPair<QString, QString>> devices;
|
||||
|
||||
AVDeviceInfoList* devlist = getRawDeviceList();
|
||||
if (!devlist)
|
||||
return devices;
|
||||
qWarning() << "avdevice_list_devices failed";
|
||||
|
||||
// Convert the list to a QVector
|
||||
devices.resize(devlist->nb_devices);
|
||||
|
@ -128,47 +125,37 @@ QVector<QPair<QString, QString>> CameraDevice::getDeviceList()
|
|||
devices[i].second = dev->device_description;
|
||||
}
|
||||
avdevice_free_list_devices(&devlist);
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
QVector<QPair<QString, QString>> CameraDevice::getDeviceList()
|
||||
{
|
||||
if (!iformat)
|
||||
if (!getDefaultInputFormat())
|
||||
return {};
|
||||
|
||||
if (iformat->name == QString("dshow"))
|
||||
return DirectShow::getDeviceList();
|
||||
else
|
||||
return getRawDeviceListGeneric();
|
||||
}
|
||||
|
||||
QString CameraDevice::getDefaultDeviceName()
|
||||
{
|
||||
QString device = Settings::getInstance().getVideoDev();
|
||||
QString defaultdev = Settings::getInstance().getVideoDev();
|
||||
|
||||
if (!getDefaultInputFormat())
|
||||
return device;
|
||||
return defaultdev;
|
||||
|
||||
AVDeviceInfoList* devlist = getRawDeviceList();
|
||||
if (!devlist)
|
||||
return device;
|
||||
QVector<QPair<QString, QString>> devlist = getDeviceList();
|
||||
for (const QPair<QString,QString>& device : devlist)
|
||||
if (defaultdev == device.first)
|
||||
return defaultdev;
|
||||
|
||||
bool actuallyExists = false;
|
||||
for (int i=0; i<devlist->nb_devices; i++)
|
||||
{
|
||||
if (device == devlist->devices[i]->device_name)
|
||||
{
|
||||
actuallyExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (actuallyExists)
|
||||
{
|
||||
avdevice_free_list_devices(&devlist);
|
||||
return device;
|
||||
}
|
||||
if (devlist.isEmpty())
|
||||
return defaultdev;
|
||||
|
||||
if (!devlist->nb_devices)
|
||||
{
|
||||
device.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
int defaultDev = devlist->default_device == -1 ? 0 : devlist->default_device;
|
||||
device = QString(devlist->devices[defaultDev]->device_name);
|
||||
}
|
||||
avdevice_free_list_devices(&devlist);
|
||||
return device;
|
||||
return devlist[0].first;
|
||||
}
|
||||
|
||||
bool CameraDevice::getDefaultInputFormat()
|
||||
|
@ -179,19 +166,23 @@ bool CameraDevice::getDefaultInputFormat()
|
|||
|
||||
avdevice_register_all();
|
||||
|
||||
// Linux
|
||||
#ifdef Q_OS_LINUX
|
||||
if ((iformat = av_find_input_format("v4l2")))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
// Windows
|
||||
#ifdef Q_OS_WIN
|
||||
if ((iformat = av_find_input_format("dshow")))
|
||||
return true;
|
||||
if ((iformat = av_find_input_format("vfwcap")))
|
||||
#endif
|
||||
|
||||
// Mac
|
||||
#ifdef Q_OS_OSX
|
||||
if ((iformat = av_find_input_format("avfoundation")))
|
||||
return true;
|
||||
if ((iformat = av_find_input_format("qtkit")))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
qWarning() << "No valid input format found";
|
||||
return false;
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
private:
|
||||
CameraDevice(const QString devName, AVFormatContext *context);
|
||||
static bool getDefaultInputFormat(); ///< Sets CameraDevice::iformat, returns success/failure
|
||||
static AVDeviceInfoList* getRawDeviceList();
|
||||
static QVector<QPair<QString, QString> > getRawDeviceListGeneric(); ///< Uses avdevice_list_devices
|
||||
|
||||
public:
|
||||
const QString devName; ///< Short name of the device
|
||||
|
|
Loading…
Reference in New Issue
Block a user