Split output/input audio capture sources

- Split input and output audio captures so that they're different
   sources.  This allows easier handling and enumeration of audio
   devices without having to do some sort of string processing.

   This way the user interface code can handle this a bit more easily,
   and so that it doesn't confuse users either.  This should be done for
   all audio capture sources for all operating systems.  You don't have
   to duplicate any code, you just need to create input/output wrapper
   functions to designate the audio as input or output before creation.

 - Make it detect soundflower and wavtap devices as mac "output" devices
   (even though they're actually input) for the mac output capture, and
   make it so that users can select a default output capture and
   automatically use soundflower or wavtap.

   I'm not entirely happy about having to do this, but because mac is
   designed this way, this is really the only way to handle it that
   makes it easier for users and UI code to deal with.

   Note that soundflower and wavtap are still also designated as input
   devices, so will still show up in input device enumeration.

 - Remove pragma messages because they were kind polluting the other
   compiler messages and just getting in the way.  In the future we can
   just do a grep for TODO to find them.

 - Redo list property again, this time using a safer internal array,
   rather than requiring sketchy array inputs.  Having functions handle
   everything behind the scenes is much safer.

 - Remove the reference counter debug log code, as it was included
   unintentionally in a commit.
master
jp9000 2014-03-03 02:56:54 -07:00
parent b4ef6cee91
commit 9c6da6f52d
15 changed files with 374 additions and 124 deletions

View File

@ -560,7 +560,6 @@ void gs_normal3v(const struct vec3 *v)
void gs_color4v(const struct vec4 *v)
{
/* TODO */
#pragma message ("TODO: implement gs_color4v")
UNUSED_PARAMETER(v);
}
@ -577,7 +576,6 @@ void gs_texcoord2v(const struct vec2 *v, int unit)
input_t gs_getinput(void)
{
/* TODO */
#pragma message ("TODO: implement gs_getinput (hmm, not sure about input yet)")
return NULL;
}
@ -677,7 +675,6 @@ shader_t gs_create_pixelshader_from_file(const char *file, char **error_string)
texture_t gs_create_texture_from_file(const char *file, uint32_t flags)
{
/* TODO */
#pragma message ("TODO: implement gs_create_texture_from_file")
UNUSED_PARAMETER(file);
UNUSED_PARAMETER(flags);
return NULL;
@ -686,7 +683,6 @@ texture_t gs_create_texture_from_file(const char *file, uint32_t flags)
texture_t gs_create_cubetexture_from_file(const char *file, uint32_t flags)
{
/* TODO */
#pragma message ("TODO: implement gs_create_cubetexture_from_file")
UNUSED_PARAMETER(file);
UNUSED_PARAMETER(flags);
return NULL;
@ -695,7 +691,6 @@ texture_t gs_create_cubetexture_from_file(const char *file, uint32_t flags)
texture_t gs_create_volumetexture_from_file(const char *file, uint32_t flags)
{
/* TODO */
#pragma message ("TODO: implement gs_create_volumetexture_from_file")
UNUSED_PARAMETER(file);
UNUSED_PARAMETER(flags);
return NULL;
@ -799,7 +794,6 @@ void gs_draw_cube_backdrop(texture_t cubetex, const struct quat *rot,
float left, float right, float top, float bottom, float znear)
{
/* TODO */
#pragma message ("TODO: implement gs_draw_cube_backdrop")
UNUSED_PARAMETER(cubetex);
UNUSED_PARAMETER(rot);
UNUSED_PARAMETER(left);
@ -827,7 +821,6 @@ void gs_set2dmode(void)
void gs_set3dmode(double fovy, double znear, double zvar)
{
/* TODO */
#pragma message ("TODO: implement gs_set3dmode")
UNUSED_PARAMETER(fovy);
UNUSED_PARAMETER(znear);
UNUSED_PARAMETER(zvar);
@ -893,7 +886,6 @@ void cubetexture_setimage(texture_t cubetex, uint32_t side, const void *data,
uint32_t linesize, bool invert)
{
/* TODO */
#pragma message ("TODO: implement cubetexture_setimage")
UNUSED_PARAMETER(cubetex);
UNUSED_PARAMETER(side);
UNUSED_PARAMETER(data);

View File

@ -237,7 +237,6 @@ obs_data_t obs_data_create()
obs_data_t obs_data_create_from_json(const char *json_string)
{
/* TODO */
#pragma message ("TODO: implement obs_data_create_from_json")
UNUSED_PARAMETER(json_string);
return NULL;
}
@ -275,7 +274,6 @@ const char *obs_data_getjson(obs_data_t data)
if (!data) return NULL;
/* TODO */
#pragma message ("TODO: implement obs_data_getjson")
return data->json;
}

View File

@ -91,8 +91,8 @@ void obs_encoder_destroy(obs_encoder_t encoder)
obs_properties_t obs_encoder_properties(const char *id, const char *locale)
{
const struct obs_encoder_info *ei = get_encoder_info(id);
if (ei && ei->get_properties)
return ei->get_properties(locale);
if (ei && ei->properties)
return ei->properties(locale);
return NULL;
}
@ -144,7 +144,7 @@ bool obs_encoder_start(obs_encoder_t encoder,
void (*new_packet)(void *param, struct encoder_packet *packet),
void *param)
{
#pragma message ("TODO: implement obs_encoder_start")
/* TODO: implement */
UNUSED_PARAMETER(encoder);
UNUSED_PARAMETER(new_packet);
UNUSED_PARAMETER(param);
@ -155,7 +155,7 @@ void obs_encoder_stop(obs_encoder_t encoder,
void (*new_packet)(void *param, struct encoder_packet *packet),
void *param)
{
#pragma message ("TODO: implement obs_encoder_stop")
/* TODO: implement */
UNUSED_PARAMETER(encoder);
UNUSED_PARAMETER(new_packet);
UNUSED_PARAMETER(param);

View File

@ -140,7 +140,7 @@ struct obs_encoder_info {
* @param locale The locale to translate with
* @return The properties data
*/
obs_properties_t (*get_properties)(const char *locale);
obs_properties_t (*properties)(const char *locale);
/**
* Updates the settings for this encoder

View File

@ -214,7 +214,6 @@ void obs_register_encoder(const struct obs_encoder_info *info)
void obs_register_service(const struct obs_service_info *info)
{
/* TODO */
#pragma message ("TODO: implement obs_register_service")
UNUSED_PARAMETER(info);
}

View File

@ -16,18 +16,11 @@
******************************************************************************/
#include "util/bmem.h"
#include "util/darray.h"
#include "obs-properties.h"
static inline void *get_property_data(struct obs_property *prop);
static inline void free_str_list(char **str_list)
{
char **temp_list = str_list;
while (*temp_list)
bfree(*(temp_list++));
bfree(str_list);
}
/* ------------------------------------------------------------------------- */
struct float_data {
@ -38,17 +31,25 @@ struct int_data {
int min, max, step;
};
struct list_item {
char *name;
char *value;
};
struct list_data {
char **names;
char **values;
enum obs_combo_type type;
enum obs_combo_format format;
DARRAY(struct list_item) items;
enum obs_combo_type type;
enum obs_combo_format format;
};
static inline void list_data_free(struct list_data *data)
{
free_str_list(data->names);
free_str_list(data->values);
for (size_t i = 0; i < data->items.num; i++) {
bfree(data->items.array[i].name);
bfree(data->items.array[i].value);
}
da_free(data->items);
}
struct obs_property {
@ -136,7 +137,7 @@ static inline size_t get_property_size(enum obs_property_type type)
case OBS_PROPERTY_FLOAT: return sizeof(struct float_data);
case OBS_PROPERTY_TEXT: return 0;
case OBS_PROPERTY_PATH: return 0;
case OBS_PROPERTY_LIST: return sizeof(struct list_data);
case OBS_PROPERTY_LIST: return sizeof(struct list_data);
case OBS_PROPERTY_COLOR: return 0;
}
@ -228,47 +229,26 @@ void obs_properties_add_path(obs_properties_t props, const char *name,
new_prop(props, name, desc, OBS_PROPERTY_PATH);
}
static char **dup_str_list(const char **str_list)
{
int count = 0;
char **new_list;
const char **temp_list = str_list;
if (!str_list)
return NULL;
while (*(temp_list++) != NULL)
count++;
new_list = bmalloc((count+1) * sizeof(char*));
new_list[count] = NULL;
for (int i = 0; i < count; i++)
new_list[i] = bstrdup(str_list[i]);
return new_list;
}
void obs_properties_add_list(obs_properties_t props,
obs_property_t obs_properties_add_list(obs_properties_t props,
const char *name, const char *desc,
const char **value_names, const char **values,
enum obs_combo_type type,
enum obs_combo_format format)
{
if (!props || has_prop(props, name)) return;
if (!props || has_prop(props, name)) return NULL;
if (type == OBS_COMBO_TYPE_EDITABLE &&
format != OBS_COMBO_FORMAT_STRING) {
blog(LOG_WARNING, "List '%s', error: Editable combo boxes "
"must be of the 'string' type", name);
return;
return NULL;
}
struct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_LIST);
struct list_data *data = get_property_data(p);
data->names = dup_str_list(value_names);
data->values = dup_str_list(values);
data->format = format;
data->type = type;
return p;
}
void obs_properties_add_color(obs_properties_t props, const char *name,
@ -278,6 +258,34 @@ void obs_properties_add_color(obs_properties_t props, const char *name,
new_prop(props, name, desc, OBS_PROPERTY_COLOR);
}
static inline bool is_combo(struct obs_property *p)
{
return p->type == OBS_PROPERTY_LIST;
}
static inline struct list_data *get_list_data(struct obs_property *p)
{
if (!p || !is_combo(p))
return NULL;
return get_property_data(p);
}
void obs_property_list_add_item(obs_property_t p,
const char *name, const char *value)
{
struct list_data *data = get_list_data(p);
if (data) {
struct list_item item = {
.name = bstrdup(name),
.value = bstrdup(value)
};
da_insert(data->items, data->items.num-1, &item);
}
}
/* ------------------------------------------------------------------------- */
bool obs_property_next(obs_property_t *p)
@ -340,43 +348,34 @@ double obs_property_float_step(obs_property_t p)
return data ? data->step : 0;
}
static inline bool is_combo(struct obs_property *p)
{
return p->type == OBS_PROPERTY_LIST;
}
const char **obs_property_list_names(obs_property_t p)
{
if (!p || !is_combo(p))
return NULL;
struct list_data *data = get_property_data(p);
return (const char **)data->names;
}
const char **obs_property_list_values(obs_property_t p)
{
if (!p || !is_combo(p))
return NULL;
struct list_data *data = get_property_data(p);
return (const char **)data->values;
}
enum obs_combo_type obs_property_list_type(obs_property_t p)
{
if (!p || !is_combo(p))
return OBS_COMBO_TYPE_INVALID;
struct list_data *data = get_property_data(p);
return data->type;
struct list_data *data = get_list_data(p);
return data ? data->type : OBS_COMBO_TYPE_INVALID;
}
enum obs_combo_format obs_property_list_format(obs_property_t p)
{
if (!p || !is_combo(p))
return OBS_COMBO_FORMAT_INVALID;
struct list_data *data = get_property_data(p);
return data->format;
struct list_data *data = get_list_data(p);
return data ? data->format : OBS_COMBO_FORMAT_INVALID;
}
size_t obs_property_list_item_count(obs_property_t p)
{
struct list_data *data = get_list_data(p);
return data ? data->items.num : 0;
}
const char *obs_property_list_item_name(obs_property_t p, size_t idx)
{
struct list_data *data = get_list_data(p);
return (data && idx < data->items.num) ?
data->items.array[idx].name : NULL;
}
const char *obs_property_list_item_value(obs_property_t p, size_t idx)
{
struct list_data *data = get_list_data(p);
return (data && idx < data->items.num) ?
data->items.array[idx].value : NULL;
}

View File

@ -71,16 +71,14 @@ EXPORT void obs_properties_add_text(obs_properties_t props, const char *name,
const char *description);
EXPORT void obs_properties_add_path(obs_properties_t props, const char *name,
const char *description);
EXPORT void obs_properties_add_list(obs_properties_t props,
EXPORT obs_property_t obs_properties_add_list(obs_properties_t props,
const char *name, const char *description,
const char **value_names, const char **values,
enum obs_combo_type type,
enum obs_combo_format format);
enum obs_combo_type type, enum obs_combo_format format);
EXPORT void obs_properties_add_color(obs_properties_t props, const char *name,
const char *description);
EXPORT bool obs_properties_next(obs_properties_t *props);
EXPORT obs_property_t obs_properties_first_property(obs_properties_t props);
EXPORT void obs_property_list_add_item(obs_property_t p,
const char *name, const char *value);
/* ------------------------------------------------------------------------- */
@ -96,11 +94,13 @@ EXPORT int obs_property_int_step(obs_property_t p);
EXPORT double obs_property_float_min(obs_property_t p);
EXPORT double obs_property_float_max(obs_property_t p);
EXPORT double obs_property_float_step(obs_property_t p);
EXPORT const char ** obs_property_list_names(obs_property_t p);
EXPORT const char ** obs_property_list_values(obs_property_t p);
EXPORT enum obs_combo_type obs_property_list_type(obs_property_t p);
EXPORT enum obs_combo_format obs_property_list_format(obs_property_t p);
EXPORT size_t obs_property_list_item_count(obs_property_t p);
EXPORT const char *obs_property_list_item_name(obs_property_t p, size_t idx);
EXPORT const char *obs_property_list_item_value(obs_property_t p, size_t idx);
#ifdef __cplusplus
}
#endif

View File

@ -374,8 +374,6 @@ void obs_sceneitem_addref(obs_sceneitem_t item)
{
if (item)
++item->ref;
blog(LOG_DEBUG, "addref %s, ref: %d", item->source->name, item->ref);
}
void obs_sceneitem_release(obs_sceneitem_t item)
@ -383,8 +381,6 @@ void obs_sceneitem_release(obs_sceneitem_t item)
if (!item)
return;
blog(LOG_DEBUG, "release %s, ref: %d", item->source->name, item->ref);
if (--item->ref == 0)
obs_sceneitem_destroy(item);
}

View File

@ -302,8 +302,8 @@ obs_properties_t obs_source_properties(enum obs_source_type type,
const char *id, const char *locale)
{
const struct obs_source_info *info = get_source_info(type, id);
if (info && info->get_properties)
return info->get_properties(locale);
if (info && info->properties)
return info->properties(locale);
return NULL;
}

View File

@ -141,7 +141,7 @@ struct obs_source_info {
* @param locale The locale to translate with
* @return The properties data
*/
obs_properties_t (*get_properties)(const char *locale);
obs_properties_t (*properties)(const char *locale);
/**
* Updates the settings for this source

View File

@ -160,6 +160,23 @@ int wstrcmpi_n(const wchar_t *str1, const wchar_t *str2, size_t n)
return 0;
}
char *astrstri(char *str, const char *find)
{
size_t len;
if (!str || !find)
return NULL;
len = strlen(find);
do {
if (astrcmpi_n(str, find, len) == 0)
return str;
} while (*str++);
return NULL;
}
char *strdepad(char *str)
{
char *temp;

View File

@ -46,6 +46,8 @@ EXPORT int wstrcmp_n(const wchar_t *str1, const wchar_t *str2, size_t n);
EXPORT int astrcmpi_n(const char *str1, const char *str2, size_t n);
EXPORT int wstrcmpi_n(const wchar_t *str1, const wchar_t *str2, size_t n);
EXPORT char *astrstri(char *str, const char *find);
EXPORT char *strdepad(char *str);
EXPORT wchar_t *wcsdepad(wchar_t *str);
@ -108,6 +110,8 @@ EXPORT void dstr_safe_printf(struct dstr *dst, const char *format,
const char *val1, const char *val2, const char *val3,
const char *val4);
static inline const char *dstr_find_i(const struct dstr *str,
const char *find);
static inline const char *dstr_find(const struct dstr *str,
const char *find);
@ -269,8 +273,12 @@ static inline void dstr_cat_ch(struct dstr *dst, char ch)
dst->array[dst->len] = 0;
}
static inline const char *dstr_find(const struct dstr *str,
const char *find)
static inline const char *dstr_find_i(const struct dstr *str, const char *find)
{
return astrstri(str->array, find);
}
static inline const char *dstr_find(const struct dstr *str, const char *find)
{
return strstr(str->array, find);
}

View File

@ -7,6 +7,7 @@
#include <obs.h>
#include <util/threading.h>
#include <util/c99defs.h>
#include <util/darray.h>
#include <util/dstr.h>
#include "mac-helpers.h"
@ -35,6 +36,7 @@ struct coreaudio_data {
bool au_initialized;
bool active;
bool default_device;
bool input;
uint32_t sample_rate;
enum audio_format format;
@ -48,14 +50,157 @@ struct coreaudio_data {
obs_source_t source;
};
struct device_item {
struct dstr name, value;
};
static inline void device_item_free(struct device_item *item)
{
dstr_free(&item->name);
dstr_free(&item->value);
}
struct device_list {
DARRAY(struct device_item) items;
};
static inline void device_list_add(struct device_list *list,
struct device_item *item)
{
da_push_back(list->items, item);
memset(item, 0, sizeof(struct device_item));
}
static inline void device_list_free(struct device_list *list)
{
for (size_t i = 0; i < list->items.num; i++)
device_item_free(list->items.array+i);
da_free(list->items);
}
/* ugh, because mac has no means of capturing output, we have to basically
* mark soundflower and wavtap as output devices. */
static inline bool device_is_input(char *device)
{
return astrstri(device, "soundflower") == NULL &&
astrstri(device, "wavtap") == NULL;
}
static inline bool enum_success(OSStatus stat, const char *msg)
{
if (stat != noErr) {
blog(LOG_WARNING, "[coreaudio_enum_devices] %s failed: %d",
msg, (int)stat);
return false;
}
return true;
}
static void coreaudio_enum_add_device(struct device_list *list,
AudioDeviceID id, bool input)
{
OSStatus stat;
UInt32 size = 0;
CFStringRef cf_name = NULL;
CFStringRef cf_value = NULL;
struct device_item item;
AudioObjectPropertyAddress addr = {
kAudioDevicePropertyStreams,
kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster
};
memset(&item, 0, sizeof(item));
/* check to see if it's a mac input device */
AudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);
if (!size)
return;
size = sizeof(CFStringRef);
addr.mSelector = kAudioDevicePropertyDeviceUID;
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_value);
if (!enum_success(stat, "get audio device UID"))
return;
addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
if (!enum_success(stat, "get audio device name"))
goto fail;
if (!cf_to_dstr(cf_name, &item.name))
goto fail;
if (!cf_to_dstr(cf_value, &item.value))
goto fail;
if (input || !device_is_input(item.value.array))
device_list_add(list, &item);
fail:
device_item_free(&item);
CFRelease(cf_name);
CFRelease(cf_value);
}
static void coreaudio_enum_devices(struct device_list *list, bool input)
{
AudioObjectPropertyAddress addr = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 size = 0;
UInt32 count;
OSStatus stat;
AudioDeviceID *ids;
stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
0, NULL, &size);
if (!enum_success(stat, "get kAudioObjectSystemObject data size"))
return;
ids = bmalloc(size);
count = size / sizeof(AudioDeviceID);
stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
0, NULL, &size, ids);
if (enum_success(stat, "get kAudioObjectSystemObject data"))
for (UInt32 i = 0; i < count; i++)
coreaudio_enum_add_device(list, ids[i], input);
bfree(ids);
}
static bool get_default_output_device(struct coreaudio_data *ca)
{
struct device_list list;
memset(&list, 0, sizeof(struct device_list));
coreaudio_enum_devices(&list, false);
if (!list.items.num)
return false;
bfree(ca->device_uid);
ca->device_uid = bstrdup(list.items.array[0].value.array);
device_list_free(&list);
return true;
}
static bool find_device_id_by_uid(struct coreaudio_data *ca)
{
OSStatus stat;
UInt32 size = sizeof(AudioDeviceID);
CFStringRef cf_uid = CFStringCreateWithCString(NULL, ca->device_uid,
kCFStringEncodingUTF8);
CFStringRef qual = NULL;
UInt32 size = sizeof(AudioDeviceID);
CFStringRef cf_uid = NULL;
CFStringRef qual = NULL;
UInt32 qual_size = 0;
OSStatus stat;
bool success;
AudioObjectPropertyAddress addr = {
@ -63,7 +208,16 @@ static bool find_device_id_by_uid(struct coreaudio_data *ca)
.mElement = kAudioObjectPropertyElementMaster
};
ca->default_device = (strcmp(ca->device_uid, "Default") == 0);
/* have to do this because mac output devices don't actually exist */
if (astrcmpi(ca->device_uid, "default") == 0) {
if (ca->input)
ca->default_device = true;
else
get_default_output_device(ca);
}
cf_uid = CFStringCreateWithCString(NULL, ca->device_uid,
kCFStringEncodingUTF8);
if (ca->default_device) {
addr.mSelector = PROPERTY_DEFAULT_DEVICE;
@ -593,11 +747,18 @@ static void coreaudio_uninit(struct coreaudio_data *ca)
/* ------------------------------------------------------------------------- */
static const char *coreaudio_getname(const char *locale)
static const char *coreaudio_input_getname(const char *locale)
{
/* TODO: Locale */
UNUSED_PARAMETER(locale);
return "CoreAudio Input";
return "CoreAudio Input Capture";
}
static const char *coreaudio_output_getname(const char *locale)
{
/* TODO: Locale */
UNUSED_PARAMETER(locale);
return "CoreAudio Output Capture";
}
static void coreaudio_destroy(void *data)
@ -622,11 +783,12 @@ static void coreaudio_destroy(void *data)
}
}
static void *coreaudio_create(obs_data_t settings, obs_source_t source)
static void *coreaudio_create(obs_data_t settings, obs_source_t source,
bool input)
{
struct coreaudio_data *ca = bzalloc(sizeof(struct coreaudio_data));
obs_data_set_default_string(settings, "device_id", "Default");
obs_data_set_default_string(settings, "device_id", "default");
if (event_init(&ca->exit_event, EVENT_TYPE_MANUAL) != 0) {
blog(LOG_WARNING, "[coreaudio_create] failed to create "
@ -636,17 +798,81 @@ static void *coreaudio_create(obs_data_t settings, obs_source_t source)
}
ca->device_uid = bstrdup(obs_data_getstring(settings, "device_id"));
ca->source = source;
ca->source = source;
ca->input = input;
coreaudio_try_init(ca);
return ca;
}
struct obs_source_info coreaudio_info = {
.id = "coreaudio_capture",
static void *coreaudio_create_input_capture(obs_data_t settings,
obs_source_t source)
{
return coreaudio_create(settings, source, true);
}
static void *coreaudio_create_output_capture(obs_data_t settings,
obs_source_t source)
{
return coreaudio_create(settings, source, false);
}
static obs_properties_t coreaudio_properties(const char *locale, bool input)
{
obs_properties_t props = obs_properties_create();
obs_property_t property;
struct device_list devices;
memset(&devices, 0, sizeof(struct device_list));
/* TODO: translate */
property = obs_properties_add_list(props, "device_id", "Device",
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
coreaudio_enum_devices(&devices, input);
/* TODO: translate */
if (devices.items.num)
obs_property_list_add_item(property, "Default", "default");
for (size_t i = 0; i < devices.items.num; i++) {
struct device_item *item = devices.items.array+i;
obs_property_list_add_item(property,
item->name.array, item->value.array);
}
device_list_free(&devices);
UNUSED_PARAMETER(locale);
return props;
}
static obs_properties_t coreaudio_input_properties(const char *locale)
{
return coreaudio_properties(locale, true);
}
static obs_properties_t coreaudio_output_properties(const char *locale)
{
return coreaudio_properties(locale, false);
}
struct obs_source_info coreaudio_input_capture_info = {
.id = "coreaudio_input_capture",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_AUDIO,
.getname = coreaudio_getname,
.create = coreaudio_create,
.getname = coreaudio_input_getname,
.create = coreaudio_create_input_capture,
.destroy = coreaudio_destroy,
.properties = coreaudio_output_properties
};
struct obs_source_info coreaudio_output_capture_info = {
.id = "coreaudio_output_capture",
.type = OBS_SOURCE_TYPE_INPUT,
.output_flags = OBS_SOURCE_AUDIO,
.getname = coreaudio_output_getname,
.create = coreaudio_create_output_capture,
.destroy = coreaudio_destroy,
.properties = coreaudio_input_properties
};

View File

@ -12,5 +12,18 @@ static inline bool mac_success(OSStatus stat, const char *action)
static inline bool cf_to_cstr(CFStringRef ref, char *buf, size_t size)
{
if (!ref) return false;
return (bool)CFStringGetCString(ref, buf, size, kCFStringEncodingUTF8);
}
static inline bool cf_to_dstr(CFStringRef ref, struct dstr *str)
{
size_t size;
if (!ref) return false;
size = (size_t)CFStringGetLength(ref);
dstr_resize(str, size);
return (bool)CFStringGetCString(ref, str->array, size+1,
kCFStringEncodingUTF8);
}

View File

@ -2,11 +2,13 @@
OBS_DECLARE_MODULE()
extern struct obs_source_info coreaudio_info;
extern struct obs_source_info coreaudio_input_capture_info;
extern struct obs_source_info coreaudio_output_capture_info;
bool obs_module_load(uint32_t libobs_version)
{
obs_register_source(&coreaudio_info);
obs_register_source(&coreaudio_input_capture_info);
obs_register_source(&coreaudio_output_capture_info);
UNUSED_PARAMETER(libobs_version);
return true;