Pārlūkot izejas kodu

Initial obs-studio jack support

Bernd Buschinski 10 gadi atpakaļ
vecāks
revīzija
e04dc57e8a

+ 82 - 0
cmake/Modules/FindJack.cmake

@@ -0,0 +1,82 @@
+# - Try to find jack-2.6
+# Once done this will define
+#
+#  JACK_FOUND - system has jack
+#  JACK_INCLUDE_DIRS - the jack include directory
+#  JACK_LIBRARIES - Link these to use jack
+#  JACK_DEFINITIONS - Compiler switches required for using jack
+#
+#  Copyright (c) 2008 Andreas Schneider <[email protected]>
+#  Modified for other libraries by Lasse Kärkkäinen <tronic>
+#
+#  Redistribution and use is allowed according to the terms of the New
+#  BSD license.
+#  For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+#
+
+if (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
+  # in cache already
+  set(JACK_FOUND TRUE)
+else (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
+  # use pkg-config to get the directories and then use these values
+  # in the FIND_PATH() and FIND_LIBRARY() calls
+  if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
+    include(UsePkgConfig)
+    pkgconfig(jack _JACK_INCLUDEDIR _JACK_LIBDIR _JACK_LDFLAGS _JACK_CFLAGS)
+  else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
+    find_package(PkgConfig)
+    if (PKG_CONFIG_FOUND)
+      pkg_check_modules(_JACK jack)
+    endif (PKG_CONFIG_FOUND)
+  endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
+  find_path(JACK_INCLUDE_DIR
+    NAMES
+      jack/jack.h
+    PATHS
+      ${_JACK_INCLUDEDIR}
+      /usr/include
+      /usr/local/include
+      /opt/local/include
+      /sw/include
+  )
+
+  find_library(JACK_LIBRARY
+    NAMES
+      jack
+    PATHS
+      ${_JACK_LIBDIR}
+      /usr/lib
+      /usr/local/lib
+      /opt/local/lib
+      /sw/lib
+  )
+
+  if (JACK_LIBRARY AND JACK_INCLUDE_DIR)
+    set(JACK_FOUND TRUE)
+
+    set(JACK_INCLUDE_DIRS
+      ${JACK_INCLUDE_DIR}
+    )
+
+    set(JACK_LIBRARIES
+      ${JACK_LIBRARIES}
+      ${JACK_LIBRARY}
+    )
+
+  endif (JACK_LIBRARY AND JACK_INCLUDE_DIR)
+
+  if (JACK_FOUND)
+    if (NOT JACK_FIND_QUIETLY)
+      message(STATUS "Found jack: ${JACK_LIBRARY}")
+    endif (NOT JACK_FIND_QUIETLY)
+  else (JACK_FOUND)
+    if (JACK_FIND_REQUIRED)
+      message(FATAL_ERROR "Could not find JACK")
+    endif (JACK_FIND_REQUIRED)
+  endif (JACK_FOUND)
+
+  # show the JACK_INCLUDE_DIRS and JACK_LIBRARIES variables only in the advanced view
+  mark_as_advanced(JACK_INCLUDE_DIRS JACK_LIBRARIES)
+
+endif (JACK_LIBRARIES AND JACK_INCLUDE_DIRS)
+

+ 1 - 0
plugins/CMakeLists.txt

@@ -11,6 +11,7 @@ elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
 	add_subdirectory(linux-capture)
 	add_subdirectory(linux-pulseaudio)
 	add_subdirectory(linux-v4l2)
+	add_subdirectory(linux-jack)
 endif()
 
 add_subdirectory(image-source)

+ 32 - 0
plugins/linux-jack/CMakeLists.txt

@@ -0,0 +1,32 @@
+project(linux-jack)
+
+if(DISABLE_JACK)
+	message(STATUS "JACK support disabled")
+	return()
+endif()
+
+find_package(Jack)
+if(NOT JACK_FOUND AND ENABLE_JACK)
+	message(FATAL_ERROR "JACK Audio Connection Kit not found but set as enabled")
+elseif(NOT JACK_FOUND)
+	message(STATUS "JACK Audio Connection Kit not found, disabling JACK plugin")
+	return()
+endif()
+
+include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs")
+
+set(linux-jack_SOURCES
+	linux-jack.c
+	jack-wrapper.c
+	jack-input.c
+)
+
+add_library(linux-jack MODULE
+	${linux-jack_SOURCES}
+)
+target_link_libraries(linux-jack
+	libobs
+	${JACK_LIBRARIES}
+)
+
+install_obs_plugin_with_data(linux-jack data)

+ 3 - 0
plugins/linux-jack/data/locale/en-US.ini

@@ -0,0 +1,3 @@
+StartJACKServer="Start JACK Server"
+Channels="Number of Channels"
+JACKInput="JACK Input Client"

+ 149 - 0
plugins/linux-jack/jack-input.c

@@ -0,0 +1,149 @@
+/*
+Copyright (C) 2015 by Bernd Buschinski <[email protected]>
+
+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 "jack-wrapper.h"
+
+#include <obs-module.h>
+
+/**
+ * Returns the name of the plugin
+ */
+static const char *jack_input_getname(void)
+{
+	return obs_module_text("JACKInput");
+}
+
+/**
+ * Destroy the plugin object and free all memory
+ */
+static void jack_destroy(void *vptr)
+{
+	struct jack_data* data = (struct jack_data*)vptr;
+
+	if (!data)
+		return;
+
+	deactivate_jack(data);
+
+	if (data->device)
+		bfree(data->device);
+	pthread_mutex_destroy(&data->jack_mutex);
+	bfree(data);
+}
+
+/**
+ * Update the input settings
+ */
+static void jack_update(void *vptr, obs_data_t *settings)
+{
+	struct jack_data* data = (struct jack_data*)vptr;
+	if (!data)
+		return;
+
+	const char *new_device;
+	bool settings_changed      = false;
+	bool new_jack_start_server = obs_data_get_bool(settings, "startjack");
+	int new_channel_count      = obs_data_get_int(settings, "channels");
+
+	if (new_jack_start_server != data->start_jack_server) {
+		data->start_jack_server = new_jack_start_server;
+		settings_changed = true;
+	}
+
+	if (new_channel_count != data->channels)
+		/*
+		 * keep "old" channel count  for now,
+		 * we need to destroy the correct number of channels
+		 */
+		settings_changed = true;
+
+	new_device = obs_source_get_name(data->source);
+	if (!data->device || strcmp(data->device, new_device) != 0) {
+		if (data->device)
+			bfree(data->device);
+		data->device = bstrdup(new_device);
+		settings_changed = true;
+	}
+
+	if (settings_changed) {
+		deactivate_jack(data);
+
+		data->channels = new_channel_count;
+
+		if (jack_init(data) != 0) {
+			deactivate_jack(data);
+		}
+	}
+}
+
+/**
+ * Create the plugin object
+ */
+static void *jack_create(obs_data_t *settings, obs_source_t *source)
+{
+	struct jack_data *data = bzalloc(sizeof(struct jack_data));
+
+	pthread_mutex_init(&data->jack_mutex, NULL);
+	data->source   = source;
+	data->channels = -1;
+
+	jack_update(data, settings);
+
+	if (data->jack_client == NULL) {
+		jack_destroy(data);
+		return NULL;
+	}
+	return data;
+}
+
+/**
+ * Get plugin defaults
+ */
+static void jack_input_defaults(obs_data_t *settings)
+{
+	obs_data_set_default_int(settings, "channels", 2);
+	obs_data_set_default_bool(settings, "startjack", false);
+}
+
+/**
+ * Get plugin properties
+ */
+static obs_properties_t *jack_input_properties(void *unused)
+{
+	(void)unused;
+
+	obs_properties_t *props = obs_properties_create();
+
+	obs_properties_add_int(props, "channels",
+		obs_module_text("Channels"), 1, 8, 1);
+	obs_properties_add_bool(props, "startjack",
+		obs_module_text("StartJACKServer"));
+
+	return props;
+}
+
+struct obs_source_info jack_output_capture = {
+	.id             = "jack_output_capture",
+	.type           = OBS_SOURCE_TYPE_INPUT,
+	.output_flags   = OBS_SOURCE_AUDIO,
+	.get_name       = jack_input_getname,
+	.create         = jack_create,
+	.destroy        = jack_destroy,
+	.update         = jack_update,
+	.get_defaults   = jack_input_defaults,
+	.get_properties = jack_input_properties
+};

+ 160 - 0
plugins/linux-jack/jack-wrapper.c

@@ -0,0 +1,160 @@
+/*
+Copyright (C) 2015 by Bernd Buschinski <[email protected]>
+
+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 "jack-wrapper.h"
+
+#include <pthread.h>
+#include <stdio.h>
+
+#include <util/platform.h>
+
+#define blog(level, msg, ...) blog(level, "jack-input: " msg, ##__VA_ARGS__)
+
+/**
+ * Get obs speaker layout from number of channels
+ *
+ * @param channels number of channels reported by jack
+ *
+ * @return obs speaker_layout id
+ *
+ * @note This *might* not work for some rather unusual setups, but should work
+ *       fine for the majority of cases.
+ */
+static enum speaker_layout jack_channels_to_obs_speakers(uint_fast32_t channels)
+{
+	switch(channels) {
+	case 1: return SPEAKERS_MONO;
+	case 2: return SPEAKERS_STEREO;
+	case 3: return SPEAKERS_2POINT1;
+	case 4: return SPEAKERS_SURROUND;
+	case 5: return SPEAKERS_4POINT1;
+	case 6: return SPEAKERS_5POINT1;
+	/* What should we do with 7 channels? */
+	/* case 7: return SPEAKERS_...; */
+	case 8: return SPEAKERS_7POINT1;
+	}
+
+	return SPEAKERS_UNKNOWN;
+}
+
+int jack_process_callback(jack_nframes_t nframes, void* arg)
+{
+	struct jack_data* data = (struct jack_data*)arg;
+	if (data == 0)
+		return 0;
+
+	pthread_mutex_lock(&data->jack_mutex);
+
+	struct obs_source_audio out;
+	out.speakers        = jack_channels_to_obs_speakers(data->channels);
+	out.samples_per_sec = jack_get_sample_rate (data->jack_client);
+	/* format is always 32 bit float for jack */
+	out.format          = AUDIO_FORMAT_FLOAT_PLANAR;
+
+	for (unsigned int i = 0; i < data->channels; ++i) {
+		jack_default_audio_sample_t *jack_buffer =
+			(jack_default_audio_sample_t *)jack_port_get_buffer(
+				data->jack_ports[i], nframes);
+		out.data[i] = (uint8_t *)jack_buffer;
+	}
+
+	out.frames    = nframes;
+	out.timestamp = os_gettime_ns() -
+				jack_frames_to_time(data->jack_client, nframes);
+
+	obs_source_output_audio(data->source, &out);
+	pthread_mutex_unlock(&data->jack_mutex);
+	return 0;
+}
+
+int_fast32_t jack_init(struct jack_data* data)
+{
+	pthread_mutex_lock(&data->jack_mutex);
+
+	if (data->jack_client != NULL)
+		goto good;
+
+	jack_options_t jack_option = data->start_jack_server ?
+		JackNullOption : JackNoStartServer;
+
+	data->jack_client = jack_client_open(data->device, jack_option, 0);
+	if (data->jack_client == NULL) {
+		blog(LOG_ERROR,
+			"jack_client_open Error:"
+			"Could not create JACK client! %s",
+			data->device);
+		goto error;
+	}
+
+	data->jack_ports = (jack_port_t**)bzalloc(
+		sizeof(jack_port_t*) * data->channels);
+	for (unsigned int i = 0; i < data->channels; ++i) {
+		char port_name[10] = {'\0'};
+		snprintf(port_name, sizeof(port_name), "in_%d", i+1);
+
+		data->jack_ports[i] = jack_port_register(data->jack_client,
+			port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+		if (data->jack_ports[i] == NULL) {
+			blog(LOG_ERROR,
+				"jack_port_register Error:"
+				"Could not create JACK port! %s",
+				port_name);
+			goto error;
+		}
+	}
+
+	if (jack_set_process_callback(data->jack_client,
+			jack_process_callback, data) != 0) {
+		blog(LOG_ERROR, "jack_set_process_callback Error");
+		goto error;
+	}
+
+	if (jack_activate(data->jack_client) != 0) {
+		blog(LOG_ERROR,
+			"jack_activate Error:"
+			"Could not activate JACK client!");
+		goto error;
+	}
+
+good:
+	pthread_mutex_unlock(&data->jack_mutex);
+	return 0;
+
+error:
+	pthread_mutex_unlock(&data->jack_mutex);
+	return 1;
+}
+
+void deactivate_jack(struct jack_data* data)
+{
+	pthread_mutex_lock(&data->jack_mutex);
+
+	if (data->jack_client) {
+		if (data->jack_ports != NULL) {
+			for (int i = 0; i < data->channels; ++i) {
+				if (data->jack_ports[i] != NULL)
+					jack_port_unregister(data->jack_client,
+						data->jack_ports[i]);
+			}
+			bfree(data->jack_ports);
+			data->jack_ports = NULL;
+		}
+		jack_client_close(data->jack_client);
+		data->jack_client = NULL;
+	}
+	pthread_mutex_unlock(&data->jack_mutex);
+}

+ 51 - 0
plugins/linux-jack/jack-wrapper.h

@@ -0,0 +1,51 @@
+/*
+Copyright (C) 2015 by Bernd Buschinski <[email protected]>
+
+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/>.
+*/
+
+#pragma once
+
+#include <jack/jack.h>
+#include <obs.h>
+#include <pthread.h>
+
+struct jack_data {
+	obs_source_t *source;
+
+	/* user settings */
+	char *device;
+	uint_fast8_t channels;
+	bool start_jack_server;
+
+	/* server info */
+	enum speaker_layout speakers;
+	uint_fast32_t samples_per_sec;
+	uint_fast32_t bytes_per_frame;
+
+	jack_client_t *jack_client;
+	jack_port_t **jack_ports;
+
+	pthread_mutex_t jack_mutex;
+};
+
+/**
+ * Initialize the jack client and register the ports
+ */
+int_fast32_t jack_init(struct jack_data* data);
+
+/**
+ * Destroys the jack client and unregisters the ports
+ */
+void deactivate_jack(struct jack_data* data);

+ 28 - 0
plugins/linux-jack/linux-jack.c

@@ -0,0 +1,28 @@
+/*
+Copyright (C) 2015 by Bernd Buschinski <[email protected]>
+
+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 <obs-module.h>
+
+OBS_DECLARE_MODULE()
+OBS_MODULE_USE_DEFAULT_LOCALE("linux-jack", "en-US")
+
+extern struct obs_source_info jack_output_capture;
+
+bool obs_module_load(void)
+{
+	obs_register_source(&jack_output_capture);
+	return true;
+}