26 #define WIN32_LEAN_AND_MEAN
33 #include <libavdevice/avdevice.h>
43 std::string WideToUtf8(
const wchar_t* value)
45 if (!value || !*value) {
48 const int length = WideCharToMultiByte(CP_UTF8, 0, value, -1,
nullptr, 0,
nullptr,
nullptr);
52 std::string converted(
static_cast<size_t>(length - 1),
'\0');
53 WideCharToMultiByte(CP_UTF8, 0, value, -1, &converted[0], length,
nullptr,
nullptr);
57 bool ContainsDeviceName(
const AudioDeviceList& devices,
const std::string& name)
59 for (
const auto& device : devices) {
60 if (device.second == name) {
70 const HRESULT init_result = CoInitializeEx(
nullptr, COINIT_MULTITHREADED);
71 const bool should_uninitialize = SUCCEEDED(init_result);
72 if (FAILED(init_result) && init_result != RPC_E_CHANGED_MODE) {
76 ICreateDevEnum* device_enumerator =
nullptr;
77 HRESULT result = CoCreateInstance(
78 CLSID_SystemDeviceEnum,
82 reinterpret_cast<void**
>(&device_enumerator)
84 if (SUCCEEDED(result) && device_enumerator) {
85 IEnumMoniker* moniker_enumerator =
nullptr;
86 result = device_enumerator->CreateClassEnumerator(
87 CLSID_VideoInputDeviceCategory,
91 if (result == S_OK && moniker_enumerator) {
92 IMoniker* moniker =
nullptr;
93 while (moniker_enumerator->Next(1, &moniker,
nullptr) == S_OK) {
94 IPropertyBag* property_bag =
nullptr;
95 result = moniker->BindToStorage(
99 reinterpret_cast<void**
>(&property_bag)
101 if (SUCCEEDED(result) && property_bag) {
102 VARIANT friendly_name;
103 VariantInit(&friendly_name);
104 result = property_bag->Read(L
"FriendlyName", &friendly_name,
nullptr);
105 if (SUCCEEDED(result) && friendly_name.vt == VT_BSTR) {
106 const std::string name = WideToUtf8(friendly_name.bstrVal);
107 if (!name.empty() && !ContainsDeviceName(devices, name)) {
108 devices.emplace_back(name, name);
111 VariantClear(&friendly_name);
112 property_bag->Release();
117 moniker_enumerator->Release();
119 device_enumerator->Release();
121 if (should_uninitialize) {
130 : settings(new_settings)
147#if defined(__linux__)
151#elif defined(__APPLE__)
161#if defined(__linux__)
165#elif defined(__APPLE__)
179 const char* input_format_name =
nullptr;
180#if defined(__linux__)
182 input_format_name =
"v4l2";
186 return GetWindowsDirectShowVideoDevices();
188#elif defined(__APPLE__)
190 input_format_name =
"avfoundation";
193 if (!input_format_name) {
197 avdevice_register_all();
198 AVInputFormat* input_format =
const_cast<AVInputFormat*
>(av_find_input_format(input_format_name));
203 AVDeviceInfoList* device_list =
nullptr;
204 const int result = avdevice_list_input_sources(input_format,
nullptr,
nullptr, &device_list);
205 if (result >= 0 && device_list) {
206 for (
int index = 0; index < device_list->nb_devices; ++index) {
207 const AVDeviceInfo* device = device_list->devices[index];
208 if (!device || !device->device_name) {
211 const std::string name = device->device_name;
212 const std::string label = device->device_description
213 ? device->device_description
214 : device->device_name;
215 #if defined(__APPLE__)
220 devices.emplace_back(label, name);
223 avdevice_free_list_devices(&device_list);
231#if defined(__linux__)
233 const int fd = open(device.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC);
234 if (fd < 0)
throw InvalidFile(
"Unable to open camera for mode discovery.", device);
235 struct DeviceHandle {
237 ~DeviceHandle() { close(fd); }
239 return detail::EnumerateCameraModes([fd](
unsigned long request,
void* value) {
240 return ioctl(fd, request, value);
249void CameraCaptureReader::ValidateSettings()
const
252 throw InvalidOptions(
"Camera capture backend is not supported on this OS.");
257 throw InvalidOptions(
"Camera capture backend is not implemented in this build.");
259 if (settings.
device.empty()) {
263 throw InvalidOptions(
"Camera capture requires a positive width and height.");
266 throw InvalidOptions(
"Camera capture requires a positive frame rate.");
276 converted.
fps = settings.
fps;
281 converted.
options[
"input_format_name"] =
"dshow";
284 converted.
display = settings.
device.find(
':') == std::string::npos
285 ? settings.
device +
":none"
287 converted.
options[
"input_format_name"] =
"avfoundation";
290 converted.
options[
"input_format_name"] =
"v4l2";
295void CameraCaptureReader::RebuildReader()
297 reader = std::make_unique<ScreenCaptureReader>(ToDeviceSettings());
319 reader->RequestClose();
330 return reader->GetFrame(number);
341 root[
"type"] =
"CameraCaptureReader";
342 root[
"backend"] = settings.
backend;
343 root[
"device"] = settings.
device;
344 root[
"width"] = settings.
width;
345 root[
"height"] = settings.
height;
346 root[
"fps"][
"num"] = settings.
fps.
num;
347 root[
"fps"][
"den"] = settings.
fps.
den;
348 root[
"options"] = Json::Value(Json::objectValue);
349 for (
const auto& option : settings.
options) {
350 root[
"options"][option.first] = option.second;
359 }
catch (
const std::exception&) {
360 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
366 if (!root[
"backend"].isNull())
368 if (!root[
"device"].isNull())
369 settings.
device = root[
"device"].asString();
370 if (!root[
"width"].isNull())
371 settings.
width = root[
"width"].asInt();
372 if (!root[
"height"].isNull())
373 settings.
height = root[
"height"].asInt();
374 if (!root[
"fps"].isNull() && root[
"fps"].isObject()) {
375 if (!root[
"fps"][
"num"].isNull())
376 settings.
fps.
num = root[
"fps"][
"num"].asInt();
377 if (!root[
"fps"][
"den"].isNull())
378 settings.
fps.
den = root[
"fps"][
"den"].asInt();
380 if (!root[
"options"].isNull() && root[
"options"].isObject()) {
382 for (
const auto& key : root[
"options"].getMemberNames()) {
383 settings.
options[key] = root[
"options"][key].asString();
std::vector< std::pair< std::string, std::string > > AudioDeviceList
Header file for live camera capture readers.
Header file for all Exception classes.
void Open() override
Open the reader (and start consuming resources, such as images or video files)
Json::Value JsonValue() const override
Generate Json::Value for this object.
void Close() override
Close the reader (and any resources it was consuming)
static bool IsBackendSupported(CameraCaptureBackend backend)
static AudioDeviceList GetDeviceNames(CameraCaptureBackend backend=CAMERA_CAPTURE_AUTO)
void RequestClose()
Request cancellation without releasing camera resources on the caller's thread.
CameraCaptureReader(const CameraCaptureSettings &settings)
std::string Json() const override
Generate JSON string of this object.
openshot::CaptureReaderStats GetStats() const
std::shared_ptr< openshot::Frame > GetFrame(int64_t number) override
static std::vector< CameraCaptureMode > GetDeviceModes(const std::string &device, CameraCaptureBackend backend=CAMERA_CAPTURE_AUTO)
~CameraCaptureReader() override
static CameraCaptureBackend DefaultBackend()
void SetJson(const std::string value) override
Load JSON string into this object.
void SetJsonValue(const Json::Value root) override
Load Json::Value into this object.
int num
Numerator for the fraction.
int den
Denominator for the fraction.
Exception for files that can not be found or opened.
Exception for invalid JSON.
Exception when invalid encoding options are used.
openshot::ReaderInfo info
Information about the current media file.
virtual Json::Value JsonValue() const =0
Generate Json::Value for this object.
This namespace is the default namespace for all code in the openshot library.
@ SCREEN_CAPTURE_MAC_AVFOUNDATION
@ SCREEN_CAPTURE_WINDOWS_GDI
@ CAMERA_CAPTURE_WINDOWS_DSHOW
@ CAMERA_CAPTURE_MAC_AVFOUNDATION
std::vector< std::pair< std::string, std::string > > AudioDeviceList
const Json::Value stringToJson(const std::string value)
std::map< std::string, std::string > options
CameraCaptureBackend backend
std::map< std::string, std::string > metadata
An optional map/dictionary of metadata for this reader.
std::map< std::string, std::string > options
ScreenCaptureBackend backend