oss-input.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. /*
  2. Copyright (C) 2020. Ka Ho Ng <[email protected]>
  3. Copyright (C) 2020. Ed Maste <[email protected]>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include <util/bmem.h>
  16. #include <util/platform.h>
  17. #include <util/threading.h>
  18. #include <obs-module.h>
  19. #include <ctype.h>
  20. #include <poll.h>
  21. #include <unistd.h>
  22. #include <fcntl.h>
  23. #include <pthread.h>
  24. #include "oss-platform.h"
  25. #define blog(level, msg, ...) blog(level, "oss-audio: " msg, ##__VA_ARGS__)
  26. #define NSEC_PER_SEC 1000000000ULL
  27. #define OSS_MAX_CHANNELS 8
  28. #define OSS_DSP_DEFAULT "/dev/dsp"
  29. #define OSS_SNDSTAT_PATH "/dev/sndstat"
  30. #define OSS_RATE_DEFAULT 48000
  31. #define OSS_CHANNELS_DEFAULT 2
  32. #define OSS_DEVICE_BEGIN "Installed devices:"
  33. #define OSS_USERDEVICE_BEGIN "Installed devices from userspace:"
  34. #define OSS_FV_BEGIN "File Versions:"
  35. /**
  36. * Control block of plugin instance
  37. */
  38. struct oss_input_data {
  39. obs_source_t *source;
  40. char *device;
  41. int channels;
  42. int rate;
  43. int sample_fmt;
  44. pthread_t reader_thr;
  45. int notify_pipe[2];
  46. int dsp_fd;
  47. void *dsp_buf;
  48. size_t dsp_fragsize;
  49. };
  50. #define OBS_PROPS_DSP "dsp"
  51. #define OBS_PROPS_CUSTOM_DSP "custom_dsp"
  52. #define OBS_PATH_DSP_CUSTOM "/"
  53. #define OBS_PROPS_CHANNELS "channels"
  54. #define OBS_PROPS_RATE "rate"
  55. #define OBS_PROPS_SAMPLE_FMT "sample_fmt"
  56. /**
  57. * Common sampling rate table
  58. */
  59. struct rate_option {
  60. int rate;
  61. char *desc;
  62. } rate_table[] = {
  63. {8000, "8000 Hz"}, {11025, "11025 Hz"}, {16000, "16000 Hz"},
  64. {22050, "22050 Hz"}, {32000, "32000 Hz"}, {44100, "44100 Hz"},
  65. {48000, "48000 Hz"}, {96000, "96000 Hz"}, {192000, "192000 Hz"},
  66. {384000, "384000 Hz"},
  67. };
  68. static unsigned int oss_sample_size(unsigned int sample_fmt)
  69. {
  70. switch (sample_fmt) {
  71. case AFMT_U8:
  72. case AFMT_S8:
  73. return 8;
  74. case AFMT_S16_LE:
  75. case AFMT_S16_BE:
  76. case AFMT_U16_LE:
  77. case AFMT_U16_BE:
  78. return 16;
  79. case AFMT_S32_LE:
  80. case AFMT_S32_BE:
  81. case AFMT_U32_LE:
  82. case AFMT_U32_BE:
  83. case AFMT_S24_LE:
  84. case AFMT_S24_BE:
  85. case AFMT_U24_LE:
  86. case AFMT_U24_BE:
  87. return 32;
  88. }
  89. return 0;
  90. }
  91. static size_t oss_calc_framesize(unsigned int channels, unsigned int sample_fmt)
  92. {
  93. return oss_sample_size(sample_fmt) * channels / 8;
  94. }
  95. static enum audio_format oss_fmt_to_obs_audio_format(int fmt)
  96. {
  97. switch (fmt) {
  98. case AFMT_U8:
  99. return AUDIO_FORMAT_U8BIT;
  100. case AFMT_S16_LE:
  101. return AUDIO_FORMAT_16BIT;
  102. case AFMT_S32_LE:
  103. return AUDIO_FORMAT_32BIT;
  104. }
  105. return AUDIO_FORMAT_UNKNOWN;
  106. }
  107. static enum speaker_layout oss_channels_to_obs_speakers(unsigned int channels)
  108. {
  109. switch (channels) {
  110. case 1:
  111. return SPEAKERS_MONO;
  112. case 2:
  113. return SPEAKERS_STEREO;
  114. case 3:
  115. return SPEAKERS_2POINT1;
  116. case 4:
  117. return SPEAKERS_4POINT0;
  118. case 5:
  119. return SPEAKERS_4POINT1;
  120. case 6:
  121. return SPEAKERS_5POINT1;
  122. case 8:
  123. return SPEAKERS_7POINT1;
  124. }
  125. return SPEAKERS_UNKNOWN;
  126. }
  127. static int oss_setup_device(struct oss_input_data *handle)
  128. {
  129. size_t dsp_fragsize;
  130. void *dsp_buf = NULL;
  131. int fd = -1, err;
  132. audio_buf_info bi;
  133. fd = open(handle->device, O_RDONLY);
  134. if (fd < 0) {
  135. blog(LOG_ERROR, "Failed to open device '%s'.", handle->device);
  136. return -1;
  137. }
  138. int val = handle->channels;
  139. err = ioctl(fd, SNDCTL_DSP_CHANNELS, &val);
  140. if (err) {
  141. blog(LOG_ERROR, "Failed to set number of channels on DSP '%s'.",
  142. handle->device);
  143. goto failed_state;
  144. }
  145. val = handle->sample_fmt;
  146. err = ioctl(fd, SNDCTL_DSP_SETFMT, &val);
  147. if (err) {
  148. blog(LOG_ERROR, "Failed to set format on DSP '%s'.",
  149. handle->device);
  150. goto failed_state;
  151. }
  152. val = handle->rate;
  153. err = ioctl(fd, SNDCTL_DSP_SPEED, &val);
  154. if (err) {
  155. blog(LOG_ERROR, "Failed to set sample rate on DSP '%s'.",
  156. handle->device);
  157. goto failed_state;
  158. }
  159. err = ioctl(fd, SNDCTL_DSP_GETISPACE, &bi);
  160. if (err) {
  161. blog(LOG_ERROR, "Failed to get fragment size on DSP '%s'.",
  162. handle->device);
  163. goto failed_state;
  164. }
  165. dsp_fragsize = bi.fragsize;
  166. dsp_buf = bmalloc(dsp_fragsize);
  167. if (dsp_buf == NULL)
  168. goto failed_state;
  169. handle->dsp_buf = dsp_buf;
  170. handle->dsp_fragsize = dsp_fragsize;
  171. handle->dsp_fd = fd;
  172. return 0;
  173. failed_state:
  174. if (fd != -1)
  175. close(fd);
  176. bfree(dsp_buf);
  177. return -1;
  178. }
  179. static void oss_close_device(struct oss_input_data *handle)
  180. {
  181. if (handle->dsp_fd != -1)
  182. close(handle->dsp_fd);
  183. bfree(handle->dsp_buf);
  184. handle->dsp_fd = -1;
  185. handle->dsp_buf = NULL;
  186. handle->dsp_fragsize = 0;
  187. }
  188. static void *oss_reader_thr(void *vptr)
  189. {
  190. struct oss_input_data *handle = vptr;
  191. struct pollfd fds[2] = {0};
  192. size_t framesize;
  193. framesize = oss_calc_framesize(handle->channels, handle->sample_fmt);
  194. fds[0].fd = handle->dsp_fd;
  195. fds[0].events = POLLIN;
  196. fds[1].fd = handle->notify_pipe[0];
  197. fds[1].events = POLLIN;
  198. assert(handle->dsp_buf);
  199. while (poll(fds, 2, INFTIM) >= 0) {
  200. if (fds[0].revents & POLLIN) {
  201. /*
  202. * Incoming audio frames
  203. */
  204. struct obs_source_audio out;
  205. ssize_t nbytes;
  206. do {
  207. nbytes = read(handle->dsp_fd, handle->dsp_buf,
  208. handle->dsp_fragsize);
  209. } while (nbytes < 0 && errno == EINTR);
  210. if (nbytes < 0) {
  211. blog(LOG_ERROR,
  212. "%s: Failed to read buffer on DSP '%s'. Errno %d",
  213. __func__, handle->device, errno);
  214. break;
  215. } else if (!nbytes) {
  216. blog(LOG_ERROR,
  217. "%s: Unexpected EOF on DSP '%s'.",
  218. __func__, handle->device);
  219. break;
  220. }
  221. out.data[0] = handle->dsp_buf;
  222. out.format =
  223. oss_fmt_to_obs_audio_format(handle->sample_fmt);
  224. out.speakers =
  225. oss_channels_to_obs_speakers(handle->channels);
  226. out.samples_per_sec = handle->rate;
  227. out.frames = nbytes / framesize;
  228. out.timestamp = os_gettime_ns() -
  229. util_mul_div64(out.frames, NSEC_PER_SEC,
  230. handle->rate);
  231. obs_source_output_audio(handle->source, &out);
  232. }
  233. if (fds[1].revents & POLLIN) {
  234. char buf;
  235. ssize_t nbytes;
  236. do {
  237. nbytes = read(handle->notify_pipe[0], &buf, 1);
  238. assert(nbytes != 0);
  239. } while (nbytes < 0 && errno == EINTR);
  240. break;
  241. }
  242. }
  243. return NULL;
  244. }
  245. static int oss_start_reader(struct oss_input_data *handle)
  246. {
  247. int pfd[2];
  248. int err;
  249. pthread_t thr;
  250. err = pipe(pfd);
  251. if (err)
  252. return -1;
  253. err = pthread_create(&thr, NULL, oss_reader_thr, handle);
  254. if (err) {
  255. close(pfd[0]);
  256. close(pfd[1]);
  257. return -1;
  258. }
  259. handle->notify_pipe[0] = pfd[0];
  260. handle->notify_pipe[1] = pfd[1];
  261. handle->reader_thr = thr;
  262. return 0;
  263. }
  264. static void oss_stop_reader(struct oss_input_data *handle)
  265. {
  266. if (handle->reader_thr) {
  267. char buf = 0x0;
  268. write(handle->notify_pipe[1], &buf, 1);
  269. pthread_join(handle->reader_thr, NULL);
  270. }
  271. if (handle->notify_pipe[0] != -1) {
  272. close(handle->notify_pipe[0]);
  273. close(handle->notify_pipe[1]);
  274. }
  275. handle->notify_pipe[0] = -1;
  276. handle->notify_pipe[1] = -1;
  277. handle->reader_thr = NULL;
  278. }
  279. /**
  280. * Returns the name of the plugin
  281. */
  282. static const char *oss_getname(void *unused)
  283. {
  284. UNUSED_PARAMETER(unused);
  285. return obs_module_text("OSSInput");
  286. }
  287. /**
  288. * Create the plugin object
  289. */
  290. static void *oss_create(obs_data_t *settings, obs_source_t *source)
  291. {
  292. const char *dsp;
  293. const char *custom_dsp;
  294. struct oss_input_data *handle;
  295. dsp = obs_data_get_string(settings, OBS_PROPS_DSP);
  296. custom_dsp = obs_data_get_string(settings, OBS_PROPS_CUSTOM_DSP);
  297. handle = bmalloc(sizeof(struct oss_input_data));
  298. if (handle == NULL)
  299. return NULL;
  300. handle->source = source;
  301. handle->device = NULL;
  302. handle->channels = 0;
  303. handle->rate = 0;
  304. handle->sample_fmt = 0;
  305. handle->dsp_buf = NULL;
  306. handle->dsp_fragsize = 0;
  307. handle->dsp_fd = -1;
  308. handle->notify_pipe[0] = -1;
  309. handle->notify_pipe[1] = -1;
  310. handle->reader_thr = NULL;
  311. if (dsp == NULL)
  312. return handle;
  313. if (!strcmp(dsp, OBS_PATH_DSP_CUSTOM)) {
  314. if (custom_dsp == NULL)
  315. return handle;
  316. handle->device = bstrdup(custom_dsp);
  317. } else
  318. handle->device = bstrdup(dsp);
  319. if (handle->device == NULL)
  320. goto failed_state;
  321. handle->channels = obs_data_get_int(settings, OBS_PROPS_CHANNELS);
  322. handle->rate = obs_data_get_int(settings, OBS_PROPS_RATE);
  323. handle->sample_fmt = obs_data_get_int(settings, OBS_PROPS_SAMPLE_FMT);
  324. int err = oss_setup_device(handle);
  325. if (err)
  326. goto failed_state;
  327. err = oss_start_reader(handle);
  328. if (err) {
  329. oss_close_device(handle);
  330. goto failed_state;
  331. }
  332. return handle;
  333. failed_state:
  334. bfree(handle);
  335. return NULL;
  336. }
  337. /**
  338. * Destroy the plugin object and free all memory
  339. */
  340. static void oss_destroy(void *vptr)
  341. {
  342. struct oss_input_data *handle = vptr;
  343. oss_stop_reader(handle);
  344. oss_close_device(handle);
  345. bfree(handle->device);
  346. bfree(handle);
  347. }
  348. /**
  349. * Update the input settings
  350. */
  351. static void oss_update(void *vptr, obs_data_t *settings)
  352. {
  353. struct oss_input_data *handle = vptr;
  354. oss_stop_reader(handle);
  355. oss_close_device(handle);
  356. const char *dsp = obs_data_get_string(settings, OBS_PROPS_DSP);
  357. const char *custom_dsp =
  358. obs_data_get_string(settings, OBS_PROPS_CUSTOM_DSP);
  359. if (dsp == NULL) {
  360. bfree(handle->device);
  361. handle->device = NULL;
  362. return;
  363. }
  364. bfree(handle->device);
  365. handle->device = NULL;
  366. if (!strcmp(dsp, OBS_PATH_DSP_CUSTOM)) {
  367. if (custom_dsp == NULL)
  368. return;
  369. handle->device = bstrdup(custom_dsp);
  370. } else
  371. handle->device = bstrdup(dsp);
  372. if (handle->device == NULL)
  373. return;
  374. handle->channels = obs_data_get_int(settings, OBS_PROPS_CHANNELS);
  375. handle->rate = obs_data_get_int(settings, OBS_PROPS_RATE);
  376. handle->sample_fmt = obs_data_get_int(settings, OBS_PROPS_SAMPLE_FMT);
  377. int err = oss_setup_device(handle);
  378. if (err) {
  379. goto failed_state;
  380. return;
  381. }
  382. err = oss_start_reader(handle);
  383. if (err) {
  384. oss_close_device(handle);
  385. goto failed_state;
  386. }
  387. return;
  388. failed_state:
  389. bfree(handle->device);
  390. handle->device = NULL;
  391. }
  392. /**
  393. * Add audio devices to property
  394. */
  395. static void oss_prop_add_devices(obs_property_t *p)
  396. {
  397. #if defined(__FreeBSD__) || defined(__DragonFly__)
  398. char *line = NULL;
  399. size_t linecap = 0;
  400. FILE *fp;
  401. bool ud_matching = false;
  402. bool skipall = false;
  403. fp = fopen(OSS_SNDSTAT_PATH, "r");
  404. if (fp == NULL) {
  405. blog(LOG_ERROR, "Failed to open sndstat at '%s'.",
  406. OSS_SNDSTAT_PATH);
  407. return;
  408. }
  409. while (getline(&line, &linecap, fp) > 0) {
  410. int pcm;
  411. char *ptr, *pdesc, *pmode;
  412. char *descr = NULL, *devname = NULL;
  413. char *udname = NULL;
  414. if (!strncmp(line, OSS_FV_BEGIN, strlen(OSS_FV_BEGIN))) {
  415. skipall = true;
  416. continue;
  417. }
  418. if (!strncmp(line, OSS_DEVICE_BEGIN,
  419. strlen(OSS_DEVICE_BEGIN))) {
  420. ud_matching = false;
  421. skipall = false;
  422. continue;
  423. }
  424. if (!strncmp(line, OSS_USERDEVICE_BEGIN,
  425. strlen(OSS_USERDEVICE_BEGIN))) {
  426. ud_matching = true;
  427. skipall = false;
  428. continue;
  429. }
  430. if (skipall || isblank(line[0]))
  431. continue;
  432. if (!ud_matching) {
  433. if (sscanf(line, "pcm%i: ", &pcm) != 1)
  434. continue;
  435. } else {
  436. char *end = strchr(line, ':');
  437. if (end == NULL || end - line == 0)
  438. continue;
  439. udname = strndup(line, end - line);
  440. if (udname == NULL)
  441. continue;
  442. }
  443. if ((ptr = strchr(line, '<')) == NULL)
  444. goto free_all_str;
  445. pdesc = ptr + 1;
  446. if ((ptr = strrchr(pdesc, '>')) == NULL)
  447. goto free_all_str;
  448. *ptr++ = '\0';
  449. if ((pmode = strchr(ptr, '(')) == NULL)
  450. goto free_all_str;
  451. pmode++;
  452. if ((ptr = strrchr(pmode, ')')) == NULL)
  453. goto free_all_str;
  454. *ptr++ = '\0';
  455. if (!isdigit(pmode[0])) {
  456. if (strcmp(pmode, "rec") != 0 &&
  457. strcmp(pmode, "play/rec") != 0)
  458. goto free_all_str;
  459. } else {
  460. int npcs, nrcs;
  461. if (sscanf(pmode, "%dp:%*dv/%dr:%*dv", &npcs, &nrcs) !=
  462. 2)
  463. goto free_all_str;
  464. if (nrcs < 1)
  465. goto free_all_str;
  466. }
  467. if (!ud_matching) {
  468. if (asprintf(&descr, "pcm%i: %s", pcm, pdesc) == -1)
  469. goto free_all_str;
  470. if (asprintf(&devname, "/dev/dsp%i", pcm) == -1)
  471. goto free_all_str;
  472. } else {
  473. if (asprintf(&descr, "%s: %s", udname, pdesc) == -1)
  474. goto free_all_str;
  475. if (asprintf(&devname, "/dev/%s", udname) == -1)
  476. goto free_all_str;
  477. }
  478. obs_property_list_add_string(p, descr, devname);
  479. free_all_str:
  480. free(descr);
  481. free(devname);
  482. free(udname);
  483. }
  484. free(line);
  485. fclose(fp);
  486. #endif /* defined(__FreeBSD__) || defined(__DragonFly__) */
  487. }
  488. /**
  489. * Get plugin defaults
  490. */
  491. static void oss_defaults(obs_data_t *settings)
  492. {
  493. obs_data_set_default_int(settings, OBS_PROPS_CHANNELS,
  494. OSS_CHANNELS_DEFAULT);
  495. obs_data_set_default_int(settings, OBS_PROPS_RATE, OSS_RATE_DEFAULT);
  496. obs_data_set_default_int(settings, OBS_PROPS_SAMPLE_FMT, AFMT_S16_LE);
  497. obs_data_set_default_string(settings, OBS_PROPS_DSP, OSS_DSP_DEFAULT);
  498. }
  499. /**
  500. * Get plugin properties:
  501. *
  502. * Fetch the engine information of the corresponding DSP
  503. */
  504. static bool oss_fill_device_info(obs_property_t *rate, obs_property_t *channels,
  505. const char *device)
  506. {
  507. oss_audioinfo ai;
  508. int fd = -1;
  509. int err;
  510. obs_property_list_clear(rate);
  511. obs_property_list_clear(channels);
  512. if (!strcmp(device, OBS_PATH_DSP_CUSTOM))
  513. goto cleanup;
  514. fd = open(device, O_RDONLY);
  515. if (fd < 0) {
  516. blog(LOG_ERROR, "Failed to open device '%s'.", device);
  517. goto cleanup;
  518. }
  519. ai.dev = -1;
  520. err = ioctl(fd, SNDCTL_ENGINEINFO, &ai);
  521. if (err) {
  522. blog(LOG_ERROR,
  523. "Failed to issue ioctl(SNDCTL_ENGINEINFO) on device '%s'. Errno: %d",
  524. device, errno);
  525. goto cleanup;
  526. }
  527. for (int i = ai.min_channels;
  528. i <= ai.max_channels && i <= OSS_MAX_CHANNELS; i++) {
  529. enum speaker_layout layout = oss_channels_to_obs_speakers(i);
  530. if (layout != SPEAKERS_UNKNOWN) {
  531. char name[] = "xxx";
  532. snprintf(name, sizeof(name), "%d", i);
  533. obs_property_list_add_int(channels, name, i);
  534. }
  535. }
  536. for (size_t i = 0; i < sizeof(rate_table) / sizeof(rate_table[0]);
  537. i++) {
  538. if (ai.min_rate <= rate_table[i].rate &&
  539. ai.max_rate >= rate_table[i].rate)
  540. obs_property_list_add_int(rate, rate_table[i].desc,
  541. rate_table[i].rate);
  542. }
  543. cleanup:
  544. if (!obs_property_list_item_count(rate))
  545. obs_property_list_add_int(rate, "48000 Hz", OSS_RATE_DEFAULT);
  546. if (!obs_property_list_item_count(channels))
  547. obs_property_list_add_int(channels, "2", OSS_CHANNELS_DEFAULT);
  548. if (fd != -1)
  549. close(fd);
  550. return true;
  551. }
  552. /**
  553. * Get plugin properties
  554. */
  555. static bool oss_on_devices_changed(obs_properties_t *props, obs_property_t *p,
  556. obs_data_t *settings)
  557. {
  558. obs_property_t *rate, *channels;
  559. obs_property_t *custom_dsp;
  560. const char *device;
  561. UNUSED_PARAMETER(p);
  562. device = obs_data_get_string(settings, OBS_PROPS_DSP);
  563. custom_dsp = obs_properties_get(props, OBS_PROPS_CUSTOM_DSP);
  564. rate = obs_properties_get(props, OBS_PROPS_RATE);
  565. channels = obs_properties_get(props, OBS_PROPS_CHANNELS);
  566. if (!strcmp(device, OBS_PATH_DSP_CUSTOM))
  567. obs_property_set_visible(custom_dsp, true);
  568. else
  569. obs_property_set_visible(custom_dsp, false);
  570. oss_fill_device_info(rate, channels, device);
  571. obs_property_modified(rate, settings);
  572. obs_property_modified(channels, settings);
  573. obs_property_modified(custom_dsp, settings);
  574. return true;
  575. }
  576. /**
  577. * Get plugin properties
  578. */
  579. static obs_properties_t *oss_properties(void *unused)
  580. {
  581. obs_properties_t *props;
  582. obs_property_t *devices;
  583. obs_property_t *rate;
  584. obs_property_t *sample_fmt;
  585. obs_property_t *channels;
  586. UNUSED_PARAMETER(unused);
  587. props = obs_properties_create();
  588. devices = obs_properties_add_list(props, OBS_PROPS_DSP,
  589. obs_module_text("DSP"),
  590. OBS_COMBO_TYPE_LIST,
  591. OBS_COMBO_FORMAT_STRING);
  592. obs_property_list_add_string(devices, obs_module_text("Default"),
  593. OSS_DSP_DEFAULT);
  594. obs_property_list_add_string(devices, obs_module_text("Custom"),
  595. OBS_PATH_DSP_CUSTOM);
  596. obs_property_set_modified_callback(devices, oss_on_devices_changed);
  597. obs_properties_add_text(props, OBS_PROPS_CUSTOM_DSP,
  598. obs_module_text("CustomDSPPath"),
  599. OBS_TEXT_DEFAULT);
  600. rate = obs_properties_add_list(props, OBS_PROPS_RATE,
  601. obs_module_text("SampleRate"),
  602. OBS_COMBO_TYPE_LIST,
  603. OBS_COMBO_FORMAT_INT);
  604. channels = obs_properties_add_list(props, OBS_PROPS_CHANNELS,
  605. obs_module_text("Channels"),
  606. OBS_COMBO_TYPE_LIST,
  607. OBS_COMBO_FORMAT_INT);
  608. oss_fill_device_info(rate, channels, OSS_DSP_DEFAULT);
  609. sample_fmt = obs_properties_add_list(props, OBS_PROPS_SAMPLE_FMT,
  610. obs_module_text("SampleFormat"),
  611. OBS_COMBO_TYPE_LIST,
  612. OBS_COMBO_FORMAT_INT);
  613. obs_property_list_add_int(sample_fmt, "pcm8", AFMT_U8);
  614. obs_property_list_add_int(sample_fmt, "pcm16le", AFMT_S16_LE);
  615. obs_property_list_add_int(sample_fmt, "pcm32le", AFMT_S32_LE);
  616. oss_prop_add_devices(devices);
  617. return props;
  618. }
  619. struct obs_source_info oss_input_capture = {
  620. .id = "oss_input_capture",
  621. .type = OBS_SOURCE_TYPE_INPUT,
  622. .output_flags = OBS_SOURCE_AUDIO,
  623. .get_name = oss_getname,
  624. .create = oss_create,
  625. .destroy = oss_destroy,
  626. .update = oss_update,
  627. .get_defaults = oss_defaults,
  628. .get_properties = oss_properties,
  629. .icon_type = OBS_ICON_TYPE_AUDIO_INPUT,
  630. };