1
0
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:
Tux3 / Mlkj / !Lev.uXFMLA 2015-05-15 23:01:38 +02:00 committed by tux3
parent e5684a4bbf
commit f0f069114c
5 changed files with 159 additions and 53 deletions

View File

@ -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 \

View 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;
}

View 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

View File

@ -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;

View File

@ -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