qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
directshow.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2010 Ramiro Polla
3  Copyright © 2015-2019 by The qTox Project Contributors
4 
5  This file is part of qTox, a Qt-based graphical interface for Tox.
6 
7  qTox is libre software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  qTox is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with qTox. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 
22 #include "directshow.h"
23 
24 // Because of replacing to incorrect order, which leads to building failing,
25 // this region is ignored for clang-format
26 // clang-format off
27 #include <cstdint>
28 #include <objbase.h>
29 #include <strmif.h>
30 #include <amvideo.h>
31 #include <dvdmedia.h>
32 #include <uuids.h>
33 #include <cassert>
34 #include <QDebug>
35 // clang-format on
36 
43 static char* wcharToUtf8(wchar_t* w)
44 {
45  int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, nullptr, 0, nullptr, nullptr);
46  char* s = new char[l];
47  if (s)
48  WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, nullptr, nullptr);
49  return s;
50 }
51 
52 QVector<QPair<QString, QString>> DirectShow::getDeviceList()
53 {
54  IMoniker* m = nullptr;
55  QVector<QPair<QString, QString>> devices;
56 
57  ICreateDevEnum* devenum = nullptr;
58  if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
59  reinterpret_cast<void**>(&devenum))
60  != S_OK)
61  return devices;
62 
63  IEnumMoniker* classenum = nullptr;
64  if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, reinterpret_cast<IEnumMoniker**>(&classenum), 0)
65  != S_OK)
66  return devices;
67 
68  while (classenum->Next(1, &m, nullptr) == S_OK) {
69  VARIANT var;
70  IPropertyBag* bag = nullptr;
71  LPMALLOC coMalloc = nullptr;
72  IBindCtx* bindCtx = nullptr;
73  LPOLESTR olestr = nullptr;
74  char *devIdString = nullptr, *devHumanName = nullptr;
75 
76  if (CoGetMalloc(1, &coMalloc) != S_OK)
77  goto fail;
78  if (CreateBindCtx(0, &bindCtx) != S_OK)
79  goto fail;
80 
81  // Get an uuid for the device that we can pass to ffmpeg directly
82  if (m->GetDisplayName(bindCtx, nullptr, &olestr) != S_OK)
83  goto fail;
84  devIdString = wcharToUtf8(olestr);
85 
86  // replace ':' with '_' since FFmpeg uses : to delimitate sources
87  for (size_t i = 0; i < strlen(devIdString); ++i)
88  if (devIdString[i] == ':')
89  devIdString[i] = '_';
90 
91  // Get a human friendly name/description
92  if (m->BindToStorage(nullptr, nullptr, IID_IPropertyBag, reinterpret_cast<void**>(&bag)) != S_OK)
93  goto fail;
94 
95  var.vt = VT_BSTR;
96  if (bag->Read(L"FriendlyName", &var, nullptr) != S_OK)
97  goto fail;
98  devHumanName = wcharToUtf8(var.bstrVal);
99 
100  devices += {QString("video=") + devIdString, devHumanName};
101 
102  fail:
103  if (olestr && coMalloc)
104  coMalloc->Free(olestr);
105  if (bindCtx)
106  bindCtx->Release();
107  delete[] devIdString;
108  delete[] devHumanName;
109  if (bag)
110  bag->Release();
111  m->Release();
112  }
113  classenum->Release();
114 
115  return devices;
116 }
117 
118 // Used (by getDeviceModes) to select a device
119 // so we can list its properties
120 static IBaseFilter* getDevFilter(QString devName)
121 {
122  IBaseFilter* devFilter = nullptr;
123  devName = devName.mid(6); // Remove the "video="
124  IMoniker* m = nullptr;
125 
126  ICreateDevEnum* devenum = nullptr;
127  if (CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
128  reinterpret_cast<void**>(&devenum))
129  != S_OK)
130  return devFilter;
131 
132  IEnumMoniker* classenum = nullptr;
133  if (devenum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, reinterpret_cast<IEnumMoniker**>(&classenum), 0)
134  != S_OK)
135  return devFilter;
136 
137  while (classenum->Next(1, &m, nullptr) == S_OK) {
138  LPMALLOC coMalloc = nullptr;
139  IBindCtx* bindCtx = nullptr;
140  LPOLESTR olestr = nullptr;
141  char* devIdString;
142 
143  if (CoGetMalloc(1, &coMalloc) != S_OK)
144  goto fail;
145  if (CreateBindCtx(0, &bindCtx) != S_OK)
146  goto fail;
147 
148  if (m->GetDisplayName(bindCtx, nullptr, &olestr) != S_OK)
149  goto fail;
150  devIdString = wcharToUtf8(olestr);
151 
152  // replace ':' with '_' since FFmpeg uses : to delimitate sources
153  for (size_t i = 0; i < strlen(devIdString); ++i)
154  if (devIdString[i] == ':')
155  devIdString[i] = '_';
156 
157  if (devName != devIdString)
158  goto fail;
159 
160  if (m->BindToObject(nullptr, nullptr, IID_IBaseFilter, reinterpret_cast<void**>(&devFilter)) != S_OK)
161  goto fail;
162 
163  fail:
164  if (olestr && coMalloc)
165  coMalloc->Free(olestr);
166  if (bindCtx)
167  bindCtx->Release();
168  delete[] devIdString;
169  m->Release();
170  }
171  classenum->Release();
172 
173  if (!devFilter)
174  qWarning() << "Couldn't find the device " << devName;
175 
176  return devFilter;
177 }
178 
179 QVector<VideoMode> DirectShow::getDeviceModes(QString devName)
180 {
181  QVector<VideoMode> modes;
182 
183  IBaseFilter* devFilter = getDevFilter(devName);
184  if (!devFilter)
185  return modes;
186 
187  // The outter loop tries to find a valid output pin
188  GUID category;
189  DWORD r2;
190  IEnumPins* pins = nullptr;
191  IPin* pin;
192  if (devFilter->EnumPins(&pins) != S_OK)
193  return modes;
194 
195  while (pins->Next(1, &pin, nullptr) == S_OK) {
196  IKsPropertySet* p = nullptr;
197  PIN_INFO info;
198 
199  pin->QueryPinInfo(&info);
200  info.pFilter->Release();
201  if (info.dir != PINDIR_OUTPUT)
202  goto next;
203  if (pin->QueryInterface(IID_IKsPropertySet, reinterpret_cast<void**>(&p)) != S_OK)
204  goto next;
205  if (p->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, nullptr, 0, &category, sizeof(GUID), &r2)
206  != S_OK)
207  goto next;
208  if (!IsEqualGUID(category, PIN_CATEGORY_CAPTURE))
209  goto next;
210 
211  // Now we can list the video modes for the current pin
212  // Prepare for another wall of spaghetti DIRECT SHOW QUALITY code
213  {
214  IAMStreamConfig* config = nullptr;
215  VIDEO_STREAM_CONFIG_CAPS* vcaps = nullptr;
216  int size, n;
217  if (pin->QueryInterface(IID_IAMStreamConfig, reinterpret_cast<void**>(&config)) != S_OK)
218  goto next;
219  if (config->GetNumberOfCapabilities(&n, &size) != S_OK)
220  goto pinend;
221 
222  assert(size == sizeof(VIDEO_STREAM_CONFIG_CAPS));
223  vcaps = new VIDEO_STREAM_CONFIG_CAPS;
224 
225  for (int i = 0; i < n; ++i) {
226  AM_MEDIA_TYPE* type = nullptr;
227  VideoMode mode;
228  if (config->GetStreamCaps(i, &type, reinterpret_cast<BYTE*>(vcaps)) != S_OK)
229  goto nextformat;
230 
231  if (!IsEqualGUID(type->formattype, FORMAT_VideoInfo)
232  && !IsEqualGUID(type->formattype, FORMAT_VideoInfo2))
233  goto nextformat;
234 
235  mode.width = vcaps->MaxOutputSize.cx;
236  mode.height = vcaps->MaxOutputSize.cy;
237  mode.FPS = 1e7 / vcaps->MinFrameInterval;
238  if (!modes.contains(mode))
239  modes.append(std::move(mode));
240 
241  nextformat:
242  if (type->pbFormat)
243  CoTaskMemFree(type->pbFormat);
244  CoTaskMemFree(type);
245  }
246  pinend:
247  config->Release();
248  delete vcaps;
249  }
250  next:
251  if (p)
252  p->Release();
253  pin->Release();
254  }
255 
256  return modes;
257 }
VideoMode::height
int height
Displayed video resolution (NOT frame resolution).
Definition: videomode.h:27
DirectShow::getDeviceModes
QVector< VideoMode > getDeviceModes(QString devName)
Definition: directshow.cpp:179
DirectShow::getDeviceList
QVector< QPair< QString, QString > > getDeviceList()
Definition: directshow.cpp:52
directshow.h
VideoMode::width
int width
Definition: videomode.h:27
VideoMode
Describes a video mode supported by a device.
Definition: videomode.h:25
VideoMode::FPS
float FPS
Frames per second supported by the device at this resolution.
Definition: videomode.h:29