2014-05-30 03:29:48 -07:00
|
|
|
#include <objbase.h>
|
|
|
|
|
|
|
|
#include <obs-module.h>
|
|
|
|
#include <obs.hpp>
|
|
|
|
#include <util/dstr.hpp>
|
|
|
|
#include <util/platform.h>
|
2014-09-12 01:31:13 -07:00
|
|
|
#include <util/windows/WinHandle.hpp>
|
2015-01-02 05:36:09 -08:00
|
|
|
#include <util/threading.h>
|
2014-05-30 03:29:48 -07:00
|
|
|
#include "libdshowcapture/dshowcapture.hpp"
|
2014-09-12 19:51:45 -07:00
|
|
|
#include "ffmpeg-decode.h"
|
2014-12-18 15:33:35 -08:00
|
|
|
#include "encode-dstr.hpp"
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:22:03 -07:00
|
|
|
#include <algorithm>
|
2014-07-02 08:42:20 -07:00
|
|
|
#include <limits>
|
2014-07-02 09:49:07 -07:00
|
|
|
#include <set>
|
2014-05-30 03:29:48 -07:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO:
|
|
|
|
* - handle disconnections and reconnections
|
|
|
|
* - if device not present, wait for device to be plugged in
|
|
|
|
*/
|
|
|
|
|
2014-07-02 08:39:32 -07:00
|
|
|
#undef min
|
|
|
|
#undef max
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
using namespace std;
|
|
|
|
using namespace DShow;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
/* clang-format off */
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
/* settings defines that will cause errors if there are typos */
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
#define VIDEO_DEVICE_ID "video_device_id"
|
2014-05-30 03:29:48 -07:00
|
|
|
#define RES_TYPE "res_type"
|
|
|
|
#define RESOLUTION "resolution"
|
|
|
|
#define FRAME_INTERVAL "frame_interval"
|
|
|
|
#define VIDEO_FORMAT "video_format"
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
#define LAST_VIDEO_DEV_ID "last_video_device_id"
|
|
|
|
#define LAST_RESOLUTION "last_resolution"
|
2014-12-31 21:59:46 -08:00
|
|
|
#define BUFFERING_VAL "buffering"
|
2015-04-13 15:01:20 -07:00
|
|
|
#define FLIP_IMAGE "flip_vertically"
|
2015-06-05 10:49:50 -07:00
|
|
|
#define AUDIO_OUTPUT_MODE "audio_output_mode"
|
2014-08-28 18:27:58 -07:00
|
|
|
#define USE_CUSTOM_AUDIO "use_custom_audio_device"
|
|
|
|
#define AUDIO_DEVICE_ID "audio_device_id"
|
2015-02-11 12:51:30 -08:00
|
|
|
#define COLOR_SPACE "color_space"
|
|
|
|
#define COLOR_RANGE "color_range"
|
2016-04-03 15:55:15 -07:00
|
|
|
#define DEACTIVATE_WNS "deactivate_when_not_showing"
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
#define TEXT_INPUT_NAME obs_module_text("VideoCaptureDevice")
|
|
|
|
#define TEXT_DEVICE obs_module_text("Device")
|
|
|
|
#define TEXT_CONFIG_VIDEO obs_module_text("ConfigureVideo")
|
|
|
|
#define TEXT_CONFIG_XBAR obs_module_text("ConfigureCrossbar")
|
|
|
|
#define TEXT_RES_FPS_TYPE obs_module_text("ResFPSType")
|
|
|
|
#define TEXT_CUSTOM_RES obs_module_text("ResFPSType.Custom")
|
|
|
|
#define TEXT_PREFERRED_RES obs_module_text("ResFPSType.DevPreferred")
|
|
|
|
#define TEXT_FPS_MATCHING obs_module_text("FPS.Matching")
|
|
|
|
#define TEXT_FPS_HIGHEST obs_module_text("FPS.Highest")
|
|
|
|
#define TEXT_RESOLUTION obs_module_text("Resolution")
|
|
|
|
#define TEXT_VIDEO_FORMAT obs_module_text("VideoFormat")
|
|
|
|
#define TEXT_FORMAT_UNKNOWN obs_module_text("VideoFormat.Unknown")
|
2014-12-31 21:59:46 -08:00
|
|
|
#define TEXT_BUFFERING obs_module_text("Buffering")
|
|
|
|
#define TEXT_BUFFERING_AUTO obs_module_text("Buffering.AutoDetect")
|
|
|
|
#define TEXT_BUFFERING_ON obs_module_text("Buffering.Enable")
|
|
|
|
#define TEXT_BUFFERING_OFF obs_module_text("Buffering.Disable")
|
2015-04-13 15:01:20 -07:00
|
|
|
#define TEXT_FLIP_IMAGE obs_module_text("FlipVertically")
|
2015-06-05 10:49:50 -07:00
|
|
|
#define TEXT_AUDIO_MODE obs_module_text("AudioOutputMode")
|
|
|
|
#define TEXT_MODE_CAPTURE obs_module_text("AudioOutputMode.Capture")
|
|
|
|
#define TEXT_MODE_DSOUND obs_module_text("AudioOutputMode.DirectSound")
|
|
|
|
#define TEXT_MODE_WAVEOUT obs_module_text("AudioOutputMode.WaveOut")
|
2014-08-28 18:27:58 -07:00
|
|
|
#define TEXT_CUSTOM_AUDIO obs_module_text("UseCustomAudioDevice")
|
|
|
|
#define TEXT_AUDIO_DEVICE obs_module_text("AudioDevice")
|
2015-01-03 20:50:57 -08:00
|
|
|
#define TEXT_ACTIVATE obs_module_text("Activate")
|
|
|
|
#define TEXT_DEACTIVATE obs_module_text("Deactivate")
|
2015-02-11 12:51:30 -08:00
|
|
|
#define TEXT_COLOR_SPACE obs_module_text("ColorSpace")
|
|
|
|
#define TEXT_COLOR_DEFAULT obs_module_text("ColorSpace.Default")
|
|
|
|
#define TEXT_COLOR_RANGE obs_module_text("ColorRange")
|
2019-07-10 23:40:53 -07:00
|
|
|
#define TEXT_RANGE_DEFAULT obs_module_text("ColorRange.Default")
|
2015-02-11 12:51:30 -08:00
|
|
|
#define TEXT_RANGE_PARTIAL obs_module_text("ColorRange.Partial")
|
|
|
|
#define TEXT_RANGE_FULL obs_module_text("ColorRange.Full")
|
2016-04-03 15:55:15 -07:00
|
|
|
#define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing")
|
2014-07-09 22:12:57 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
/* clang-format on */
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
enum ResType {
|
|
|
|
ResType_Preferred,
|
2019-06-22 22:13:45 -07:00
|
|
|
ResType_Custom,
|
2014-05-30 03:29:48 -07:00
|
|
|
};
|
|
|
|
|
2014-12-31 21:59:46 -08:00
|
|
|
enum class BufferingType : int64_t {
|
|
|
|
Auto,
|
|
|
|
On,
|
2019-06-22 22:13:45 -07:00
|
|
|
Off,
|
2014-12-31 21:59:46 -08:00
|
|
|
};
|
|
|
|
|
2014-09-12 19:51:45 -07:00
|
|
|
void ffmpeg_log(void *bla, int level, const char *msg, va_list args)
|
|
|
|
{
|
|
|
|
DStr str;
|
2019-11-24 23:59:27 -08:00
|
|
|
if (level == AV_LOG_WARNING) {
|
2014-09-12 19:51:45 -07:00
|
|
|
dstr_copy(str, "warning: ");
|
2019-11-24 23:59:27 -08:00
|
|
|
} else if (level == AV_LOG_ERROR) {
|
|
|
|
/* only print first of this message to avoid spam */
|
|
|
|
static bool suppress_app_field_spam = false;
|
|
|
|
if (strcmp(msg, "unable to decode APP fields: %s\n") == 0) {
|
|
|
|
if (suppress_app_field_spam)
|
|
|
|
return;
|
|
|
|
|
|
|
|
suppress_app_field_spam = true;
|
|
|
|
}
|
|
|
|
|
2014-09-12 19:51:45 -07:00
|
|
|
dstr_copy(str, "error: ");
|
2019-11-24 23:59:27 -08:00
|
|
|
} else if (level < AV_LOG_ERROR) {
|
2014-09-12 19:51:45 -07:00
|
|
|
dstr_copy(str, "fatal: ");
|
2019-11-24 23:59:27 -08:00
|
|
|
} else {
|
2014-09-12 19:51:45 -07:00
|
|
|
return;
|
2019-11-24 23:59:27 -08:00
|
|
|
}
|
2014-09-12 19:51:45 -07:00
|
|
|
|
|
|
|
dstr_cat(str, msg);
|
|
|
|
if (dstr_end(str) == '\n')
|
|
|
|
dstr_resize(str, str->len - 1);
|
|
|
|
|
|
|
|
blogva(LOG_WARNING, str, args);
|
|
|
|
av_log_default_callback(bla, level, msg, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
class Decoder {
|
|
|
|
struct ffmpeg_decode decode;
|
|
|
|
|
|
|
|
public:
|
2019-06-22 22:13:45 -07:00
|
|
|
inline Decoder() { memset(&decode, 0, sizeof(decode)); }
|
|
|
|
inline ~Decoder() { ffmpeg_decode_free(&decode); }
|
2014-09-12 19:51:45 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
inline operator ffmpeg_decode *() { return &decode; }
|
|
|
|
inline ffmpeg_decode *operator->() { return &decode; }
|
2014-09-12 19:51:45 -07:00
|
|
|
};
|
|
|
|
|
2014-09-12 01:31:13 -07:00
|
|
|
class CriticalSection {
|
|
|
|
CRITICAL_SECTION mutex;
|
|
|
|
|
|
|
|
public:
|
2019-06-22 22:13:45 -07:00
|
|
|
inline CriticalSection() { InitializeCriticalSection(&mutex); }
|
|
|
|
inline ~CriticalSection() { DeleteCriticalSection(&mutex); }
|
2014-09-12 01:31:13 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
inline operator CRITICAL_SECTION *() { return &mutex; }
|
2014-09-12 01:31:13 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
class CriticalScope {
|
|
|
|
CriticalSection &mutex;
|
|
|
|
|
|
|
|
CriticalScope() = delete;
|
2019-06-22 22:13:45 -07:00
|
|
|
CriticalScope &operator=(CriticalScope &cs) = delete;
|
2014-09-12 01:31:13 -07:00
|
|
|
|
|
|
|
public:
|
|
|
|
inline CriticalScope(CriticalSection &mutex_) : mutex(mutex_)
|
|
|
|
{
|
|
|
|
EnterCriticalSection(mutex);
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
inline ~CriticalScope() { LeaveCriticalSection(mutex); }
|
2014-09-12 01:31:13 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
enum class Action {
|
|
|
|
None,
|
2015-01-03 20:50:57 -08:00
|
|
|
Activate,
|
2018-05-05 11:38:32 -07:00
|
|
|
ActivateBlock,
|
2015-01-03 20:50:57 -08:00
|
|
|
Deactivate,
|
2014-09-12 01:31:13 -07:00
|
|
|
Shutdown,
|
|
|
|
ConfigVideo,
|
|
|
|
ConfigAudio,
|
|
|
|
ConfigCrossbar1,
|
|
|
|
ConfigCrossbar2,
|
|
|
|
};
|
|
|
|
|
|
|
|
static DWORD CALLBACK DShowThread(LPVOID ptr);
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
struct DShowInput {
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_source_t *source;
|
2019-06-22 22:13:45 -07:00
|
|
|
Device device;
|
|
|
|
bool deactivateWhenNotShowing = false;
|
|
|
|
bool deviceHasAudio = false;
|
|
|
|
bool deviceHasSeparateAudioFilter = false;
|
|
|
|
bool flip = false;
|
|
|
|
bool active = false;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
Decoder audio_decoder;
|
|
|
|
Decoder video_decoder;
|
2014-09-12 19:53:59 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
VideoConfig videoConfig;
|
|
|
|
AudioConfig audioConfig;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2019-09-14 17:04:22 -07:00
|
|
|
video_range_type range;
|
2019-04-25 13:32:32 -07:00
|
|
|
obs_source_frame2 frame;
|
2014-08-28 18:27:58 -07:00
|
|
|
obs_source_audio audio;
|
2020-03-01 03:42:46 -08:00
|
|
|
long lastRotation = 0;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-09-12 01:31:13 -07:00
|
|
|
WinHandle semaphore;
|
2018-05-05 11:38:32 -07:00
|
|
|
WinHandle activated_event;
|
2014-09-12 01:31:13 -07:00
|
|
|
WinHandle thread;
|
|
|
|
CriticalSection mutex;
|
|
|
|
vector<Action> actions;
|
|
|
|
|
|
|
|
inline void QueueAction(Action action)
|
|
|
|
{
|
|
|
|
CriticalScope scope(mutex);
|
|
|
|
actions.push_back(action);
|
|
|
|
ReleaseSemaphore(semaphore, 1, nullptr);
|
|
|
|
}
|
|
|
|
|
2018-05-05 11:38:32 -07:00
|
|
|
inline void QueueActivate(obs_data_t *settings)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
bool block =
|
|
|
|
obs_data_get_bool(settings, "synchronous_activate");
|
2018-05-05 11:38:32 -07:00
|
|
|
QueueAction(block ? Action::ActivateBlock : Action::Activate);
|
|
|
|
if (block) {
|
|
|
|
obs_data_erase(settings, "synchronous_activate");
|
|
|
|
WaitForSingleObject(activated_event, INFINITE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
inline DShowInput(obs_source_t *source_, obs_data_t *settings)
|
2019-06-22 22:13:45 -07:00
|
|
|
: source(source_), device(InitGraph::False)
|
2014-09-12 19:51:45 -07:00
|
|
|
{
|
2014-09-12 19:53:18 -07:00
|
|
|
memset(&audio, 0, sizeof(audio));
|
|
|
|
memset(&frame, 0, sizeof(frame));
|
|
|
|
|
2014-09-12 19:51:45 -07:00
|
|
|
av_log_set_level(AV_LOG_WARNING);
|
|
|
|
av_log_set_callback(ffmpeg_log);
|
2014-09-12 01:31:13 -07:00
|
|
|
|
|
|
|
semaphore = CreateSemaphore(nullptr, 0, 0x7FFFFFFF, nullptr);
|
|
|
|
if (!semaphore)
|
|
|
|
throw "Failed to create semaphore";
|
|
|
|
|
2018-05-05 11:38:32 -07:00
|
|
|
activated_event = CreateEvent(nullptr, false, false, nullptr);
|
|
|
|
if (!activated_event)
|
|
|
|
throw "Failed to create activated_event";
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
thread =
|
|
|
|
CreateThread(nullptr, 0, DShowThread, this, 0, nullptr);
|
2014-09-12 01:31:13 -07:00
|
|
|
if (!thread)
|
|
|
|
throw "Failed to create thread";
|
|
|
|
|
2017-03-24 00:44:18 -07:00
|
|
|
deactivateWhenNotShowing =
|
|
|
|
obs_data_get_bool(settings, DEACTIVATE_WNS);
|
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
if (obs_data_get_bool(settings, "active")) {
|
2017-03-24 00:44:18 -07:00
|
|
|
bool showing = obs_source_showing(source);
|
|
|
|
if (!deactivateWhenNotShowing || showing)
|
2018-05-05 11:38:32 -07:00
|
|
|
QueueActivate(settings);
|
2017-03-24 00:44:18 -07:00
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
active = true;
|
|
|
|
}
|
2014-09-12 01:31:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
inline ~DShowInput()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
CriticalScope scope(mutex);
|
|
|
|
actions.resize(1);
|
|
|
|
actions[0] = Action::Shutdown;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReleaseSemaphore(semaphore, 1, nullptr);
|
|
|
|
|
|
|
|
WaitForSingleObject(thread, INFINITE);
|
2014-09-12 19:51:45 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void OnEncodedVideoData(enum AVCodecID id, unsigned char *data,
|
|
|
|
size_t size, long long ts);
|
|
|
|
void OnEncodedAudioData(enum AVCodecID id, unsigned char *data,
|
|
|
|
size_t size, long long ts);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void OnVideoData(const VideoConfig &config, unsigned char *data,
|
2020-03-01 03:42:46 -08:00
|
|
|
size_t size, long long startTime, long long endTime,
|
|
|
|
long rotation);
|
2019-06-22 22:13:45 -07:00
|
|
|
void OnAudioData(const AudioConfig &config, unsigned char *data,
|
|
|
|
size_t size, long long startTime, long long endTime);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
bool UpdateVideoConfig(obs_data_t *settings);
|
|
|
|
bool UpdateAudioConfig(obs_data_t *settings);
|
2015-01-03 20:50:57 -08:00
|
|
|
void SetActive(bool active);
|
2015-02-11 12:51:30 -08:00
|
|
|
inline enum video_colorspace GetColorSpace(obs_data_t *settings) const;
|
|
|
|
inline enum video_range_type GetColorRange(obs_data_t *settings) const;
|
2015-03-19 16:03:09 -07:00
|
|
|
inline bool Activate(obs_data_t *settings);
|
2015-01-03 20:50:57 -08:00
|
|
|
inline void Deactivate();
|
2014-09-12 01:31:13 -07:00
|
|
|
|
2014-12-31 21:59:46 -08:00
|
|
|
inline void SetupBuffering(obs_data_t *settings);
|
|
|
|
|
2014-09-12 01:31:13 -07:00
|
|
|
void DShowLoop();
|
2014-05-30 03:29:48 -07:00
|
|
|
};
|
|
|
|
|
2014-09-12 01:31:13 -07:00
|
|
|
static DWORD CALLBACK DShowThread(LPVOID ptr)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *dshowInput = (DShowInput *)ptr;
|
2014-09-12 01:31:13 -07:00
|
|
|
|
2015-01-02 05:36:09 -08:00
|
|
|
os_set_thread_name("win-dshow: DShowThread");
|
|
|
|
|
2014-09-12 01:31:13 -07:00
|
|
|
CoInitialize(nullptr);
|
|
|
|
dshowInput->DShowLoop();
|
|
|
|
CoUninitialize();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void ProcessMessages()
|
|
|
|
{
|
|
|
|
MSG msg;
|
|
|
|
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Always keep directshow in a single thread for a given device */
|
|
|
|
void DShowInput::DShowLoop()
|
|
|
|
{
|
|
|
|
while (true) {
|
|
|
|
DWORD ret = MsgWaitForMultipleObjects(1, &semaphore, false,
|
2019-06-22 22:13:45 -07:00
|
|
|
INFINITE, QS_ALLINPUT);
|
2014-09-12 01:31:13 -07:00
|
|
|
if (ret == (WAIT_OBJECT_0 + 1)) {
|
|
|
|
ProcessMessages();
|
|
|
|
continue;
|
|
|
|
} else if (ret != WAIT_OBJECT_0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Action action = Action::None;
|
|
|
|
{
|
|
|
|
CriticalScope scope(mutex);
|
|
|
|
if (actions.size()) {
|
|
|
|
action = actions.front();
|
|
|
|
actions.erase(actions.begin());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (action) {
|
2015-01-03 20:50:57 -08:00
|
|
|
case Action::Activate:
|
2019-06-22 22:13:45 -07:00
|
|
|
case Action::ActivateBlock: {
|
|
|
|
bool block = action == Action::ActivateBlock;
|
|
|
|
|
|
|
|
obs_data_t *settings;
|
|
|
|
settings = obs_source_get_settings(source);
|
|
|
|
if (!Activate(settings)) {
|
|
|
|
obs_source_output_video2(source, nullptr);
|
2014-09-12 01:31:13 -07:00
|
|
|
}
|
2019-06-22 22:13:45 -07:00
|
|
|
if (block)
|
|
|
|
SetEvent(activated_event);
|
|
|
|
obs_data_release(settings);
|
|
|
|
break;
|
|
|
|
}
|
2014-09-12 01:31:13 -07:00
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
case Action::Deactivate:
|
|
|
|
Deactivate();
|
|
|
|
break;
|
|
|
|
|
2014-09-12 01:31:13 -07:00
|
|
|
case Action::Shutdown:
|
|
|
|
device.ShutdownGraph();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case Action::ConfigVideo:
|
|
|
|
device.OpenDialog(nullptr, DialogType::ConfigVideo);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Action::ConfigAudio:
|
|
|
|
device.OpenDialog(nullptr, DialogType::ConfigAudio);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Action::ConfigCrossbar1:
|
|
|
|
device.OpenDialog(nullptr, DialogType::ConfigCrossbar);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Action::ConfigCrossbar2:
|
|
|
|
device.OpenDialog(nullptr, DialogType::ConfigCrossbar2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Action::None:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
#define FPS_HIGHEST 0LL
|
2014-07-02 10:10:38 -07:00
|
|
|
#define FPS_MATCHING -1LL
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
template<typename T, typename U, typename V>
|
2014-07-02 08:39:32 -07:00
|
|
|
static bool between(T &&lower, U &&value, V &&upper)
|
|
|
|
{
|
|
|
|
return value >= lower && value <= upper;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ResolutionAvailable(const VideoInfo &cap, int cx, int cy)
|
|
|
|
{
|
|
|
|
return between(cap.minCX, cx, cap.maxCX) &&
|
2019-06-22 22:13:45 -07:00
|
|
|
between(cap.minCY, cy, cap.maxCY);
|
2014-07-02 08:39:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#define DEVICE_INTERVAL_DIFF_LIMIT 20
|
|
|
|
|
|
|
|
static bool FrameRateAvailable(const VideoInfo &cap, long long interval)
|
|
|
|
{
|
2014-07-02 10:10:38 -07:00
|
|
|
return interval == FPS_HIGHEST || interval == FPS_MATCHING ||
|
2019-06-22 22:13:45 -07:00
|
|
|
between(cap.minInterval - DEVICE_INTERVAL_DIFF_LIMIT, interval,
|
|
|
|
cap.maxInterval + DEVICE_INTERVAL_DIFF_LIMIT);
|
2014-07-02 08:39:32 -07:00
|
|
|
}
|
|
|
|
|
2014-07-02 09:22:03 -07:00
|
|
|
static long long FrameRateInterval(const VideoInfo &cap,
|
2019-06-22 22:13:45 -07:00
|
|
|
long long desired_interval)
|
2014-07-02 09:22:03 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
return desired_interval < cap.minInterval
|
|
|
|
? cap.minInterval
|
|
|
|
: min(desired_interval, cap.maxInterval);
|
2014-07-02 09:22:03 -07:00
|
|
|
}
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
static inline video_format ConvertVideoFormat(VideoFormat format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
2019-06-22 22:13:45 -07:00
|
|
|
case VideoFormat::ARGB:
|
|
|
|
return VIDEO_FORMAT_BGRA;
|
|
|
|
case VideoFormat::XRGB:
|
|
|
|
return VIDEO_FORMAT_BGRX;
|
|
|
|
case VideoFormat::I420:
|
|
|
|
return VIDEO_FORMAT_I420;
|
|
|
|
case VideoFormat::YV12:
|
|
|
|
return VIDEO_FORMAT_I420;
|
|
|
|
case VideoFormat::NV12:
|
|
|
|
return VIDEO_FORMAT_NV12;
|
|
|
|
case VideoFormat::Y800:
|
|
|
|
return VIDEO_FORMAT_Y800;
|
|
|
|
case VideoFormat::YVYU:
|
|
|
|
return VIDEO_FORMAT_YVYU;
|
|
|
|
case VideoFormat::YUY2:
|
|
|
|
return VIDEO_FORMAT_YUY2;
|
|
|
|
case VideoFormat::UYVY:
|
|
|
|
return VIDEO_FORMAT_UYVY;
|
|
|
|
case VideoFormat::HDYC:
|
|
|
|
return VIDEO_FORMAT_UYVY;
|
|
|
|
default:
|
|
|
|
return VIDEO_FORMAT_NONE;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-28 18:27:58 -07:00
|
|
|
static inline audio_format ConvertAudioFormat(AudioFormat format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
2019-06-22 22:13:45 -07:00
|
|
|
case AudioFormat::Wave16bit:
|
|
|
|
return AUDIO_FORMAT_16BIT;
|
|
|
|
case AudioFormat::WaveFloat:
|
|
|
|
return AUDIO_FORMAT_FLOAT;
|
|
|
|
default:
|
|
|
|
return AUDIO_FORMAT_UNKNOWN;
|
2014-08-28 18:27:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
libobs: Add surround sound audio support
(This commit also modifies the following modules: UI,
deps/media-playback, coreaudio-encoder, decklink, linux-alsa,
linux-pulseaudio, mac-capture, obs-ffmpeg, obs-filters, obs-libfdk,
obs-outputs, win-dshow, and win-wasapi)
Adds surround sound audio support to the core, core plugins, and user
interface.
Compatible streaming services: Twitch, FB 360 live
Compatible protocols: rtmp / mpeg-ts tcp udp
Compatible file formats: mkv mp4 ts (others untested)
Compatible codecs: ffmpeg aac, fdk_aac, CoreAudio aac,
opus, vorbis, pcm (others untested).
Tested streaming servers: wowza, nginx
HLS, mpeg-dash : surround passthrough
Html5 players tested with live surround:
videojs, mediaelement, viblast (hls+dash), hls.js
Decklink: on win32, swap channels order for 5.1 7.1
(due to different channel mapping on wav, mpeg, ffmpeg)
Audio filters: surround working.
Monitoring: surround working (win macOs linux (pulse-audio)).
VST: stereo plugins keep in general only the first two channels.
surround plugins should work (e.g. mcfx does).
OS: win, macOs, linux (alsa, pulse-audio).
Misc: larger audio bitrates unlocked to accommodate more channels
NB: mf-aac only supports mono and stereo + 5.1 on win 10
(not implemented due to lack of usefulness)
Closes jp9000/obs-studio#968
2017-05-26 17:15:54 -07:00
|
|
|
static inline enum speaker_layout convert_speaker_layout(uint8_t channels)
|
|
|
|
{
|
|
|
|
switch (channels) {
|
2019-06-22 22:13:45 -07:00
|
|
|
case 0:
|
|
|
|
return SPEAKERS_UNKNOWN;
|
|
|
|
case 1:
|
|
|
|
return SPEAKERS_MONO;
|
|
|
|
case 2:
|
|
|
|
return SPEAKERS_STEREO;
|
|
|
|
case 3:
|
|
|
|
return SPEAKERS_2POINT1;
|
|
|
|
case 4:
|
|
|
|
return SPEAKERS_4POINT0;
|
|
|
|
case 5:
|
|
|
|
return SPEAKERS_4POINT1;
|
|
|
|
case 6:
|
|
|
|
return SPEAKERS_5POINT1;
|
|
|
|
case 8:
|
|
|
|
return SPEAKERS_7POINT1;
|
|
|
|
default:
|
|
|
|
return SPEAKERS_UNKNOWN;
|
libobs: Add surround sound audio support
(This commit also modifies the following modules: UI,
deps/media-playback, coreaudio-encoder, decklink, linux-alsa,
linux-pulseaudio, mac-capture, obs-ffmpeg, obs-filters, obs-libfdk,
obs-outputs, win-dshow, and win-wasapi)
Adds surround sound audio support to the core, core plugins, and user
interface.
Compatible streaming services: Twitch, FB 360 live
Compatible protocols: rtmp / mpeg-ts tcp udp
Compatible file formats: mkv mp4 ts (others untested)
Compatible codecs: ffmpeg aac, fdk_aac, CoreAudio aac,
opus, vorbis, pcm (others untested).
Tested streaming servers: wowza, nginx
HLS, mpeg-dash : surround passthrough
Html5 players tested with live surround:
videojs, mediaelement, viblast (hls+dash), hls.js
Decklink: on win32, swap channels order for 5.1 7.1
(due to different channel mapping on wav, mpeg, ffmpeg)
Audio filters: surround working.
Monitoring: surround working (win macOs linux (pulse-audio)).
VST: stereo plugins keep in general only the first two channels.
surround plugins should work (e.g. mcfx does).
OS: win, macOs, linux (alsa, pulse-audio).
Misc: larger audio bitrates unlocked to accommodate more channels
NB: mf-aac only supports mono and stereo + 5.1 on win 10
(not implemented due to lack of usefulness)
Closes jp9000/obs-studio#968
2017-05-26 17:15:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-22 17:25:55 -07:00
|
|
|
//#define LOG_ENCODED_VIDEO_TS 1
|
|
|
|
//#define LOG_ENCODED_AUDIO_TS 1
|
|
|
|
|
2019-07-27 21:44:30 -07:00
|
|
|
#define MAX_SW_RES_INT (1920 * 1080)
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void DShowInput::OnEncodedVideoData(enum AVCodecID id, unsigned char *data,
|
|
|
|
size_t size, long long ts)
|
2014-09-12 19:53:59 -07:00
|
|
|
{
|
2019-08-05 22:31:06 -07:00
|
|
|
/* If format changes, free and allow it to recreate the decoder */
|
|
|
|
if (ffmpeg_decode_valid(video_decoder) &&
|
|
|
|
video_decoder->codec->id != id) {
|
|
|
|
ffmpeg_decode_free(video_decoder);
|
|
|
|
}
|
|
|
|
|
2014-09-12 19:53:59 -07:00
|
|
|
if (!ffmpeg_decode_valid(video_decoder)) {
|
2019-07-27 21:44:30 -07:00
|
|
|
/* Only use MJPEG hardware decoding on resolutions higher
|
|
|
|
* than 1920x1080. The reason why is because we want to strike
|
|
|
|
* a reasonable balance between hardware and CPU usage. */
|
|
|
|
bool useHW = videoConfig.format != VideoFormat::MJPEG ||
|
2019-11-12 17:59:59 -08:00
|
|
|
(videoConfig.cx * videoConfig.cy_abs) >
|
|
|
|
MAX_SW_RES_INT;
|
2019-07-27 21:44:30 -07:00
|
|
|
if (ffmpeg_decode_init(video_decoder, id, useHW) < 0) {
|
2014-09-12 19:53:59 -07:00
|
|
|
blog(LOG_WARNING, "Could not initialize video decoder");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool got_output;
|
2017-12-05 13:53:18 -08:00
|
|
|
bool success = ffmpeg_decode_video(video_decoder, data, size, &ts,
|
2019-09-14 17:04:22 -07:00
|
|
|
range, &frame, &got_output);
|
2017-12-05 13:53:18 -08:00
|
|
|
if (!success) {
|
2014-09-12 19:53:59 -07:00
|
|
|
blog(LOG_WARNING, "Error decoding video");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got_output) {
|
|
|
|
frame.timestamp = (uint64_t)ts * 100;
|
2015-04-13 15:01:20 -07:00
|
|
|
if (flip)
|
|
|
|
frame.flip = !frame.flip;
|
2014-09-22 17:25:55 -07:00
|
|
|
#if LOG_ENCODED_VIDEO_TS
|
2014-09-12 19:53:59 -07:00
|
|
|
blog(LOG_DEBUG, "video ts: %llu", frame.timestamp);
|
2014-09-22 17:25:55 -07:00
|
|
|
#endif
|
2019-04-25 13:32:32 -07:00
|
|
|
obs_source_output_video2(source, &frame);
|
2014-09-12 19:53:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void DShowInput::OnVideoData(const VideoConfig &config, unsigned char *data,
|
|
|
|
size_t size, long long startTime,
|
2020-03-01 03:42:46 -08:00
|
|
|
long long endTime, long rotation)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2020-03-07 06:48:07 -08:00
|
|
|
if (rotation != lastRotation) {
|
|
|
|
lastRotation = rotation;
|
|
|
|
obs_source_set_async_rotation(source, rotation);
|
|
|
|
}
|
|
|
|
|
2014-09-12 19:53:59 -07:00
|
|
|
if (videoConfig.format == VideoFormat::H264) {
|
|
|
|
OnEncodedVideoData(AV_CODEC_ID_H264, data, size, startTime);
|
|
|
|
return;
|
|
|
|
}
|
2014-08-28 11:06:54 -07:00
|
|
|
|
2019-06-17 22:25:56 -07:00
|
|
|
if (videoConfig.format == VideoFormat::MJPEG) {
|
|
|
|
OnEncodedVideoData(AV_CODEC_ID_MJPEG, data, size, startTime);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-09-12 01:27:57 -07:00
|
|
|
const int cx = config.cx;
|
2019-11-12 17:59:59 -08:00
|
|
|
const int cy_abs = config.cy_abs;
|
2014-09-12 01:27:57 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
frame.timestamp = (uint64_t)startTime * 100;
|
|
|
|
frame.width = config.cx;
|
2019-11-12 17:59:59 -08:00
|
|
|
frame.height = cy_abs;
|
2019-06-22 22:13:45 -07:00
|
|
|
frame.format = ConvertVideoFormat(config.format);
|
2019-11-12 17:59:59 -08:00
|
|
|
frame.flip = flip;
|
2014-08-28 11:06:54 -07:00
|
|
|
|
2020-01-13 21:50:54 -08:00
|
|
|
/* YUV DIBS are always top-down */
|
|
|
|
if (config.format == VideoFormat::XRGB ||
|
|
|
|
config.format == VideoFormat::ARGB) {
|
|
|
|
/* RGB DIBs are bottom-up by default */
|
|
|
|
if (!config.cy_flip)
|
|
|
|
frame.flip = !frame.flip;
|
|
|
|
}
|
2015-04-13 15:01:20 -07:00
|
|
|
|
2014-08-28 11:06:54 -07:00
|
|
|
if (videoConfig.format == VideoFormat::XRGB ||
|
|
|
|
videoConfig.format == VideoFormat::ARGB) {
|
2019-06-22 22:13:45 -07:00
|
|
|
frame.data[0] = data;
|
2014-08-28 11:06:54 -07:00
|
|
|
frame.linesize[0] = cx * 4;
|
|
|
|
|
|
|
|
} else if (videoConfig.format == VideoFormat::YVYU ||
|
2019-06-22 22:13:45 -07:00
|
|
|
videoConfig.format == VideoFormat::YUY2 ||
|
|
|
|
videoConfig.format == VideoFormat::HDYC ||
|
|
|
|
videoConfig.format == VideoFormat::UYVY) {
|
|
|
|
frame.data[0] = data;
|
2014-08-28 11:06:54 -07:00
|
|
|
frame.linesize[0] = cx * 2;
|
|
|
|
|
|
|
|
} else if (videoConfig.format == VideoFormat::I420) {
|
|
|
|
frame.data[0] = data;
|
2019-11-12 17:59:59 -08:00
|
|
|
frame.data[1] = frame.data[0] + (cx * cy_abs);
|
|
|
|
frame.data[2] = frame.data[1] + (cx * cy_abs / 4);
|
2014-08-28 11:06:54 -07:00
|
|
|
frame.linesize[0] = cx;
|
|
|
|
frame.linesize[1] = cx / 2;
|
|
|
|
frame.linesize[2] = cx / 2;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-12-19 07:45:10 -08:00
|
|
|
} else if (videoConfig.format == VideoFormat::YV12) {
|
|
|
|
frame.data[0] = data;
|
2019-11-12 17:59:59 -08:00
|
|
|
frame.data[2] = frame.data[0] + (cx * cy_abs);
|
|
|
|
frame.data[1] = frame.data[2] + (cx * cy_abs / 4);
|
2014-12-19 07:45:10 -08:00
|
|
|
frame.linesize[0] = cx;
|
|
|
|
frame.linesize[1] = cx / 2;
|
|
|
|
frame.linesize[2] = cx / 2;
|
|
|
|
|
|
|
|
} else if (videoConfig.format == VideoFormat::NV12) {
|
|
|
|
frame.data[0] = data;
|
2019-11-12 17:59:59 -08:00
|
|
|
frame.data[1] = frame.data[0] + (cx * cy_abs);
|
2014-12-19 07:45:10 -08:00
|
|
|
frame.linesize[0] = cx;
|
|
|
|
frame.linesize[1] = cx;
|
|
|
|
|
2016-03-24 03:14:34 -07:00
|
|
|
} else if (videoConfig.format == VideoFormat::Y800) {
|
|
|
|
frame.data[0] = data;
|
|
|
|
frame.linesize[0] = cx;
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
} else {
|
|
|
|
/* TODO: other formats */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-25 13:32:32 -07:00
|
|
|
obs_source_output_video2(source, &frame);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(endTime); /* it's the enndd tiimmes! */
|
|
|
|
UNUSED_PARAMETER(size);
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void DShowInput::OnEncodedAudioData(enum AVCodecID id, unsigned char *data,
|
|
|
|
size_t size, long long ts)
|
2014-09-12 19:53:59 -07:00
|
|
|
{
|
|
|
|
if (!ffmpeg_decode_valid(audio_decoder)) {
|
2019-07-27 21:44:30 -07:00
|
|
|
if (ffmpeg_decode_init(audio_decoder, id, false) < 0) {
|
2014-09-12 19:53:59 -07:00
|
|
|
blog(LOG_WARNING, "Could not initialize audio decoder");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-05 13:53:18 -08:00
|
|
|
bool got_output = false;
|
2017-03-03 16:58:42 -08:00
|
|
|
do {
|
2017-12-05 13:53:18 -08:00
|
|
|
bool success = ffmpeg_decode_audio(audio_decoder, data, size,
|
2019-06-22 22:13:45 -07:00
|
|
|
&audio, &got_output);
|
2017-12-05 13:53:18 -08:00
|
|
|
if (!success) {
|
2017-03-03 16:58:42 -08:00
|
|
|
blog(LOG_WARNING, "Error decoding audio");
|
|
|
|
return;
|
|
|
|
}
|
2014-09-12 19:53:59 -07:00
|
|
|
|
2017-03-03 16:58:42 -08:00
|
|
|
if (got_output) {
|
|
|
|
audio.timestamp = (uint64_t)ts * 100;
|
2014-09-22 17:25:55 -07:00
|
|
|
#if LOG_ENCODED_AUDIO_TS
|
2017-03-03 16:58:42 -08:00
|
|
|
blog(LOG_DEBUG, "audio ts: %llu", audio.timestamp);
|
2014-09-22 17:25:55 -07:00
|
|
|
#endif
|
2017-03-03 16:58:42 -08:00
|
|
|
obs_source_output_audio(source, &audio);
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ts += int64_t(audio_decoder->frame->nb_samples) * 10000000LL /
|
2019-06-22 22:13:45 -07:00
|
|
|
int64_t(audio_decoder->frame->sample_rate);
|
2017-12-05 13:53:18 -08:00
|
|
|
size = 0;
|
|
|
|
data = nullptr;
|
|
|
|
} while (got_output);
|
2014-09-12 19:53:59 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
void DShowInput::OnAudioData(const AudioConfig &config, unsigned char *data,
|
|
|
|
size_t size, long long startTime,
|
|
|
|
long long endTime)
|
2014-08-28 18:27:58 -07:00
|
|
|
{
|
2014-09-12 01:27:57 -07:00
|
|
|
size_t block_size;
|
|
|
|
|
2014-09-12 19:53:59 -07:00
|
|
|
if (config.format == AudioFormat::AAC) {
|
|
|
|
OnEncodedAudioData(AV_CODEC_ID_AAC, data, size, startTime);
|
|
|
|
return;
|
|
|
|
} else if (config.format == AudioFormat::AC3) {
|
|
|
|
OnEncodedAudioData(AV_CODEC_ID_AC3, data, size, startTime);
|
2014-08-28 18:27:58 -07:00
|
|
|
return;
|
2014-09-12 19:53:59 -07:00
|
|
|
} else if (config.format == AudioFormat::MPGA) {
|
2014-09-23 04:55:00 -07:00
|
|
|
OnEncodedAudioData(AV_CODEC_ID_MP2, data, size, startTime);
|
2014-09-12 19:53:59 -07:00
|
|
|
return;
|
|
|
|
}
|
2014-08-28 18:27:58 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
audio.speakers = convert_speaker_layout((uint8_t)config.channels);
|
|
|
|
audio.format = ConvertAudioFormat(config.format);
|
2014-09-12 01:27:57 -07:00
|
|
|
audio.samples_per_sec = (uint32_t)config.sampleRate;
|
2019-06-22 22:13:45 -07:00
|
|
|
audio.data[0] = data;
|
2014-09-12 01:27:57 -07:00
|
|
|
|
|
|
|
block_size = get_audio_bytes_per_channel(audio.format) *
|
2019-06-22 22:13:45 -07:00
|
|
|
get_audio_channels(audio.speakers);
|
2014-08-28 18:27:58 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
audio.frames = (uint32_t)(size / block_size);
|
|
|
|
audio.timestamp = (uint64_t)startTime * 100;
|
2014-08-28 18:27:58 -07:00
|
|
|
|
2014-09-12 19:53:59 -07:00
|
|
|
if (audio.format != AUDIO_FORMAT_UNKNOWN)
|
|
|
|
obs_source_output_audio(source, &audio);
|
2014-08-28 18:27:58 -07:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(endTime);
|
|
|
|
}
|
|
|
|
|
2014-07-02 09:57:33 -07:00
|
|
|
struct PropertiesData {
|
2015-01-03 20:50:57 -08:00
|
|
|
DShowInput *input;
|
2014-07-02 09:57:33 -07:00
|
|
|
vector<VideoDevice> devices;
|
2014-08-28 18:27:58 -07:00
|
|
|
vector<AudioDevice> audioDevices;
|
2014-07-02 09:57:33 -07:00
|
|
|
|
|
|
|
bool GetDevice(VideoDevice &device, const char *encoded_id) const
|
|
|
|
{
|
|
|
|
DeviceId deviceId;
|
|
|
|
DecodeDeviceId(deviceId, encoded_id);
|
|
|
|
|
|
|
|
for (const VideoDevice &curDevice : devices) {
|
|
|
|
if (deviceId.name == curDevice.name &&
|
2019-06-22 22:13:45 -07:00
|
|
|
deviceId.path == curDevice.path) {
|
2014-07-02 09:57:33 -07:00
|
|
|
device = curDevice;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
static inline bool ConvertRes(int &cx, int &cy, const char *res)
|
|
|
|
{
|
|
|
|
return sscanf(res, "%dx%d", &cx, &cy) == 2;
|
|
|
|
}
|
|
|
|
|
2014-08-28 10:09:53 -07:00
|
|
|
static inline bool FormatMatches(VideoFormat left, VideoFormat right)
|
2014-07-02 08:39:32 -07:00
|
|
|
{
|
|
|
|
return left == VideoFormat::Any || right == VideoFormat::Any ||
|
2019-06-22 22:13:45 -07:00
|
|
|
left == right;
|
2014-07-02 08:39:32 -07:00
|
|
|
}
|
|
|
|
|
2014-08-28 10:09:53 -07:00
|
|
|
static inline bool ResolutionValid(string res, int &cx, int &cy)
|
2014-07-02 09:22:03 -07:00
|
|
|
{
|
|
|
|
if (!res.size())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return ConvertRes(cx, cy, res.c_str());
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static inline bool CapsMatch(const VideoInfo &)
|
2015-02-14 11:14:27 -08:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
template<typename... F> static bool CapsMatch(const VideoDevice &dev, F... fs);
|
2015-02-11 12:49:20 -08:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
template<typename F, typename... Fs>
|
|
|
|
static inline bool CapsMatch(const VideoInfo &info, F &&f, Fs... fs)
|
2014-07-02 09:22:03 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
return f(info) && CapsMatch(info, fs...);
|
2014-07-02 09:22:03 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
template<typename... F> static bool CapsMatch(const VideoDevice &dev, F... fs)
|
2014-07-02 09:22:03 -07:00
|
|
|
{
|
2019-03-03 10:05:09 -08:00
|
|
|
// no early exit, trigger all side effects.
|
|
|
|
bool match = false;
|
|
|
|
for (const VideoInfo &info : dev.caps)
|
2019-06-22 22:13:45 -07:00
|
|
|
if (CapsMatch(info, fs...))
|
2019-03-03 10:05:09 -08:00
|
|
|
match = true;
|
|
|
|
return match;
|
2014-07-02 09:22:03 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static inline bool MatcherMatchVideoFormat(VideoFormat format, bool &did_match,
|
|
|
|
const VideoInfo &info)
|
2014-07-02 09:22:03 -07:00
|
|
|
{
|
|
|
|
bool match = FormatMatches(format, info.format);
|
|
|
|
did_match = did_match || match;
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
2014-08-28 10:09:53 -07:00
|
|
|
static inline bool MatcherClosestFrameRateSelector(long long interval,
|
2019-06-22 22:13:45 -07:00
|
|
|
long long &best_match,
|
|
|
|
const VideoInfo &info)
|
2014-07-02 09:22:03 -07:00
|
|
|
{
|
|
|
|
long long current = FrameRateInterval(info, interval);
|
|
|
|
if (llabs(interval - best_match) > llabs(interval - current))
|
|
|
|
best_match = current;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
auto ResolutionMatcher = [](int cx, int cy)
|
|
|
|
{
|
|
|
|
return [cx, cy](const VideoInfo &info)
|
|
|
|
{
|
|
|
|
return ResolutionAvailable(info, cx, cy);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
auto FrameRateMatcher = [](long long interval)
|
|
|
|
{
|
|
|
|
return [interval](const VideoInfo &info)
|
|
|
|
{
|
|
|
|
return FrameRateAvailable(info, interval);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
auto VideoFormatMatcher = [](VideoFormat format, bool &did_match)
|
|
|
|
{
|
|
|
|
return [format, &did_match](const VideoInfo &info)
|
|
|
|
{
|
|
|
|
return MatcherMatchVideoFormat(format, did_match, info);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
auto ClosestFrameRateSelector = [](long long interval, long long &best_match)
|
|
|
|
{
|
|
|
|
return [interval, &best_match](const VideoInfo &info) mutable -> bool
|
|
|
|
{
|
|
|
|
MatcherClosestFrameRateSelector(interval, best_match, info);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#else
|
2019-06-22 22:13:45 -07:00
|
|
|
#define ResolutionMatcher(cx, cy) \
|
|
|
|
[cx, cy](const VideoInfo &info) -> bool { \
|
|
|
|
return ResolutionAvailable(info, cx, cy); \
|
|
|
|
}
|
|
|
|
#define FrameRateMatcher(interval) \
|
|
|
|
[interval](const VideoInfo &info) -> bool { \
|
|
|
|
return FrameRateAvailable(info, interval); \
|
|
|
|
}
|
|
|
|
#define VideoFormatMatcher(format, did_match) \
|
|
|
|
[format, &did_match](const VideoInfo &info) mutable -> bool { \
|
|
|
|
return MatcherMatchVideoFormat(format, did_match, info); \
|
|
|
|
}
|
|
|
|
#define ClosestFrameRateSelector(interval, best_match) \
|
|
|
|
[interval, &best_match](const VideoInfo &info) mutable -> bool { \
|
|
|
|
return MatcherClosestFrameRateSelector(interval, best_match, \
|
|
|
|
info); \
|
|
|
|
}
|
2014-07-02 09:22:03 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static bool ResolutionAvailable(const VideoDevice &dev, int cx, int cy)
|
|
|
|
{
|
|
|
|
return CapsMatch(dev, ResolutionMatcher(cx, cy));
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool DetermineResolution(int &cx, int &cy, obs_data_t *settings,
|
2019-06-22 22:13:45 -07:00
|
|
|
VideoDevice dev)
|
2014-07-02 09:22:03 -07:00
|
|
|
{
|
|
|
|
const char *res = obs_data_get_autoselect_string(settings, RESOLUTION);
|
2014-08-09 12:38:56 -07:00
|
|
|
if (obs_data_has_autoselect_value(settings, RESOLUTION) &&
|
2019-06-22 22:13:45 -07:00
|
|
|
ConvertRes(cx, cy, res) && ResolutionAvailable(dev, cx, cy))
|
2014-07-02 09:22:03 -07:00
|
|
|
return true;
|
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
res = obs_data_get_string(settings, RESOLUTION);
|
2014-07-02 09:22:03 -07:00
|
|
|
if (ConvertRes(cx, cy, res) && ResolutionAvailable(dev, cx, cy))
|
|
|
|
return true;
|
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
res = obs_data_get_string(settings, LAST_RESOLUTION);
|
2014-07-02 09:22:03 -07:00
|
|
|
if (ConvertRes(cx, cy, res) && ResolutionAvailable(dev, cx, cy))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-07-02 10:10:38 -07:00
|
|
|
static long long GetOBSFPS();
|
|
|
|
|
2019-07-28 22:46:32 -07:00
|
|
|
static inline bool IsDelayedDevice(const VideoConfig &config)
|
2014-12-31 21:59:46 -08:00
|
|
|
{
|
2019-07-28 22:50:24 -07:00
|
|
|
return config.format > VideoFormat::MJPEG ||
|
2019-06-22 22:13:45 -07:00
|
|
|
wstrstri(config.name.c_str(), L"elgato") != NULL ||
|
|
|
|
wstrstri(config.name.c_str(), L"stream engine") != NULL;
|
2014-12-31 21:59:46 -08:00
|
|
|
}
|
|
|
|
|
2019-07-05 07:31:07 -07:00
|
|
|
static inline bool IsDecoupled(const VideoConfig &config)
|
|
|
|
{
|
|
|
|
return wstrstri(config.name.c_str(), L"GV-USB2") != NULL;
|
|
|
|
}
|
|
|
|
|
2014-12-31 21:59:46 -08:00
|
|
|
inline void DShowInput::SetupBuffering(obs_data_t *settings)
|
|
|
|
{
|
|
|
|
BufferingType bufType;
|
|
|
|
bool useBuffering;
|
|
|
|
|
|
|
|
bufType = (BufferingType)obs_data_get_int(settings, BUFFERING_VAL);
|
|
|
|
|
|
|
|
if (bufType == BufferingType::Auto)
|
2019-07-28 22:46:32 -07:00
|
|
|
useBuffering = IsDelayedDevice(videoConfig);
|
2014-12-31 21:59:46 -08:00
|
|
|
else
|
|
|
|
useBuffering = bufType == BufferingType::On;
|
|
|
|
|
2017-05-13 23:32:40 -07:00
|
|
|
obs_source_set_async_unbuffered(source, !useBuffering);
|
2019-07-05 07:31:07 -07:00
|
|
|
obs_source_set_async_decoupled(source, IsDecoupled(videoConfig));
|
2014-12-31 21:59:46 -08:00
|
|
|
}
|
|
|
|
|
2015-07-05 23:56:01 -07:00
|
|
|
static DStr GetVideoFormatName(VideoFormat format);
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
bool DShowInput::UpdateVideoConfig(obs_data_t *settings)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2014-08-05 11:09:29 -07:00
|
|
|
string video_device_id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
|
2016-04-03 15:55:15 -07:00
|
|
|
deactivateWhenNotShowing = obs_data_get_bool(settings, DEACTIVATE_WNS);
|
2015-04-13 15:01:20 -07:00
|
|
|
flip = obs_data_get_bool(settings, FLIP_IMAGE);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:57:33 -07:00
|
|
|
DeviceId id;
|
2016-03-24 03:14:34 -07:00
|
|
|
if (!DecodeDeviceId(id, video_device_id.c_str())) {
|
|
|
|
blog(LOG_WARNING, "%s: DecodeDeviceId failed",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_source_get_name(source));
|
2014-08-28 18:27:58 -07:00
|
|
|
return false;
|
2016-03-24 03:14:34 -07:00
|
|
|
}
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:57:33 -07:00
|
|
|
PropertiesData data;
|
|
|
|
Device::EnumVideoDevices(data.devices);
|
|
|
|
VideoDevice dev;
|
2016-03-24 03:14:34 -07:00
|
|
|
if (!data.GetDevice(dev, video_device_id.c_str())) {
|
|
|
|
blog(LOG_WARNING, "%s: data.GetDevice failed",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_source_get_name(source));
|
2014-08-28 18:27:58 -07:00
|
|
|
return false;
|
2016-03-24 03:14:34 -07:00
|
|
|
}
|
2014-07-02 09:57:33 -07:00
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
int resType = (int)obs_data_get_int(settings, RES_TYPE);
|
2014-07-02 09:57:33 -07:00
|
|
|
int cx = 0, cy = 0;
|
|
|
|
long long interval = 0;
|
|
|
|
VideoFormat format = VideoFormat::Any;
|
|
|
|
|
|
|
|
if (resType == ResType_Custom) {
|
2014-08-09 12:38:56 -07:00
|
|
|
bool has_autosel_val;
|
2014-08-05 11:09:29 -07:00
|
|
|
string resolution = obs_data_get_string(settings, RESOLUTION);
|
2016-03-24 03:14:34 -07:00
|
|
|
if (!ResolutionValid(resolution, cx, cy)) {
|
|
|
|
blog(LOG_WARNING, "%s: ResolutionValid failed",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_source_get_name(source));
|
2014-08-28 18:27:58 -07:00
|
|
|
return false;
|
2016-03-24 03:14:34 -07:00
|
|
|
}
|
2014-07-02 09:57:33 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
has_autosel_val =
|
|
|
|
obs_data_has_autoselect_value(settings, FRAME_INTERVAL);
|
|
|
|
interval = has_autosel_val
|
|
|
|
? obs_data_get_autoselect_int(settings,
|
|
|
|
FRAME_INTERVAL)
|
|
|
|
: obs_data_get_int(settings, FRAME_INTERVAL);
|
2014-07-02 09:57:33 -07:00
|
|
|
|
2014-07-02 10:10:38 -07:00
|
|
|
if (interval == FPS_MATCHING)
|
|
|
|
interval = GetOBSFPS();
|
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
format = (VideoFormat)obs_data_get_int(settings, VIDEO_FORMAT);
|
2014-07-02 09:57:33 -07:00
|
|
|
|
|
|
|
long long best_interval = numeric_limits<long long>::max();
|
|
|
|
bool video_format_match = false;
|
2019-06-22 22:13:45 -07:00
|
|
|
bool caps_match = CapsMatch(
|
|
|
|
dev, ResolutionMatcher(cx, cy),
|
|
|
|
VideoFormatMatcher(format, video_format_match),
|
|
|
|
ClosestFrameRateSelector(interval, best_interval),
|
|
|
|
FrameRateMatcher(interval));
|
2016-03-24 03:14:34 -07:00
|
|
|
|
|
|
|
if (!caps_match && !video_format_match) {
|
|
|
|
blog(LOG_WARNING, "%s: Video format match failed",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_source_get_name(source));
|
2014-08-28 18:27:58 -07:00
|
|
|
return false;
|
2016-03-24 03:14:34 -07:00
|
|
|
}
|
2014-07-02 09:57:33 -07:00
|
|
|
|
|
|
|
interval = best_interval;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
videoConfig.name = id.name.c_str();
|
|
|
|
videoConfig.path = id.path.c_str();
|
2014-05-30 03:29:48 -07:00
|
|
|
videoConfig.useDefaultConfig = resType == ResType_Preferred;
|
2019-06-22 22:13:45 -07:00
|
|
|
videoConfig.cx = cx;
|
2019-11-12 17:59:59 -08:00
|
|
|
videoConfig.cy_abs = abs(cy);
|
|
|
|
videoConfig.cy_flip = cy < 0;
|
2019-06-22 22:13:45 -07:00
|
|
|
videoConfig.frameInterval = interval;
|
|
|
|
videoConfig.internalFormat = format;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-08-28 18:27:58 -07:00
|
|
|
deviceHasAudio = dev.audioAttached;
|
2017-11-28 01:15:57 -08:00
|
|
|
deviceHasSeparateAudioFilter = dev.separateAudioFilter;
|
2014-08-28 18:27:58 -07:00
|
|
|
|
2014-08-28 11:06:54 -07:00
|
|
|
videoConfig.callback = std::bind(&DShowInput::OnVideoData, this,
|
2019-06-22 22:13:45 -07:00
|
|
|
placeholders::_1, placeholders::_2,
|
|
|
|
placeholders::_3, placeholders::_4,
|
2020-03-01 03:42:46 -08:00
|
|
|
placeholders::_5, placeholders::_6);
|
2014-08-28 11:06:54 -07:00
|
|
|
|
2019-06-17 22:25:56 -07:00
|
|
|
videoConfig.format = videoConfig.internalFormat;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2016-03-24 03:14:34 -07:00
|
|
|
if (!device.SetVideoConfig(&videoConfig)) {
|
|
|
|
blog(LOG_WARNING, "%s: device.SetVideoConfig failed",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_source_get_name(source));
|
2014-08-28 18:27:58 -07:00
|
|
|
return false;
|
2016-03-24 03:14:34 -07:00
|
|
|
}
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2015-07-05 23:56:01 -07:00
|
|
|
DStr formatName = GetVideoFormatName(videoConfig.internalFormat);
|
|
|
|
|
|
|
|
double fps = 0.0;
|
|
|
|
|
|
|
|
if (videoConfig.frameInterval)
|
|
|
|
fps = 10000000.0 / double(videoConfig.frameInterval);
|
|
|
|
|
|
|
|
BPtr<char> name_utf8;
|
|
|
|
BPtr<char> path_utf8;
|
|
|
|
os_wcs_to_utf8_ptr(videoConfig.name.c_str(), videoConfig.name.size(),
|
2019-06-22 22:13:45 -07:00
|
|
|
&name_utf8);
|
2015-07-05 23:56:01 -07:00
|
|
|
os_wcs_to_utf8_ptr(videoConfig.path.c_str(), videoConfig.path.size(),
|
2019-06-22 22:13:45 -07:00
|
|
|
&path_utf8);
|
2015-07-05 23:56:01 -07:00
|
|
|
|
|
|
|
blog(LOG_INFO, "---------------------------------");
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_INFO,
|
|
|
|
"[DShow Device: '%s'] settings updated: \n"
|
|
|
|
"\tvideo device: %s\n"
|
|
|
|
"\tvideo path: %s\n"
|
|
|
|
"\tresolution: %dx%d\n"
|
2019-11-12 17:59:59 -08:00
|
|
|
"\tflip: %d\n"
|
2019-06-22 22:13:45 -07:00
|
|
|
"\tfps: %0.2f (interval: %lld)\n"
|
|
|
|
"\tformat: %s",
|
|
|
|
obs_source_get_name(source), (const char *)name_utf8,
|
2019-11-12 17:59:59 -08:00
|
|
|
(const char *)path_utf8, videoConfig.cx, videoConfig.cy_abs,
|
|
|
|
(int)videoConfig.cy_flip, fps, videoConfig.frameInterval,
|
|
|
|
formatName->array);
|
2015-07-05 23:56:01 -07:00
|
|
|
|
2014-12-31 21:59:46 -08:00
|
|
|
SetupBuffering(settings);
|
|
|
|
|
2014-08-28 18:27:58 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
bool DShowInput::UpdateAudioConfig(obs_data_t *settings)
|
2014-08-28 18:27:58 -07:00
|
|
|
{
|
|
|
|
string audio_device_id = obs_data_get_string(settings, AUDIO_DEVICE_ID);
|
2019-06-22 22:13:45 -07:00
|
|
|
bool useCustomAudio = obs_data_get_bool(settings, USE_CUSTOM_AUDIO);
|
2014-08-28 18:27:58 -07:00
|
|
|
|
|
|
|
if (useCustomAudio) {
|
|
|
|
DeviceId id;
|
|
|
|
if (!DecodeDeviceId(id, audio_device_id.c_str()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
audioConfig.name = id.name.c_str();
|
|
|
|
audioConfig.path = id.path.c_str();
|
|
|
|
|
|
|
|
} else if (!deviceHasAudio) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
audioConfig.useVideoDevice = !useCustomAudio &&
|
|
|
|
!deviceHasSeparateAudioFilter;
|
2017-11-28 01:15:57 -08:00
|
|
|
audioConfig.useSeparateAudioFilter = deviceHasSeparateAudioFilter;
|
2014-08-28 18:27:58 -07:00
|
|
|
|
|
|
|
audioConfig.callback = std::bind(&DShowInput::OnAudioData, this,
|
2019-06-22 22:13:45 -07:00
|
|
|
placeholders::_1, placeholders::_2,
|
|
|
|
placeholders::_3, placeholders::_4,
|
|
|
|
placeholders::_5);
|
2014-08-28 18:27:58 -07:00
|
|
|
|
2015-06-05 10:49:50 -07:00
|
|
|
audioConfig.mode =
|
|
|
|
(AudioMode)obs_data_get_int(settings, AUDIO_OUTPUT_MODE);
|
|
|
|
|
2015-07-05 23:56:01 -07:00
|
|
|
bool success = device.SetAudioConfig(&audioConfig);
|
|
|
|
if (!success)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
BPtr<char> name_utf8;
|
|
|
|
os_wcs_to_utf8_ptr(audioConfig.name.c_str(), audioConfig.name.size(),
|
2019-06-22 22:13:45 -07:00
|
|
|
&name_utf8);
|
2015-07-05 23:56:01 -07:00
|
|
|
|
|
|
|
blog(LOG_INFO, "\tusing video device audio: %s",
|
2019-06-22 22:13:45 -07:00
|
|
|
audioConfig.useVideoDevice ? "yes" : "no");
|
2015-07-05 23:56:01 -07:00
|
|
|
|
2017-11-28 01:15:57 -08:00
|
|
|
if (!audioConfig.useVideoDevice) {
|
|
|
|
if (audioConfig.useSeparateAudioFilter)
|
|
|
|
blog(LOG_INFO, "\tseparate audio filter");
|
|
|
|
else
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_INFO, "\taudio device: %s",
|
|
|
|
(const char *)name_utf8);
|
2017-11-28 01:15:57 -08:00
|
|
|
}
|
2015-07-05 23:56:01 -07:00
|
|
|
|
|
|
|
const char *mode = "";
|
|
|
|
|
|
|
|
switch (audioConfig.mode) {
|
2019-06-22 22:13:45 -07:00
|
|
|
case AudioMode::Capture:
|
|
|
|
mode = "Capture";
|
|
|
|
break;
|
|
|
|
case AudioMode::DirectSound:
|
|
|
|
mode = "DirectSound";
|
|
|
|
break;
|
|
|
|
case AudioMode::WaveOut:
|
|
|
|
mode = "WaveOut";
|
|
|
|
break;
|
2015-07-05 23:56:01 -07:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_INFO,
|
|
|
|
"\tsample rate: %d\n"
|
|
|
|
"\tchannels: %d\n"
|
|
|
|
"\taudio type: %s",
|
|
|
|
audioConfig.sampleRate, audioConfig.channels, mode);
|
2015-07-05 23:56:01 -07:00
|
|
|
return true;
|
2014-08-28 18:27:58 -07:00
|
|
|
}
|
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
void DShowInput::SetActive(bool active_)
|
|
|
|
{
|
|
|
|
obs_data_t *settings = obs_source_get_settings(source);
|
|
|
|
QueueAction(active_ ? Action::Activate : Action::Deactivate);
|
|
|
|
obs_data_set_bool(settings, "active", active_);
|
|
|
|
active = active_;
|
|
|
|
obs_data_release(settings);
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
inline enum video_colorspace
|
|
|
|
DShowInput::GetColorSpace(obs_data_t *settings) const
|
2015-02-11 12:51:30 -08:00
|
|
|
{
|
|
|
|
const char *space = obs_data_get_string(settings, COLOR_SPACE);
|
|
|
|
|
|
|
|
if (astrcmpi(space, "709") == 0)
|
|
|
|
return VIDEO_CS_709;
|
2020-09-06 16:19:20 -07:00
|
|
|
|
|
|
|
if (astrcmpi(space, "601") == 0)
|
2015-02-11 12:51:30 -08:00
|
|
|
return VIDEO_CS_601;
|
2020-09-06 16:19:20 -07:00
|
|
|
|
|
|
|
return VIDEO_CS_DEFAULT;
|
2015-02-11 12:51:30 -08:00
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
inline enum video_range_type
|
|
|
|
DShowInput::GetColorRange(obs_data_t *settings) const
|
2015-02-11 12:51:30 -08:00
|
|
|
{
|
|
|
|
const char *range = obs_data_get_string(settings, COLOR_RANGE);
|
|
|
|
|
2019-07-10 23:40:53 -07:00
|
|
|
if (astrcmpi(range, "full") == 0)
|
|
|
|
return VIDEO_RANGE_FULL;
|
|
|
|
if (astrcmpi(range, "partial") == 0)
|
|
|
|
return VIDEO_RANGE_PARTIAL;
|
|
|
|
return VIDEO_RANGE_DEFAULT;
|
2015-02-11 12:51:30 -08:00
|
|
|
}
|
|
|
|
|
2015-03-19 16:03:09 -07:00
|
|
|
inline bool DShowInput::Activate(obs_data_t *settings)
|
2014-08-28 18:27:58 -07:00
|
|
|
{
|
|
|
|
if (!device.ResetGraph())
|
2015-03-19 16:03:09 -07:00
|
|
|
return false;
|
2014-08-28 18:27:58 -07:00
|
|
|
|
|
|
|
if (!UpdateVideoConfig(settings)) {
|
|
|
|
blog(LOG_WARNING, "%s: Video configuration failed",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_source_get_name(source));
|
2015-03-19 16:03:09 -07:00
|
|
|
return false;
|
2014-08-28 18:27:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!UpdateAudioConfig(settings))
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_WARNING,
|
|
|
|
"%s: Audio configuration failed, ignoring "
|
|
|
|
"audio",
|
|
|
|
obs_source_get_name(source));
|
2014-08-28 18:27:58 -07:00
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
if (!device.ConnectFilters())
|
2015-03-19 16:03:09 -07:00
|
|
|
return false;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
if (device.Start() != Result::Success)
|
2015-03-19 16:03:09 -07:00
|
|
|
return false;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2019-09-14 17:04:22 -07:00
|
|
|
enum video_colorspace cs = GetColorSpace(settings);
|
|
|
|
range = GetColorRange(settings);
|
|
|
|
frame.range = range;
|
|
|
|
|
|
|
|
bool success = video_format_get_parameters(cs, range,
|
2019-06-22 22:13:45 -07:00
|
|
|
frame.color_matrix,
|
|
|
|
frame.color_range_min,
|
|
|
|
frame.color_range_max);
|
2015-02-11 12:51:30 -08:00
|
|
|
if (!success) {
|
2019-06-22 22:13:45 -07:00
|
|
|
blog(LOG_ERROR,
|
|
|
|
"Failed to get video format parameters for "
|
|
|
|
"video format %u",
|
|
|
|
cs);
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
2015-03-19 16:03:09 -07:00
|
|
|
|
|
|
|
return true;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
inline void DShowInput::Deactivate()
|
|
|
|
{
|
|
|
|
device.ResetGraph();
|
2019-04-25 13:32:32 -07:00
|
|
|
obs_source_output_video2(source, nullptr);
|
2015-01-03 20:50:57 -08:00
|
|
|
}
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static const char *GetDShowInputName(void *)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2014-07-09 22:12:57 -07:00
|
|
|
return TEXT_INPUT_NAME;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2020-09-07 16:11:01 -07:00
|
|
|
static void proc_activate(void *data, calldata_t *cd)
|
|
|
|
{
|
|
|
|
bool activate = calldata_bool(cd, "active");
|
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(data);
|
|
|
|
input->SetActive(activate);
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void *CreateDShowInput(obs_data_t *settings, obs_source_t *source)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2014-09-12 01:31:13 -07:00
|
|
|
DShowInput *dshow = nullptr;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-09-12 01:31:13 -07:00
|
|
|
try {
|
2015-01-03 20:50:57 -08:00
|
|
|
dshow = new DShowInput(source, settings);
|
2020-09-07 16:11:01 -07:00
|
|
|
proc_handler_t *ph = obs_source_get_proc_handler(source);
|
|
|
|
proc_handler_add(ph, "void activate(bool active)",
|
|
|
|
proc_activate, dshow);
|
2014-09-12 01:31:13 -07:00
|
|
|
} catch (const char *error) {
|
|
|
|
blog(LOG_ERROR, "Could not create device '%s': %s",
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_source_get_name(source), error);
|
2014-09-12 01:31:13 -07:00
|
|
|
}
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
return dshow;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DestroyDShowInput(void *data)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
delete reinterpret_cast<DShowInput *>(data);
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void UpdateDShowInput(void *data, obs_data_t *settings)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(data);
|
2015-01-03 20:50:57 -08:00
|
|
|
if (input->active)
|
2018-05-05 11:38:32 -07:00
|
|
|
input->QueueActivate(settings);
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void GetDShowDefaults(obs_data_t *settings)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2014-07-02 10:10:38 -07:00
|
|
|
obs_data_set_default_int(settings, FRAME_INTERVAL, FPS_MATCHING);
|
2014-05-30 03:29:48 -07:00
|
|
|
obs_data_set_default_int(settings, RES_TYPE, ResType_Preferred);
|
|
|
|
obs_data_set_default_int(settings, VIDEO_FORMAT, (int)VideoFormat::Any);
|
2015-03-19 08:44:27 -07:00
|
|
|
obs_data_set_default_bool(settings, "active", true);
|
2015-02-11 12:51:30 -08:00
|
|
|
obs_data_set_default_string(settings, COLOR_SPACE, "default");
|
2019-07-10 23:40:53 -07:00
|
|
|
obs_data_set_default_string(settings, COLOR_RANGE, "default");
|
2015-06-05 10:49:50 -07:00
|
|
|
obs_data_set_default_int(settings, AUDIO_OUTPUT_MODE,
|
2019-06-22 22:13:45 -07:00
|
|
|
(int)AudioMode::Capture);
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Resolution {
|
|
|
|
int cx, cy;
|
|
|
|
|
|
|
|
inline Resolution(int cx, int cy) : cx(cx), cy(cy) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
static void InsertResolution(vector<Resolution> &resolutions, int cx, int cy)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
int bestCY = 0;
|
|
|
|
size_t idx = 0;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
for (; idx < resolutions.size(); idx++) {
|
|
|
|
const Resolution &res = resolutions[idx];
|
|
|
|
if (res.cx > cx)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (res.cx == cx) {
|
|
|
|
if (res.cy == cy)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!bestCY)
|
|
|
|
bestCY = res.cy;
|
|
|
|
else if (res.cy > bestCY)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
resolutions.insert(resolutions.begin() + idx, Resolution(cx, cy));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void AddCap(vector<Resolution> &resolutions, const VideoInfo &cap)
|
|
|
|
{
|
|
|
|
InsertResolution(resolutions, cap.minCX, cap.minCY);
|
|
|
|
InsertResolution(resolutions, cap.maxCX, cap.maxCY);
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
#define MAKE_DSHOW_FPS(fps) (10000000LL / (fps))
|
|
|
|
#define MAKE_DSHOW_FRACTIONAL_FPS(den, num) ((num)*10000000LL / (den))
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 10:10:38 -07:00
|
|
|
static long long GetOBSFPS()
|
|
|
|
{
|
|
|
|
obs_video_info ovi;
|
|
|
|
if (!obs_get_video_info(&ovi))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return MAKE_DSHOW_FRACTIONAL_FPS(ovi.fps_num, ovi.fps_den);
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
struct FPSFormat {
|
|
|
|
const char *text;
|
2019-06-22 22:13:45 -07:00
|
|
|
long long interval;
|
2014-05-30 03:29:48 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static const FPSFormat validFPSFormats[] = {
|
2019-06-22 22:13:45 -07:00
|
|
|
{"60", MAKE_DSHOW_FPS(60)},
|
2014-05-30 03:29:48 -07:00
|
|
|
{"59.94 NTSC", MAKE_DSHOW_FRACTIONAL_FPS(60000, 1001)},
|
2019-06-22 22:13:45 -07:00
|
|
|
{"50", MAKE_DSHOW_FPS(50)},
|
|
|
|
{"48 film", MAKE_DSHOW_FRACTIONAL_FPS(48000, 1001)},
|
|
|
|
{"40", MAKE_DSHOW_FPS(40)},
|
|
|
|
{"30", MAKE_DSHOW_FPS(30)},
|
2014-05-30 03:29:48 -07:00
|
|
|
{"29.97 NTSC", MAKE_DSHOW_FRACTIONAL_FPS(30000, 1001)},
|
2019-06-22 22:13:45 -07:00
|
|
|
{"25", MAKE_DSHOW_FPS(25)},
|
|
|
|
{"24 film", MAKE_DSHOW_FRACTIONAL_FPS(24000, 1001)},
|
|
|
|
{"20", MAKE_DSHOW_FPS(20)},
|
|
|
|
{"15", MAKE_DSHOW_FPS(15)},
|
|
|
|
{"10", MAKE_DSHOW_FPS(10)},
|
|
|
|
{"5", MAKE_DSHOW_FPS(5)},
|
|
|
|
{"4", MAKE_DSHOW_FPS(4)},
|
|
|
|
{"3", MAKE_DSHOW_FPS(3)},
|
|
|
|
{"2", MAKE_DSHOW_FPS(2)},
|
|
|
|
{"1", MAKE_DSHOW_FPS(1)},
|
2014-05-30 03:29:48 -07:00
|
|
|
};
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool DeviceIntervalChanged(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
static bool TryResolution(VideoDevice &dev, string res)
|
|
|
|
{
|
|
|
|
int cx, cy;
|
|
|
|
if (!ConvertRes(cx, cy, res.c_str()))
|
2014-07-02 08:39:32 -07:00
|
|
|
return false;
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
return ResolutionAvailable(dev, cx, cy);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool SetResolution(obs_properties_t *props, obs_data_t *settings,
|
2019-06-22 22:13:45 -07:00
|
|
|
string res, bool autoselect = false)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2014-07-02 09:49:07 -07:00
|
|
|
if (autoselect)
|
|
|
|
obs_data_set_autoselect_string(settings, RESOLUTION,
|
2019-06-22 22:13:45 -07:00
|
|
|
res.c_str());
|
2014-07-02 09:49:07 -07:00
|
|
|
else
|
|
|
|
obs_data_unset_autoselect_value(settings, RESOLUTION);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
DeviceIntervalChanged(props, obs_properties_get(props, FRAME_INTERVAL),
|
2019-06-22 22:13:45 -07:00
|
|
|
settings);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
if (!autoselect)
|
2014-08-05 11:09:29 -07:00
|
|
|
obs_data_set_string(settings, LAST_RESOLUTION, res.c_str());
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
return true;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool DeviceResolutionChanged(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2014-07-02 09:49:07 -07:00
|
|
|
UNUSED_PARAMETER(p);
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
PropertiesData *data =
|
|
|
|
(PropertiesData *)obs_properties_get_param(props);
|
2014-07-02 09:49:07 -07:00
|
|
|
const char *id;
|
2014-05-30 03:29:48 -07:00
|
|
|
VideoDevice device;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
|
2014-08-05 11:09:29 -07:00
|
|
|
string res = obs_data_get_string(settings, RESOLUTION);
|
|
|
|
string last_res = obs_data_get_string(settings, LAST_RESOLUTION);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
if (!data->GetDevice(device, id))
|
2014-07-02 09:49:07 -07:00
|
|
|
return false;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
if (TryResolution(device, res))
|
|
|
|
return SetResolution(props, settings, res);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
if (TryResolution(device, last_res))
|
|
|
|
return SetResolution(props, settings, last_res, true);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
return false;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct VideoFormatName {
|
|
|
|
VideoFormat format;
|
2019-06-22 22:13:45 -07:00
|
|
|
const char *name;
|
2014-05-30 03:29:48 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static const VideoFormatName videoFormatNames[] = {
|
2014-07-02 10:07:48 -07:00
|
|
|
/* autoselect format*/
|
2019-06-22 22:13:45 -07:00
|
|
|
{VideoFormat::Any, "VideoFormat.Any"},
|
2014-07-02 10:07:48 -07:00
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
/* raw formats */
|
2019-06-22 22:13:45 -07:00
|
|
|
{VideoFormat::ARGB, "ARGB"},
|
|
|
|
{VideoFormat::XRGB, "XRGB"},
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
/* planar YUV formats */
|
2019-06-22 22:13:45 -07:00
|
|
|
{VideoFormat::I420, "I420"},
|
|
|
|
{VideoFormat::NV12, "NV12"},
|
|
|
|
{VideoFormat::YV12, "YV12"},
|
|
|
|
{VideoFormat::Y800, "Y800"},
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
/* packed YUV formats */
|
2019-06-22 22:13:45 -07:00
|
|
|
{VideoFormat::YVYU, "YVYU"},
|
|
|
|
{VideoFormat::YUY2, "YUY2"},
|
|
|
|
{VideoFormat::UYVY, "UYVY"},
|
|
|
|
{VideoFormat::HDYC, "HDYC"},
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
/* encoded formats */
|
|
|
|
{VideoFormat::MJPEG, "MJPEG"},
|
2019-06-22 22:13:45 -07:00
|
|
|
{VideoFormat::H264, "H264"}};
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool ResTypeChanged(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings);
|
2014-07-02 09:49:07 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static size_t AddDevice(obs_property_t *device_list, const string &id)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2014-07-02 09:49:07 -07:00
|
|
|
DStr name, path;
|
2014-12-18 15:33:35 -08:00
|
|
|
if (!DecodeDeviceDStr(name, path, id.c_str()))
|
2014-07-02 09:49:07 -07:00
|
|
|
return numeric_limits<size_t>::max();
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
return obs_property_list_add_string(device_list, name, id.c_str());
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool UpdateDeviceList(obs_property_t *list, const string &id)
|
2014-07-02 09:49:07 -07:00
|
|
|
{
|
|
|
|
size_t size = obs_property_list_item_count(list);
|
|
|
|
bool found = false;
|
|
|
|
bool disabled_unknown_found = false;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
|
|
if (obs_property_list_item_string(list, i) == id) {
|
|
|
|
found = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (obs_property_list_item_disabled(list, i))
|
|
|
|
disabled_unknown_found = true;
|
|
|
|
}
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
if (!found && !disabled_unknown_found) {
|
|
|
|
size_t idx = AddDevice(list, id);
|
|
|
|
obs_property_list_item_disable(list, idx, true);
|
|
|
|
return true;
|
|
|
|
}
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
if (found && !disabled_unknown_found)
|
|
|
|
return false;
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
for (size_t i = 0; i < size;) {
|
|
|
|
if (obs_property_list_item_disabled(list, i)) {
|
|
|
|
obs_property_list_item_remove(list, i);
|
|
|
|
continue;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
2014-07-02 09:49:07 -07:00
|
|
|
i += 1;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
return true;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool DeviceSelectionChanged(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
PropertiesData *data =
|
|
|
|
(PropertiesData *)obs_properties_get_param(props);
|
2014-05-30 03:29:48 -07:00
|
|
|
VideoDevice device;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
string id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
|
2014-08-05 11:09:29 -07:00
|
|
|
string old_id = obs_data_get_string(settings, LAST_VIDEO_DEV_ID);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
bool device_list_updated = UpdateDeviceList(p, id);
|
|
|
|
|
|
|
|
if (!data->GetDevice(device, id.c_str()))
|
|
|
|
return !device_list_updated;
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
vector<Resolution> resolutions;
|
|
|
|
for (const VideoInfo &cap : device.caps)
|
|
|
|
AddCap(resolutions, cap);
|
|
|
|
|
|
|
|
p = obs_properties_get(props, RESOLUTION);
|
|
|
|
obs_property_list_clear(p);
|
|
|
|
|
|
|
|
for (size_t idx = resolutions.size(); idx > 0; idx--) {
|
2019-06-22 22:13:45 -07:00
|
|
|
const Resolution &res = resolutions[idx - 1];
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
string strRes;
|
|
|
|
strRes += to_string(res.cx);
|
|
|
|
strRes += "x";
|
|
|
|
strRes += to_string(res.cy);
|
|
|
|
|
|
|
|
obs_property_list_add_string(p, strRes.c_str(), strRes.c_str());
|
|
|
|
}
|
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
/* only refresh properties if device legitimately changed */
|
|
|
|
if (!id.size() || !old_id.size() || id != old_id) {
|
|
|
|
p = obs_properties_get(props, RES_TYPE);
|
|
|
|
ResTypeChanged(props, p, settings);
|
2014-08-05 11:09:29 -07:00
|
|
|
obs_data_set_string(settings, LAST_VIDEO_DEV_ID, id.c_str());
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool VideoConfigClicked(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
void *data)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(data);
|
2014-09-12 01:31:13 -07:00
|
|
|
input->QueueAction(Action::ConfigVideo);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(props);
|
|
|
|
UNUSED_PARAMETER(p);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
/*static bool AudioConfigClicked(obs_properties_t *props, obs_property_t *p,
|
2014-05-30 03:29:48 -07:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
DShowInput *input = reinterpret_cast<DShowInput*>(data);
|
2014-09-12 01:31:13 -07:00
|
|
|
input->QueueAction(Action::ConfigAudio);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(props);
|
|
|
|
UNUSED_PARAMETER(p);
|
|
|
|
return false;
|
2014-06-25 01:53:41 -07:00
|
|
|
}*/
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool CrossbarConfigClicked(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
void *data)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(data);
|
2014-09-12 01:31:13 -07:00
|
|
|
input->QueueAction(Action::ConfigCrossbar1);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(props);
|
|
|
|
UNUSED_PARAMETER(p);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
/*static bool Crossbar2ConfigClicked(obs_properties_t *props, obs_property_t *p,
|
2014-05-30 03:29:48 -07:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
DShowInput *input = reinterpret_cast<DShowInput*>(data);
|
2014-09-12 01:31:13 -07:00
|
|
|
input->QueueAction(Action::ConfigCrossbar2);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
UNUSED_PARAMETER(props);
|
|
|
|
UNUSED_PARAMETER(p);
|
|
|
|
return false;
|
2014-06-25 01:53:41 -07:00
|
|
|
}*/
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool AddDevice(obs_property_t *device_list, const VideoDevice &device)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
|
|
|
DStr name, path, device_id;
|
|
|
|
|
|
|
|
dstr_from_wcs(name, device.name.c_str());
|
|
|
|
dstr_from_wcs(path, device.path.c_str());
|
|
|
|
|
|
|
|
encode_dstr(path);
|
|
|
|
|
|
|
|
dstr_copy_dstr(device_id, name);
|
|
|
|
encode_dstr(device_id);
|
|
|
|
dstr_cat(device_id, ":");
|
|
|
|
dstr_cat_dstr(device_id, path);
|
|
|
|
|
|
|
|
obs_property_list_add_string(device_list, name, device_id);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool AddAudioDevice(obs_property_t *device_list,
|
2019-06-22 22:13:45 -07:00
|
|
|
const AudioDevice &device)
|
2014-08-28 18:27:58 -07:00
|
|
|
{
|
|
|
|
DStr name, path, device_id;
|
|
|
|
|
|
|
|
dstr_from_wcs(name, device.name.c_str());
|
|
|
|
dstr_from_wcs(path, device.path.c_str());
|
|
|
|
|
|
|
|
encode_dstr(path);
|
|
|
|
|
|
|
|
dstr_copy_dstr(device_id, name);
|
|
|
|
encode_dstr(device_id);
|
|
|
|
dstr_cat(device_id, ":");
|
|
|
|
dstr_cat_dstr(device_id, path);
|
|
|
|
|
|
|
|
obs_property_list_add_string(device_list, name, device_id);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
static void PropertiesDataDestroy(void *data)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
delete reinterpret_cast<PropertiesData *>(data);
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool ResTypeChanged(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
int val = (int)obs_data_get_int(settings, RES_TYPE);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
bool enabled = (val != ResType_Preferred);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
p = obs_properties_get(props, RESOLUTION);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
obs_property_set_enabled(p, enabled);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
p = obs_properties_get(props, FRAME_INTERVAL);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
obs_property_set_enabled(p, enabled);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
p = obs_properties_get(props, VIDEO_FORMAT);
|
|
|
|
obs_property_set_enabled(p, enabled);
|
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
if (val == ResType_Custom) {
|
|
|
|
p = obs_properties_get(props, RESOLUTION);
|
|
|
|
DeviceResolutionChanged(props, p, settings);
|
|
|
|
} else {
|
|
|
|
obs_data_unset_autoselect_value(settings, FRAME_INTERVAL);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
}
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-07-02 09:32:26 -07:00
|
|
|
static DStr GetFPSName(long long interval)
|
|
|
|
{
|
|
|
|
DStr name;
|
|
|
|
|
2014-07-02 10:10:38 -07:00
|
|
|
if (interval == FPS_MATCHING) {
|
2014-07-09 22:12:57 -07:00
|
|
|
dstr_cat(name, TEXT_FPS_MATCHING);
|
2014-07-02 10:10:38 -07:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interval == FPS_HIGHEST) {
|
2014-07-09 22:12:57 -07:00
|
|
|
dstr_cat(name, TEXT_FPS_HIGHEST);
|
2014-07-02 10:10:38 -07:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2014-07-02 09:32:26 -07:00
|
|
|
for (const FPSFormat &format : validFPSFormats) {
|
|
|
|
if (format.interval != interval)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
dstr_cat(name, format.text);
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
dstr_cat(name, to_string(10000000. / interval).c_str());
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
static void UpdateFPS(VideoDevice &device, VideoFormat format,
|
2019-06-22 22:13:45 -07:00
|
|
|
long long interval, int cx, int cy,
|
|
|
|
obs_properties_t *props)
|
2014-07-02 09:49:07 -07:00
|
|
|
{
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_property_t *list = obs_properties_get(props, FRAME_INTERVAL);
|
2014-07-02 09:49:07 -07:00
|
|
|
|
|
|
|
obs_property_list_clear(list);
|
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
obs_property_list_add_int(list, TEXT_FPS_MATCHING, FPS_MATCHING);
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_property_list_add_int(list, TEXT_FPS_HIGHEST, FPS_HIGHEST);
|
2014-07-02 10:10:38 -07:00
|
|
|
|
|
|
|
bool interval_added = interval == FPS_HIGHEST ||
|
2019-06-22 22:13:45 -07:00
|
|
|
interval == FPS_MATCHING;
|
2014-07-02 09:49:07 -07:00
|
|
|
for (const FPSFormat &fps_format : validFPSFormats) {
|
|
|
|
bool video_format_match = false;
|
|
|
|
long long format_interval = fps_format.interval;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
bool available = CapsMatch(
|
|
|
|
device, ResolutionMatcher(cx, cy),
|
|
|
|
VideoFormatMatcher(format, video_format_match),
|
|
|
|
FrameRateMatcher(format_interval));
|
2014-07-02 09:49:07 -07:00
|
|
|
|
|
|
|
if (!available && interval != fps_format.interval)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (interval == fps_format.interval)
|
|
|
|
interval_added = true;
|
|
|
|
|
|
|
|
size_t idx = obs_property_list_add_int(list, fps_format.text,
|
2019-06-22 22:13:45 -07:00
|
|
|
fps_format.interval);
|
2014-07-02 09:49:07 -07:00
|
|
|
obs_property_list_item_disable(list, idx, !available);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (interval_added)
|
|
|
|
return;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
size_t idx =
|
|
|
|
obs_property_list_add_int(list, GetFPSName(interval), interval);
|
2014-07-02 09:49:07 -07:00
|
|
|
obs_property_list_item_disable(list, idx, true);
|
|
|
|
}
|
|
|
|
|
2014-07-02 09:32:26 -07:00
|
|
|
static DStr GetVideoFormatName(VideoFormat format)
|
|
|
|
{
|
|
|
|
DStr name;
|
|
|
|
for (const VideoFormatName &format_ : videoFormatNames) {
|
|
|
|
if (format_.format == format) {
|
2014-07-09 22:12:57 -07:00
|
|
|
dstr_cat(name, obs_module_text(format_.name));
|
2014-07-02 09:32:26 -07:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
dstr_cat(name, TEXT_FORMAT_UNKNOWN);
|
|
|
|
dstr_replace(name, "%1", std::to_string((long long)format).c_str());
|
2014-07-02 09:32:26 -07:00
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static void UpdateVideoFormats(VideoDevice &device, VideoFormat format_, int cx,
|
|
|
|
int cy, long long interval,
|
|
|
|
obs_properties_t *props)
|
2014-07-02 09:49:07 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
set<VideoFormat> formats = {VideoFormat::Any};
|
|
|
|
auto format_gatherer =
|
|
|
|
[&formats](const VideoInfo &info) mutable -> bool {
|
2014-07-02 09:49:07 -07:00
|
|
|
formats.insert(info.format);
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
CapsMatch(device, ResolutionMatcher(cx, cy), FrameRateMatcher(interval),
|
|
|
|
format_gatherer);
|
2014-07-02 09:49:07 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_property_t *list = obs_properties_get(props, VIDEO_FORMAT);
|
2014-07-02 09:49:07 -07:00
|
|
|
obs_property_list_clear(list);
|
|
|
|
|
|
|
|
bool format_added = false;
|
|
|
|
for (const VideoFormatName &format : videoFormatNames) {
|
|
|
|
bool available = formats.find(format.format) != end(formats);
|
|
|
|
|
|
|
|
if (!available && format.format != format_)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (format.format == format_)
|
|
|
|
format_added = true;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
size_t idx = obs_property_list_add_int(
|
|
|
|
list, obs_module_text(format.name),
|
|
|
|
(long long)format.format);
|
2014-07-02 09:49:07 -07:00
|
|
|
obs_property_list_item_disable(list, idx, !available);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (format_added)
|
|
|
|
return;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
size_t idx = obs_property_list_add_int(
|
|
|
|
list, GetVideoFormatName(format_), (long long)format_);
|
2014-07-02 09:49:07 -07:00
|
|
|
obs_property_list_item_disable(list, idx, true);
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool UpdateFPS(long long interval, obs_property_t *list)
|
2014-07-02 09:49:07 -07:00
|
|
|
{
|
|
|
|
size_t size = obs_property_list_item_count(list);
|
|
|
|
DStr name;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
|
|
if (obs_property_list_item_int(list, i) != interval)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
obs_property_list_item_disable(list, i, true);
|
|
|
|
if (size == 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dstr_cat(name, obs_property_list_item_name(list, i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
obs_property_list_clear(list);
|
|
|
|
|
|
|
|
if (!name->len)
|
|
|
|
name = GetFPSName(interval);
|
|
|
|
|
|
|
|
obs_property_list_add_int(list, name, interval);
|
|
|
|
obs_property_list_item_disable(list, 0, true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool DeviceIntervalChanged(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings)
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
{
|
2014-08-05 11:09:29 -07:00
|
|
|
long long val = obs_data_get_int(settings, FRAME_INTERVAL);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
PropertiesData *data =
|
|
|
|
(PropertiesData *)obs_properties_get_param(props);
|
2014-08-05 11:09:29 -07:00
|
|
|
const char *id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
VideoDevice device;
|
|
|
|
|
|
|
|
if (!data->GetDevice(device, id))
|
2014-07-02 09:49:07 -07:00
|
|
|
return UpdateFPS(val, p);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
int cx = 0, cy = 0;
|
|
|
|
if (!DetermineResolution(cx, cy, settings, device)) {
|
|
|
|
UpdateVideoFormats(device, VideoFormat::Any, 0, 0, 0, props);
|
|
|
|
UpdateFPS(device, VideoFormat::Any, 0, 0, 0, props);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
return true;
|
2014-07-02 09:49:07 -07:00
|
|
|
}
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
int resType = (int)obs_data_get_int(settings, RES_TYPE);
|
2014-07-02 09:49:07 -07:00
|
|
|
if (resType != ResType_Custom)
|
|
|
|
return true;
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 10:10:38 -07:00
|
|
|
if (val == FPS_MATCHING)
|
|
|
|
val = GetOBSFPS();
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
VideoFormat format =
|
|
|
|
(VideoFormat)obs_data_get_int(settings, VIDEO_FORMAT);
|
2014-07-02 09:49:07 -07:00
|
|
|
|
|
|
|
bool video_format_matches = false;
|
|
|
|
long long best_interval = numeric_limits<long long>::max();
|
2019-06-22 22:13:45 -07:00
|
|
|
bool frameRateSupported =
|
|
|
|
CapsMatch(device, ResolutionMatcher(cx, cy),
|
|
|
|
VideoFormatMatcher(format, video_format_matches),
|
|
|
|
ClosestFrameRateSelector(val, best_interval),
|
|
|
|
FrameRateMatcher(val));
|
|
|
|
|
|
|
|
if (video_format_matches && !frameRateSupported &&
|
|
|
|
best_interval != val) {
|
2014-07-02 09:49:07 -07:00
|
|
|
long long listed_val = 0;
|
|
|
|
for (const FPSFormat &format : validFPSFormats) {
|
|
|
|
long long diff = llabs(format.interval - best_interval);
|
|
|
|
if (diff < DEVICE_INTERVAL_DIFF_LIMIT) {
|
|
|
|
listed_val = format.interval;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2015-04-09 18:09:22 -07:00
|
|
|
if (listed_val != val) {
|
2014-07-02 09:49:07 -07:00
|
|
|
obs_data_set_autoselect_int(settings, FRAME_INTERVAL,
|
2019-06-22 22:13:45 -07:00
|
|
|
listed_val);
|
2015-04-09 18:09:22 -07:00
|
|
|
val = listed_val;
|
|
|
|
}
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
} else {
|
|
|
|
obs_data_unset_autoselect_value(settings, FRAME_INTERVAL);
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
}
|
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
UpdateVideoFormats(device, format, cx, cy, val, props);
|
|
|
|
UpdateFPS(device, format, val, cx, cy, props);
|
|
|
|
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
UNUSED_PARAMETER(p);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool UpdateVideoFormats(VideoFormat format, obs_property_t *list)
|
2014-07-02 09:49:07 -07:00
|
|
|
{
|
|
|
|
size_t size = obs_property_list_item_count(list);
|
|
|
|
DStr name;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
|
|
if ((VideoFormat)obs_property_list_item_int(list, i) != format)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (size == 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dstr_cat(name, obs_property_list_item_name(list, i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
obs_property_list_clear(list);
|
|
|
|
|
|
|
|
if (!name->len)
|
|
|
|
name = GetVideoFormatName(format);
|
|
|
|
|
|
|
|
obs_property_list_add_int(list, name, (long long)format);
|
|
|
|
obs_property_list_item_disable(list, 0, true);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool VideoFormatChanged(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings)
|
2014-07-02 09:49:07 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
PropertiesData *data =
|
|
|
|
(PropertiesData *)obs_properties_get_param(props);
|
2014-08-05 11:09:29 -07:00
|
|
|
const char *id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
|
2014-07-02 09:49:07 -07:00
|
|
|
VideoDevice device;
|
|
|
|
|
|
|
|
VideoFormat curFormat =
|
2014-08-05 11:09:29 -07:00
|
|
|
(VideoFormat)obs_data_get_int(settings, VIDEO_FORMAT);
|
2014-07-02 09:49:07 -07:00
|
|
|
|
|
|
|
if (!data->GetDevice(device, id))
|
|
|
|
return UpdateVideoFormats(curFormat, p);
|
|
|
|
|
|
|
|
int cx, cy;
|
|
|
|
if (!DetermineResolution(cx, cy, settings, device)) {
|
|
|
|
UpdateVideoFormats(device, VideoFormat::Any, cx, cy, 0, props);
|
|
|
|
UpdateFPS(device, VideoFormat::Any, 0, 0, 0, props);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
long long interval = obs_data_get_int(settings, FRAME_INTERVAL);
|
2014-07-02 09:49:07 -07:00
|
|
|
|
|
|
|
UpdateVideoFormats(device, curFormat, cx, cy, interval, props);
|
|
|
|
UpdateFPS(device, curFormat, interval, cx, cy, props);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool CustomAudioClicked(obs_properties_t *props, obs_property_t *p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_data_t *settings)
|
2014-08-28 18:27:58 -07:00
|
|
|
{
|
|
|
|
bool useCustomAudio = obs_data_get_bool(settings, USE_CUSTOM_AUDIO);
|
|
|
|
p = obs_properties_get(props, AUDIO_DEVICE_ID);
|
|
|
|
obs_property_set_visible(p, useCustomAudio);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static bool ActivateClicked(obs_properties_t *, obs_property_t *p, void *data)
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(data);
|
2015-01-03 20:50:57 -08:00
|
|
|
|
|
|
|
if (input->active) {
|
|
|
|
input->SetActive(false);
|
|
|
|
obs_property_set_description(p, TEXT_ACTIVATE);
|
|
|
|
} else {
|
|
|
|
input->SetActive(true);
|
|
|
|
obs_property_set_description(p, TEXT_DEACTIVATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static obs_properties_t *GetDShowProperties(void *obj)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(obj);
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_properties_t *ppts = obs_properties_create();
|
2014-05-30 03:29:48 -07:00
|
|
|
PropertiesData *data = new PropertiesData;
|
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
data->input = input;
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
obs_properties_set_param(ppts, data, PropertiesDataDestroy);
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_property_t *p = obs_properties_add_list(ppts, VIDEO_DEVICE_ID,
|
|
|
|
TEXT_DEVICE,
|
|
|
|
OBS_COMBO_TYPE_LIST,
|
|
|
|
OBS_COMBO_FORMAT_STRING);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
obs_property_set_modified_callback(p, DeviceSelectionChanged);
|
|
|
|
|
|
|
|
Device::EnumVideoDevices(data->devices);
|
|
|
|
for (const VideoDevice &device : data->devices)
|
|
|
|
AddDevice(p, device);
|
|
|
|
|
2015-01-03 20:50:57 -08:00
|
|
|
const char *activateText = TEXT_ACTIVATE;
|
|
|
|
if (input) {
|
|
|
|
if (input->active)
|
|
|
|
activateText = TEXT_DEACTIVATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
obs_properties_add_button(ppts, "activate", activateText,
|
2019-06-22 22:13:45 -07:00
|
|
|
ActivateClicked);
|
2014-07-09 22:12:57 -07:00
|
|
|
obs_properties_add_button(ppts, "video_config", TEXT_CONFIG_VIDEO,
|
2019-06-22 22:13:45 -07:00
|
|
|
VideoConfigClicked);
|
2014-07-09 22:12:57 -07:00
|
|
|
obs_properties_add_button(ppts, "xbar_config", TEXT_CONFIG_XBAR,
|
2019-06-22 22:13:45 -07:00
|
|
|
CrossbarConfigClicked);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2016-04-03 15:55:15 -07:00
|
|
|
obs_properties_add_bool(ppts, DEACTIVATE_WNS, TEXT_DWNS);
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
/* ------------------------------------- */
|
2014-08-28 18:27:58 -07:00
|
|
|
/* video settings */
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
p = obs_properties_add_list(ppts, RES_TYPE, TEXT_RES_FPS_TYPE,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
obs_property_set_modified_callback(p, ResTypeChanged);
|
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
obs_property_list_add_int(p, TEXT_PREFERRED_RES, ResType_Preferred);
|
|
|
|
obs_property_list_add_int(p, TEXT_CUSTOM_RES, ResType_Custom);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
p = obs_properties_add_list(ppts, RESOLUTION, TEXT_RESOLUTION,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_EDITABLE,
|
|
|
|
OBS_COMBO_FORMAT_STRING);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
|
|
|
obs_property_set_modified_callback(p, DeviceResolutionChanged);
|
|
|
|
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
p = obs_properties_add_list(ppts, FRAME_INTERVAL, "FPS",
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration
capabilities can change if you so much as look at it the wrong way.
Previously, configuring devices often didn't configure the device
settings correctly, you would choose one setting and then another
setting wouldn't be compatible with that settings.
Let's take the terrible microsoft lifecam series for example. First,
you'd be at 640x480 happily webcam'ing away, which is using the YUY2
format. Then you decide "hey, this webcam resolution is a bit low. I
would love to have it a bit high resolution so it's a bit more crisp and
clear." You'd select 1280x720, and then suddenly the only format
supported is MJPEG output. However, the interface has to update that
fact, it can't list YUY2 if MJPEG is the only one available for this
resolution. This doesn't just apply to formats either, this applies to
framerates and such as well. Some framerates will only be supported by
certain resolutions which can in turn only be supported by certain
formats.
This causes user interface for configuration to be really be a nightmare
to manage if you want these features to be available to the user. It's
extremely annoying because you have to update all the configuration UI
after something has changed, double check the configuration values, and
if the values aren't supported, update those configuration values.
2014-05-30 19:47:51 -07:00
|
|
|
obs_property_set_modified_callback(p, DeviceIntervalChanged);
|
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
p = obs_properties_add_list(ppts, VIDEO_FORMAT, TEXT_VIDEO_FORMAT,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
2014-05-30 03:29:48 -07:00
|
|
|
|
2014-07-02 09:49:07 -07:00
|
|
|
obs_property_set_modified_callback(p, VideoFormatChanged);
|
|
|
|
|
2015-02-11 12:51:30 -08:00
|
|
|
p = obs_properties_add_list(ppts, COLOR_SPACE, TEXT_COLOR_SPACE,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST,
|
|
|
|
OBS_COMBO_FORMAT_STRING);
|
2015-02-11 12:51:30 -08:00
|
|
|
obs_property_list_add_string(p, TEXT_COLOR_DEFAULT, "default");
|
|
|
|
obs_property_list_add_string(p, "709", "709");
|
|
|
|
obs_property_list_add_string(p, "601", "601");
|
|
|
|
|
|
|
|
p = obs_properties_add_list(ppts, COLOR_RANGE, TEXT_COLOR_RANGE,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST,
|
|
|
|
OBS_COMBO_FORMAT_STRING);
|
2019-07-10 23:40:53 -07:00
|
|
|
obs_property_list_add_string(p, TEXT_RANGE_DEFAULT, "default");
|
2015-02-11 12:51:30 -08:00
|
|
|
obs_property_list_add_string(p, TEXT_RANGE_PARTIAL, "partial");
|
|
|
|
obs_property_list_add_string(p, TEXT_RANGE_FULL, "full");
|
|
|
|
|
2014-12-31 21:59:46 -08:00
|
|
|
p = obs_properties_add_list(ppts, BUFFERING_VAL, TEXT_BUFFERING,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
2014-12-31 21:59:46 -08:00
|
|
|
obs_property_list_add_int(p, TEXT_BUFFERING_AUTO,
|
2019-06-22 22:13:45 -07:00
|
|
|
(int64_t)BufferingType::Auto);
|
2014-12-31 21:59:46 -08:00
|
|
|
obs_property_list_add_int(p, TEXT_BUFFERING_ON,
|
2019-06-22 22:13:45 -07:00
|
|
|
(int64_t)BufferingType::On);
|
2014-12-31 21:59:46 -08:00
|
|
|
obs_property_list_add_int(p, TEXT_BUFFERING_OFF,
|
2019-06-22 22:13:45 -07:00
|
|
|
(int64_t)BufferingType::Off);
|
2014-12-31 21:59:46 -08:00
|
|
|
|
2016-09-11 03:15:09 -07:00
|
|
|
obs_property_set_long_description(p,
|
2019-06-22 22:13:45 -07:00
|
|
|
obs_module_text("Buffering.ToolTip"));
|
2016-09-11 03:15:09 -07:00
|
|
|
|
2015-04-13 15:01:20 -07:00
|
|
|
obs_properties_add_bool(ppts, FLIP_IMAGE, TEXT_FLIP_IMAGE);
|
|
|
|
|
2014-08-28 18:27:58 -07:00
|
|
|
/* ------------------------------------- */
|
|
|
|
/* audio settings */
|
|
|
|
|
|
|
|
Device::EnumAudioDevices(data->audioDevices);
|
|
|
|
|
2015-06-05 10:49:50 -07:00
|
|
|
p = obs_properties_add_list(ppts, AUDIO_OUTPUT_MODE, TEXT_AUDIO_MODE,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
2015-06-05 10:49:50 -07:00
|
|
|
obs_property_list_add_int(p, TEXT_MODE_CAPTURE,
|
2019-06-22 22:13:45 -07:00
|
|
|
(int64_t)AudioMode::Capture);
|
2015-06-05 10:49:50 -07:00
|
|
|
obs_property_list_add_int(p, TEXT_MODE_DSOUND,
|
2019-06-22 22:13:45 -07:00
|
|
|
(int64_t)AudioMode::DirectSound);
|
2015-06-05 10:49:50 -07:00
|
|
|
obs_property_list_add_int(p, TEXT_MODE_WAVEOUT,
|
2019-06-22 22:13:45 -07:00
|
|
|
(int64_t)AudioMode::WaveOut);
|
2015-06-05 10:49:50 -07:00
|
|
|
|
2016-06-04 08:27:28 -07:00
|
|
|
if (!data->audioDevices.size())
|
|
|
|
return ppts;
|
|
|
|
|
2014-08-28 18:27:58 -07:00
|
|
|
p = obs_properties_add_bool(ppts, USE_CUSTOM_AUDIO, TEXT_CUSTOM_AUDIO);
|
|
|
|
|
|
|
|
obs_property_set_modified_callback(p, CustomAudioClicked);
|
|
|
|
|
|
|
|
p = obs_properties_add_list(ppts, AUDIO_DEVICE_ID, TEXT_AUDIO_DEVICE,
|
2019-06-22 22:13:45 -07:00
|
|
|
OBS_COMBO_TYPE_LIST,
|
|
|
|
OBS_COMBO_FORMAT_STRING);
|
2014-08-28 18:27:58 -07:00
|
|
|
|
|
|
|
for (const AudioDevice &device : data->audioDevices)
|
|
|
|
AddAudioDevice(p, device);
|
|
|
|
|
2014-05-30 03:29:48 -07:00
|
|
|
return ppts;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DShowModuleLogCallback(LogType type, const wchar_t *msg, void *param)
|
|
|
|
{
|
|
|
|
int obs_type = LOG_DEBUG;
|
|
|
|
|
|
|
|
switch (type) {
|
2019-06-22 22:13:45 -07:00
|
|
|
case LogType::Error:
|
|
|
|
obs_type = LOG_ERROR;
|
|
|
|
break;
|
|
|
|
case LogType::Warning:
|
|
|
|
obs_type = LOG_WARNING;
|
|
|
|
break;
|
|
|
|
case LogType::Info:
|
|
|
|
obs_type = LOG_INFO;
|
|
|
|
break;
|
|
|
|
case LogType::Debug:
|
|
|
|
obs_type = LOG_DEBUG;
|
|
|
|
break;
|
2014-05-30 03:29:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
DStr dmsg;
|
|
|
|
|
|
|
|
dstr_from_wcs(dmsg, msg);
|
|
|
|
blog(obs_type, "DShow: %s", dmsg->array);
|
|
|
|
|
|
|
|
UNUSED_PARAMETER(param);
|
|
|
|
}
|
|
|
|
|
2016-04-03 15:55:15 -07:00
|
|
|
static void HideDShowInput(void *data)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(data);
|
2016-04-03 15:55:15 -07:00
|
|
|
|
|
|
|
if (input->deactivateWhenNotShowing && input->active)
|
|
|
|
input->QueueAction(Action::Deactivate);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ShowDShowInput(void *data)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
DShowInput *input = reinterpret_cast<DShowInput *>(data);
|
2016-04-03 15:55:15 -07:00
|
|
|
|
|
|
|
if (input->deactivateWhenNotShowing && input->active)
|
|
|
|
input->QueueAction(Action::Activate);
|
|
|
|
}
|
|
|
|
|
2014-12-19 07:53:15 -08:00
|
|
|
void RegisterDShowSource()
|
2014-05-30 03:29:48 -07:00
|
|
|
{
|
|
|
|
SetLogCallback(DShowModuleLogCallback, nullptr);
|
|
|
|
|
|
|
|
obs_source_info info = {};
|
2019-06-22 22:13:45 -07:00
|
|
|
info.id = "dshow_input";
|
|
|
|
info.type = OBS_SOURCE_TYPE_INPUT;
|
|
|
|
info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_AUDIO |
|
|
|
|
OBS_SOURCE_ASYNC | OBS_SOURCE_DO_NOT_DUPLICATE;
|
|
|
|
info.show = ShowDShowInput;
|
|
|
|
info.hide = HideDShowInput;
|
|
|
|
info.get_name = GetDShowInputName;
|
|
|
|
info.create = CreateDShowInput;
|
|
|
|
info.destroy = DestroyDShowInput;
|
|
|
|
info.update = UpdateDShowInput;
|
|
|
|
info.get_defaults = GetDShowDefaults;
|
|
|
|
info.get_properties = GetDShowProperties;
|
2019-07-27 21:59:16 -07:00
|
|
|
info.icon_type = OBS_ICON_TYPE_CAMERA;
|
2014-05-30 03:29:48 -07:00
|
|
|
obs_register_source(&info);
|
|
|
|
}
|