diff --git a/ChangeLog b/ChangeLog index f60b58e0..3762f4fc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,7 +22,12 @@ Michael Voigt --------------------------- Jackdmp changes log --------------------------- - + +2009-02-25 Stephane Letz + + * Fix JackNetDriver::Close method. + * For audio device reservation, add card_to_num function. + 2009-02-24 Stephane Letz * New libjacknet library with net.h and JackNetAPI.cpp files. New netmaster.c and netmaster.c examples. @@ -30,13 +35,14 @@ Michael Voigt 2009-02-23 Stephane Letz - * Another fix in systemdeps.h and types.h: jack_time_t now uniquely defined in types.h. + * Another fix in systemdeps.h and types.h: jack_time_t now uniquely defined in types.h. * Move generic code and data in JackNetInterface and JackNetMasterInterface classes. + * First version of D-Bus based audio device rerservation. 2009-02-20 Stephane Letz * Add InitConnection and InitRendering methods in JackNetSlaveInterface, better packet type checking in JackNetSlaveInterface::SyncRecv. - * Change fMulticastIP handling in JackNetInterface. + * Change fMulticastIP handling in JackNetInterface. * Cleanup systemdeps.h on Windows. 2009-02-17 Stephane Letz diff --git a/common/JackAudioAdapterInterface.h b/common/JackAudioAdapterInterface.h index 07366d8c..83b15109 100644 --- a/common/JackAudioAdapterInterface.h +++ b/common/JackAudioAdapterInterface.h @@ -22,6 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackResampler.h" #include "JackFilters.h" +#include "JackConstants.h" namespace Jack { diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index 7fd71a0d..0bec8906 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -576,8 +576,8 @@ jackctl_wait_signals(sigset_t signals) // driver exit waiting = false; break; - case SIGTTOU: - break; + case SIGTTOU: + break; default: waiting = false; break; diff --git a/common/JackInternalClient.cpp b/common/JackInternalClient.cpp index d7d377ef..45350802 100644 --- a/common/JackInternalClient.cpp +++ b/common/JackInternalClient.cpp @@ -179,20 +179,20 @@ int JackLoadableInternalClient::Init(const char* so_name) fHandle = LoadJackModule(path_to_so); jack_log("JackLoadableInternalClient::JackLoadableInternalClient path_to_so = %s", path_to_so); - if (fHandle == 0) { + if (fHandle == NULL) { PrintLoadError(so_name); return -1; } fFinish = (FinishCallback)GetJackProc(fHandle, "jack_finish"); - if (!fFinish) { + if (fFinish == NULL) { UnloadJackModule(fHandle); jack_error("symbol jack_finish cannot be found in %s", so_name); return -1; } fDescriptor = (JackDriverDescFunction)GetJackProc(fHandle, "jack_get_descriptor"); - if (!fDescriptor) { + if (fDescriptor == NULL) { jack_info("No jack_get_descriptor entry-point for %s", so_name); } return 0; @@ -205,7 +205,7 @@ int JackLoadableInternalClient1::Init(const char* so_name) } fInitialize = (InitializeCallback)GetJackProc(fHandle, "jack_initialize"); - if (!fInitialize) { + if (fInitialize == NULL) { UnloadJackModule(fHandle); jack_error("symbol jack_initialize cannot be found in %s", so_name); return -1; @@ -221,7 +221,7 @@ int JackLoadableInternalClient2::Init(const char* so_name) } fInitialize = (InternalInitializeCallback)GetJackProc(fHandle, "jack_internal_initialize"); - if (!fInitialize) { + if (fInitialize == NULL) { UnloadJackModule(fHandle); jack_error("symbol jack_internal_initialize cannot be found in %s", so_name); return -1; @@ -244,9 +244,10 @@ JackLoadableInternalClient2::JackLoadableInternalClient2(JackServer* server, Jac JackLoadableInternalClient::~JackLoadableInternalClient() { - if (fFinish) + if (fFinish != NULL) fFinish(fProcessArg); - UnloadJackModule(fHandle); + if (fHandle != NULL) + UnloadJackModule(fHandle); } int JackLoadableInternalClient1::Open(const char* server_name, const char* name, jack_options_t options, jack_status_t* status) diff --git a/common/JackNetAPI.cpp b/common/JackNetAPI.cpp index 7e41e8c8..8c2addc6 100644 --- a/common/JackNetAPI.cpp +++ b/common/JackNetAPI.cpp @@ -124,7 +124,7 @@ namespace Jack struct JackNetExtMaster : public JackNetMasterInterface { - //sample buffers + // Data buffers float** fAudioCaptureBuffer; float** fAudioPlaybackBuffer; @@ -142,8 +142,8 @@ struct JackNetExtMaster : public JackNetMasterInterface { assert(strlen(ip) < 32); strcpy(fMulticastIP, ip); fSocket.SetPort(port); - fRequest.buffer_size = request->buffer_size; - fRequest.sample_rate = request->sample_rate; + fRequest.buffer_size = request->buffer_size; + fRequest.sample_rate = request->sample_rate; } virtual ~JackNetExtMaster() @@ -391,8 +391,7 @@ struct JackNetExtMaster : public JackNetMasterInterface { { return 0; } - - + }; struct JackNetExtSlave : public JackNetSlaveInterface, public JackRunnableInterface { diff --git a/common/JackNetDriver.cpp b/common/JackNetDriver.cpp index 5b17afb4..7b17ac90 100644 --- a/common/JackNetDriver.cpp +++ b/common/JackNetDriver.cpp @@ -91,14 +91,15 @@ namespace Jack } } -#ifdef JACK_MONITOR int JackNetDriver::Close() { +#ifdef JACK_MONITOR if ( fNetTimeMon ) fNetTimeMon->Save(); +#endif + FreeAll(); return JackDriver::Close(); } -#endif // Attach and Detach are defined as empty methods: port allocation is done when driver actually start (that is in Init) int JackNetDriver::Attach() @@ -122,8 +123,10 @@ namespace Jack jack_log ( "JackNetDriver::Init()" ); //new loading, but existing socket, restart the driver - if ( fSocket.IsSocket() ) - Restart(); + if (fSocket.IsSocket()) { + jack_info( "Restarting driver..." ); + FreeAll(); + } //set the parameters to send fParams.fSendAudioChannels = fCaptureChannels; @@ -206,28 +209,28 @@ namespace Jack return true; } - void JackNetDriver::Restart() + void JackNetDriver::FreeAll() { - jack_log ( "JackNetDriver::Restart" ); - - jack_info ( "Restarting driver..." ); + FreePorts(); + delete[] fTxBuffer; - fTxBuffer = NULL; delete[] fRxBuffer; - fRxBuffer = NULL; delete fNetAudioCaptureBuffer; - fNetAudioCaptureBuffer = NULL; delete fNetAudioPlaybackBuffer; - fNetAudioPlaybackBuffer = NULL; delete fNetMidiCaptureBuffer; - fNetMidiCaptureBuffer = NULL; delete fNetMidiPlaybackBuffer; - fNetMidiPlaybackBuffer = NULL; - FreePorts(); delete[] fMidiCapturePortList; - fMidiCapturePortList = NULL; delete[] fMidiPlaybackPortList; + + fTxBuffer = NULL; + fRxBuffer = NULL; + fNetAudioCaptureBuffer = NULL; + fNetAudioPlaybackBuffer = NULL; + fNetMidiCaptureBuffer = NULL; + fNetMidiPlaybackBuffer = NULL; + fMidiCapturePortList = NULL; fMidiPlaybackPortList = NULL; + #ifdef JACK_MONITOR delete fNetTimeMon; fNetTimeMon = NULL; @@ -353,13 +356,17 @@ namespace Jack int audio_port_index; uint midi_port_index; for ( audio_port_index = 0; audio_port_index < fCaptureChannels; audio_port_index++ ) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fCapturePortList[audio_port_index] ); + if (fCapturePortList[audio_port_index] > 0) + fGraphManager->ReleasePort ( fClientControl.fRefNum, fCapturePortList[audio_port_index] ); for ( audio_port_index = 0; audio_port_index < fPlaybackChannels; audio_port_index++ ) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fPlaybackPortList[audio_port_index] ); + if (fPlaybackPortList[audio_port_index] > 0) + fGraphManager->ReleasePort ( fClientControl.fRefNum, fPlaybackPortList[audio_port_index] ); for ( midi_port_index = 0; midi_port_index < fParams.fSendMidiChannels; midi_port_index++ ) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiCapturePortList[midi_port_index] ); + if (fMidiCapturePortList[midi_port_index] > 0) + fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiCapturePortList[midi_port_index] ); for ( midi_port_index = 0; midi_port_index < fParams.fReturnMidiChannels; midi_port_index++ ) - fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index] ); + if (fMidiPlaybackPortList[midi_port_index] > 0) + fGraphManager->ReleasePort ( fClientControl.fRefNum, fMidiPlaybackPortList[midi_port_index] ); return 0; } diff --git a/common/JackNetDriver.h b/common/JackNetDriver.h index 9aced551..af432084 100644 --- a/common/JackNetDriver.h +++ b/common/JackNetDriver.h @@ -51,7 +51,8 @@ namespace Jack #endif bool Init(); - void Restart(); + void FreeAll(); + int AllocPorts(); int FreePorts(); @@ -71,10 +72,7 @@ namespace Jack int Open ( jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing, int inchannels, int outchannels, bool monitor, const char* capture_driver_name, const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency ); - -#ifdef JACK_MONITOR int Close(); -#endif int Attach(); int Detach(); diff --git a/common/JackWaitThreadedDriver.h b/common/JackWaitThreadedDriver.h index 93dc3536..876b6551 100644 --- a/common/JackWaitThreadedDriver.h +++ b/common/JackWaitThreadedDriver.h @@ -31,7 +31,7 @@ namespace Jack \brief To be used as a wrapper of JackNetDriver. The idea is to behave as the "dummy" driver, until the network connection is really started and processing starts. -The Execute method will call the ProcessNull() methods until the decorated driver Init method returns. +The Execute method will call the ProcessNull method until the decorated driver Init method returns. A helper JackDriverStarter thread is used for that purpose. */ diff --git a/common/wscript b/common/wscript index 026ec5c8..0156c274 100644 --- a/common/wscript +++ b/common/wscript @@ -139,6 +139,9 @@ def build(bld): '../posix/JackSocketServerNotifyChannel.cpp', '../posix/JackNetUnixSocket.cpp', ] + + if bld.env['IS_LINUX'] and bld.env['BUILD_JACKDBUS']: + serverlib.source += ['../dbus/reserve.c', '../dbus/audio_reserve.c'] if bld.env['IS_SUN']: serverlib.source += [ diff --git a/dbus/audio_reserve.c b/dbus/audio_reserve.c new file mode 100644 index 00000000..cae5eac9 --- /dev/null +++ b/dbus/audio_reserve.c @@ -0,0 +1,93 @@ +/* + Copyright (C) 2009 Grame + + 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. + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include + +#include "reserve.h" +#include "audio_reserve.h" +#include "JackError.h" + +static DBusConnection* connection = NULL; + +SERVER_EXPORT int audio_reservation_init() +{ + DBusError error; + + dbus_error_init(&error); + + if (!(connection = dbus_bus_get(DBUS_BUS_SESSION, &error))) { + jack_error("Failed to connect to session bus for device reservation %s\n", error.message); + return -1; + } + + return 0; +} + +SERVER_EXPORT int audio_reservation_finish() +{ + if (connection) + dbus_connection_unref(connection); + return 0; +} + +SERVER_EXPORT void* audio_acquire(int num) +{ + DBusError error; + rd_device* device; + char audio_name[32]; + int e; + + snprintf(audio_name, sizeof(audio_name) - 1, "Audio%d", num); + if ((e = rd_acquire( + &device, + connection, + audio_name, + "Jack audio server", + INT32_MAX, + NULL, + &error)) < 0) { + + jack_error ("Failed to acquire device: %s\n", error.message ? error.message : strerror(-e)); + return NULL; + } + + jack_info("Acquire audio card %s", audio_name); + return (void*)device; +} + +SERVER_EXPORT void audio_reserve_loop() +{ + if (connection) { + while (dbus_connection_read_write_dispatch (connection, -1)) + ; // empty loop body + } +} + +SERVER_EXPORT void audio_release(void* dev) +{ + rd_device* device = (rd_device*)dev; + if (device) { + jack_info("Release audio card"); + rd_release(device); + } +} diff --git a/dbus/audio_reserve.h b/dbus/audio_reserve.h new file mode 100644 index 00000000..9a5c34c5 --- /dev/null +++ b/dbus/audio_reserve.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2009 Grame + + 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. + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __audio_reserve__ +#define __audio_reserve__ + +#include "JackCompilerDeps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +SERVER_EXPORT int audio_reservation_init(); +SERVER_EXPORT int audio_reservation_finish(); + +SERVER_EXPORT void* audio_acquire(int num); +SERVER_EXPORT void audio_release(void* dev); +SERVER_EXPORT void audio_reserve_loop(); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/dbus/reserve.c b/dbus/reserve.c new file mode 100644 index 00000000..9a9591d2 --- /dev/null +++ b/dbus/reserve.c @@ -0,0 +1,635 @@ +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include +#include +#include +#include +#include + +#include "reserve.h" + +struct rd_device { + int ref; + + char *device_name; + char *application_name; + char *application_device_name; + char *service_name; + char *object_path; + int32_t priority; + + DBusConnection *connection; + + int owning:1; + int registered:1; + int filtering:1; + int gave_up:1; + + rd_request_cb_t request_cb; + void *userdata; +}; + + +#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1." +#define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/" + +static const char introspection[] = + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE + "" + " \n" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +static dbus_bool_t add_variant( + DBusMessage *m, + int type, + const void *data) { + + DBusMessageIter iter, sub; + char t[2]; + + t[0] = (char) type; + t[1] = 0; + + dbus_message_iter_init_append(m, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub)) + return FALSE; + + if (!dbus_message_iter_append_basic(&sub, type, data)) + return FALSE; + + if (!dbus_message_iter_close_container(&iter, &sub)) + return FALSE; + + return TRUE; +} + +static DBusHandlerResult object_handler( + DBusConnection *c, + DBusMessage *m, + void *userdata) { + + rd_device *d; + DBusError error; + DBusMessage *reply = NULL; + + dbus_error_init(&error); + + d = userdata; + assert(d->ref >= 1); + + if (dbus_message_is_method_call( + m, + "org.freedesktop.ReserveDevice1", + "RequestRelease")) { + + int32_t priority; + dbus_bool_t ret; + + if (!dbus_message_get_args( + m, + &error, + DBUS_TYPE_INT32, &priority, + DBUS_TYPE_INVALID)) + goto invalid; + + ret = FALSE; + + if (priority > d->priority && d->request_cb) { + d->ref++; + + if (d->request_cb(d, 0) > 0) { + ret = TRUE; + d->gave_up = 1; + } + + rd_release(d); + } + + if (!(reply = dbus_message_new_method_return(m))) + goto oom; + + if (!dbus_message_append_args( + reply, + DBUS_TYPE_BOOLEAN, &ret, + DBUS_TYPE_INVALID)) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + + } else if (dbus_message_is_method_call( + m, + "org.freedesktop.DBus.Properties", + "Get")) { + + const char *interface, *property; + + if (!dbus_message_get_args( + m, + &error, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID)) + goto invalid; + + if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) { + const char *empty = ""; + + if (strcmp(property, "ApplicationName") == 0 && d->application_name) { + if (!(reply = dbus_message_new_method_return(m))) + goto oom; + + if (!add_variant( + reply, + DBUS_TYPE_STRING, + d->application_name ? (const char**) &d->application_name : &empty)) + goto oom; + + } else if (strcmp(property, "ApplicationDeviceName") == 0) { + if (!(reply = dbus_message_new_method_return(m))) + goto oom; + + if (!add_variant( + reply, + DBUS_TYPE_STRING, + d->application_device_name ? (const char**) &d->application_device_name : &empty)) + goto oom; + + } else if (strcmp(property, "Priority") == 0) { + if (!(reply = dbus_message_new_method_return(m))) + goto oom; + + if (!add_variant( + reply, + DBUS_TYPE_INT32, + &d->priority)) + goto oom; + } else { + if (!(reply = dbus_message_new_error_printf( + m, + DBUS_ERROR_UNKNOWN_METHOD, + "Unknown property %s", + property))) + goto oom; + } + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + } else if (dbus_message_is_method_call( + m, + "org.freedesktop.DBus.Introspectable", + "Introspect")) { + const char *i = introspection; + + if (!(reply = dbus_message_new_method_return(m))) + goto oom; + + if (!dbus_message_append_args( + reply, + DBUS_TYPE_STRING, + &i, + DBUS_TYPE_INVALID)) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +invalid: + if (reply) + dbus_message_unref(reply); + + if (!(reply = dbus_message_new_error( + m, + DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"))) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + +static DBusHandlerResult filter_handler( + DBusConnection *c, + DBusMessage *m, + void *userdata) { + + DBusMessage *reply; + rd_device *d; + DBusError error; + + dbus_error_init(&error); + + d = userdata; + + if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) { + const char *name; + + if (!dbus_message_get_args( + m, + &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + goto invalid; + + if (strcmp(name, d->service_name) == 0 && d->owning) { + d->owning = 0; + + if (!d->gave_up) { + d->ref++; + + if (d->request_cb) + d->request_cb(d, 1); + d->gave_up = 1; + + rd_release(d); + } + + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +invalid: + if (!(reply = dbus_message_new_error( + m, + DBUS_ERROR_INVALID_ARGS, + "Invalid arguments"))) + goto oom; + + if (!dbus_connection_send(c, reply, NULL)) + goto oom; + + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_HANDLED; + +oom: + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + + +static const struct DBusObjectPathVTable vtable ={ + .message_function = object_handler +}; + +int rd_acquire( + rd_device **_d, + DBusConnection *connection, + const char *device_name, + const char *application_name, + int32_t priority, + rd_request_cb_t request_cb, + DBusError *error) { + + rd_device *d = NULL; + int r, k; + DBusError _error; + DBusMessage *m = NULL, *reply = NULL; + dbus_bool_t good; + + if (!error) + error = &_error; + + dbus_error_init(error); + + if (!_d) + return -EINVAL; + + if (!connection) + return -EINVAL; + + if (!device_name) + return -EINVAL; + + if (!request_cb && priority != INT32_MAX) + return -EINVAL; + + if (!(d = calloc(sizeof(rd_device), 1))) + return -ENOMEM; + + d->ref = 1; + + if (!(d->device_name = strdup(device_name))) { + r = -ENOMEM; + goto fail; + } + + if (!(d->application_name = strdup(application_name))) { + r = -ENOMEM; + goto fail; + } + + d->priority = priority; + d->connection = dbus_connection_ref(connection); + d->request_cb = request_cb; + + if (!(d->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) { + r = -ENOMEM; + goto fail; + } + sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name); + + if (!(d->object_path = malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) { + r = -ENOMEM; + goto fail; + } + sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name); + + if ((k = dbus_bus_request_name( + d->connection, + d->service_name, + DBUS_NAME_FLAG_DO_NOT_QUEUE| + (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0), + error)) < 0) { + r = -EIO; + goto fail; + } + + if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + goto success; + + if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) { + r = -EIO; + goto fail; + } + + if (priority <= INT32_MIN) { + r = -EBUSY; + goto fail; + } + + if (!(m = dbus_message_new_method_call( + d->service_name, + d->object_path, + "org.freedesktop.ReserveDevice1", + "RequestRelease"))) { + r = -ENOMEM; + goto fail; + } + + if (!dbus_message_append_args( + m, + DBUS_TYPE_INT32, &d->priority, + DBUS_TYPE_INVALID)) { + r = -ENOMEM; + goto fail; + } + + if (!(reply = dbus_connection_send_with_reply_and_block( + d->connection, + m, + 5000, /* 5s */ + error))) { + + if (dbus_error_has_name(error, DBUS_ERROR_TIMED_OUT) || + dbus_error_has_name(error, DBUS_ERROR_UNKNOWN_METHOD) || + dbus_error_has_name(error, DBUS_ERROR_NO_REPLY)) { + /* This must be treated as denied. */ + r = -EBUSY; + goto fail; + } + + r = -EIO; + goto fail; + } + + if (!dbus_message_get_args( + reply, + error, + DBUS_TYPE_BOOLEAN, &good, + DBUS_TYPE_INVALID)) { + r = -EIO; + goto fail; + } + + if (!good) { + r = -EBUSY; + goto fail; + } + + if ((k = dbus_bus_request_name( + d->connection, + d->service_name, + DBUS_NAME_FLAG_DO_NOT_QUEUE| + (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)| + DBUS_NAME_FLAG_REPLACE_EXISTING, + error)) < 0) { + r = -EIO; + goto fail; + } + + if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + r = -EIO; + goto fail; + } + +success: + d->owning = 1; + + if (!(dbus_connection_register_object_path( + d->connection, + d->object_path, + &vtable, + d))) { + r = -ENOMEM; + goto fail; + } + + d->registered = 1; + + if (!dbus_connection_add_filter( + d->connection, + filter_handler, + d, + NULL)) { + r = -ENOMEM; + goto fail; + } + + d->filtering = 1; + + *_d = d; + return 0; + +fail: + if (m) + dbus_message_unref(m); + + if (reply) + dbus_message_unref(reply); + + if (&_error == error) + dbus_error_free(&_error); + + if (d) + rd_release(d); + + return r; +} + +void rd_release( + rd_device *d) { + + if (!d) + return; + + assert(d->ref > 0); + + if (--d->ref) + return; + + + if (d->filtering) + dbus_connection_remove_filter( + d->connection, + filter_handler, + d); + + if (d->registered) + dbus_connection_unregister_object_path( + d->connection, + d->object_path); + + if (d->owning) { + DBusError error; + dbus_error_init(&error); + + dbus_bus_release_name( + d->connection, + d->service_name, + &error); + + dbus_error_free(&error); + } + + free(d->device_name); + free(d->application_name); + free(d->application_device_name); + free(d->service_name); + free(d->object_path); + + if (d->connection) + dbus_connection_unref(d->connection); + + free(d); +} + +int rd_set_application_device_name(rd_device *d, const char *n) { + char *t; + + if (!d) + return -EINVAL; + + assert(d->ref > 0); + + if (!(t = strdup(n))) + return -ENOMEM; + + free(d->application_device_name); + d->application_device_name = t; + return 0; +} + +void rd_set_userdata(rd_device *d, void *userdata) { + + if (!d) + return; + + assert(d->ref > 0); + d->userdata = userdata; +} + +void* rd_get_userdata(rd_device *d) { + + if (!d) + return NULL; + + assert(d->ref > 0); + + return d->userdata; +} diff --git a/dbus/reserve.h b/dbus/reserve.h new file mode 100644 index 00000000..b315a08c --- /dev/null +++ b/dbus/reserve.h @@ -0,0 +1,69 @@ +#ifndef fooreservehfoo +#define fooreservehfoo + +/*** + Copyright 2009 Lennart Poettering + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +***/ + +#include +#include + +typedef struct rd_device rd_device; + +/* Prototype for a function that is called whenever someone else wants + * your application to release the device it has locked. A return + * value <= 0 denies the request, a positive return value agrees to + * it. Before returning your application should close the device in + * question completely to make sure the new application may access + * it. */ +typedef int (*rd_request_cb_t)( + rd_device *d, + int forced); /* Non-zero if an application forcibly took the lock away without asking. If this is the case then the return value of this call is ignored. */ + +/* Try to lock the device. Returns 0 on success, a negative errno + * style return value on error. The DBus error might be set as well if + * the error was caused D-Bus. */ +int rd_acquire( + rd_device **d, /* On success a pointer to the newly allocated rd_device object will be filled in here */ + DBusConnection *connection, + const char *device_name, /* The device to lock, e.g. "Audio0" */ + const char *application_name, /* A human readable name of the application, e.g. "PulseAudio Sound Server" */ + int32_t priority, /* The priority for this application. If unsure use 0 */ + rd_request_cb_t request_cb, /* Will be called whenever someone requests that this device shall be released. May be NULL if priority is INT32_MAX */ + DBusError *error); /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */ + +/* Unlock (if needed) and destroy an rd_device object again */ +void rd_release(rd_device *d); + +/* Set the application device name for an rd_device object. Returns 0 + * on success, a negative errno style return value on error. */ +int rd_set_application_device_name(rd_device *d, const char *name); + +/* Attach a userdata pointer to an rd_device */ +void rd_set_userdata(rd_device *d, void *userdata); + +/* Query the userdata pointer from an rd_device. Returns NULL if no + * userdata was set. */ +void* rd_get_userdata(rd_device *d); + +#endif diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 04d85b02..455f73ca 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -49,7 +49,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "generic.h" #include "memops.h" - +#include "audio_reserve.h" namespace Jack { @@ -2138,6 +2138,23 @@ int JackAlsaDriver::Detach() return JackAudioDriver::Detach(); } +#if defined(JACK_DBUS) +static int card_to_num(const char* device) +{ + const char* t; + int i; + + if ((t = strchr(device, ':'))) + device = t + 1; + + if ((i = snd_card_get_index(device)) < 0) { + i = atoi(device); + } + + return i; +} +#endif + int JackAlsaDriver::Open(jack_nframes_t nframes, jack_nframes_t user_nperiods, jack_nframes_t samplerate, @@ -2170,6 +2187,29 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, else if (strcmp(midi_driver_name, "raw") == 0) midi = alsa_rawmidi_new((jack_client_t*)this); +#if defined(JACK_DBUS) + if (audio_reservation_init() < 0) { + jack_error("Audio device reservation service not available...."); + } else if (strcmp(capture_driver_name, playback_driver_name) == 0) { // Same device for input and output + fReservedCaptureDevice = audio_acquire(card_to_num(capture_driver_name)); + if (fReservedCaptureDevice == NULL) { + jack_error("Error audio device %s not available...", capture_driver_name); + return -1; + } + } else { + fReservedCaptureDevice = audio_acquire(card_to_num(capture_driver_name)); + if (fReservedCaptureDevice == NULL) { + jack_error("Error capture audio device %s not available...", capture_driver_name); + return -1; + } + fReservedPlaybackDevice = audio_acquire(card_to_num(playback_driver_name)); + if (fReservedPlaybackDevice == NULL) { + jack_error("Error playback audio device %s not available...", playback_driver_name); + return -1; + } + } +#endif + fDriver = alsa_driver_new ("alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name, NULL, nframes, @@ -2203,6 +2243,11 @@ int JackAlsaDriver::Close() { JackAudioDriver::Close(); alsa_driver_delete((alsa_driver_t*)fDriver); +#if defined(JACK_DBUS) + audio_release(fReservedCaptureDevice); + audio_release(fReservedPlaybackDevice); + audio_reservation_finish(); +#endif return 0; } diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index c6debff2..6fab0baf 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -40,6 +40,8 @@ class JackAlsaDriver : public JackAudioDriver private: jack_driver_t* fDriver; + void* fReservedCaptureDevice; + void* fReservedPlaybackDevice; void alsa_driver_release_channel_dependent_memory(alsa_driver_t *driver); int alsa_driver_check_capabilities(alsa_driver_t *driver); @@ -116,7 +118,11 @@ class JackAlsaDriver : public JackAudioDriver public: - JackAlsaDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table): JackAudioDriver(name, alias, engine, table) + JackAlsaDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) + : JackAudioDriver(name, alias, engine, table) + ,fDriver(NULL) + ,fReservedCaptureDevice(NULL) + ,fReservedPlaybackDevice(NULL) {} virtual ~JackAlsaDriver() {} diff --git a/linux/wscript b/linux/wscript index 0df5e866..5751bf60 100644 --- a/linux/wscript +++ b/linux/wscript @@ -16,7 +16,7 @@ def create_jack_driver_obj(bld, target, sources, uselib = None): driver.features.append('cc') driver.env['shlib_PATTERN'] = 'jack_%s.so' driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] - driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack'] + driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus'] driver.target = target driver.source = sources driver.install_path = '${ADDON_DIR}/' @@ -26,7 +26,7 @@ def create_jack_driver_obj(bld, target, sources, uselib = None): def build(bld): jackd = bld.new_task_gen('cxx', 'program') - jackd.includes = ['../linux', '../posix', '../common/jack', '../common'] + jackd.includes = ['../linux', '../posix', '../common/jack', '../common', '../dbus'] jackd.defines = 'HAVE_CONFIG_H' jackd.source = ['../common/Jackdmp.cpp'] jackd.uselib = 'PTHREAD DL RT'