diff options
author | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2018-07-18 10:30:17 +0300 |
---|---|---|
committer | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2018-08-28 12:14:08 +0300 |
commit | 30dc02f090dfb68e874297ea8b9c88e41a9d9428 (patch) | |
tree | a426ce25b6b95d4e1e1e9d24e00ef5d3982865eb | |
parent | 699e0ab7dd2ff86192e92d038ca0d008ec0bfb34 (diff) |
switch to using a unix socket with afb-proto-ws
Change-Id: I58861247f7ca23be9cf05d2055d948713547e3d5
Signed-off-by: George Kiagiadakis <george.kiagiadakis@collabora.com>
-rw-r--r-- | m4a_afb_comm.c | 274 | ||||
-rw-r--r-- | m4a_afb_comm.h | 12 | ||||
-rw-r--r-- | module-4a-client.c | 85 |
3 files changed, 154 insertions, 217 deletions
diff --git a/m4a_afb_comm.c b/m4a_afb_comm.c index 63b8d61..30e1521 100644 --- a/m4a_afb_comm.c +++ b/m4a_afb_comm.c @@ -24,6 +24,8 @@ #include "m4a_afb_comm.h" +#define _GNU_SOURCE +#include <stdio.h> #include <errno.h> #include <unistd.h> @@ -33,40 +35,30 @@ #include <pulse/xmalloc.h> #include <systemd/sd-event.h> -#include <afb/afb-wsj1.h> +#include <afb/afb-proto-ws.h> #include <afb/afb-ws-client.h> -typedef struct _m4a_ws_comm_pipe_data m4a_ws_comm_pipe_data; -typedef struct _m4a_afb_api_call_data m4a_afb_api_call_data; - -enum m4a_ws_comm_pipe_opcode { +enum m4a_internal_pipe_opcode { OPCODE_CALL_ASYNC = 1, OPCODE_EXIT }; -struct _m4a_ws_comm_pipe_data { - enum m4a_ws_comm_pipe_opcode opcode; - void *data; -}; - -struct _m4a_afb_comm { - struct afb_wsj1 *wsj1; - - pa_thread *thread; - sd_event *loop; - int pipe[2]; - sd_event_source *source; +typedef struct _m4a_internal_pipe_data m4a_internal_pipe_data; +typedef struct _m4a_afb_api_call_data m4a_afb_api_call_data; - pa_mutex *pending_calls_lock; - PA_LLIST_HEAD(m4a_afb_api_call_data, pending_calls); +/* structure sent over the internal pipe + * for communication between the two threads */ +struct _m4a_internal_pipe_data { + enum m4a_internal_pipe_opcode opcode; + void *data; }; +/* structure holding data related to an ongoing AFB API call */ struct _m4a_afb_api_call_data { m4a_afb_comm *comm; - const char *api; const char *verb; - char *object; + json_object *object; m4a_afb_done_cb_t done_cb; void *userdata; @@ -74,9 +66,30 @@ struct _m4a_afb_api_call_data { PA_LLIST_FIELDS(m4a_afb_api_call_data); }; -static void afb_api_call_data_free(m4a_afb_api_call_data *c) { +/* the main structure of this class */ +struct _m4a_afb_comm { + struct afb_proto_ws *afbhandle; + + char *sessid; + + /* the thread that runs the loop below */ + pa_thread *thread; + /* our internal event loop */ + sd_event *loop; + /* the pipe fd pair used to send commands from + * the pulse threads to our internal thread */ + int tx_pipe[2]; + /* the source for listening to the above pipe from our event loop */ + sd_event_source *tx_source; + + /* a list of pending API calls, protected with a mutex */ + pa_mutex *pending_calls_lock; + PA_LLIST_HEAD(m4a_afb_api_call_data, pending_calls); +}; + +static void m4a_afb_api_call_data_free(m4a_afb_api_call_data *c) { if (c->object) - pa_xfree(c->object); + json_object_put(c->object); pa_mutex_lock(c->comm->pending_calls_lock); PA_LLIST_REMOVE(m4a_afb_api_call_data, c->comm->pending_calls, c); @@ -85,101 +98,30 @@ static void afb_api_call_data_free(m4a_afb_api_call_data *c) { pa_xfree(c); } -static void afb_call_async_step3(void *cb_data, struct afb_wsj1_msg *msg) { - m4a_afb_api_call_data *c = cb_data; - pa_json_object *j; - const pa_json_object *jr = NULL; - - j = pa_json_parse(afb_wsj1_msg_object_s(msg)); - if (pa_json_object_get_type(j) != PA_JSON_TYPE_OBJECT) { - pa_log_warn("4a reply is not a json object"); - } else if (afb_wsj1_msg_is_reply_ok(msg)) { - jr = pa_json_object_get_object_member(j, "response"); - } else { - jr = pa_json_object_get_object_member(j, "request"); - } - - if (afb_wsj1_msg_is_reply_ok(msg)) { - pa_log_debug("Got OK reply from 4a in call to %s/%s", c->api, c->verb); - c->done_cb(M4A_AFB_REPLY_OK, jr, c->userdata); - } else if (jr && pa_json_object_get_type(jr) == PA_JSON_TYPE_OBJECT) { - const pa_json_object *status, *info; - const char *status_str, *info_str; +static void m4a_afb_on_reply(void *ctx, void *handle, json_object *response, + const char *error, const char *info) { + m4a_afb_api_call_data *c = handle; - status = pa_json_object_get_object_member(jr, "status"); - info = pa_json_object_get_object_member(jr, "info"); - - if (status && pa_json_object_get_type(status) == PA_JSON_TYPE_STRING) - status_str = pa_json_object_get_string(status); - else - status_str = "(null)"; - - if (info && pa_json_object_get_type(info) == PA_JSON_TYPE_STRING) - info_str = pa_json_object_get_string(info); - else - info_str = "(null)"; - - pa_log("Got error reply from 4a, status: '%s', info: '%s'", status_str, info_str); - c->done_cb(M4A_AFB_REPLY_ERROR, jr, c->userdata); - } else { - pa_log("Got error reply from 4a"); - c->done_cb(M4A_AFB_REPLY_ERROR, NULL, c->userdata); - } - - pa_json_object_free(j); - afb_api_call_data_free(c); -} - -static void afb_call_async_step2(m4a_afb_api_call_data *c) { - pa_log_debug("calling afb: %s/%s", c->api, c->verb); - - if (afb_wsj1_call_s(c->comm->wsj1, c->api, c->verb, c->object, afb_call_async_step3, c) < 0) { - pa_log("afb_wsj1_call_s: failed to call %s/%s: %s", c->api, c->verb, strerror(errno)); - c->done_cb(M4A_AFB_REPLY_ERROR, NULL, c->userdata); - afb_api_call_data_free(c); - } -} - -static void discard_pending_calls(m4a_afb_comm *comm) { - m4a_afb_api_call_data *c; - - pa_mutex_lock(comm->pending_calls_lock); - while ((c = comm->pending_calls)) { + if (error) { + pa_log("Got error reply from 4a: %s", error); c->done_cb(M4A_AFB_REPLY_ERROR, NULL, c->userdata); - afb_api_call_data_free(c); + } else { + pa_log_debug("Got OK reply from 4a in call to %s", c->verb); + c->done_cb(M4A_AFB_REPLY_OK, response, c->userdata); } - pa_mutex_unlock(comm->pending_calls_lock); -} - -static void on_wsj1_hangup(void *closure, struct afb_wsj1 *wsj1) { - m4a_afb_comm *comm = closure; - - pa_log_warn("afb closed the communication websocket!"); - discard_pending_calls(comm); - - //TODO: attempt to re-connect -} -static void on_wsj1_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) { - /* we don't implement any method calls */ - afb_wsj1_reply_error_s(msg, "\"unimplemented\"", NULL); + m4a_afb_api_call_data_free(c); } -static void on_wsj1_event(void *closure, const char *event, struct afb_wsj1_msg *msg) { - //TODO dispatch events -} - -static struct afb_wsj1_itf wsj1_itf = { - .on_hangup = on_wsj1_hangup, - .on_call = on_wsj1_call, - .on_event = on_wsj1_event +static struct afb_proto_ws_client_itf itf = { + .on_reply = m4a_afb_on_reply }; -static int ws_comm_thread_event(sd_event_source *s, int fd, uint32_t revents, m4a_afb_comm *comm) { +static int internal_thread_event(sd_event_source *s, int fd, uint32_t revents, m4a_afb_comm *comm) { ssize_t r; - m4a_ws_comm_pipe_data d = { 0 }; + m4a_internal_pipe_data d = { 0 }; - if ((r = read(fd, &d, sizeof(m4a_ws_comm_pipe_data))) < (ssize_t) sizeof(m4a_ws_comm_pipe_data)) { + if ((r = read(fd, &d, sizeof(m4a_internal_pipe_data))) < (ssize_t) sizeof(m4a_internal_pipe_data)) { if (errno == EINTR) return 0; @@ -190,7 +132,14 @@ static int ws_comm_thread_event(sd_event_source *s, int fd, uint32_t revents, m4 switch(d.opcode) { case OPCODE_CALL_ASYNC: { m4a_afb_api_call_data *c = d.data; - afb_call_async_step2(c); + + if (afb_proto_ws_client_call(c->comm->afbhandle, c->verb, c->object, + c->comm->sessid, c, NULL) < 0) { + pa_log("afb_proto_ws_client_call: failed to call %s: %s", + c->verb, strerror(errno)); + c->done_cb(M4A_AFB_REPLY_ERROR, NULL, c->userdata); + m4a_afb_api_call_data_free(c); + } break; } case OPCODE_EXIT: @@ -203,24 +152,24 @@ static int ws_comm_thread_event(sd_event_source *s, int fd, uint32_t revents, m4 return 0; } -static void ws_comm_thread(m4a_afb_comm *comm) { - pa_log_debug("websocket thread starting..."); +static void internal_thread(m4a_afb_comm *comm) { + pa_log_debug("afb comm thread starting..."); sd_event_loop(comm->loop); - pa_log_debug("websocket thread exiting..."); + pa_log_debug("afb comm thread exiting..."); } -static bool ws_comm_thread_send(int fd, char opcode, void *data) { - m4a_ws_comm_pipe_data d; +static bool internal_thread_send(m4a_afb_comm *comm, char opcode, void *data) { + m4a_internal_pipe_data d; ssize_t ret; d.opcode = opcode; d.data = data; do { - ret = write(fd, &d, sizeof(m4a_ws_comm_pipe_data)); + ret = write(comm->tx_pipe[1], &d, sizeof(m4a_internal_pipe_data)); } while (ret < 0 && errno == EINTR); - if (ret < (ssize_t) sizeof(m4a_ws_comm_pipe_data)) { + if (ret < (ssize_t) sizeof(m4a_internal_pipe_data)) { pa_log("write: %s", strerror(errno)); return false; } @@ -228,58 +177,36 @@ static bool ws_comm_thread_send(int fd, char opcode, void *data) { return true; } -bool m4a_afb_call_async(m4a_afb_comm *comm, const char *api, const char *verb, - char *object, m4a_afb_done_cb_t done_cb, void *userdata) { - m4a_afb_api_call_data *c; - - c = pa_xnew0(m4a_afb_api_call_data, 1); - c->comm = comm; - c->api = api; - c->verb = verb; - c->object = object; - c->done_cb = done_cb; - c->userdata = userdata; - - pa_mutex_lock(comm->pending_calls_lock); - PA_LLIST_PREPEND(m4a_afb_api_call_data, comm->pending_calls, c); - pa_mutex_unlock(comm->pending_calls_lock); - - if (!ws_comm_thread_send(comm->pipe[1], OPCODE_CALL_ASYNC, c)) { - c->done_cb(M4A_AFB_REPLY_ERROR, NULL, c->userdata); - afb_api_call_data_free(c); - return false; - } - return true; -} - m4a_afb_comm *m4a_afb_comm_new(const char *uri) { int ret; m4a_afb_comm *comm; comm = pa_xnew0(m4a_afb_comm, 1); + asprintf(&comm->sessid, "pulseaudio:%d", getpid()); if ((ret = sd_event_new(&comm->loop)) < 0) { pa_log("Failed to create systemd event loop: %s", strerror(-ret)); goto fail; } - if (!(comm->wsj1 = afb_ws_client_connect_wsj1(comm->loop, uri, &wsj1_itf, comm))) { + if (!(comm->afbhandle = afb_ws_client_connect_api(comm->loop, uri, &itf, comm))) { pa_log("Connection to %s failed: %s", uri, strerror(errno)); goto fail; } - if (pipe(comm->pipe) < 0) { - pa_log("pipe2 failed: %s", strerror(errno)); + if (pipe(comm->tx_pipe) < 0) { + pa_log("pipe failed: %s", strerror(errno)); goto fail; } - if ((ret = sd_event_add_io(comm->loop, &comm->source, comm->pipe[0], EPOLLIN, - (sd_event_io_handler_t) ws_comm_thread_event, comm)) < 0) { + if ((ret = sd_event_add_io(comm->loop, &comm->tx_source, comm->tx_pipe[0], EPOLLIN, + (sd_event_io_handler_t) internal_thread_event, comm)) < 0) { pa_log("sd_event_add_io failed: %s", strerror(-ret)); goto fail; } - if (!(comm->thread = pa_thread_new("afb_comm_loop", (pa_thread_func_t) ws_comm_thread, comm))) { + if (!(comm->thread = pa_thread_new("m4a_afb_comm_internal_thread", + (pa_thread_func_t) internal_thread, comm))) { pa_log("Failed to start websocket communication thread"); goto fail; } @@ -296,32 +223,69 @@ fail: return NULL; } + +static void discard_pending_calls(m4a_afb_comm *comm) { + m4a_afb_api_call_data *c; + + pa_mutex_lock(comm->pending_calls_lock); + while ((c = comm->pending_calls)) { + c->done_cb(M4A_AFB_REPLY_ERROR, NULL, c->userdata); + m4a_afb_api_call_data_free(c); + } + pa_mutex_unlock(comm->pending_calls_lock); +} + void m4a_afb_comm_free(m4a_afb_comm *comm) { if (comm->thread) { - if (!ws_comm_thread_send(comm->pipe[1], OPCODE_EXIT, NULL)) { + if (!internal_thread_send(comm, OPCODE_EXIT, NULL)) { pa_log("failed to shutdown thread gracefully"); pa_thread_free_nojoin(comm->thread); } else { pa_thread_free(comm->thread); } } - if (comm->source) - sd_event_source_unref(comm->source); + if (comm->tx_source) + sd_event_source_unref(comm->tx_source); if (comm->loop) sd_event_unref(comm->loop); - if (comm->wsj1) - afb_wsj1_unref(comm->wsj1); + if (comm->afbhandle) + afb_proto_ws_unref(comm->afbhandle); if (comm->pending_calls_lock) { discard_pending_calls(comm); pa_mutex_free(comm->pending_calls_lock); } - if (comm->pipe[0] > 0) - close(comm->pipe[0]); - if (comm->pipe[1] > 0) - close(comm->pipe[1]); + if (comm->tx_pipe[0] > 0) + close(comm->tx_pipe[0]); + if (comm->tx_pipe[1] > 0) + close(comm->tx_pipe[1]); + free(comm->sessid); pa_xfree(comm); } + +bool m4a_afb_call_async(m4a_afb_comm *comm, const char *verb, + json_object *object, m4a_afb_done_cb_t done_cb, + void *userdata) { + m4a_afb_api_call_data *c; + + c = pa_xnew0(m4a_afb_api_call_data, 1); + c->comm = comm; + c->verb = verb; + c->object = object; + c->done_cb = done_cb; + c->userdata = userdata; + + pa_mutex_lock(comm->pending_calls_lock); + PA_LLIST_PREPEND(m4a_afb_api_call_data, comm->pending_calls, c); + pa_mutex_unlock(comm->pending_calls_lock); + + if (!internal_thread_send(comm, OPCODE_CALL_ASYNC, c)) { + c->done_cb(M4A_AFB_REPLY_ERROR, NULL, c->userdata); + m4a_afb_api_call_data_free(c); + return false; + } + return true; +} diff --git a/m4a_afb_comm.h b/m4a_afb_comm.h index 7760bf9..7b072a2 100644 --- a/m4a_afb_comm.h +++ b/m4a_afb_comm.h @@ -21,7 +21,8 @@ #ifndef __M4A_AFB_COMM_H__ #define __M4A_AFB_COMM_H__ -#include <pulse/json.h> +#include <stdbool.h> +#include <json-c/json.h> typedef struct _m4a_afb_comm m4a_afb_comm; @@ -31,11 +32,14 @@ enum m4a_afb_reply { }; typedef void (*m4a_afb_done_cb_t)(enum m4a_afb_reply r, - const pa_json_object *response, + json_object *response, void *userdata); -bool m4a_afb_call_async(m4a_afb_comm *comm, const char *api, const char *verb, - char *object, m4a_afb_done_cb_t done_cb, void *userdata); +bool m4a_afb_call_async(m4a_afb_comm *comm, + const char *verb, + json_object *object, + m4a_afb_done_cb_t done_cb, + void *userdata); m4a_afb_comm *m4a_afb_comm_new(const char *uri); void m4a_afb_comm_free(m4a_afb_comm *comm); diff --git a/module-4a-client.c b/module-4a-client.c index 64502c8..9eda2fa 100644 --- a/module-4a-client.c +++ b/module-4a-client.c @@ -34,8 +34,7 @@ #include "m4a_afb_comm.h" -#define DEFAULT_URI "ws://localhost:1234/api?token=" -#define AHL_4A_API "ahl-4a" +#define DEFAULT_URI "sd:ahl-4a" PA_MODULE_AUTHOR("George Kiagiadakis"); PA_MODULE_DESCRIPTION("Makes PulseAudio work as a client of the AGL Advanced Audio Architecture"); @@ -78,13 +77,9 @@ typedef struct { /* operational data */ pa_mutex *lock; - bool null_sink_loaded; - uint32_t null_sink_id; - uint32_t null_sink_module_id; pa_dynarray *roles; /* element-type: char* */ PA_LLIST_HEAD(m4a_stream, streams); pa_hook_slot - *sink_input_new_slot, *sink_input_put_slot, *sink_input_unlink_post_slot; m4a_afb_comm *comm; @@ -146,21 +141,21 @@ static pa_sink *load_sink_module(pa_core *core, const char *module, char *params /* invoked in the afb communication thread */ static void got_roles_cb(enum m4a_afb_reply r, - const pa_json_object *response, + json_object *response, pa_module *self) { m4a_data *d = self->userdata; int length, i; - const pa_json_object *jr; + json_object *jr; char *role; pa_mutex_lock(d->lock); - if (r == M4A_AFB_REPLY_OK && pa_json_object_get_type(response) == PA_JSON_TYPE_ARRAY) { - length = pa_json_object_get_array_length(response); + if (r == M4A_AFB_REPLY_OK && json_object_get_type(response) == json_type_array) { + length = json_object_array_length(response); for (i = 0; i < length; i++) { - jr = pa_json_object_get_array_member(response, i); - if (pa_json_object_get_type(jr) == PA_JSON_TYPE_STRING) { - role = pa_xstrdup(pa_json_object_get_string(jr)); + jr = json_object_array_get_idx(response, i); + if (json_object_get_type(jr) == json_type_string) { + role = pa_xstrdup(json_object_get_string(jr)); pa_log_debug("Found 4a role: %s", role); pa_dynarray_append(d->roles, role); } @@ -201,35 +196,18 @@ static void m4a_stream_free(m4a_stream *stream) { pa_xfree(stream); } -static pa_hook_result_t sink_input_new_cb(pa_core *core, - pa_sink_input *i, - pa_module *self) { - m4a_data *d = self->userdata; - pa_sink *sink; - - pa_core_assert_ref(core); - - /* first, forcibly set the sink to be our null sink */ - sink = pa_idxset_get_by_index(core->sinks, d->null_sink_id); - if (sink) { - i->sink = sink; - i->sink_requested_by_application = false; - } - - return PA_HOOK_OK; -} - /* invoked in the afb communication thread */ static void device_open_cb(enum m4a_afb_reply r, - const pa_json_object *response, + json_object *response, m4a_stream *stream) { - const pa_json_object *jdu; + json_object *jdu; const char *device_uri = NULL; - if (r == M4A_AFB_REPLY_OK && pa_json_object_get_type(response) == PA_JSON_TYPE_OBJECT) { - jdu = pa_json_object_get_object_member(response, "device_uri"); - if (jdu && pa_json_object_get_type(jdu) == PA_JSON_TYPE_STRING) { - device_uri = pa_json_object_get_string(jdu); + if (r == M4A_AFB_REPLY_OK && json_object_get_type(response) == json_type_object) { + json_object_object_get_ex(response, "device_uri", &jdu); + if (json_object_object_get_ex(response, "device_uri", &jdu) && + json_object_get_type(jdu) == json_type_string) { + device_uri = json_object_get_string(jdu); } } @@ -245,6 +223,7 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, pa_module *self) { m4a_data *d = self->userdata; + json_object *jparams; char *role; pa_mutex_lock(d->lock); @@ -260,8 +239,10 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_log_debug("Calling 4A to open the device"); - m4a_afb_call_async(d->comm, AHL_4A_API, role, - pa_xstrdup("{\"action\":\"open\"}"), + jparams = json_object_new_object(); + json_object_object_add(jparams, "action", json_object_new_string("open")); + + m4a_afb_call_async(d->comm, role, jparams, (m4a_afb_done_cb_t) device_open_cb, stream); pa_log_debug("waiting for 4A to reply"); @@ -298,7 +279,7 @@ static pa_hook_result_t sink_input_put_cb(pa_core *core, /* invoked in the afb communication thread */ static void device_close_cb(enum m4a_afb_reply r, - const pa_json_object *response, + json_object *response, m4a_stream *stream) { pa_log_debug("4A replied: %s", (r == M4A_AFB_REPLY_OK) ? "OK" : "ERROR"); @@ -310,6 +291,7 @@ static pa_hook_result_t sink_input_unlink_post_cb(pa_core *core, pa_module *self) { m4a_data *d = self->userdata; m4a_stream *stream = NULL; + json_object *jparams; PA_LLIST_FOREACH(stream, d->streams) { if (stream->sink_input == i) @@ -323,8 +305,10 @@ static pa_hook_result_t sink_input_unlink_post_cb(pa_core *core, stream->entity_module_id, false); stream->entity_loaded = false; - m4a_afb_call_async(d->comm, AHL_4A_API, stream->role, - pa_xstrdup("{\"action\":\"close\"}"), + jparams = json_object_new_object(); + json_object_object_add(jparams, "action", json_object_new_string("close")); + + m4a_afb_call_async(d->comm, stream->role, jparams, (m4a_afb_done_cb_t) device_close_cb, stream); } @@ -334,7 +318,6 @@ static pa_hook_result_t sink_input_unlink_post_cb(pa_core *core, int pa__init(pa_module *self) { m4a_data *d; const char *uri; - pa_sink *null_sink; pa_assert(self); @@ -352,20 +335,11 @@ int pa__init(pa_module *self) { goto fail; } - if (!m4a_afb_call_async(d->comm, AHL_4A_API, "get_roles", NULL, + if (!m4a_afb_call_async(d->comm, "get_roles", NULL, (m4a_afb_done_cb_t) got_roles_cb, self)) { goto fail; } - null_sink = load_sink_module(self->core, "module-null-sink", pa_sprintf_malloc( - "sink_name=aaaa_null_sink sink_properties='device.description=\"%s\"'", - _("4A Null Output"))); - d->null_sink_id = null_sink->index; - d->null_sink_module_id = null_sink->module->index; - d->null_sink_loaded = true; - - d->sink_input_new_slot = pa_hook_connect(&self->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], - PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_new_cb, self); d->sink_input_put_slot = pa_hook_connect(&self->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_put_cb, self); d->sink_input_unlink_post_slot = pa_hook_connect(&self->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], @@ -393,16 +367,11 @@ void pa__done(pa_module *self) { if (d->ma) pa_modargs_free(d->ma); - if (d->sink_input_new_slot) - pa_hook_slot_free(d->sink_input_new_slot); if (d->sink_input_put_slot) pa_hook_slot_free(d->sink_input_put_slot); if (d->sink_input_unlink_post_slot) pa_hook_slot_free(d->sink_input_unlink_post_slot); - if (d->null_sink_loaded) - pa_module_unload_request_by_index(self->core, d->null_sink_module_id, false); - if (d->comm) m4a_afb_comm_free(d->comm); } |