Changeset View
Changeset View
Standalone View
Standalone View
multimedia/obs-studio/files/patch-plugins_oss-audio_oss-input.c
- This file was added.
--- plugins/oss-audio/oss-input.c.orig 2020-06-01 19:59:40 UTC | |||||
+++ plugins/oss-audio/oss-input.c | |||||
@@ -0,0 +1,676 @@ | |||||
+/* | |||||
+Copyright (C) 2020. Ka Ho Ng <khng300@gmail.com> | |||||
+Copyright (C) 2020. Ed Maste <emaste@freebsd.org> | |||||
+ | |||||
+This program is free software: you can redistribute it and/or modify | |||||
+it under the terms of the GNU General Public License as published by | |||||
+the Free Software Foundation, either version 2 of the License, or | |||||
+(at your option) any later version. | |||||
+ | |||||
+This program is distributed in the hope that it will be useful, | |||||
+but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
+GNU General Public License for more details. | |||||
+ | |||||
+You should have received a copy of the GNU General Public License | |||||
+along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
+*/ | |||||
+ | |||||
+#include <util/bmem.h> | |||||
+#include <util/platform.h> | |||||
+#include <util/threading.h> | |||||
+#include <obs-module.h> | |||||
+ | |||||
+#include <poll.h> | |||||
+#include <unistd.h> | |||||
+#include <fcntl.h> | |||||
+#include <pthread.h> | |||||
+#include "oss-platform.h" | |||||
+ | |||||
+#define blog(level, msg, ...) blog(level, "oss-audio: " msg, ##__VA_ARGS__) | |||||
+ | |||||
+#define NSEC_PER_SEC 1000000000LL | |||||
+ | |||||
+#define OSS_MAX_CHANNELS 8 | |||||
+ | |||||
+#define OSS_DSP_DEFAULT "/dev/dsp" | |||||
+#define OSS_SNDSTAT_PATH "/dev/sndstat" | |||||
+#define OSS_RATE_DEFAULT 48000 | |||||
+#define OSS_CHANNELS_DEFAULT 2 | |||||
+ | |||||
+/** | |||||
+ * Control block of plugin instance | |||||
+ */ | |||||
+struct oss_input_data { | |||||
+ obs_source_t *source; | |||||
+ | |||||
+ char *device; | |||||
+ int channels; | |||||
+ int rate; | |||||
+ int sample_fmt; | |||||
+ | |||||
+ pthread_t reader_thr; | |||||
+ int notify_pipe[2]; | |||||
+ | |||||
+ int dsp_fd; | |||||
+ void *dsp_buf; | |||||
+ size_t dsp_fragsize; | |||||
+}; | |||||
+#define OBS_PROPS_DSP "dsp" | |||||
+#define OBS_PROPS_CUSTOM_DSP "custom_dsp" | |||||
+#define OBS_PATH_DSP_CUSTOM "/" | |||||
+#define OBS_PROPS_CHANNELS "channels" | |||||
+#define OBS_PROPS_RATE "rate" | |||||
+#define OBS_PROPS_SAMPLE_FMT "sample_fmt" | |||||
+ | |||||
+/** | |||||
+ * Common sampling rate table | |||||
+ */ | |||||
+struct rate_option { | |||||
+ int rate; | |||||
+ char *desc; | |||||
+} rate_table[] = { | |||||
+ {8000, "8000 Hz"}, {11025, "11025 Hz"}, {16000, "16000 Hz"}, | |||||
+ {22050, "22050 Hz"}, {32000, "32000 Hz"}, {44100, "44100 Hz"}, | |||||
+ {48000, "48000 Hz"}, {96000, "96000 Hz"}, {192000, "192000 Hz"}, | |||||
+ {384000, "384000 Hz"}, | |||||
+}; | |||||
+ | |||||
+static unsigned int oss_sample_size(unsigned int sample_fmt) | |||||
+{ | |||||
+ switch (sample_fmt) { | |||||
+ case AFMT_U8: | |||||
+ case AFMT_S8: | |||||
+ return 8; | |||||
+ case AFMT_S16_LE: | |||||
+ case AFMT_S16_BE: | |||||
+ case AFMT_U16_LE: | |||||
+ case AFMT_U16_BE: | |||||
+ return 16; | |||||
+ case AFMT_S32_LE: | |||||
+ case AFMT_S32_BE: | |||||
+ case AFMT_U32_LE: | |||||
+ case AFMT_U32_BE: | |||||
+ case AFMT_S24_LE: | |||||
+ case AFMT_S24_BE: | |||||
+ case AFMT_U24_LE: | |||||
+ case AFMT_U24_BE: | |||||
+ return 32; | |||||
+ } | |||||
+ return 0; | |||||
+} | |||||
+ | |||||
+static size_t oss_calc_framesize(unsigned int channels, unsigned int sample_fmt) | |||||
+{ | |||||
+ return oss_sample_size(sample_fmt) * channels / 8; | |||||
+} | |||||
+ | |||||
+static enum audio_format oss_fmt_to_obs_audio_format(int fmt) | |||||
+{ | |||||
+ switch (fmt) { | |||||
+ case AFMT_U8: | |||||
+ return AUDIO_FORMAT_U8BIT; | |||||
+ case AFMT_S16_LE: | |||||
+ return AUDIO_FORMAT_16BIT; | |||||
+ case AFMT_S32_LE: | |||||
+ return AUDIO_FORMAT_32BIT; | |||||
+ } | |||||
+ | |||||
+ return AUDIO_FORMAT_UNKNOWN; | |||||
+} | |||||
+ | |||||
+static enum speaker_layout oss_channels_to_obs_speakers(unsigned int channels) | |||||
+{ | |||||
+ switch (channels) { | |||||
+ case 1: | |||||
+ return SPEAKERS_MONO; | |||||
+ case 2: | |||||
+ return SPEAKERS_STEREO; | |||||
+ case 3: | |||||
+ return SPEAKERS_2POINT1; | |||||
+ case 4: | |||||
+ return SPEAKERS_4POINT0; | |||||
+ case 5: | |||||
+ return SPEAKERS_4POINT1; | |||||
+ case 6: | |||||
+ return SPEAKERS_5POINT1; | |||||
+ case 8: | |||||
+ return SPEAKERS_7POINT1; | |||||
+ } | |||||
+ | |||||
+ return SPEAKERS_UNKNOWN; | |||||
+} | |||||
+ | |||||
+static int oss_setup_device(struct oss_input_data *handle) | |||||
+{ | |||||
+ size_t dsp_fragsize; | |||||
+ void *dsp_buf = NULL; | |||||
+ int fd = -1, err; | |||||
+ audio_buf_info bi; | |||||
+ | |||||
+ fd = open(handle->device, O_RDONLY); | |||||
+ if (fd < 0) { | |||||
+ blog(LOG_ERROR, "Failed to open device '%s'.", handle->device); | |||||
+ return -1; | |||||
+ } | |||||
+ int val = handle->channels; | |||||
+ err = ioctl(fd, SNDCTL_DSP_CHANNELS, &val); | |||||
+ if (err) { | |||||
+ blog(LOG_ERROR, "Failed to set number of channels on DSP '%s'.", | |||||
+ handle->device); | |||||
+ goto failed_state; | |||||
+ } | |||||
+ val = handle->sample_fmt; | |||||
+ err = ioctl(fd, SNDCTL_DSP_SETFMT, &val); | |||||
+ if (err) { | |||||
+ blog(LOG_ERROR, "Failed to set format on DSP '%s'.", | |||||
+ handle->device); | |||||
+ goto failed_state; | |||||
+ } | |||||
+ val = handle->rate; | |||||
+ err = ioctl(fd, SNDCTL_DSP_SPEED, &val); | |||||
+ if (err) { | |||||
+ blog(LOG_ERROR, "Failed to set sample rate on DSP '%s'.", | |||||
+ handle->device); | |||||
+ goto failed_state; | |||||
+ } | |||||
+ err = ioctl(fd, SNDCTL_DSP_GETISPACE, &bi); | |||||
+ if (err) { | |||||
+ blog(LOG_ERROR, "Failed to get fragment size on DSP '%s'.", | |||||
+ handle->device); | |||||
+ goto failed_state; | |||||
+ } | |||||
+ | |||||
+ dsp_fragsize = bi.fragsize; | |||||
+ dsp_buf = bmalloc(dsp_fragsize); | |||||
+ if (dsp_buf == NULL) | |||||
+ goto failed_state; | |||||
+ | |||||
+ handle->dsp_buf = dsp_buf; | |||||
+ handle->dsp_fragsize = dsp_fragsize; | |||||
+ handle->dsp_fd = fd; | |||||
+ return 0; | |||||
+ | |||||
+failed_state: | |||||
+ if (fd != -1) | |||||
+ close(fd); | |||||
+ bfree(dsp_buf); | |||||
+ return -1; | |||||
+} | |||||
+ | |||||
+static void oss_close_device(struct oss_input_data *handle) | |||||
+{ | |||||
+ if (handle->dsp_fd != -1) | |||||
+ close(handle->dsp_fd); | |||||
+ bfree(handle->dsp_buf); | |||||
+ handle->dsp_fd = -1; | |||||
+ handle->dsp_buf = NULL; | |||||
+ handle->dsp_fragsize = 0; | |||||
+} | |||||
+ | |||||
+static void *oss_reader_thr(void *vptr) | |||||
+{ | |||||
+ struct oss_input_data *handle = vptr; | |||||
+ struct pollfd fds[2] = {0}; | |||||
+ size_t framesize; | |||||
+ | |||||
+ framesize = oss_calc_framesize(handle->channels, handle->sample_fmt); | |||||
+ fds[0].fd = handle->dsp_fd; | |||||
+ fds[0].events = POLLIN; | |||||
+ fds[1].fd = handle->notify_pipe[0]; | |||||
+ fds[1].events = POLLIN; | |||||
+ | |||||
+ assert(handle->dsp_buf); | |||||
+ | |||||
+ while (poll(fds, 2, INFTIM) >= 0) { | |||||
+ if (fds[0].revents & POLLIN) { | |||||
+ /* | |||||
+ * Incoming audio frames | |||||
+ */ | |||||
+ | |||||
+ struct obs_source_audio out; | |||||
+ ssize_t nbytes; | |||||
+ | |||||
+ do { | |||||
+ nbytes = read(handle->dsp_fd, handle->dsp_buf, | |||||
+ handle->dsp_fragsize); | |||||
+ } while (nbytes < 0 && errno == EINTR); | |||||
+ | |||||
+ if (nbytes < 0) { | |||||
+ blog(LOG_ERROR, | |||||
+ "%s: Failed to read buffer on DSP '%s'. Errno %d", | |||||
+ __func__, handle->device, errno); | |||||
+ break; | |||||
+ } else if (!nbytes) { | |||||
+ blog(LOG_ERROR, | |||||
+ "%s: Unexpected EOF on DSP '%s'.", | |||||
+ __func__, handle->device); | |||||
+ break; | |||||
+ } | |||||
+ | |||||
+ out.data[0] = handle->dsp_buf; | |||||
+ out.format = | |||||
+ oss_fmt_to_obs_audio_format(handle->sample_fmt); | |||||
+ out.speakers = | |||||
+ oss_channels_to_obs_speakers(handle->channels); | |||||
+ out.samples_per_sec = handle->rate; | |||||
+ out.frames = nbytes / framesize; | |||||
+ out.timestamp = | |||||
+ os_gettime_ns() - | |||||
+ ((out.frames * NSEC_PER_SEC) / handle->rate); | |||||
+ obs_source_output_audio(handle->source, &out); | |||||
+ } | |||||
+ if (fds[1].revents & POLLIN) { | |||||
+ char buf; | |||||
+ ssize_t nbytes; | |||||
+ | |||||
+ do { | |||||
+ nbytes = read(handle->notify_pipe[0], &buf, 1); | |||||
+ assert(nbytes != 0); | |||||
+ } while (nbytes < 0 && errno == EINTR); | |||||
+ | |||||
+ break; | |||||
+ } | |||||
+ } | |||||
+ | |||||
+ return NULL; | |||||
+} | |||||
+ | |||||
+static int oss_start_reader(struct oss_input_data *handle) | |||||
+{ | |||||
+ int pfd[2]; | |||||
+ int err; | |||||
+ pthread_t thr; | |||||
+ | |||||
+ err = pipe(pfd); | |||||
+ if (err) | |||||
+ return -1; | |||||
+ | |||||
+ err = pthread_create(&thr, NULL, oss_reader_thr, handle); | |||||
+ if (err) { | |||||
+ close(pfd[0]); | |||||
+ close(pfd[1]); | |||||
+ return -1; | |||||
+ } | |||||
+ | |||||
+ handle->notify_pipe[0] = pfd[0]; | |||||
+ handle->notify_pipe[1] = pfd[1]; | |||||
+ handle->reader_thr = thr; | |||||
+ return 0; | |||||
+} | |||||
+ | |||||
+static void oss_stop_reader(struct oss_input_data *handle) | |||||
+{ | |||||
+ if (handle->reader_thr) { | |||||
+ char buf = 0x0; | |||||
+ | |||||
+ write(handle->notify_pipe[1], &buf, 1); | |||||
+ pthread_join(handle->reader_thr, NULL); | |||||
+ } | |||||
+ if (handle->notify_pipe[0] != -1) { | |||||
+ close(handle->notify_pipe[0]); | |||||
+ close(handle->notify_pipe[1]); | |||||
+ } | |||||
+ handle->notify_pipe[0] = -1; | |||||
+ handle->notify_pipe[1] = -1; | |||||
+ handle->reader_thr = NULL; | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Returns the name of the plugin | |||||
+ */ | |||||
+static const char *oss_getname(void *unused) | |||||
+{ | |||||
+ UNUSED_PARAMETER(unused); | |||||
+ return obs_module_text("OSS Input"); | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Create the plugin object | |||||
+ */ | |||||
+static void *oss_create(obs_data_t *settings, obs_source_t *source) | |||||
+{ | |||||
+ const char *dsp; | |||||
+ const char *custom_dsp; | |||||
+ struct oss_input_data *handle; | |||||
+ | |||||
+ dsp = obs_data_get_string(settings, OBS_PROPS_DSP); | |||||
+ custom_dsp = obs_data_get_string(settings, OBS_PROPS_CUSTOM_DSP); | |||||
+ handle = bmalloc(sizeof(struct oss_input_data)); | |||||
+ if (handle == NULL) | |||||
+ return NULL; | |||||
+ | |||||
+ handle->source = source; | |||||
+ handle->device = NULL; | |||||
+ handle->channels = 0; | |||||
+ handle->rate = 0; | |||||
+ handle->sample_fmt = 0; | |||||
+ | |||||
+ handle->dsp_buf = NULL; | |||||
+ handle->dsp_fragsize = 0; | |||||
+ handle->dsp_fd = -1; | |||||
+ | |||||
+ handle->notify_pipe[0] = -1; | |||||
+ handle->notify_pipe[1] = -1; | |||||
+ handle->reader_thr = NULL; | |||||
+ | |||||
+ if (dsp == NULL) | |||||
+ return handle; | |||||
+ if (!strcmp(dsp, OBS_PATH_DSP_CUSTOM)) { | |||||
+ if (custom_dsp == NULL) | |||||
+ return handle; | |||||
+ | |||||
+ handle->device = bstrdup(custom_dsp); | |||||
+ } else | |||||
+ handle->device = bstrdup(dsp); | |||||
+ if (handle->device == NULL) | |||||
+ goto failed_state; | |||||
+ | |||||
+ handle->channels = obs_data_get_int(settings, OBS_PROPS_CHANNELS); | |||||
+ handle->rate = obs_data_get_int(settings, OBS_PROPS_RATE); | |||||
+ handle->sample_fmt = obs_data_get_int(settings, OBS_PROPS_SAMPLE_FMT); | |||||
+ | |||||
+ int err = oss_setup_device(handle); | |||||
+ if (err) | |||||
+ goto failed_state; | |||||
+ err = oss_start_reader(handle); | |||||
+ if (err) { | |||||
+ oss_close_device(handle); | |||||
+ goto failed_state; | |||||
+ } | |||||
+ | |||||
+ return handle; | |||||
+ | |||||
+failed_state: | |||||
+ bfree(handle); | |||||
+ return NULL; | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Destroy the plugin object and free all memory | |||||
+ */ | |||||
+static void oss_destroy(void *vptr) | |||||
+{ | |||||
+ struct oss_input_data *handle = vptr; | |||||
+ | |||||
+ oss_stop_reader(handle); | |||||
+ oss_close_device(handle); | |||||
+ bfree(handle->device); | |||||
+ bfree(handle); | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Update the input settings | |||||
+ */ | |||||
+static void oss_update(void *vptr, obs_data_t *settings) | |||||
+{ | |||||
+ struct oss_input_data *handle = vptr; | |||||
+ | |||||
+ oss_stop_reader(handle); | |||||
+ oss_close_device(handle); | |||||
+ | |||||
+ const char *dsp = obs_data_get_string(settings, OBS_PROPS_DSP); | |||||
+ const char *custom_dsp = | |||||
+ obs_data_get_string(settings, OBS_PROPS_CUSTOM_DSP); | |||||
+ if (dsp == NULL) { | |||||
+ bfree(handle->device); | |||||
+ handle->device = NULL; | |||||
+ return; | |||||
+ } | |||||
+ | |||||
+ bfree(handle->device); | |||||
+ handle->device = NULL; | |||||
+ if (!strcmp(dsp, OBS_PATH_DSP_CUSTOM)) { | |||||
+ if (custom_dsp == NULL) | |||||
+ return; | |||||
+ handle->device = bstrdup(custom_dsp); | |||||
+ } else | |||||
+ handle->device = bstrdup(dsp); | |||||
+ if (handle->device == NULL) | |||||
+ return; | |||||
+ | |||||
+ handle->channels = obs_data_get_int(settings, OBS_PROPS_CHANNELS); | |||||
+ handle->rate = obs_data_get_int(settings, OBS_PROPS_RATE); | |||||
+ handle->sample_fmt = obs_data_get_int(settings, OBS_PROPS_SAMPLE_FMT); | |||||
+ | |||||
+ int err = oss_setup_device(handle); | |||||
+ if (err) { | |||||
+ goto failed_state; | |||||
+ return; | |||||
+ } | |||||
+ err = oss_start_reader(handle); | |||||
+ if (err) { | |||||
+ oss_close_device(handle); | |||||
+ goto failed_state; | |||||
+ } | |||||
+ | |||||
+ return; | |||||
+ | |||||
+failed_state: | |||||
+ bfree(handle->device); | |||||
+ handle->device = NULL; | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Add audio devices to property | |||||
+ */ | |||||
+static void oss_prop_add_devices(obs_property_t *p) | |||||
+{ | |||||
+#if defined(__FreeBSD__) || defined(__DragonFly__) | |||||
+ char *line = NULL; | |||||
+ size_t linecap = 0; | |||||
+ FILE *fp; | |||||
+ | |||||
+ fp = fopen(OSS_SNDSTAT_PATH, "r"); | |||||
+ if (fp == NULL) { | |||||
+ blog(LOG_ERROR, "Failed to open sndstat at '%s'.", | |||||
+ OSS_SNDSTAT_PATH); | |||||
+ return; | |||||
+ } | |||||
+ | |||||
+ while (getline(&line, &linecap, fp) > 0) { | |||||
+ int pcm; | |||||
+ char *ptr, *pdesc, *descr, *devname, *pmode; | |||||
+ | |||||
+ if (sscanf(line, "pcm%i: ", &pcm) != 1) | |||||
+ continue; | |||||
+ if ((ptr = strchr(line, '<')) == NULL) | |||||
+ continue; | |||||
+ pdesc = ptr + 1; | |||||
+ if ((ptr = strrchr(pdesc, '>')) == NULL) | |||||
+ continue; | |||||
+ *ptr++ = '\0'; | |||||
+ if (*ptr++ != ' ' || *ptr++ != '(') | |||||
+ continue; | |||||
+ pmode = ptr; | |||||
+ if ((ptr = strrchr(pmode, ')')) == NULL) | |||||
+ continue; | |||||
+ *ptr++ = '\0'; | |||||
+ if (strcmp(pmode, "rec") != 0 && strcmp(pmode, "play/rec") != 0) | |||||
+ continue; | |||||
+ if (asprintf(&descr, "pcm%i: %s", pcm, pdesc) == -1) | |||||
+ continue; | |||||
+ if (asprintf(&devname, "/dev/dsp%i", pcm) == -1) { | |||||
+ free(descr); | |||||
+ continue; | |||||
+ } | |||||
+ | |||||
+ obs_property_list_add_string(p, descr, devname); | |||||
+ | |||||
+ free(descr); | |||||
+ free(devname); | |||||
+ } | |||||
+ free(line); | |||||
+ | |||||
+ fclose(fp); | |||||
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Get plugin defaults | |||||
+ */ | |||||
+static void oss_defaults(obs_data_t *settings) | |||||
+{ | |||||
+ obs_data_set_default_int(settings, OBS_PROPS_CHANNELS, | |||||
+ OSS_CHANNELS_DEFAULT); | |||||
+ obs_data_set_default_int(settings, OBS_PROPS_RATE, OSS_RATE_DEFAULT); | |||||
+ obs_data_set_default_int(settings, OBS_PROPS_SAMPLE_FMT, AFMT_S16_LE); | |||||
+ obs_data_set_default_string(settings, OBS_PROPS_DSP, OSS_DSP_DEFAULT); | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Get plugin properties: | |||||
+ * | |||||
+ * Fetch the engine information of the corresponding DSP | |||||
+ */ | |||||
+static bool oss_fill_device_info(obs_property_t *rate, obs_property_t *channels, | |||||
+ const char *device) | |||||
+{ | |||||
+ oss_audioinfo ai; | |||||
+ int fd = -1; | |||||
+ int err; | |||||
+ | |||||
+ obs_property_list_clear(rate); | |||||
+ obs_property_list_clear(channels); | |||||
+ | |||||
+ if (!strcmp(device, OBS_PATH_DSP_CUSTOM)) | |||||
+ goto cleanup; | |||||
+ | |||||
+ fd = open(device, O_RDONLY); | |||||
+ if (fd < 0) { | |||||
+ blog(LOG_ERROR, "Failed to open device '%s'.", device); | |||||
+ goto cleanup; | |||||
+ } | |||||
+ | |||||
+ ai.dev = -1; | |||||
+ err = ioctl(fd, SNDCTL_ENGINEINFO, &ai); | |||||
+ if (err) { | |||||
+ blog(LOG_ERROR, | |||||
+ "Failed to issue ioctl(SNDCTL_ENGINEINFO) on device '%s'. Errno: %d", | |||||
+ device, errno); | |||||
+ goto cleanup; | |||||
+ } | |||||
+ | |||||
+ for (int i = ai.min_channels; | |||||
+ i <= ai.max_channels && i <= OSS_MAX_CHANNELS; i++) { | |||||
+ enum speaker_layout layout = oss_channels_to_obs_speakers(i); | |||||
+ | |||||
+ if (layout != SPEAKERS_UNKNOWN) { | |||||
+ char name[] = "xxx"; | |||||
+ snprintf(name, sizeof(name), "%d", i); | |||||
+ obs_property_list_add_int(channels, name, i); | |||||
+ } | |||||
+ } | |||||
+ | |||||
+ for (size_t i = 0; i < sizeof(rate_table) / sizeof(rate_table[0]); | |||||
+ i++) { | |||||
+ if (ai.min_rate <= rate_table[i].rate && | |||||
+ ai.max_rate >= rate_table[i].rate) | |||||
+ obs_property_list_add_int(rate, rate_table[i].desc, | |||||
+ rate_table[i].rate); | |||||
+ } | |||||
+ | |||||
+cleanup: | |||||
+ if (!obs_property_list_item_count(rate)) | |||||
+ obs_property_list_add_int(rate, "48000 Hz", OSS_RATE_DEFAULT); | |||||
+ if (!obs_property_list_item_count(channels)) | |||||
+ obs_property_list_add_int(channels, "2", OSS_CHANNELS_DEFAULT); | |||||
+ if (fd != -1) | |||||
+ close(fd); | |||||
+ return true; | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Get plugin properties | |||||
+ */ | |||||
+static bool oss_on_devices_changed(obs_properties_t *props, obs_property_t *p, | |||||
+ obs_data_t *settings) | |||||
+{ | |||||
+ obs_property_t *rate, *channels; | |||||
+ obs_property_t *custom_dsp; | |||||
+ const char *device; | |||||
+ | |||||
+ UNUSED_PARAMETER(p); | |||||
+ | |||||
+ device = obs_data_get_string(settings, OBS_PROPS_DSP); | |||||
+ custom_dsp = obs_properties_get(props, OBS_PROPS_CUSTOM_DSP); | |||||
+ rate = obs_properties_get(props, OBS_PROPS_RATE); | |||||
+ channels = obs_properties_get(props, OBS_PROPS_CHANNELS); | |||||
+ | |||||
+ if (!strcmp(device, OBS_PATH_DSP_CUSTOM)) | |||||
+ obs_property_set_visible(custom_dsp, true); | |||||
+ else | |||||
+ obs_property_set_visible(custom_dsp, false); | |||||
+ oss_fill_device_info(rate, channels, device); | |||||
+ obs_property_modified(rate, settings); | |||||
+ obs_property_modified(channels, settings); | |||||
+ | |||||
+ obs_property_modified(custom_dsp, settings); | |||||
+ | |||||
+ return true; | |||||
+} | |||||
+ | |||||
+/** | |||||
+ * Get plugin properties | |||||
+ */ | |||||
+static obs_properties_t *oss_properties(void *unused) | |||||
+{ | |||||
+ obs_properties_t *props; | |||||
+ obs_property_t *devices; | |||||
+ obs_property_t *rate; | |||||
+ obs_property_t *sample_fmt; | |||||
+ obs_property_t *channels; | |||||
+ | |||||
+ UNUSED_PARAMETER(unused); | |||||
+ | |||||
+ props = obs_properties_create(); | |||||
+ | |||||
+ devices = obs_properties_add_list(props, OBS_PROPS_DSP, | |||||
+ obs_module_text("DSP"), | |||||
+ OBS_COMBO_TYPE_LIST, | |||||
+ OBS_COMBO_FORMAT_STRING); | |||||
+ | |||||
+ obs_property_list_add_string(devices, "Default", OSS_DSP_DEFAULT); | |||||
+ obs_property_list_add_string(devices, "Custom", OBS_PATH_DSP_CUSTOM); | |||||
+ obs_property_set_modified_callback(devices, oss_on_devices_changed); | |||||
+ | |||||
+ obs_properties_add_text(props, OBS_PROPS_CUSTOM_DSP, | |||||
+ obs_module_text("Custom DSP Path"), | |||||
+ OBS_TEXT_DEFAULT); | |||||
+ | |||||
+ rate = obs_properties_add_list(props, OBS_PROPS_RATE, | |||||
+ obs_module_text("Sample rate"), | |||||
+ OBS_COMBO_TYPE_LIST, | |||||
+ OBS_COMBO_FORMAT_INT); | |||||
+ channels = obs_properties_add_list(props, OBS_PROPS_CHANNELS, | |||||
+ obs_module_text("Channels"), | |||||
+ OBS_COMBO_TYPE_LIST, | |||||
+ OBS_COMBO_FORMAT_INT); | |||||
+ oss_fill_device_info(rate, channels, OSS_DSP_DEFAULT); | |||||
+ | |||||
+ sample_fmt = obs_properties_add_list(props, OBS_PROPS_SAMPLE_FMT, | |||||
+ obs_module_text("Sample format"), | |||||
+ OBS_COMBO_TYPE_LIST, | |||||
+ OBS_COMBO_FORMAT_INT); | |||||
+ | |||||
+ obs_property_list_add_int(sample_fmt, "pcm8", AFMT_U8); | |||||
+ obs_property_list_add_int(sample_fmt, "pcm16le", AFMT_S16_LE); | |||||
+ obs_property_list_add_int(sample_fmt, "pcm32le", AFMT_S32_LE); | |||||
+ | |||||
+ oss_prop_add_devices(devices); | |||||
+ | |||||
+ return props; | |||||
+} | |||||
+ | |||||
+struct obs_source_info oss_input_capture = { | |||||
+ .id = "oss_input_capture", | |||||
+ .type = OBS_SOURCE_TYPE_INPUT, | |||||
+ .output_flags = OBS_SOURCE_AUDIO, | |||||
+ .get_name = oss_getname, | |||||
+ .create = oss_create, | |||||
+ .destroy = oss_destroy, | |||||
+ .update = oss_update, | |||||
+ .get_defaults = oss_defaults, | |||||
+ .get_properties = oss_properties, | |||||
+ .icon_type = OBS_ICON_TYPE_AUDIO_INPUT, | |||||
+}; |