win-dshow.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113
  1. #include <objbase.h>
  2. #include <obs-module.h>
  3. #include <obs.hpp>
  4. #include <util/dstr.hpp>
  5. #include <util/platform.h>
  6. #include <util/windows/WinHandle.hpp>
  7. #include <util/threading.h>
  8. #include "libdshowcapture/dshowcapture.hpp"
  9. #include "ffmpeg-decode.h"
  10. #include "encode-dstr.hpp"
  11. #include <algorithm>
  12. #include <limits>
  13. #include <set>
  14. #include <string>
  15. #include <vector>
  16. /*
  17. * TODO:
  18. * - handle disconnections and reconnections
  19. * - if device not present, wait for device to be plugged in
  20. */
  21. #undef min
  22. #undef max
  23. using namespace std;
  24. using namespace DShow;
  25. /* clang-format off */
  26. /* settings defines that will cause errors if there are typos */
  27. #define VIDEO_DEVICE_ID "video_device_id"
  28. #define RES_TYPE "res_type"
  29. #define RESOLUTION "resolution"
  30. #define FRAME_INTERVAL "frame_interval"
  31. #define VIDEO_FORMAT "video_format"
  32. #define LAST_VIDEO_DEV_ID "last_video_device_id"
  33. #define LAST_RESOLUTION "last_resolution"
  34. #define BUFFERING_VAL "buffering"
  35. #define FLIP_IMAGE "flip_vertically"
  36. #define AUDIO_OUTPUT_MODE "audio_output_mode"
  37. #define USE_CUSTOM_AUDIO "use_custom_audio_device"
  38. #define AUDIO_DEVICE_ID "audio_device_id"
  39. #define COLOR_SPACE "color_space"
  40. #define COLOR_RANGE "color_range"
  41. #define DEACTIVATE_WNS "deactivate_when_not_showing"
  42. #define AUTOROTATION "autorotation"
  43. #define HW_DECODE "hw_decode"
  44. #define TEXT_INPUT_NAME obs_module_text("VideoCaptureDevice")
  45. #define TEXT_DEVICE obs_module_text("Device")
  46. #define TEXT_CONFIG_VIDEO obs_module_text("ConfigureVideo")
  47. #define TEXT_CONFIG_XBAR obs_module_text("ConfigureCrossbar")
  48. #define TEXT_RES_FPS_TYPE obs_module_text("ResFPSType")
  49. #define TEXT_CUSTOM_RES obs_module_text("ResFPSType.Custom")
  50. #define TEXT_PREFERRED_RES obs_module_text("ResFPSType.DevPreferred")
  51. #define TEXT_FPS_MATCHING obs_module_text("FPS.Matching")
  52. #define TEXT_FPS_HIGHEST obs_module_text("FPS.Highest")
  53. #define TEXT_RESOLUTION obs_module_text("Resolution")
  54. #define TEXT_VIDEO_FORMAT obs_module_text("VideoFormat")
  55. #define TEXT_FORMAT_UNKNOWN obs_module_text("VideoFormat.Unknown")
  56. #define TEXT_BUFFERING obs_module_text("Buffering")
  57. #define TEXT_BUFFERING_AUTO obs_module_text("Buffering.AutoDetect")
  58. #define TEXT_BUFFERING_ON obs_module_text("Buffering.Enable")
  59. #define TEXT_BUFFERING_OFF obs_module_text("Buffering.Disable")
  60. #define TEXT_FLIP_IMAGE obs_module_text("FlipVertically")
  61. #define TEXT_AUTOROTATION obs_module_text("Autorotation")
  62. #define TEXT_HW_DECODE obs_module_text("HardwareDecode")
  63. #define TEXT_AUDIO_MODE obs_module_text("AudioOutputMode")
  64. #define TEXT_MODE_CAPTURE obs_module_text("AudioOutputMode.Capture")
  65. #define TEXT_MODE_DSOUND obs_module_text("AudioOutputMode.DirectSound")
  66. #define TEXT_MODE_WAVEOUT obs_module_text("AudioOutputMode.WaveOut")
  67. #define TEXT_CUSTOM_AUDIO obs_module_text("UseCustomAudioDevice")
  68. #define TEXT_AUDIO_DEVICE obs_module_text("AudioDevice")
  69. #define TEXT_ACTIVATE obs_module_text("Activate")
  70. #define TEXT_DEACTIVATE obs_module_text("Deactivate")
  71. #define TEXT_COLOR_SPACE obs_module_text("ColorSpace")
  72. #define TEXT_COLOR_DEFAULT obs_module_text("ColorSpace.Default")
  73. #define TEXT_COLOR_709 obs_module_text("ColorSpace.709")
  74. #define TEXT_COLOR_601 obs_module_text("ColorSpace.601")
  75. #define TEXT_COLOR_2100PQ obs_module_text("ColorSpace.2100PQ")
  76. #define TEXT_COLOR_2100HLG obs_module_text("ColorSpace.2100HLG")
  77. #define TEXT_COLOR_RANGE obs_module_text("ColorRange")
  78. #define TEXT_RANGE_DEFAULT obs_module_text("ColorRange.Default")
  79. #define TEXT_RANGE_PARTIAL obs_module_text("ColorRange.Partial")
  80. #define TEXT_RANGE_FULL obs_module_text("ColorRange.Full")
  81. #define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing")
  82. /* clang-format on */
  83. enum ResType {
  84. ResType_Preferred,
  85. ResType_Custom,
  86. };
  87. enum class BufferingType : int64_t {
  88. Auto,
  89. On,
  90. Off,
  91. };
  92. void ffmpeg_log(void *bla, int level, const char *msg, va_list args)
  93. {
  94. DStr str;
  95. if (level == AV_LOG_WARNING) {
  96. dstr_copy(str, "warning: ");
  97. } else if (level == AV_LOG_ERROR) {
  98. /* only print first of this message to avoid spam */
  99. static bool suppress_app_field_spam = false;
  100. if (strcmp(msg, "unable to decode APP fields: %s\n") == 0) {
  101. if (suppress_app_field_spam)
  102. return;
  103. suppress_app_field_spam = true;
  104. }
  105. dstr_copy(str, "error: ");
  106. } else if (level < AV_LOG_ERROR) {
  107. dstr_copy(str, "fatal: ");
  108. } else {
  109. return;
  110. }
  111. dstr_cat(str, msg);
  112. if (dstr_end(str) == '\n')
  113. dstr_resize(str, str->len - 1);
  114. blogva(LOG_WARNING, str, args);
  115. av_log_default_callback(bla, level, msg, args);
  116. }
  117. class Decoder {
  118. struct ffmpeg_decode decode;
  119. public:
  120. inline Decoder() { memset(&decode, 0, sizeof(decode)); }
  121. inline ~Decoder() { ffmpeg_decode_free(&decode); }
  122. inline operator ffmpeg_decode *() { return &decode; }
  123. inline ffmpeg_decode *operator->() { return &decode; }
  124. };
  125. class CriticalSection {
  126. CRITICAL_SECTION mutex;
  127. public:
  128. inline CriticalSection() { InitializeCriticalSection(&mutex); }
  129. inline ~CriticalSection() { DeleteCriticalSection(&mutex); }
  130. inline operator CRITICAL_SECTION *() { return &mutex; }
  131. };
  132. class CriticalScope {
  133. CriticalSection &mutex;
  134. CriticalScope() = delete;
  135. CriticalScope &operator=(CriticalScope &cs) = delete;
  136. public:
  137. inline CriticalScope(CriticalSection &mutex_) : mutex(mutex_)
  138. {
  139. EnterCriticalSection(mutex);
  140. }
  141. inline ~CriticalScope() { LeaveCriticalSection(mutex); }
  142. };
  143. enum class Action {
  144. None,
  145. Activate,
  146. ActivateBlock,
  147. Deactivate,
  148. Shutdown,
  149. ConfigVideo,
  150. ConfigAudio,
  151. ConfigCrossbar1,
  152. ConfigCrossbar2,
  153. };
  154. static DWORD CALLBACK DShowThread(LPVOID ptr);
  155. struct DShowInput {
  156. obs_source_t *source;
  157. Device device;
  158. bool deactivateWhenNotShowing = false;
  159. bool deviceHasAudio = false;
  160. bool deviceHasSeparateAudioFilter = false;
  161. bool flip = false;
  162. bool active = false;
  163. bool autorotation = true;
  164. bool hw_decode = false;
  165. Decoder audio_decoder;
  166. Decoder video_decoder;
  167. VideoConfig videoConfig;
  168. AudioConfig audioConfig;
  169. enum video_colorspace cs;
  170. obs_source_frame2 frame;
  171. obs_source_audio audio;
  172. long lastRotation = 0;
  173. WinHandle semaphore;
  174. WinHandle activated_event;
  175. WinHandle thread;
  176. CriticalSection mutex;
  177. vector<Action> actions;
  178. inline void QueueAction(Action action)
  179. {
  180. CriticalScope scope(mutex);
  181. actions.push_back(action);
  182. ReleaseSemaphore(semaphore, 1, nullptr);
  183. }
  184. inline void QueueActivate(obs_data_t *settings)
  185. {
  186. bool block =
  187. obs_data_get_bool(settings, "synchronous_activate");
  188. QueueAction(block ? Action::ActivateBlock : Action::Activate);
  189. if (block) {
  190. obs_data_erase(settings, "synchronous_activate");
  191. WaitForSingleObject(activated_event, INFINITE);
  192. }
  193. }
  194. inline DShowInput(obs_source_t *source_, obs_data_t *settings)
  195. : source(source_), device(InitGraph::False)
  196. {
  197. memset(&audio, 0, sizeof(audio));
  198. memset(&frame, 0, sizeof(frame));
  199. av_log_set_level(AV_LOG_WARNING);
  200. av_log_set_callback(ffmpeg_log);
  201. semaphore = CreateSemaphore(nullptr, 0, 0x7FFFFFFF, nullptr);
  202. if (!semaphore)
  203. throw "Failed to create semaphore";
  204. activated_event = CreateEvent(nullptr, false, false, nullptr);
  205. if (!activated_event)
  206. throw "Failed to create activated_event";
  207. thread =
  208. CreateThread(nullptr, 0, DShowThread, this, 0, nullptr);
  209. if (!thread)
  210. throw "Failed to create thread";
  211. deactivateWhenNotShowing =
  212. obs_data_get_bool(settings, DEACTIVATE_WNS);
  213. if (obs_data_get_bool(settings, "active")) {
  214. bool showing = obs_source_showing(source);
  215. if (!deactivateWhenNotShowing || showing)
  216. QueueActivate(settings);
  217. active = true;
  218. }
  219. }
  220. inline ~DShowInput()
  221. {
  222. {
  223. CriticalScope scope(mutex);
  224. actions.resize(1);
  225. actions[0] = Action::Shutdown;
  226. }
  227. ReleaseSemaphore(semaphore, 1, nullptr);
  228. WaitForSingleObject(thread, INFINITE);
  229. }
  230. void OnEncodedVideoData(enum AVCodecID id, unsigned char *data,
  231. size_t size, long long ts);
  232. void OnEncodedAudioData(enum AVCodecID id, unsigned char *data,
  233. size_t size, long long ts);
  234. void OnReactivate();
  235. void OnVideoData(const VideoConfig &config, unsigned char *data,
  236. size_t size, long long startTime, long long endTime,
  237. long rotation);
  238. void OnAudioData(const AudioConfig &config, unsigned char *data,
  239. size_t size, long long startTime, long long endTime);
  240. bool UpdateVideoConfig(obs_data_t *settings);
  241. bool UpdateAudioConfig(obs_data_t *settings);
  242. void SetActive(bool active);
  243. inline enum video_colorspace GetColorSpace(obs_data_t *settings) const;
  244. inline enum video_range_type GetColorRange(obs_data_t *settings) const;
  245. inline bool Activate(obs_data_t *settings);
  246. inline void Deactivate();
  247. inline void SetupBuffering(obs_data_t *settings);
  248. void DShowLoop();
  249. };
  250. static DWORD CALLBACK DShowThread(LPVOID ptr)
  251. {
  252. DShowInput *dshowInput = (DShowInput *)ptr;
  253. os_set_thread_name("win-dshow: DShowThread");
  254. CoInitialize(nullptr);
  255. dshowInput->DShowLoop();
  256. CoUninitialize();
  257. return 0;
  258. }
  259. static inline void ProcessMessages()
  260. {
  261. MSG msg;
  262. while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
  263. TranslateMessage(&msg);
  264. DispatchMessage(&msg);
  265. }
  266. }
  267. /* Always keep directshow in a single thread for a given device */
  268. void DShowInput::DShowLoop()
  269. {
  270. while (true) {
  271. DWORD ret = MsgWaitForMultipleObjects(1, &semaphore, false,
  272. INFINITE, QS_ALLINPUT);
  273. if (ret == (WAIT_OBJECT_0 + 1)) {
  274. ProcessMessages();
  275. continue;
  276. } else if (ret != WAIT_OBJECT_0) {
  277. break;
  278. }
  279. Action action = Action::None;
  280. {
  281. CriticalScope scope(mutex);
  282. if (actions.size()) {
  283. action = actions.front();
  284. actions.erase(actions.begin());
  285. }
  286. }
  287. switch (action) {
  288. case Action::Activate:
  289. case Action::ActivateBlock: {
  290. bool block = action == Action::ActivateBlock;
  291. obs_data_t *settings;
  292. settings = obs_source_get_settings(source);
  293. if (!Activate(settings)) {
  294. obs_source_output_video2(source, nullptr);
  295. }
  296. if (block)
  297. SetEvent(activated_event);
  298. obs_data_release(settings);
  299. break;
  300. }
  301. case Action::Deactivate:
  302. Deactivate();
  303. break;
  304. case Action::Shutdown:
  305. device.ShutdownGraph();
  306. return;
  307. case Action::ConfigVideo:
  308. device.OpenDialog(nullptr, DialogType::ConfigVideo);
  309. break;
  310. case Action::ConfigAudio:
  311. device.OpenDialog(nullptr, DialogType::ConfigAudio);
  312. break;
  313. case Action::ConfigCrossbar1:
  314. device.OpenDialog(nullptr, DialogType::ConfigCrossbar);
  315. break;
  316. case Action::ConfigCrossbar2:
  317. device.OpenDialog(nullptr, DialogType::ConfigCrossbar2);
  318. break;
  319. case Action::None:;
  320. }
  321. }
  322. }
  323. #define FPS_HIGHEST 0LL
  324. #define FPS_MATCHING -1LL
  325. template<typename T, typename U, typename V>
  326. static bool between(T &&lower, U &&value, V &&upper)
  327. {
  328. return value >= lower && value <= upper;
  329. }
  330. static bool ResolutionAvailable(const VideoInfo &cap, int cx, int cy)
  331. {
  332. return between(cap.minCX, cx, cap.maxCX) &&
  333. between(cap.minCY, cy, cap.maxCY);
  334. }
  335. #define DEVICE_INTERVAL_DIFF_LIMIT 20
  336. static bool FrameRateAvailable(const VideoInfo &cap, long long interval)
  337. {
  338. return interval == FPS_HIGHEST || interval == FPS_MATCHING ||
  339. between(cap.minInterval - DEVICE_INTERVAL_DIFF_LIMIT, interval,
  340. cap.maxInterval + DEVICE_INTERVAL_DIFF_LIMIT);
  341. }
  342. static long long FrameRateInterval(const VideoInfo &cap,
  343. long long desired_interval)
  344. {
  345. return desired_interval < cap.minInterval
  346. ? cap.minInterval
  347. : min(desired_interval, cap.maxInterval);
  348. }
  349. static inline video_format ConvertVideoFormat(VideoFormat format)
  350. {
  351. switch (format) {
  352. case VideoFormat::ARGB:
  353. return VIDEO_FORMAT_BGRA;
  354. case VideoFormat::XRGB:
  355. return VIDEO_FORMAT_BGRX;
  356. case VideoFormat::I420:
  357. return VIDEO_FORMAT_I420;
  358. case VideoFormat::YV12:
  359. return VIDEO_FORMAT_I420;
  360. case VideoFormat::NV12:
  361. return VIDEO_FORMAT_NV12;
  362. case VideoFormat::Y800:
  363. return VIDEO_FORMAT_Y800;
  364. case VideoFormat::YVYU:
  365. return VIDEO_FORMAT_YVYU;
  366. case VideoFormat::YUY2:
  367. return VIDEO_FORMAT_YUY2;
  368. case VideoFormat::UYVY:
  369. return VIDEO_FORMAT_UYVY;
  370. case VideoFormat::HDYC:
  371. return VIDEO_FORMAT_UYVY;
  372. case VideoFormat::P010:
  373. return VIDEO_FORMAT_P010;
  374. default:
  375. return VIDEO_FORMAT_NONE;
  376. }
  377. }
  378. static inline audio_format ConvertAudioFormat(AudioFormat format)
  379. {
  380. switch (format) {
  381. case AudioFormat::Wave16bit:
  382. return AUDIO_FORMAT_16BIT;
  383. case AudioFormat::WaveFloat:
  384. return AUDIO_FORMAT_FLOAT;
  385. default:
  386. return AUDIO_FORMAT_UNKNOWN;
  387. }
  388. }
  389. static inline enum speaker_layout convert_speaker_layout(uint8_t channels)
  390. {
  391. switch (channels) {
  392. case 0:
  393. return SPEAKERS_UNKNOWN;
  394. case 1:
  395. return SPEAKERS_MONO;
  396. case 2:
  397. return SPEAKERS_STEREO;
  398. case 3:
  399. return SPEAKERS_2POINT1;
  400. case 4:
  401. return SPEAKERS_4POINT0;
  402. case 5:
  403. return SPEAKERS_4POINT1;
  404. case 6:
  405. return SPEAKERS_5POINT1;
  406. case 8:
  407. return SPEAKERS_7POINT1;
  408. default:
  409. return SPEAKERS_UNKNOWN;
  410. }
  411. }
  412. //#define LOG_ENCODED_VIDEO_TS 1
  413. //#define LOG_ENCODED_AUDIO_TS 1
  414. void DShowInput::OnEncodedVideoData(enum AVCodecID id, unsigned char *data,
  415. size_t size, long long ts)
  416. {
  417. /* If format or hw decode changes, recreate the decoder */
  418. if (ffmpeg_decode_valid(video_decoder) &&
  419. ((video_decoder->codec->id != id) ||
  420. (video_decoder->hw != hw_decode))) {
  421. ffmpeg_decode_free(video_decoder);
  422. }
  423. if (!ffmpeg_decode_valid(video_decoder)) {
  424. if (ffmpeg_decode_init(video_decoder, id, hw_decode) < 0) {
  425. blog(LOG_WARNING, "Could not initialize video decoder");
  426. return;
  427. }
  428. }
  429. bool got_output;
  430. bool success = ffmpeg_decode_video(video_decoder, data, size, &ts, cs,
  431. frame.range, &frame, &got_output);
  432. if (!success) {
  433. blog(LOG_WARNING, "Error decoding video");
  434. return;
  435. }
  436. if (got_output) {
  437. frame.timestamp = (uint64_t)ts * 100;
  438. if (flip)
  439. frame.flip = !frame.flip;
  440. #if LOG_ENCODED_VIDEO_TS
  441. blog(LOG_DEBUG, "video ts: %llu", frame.timestamp);
  442. #endif
  443. obs_source_output_video2(source, &frame);
  444. }
  445. }
  446. void DShowInput::OnReactivate()
  447. {
  448. SetActive(true);
  449. }
  450. void DShowInput::OnVideoData(const VideoConfig &config, unsigned char *data,
  451. size_t size, long long startTime,
  452. long long endTime, long rotation)
  453. {
  454. if (autorotation && rotation != lastRotation) {
  455. lastRotation = rotation;
  456. obs_source_set_async_rotation(source, rotation);
  457. }
  458. if (videoConfig.format == VideoFormat::H264) {
  459. OnEncodedVideoData(AV_CODEC_ID_H264, data, size, startTime);
  460. return;
  461. }
  462. #ifdef ENABLE_HEVC
  463. if (videoConfig.format == VideoFormat::HEVC) {
  464. OnEncodedVideoData(AV_CODEC_ID_HEVC, data, size, startTime);
  465. return;
  466. }
  467. #endif
  468. if (videoConfig.format == VideoFormat::MJPEG) {
  469. OnEncodedVideoData(AV_CODEC_ID_MJPEG, data, size, startTime);
  470. return;
  471. }
  472. const int cx = config.cx;
  473. const int cy_abs = config.cy_abs;
  474. frame.timestamp = (uint64_t)startTime * 100;
  475. frame.width = config.cx;
  476. frame.height = cy_abs;
  477. frame.format = ConvertVideoFormat(config.format);
  478. frame.flip = flip;
  479. frame.flags = OBS_SOURCE_FRAME_LINEAR_ALPHA;
  480. /* YUV DIBS are always top-down */
  481. if (config.format == VideoFormat::XRGB ||
  482. config.format == VideoFormat::ARGB) {
  483. /* RGB DIBs are bottom-up by default */
  484. if (!config.cy_flip)
  485. frame.flip = !frame.flip;
  486. }
  487. if (videoConfig.format == VideoFormat::XRGB ||
  488. videoConfig.format == VideoFormat::ARGB) {
  489. frame.data[0] = data;
  490. frame.linesize[0] = cx * 4;
  491. } else if (videoConfig.format == VideoFormat::YVYU ||
  492. videoConfig.format == VideoFormat::YUY2 ||
  493. videoConfig.format == VideoFormat::HDYC ||
  494. videoConfig.format == VideoFormat::UYVY) {
  495. frame.data[0] = data;
  496. frame.linesize[0] = cx * 2;
  497. } else if (videoConfig.format == VideoFormat::I420) {
  498. frame.data[0] = data;
  499. frame.data[1] = frame.data[0] + (cx * cy_abs);
  500. frame.data[2] = frame.data[1] + (cx * cy_abs / 4);
  501. frame.linesize[0] = cx;
  502. frame.linesize[1] = cx / 2;
  503. frame.linesize[2] = cx / 2;
  504. } else if (videoConfig.format == VideoFormat::YV12) {
  505. frame.data[0] = data;
  506. frame.data[2] = frame.data[0] + (cx * cy_abs);
  507. frame.data[1] = frame.data[2] + (cx * cy_abs / 4);
  508. frame.linesize[0] = cx;
  509. frame.linesize[1] = cx / 2;
  510. frame.linesize[2] = cx / 2;
  511. } else if (videoConfig.format == VideoFormat::NV12) {
  512. frame.data[0] = data;
  513. frame.data[1] = frame.data[0] + (cx * cy_abs);
  514. frame.linesize[0] = cx;
  515. frame.linesize[1] = cx;
  516. } else if (videoConfig.format == VideoFormat::Y800) {
  517. frame.data[0] = data;
  518. frame.linesize[0] = cx;
  519. } else if (videoConfig.format == VideoFormat::P010) {
  520. frame.data[0] = data;
  521. frame.data[1] = frame.data[0] + (cx * cy_abs) * 2;
  522. frame.linesize[0] = cx * 2;
  523. frame.linesize[1] = cx * 2;
  524. } else {
  525. /* TODO: other formats */
  526. return;
  527. }
  528. obs_source_output_video2(source, &frame);
  529. UNUSED_PARAMETER(endTime); /* it's the enndd tiimmes! */
  530. }
  531. void DShowInput::OnEncodedAudioData(enum AVCodecID id, unsigned char *data,
  532. size_t size, long long ts)
  533. {
  534. if (!ffmpeg_decode_valid(audio_decoder)) {
  535. if (ffmpeg_decode_init(audio_decoder, id, false) < 0) {
  536. blog(LOG_WARNING, "Could not initialize audio decoder");
  537. return;
  538. }
  539. }
  540. bool got_output = false;
  541. do {
  542. bool success = ffmpeg_decode_audio(audio_decoder, data, size,
  543. &audio, &got_output);
  544. if (!success) {
  545. blog(LOG_WARNING, "Error decoding audio");
  546. return;
  547. }
  548. if (got_output) {
  549. audio.timestamp = (uint64_t)ts * 100;
  550. #if LOG_ENCODED_AUDIO_TS
  551. blog(LOG_DEBUG, "audio ts: %llu", audio.timestamp);
  552. #endif
  553. obs_source_output_audio(source, &audio);
  554. } else {
  555. break;
  556. }
  557. ts += int64_t(audio_decoder->frame->nb_samples) * 10000000LL /
  558. int64_t(audio_decoder->frame->sample_rate);
  559. size = 0;
  560. data = nullptr;
  561. } while (got_output);
  562. }
  563. void DShowInput::OnAudioData(const AudioConfig &config, unsigned char *data,
  564. size_t size, long long startTime,
  565. long long endTime)
  566. {
  567. size_t block_size;
  568. if (config.format == AudioFormat::AAC) {
  569. OnEncodedAudioData(AV_CODEC_ID_AAC, data, size, startTime);
  570. return;
  571. } else if (config.format == AudioFormat::AC3) {
  572. OnEncodedAudioData(AV_CODEC_ID_AC3, data, size, startTime);
  573. return;
  574. } else if (config.format == AudioFormat::MPGA) {
  575. OnEncodedAudioData(AV_CODEC_ID_MP2, data, size, startTime);
  576. return;
  577. }
  578. audio.speakers = convert_speaker_layout((uint8_t)config.channels);
  579. audio.format = ConvertAudioFormat(config.format);
  580. audio.samples_per_sec = (uint32_t)config.sampleRate;
  581. audio.data[0] = data;
  582. block_size = get_audio_bytes_per_channel(audio.format) *
  583. get_audio_channels(audio.speakers);
  584. audio.frames = (uint32_t)(size / block_size);
  585. audio.timestamp = (uint64_t)startTime * 100;
  586. if (audio.format != AUDIO_FORMAT_UNKNOWN)
  587. obs_source_output_audio(source, &audio);
  588. UNUSED_PARAMETER(endTime);
  589. }
  590. struct PropertiesData {
  591. DShowInput *input;
  592. vector<VideoDevice> devices;
  593. vector<AudioDevice> audioDevices;
  594. bool GetDevice(VideoDevice &device, const char *encoded_id) const
  595. {
  596. DeviceId deviceId;
  597. DecodeDeviceId(deviceId, encoded_id);
  598. for (const VideoDevice &curDevice : devices) {
  599. if (deviceId.name == curDevice.name &&
  600. deviceId.path == curDevice.path) {
  601. device = curDevice;
  602. return true;
  603. }
  604. }
  605. return false;
  606. }
  607. };
  608. static inline bool ConvertRes(int &cx, int &cy, const char *res)
  609. {
  610. return sscanf(res, "%dx%d", &cx, &cy) == 2;
  611. }
  612. static inline bool FormatMatches(VideoFormat left, VideoFormat right)
  613. {
  614. return left == VideoFormat::Any || right == VideoFormat::Any ||
  615. left == right;
  616. }
  617. static inline bool ResolutionValid(const string &res, int &cx, int &cy)
  618. {
  619. if (!res.size())
  620. return false;
  621. return ConvertRes(cx, cy, res.c_str());
  622. }
  623. static inline bool CapsMatch(const VideoInfo &)
  624. {
  625. return true;
  626. }
  627. template<typename... F> static bool CapsMatch(const VideoDevice &dev, F... fs);
  628. template<typename F, typename... Fs>
  629. static inline bool CapsMatch(const VideoInfo &info, F &&f, Fs... fs)
  630. {
  631. return f(info) && CapsMatch(info, fs...);
  632. }
  633. template<typename... F> static bool CapsMatch(const VideoDevice &dev, F... fs)
  634. {
  635. // no early exit, trigger all side effects.
  636. bool match = false;
  637. for (const VideoInfo &info : dev.caps)
  638. if (CapsMatch(info, fs...))
  639. match = true;
  640. return match;
  641. }
  642. static inline bool MatcherMatchVideoFormat(VideoFormat format, bool &did_match,
  643. const VideoInfo &info)
  644. {
  645. bool match = FormatMatches(format, info.format);
  646. did_match = did_match || match;
  647. return match;
  648. }
  649. static inline bool MatcherClosestFrameRateSelector(long long interval,
  650. long long &best_match,
  651. const VideoInfo &info)
  652. {
  653. long long current = FrameRateInterval(info, interval);
  654. if (llabs(interval - best_match) > llabs(interval - current))
  655. best_match = current;
  656. return true;
  657. }
  658. #if 0
  659. auto ResolutionMatcher = [](int cx, int cy)
  660. {
  661. return [cx, cy](const VideoInfo &info)
  662. {
  663. return ResolutionAvailable(info, cx, cy);
  664. };
  665. };
  666. auto FrameRateMatcher = [](long long interval)
  667. {
  668. return [interval](const VideoInfo &info)
  669. {
  670. return FrameRateAvailable(info, interval);
  671. };
  672. };
  673. auto VideoFormatMatcher = [](VideoFormat format, bool &did_match)
  674. {
  675. return [format, &did_match](const VideoInfo &info)
  676. {
  677. return MatcherMatchVideoFormat(format, did_match, info);
  678. };
  679. };
  680. auto ClosestFrameRateSelector = [](long long interval, long long &best_match)
  681. {
  682. return [interval, &best_match](const VideoInfo &info) mutable -> bool
  683. {
  684. MatcherClosestFrameRateSelector(interval, best_match, info);
  685. };
  686. }
  687. #else
  688. #define ResolutionMatcher(cx, cy) \
  689. [cx, cy](const VideoInfo &info) -> bool { \
  690. return ResolutionAvailable(info, cx, cy); \
  691. }
  692. #define FrameRateMatcher(interval) \
  693. [interval](const VideoInfo &info) -> bool { \
  694. return FrameRateAvailable(info, interval); \
  695. }
  696. #define VideoFormatMatcher(format, did_match) \
  697. [format, &did_match](const VideoInfo &info) mutable -> bool { \
  698. return MatcherMatchVideoFormat(format, did_match, info); \
  699. }
  700. #define ClosestFrameRateSelector(interval, best_match) \
  701. [interval, &best_match](const VideoInfo &info) mutable -> bool { \
  702. return MatcherClosestFrameRateSelector(interval, best_match, \
  703. info); \
  704. }
  705. #endif
  706. static bool ResolutionAvailable(const VideoDevice &dev, int cx, int cy)
  707. {
  708. return CapsMatch(dev, ResolutionMatcher(cx, cy));
  709. }
  710. static bool DetermineResolution(int &cx, int &cy, obs_data_t *settings,
  711. VideoDevice &dev)
  712. {
  713. const char *res = obs_data_get_autoselect_string(settings, RESOLUTION);
  714. if (obs_data_has_autoselect_value(settings, RESOLUTION) &&
  715. ConvertRes(cx, cy, res) && ResolutionAvailable(dev, cx, cy))
  716. return true;
  717. res = obs_data_get_string(settings, RESOLUTION);
  718. if (ConvertRes(cx, cy, res) && ResolutionAvailable(dev, cx, cy))
  719. return true;
  720. res = obs_data_get_string(settings, LAST_RESOLUTION);
  721. if (ConvertRes(cx, cy, res) && ResolutionAvailable(dev, cx, cy))
  722. return true;
  723. return false;
  724. }
  725. static long long GetOBSFPS();
  726. static inline bool IsDelayedDevice(const VideoConfig &config)
  727. {
  728. return config.format > VideoFormat::MJPEG ||
  729. (wstrstri(config.name.c_str(), L"elgato") != NULL &&
  730. wstrstri(config.name.c_str(), L"facecam") == NULL) ||
  731. wstrstri(config.name.c_str(), L"stream engine") != NULL;
  732. }
  733. static inline bool IsDecoupled(const VideoConfig &config)
  734. {
  735. return wstrstri(config.name.c_str(), L"GV-USB2") != NULL;
  736. }
  737. inline void DShowInput::SetupBuffering(obs_data_t *settings)
  738. {
  739. BufferingType bufType;
  740. bool useBuffering;
  741. bufType = (BufferingType)obs_data_get_int(settings, BUFFERING_VAL);
  742. if (bufType == BufferingType::Auto)
  743. useBuffering = IsDelayedDevice(videoConfig);
  744. else
  745. useBuffering = bufType == BufferingType::On;
  746. obs_source_set_async_unbuffered(source, !useBuffering);
  747. obs_source_set_async_decoupled(source, IsDecoupled(videoConfig));
  748. }
  749. static DStr GetVideoFormatName(VideoFormat format);
  750. bool DShowInput::UpdateVideoConfig(obs_data_t *settings)
  751. {
  752. string video_device_id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
  753. deactivateWhenNotShowing = obs_data_get_bool(settings, DEACTIVATE_WNS);
  754. flip = obs_data_get_bool(settings, FLIP_IMAGE);
  755. autorotation = obs_data_get_bool(settings, AUTOROTATION);
  756. hw_decode = obs_data_get_bool(settings, HW_DECODE);
  757. DeviceId id;
  758. if (!DecodeDeviceId(id, video_device_id.c_str())) {
  759. blog(LOG_WARNING, "%s: DecodeDeviceId failed",
  760. obs_source_get_name(source));
  761. return false;
  762. }
  763. PropertiesData data;
  764. Device::EnumVideoDevices(data.devices);
  765. VideoDevice dev;
  766. if (!data.GetDevice(dev, video_device_id.c_str())) {
  767. blog(LOG_WARNING, "%s: data.GetDevice failed",
  768. obs_source_get_name(source));
  769. return false;
  770. }
  771. int resType = (int)obs_data_get_int(settings, RES_TYPE);
  772. int cx = 0, cy = 0;
  773. long long interval = 0;
  774. VideoFormat format = VideoFormat::Any;
  775. if (resType == ResType_Custom) {
  776. bool has_autosel_val;
  777. string resolution = obs_data_get_string(settings, RESOLUTION);
  778. if (!ResolutionValid(resolution, cx, cy)) {
  779. blog(LOG_WARNING, "%s: ResolutionValid failed",
  780. obs_source_get_name(source));
  781. return false;
  782. }
  783. has_autosel_val =
  784. obs_data_has_autoselect_value(settings, FRAME_INTERVAL);
  785. interval = has_autosel_val
  786. ? obs_data_get_autoselect_int(settings,
  787. FRAME_INTERVAL)
  788. : obs_data_get_int(settings, FRAME_INTERVAL);
  789. if (interval == FPS_MATCHING)
  790. interval = GetOBSFPS();
  791. format = (VideoFormat)obs_data_get_int(settings, VIDEO_FORMAT);
  792. long long best_interval = numeric_limits<long long>::max();
  793. bool video_format_match = false;
  794. bool caps_match = CapsMatch(
  795. dev, ResolutionMatcher(cx, cy),
  796. VideoFormatMatcher(format, video_format_match),
  797. ClosestFrameRateSelector(interval, best_interval),
  798. FrameRateMatcher(interval));
  799. if (!caps_match && !video_format_match) {
  800. blog(LOG_WARNING, "%s: Video format match failed",
  801. obs_source_get_name(source));
  802. return false;
  803. }
  804. interval = best_interval;
  805. }
  806. videoConfig.name = id.name;
  807. videoConfig.path = id.path;
  808. videoConfig.useDefaultConfig = resType == ResType_Preferred;
  809. videoConfig.cx = cx;
  810. videoConfig.cy_abs = abs(cy);
  811. videoConfig.cy_flip = cy < 0;
  812. videoConfig.frameInterval = interval;
  813. videoConfig.internalFormat = format;
  814. deviceHasAudio = dev.audioAttached;
  815. deviceHasSeparateAudioFilter = dev.separateAudioFilter;
  816. videoConfig.callback = std::bind(&DShowInput::OnVideoData, this,
  817. placeholders::_1, placeholders::_2,
  818. placeholders::_3, placeholders::_4,
  819. placeholders::_5, placeholders::_6);
  820. videoConfig.reactivateCallback =
  821. std::bind(&DShowInput::OnReactivate, this);
  822. videoConfig.format = videoConfig.internalFormat;
  823. if (!device.SetVideoConfig(&videoConfig)) {
  824. blog(LOG_WARNING, "%s: device.SetVideoConfig failed",
  825. obs_source_get_name(source));
  826. return false;
  827. }
  828. DStr formatName = GetVideoFormatName(videoConfig.internalFormat);
  829. double fps = 0.0;
  830. if (videoConfig.frameInterval)
  831. fps = 10000000.0 / double(videoConfig.frameInterval);
  832. BPtr<char> name_utf8;
  833. BPtr<char> path_utf8;
  834. os_wcs_to_utf8_ptr(videoConfig.name.c_str(), videoConfig.name.size(),
  835. &name_utf8);
  836. os_wcs_to_utf8_ptr(videoConfig.path.c_str(), videoConfig.path.size(),
  837. &path_utf8);
  838. SetupBuffering(settings);
  839. blog(LOG_INFO, "---------------------------------");
  840. blog(LOG_INFO,
  841. "[DShow Device: '%s'] settings updated: \n"
  842. "\tvideo device: %s\n"
  843. "\tvideo path: %s\n"
  844. "\tresolution: %dx%d\n"
  845. "\tflip: %d\n"
  846. "\tfps: %0.2f (interval: %lld)\n"
  847. "\tformat: %s\n"
  848. "\tbuffering: %s\n"
  849. "\thardware decode: %s",
  850. obs_source_get_name(source), (const char *)name_utf8,
  851. (const char *)path_utf8, videoConfig.cx, videoConfig.cy_abs,
  852. (int)videoConfig.cy_flip, fps, videoConfig.frameInterval,
  853. formatName->array,
  854. obs_source_async_unbuffered(source) ? "disabled" : "enabled",
  855. hw_decode ? "enabled" : "disabled");
  856. return true;
  857. }
  858. bool DShowInput::UpdateAudioConfig(obs_data_t *settings)
  859. {
  860. string audio_device_id = obs_data_get_string(settings, AUDIO_DEVICE_ID);
  861. bool useCustomAudio = obs_data_get_bool(settings, USE_CUSTOM_AUDIO);
  862. if (useCustomAudio) {
  863. DeviceId id;
  864. if (!DecodeDeviceId(id, audio_device_id.c_str()))
  865. return false;
  866. audioConfig.name = id.name;
  867. audioConfig.path = id.path;
  868. } else if (!deviceHasAudio) {
  869. return true;
  870. }
  871. audioConfig.useVideoDevice = !useCustomAudio &&
  872. !deviceHasSeparateAudioFilter;
  873. audioConfig.useSeparateAudioFilter = deviceHasSeparateAudioFilter;
  874. audioConfig.callback = std::bind(&DShowInput::OnAudioData, this,
  875. placeholders::_1, placeholders::_2,
  876. placeholders::_3, placeholders::_4,
  877. placeholders::_5);
  878. audioConfig.mode =
  879. (AudioMode)obs_data_get_int(settings, AUDIO_OUTPUT_MODE);
  880. bool success = device.SetAudioConfig(&audioConfig);
  881. if (!success)
  882. return false;
  883. BPtr<char> name_utf8;
  884. os_wcs_to_utf8_ptr(audioConfig.name.c_str(), audioConfig.name.size(),
  885. &name_utf8);
  886. blog(LOG_INFO, "\tusing video device audio: %s",
  887. audioConfig.useVideoDevice ? "yes" : "no");
  888. if (!audioConfig.useVideoDevice) {
  889. if (audioConfig.useSeparateAudioFilter)
  890. blog(LOG_INFO, "\tseparate audio filter");
  891. else
  892. blog(LOG_INFO, "\taudio device: %s",
  893. (const char *)name_utf8);
  894. }
  895. const char *mode = "";
  896. switch (audioConfig.mode) {
  897. case AudioMode::Capture:
  898. mode = "Capture";
  899. break;
  900. case AudioMode::DirectSound:
  901. mode = "DirectSound";
  902. break;
  903. case AudioMode::WaveOut:
  904. mode = "WaveOut";
  905. break;
  906. }
  907. blog(LOG_INFO,
  908. "\tsample rate: %d\n"
  909. "\tchannels: %d\n"
  910. "\taudio type: %s",
  911. audioConfig.sampleRate, audioConfig.channels, mode);
  912. return true;
  913. }
  914. void DShowInput::SetActive(bool active_)
  915. {
  916. obs_data_t *settings = obs_source_get_settings(source);
  917. QueueAction(active_ ? Action::Activate : Action::Deactivate);
  918. obs_data_set_bool(settings, "active", active_);
  919. active = active_;
  920. obs_data_release(settings);
  921. }
  922. inline enum video_colorspace
  923. DShowInput::GetColorSpace(obs_data_t *settings) const
  924. {
  925. const char *space = obs_data_get_string(settings, COLOR_SPACE);
  926. if (astrcmpi(space, "709") == 0)
  927. return VIDEO_CS_709;
  928. if (astrcmpi(space, "601") == 0)
  929. return VIDEO_CS_601;
  930. if (astrcmpi(space, "2100PQ") == 0)
  931. return VIDEO_CS_2100_PQ;
  932. if (astrcmpi(space, "2100HLG") == 0)
  933. return VIDEO_CS_2100_HLG;
  934. if (videoConfig.format == VideoFormat::P010)
  935. return VIDEO_CS_2100_PQ;
  936. return VIDEO_CS_DEFAULT;
  937. }
  938. inline enum video_range_type
  939. DShowInput::GetColorRange(obs_data_t *settings) const
  940. {
  941. const char *range = obs_data_get_string(settings, COLOR_RANGE);
  942. if (astrcmpi(range, "full") == 0)
  943. return VIDEO_RANGE_FULL;
  944. if (astrcmpi(range, "partial") == 0)
  945. return VIDEO_RANGE_PARTIAL;
  946. return VIDEO_RANGE_DEFAULT;
  947. }
  948. inline bool DShowInput::Activate(obs_data_t *settings)
  949. {
  950. if (!device.ResetGraph())
  951. return false;
  952. if (!UpdateVideoConfig(settings)) {
  953. blog(LOG_WARNING, "%s: Video configuration failed",
  954. obs_source_get_name(source));
  955. return false;
  956. }
  957. if (!UpdateAudioConfig(settings))
  958. blog(LOG_WARNING,
  959. "%s: Audio configuration failed, ignoring "
  960. "audio",
  961. obs_source_get_name(source));
  962. if (!device.ConnectFilters())
  963. return false;
  964. if (device.Start() != Result::Success)
  965. return false;
  966. cs = GetColorSpace(settings);
  967. const enum video_range_type range = GetColorRange(settings);
  968. enum video_trc trc = VIDEO_TRC_DEFAULT;
  969. switch (cs) {
  970. case VIDEO_CS_DEFAULT:
  971. case VIDEO_CS_601:
  972. case VIDEO_CS_709:
  973. case VIDEO_CS_SRGB:
  974. trc = VIDEO_TRC_SRGB;
  975. break;
  976. case VIDEO_CS_2100_PQ:
  977. trc = VIDEO_TRC_PQ;
  978. break;
  979. case VIDEO_CS_2100_HLG:
  980. trc = VIDEO_TRC_HLG;
  981. }
  982. frame.range = range;
  983. frame.trc = trc;
  984. bool success = video_format_get_parameters_for_format(
  985. cs, range, ConvertVideoFormat(videoConfig.format),
  986. frame.color_matrix, frame.color_range_min,
  987. frame.color_range_max);
  988. if (!success) {
  989. blog(LOG_ERROR,
  990. "Failed to get video format parameters for "
  991. "video format %u",
  992. cs);
  993. }
  994. return true;
  995. }
  996. inline void DShowInput::Deactivate()
  997. {
  998. device.ResetGraph();
  999. obs_source_output_video2(source, nullptr);
  1000. }
  1001. /* ------------------------------------------------------------------------- */
  1002. static const char *GetDShowInputName(void *)
  1003. {
  1004. return TEXT_INPUT_NAME;
  1005. }
  1006. static void proc_activate(void *data, calldata_t *cd)
  1007. {
  1008. bool activate = calldata_bool(cd, "active");
  1009. DShowInput *input = reinterpret_cast<DShowInput *>(data);
  1010. input->SetActive(activate);
  1011. }
  1012. static void *CreateDShowInput(obs_data_t *settings, obs_source_t *source)
  1013. {
  1014. DShowInput *dshow = nullptr;
  1015. try {
  1016. dshow = new DShowInput(source, settings);
  1017. proc_handler_t *ph = obs_source_get_proc_handler(source);
  1018. proc_handler_add(ph, "void activate(bool active)",
  1019. proc_activate, dshow);
  1020. } catch (const char *error) {
  1021. blog(LOG_ERROR, "Could not create device '%s': %s",
  1022. obs_source_get_name(source), error);
  1023. }
  1024. return dshow;
  1025. }
  1026. static void DestroyDShowInput(void *data)
  1027. {
  1028. delete reinterpret_cast<DShowInput *>(data);
  1029. }
  1030. static void UpdateDShowInput(void *data, obs_data_t *settings)
  1031. {
  1032. DShowInput *input = reinterpret_cast<DShowInput *>(data);
  1033. if (input->active)
  1034. input->QueueActivate(settings);
  1035. }
  1036. static void GetDShowDefaults(obs_data_t *settings)
  1037. {
  1038. obs_data_set_default_int(settings, FRAME_INTERVAL, FPS_MATCHING);
  1039. obs_data_set_default_int(settings, RES_TYPE, ResType_Preferred);
  1040. obs_data_set_default_int(settings, VIDEO_FORMAT, (int)VideoFormat::Any);
  1041. obs_data_set_default_bool(settings, "active", true);
  1042. obs_data_set_default_string(settings, COLOR_SPACE, "default");
  1043. obs_data_set_default_string(settings, COLOR_RANGE, "default");
  1044. obs_data_set_default_int(settings, AUDIO_OUTPUT_MODE,
  1045. (int)AudioMode::Capture);
  1046. obs_data_set_default_bool(settings, AUTOROTATION, true);
  1047. obs_data_set_default_bool(settings, HW_DECODE, false);
  1048. }
  1049. struct Resolution {
  1050. int cx, cy;
  1051. inline Resolution(int cx, int cy) : cx(cx), cy(cy) {}
  1052. };
  1053. static void InsertResolution(vector<Resolution> &resolutions, int cx, int cy)
  1054. {
  1055. int bestCY = 0;
  1056. size_t idx = 0;
  1057. for (; idx < resolutions.size(); idx++) {
  1058. const Resolution &res = resolutions[idx];
  1059. if (res.cx > cx)
  1060. break;
  1061. if (res.cx == cx) {
  1062. if (res.cy == cy)
  1063. return;
  1064. if (!bestCY)
  1065. bestCY = res.cy;
  1066. else if (res.cy > bestCY)
  1067. break;
  1068. }
  1069. }
  1070. resolutions.insert(resolutions.begin() + idx, Resolution(cx, cy));
  1071. }
  1072. static inline void AddCap(vector<Resolution> &resolutions, const VideoInfo &cap)
  1073. {
  1074. InsertResolution(resolutions, cap.minCX, cap.minCY);
  1075. InsertResolution(resolutions, cap.maxCX, cap.maxCY);
  1076. }
  1077. #define MAKE_DSHOW_FPS(fps) (10000000LL / (fps))
  1078. #define MAKE_DSHOW_FRACTIONAL_FPS(den, num) ((num)*10000000LL / (den))
  1079. static long long GetOBSFPS()
  1080. {
  1081. obs_video_info ovi;
  1082. if (!obs_get_video_info(&ovi))
  1083. return 0;
  1084. return MAKE_DSHOW_FRACTIONAL_FPS(ovi.fps_num, ovi.fps_den);
  1085. }
  1086. struct FPSFormat {
  1087. const char *text;
  1088. long long interval;
  1089. };
  1090. static const FPSFormat validFPSFormats[] = {
  1091. {"360", MAKE_DSHOW_FPS(360)},
  1092. {"240", MAKE_DSHOW_FPS(240)},
  1093. {"144", MAKE_DSHOW_FPS(144)},
  1094. {"120", MAKE_DSHOW_FPS(120)},
  1095. {"119.88 NTSC", MAKE_DSHOW_FRACTIONAL_FPS(120000, 1001)},
  1096. {"100", MAKE_DSHOW_FPS(100)},
  1097. {"60", MAKE_DSHOW_FPS(60)},
  1098. {"59.94 NTSC", MAKE_DSHOW_FRACTIONAL_FPS(60000, 1001)},
  1099. {"50", MAKE_DSHOW_FPS(50)},
  1100. {"48 film", MAKE_DSHOW_FRACTIONAL_FPS(48000, 1001)},
  1101. {"40", MAKE_DSHOW_FPS(40)},
  1102. {"30", MAKE_DSHOW_FPS(30)},
  1103. {"29.97 NTSC", MAKE_DSHOW_FRACTIONAL_FPS(30000, 1001)},
  1104. {"25", MAKE_DSHOW_FPS(25)},
  1105. {"24 film", MAKE_DSHOW_FRACTIONAL_FPS(24000, 1001)},
  1106. {"20", MAKE_DSHOW_FPS(20)},
  1107. {"15", MAKE_DSHOW_FPS(15)},
  1108. {"10", MAKE_DSHOW_FPS(10)},
  1109. {"5", MAKE_DSHOW_FPS(5)},
  1110. {"4", MAKE_DSHOW_FPS(4)},
  1111. {"3", MAKE_DSHOW_FPS(3)},
  1112. {"2", MAKE_DSHOW_FPS(2)},
  1113. {"1", MAKE_DSHOW_FPS(1)},
  1114. };
  1115. static bool DeviceIntervalChanged(obs_properties_t *props, obs_property_t *p,
  1116. obs_data_t *settings);
  1117. static bool TryResolution(const VideoDevice &dev, const string &res)
  1118. {
  1119. int cx, cy;
  1120. if (!ConvertRes(cx, cy, res.c_str()))
  1121. return false;
  1122. return ResolutionAvailable(dev, cx, cy);
  1123. }
  1124. static bool SetResolution(obs_properties_t *props, obs_data_t *settings,
  1125. const string &res, bool autoselect = false)
  1126. {
  1127. if (autoselect)
  1128. obs_data_set_autoselect_string(settings, RESOLUTION,
  1129. res.c_str());
  1130. else
  1131. obs_data_unset_autoselect_value(settings, RESOLUTION);
  1132. DeviceIntervalChanged(props, obs_properties_get(props, FRAME_INTERVAL),
  1133. settings);
  1134. if (!autoselect)
  1135. obs_data_set_string(settings, LAST_RESOLUTION, res.c_str());
  1136. return true;
  1137. }
  1138. static bool DeviceResolutionChanged(obs_properties_t *props, obs_property_t *p,
  1139. obs_data_t *settings)
  1140. {
  1141. UNUSED_PARAMETER(p);
  1142. PropertiesData *data =
  1143. (PropertiesData *)obs_properties_get_param(props);
  1144. const char *id;
  1145. VideoDevice device;
  1146. id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
  1147. string res = obs_data_get_string(settings, RESOLUTION);
  1148. string last_res = obs_data_get_string(settings, LAST_RESOLUTION);
  1149. if (!data->GetDevice(device, id))
  1150. return false;
  1151. if (TryResolution(device, res))
  1152. return SetResolution(props, settings, res);
  1153. if (TryResolution(device, last_res))
  1154. return SetResolution(props, settings, last_res, true);
  1155. return false;
  1156. }
  1157. struct VideoFormatName {
  1158. VideoFormat format;
  1159. const char *name;
  1160. };
  1161. static const VideoFormatName videoFormatNames[] = {
  1162. /* autoselect format*/
  1163. {VideoFormat::Any, "VideoFormat.Any"},
  1164. /* raw formats */
  1165. {VideoFormat::ARGB, "ARGB"},
  1166. {VideoFormat::XRGB, "XRGB"},
  1167. /* planar YUV formats */
  1168. {VideoFormat::I420, "I420"},
  1169. {VideoFormat::NV12, "NV12"},
  1170. {VideoFormat::YV12, "YV12"},
  1171. {VideoFormat::Y800, "Y800"},
  1172. {VideoFormat::P010, "P010"},
  1173. /* packed YUV formats */
  1174. {VideoFormat::YVYU, "YVYU"},
  1175. {VideoFormat::YUY2, "YUY2"},
  1176. {VideoFormat::UYVY, "UYVY"},
  1177. {VideoFormat::HDYC, "HDYC"},
  1178. /* encoded formats */
  1179. {VideoFormat::MJPEG, "MJPEG"},
  1180. {VideoFormat::H264, "H264"},
  1181. #ifdef ENABLE_HEVC
  1182. {VideoFormat::HEVC, "HEVC"},
  1183. #endif
  1184. };
  1185. static bool ResTypeChanged(obs_properties_t *props, obs_property_t *p,
  1186. obs_data_t *settings);
  1187. static size_t AddDevice(obs_property_t *device_list, const string &id)
  1188. {
  1189. DStr name, path;
  1190. if (!DecodeDeviceDStr(name, path, id.c_str()))
  1191. return numeric_limits<size_t>::max();
  1192. return obs_property_list_add_string(device_list, name, id.c_str());
  1193. }
  1194. static bool UpdateDeviceList(obs_property_t *list, const string &id)
  1195. {
  1196. size_t size = obs_property_list_item_count(list);
  1197. bool found = false;
  1198. bool disabled_unknown_found = false;
  1199. for (size_t i = 0; i < size; i++) {
  1200. if (obs_property_list_item_string(list, i) == id) {
  1201. found = true;
  1202. continue;
  1203. }
  1204. if (obs_property_list_item_disabled(list, i))
  1205. disabled_unknown_found = true;
  1206. }
  1207. if (!found && !disabled_unknown_found) {
  1208. size_t idx = AddDevice(list, id);
  1209. obs_property_list_item_disable(list, idx, true);
  1210. return true;
  1211. }
  1212. if (found && !disabled_unknown_found)
  1213. return false;
  1214. for (size_t i = 0; i < size;) {
  1215. if (obs_property_list_item_disabled(list, i)) {
  1216. obs_property_list_item_remove(list, i);
  1217. continue;
  1218. }
  1219. i += 1;
  1220. }
  1221. return true;
  1222. }
  1223. static bool DeviceSelectionChanged(obs_properties_t *props, obs_property_t *p,
  1224. obs_data_t *settings)
  1225. {
  1226. PropertiesData *data =
  1227. (PropertiesData *)obs_properties_get_param(props);
  1228. VideoDevice device;
  1229. string id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
  1230. string old_id = obs_data_get_string(settings, LAST_VIDEO_DEV_ID);
  1231. bool device_list_updated = UpdateDeviceList(p, id);
  1232. if (!data->GetDevice(device, id.c_str()))
  1233. return !device_list_updated;
  1234. vector<Resolution> resolutions;
  1235. for (const VideoInfo &cap : device.caps)
  1236. AddCap(resolutions, cap);
  1237. p = obs_properties_get(props, RESOLUTION);
  1238. obs_property_list_clear(p);
  1239. for (size_t idx = resolutions.size(); idx > 0; idx--) {
  1240. const Resolution &res = resolutions[idx - 1];
  1241. string strRes;
  1242. strRes += to_string(res.cx);
  1243. strRes += "x";
  1244. strRes += to_string(res.cy);
  1245. obs_property_list_add_string(p, strRes.c_str(), strRes.c_str());
  1246. }
  1247. /* only refresh properties if device legitimately changed */
  1248. if (!id.size() || !old_id.size() || id != old_id) {
  1249. p = obs_properties_get(props, RES_TYPE);
  1250. ResTypeChanged(props, p, settings);
  1251. obs_data_set_string(settings, LAST_VIDEO_DEV_ID, id.c_str());
  1252. }
  1253. return true;
  1254. }
  1255. static bool VideoConfigClicked(obs_properties_t *props, obs_property_t *p,
  1256. void *data)
  1257. {
  1258. DShowInput *input = reinterpret_cast<DShowInput *>(data);
  1259. input->QueueAction(Action::ConfigVideo);
  1260. UNUSED_PARAMETER(props);
  1261. UNUSED_PARAMETER(p);
  1262. return false;
  1263. }
  1264. /*static bool AudioConfigClicked(obs_properties_t *props, obs_property_t *p,
  1265. void *data)
  1266. {
  1267. DShowInput *input = reinterpret_cast<DShowInput*>(data);
  1268. input->QueueAction(Action::ConfigAudio);
  1269. UNUSED_PARAMETER(props);
  1270. UNUSED_PARAMETER(p);
  1271. return false;
  1272. }*/
  1273. static bool CrossbarConfigClicked(obs_properties_t *props, obs_property_t *p,
  1274. void *data)
  1275. {
  1276. DShowInput *input = reinterpret_cast<DShowInput *>(data);
  1277. input->QueueAction(Action::ConfigCrossbar1);
  1278. UNUSED_PARAMETER(props);
  1279. UNUSED_PARAMETER(p);
  1280. return false;
  1281. }
  1282. /*static bool Crossbar2ConfigClicked(obs_properties_t *props, obs_property_t *p,
  1283. void *data)
  1284. {
  1285. DShowInput *input = reinterpret_cast<DShowInput*>(data);
  1286. input->QueueAction(Action::ConfigCrossbar2);
  1287. UNUSED_PARAMETER(props);
  1288. UNUSED_PARAMETER(p);
  1289. return false;
  1290. }*/
  1291. static bool AddDevice(obs_property_t *device_list, const VideoDevice &device)
  1292. {
  1293. DStr name, path, device_id;
  1294. dstr_from_wcs(name, device.name.c_str());
  1295. dstr_from_wcs(path, device.path.c_str());
  1296. encode_dstr(path);
  1297. dstr_copy_dstr(device_id, name);
  1298. encode_dstr(device_id);
  1299. dstr_cat(device_id, ":");
  1300. dstr_cat_dstr(device_id, path);
  1301. obs_property_list_add_string(device_list, name, device_id);
  1302. return true;
  1303. }
  1304. static bool AddAudioDevice(obs_property_t *device_list,
  1305. const AudioDevice &device)
  1306. {
  1307. DStr name, path, device_id;
  1308. dstr_from_wcs(name, device.name.c_str());
  1309. dstr_from_wcs(path, device.path.c_str());
  1310. encode_dstr(path);
  1311. dstr_copy_dstr(device_id, name);
  1312. encode_dstr(device_id);
  1313. dstr_cat(device_id, ":");
  1314. dstr_cat_dstr(device_id, path);
  1315. obs_property_list_add_string(device_list, name, device_id);
  1316. return true;
  1317. }
  1318. static void PropertiesDataDestroy(void *data)
  1319. {
  1320. delete reinterpret_cast<PropertiesData *>(data);
  1321. }
  1322. static bool ResTypeChanged(obs_properties_t *props, obs_property_t *p,
  1323. obs_data_t *settings)
  1324. {
  1325. int val = (int)obs_data_get_int(settings, RES_TYPE);
  1326. bool enabled = (val != ResType_Preferred);
  1327. p = obs_properties_get(props, RESOLUTION);
  1328. obs_property_set_enabled(p, enabled);
  1329. p = obs_properties_get(props, FRAME_INTERVAL);
  1330. obs_property_set_enabled(p, enabled);
  1331. p = obs_properties_get(props, VIDEO_FORMAT);
  1332. obs_property_set_enabled(p, enabled);
  1333. if (val == ResType_Custom) {
  1334. p = obs_properties_get(props, RESOLUTION);
  1335. DeviceResolutionChanged(props, p, settings);
  1336. } else {
  1337. obs_data_unset_autoselect_value(settings, FRAME_INTERVAL);
  1338. }
  1339. return true;
  1340. }
  1341. static DStr GetFPSName(long long interval)
  1342. {
  1343. DStr name;
  1344. if (interval == FPS_MATCHING) {
  1345. dstr_cat(name, TEXT_FPS_MATCHING);
  1346. return name;
  1347. }
  1348. if (interval == FPS_HIGHEST) {
  1349. dstr_cat(name, TEXT_FPS_HIGHEST);
  1350. return name;
  1351. }
  1352. for (const FPSFormat &format : validFPSFormats) {
  1353. if (format.interval != interval)
  1354. continue;
  1355. dstr_cat(name, format.text);
  1356. return name;
  1357. }
  1358. dstr_cat(name, to_string(10000000. / interval).c_str());
  1359. return name;
  1360. }
  1361. static void UpdateFPS(VideoDevice &device, VideoFormat format,
  1362. long long interval, int cx, int cy,
  1363. obs_properties_t *props)
  1364. {
  1365. obs_property_t *list = obs_properties_get(props, FRAME_INTERVAL);
  1366. obs_property_list_clear(list);
  1367. obs_property_list_add_int(list, TEXT_FPS_MATCHING, FPS_MATCHING);
  1368. obs_property_list_add_int(list, TEXT_FPS_HIGHEST, FPS_HIGHEST);
  1369. bool interval_added = interval == FPS_HIGHEST ||
  1370. interval == FPS_MATCHING;
  1371. for (const FPSFormat &fps_format : validFPSFormats) {
  1372. bool video_format_match = false;
  1373. long long format_interval = fps_format.interval;
  1374. bool available = CapsMatch(
  1375. device, ResolutionMatcher(cx, cy),
  1376. VideoFormatMatcher(format, video_format_match),
  1377. FrameRateMatcher(format_interval));
  1378. if (!available && interval != fps_format.interval)
  1379. continue;
  1380. if (interval == fps_format.interval)
  1381. interval_added = true;
  1382. size_t idx = obs_property_list_add_int(list, fps_format.text,
  1383. fps_format.interval);
  1384. obs_property_list_item_disable(list, idx, !available);
  1385. }
  1386. if (interval_added)
  1387. return;
  1388. size_t idx =
  1389. obs_property_list_add_int(list, GetFPSName(interval), interval);
  1390. obs_property_list_item_disable(list, idx, true);
  1391. }
  1392. static DStr GetVideoFormatName(VideoFormat format)
  1393. {
  1394. DStr name;
  1395. for (const VideoFormatName &format_ : videoFormatNames) {
  1396. if (format_.format == format) {
  1397. dstr_cat(name, obs_module_text(format_.name));
  1398. return name;
  1399. }
  1400. }
  1401. dstr_cat(name, TEXT_FORMAT_UNKNOWN);
  1402. dstr_replace(name, "%1", std::to_string((long long)format).c_str());
  1403. return name;
  1404. }
  1405. static void UpdateVideoFormats(VideoDevice &device, VideoFormat format_, int cx,
  1406. int cy, long long interval,
  1407. obs_properties_t *props)
  1408. {
  1409. set<VideoFormat> formats = {VideoFormat::Any};
  1410. auto format_gatherer =
  1411. [&formats](const VideoInfo &info) mutable -> bool {
  1412. formats.insert(info.format);
  1413. return false;
  1414. };
  1415. CapsMatch(device, ResolutionMatcher(cx, cy), FrameRateMatcher(interval),
  1416. format_gatherer);
  1417. obs_property_t *list = obs_properties_get(props, VIDEO_FORMAT);
  1418. obs_property_list_clear(list);
  1419. bool format_added = false;
  1420. for (const VideoFormatName &format : videoFormatNames) {
  1421. bool available = formats.find(format.format) != end(formats);
  1422. if (!available && format.format != format_)
  1423. continue;
  1424. if (format.format == format_)
  1425. format_added = true;
  1426. size_t idx = obs_property_list_add_int(
  1427. list, obs_module_text(format.name),
  1428. (long long)format.format);
  1429. obs_property_list_item_disable(list, idx, !available);
  1430. }
  1431. if (format_added)
  1432. return;
  1433. size_t idx = obs_property_list_add_int(
  1434. list, GetVideoFormatName(format_), (long long)format_);
  1435. obs_property_list_item_disable(list, idx, true);
  1436. }
  1437. static bool UpdateFPS(long long interval, obs_property_t *list)
  1438. {
  1439. size_t size = obs_property_list_item_count(list);
  1440. DStr name;
  1441. for (size_t i = 0; i < size; i++) {
  1442. if (obs_property_list_item_int(list, i) != interval)
  1443. continue;
  1444. obs_property_list_item_disable(list, i, true);
  1445. if (size == 1)
  1446. return false;
  1447. dstr_cat(name, obs_property_list_item_name(list, i));
  1448. break;
  1449. }
  1450. obs_property_list_clear(list);
  1451. if (!name->len)
  1452. name = GetFPSName(interval);
  1453. obs_property_list_add_int(list, name, interval);
  1454. obs_property_list_item_disable(list, 0, true);
  1455. return true;
  1456. }
  1457. static bool DeviceIntervalChanged(obs_properties_t *props, obs_property_t *p,
  1458. obs_data_t *settings)
  1459. {
  1460. long long val = obs_data_get_int(settings, FRAME_INTERVAL);
  1461. PropertiesData *data =
  1462. (PropertiesData *)obs_properties_get_param(props);
  1463. const char *id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
  1464. VideoDevice device;
  1465. if (!data->GetDevice(device, id))
  1466. return UpdateFPS(val, p);
  1467. int cx = 0, cy = 0;
  1468. if (!DetermineResolution(cx, cy, settings, device)) {
  1469. UpdateVideoFormats(device, VideoFormat::Any, 0, 0, 0, props);
  1470. UpdateFPS(device, VideoFormat::Any, 0, 0, 0, props);
  1471. return true;
  1472. }
  1473. int resType = (int)obs_data_get_int(settings, RES_TYPE);
  1474. if (resType != ResType_Custom)
  1475. return true;
  1476. if (val == FPS_MATCHING)
  1477. val = GetOBSFPS();
  1478. VideoFormat format =
  1479. (VideoFormat)obs_data_get_int(settings, VIDEO_FORMAT);
  1480. bool video_format_matches = false;
  1481. long long best_interval = numeric_limits<long long>::max();
  1482. bool frameRateSupported =
  1483. CapsMatch(device, ResolutionMatcher(cx, cy),
  1484. VideoFormatMatcher(format, video_format_matches),
  1485. ClosestFrameRateSelector(val, best_interval),
  1486. FrameRateMatcher(val));
  1487. if (video_format_matches && !frameRateSupported &&
  1488. best_interval != val) {
  1489. long long listed_val = 0;
  1490. for (const FPSFormat &format : validFPSFormats) {
  1491. long long diff = llabs(format.interval - best_interval);
  1492. if (diff < DEVICE_INTERVAL_DIFF_LIMIT) {
  1493. listed_val = format.interval;
  1494. break;
  1495. }
  1496. }
  1497. if (listed_val != val) {
  1498. obs_data_set_autoselect_int(settings, FRAME_INTERVAL,
  1499. listed_val);
  1500. val = listed_val;
  1501. }
  1502. } else {
  1503. obs_data_unset_autoselect_value(settings, FRAME_INTERVAL);
  1504. }
  1505. UpdateVideoFormats(device, format, cx, cy, val, props);
  1506. UpdateFPS(device, format, val, cx, cy, props);
  1507. return true;
  1508. }
  1509. static bool UpdateVideoFormats(VideoFormat format, obs_property_t *list)
  1510. {
  1511. size_t size = obs_property_list_item_count(list);
  1512. DStr name;
  1513. for (size_t i = 0; i < size; i++) {
  1514. if ((VideoFormat)obs_property_list_item_int(list, i) != format)
  1515. continue;
  1516. if (size == 1)
  1517. return false;
  1518. dstr_cat(name, obs_property_list_item_name(list, i));
  1519. break;
  1520. }
  1521. obs_property_list_clear(list);
  1522. if (!name->len)
  1523. name = GetVideoFormatName(format);
  1524. obs_property_list_add_int(list, name, (long long)format);
  1525. obs_property_list_item_disable(list, 0, true);
  1526. return true;
  1527. }
  1528. static bool VideoFormatChanged(obs_properties_t *props, obs_property_t *p,
  1529. obs_data_t *settings)
  1530. {
  1531. PropertiesData *data =
  1532. (PropertiesData *)obs_properties_get_param(props);
  1533. const char *id = obs_data_get_string(settings, VIDEO_DEVICE_ID);
  1534. VideoDevice device;
  1535. VideoFormat curFormat =
  1536. (VideoFormat)obs_data_get_int(settings, VIDEO_FORMAT);
  1537. if (!data->GetDevice(device, id))
  1538. return UpdateVideoFormats(curFormat, p);
  1539. int cx, cy;
  1540. if (!DetermineResolution(cx, cy, settings, device)) {
  1541. UpdateVideoFormats(device, VideoFormat::Any, cx, cy, 0, props);
  1542. UpdateFPS(device, VideoFormat::Any, 0, 0, 0, props);
  1543. return true;
  1544. }
  1545. long long interval = obs_data_get_int(settings, FRAME_INTERVAL);
  1546. UpdateVideoFormats(device, curFormat, cx, cy, interval, props);
  1547. UpdateFPS(device, curFormat, interval, cx, cy, props);
  1548. return true;
  1549. }
  1550. static bool CustomAudioClicked(obs_properties_t *props, obs_property_t *p,
  1551. obs_data_t *settings)
  1552. {
  1553. bool useCustomAudio = obs_data_get_bool(settings, USE_CUSTOM_AUDIO);
  1554. p = obs_properties_get(props, AUDIO_DEVICE_ID);
  1555. obs_property_set_visible(p, useCustomAudio);
  1556. return true;
  1557. }
  1558. static bool ActivateClicked(obs_properties_t *, obs_property_t *p, void *data)
  1559. {
  1560. DShowInput *input = reinterpret_cast<DShowInput *>(data);
  1561. if (input->active) {
  1562. input->SetActive(false);
  1563. obs_property_set_description(p, TEXT_ACTIVATE);
  1564. } else {
  1565. input->SetActive(true);
  1566. obs_property_set_description(p, TEXT_DEACTIVATE);
  1567. }
  1568. return true;
  1569. }
  1570. static obs_properties_t *GetDShowProperties(void *obj)
  1571. {
  1572. DShowInput *input = reinterpret_cast<DShowInput *>(obj);
  1573. obs_properties_t *ppts = obs_properties_create();
  1574. PropertiesData *data = new PropertiesData;
  1575. data->input = input;
  1576. obs_properties_set_param(ppts, data, PropertiesDataDestroy);
  1577. obs_property_t *p = obs_properties_add_list(ppts, VIDEO_DEVICE_ID,
  1578. TEXT_DEVICE,
  1579. OBS_COMBO_TYPE_LIST,
  1580. OBS_COMBO_FORMAT_STRING);
  1581. obs_property_set_modified_callback(p, DeviceSelectionChanged);
  1582. Device::EnumVideoDevices(data->devices);
  1583. for (const VideoDevice &device : data->devices)
  1584. AddDevice(p, device);
  1585. const char *activateText = TEXT_ACTIVATE;
  1586. if (input) {
  1587. if (input->active)
  1588. activateText = TEXT_DEACTIVATE;
  1589. }
  1590. obs_properties_add_button(ppts, "activate", activateText,
  1591. ActivateClicked);
  1592. obs_properties_add_button(ppts, "video_config", TEXT_CONFIG_VIDEO,
  1593. VideoConfigClicked);
  1594. obs_properties_add_button(ppts, "xbar_config", TEXT_CONFIG_XBAR,
  1595. CrossbarConfigClicked);
  1596. obs_properties_add_bool(ppts, DEACTIVATE_WNS, TEXT_DWNS);
  1597. /* ------------------------------------- */
  1598. /* video settings */
  1599. p = obs_properties_add_list(ppts, RES_TYPE, TEXT_RES_FPS_TYPE,
  1600. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  1601. obs_property_set_modified_callback(p, ResTypeChanged);
  1602. obs_property_list_add_int(p, TEXT_PREFERRED_RES, ResType_Preferred);
  1603. obs_property_list_add_int(p, TEXT_CUSTOM_RES, ResType_Custom);
  1604. p = obs_properties_add_list(ppts, RESOLUTION, TEXT_RESOLUTION,
  1605. OBS_COMBO_TYPE_EDITABLE,
  1606. OBS_COMBO_FORMAT_STRING);
  1607. obs_property_set_modified_callback(p, DeviceResolutionChanged);
  1608. p = obs_properties_add_list(ppts, FRAME_INTERVAL, "FPS",
  1609. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  1610. obs_property_set_modified_callback(p, DeviceIntervalChanged);
  1611. p = obs_properties_add_list(ppts, VIDEO_FORMAT, TEXT_VIDEO_FORMAT,
  1612. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  1613. obs_property_set_modified_callback(p, VideoFormatChanged);
  1614. p = obs_properties_add_list(ppts, COLOR_SPACE, TEXT_COLOR_SPACE,
  1615. OBS_COMBO_TYPE_LIST,
  1616. OBS_COMBO_FORMAT_STRING);
  1617. obs_property_list_add_string(p, TEXT_COLOR_DEFAULT, "default");
  1618. obs_property_list_add_string(p, TEXT_COLOR_709, "709");
  1619. obs_property_list_add_string(p, TEXT_COLOR_601, "601");
  1620. obs_property_list_add_string(p, TEXT_COLOR_2100PQ, "2100PQ");
  1621. obs_property_list_add_string(p, TEXT_COLOR_2100HLG, "2100HLG");
  1622. p = obs_properties_add_list(ppts, COLOR_RANGE, TEXT_COLOR_RANGE,
  1623. OBS_COMBO_TYPE_LIST,
  1624. OBS_COMBO_FORMAT_STRING);
  1625. obs_property_list_add_string(p, TEXT_RANGE_DEFAULT, "default");
  1626. obs_property_list_add_string(p, TEXT_RANGE_PARTIAL, "partial");
  1627. obs_property_list_add_string(p, TEXT_RANGE_FULL, "full");
  1628. p = obs_properties_add_list(ppts, BUFFERING_VAL, TEXT_BUFFERING,
  1629. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  1630. obs_property_list_add_int(p, TEXT_BUFFERING_AUTO,
  1631. (int64_t)BufferingType::Auto);
  1632. obs_property_list_add_int(p, TEXT_BUFFERING_ON,
  1633. (int64_t)BufferingType::On);
  1634. obs_property_list_add_int(p, TEXT_BUFFERING_OFF,
  1635. (int64_t)BufferingType::Off);
  1636. obs_property_set_long_description(p,
  1637. obs_module_text("Buffering.ToolTip"));
  1638. obs_properties_add_bool(ppts, FLIP_IMAGE, TEXT_FLIP_IMAGE);
  1639. obs_properties_add_bool(ppts, AUTOROTATION, TEXT_AUTOROTATION);
  1640. obs_properties_add_bool(ppts, HW_DECODE, TEXT_HW_DECODE);
  1641. /* ------------------------------------- */
  1642. /* audio settings */
  1643. Device::EnumAudioDevices(data->audioDevices);
  1644. p = obs_properties_add_list(ppts, AUDIO_OUTPUT_MODE, TEXT_AUDIO_MODE,
  1645. OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
  1646. obs_property_list_add_int(p, TEXT_MODE_CAPTURE,
  1647. (int64_t)AudioMode::Capture);
  1648. obs_property_list_add_int(p, TEXT_MODE_DSOUND,
  1649. (int64_t)AudioMode::DirectSound);
  1650. obs_property_list_add_int(p, TEXT_MODE_WAVEOUT,
  1651. (int64_t)AudioMode::WaveOut);
  1652. if (!data->audioDevices.size())
  1653. return ppts;
  1654. p = obs_properties_add_bool(ppts, USE_CUSTOM_AUDIO, TEXT_CUSTOM_AUDIO);
  1655. obs_property_set_modified_callback(p, CustomAudioClicked);
  1656. p = obs_properties_add_list(ppts, AUDIO_DEVICE_ID, TEXT_AUDIO_DEVICE,
  1657. OBS_COMBO_TYPE_LIST,
  1658. OBS_COMBO_FORMAT_STRING);
  1659. for (const AudioDevice &device : data->audioDevices)
  1660. AddAudioDevice(p, device);
  1661. return ppts;
  1662. }
  1663. void DShowModuleLogCallback(LogType type, const wchar_t *msg, void *param)
  1664. {
  1665. int obs_type = LOG_DEBUG;
  1666. switch (type) {
  1667. case LogType::Error:
  1668. obs_type = LOG_ERROR;
  1669. break;
  1670. case LogType::Warning:
  1671. obs_type = LOG_WARNING;
  1672. break;
  1673. case LogType::Info:
  1674. obs_type = LOG_INFO;
  1675. break;
  1676. case LogType::Debug:
  1677. obs_type = LOG_DEBUG;
  1678. break;
  1679. }
  1680. DStr dmsg;
  1681. dstr_from_wcs(dmsg, msg);
  1682. blog(obs_type, "DShow: %s", dmsg->array);
  1683. UNUSED_PARAMETER(param);
  1684. }
  1685. static void HideDShowInput(void *data)
  1686. {
  1687. DShowInput *input = reinterpret_cast<DShowInput *>(data);
  1688. if (input->deactivateWhenNotShowing && input->active)
  1689. input->QueueAction(Action::Deactivate);
  1690. }
  1691. static void ShowDShowInput(void *data)
  1692. {
  1693. DShowInput *input = reinterpret_cast<DShowInput *>(data);
  1694. if (input->deactivateWhenNotShowing && input->active)
  1695. input->QueueAction(Action::Activate);
  1696. }
  1697. void RegisterDShowSource()
  1698. {
  1699. SetLogCallback(DShowModuleLogCallback, nullptr);
  1700. obs_source_info info = {};
  1701. info.id = "dshow_input";
  1702. info.type = OBS_SOURCE_TYPE_INPUT;
  1703. info.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_AUDIO |
  1704. OBS_SOURCE_ASYNC | OBS_SOURCE_DO_NOT_DUPLICATE;
  1705. info.show = ShowDShowInput;
  1706. info.hide = HideDShowInput;
  1707. info.get_name = GetDShowInputName;
  1708. info.create = CreateDShowInput;
  1709. info.destroy = DestroyDShowInput;
  1710. info.update = UpdateDShowInput;
  1711. info.get_defaults = GetDShowDefaults;
  1712. info.get_properties = GetDShowProperties;
  1713. info.icon_type = OBS_ICON_TYPE_CAMERA;
  1714. obs_register_source(&info);
  1715. }