git-svn-id: http://subversion.jackaudio.org/jack/jack2/branches/libjacknet@3354 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.9.8
| @@ -22,7 +22,12 @@ Michael Voigt | |||
| --------------------------- | |||
| Jackdmp changes log | |||
| --------------------------- | |||
| 2009-02-25 Stephane Letz <letz@grame.fr> | |||
| * Fix JackNetDriver::Close method. | |||
| * For audio device reservation, add card_to_num function. | |||
| 2009-02-24 Stephane Letz <letz@grame.fr> | |||
| * 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 <letz@grame.fr> | |||
| * 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 <letz@grame.fr> | |||
| * 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 <letz@grame.fr> | |||
| @@ -22,6 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| #include "JackResampler.h" | |||
| #include "JackFilters.h" | |||
| #include "JackConstants.h" | |||
| namespace Jack | |||
| { | |||
| @@ -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; | |||
| @@ -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) | |||
| @@ -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 { | |||
| @@ -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; | |||
| } | |||
| @@ -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(); | |||
| @@ -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. | |||
| */ | |||
| @@ -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 += [ | |||
| @@ -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 <string.h> | |||
| #include <unistd.h> | |||
| #include <errno.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <assert.h> | |||
| #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); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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 <string.h> | |||
| #include <unistd.h> | |||
| #include <errno.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <assert.h> | |||
| #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 | |||
| "<node>" | |||
| " <!-- If you are looking for documentation make sure to check out\n" | |||
| " http://git.0pointer.de/?p=reserve.git;a=blob;f=reserve.txt -->\n" | |||
| " <interface name=\"org.freedesktop.ReserveDevice1\">" | |||
| " <method name=\"RequestRelease\">" | |||
| " <arg name=\"priority\" type=\"i\" direction=\"in\"/>" | |||
| " <arg name=\"result\" type=\"b\" direction=\"out\"/>" | |||
| " </method>" | |||
| " <property name=\"Priority\" type=\"i\" access=\"read\"/>" | |||
| " <property name=\"ApplicationName\" type=\"s\" access=\"read\"/>" | |||
| " <property name=\"ApplicationDeviceName\" type=\"s\" access=\"read\"/>" | |||
| " </interface>" | |||
| " <interface name=\"org.freedesktop.DBus.Properties\">" | |||
| " <method name=\"Get\">" | |||
| " <arg name=\"interface\" direction=\"in\" type=\"s\"/>" | |||
| " <arg name=\"property\" direction=\"in\" type=\"s\"/>" | |||
| " <arg name=\"value\" direction=\"out\" type=\"v\"/>" | |||
| " </method>" | |||
| " </interface>" | |||
| " <interface name=\"org.freedesktop.DBus.Introspectable\">" | |||
| " <method name=\"Introspect\">" | |||
| " <arg name=\"data\" type=\"s\" direction=\"out\"/>" | |||
| " </method>" | |||
| " </interface>" | |||
| "</node>"; | |||
| 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; | |||
| } | |||
| @@ -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 <dbus/dbus.h> | |||
| #include <inttypes.h> | |||
| 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 | |||
| @@ -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; | |||
| } | |||
| @@ -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() | |||
| {} | |||
| @@ -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' | |||