OpenShot Library | libopenshot  0.7.0
CameraCaptureReader.cpp
Go to the documentation of this file.
1 
9 // Copyright (c) 2008-2026 OpenShot Studios, LLC
10 //
11 // SPDX-License-Identifier: LGPL-3.0-or-later
12 
13 #include "CameraCaptureReader.h"
14 
15 #include <cstdlib>
16 #include <string>
17 
18 #if defined(_WIN32)
19  #define WIN32_LEAN_AND_MEAN
20  #include <windows.h>
21  #include <dshow.h>
22  #include <oleauto.h>
23 #endif
24 
25 extern "C" {
26  #include <libavdevice/avdevice.h>
27 }
28 
29 #include "Exceptions.h"
30 
31 using namespace openshot;
32 
33 #if defined(_WIN32)
34 namespace
35 {
36  std::string WideToUtf8(const wchar_t* value)
37  {
38  if (!value || !*value) {
39  return "";
40  }
41  const int length = WideCharToMultiByte(CP_UTF8, 0, value, -1, nullptr, 0, nullptr, nullptr);
42  if (length <= 1) {
43  return "";
44  }
45  std::string converted(static_cast<size_t>(length - 1), '\0');
46  WideCharToMultiByte(CP_UTF8, 0, value, -1, &converted[0], length, nullptr, nullptr);
47  return converted;
48  }
49 
50  bool ContainsDeviceName(const AudioDeviceList& devices, const std::string& name)
51  {
52  for (const auto& device : devices) {
53  if (device.second == name) {
54  return true;
55  }
56  }
57  return false;
58  }
59 
60  AudioDeviceList GetWindowsDirectShowVideoDevices()
61  {
62  AudioDeviceList devices;
63  const HRESULT init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
64  const bool should_uninitialize = SUCCEEDED(init_result);
65  if (FAILED(init_result) && init_result != RPC_E_CHANGED_MODE) {
66  return devices;
67  }
68 
69  ICreateDevEnum* device_enumerator = nullptr;
70  HRESULT result = CoCreateInstance(
71  CLSID_SystemDeviceEnum,
72  nullptr,
73  CLSCTX_INPROC_SERVER,
74  IID_ICreateDevEnum,
75  reinterpret_cast<void**>(&device_enumerator)
76  );
77  if (SUCCEEDED(result) && device_enumerator) {
78  IEnumMoniker* moniker_enumerator = nullptr;
79  result = device_enumerator->CreateClassEnumerator(
80  CLSID_VideoInputDeviceCategory,
81  &moniker_enumerator,
82  0
83  );
84  if (result == S_OK && moniker_enumerator) {
85  IMoniker* moniker = nullptr;
86  while (moniker_enumerator->Next(1, &moniker, nullptr) == S_OK) {
87  IPropertyBag* property_bag = nullptr;
88  result = moniker->BindToStorage(
89  nullptr,
90  nullptr,
91  IID_IPropertyBag,
92  reinterpret_cast<void**>(&property_bag)
93  );
94  if (SUCCEEDED(result) && property_bag) {
95  VARIANT friendly_name;
96  VariantInit(&friendly_name);
97  result = property_bag->Read(L"FriendlyName", &friendly_name, nullptr);
98  if (SUCCEEDED(result) && friendly_name.vt == VT_BSTR) {
99  const std::string name = WideToUtf8(friendly_name.bstrVal);
100  if (!name.empty() && !ContainsDeviceName(devices, name)) {
101  devices.emplace_back(name, name);
102  }
103  }
104  VariantClear(&friendly_name);
105  property_bag->Release();
106  }
107  moniker->Release();
108  moniker = nullptr;
109  }
110  moniker_enumerator->Release();
111  }
112  device_enumerator->Release();
113  }
114  if (should_uninitialize) {
115  CoUninitialize();
116  }
117  return devices;
118  }
119 }
120 #endif
121 
123  : settings(new_settings)
124  , reader(nullptr)
125 {
126  if (settings.backend == CAMERA_CAPTURE_AUTO) {
127  settings.backend = DefaultBackend();
128  }
129  ValidateSettings();
130  RebuildReader();
131 }
132 
134 {
135  Close();
136 }
137 
139 {
140 #if defined(__linux__)
141  return backend == CAMERA_CAPTURE_V4L2 || backend == CAMERA_CAPTURE_AUTO;
142 #elif defined(_WIN32)
143  return backend == CAMERA_CAPTURE_WINDOWS_DSHOW || backend == CAMERA_CAPTURE_AUTO;
144 #elif defined(__APPLE__)
145  return backend == CAMERA_CAPTURE_MAC_AVFOUNDATION || backend == CAMERA_CAPTURE_AUTO;
146 #else
147  (void) backend;
148  return false;
149 #endif
150 }
151 
153 {
154 #if defined(__linux__)
155  return CAMERA_CAPTURE_V4L2;
156 #elif defined(_WIN32)
158 #elif defined(__APPLE__)
160 #else
161  return CAMERA_CAPTURE_AUTO;
162 #endif
163 }
164 
166 {
167  if (backend == CAMERA_CAPTURE_AUTO) {
168  backend = DefaultBackend();
169  }
170 
171  AudioDeviceList devices;
172  const char* input_format_name = nullptr;
173 #if defined(__linux__)
174  if (backend == CAMERA_CAPTURE_V4L2) {
175  input_format_name = "v4l2";
176  }
177 #elif defined(_WIN32)
178  if (backend == CAMERA_CAPTURE_WINDOWS_DSHOW) {
179  return GetWindowsDirectShowVideoDevices();
180  }
181 #elif defined(__APPLE__)
182  if (backend == CAMERA_CAPTURE_MAC_AVFOUNDATION) {
183  input_format_name = "avfoundation";
184  }
185 #endif
186  if (!input_format_name) {
187  return devices;
188  }
189 
190  avdevice_register_all();
191  AVInputFormat* input_format = const_cast<AVInputFormat*>(av_find_input_format(input_format_name));
192  if (!input_format) {
193  return devices;
194  }
195 
196  AVDeviceInfoList* device_list = nullptr;
197  const int result = avdevice_list_input_sources(input_format, nullptr, nullptr, &device_list);
198  if (result >= 0 && device_list) {
199  for (int index = 0; index < device_list->nb_devices; ++index) {
200  const AVDeviceInfo* device = device_list->devices[index];
201  if (!device || !device->device_name) {
202  continue;
203  }
204  const std::string name = device->device_name;
205  const std::string label = device->device_description
206  ? device->device_description
207  : device->device_name;
208  #if defined(__APPLE__)
209  if (backend == CAMERA_CAPTURE_MAC_AVFOUNDATION && label.find("Capture screen") != std::string::npos) {
210  continue;
211  }
212  #endif
213  devices.emplace_back(label, name);
214  }
215  }
216  avdevice_free_list_devices(&device_list);
217  return devices;
218 }
219 
220 void CameraCaptureReader::ValidateSettings() const
221 {
222  if (!IsBackendSupported(settings.backend)) {
223  throw InvalidOptions("Camera capture backend is not supported on this OS.");
224  }
225  if (settings.backend != CAMERA_CAPTURE_V4L2
227  && settings.backend != CAMERA_CAPTURE_MAC_AVFOUNDATION) {
228  throw InvalidOptions("Camera capture backend is not implemented in this build.");
229  }
230  if (settings.device.empty()) {
231  throw InvalidOptions("Camera capture requires a device path.");
232  }
233  if (settings.width <= 0 || settings.height <= 0) {
234  throw InvalidOptions("Camera capture requires a positive width and height.");
235  }
236  if (settings.fps.num <= 0 || settings.fps.den <= 0) {
237  throw InvalidOptions("Camera capture requires a positive frame rate.");
238  }
239 }
240 
241 ScreenCaptureSettings CameraCaptureReader::ToDeviceSettings() const
242 {
243  ScreenCaptureSettings converted;
244  converted.display = settings.device;
245  converted.width = settings.width;
246  converted.height = settings.height;
247  converted.fps = settings.fps;
248  converted.options = settings.options;
249  if (settings.backend == CAMERA_CAPTURE_WINDOWS_DSHOW) {
251  converted.display = "video=" + settings.device;
252  converted.options["input_format_name"] = "dshow";
253  } else if (settings.backend == CAMERA_CAPTURE_MAC_AVFOUNDATION) {
255  converted.display = settings.device.find(':') == std::string::npos
256  ? settings.device + ":none"
257  : settings.device;
258  converted.options["input_format_name"] = "avfoundation";
259  } else {
260  converted.backend = SCREEN_CAPTURE_X11;
261  converted.options["input_format_name"] = "v4l2";
262  }
263  return converted;
264 }
265 
266 void CameraCaptureReader::RebuildReader()
267 {
268  reader = std::make_unique<ScreenCaptureReader>(ToDeviceSettings());
269  info = reader->info;
270  info.metadata["capture_type"] = "camera";
271 }
272 
274 {
275  reader->Open();
276  info = reader->info;
277  info.metadata["capture_type"] = "camera";
278 }
279 
281 {
282  if (reader) {
283  reader->Close();
284  }
285 }
286 
288 {
289  return reader ? reader->GetStats() : CaptureReaderStats();
290 }
291 
292 std::shared_ptr<Frame> CameraCaptureReader::GetFrame(int64_t number)
293 {
294  return reader->GetFrame(number);
295 }
296 
297 std::string CameraCaptureReader::Json() const
298 {
299  return JsonValue().toStyledString();
300 }
301 
303 {
304  Json::Value root = ReaderBase::JsonValue();
305  root["type"] = "CameraCaptureReader";
306  root["backend"] = settings.backend;
307  root["device"] = settings.device;
308  root["width"] = settings.width;
309  root["height"] = settings.height;
310  root["fps"]["num"] = settings.fps.num;
311  root["fps"]["den"] = settings.fps.den;
312  root["options"] = Json::Value(Json::objectValue);
313  for (const auto& option : settings.options) {
314  root["options"][option.first] = option.second;
315  }
316  return root;
317 }
318 
319 void CameraCaptureReader::SetJson(const std::string value)
320 {
321  try {
323  } catch (const std::exception&) {
324  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
325  }
326 }
327 
328 void CameraCaptureReader::SetJsonValue(const Json::Value root)
329 {
330  if (!root["backend"].isNull())
331  settings.backend = static_cast<CameraCaptureBackend>(root["backend"].asInt());
332  if (!root["device"].isNull())
333  settings.device = root["device"].asString();
334  if (!root["width"].isNull())
335  settings.width = root["width"].asInt();
336  if (!root["height"].isNull())
337  settings.height = root["height"].asInt();
338  if (!root["fps"].isNull() && root["fps"].isObject()) {
339  if (!root["fps"]["num"].isNull())
340  settings.fps.num = root["fps"]["num"].asInt();
341  if (!root["fps"]["den"].isNull())
342  settings.fps.den = root["fps"]["den"].asInt();
343  }
344  if (!root["options"].isNull() && root["options"].isObject()) {
345  settings.options.clear();
346  for (const auto& key : root["options"].getMemberNames()) {
347  settings.options[key] = root["options"][key].asString();
348  }
349  }
350  if (settings.backend == CAMERA_CAPTURE_AUTO) {
351  settings.backend = DefaultBackend();
352  }
353  ValidateSettings();
354  RebuildReader();
355 }
openshot::CAMERA_CAPTURE_AUTO
@ CAMERA_CAPTURE_AUTO
Definition: CameraCaptureReader.h:25
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
openshot::CameraCaptureSettings::options
std::map< std::string, std::string > options
Definition: CameraCaptureReader.h:38
openshot::CameraCaptureReader::Open
void Open() override
Open the reader (and start consuming resources, such as images or video files)
Definition: CameraCaptureReader.cpp:273
openshot::ReaderBase::JsonValue
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
Definition: ReaderBase.cpp:110
openshot::AudioDeviceList
std::vector< std::pair< std::string, std::string > > AudioDeviceList
Definition: AudioDevices.h:42
openshot::ScreenCaptureSettings::display
std::string display
Definition: ScreenCaptureReader.h:39
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
openshot::CameraCaptureReader::GetFrame
std::shared_ptr< openshot::Frame > GetFrame(int64_t number) override
Definition: CameraCaptureReader.cpp:292
openshot::CameraCaptureReader::CameraCaptureReader
CameraCaptureReader(const CameraCaptureSettings &settings)
Definition: CameraCaptureReader.cpp:122
openshot::CaptureReaderStats
Definition: ScreenCaptureReader.h:54
openshot::ReaderBase::info
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:91
openshot::ScreenCaptureSettings
Definition: ScreenCaptureReader.h:36
openshot::ScreenCaptureSettings::fps
openshot::Fraction fps
Definition: ScreenCaptureReader.h:44
openshot::SCREEN_CAPTURE_MAC_AVFOUNDATION
@ SCREEN_CAPTURE_MAC_AVFOUNDATION
Definition: ScreenCaptureReader.h:33
openshot::CameraCaptureSettings::device
std::string device
Definition: CameraCaptureReader.h:34
CameraCaptureReader.h
Header file for live camera capture readers.
openshot::CameraCaptureReader::GetDeviceNames
static AudioDeviceList GetDeviceNames(CameraCaptureBackend backend=CAMERA_CAPTURE_AUTO)
Definition: CameraCaptureReader.cpp:165
AudioDeviceList
std::vector< std::pair< std::string, std::string > > AudioDeviceList
Definition: AudioDevices.cpp:18
openshot::CameraCaptureSettings::fps
openshot::Fraction fps
Definition: CameraCaptureReader.h:37
openshot::Fraction::num
int num
Numerator for the fraction.
Definition: Fraction.h:32
openshot::CAMERA_CAPTURE_WINDOWS_DSHOW
@ CAMERA_CAPTURE_WINDOWS_DSHOW
Definition: CameraCaptureReader.h:27
openshot::CameraCaptureReader::SetJson
void SetJson(const std::string value) override
Load JSON string into this object.
Definition: CameraCaptureReader.cpp:319
openshot::Fraction::den
int den
Denominator for the fraction.
Definition: Fraction.h:33
openshot::CameraCaptureSettings::backend
CameraCaptureBackend backend
Definition: CameraCaptureReader.h:33
openshot::ScreenCaptureSettings::backend
ScreenCaptureBackend backend
Definition: ScreenCaptureReader.h:38
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:223
openshot::CameraCaptureReader::DefaultBackend
static CameraCaptureBackend DefaultBackend()
Definition: CameraCaptureReader.cpp:152
openshot::CameraCaptureSettings::height
int height
Definition: CameraCaptureReader.h:36
openshot::CameraCaptureReader::SetJsonValue
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
Definition: CameraCaptureReader.cpp:328
openshot::CameraCaptureReader::GetStats
openshot::CaptureReaderStats GetStats() const
Definition: CameraCaptureReader.cpp:287
openshot::CAMERA_CAPTURE_MAC_AVFOUNDATION
@ CAMERA_CAPTURE_MAC_AVFOUNDATION
Definition: CameraCaptureReader.h:28
openshot::ScreenCaptureSettings::height
int height
Definition: ScreenCaptureReader.h:43
openshot::ReaderInfo::metadata
std::map< std::string, std::string > metadata
An optional map/dictionary of metadata for this reader.
Definition: ReaderBase.h:65
openshot::SCREEN_CAPTURE_X11
@ SCREEN_CAPTURE_X11
Definition: ScreenCaptureReader.h:30
openshot::CameraCaptureSettings
Definition: CameraCaptureReader.h:31
openshot::CameraCaptureReader::IsBackendSupported
static bool IsBackendSupported(CameraCaptureBackend backend)
Definition: CameraCaptureReader.cpp:138
openshot::CameraCaptureReader::~CameraCaptureReader
~CameraCaptureReader() override
Definition: CameraCaptureReader.cpp:133
openshot::ScreenCaptureSettings::options
std::map< std::string, std::string > options
Definition: ScreenCaptureReader.h:51
openshot::CameraCaptureBackend
CameraCaptureBackend
Definition: CameraCaptureReader.h:23
openshot::ScreenCaptureSettings::width
int width
Definition: ScreenCaptureReader.h:42
openshot::CameraCaptureReader::JsonValue
Json::Value JsonValue() const override
Generate Json::Value for this object.
Definition: CameraCaptureReader.cpp:302
openshot::CAMERA_CAPTURE_V4L2
@ CAMERA_CAPTURE_V4L2
Definition: CameraCaptureReader.h:26
openshot::CameraCaptureSettings::width
int width
Definition: CameraCaptureReader.h:35
openshot::InvalidOptions
Exception when invalid encoding options are used.
Definition: Exceptions.h:238
openshot::SCREEN_CAPTURE_WINDOWS_GDI
@ SCREEN_CAPTURE_WINDOWS_GDI
Definition: ScreenCaptureReader.h:32
openshot::CameraCaptureReader::Json
std::string Json() const override
Generate JSON string of this object.
Definition: CameraCaptureReader.cpp:297
Exceptions.h
Header file for all Exception classes.
openshot::CameraCaptureReader::Close
void Close() override
Close the reader (and any resources it was consuming)
Definition: CameraCaptureReader.cpp:280