Skip to content

Commit

Permalink
device_reservation: Support for giving up of the audio device, at pri…
Browse files Browse the repository at this point in the history
…ority (INT32_MAX-1000)

 * Releasing device name ownership switches master driver to the dummy driver
 * Releasing device name ownership is now decoupled from releasing reference to rd_device.
 * Acquiring device via rd_acquire() is now alowed to reuse already creaed rd_device.
 * NameOwnerChanged signals are now monitored and when device is available for reaquire,
   a rd_available_cb_t callback is called and switches back to previous driver,
   resuting in device name ownership is reaquire.
  • Loading branch information
nedko committed Jan 15, 2025
1 parent fbec91b commit c3b24cc
Show file tree
Hide file tree
Showing 5 changed files with 285 additions and 48 deletions.
78 changes: 76 additions & 2 deletions dbus/controller.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* -*- Mode: C ; c-basic-offset: 4 -*- */
/*
Copyright (C) 2007-2024 Nedko Arnaudov
Copyright (C) 2007-2025 Nedko Arnaudov
Copyright (C) 2007-2008 Juuso Alasuutari
This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -483,13 +483,19 @@ jack_controller_create(

INIT_LIST_HEAD(&controller_ptr->session_pending_commands);

if (device_reservation_init(connection, controller_ptr) != 0)
{
jack_error("Failed to initialize device reservation module");
goto fail_uninit_mutex;
}

controller_ptr->server = jackctl_server_create(
device_reservation_acquire,
device_reservation_release);
if (controller_ptr->server == NULL)
{
jack_error("Failed to create server object");
goto fail_uninit_mutex;
goto fail_uninit_device_reservation;
}

controller_ptr->params = jack_params_create(controller_ptr->server);
Expand Down Expand Up @@ -549,6 +555,9 @@ jack_controller_create(
fail_destroy_server:
jackctl_server_destroy(controller_ptr->server);

fail_uninit_device_reservation:
device_reservation_finish();

fail_uninit_mutex:
pthread_mutex_destroy(&controller_ptr->lock);

Expand Down Expand Up @@ -716,6 +725,7 @@ jack_controller_destroy(
jack_controller_remove_slave_drivers(controller_ptr);
jack_params_destroy(controller_ptr->params);
jackctl_server_destroy(controller_ptr->server);
device_reservation_finish();
pthread_mutex_destroy(&controller_ptr->lock);
free(controller_ptr);
}
Expand Down Expand Up @@ -762,3 +772,67 @@ jack_controller_pending_save(

controller_ptr->pending_save = si.uptime;
}

#define controller_ptr ((struct jack_controller *)ctx)

static union jackctl_parameter_value g_saved_driver_name_value;

void device_reservation_on_takeover(void * ctx, const char * device_name)
{
const char * address[PARAM_ADDRESS_SIZE];
const struct jack_parameter * param_ptr;

jack_info("Device \"%s\" taken over", device_name);

address[0] = PTNODE_ENGINE;
address[1] = PTNODE_DRIVER;
address[2] = NULL;

param_ptr = jack_params_get_parameter(controller_ptr->params, address);
if (param_ptr == NULL)
{
jack_error(
"Invalid container address '%s':'%s':'%s'.",
address[0],
address[1],
address[2]);
return;
}

g_saved_driver_name_value = param_ptr->vtable.get_value(param_ptr->obj);

union jackctl_parameter_value value;
strcpy(value.str, "dummy");
param_ptr->vtable.set_value(param_ptr->obj, &value);
jack_info("Switching to \"%s\" driver", value.str);
jackctl_server_switch_master(controller_ptr->server, jack_params_get_driver(controller_ptr->params));
}

void device_reservation_on_giveback(void * ctx, const char * device_name)
{
const char * address[PARAM_ADDRESS_SIZE];
const struct jack_parameter * param_ptr;

jack_info("Device \"%s\" given back", device_name);

address[0] = PTNODE_ENGINE;
address[1] = PTNODE_DRIVER;
address[2] = NULL;

param_ptr = jack_params_get_parameter(controller_ptr->params, address);
if (param_ptr == NULL)
{
jack_error(
"Invalid container address '%s':'%s':'%s'.",
address[0],
address[1],
address[2]);
return;
}

param_ptr->vtable.set_value(param_ptr->obj, &g_saved_driver_name_value);
jack_info("Switching to \"%s\" driver", g_saved_driver_name_value.str);
jackctl_server_switch_master(controller_ptr->server, jack_params_get_driver(controller_ptr->params));
}

#undef controller_ptr
94 changes: 81 additions & 13 deletions dbus/device_reservation.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
Copyright (C) 2009 Grame
Copyright (C) 2024 Nedko Arnaudov
Copyright (C) 2012-2025 Nedko Arnaudov
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
Expand Down Expand Up @@ -34,58 +34,115 @@
/* string like "Audio2", "Midi5" or "Video3" */
#define MAX_DEVICE_NAME 63

typedef struct reserved_audio_device {

struct reserved_audio_device {
void * ctx;
char device_name[MAX_DEVICE_NAME+1];
rd_device * reserved_device;

} reserved_audio_device;
};

static DBusConnection* gConnection = NULL;
static reserved_audio_device gReservedDevice[DEVICE_MAX];
static struct reserved_audio_device gReservedDevice[DEVICE_MAX];
static int gReserveCount = 0;
static void * g_device_reservation_ctx;

int device_reservation_init(void)
int device_reservation_init(DBusConnection* connection, void * ctx)
{
#if 1 /* jackdbus */
gConnection = connection;
g_device_reservation_ctx = ctx;
#else
DBusError error;
dbus_error_init(&error);

if (!(gConnection = dbus_bus_get(DBUS_BUS_SESSION, &error))) {
jack_error("Failed to connect to session bus for device reservation: %s\n", error.message);
return -1;
}

#endif
jack_info("audio_reservation_init");
return 0;
}

int device_reservation_finish(void)
{
int i;

if (gConnection) {
#if 1 /* jackdbus */
for (i = 0; i < DEVICE_MAX; i++) {
if (gReservedDevice[i].reserved_device)
rd_release(gReservedDevice[i].reserved_device, 0);
}
#else
dbus_connection_unref(gConnection);
#endif
gConnection = NULL;
jack_info("audio_reservation_finish");
}
return 0;
}

int
device_reservation_request_cb(
void *userdata,
rd_device *d,
int forced)
{
struct reserved_audio_device * ptr = userdata;

// jack_info("device_reservation_request_cb");
assert(ptr->reserved_device == d);

device_reservation_on_takeover(ptr->ctx, ptr->device_name);

// rd_release(ptr->reserved_device);
// memset(ptr, 0, sizeof(struct reserved_audio_device));

return 1; /* device was released */
}

void
device_reservation_available_cb(
void *userdata,
rd_device *d)
{
struct reserved_audio_device * ptr = userdata;

assert(ptr->reserved_device == d);

device_reservation_on_giveback(ptr->ctx, ptr->device_name);
}

bool device_reservation_acquire(const char * device_name)
{
DBusError error;
int ret;

#if 0 /* user calling device_reservation_init() is to call device_reservation_finish() */
// Open DBus connection first time
if (gReserveCount == 0) {
if (device_reservation_init() != 0) {
return false;
}
}
#else
if (!gConnection) {
jack_error("device reservation requires dbus connection");
assert(false);
return false;
}
#endif

assert(gReserveCount < DEVICE_MAX);

if (gReserveCount != 0) {
jack_error("Ignoring reservation for more than one device (acquire)");
return false;
/* reuse device entry if name matches */
if (gReserveCount != 1 || strcmp(gReservedDevice[0].device_name, device_name)) {
jack_error("Ignoring reservation for more than one device (acquire)");
return false;
}
gReserveCount = 0;
}

strncpy(gReservedDevice[gReserveCount].device_name, device_name, MAX_DEVICE_NAME);
Expand All @@ -101,17 +158,25 @@ bool device_reservation_acquire(const char * device_name)
&gReservedDevice[gReserveCount].reserved_device,
gConnection,
device_name,
"Jack audio server",
INT32_MAX,
"Jack audio server (jackdbus)",
NULL,
INT32_MAX - 1000, /* value lower than INT32_MAX
allows other process to take over */
gReservedDevice + gReserveCount,
device_reservation_request_cb, /* request device release callback */
device_reservation_available_cb, /* device available callback */
&error)) < 0) {

jack_error("Failed to acquire device name : %s error : %s", device_name, (error.message ? error.message : strerror(-ret)));
dbus_error_free(&error);
return false;
}

gReservedDevice[gReserveCount].ctx = g_device_reservation_ctx;
// rd_set_userdata(gReservedDevice[gReserveCount].reserved_device, gReservedDevice + gReserveCount);

gReserveCount++;

jack_info("Acquire audio card %s", device_name);
return true;
}
Expand All @@ -128,15 +193,18 @@ void device_reservation_release(const char * device_name)

if (i < DEVICE_MAX) {
jack_info("Released audio card %s", device_name);
rd_release(gReservedDevice[i].reserved_device);
rd_release(gReservedDevice[i].reserved_device, 1);
} else {
jack_error("Audio card %s not found!!", device_name);
}

// gReserveCount--;

#if 0 /* user calling device_reservation_init() is to call device_reservation_finish() */
// Close DBus connection last time
gReserveCount--;
if (gReserveCount == 0)
device_reservation_finish();
#endif
}

void device_reservation_loop(void)
Expand Down
8 changes: 6 additions & 2 deletions dbus/device_reservation.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
Copyright (C) 2009 Grame
Copyright (C) 2024 Nedko Arnaudov
Copyright (C) 2024-2025 Nedko Arnaudov
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
Expand All @@ -26,13 +26,17 @@
extern "C" {
#endif

int device_reservation_init(void);
int device_reservation_init(DBusConnection * connection, void * ctx);
int device_reservation_finish(void);

bool device_reservation_acquire(const char * device_name);
void device_reservation_release(const char * device_name);
void device_reservation_loop(void);

/* callbacks */
void device_reservation_on_takeover(void * ctx, const char * device_name);
void device_reservation_on_giveback(void * ctx, const char * device_name);

#ifdef __cplusplus
} /* extern "C" */
#endif
Expand Down
Loading

0 comments on commit c3b24cc

Please sign in to comment.