Skip to content

Commit

Permalink
libobs: Implement audio monitoring
Browse files Browse the repository at this point in the history
Adds functions to turn on audio monitoring to allow the user to hear
playback of an audio source over the user's speaker.  It can be set to
turn off monitoring and only output to stream, or it can be set to
output only to monitoring, or it can be set to both.

On windows, audio monitoring uses WASAPI.  Windows also is capable of
syncing the audio to the video according to when the video frame itself
was played.

On mac, it uses AudioQueue.

On linux, it's not currently implemented and won't do anything (to be
implemented).
  • Loading branch information
jp9000 committed Feb 6, 2017
1 parent 74f9c38 commit d2934ec
Show file tree
Hide file tree
Showing 13 changed files with 1,170 additions and 7 deletions.
39 changes: 38 additions & 1 deletion libobs/CMakeLists.txt
Expand Up @@ -68,6 +68,13 @@ if(WIN32)
util/windows/CoTaskMemPtr.hpp
util/windows/HRError.hpp
util/windows/WinHandle.hpp)
set(libobs_audio_monitoring_SOURCES
audio-monitoring/win32/wasapi-output.c
audio-monitoring/win32/wasapi-enum-devices.c
)
set(libobs_audio_monitoring_HEADERS
audio-monitoring/win32/wasapi-output.h
)
set(libobs_PLATFORM_DEPS winmm)
if(MSVC)
set(libobs_PLATFORM_DEPS
Expand All @@ -83,6 +90,13 @@ elseif(APPLE)
util/platform-cocoa.m)
set(libobs_PLATFORM_HEADERS
util/threading-posix.h)
set(libobs_audio_monitoring_SOURCES
audio-monitoring/osx/coreaudio-enum-devices.c
audio-monitoring/osx/coreaudio-output.c
)
set(libobs_audio_monitoring_HEADERS
audio-monitoring/osx/mac-helpers.h
)

set_source_files_properties(${libobs_PLATFORM_SOURCES}
PROPERTIES
Expand All @@ -93,6 +107,18 @@ elseif(APPLE)
mark_as_advanced(COCOA)
include_directories(${COCOA})

find_library(COREAUDIO CoreAudio)
mark_as_advanced(COREAUDIO)
include_directories(${COREAUDIO})

find_library(AUDIOTOOLBOX AudioToolbox)
mark_as_advanced(AUDIOTOOLBOX)
include_directories(${AUDIOTOOLBOX})

find_library(AUDIOUNIT AudioUnit)
mark_as_advanced(AUDIOUNIT)
include_directories(${AUDIOUNIT})

find_library(APPKIT AppKit)
mark_as_advanced(APPKIT)
include_directories(${APPKIT})
Expand All @@ -107,6 +133,9 @@ elseif(APPLE)

set(libobs_PLATFORM_DEPS
${COCOA}
${COREAUDIO}
${AUDIOUNIT}
${AUDIOTOOLBOX}
${APPKIT}
${IOKIT}
${CARBON})
Expand All @@ -118,6 +147,9 @@ elseif(UNIX)
util/platform-nix.c)
set(libobs_PLATFORM_HEADERS
util/threading-posix.h)
set(libobs_audio_monitoring_SOURCES
audio-monitoring/null/null-audio-monitoring.c
)

if(DBUS_FOUND)
set(libobs_PLATFORM_SOURCES ${libobs_PLATFORM_SOURCES}
Expand Down Expand Up @@ -334,7 +366,10 @@ set(libobs_HEADERS
${libobs_graphics_HEADERS}
${libobs_mediaio_HEADERS}
${libobs_util_HEADERS}
${libobs_libobs_HEADERS})
${libobs_libobs_HEADERS}
${libobs_audio_monitoring_SOURCES}
${libobs_audio_monitoring_HEADERS}
)

source_group("callback\\Source Files" FILES ${libobs_callback_SOURCES})
source_group("callback\\Header Files" FILES ${libobs_callback_HEADERS})
Expand All @@ -346,6 +381,8 @@ source_group("media-io\\Source Files" FILES ${libobs_mediaio_SOURCES})
source_group("media-io\\Header Files" FILES ${libobs_mediaio_HEADERS})
source_group("util\\Source Files" FILES ${libobs_util_SOURCES})
source_group("util\\Header Files" FILES ${libobs_util_HEADERS})
source_group("audio-monitoring\\Source Files" FILES ${libobs_audio_monitoring_SOURCES})
source_group("audio-monitoring\\Header Files" FILES ${libobs_audio_monitoring_HEADERS})

if(BUILD_CAPTIONS)
include_directories(${CMAKE_SOURCE_DIR}/deps/libcaption)
Expand Down
23 changes: 23 additions & 0 deletions libobs/audio-monitoring/null/null-audio-monitoring.c
@@ -0,0 +1,23 @@
#include "../../obs-internal.h"

void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)
{
UNUSED_PARAMETER(cb);
UNUSED_PARAMETER(data);
}

struct audio_monitor *audio_monitor_create(obs_source_t *source)
{
UNUSED_PARAMETER(source);
return NULL;
}

void audio_monitor_reset(struct audio_monitor *monitor)
{
UNUSED_PARAMETER(monitor);
}

void audio_monitor_destroy(struct audio_monitor *monitor)
{
UNUSED_PARAMETER(monitor);
}
96 changes: 96 additions & 0 deletions libobs/audio-monitoring/osx/coreaudio-enum-devices.c
@@ -0,0 +1,96 @@
#include <CoreFoundation/CFString.h>
#include <CoreAudio/CoreAudio.h>

#include "../../obs-internal.h"
#include "../../util/dstr.h"

#include "mac-helpers.h"

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 void obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb,
void *data, AudioDeviceID id)
{
UInt32 size = 0;
CFStringRef cf_name = NULL;
CFStringRef cf_uid = NULL;
char name[1024];
char uid[1024];
OSStatus stat;

AudioObjectPropertyAddress addr = {
kAudioDevicePropertyStreams,
kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster
};

/* 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_uid);
if (!success(stat, "get audio device UID"))
return;

addr.mSelector = kAudioDevicePropertyDeviceNameCFString;
stat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);
if (!success(stat, "get audio device name"))
goto fail;

if (!cf_to_cstr(cf_name, name, sizeof(name))) {
blog(LOG_WARNING, "%s: failed to convert name", __FUNCTION__);
goto fail;
}

if (!cf_to_cstr(cf_uid, uid, sizeof(uid))) {
blog(LOG_WARNING, "%s: failed to convert uid", __FUNCTION__);
goto fail;
}

cb(data, name, uid);

fail:
if (cf_name)
CFRelease(cf_name);
if (cf_uid)
CFRelease(cf_uid);
}

void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)
{
AudioObjectPropertyAddress addr = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};

UInt32 size = 0;
UInt32 count;
OSStatus stat;
AudioDeviceID *ids;

stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
0, NULL, &size);
if (!success(stat, "get data size"))
return;

ids = malloc(size);
count = size / sizeof(AudioDeviceID);

stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
0, NULL, &size, ids);
if (success(stat, "get data")) {
for (UInt32 i = 0; i < count; i++)
obs_enum_audio_monitoring_device(cb, data, ids[i]);
}

free(ids);
}

0 comments on commit d2934ec

Please sign in to comment.