diff --git a/0001-Add-a-manual.patch b/0001-Add-a-manual.patch new file mode 100644 index 0000000..9d81948 --- /dev/null +++ b/0001-Add-a-manual.patch @@ -0,0 +1,264 @@ +From 3c8e85a522516437151bc2c441ddc3eb406593ea Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 22 Jun 2020 17:02:42 +0200 +Subject: [PATCH] Add a manual + +https://github.com/phako/dleyna-renderer/pull/1 +--- + Makefile.am | 2 +- + configure.ac | 6 + + doc/Makefile.am | 18 +++ + doc/dleyna-renderer-service.conf.xml | 182 +++++++++++++++++++++++++++ + 4 files changed, 207 insertions(+), 1 deletion(-) + create mode 100644 doc/Makefile.am + create mode 100644 doc/dleyna-renderer-service.conf.xml + +diff --git a/Makefile.am b/Makefile.am +index 200e1967f0e5..35bd628faf47 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,4 +1,4 @@ +-SUBDIRS = libdleyna/renderer ++SUBDIRS = doc libdleyna/renderer + + if BUILD_SERVER + SUBDIRS += server +diff --git a/configure.ac b/configure.ac +index 364659d30383..b224ac7c832b 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -34,6 +34,11 @@ LT_PREREQ([2.2.6]) + LT_INIT([dlopen disable-static]) + LT_LANG([C]) + ++AC_PATH_PROG([XSLTPROC], [xsltproc]) ++if test -z "$XSLTPROC"; then ++ AC_MSG_ERROR([xsltproc not found]) ++fi ++ + # Checks for libraries. + PKG_PROG_PKG_CONFIG(0.16) + PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28]) +@@ -231,6 +236,7 @@ AC_SUBST([with_log_level]) + AC_SUBST([with_log_type]) + + AC_CONFIG_FILES([Makefile \ ++ doc/Makefile \ + libdleyna/renderer/Makefile \ + libdleyna/renderer/dleyna-renderer-service.conf \ + server/dleyna-renderer-service-1.0.pc \ +diff --git a/doc/Makefile.am b/doc/Makefile.am +new file mode 100644 +index 000000000000..80069b1c82e7 +--- /dev/null ++++ b/doc/Makefile.am +@@ -0,0 +1,18 @@ ++XSLTPROC_FLAGS = \ ++ --nonet \ ++ --stringparam man.output.quietly 1 \ ++ --stringparam funcsynopsis.style ansi \ ++ --stringparam man.authors.section.enabled 0 \ ++ --stringparam man.copyright.section.enabled 0 ++ ++.xml.5: ++ $(AM_V_GEN) $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< ++ ++man_MANS = \ ++ dleyna-renderer-service.conf.5 ++ ++xml_files = $(man_MANS:.5=.xml) ++ ++EXTRA_DIST = $(xml_files) ++ ++CLEANFILES = $(man_MANS) +diff --git a/doc/dleyna-renderer-service.conf.xml b/doc/dleyna-renderer-service.conf.xml +new file mode 100644 +index 000000000000..e6be27411cf9 +--- /dev/null ++++ b/doc/dleyna-renderer-service.conf.xml +@@ -0,0 +1,182 @@ ++ ++ ++ ++ dleyna-renderer-service.conf ++ June 2020 ++ dLeyna ++ ++ ++ ++ dleyna-renderer-service.conf ++ 5 ++ Linux Programmer's Manual ++ ++ ++ ++ dleyna-renderer-service.conf ++ Configuration file for dleyna-renderer ++ ++ ++ ++ ++ /etc/dleyna-renderer-service.conf ++ ++ ++ ++ ++ ++ DESCRIPTION ++ ++ The dleyna-renderer-service program provides the ++ com.intel.dleyna-renderer name on the session ++ message bus. It is a service for discovering and manipulating DLNA ++ Digital Media Renderers (or DMRs). ++ ++ ++ Users or administrators should never need to start this daemon as it will ++ be automatically started by ++ dbus-daemon1 ++ whenever a process sends a D-Bus message to the ++ com.intel.dleyna-renderer name on the session bus. ++ ++ ++ This file can be used to configure the behaviour of the ++ dleyna-renderer-service program. ++ ++ ++ ++ ++ FILE FORMAT ++ ++ The configuration file format is the so-called key file (sort of ++ ini-style) format. It consists of sections (or groups) of key-value ++ Lines beginning with a '#' and blank lines are considered comments. ++ Sections are started by a header line containing the section enclosed ++ in '[' and ']', and ended implicitly by the start of the next section ++ or the end of the file. Each key-value pair must be contained in a ++ section. ++ ++ ++ ++ ++ GENERAL SECTION ++ ++ ++ ++ ++ ++ The name of the inter-process communication method to be used. ++ The only supported value is dbus. ++ ++ ++ ++ ++ ++ ++ ++ Whether the process should stay alive forever or quit when the ++ last client disconnects. The default value is ++ false. ++ ++ ++ ++ ++ ++ ++ ++ Source port for Simple Service Discovery Protocol (or SSDP) ++ messages. The default value is 0 which ++ means that a random available port will be used. ++ ++ ++ ++ ++ ++ ++ ++ Port for push host file server. The default value is ++ 0 which means that a random available ++ port will be used. ++ ++ ++ ++ ++ ++ ++ ++ LOG SECTION ++ ++ ++ ++ ++ ++ The logging method to be used. Three technologies are supported. ++ 0 for syslog, 1 for GLib, and 2 for file. The default value is ++ 0. ++ ++ ++ ++ ++ ++ ++ ++ Comma-separated list of logging levels. The levels are 1 for ++ critical, 2 for error, 3 for warning, 4 for message, 5 for info ++ and 6 for debug. 0 excludes all levels, 7 is a combination of ++ critical, error and info, and 8 includes all levels. ++ ++ ++ The default value is 7. ++ ++ ++ ++ ++ ++ ++ ++ NETF SECTION ++ ++ ++ ++ ++ ++ Whether network filtering should be enabled or not. The default ++ values is false. ++ ++ ++ ++ ++ ++ ++ ++ Comma-separated list of interface names, service set identifiers ++ (or SSIDs), and Internet Protocol (or IP) addresses. If network ++ filtering is requested but the list is empty, then filtering ++ remains disabled. ++ ++ ++ The list is empty by default. ++ ++ ++ ++ ++ ++ ++ ++ BUGS ++ ++ Please send bug reports to either the distribution bug tracker ++ or the upstream bug tracker at ++ . ++ ++ ++ ++ ++ SEE ALSO ++ ++ ++ dbus-daemon1 ++ ++ ++ ++ +-- +2.28.0 + diff --git a/0001-UPnP-Disconnect-signal-handlers-during-destruction.patch b/0001-UPnP-Disconnect-signal-handlers-during-destruction.patch new file mode 100644 index 0000000..f830435 --- /dev/null +++ b/0001-UPnP-Disconnect-signal-handlers-during-destruction.patch @@ -0,0 +1,77 @@ +From 916daa1bf04bfb1d8823c3f677a021bf41df1db0 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Tue, 20 Jan 2015 13:59:33 +0100 +Subject: [PATCH] Disconnect signal handlers during destruction + +A GUPnPContextManager can outlive a dlr_upnp_t because it might be +using asynchronous operations during its construction (eg., +GUPnPNetworkManager) which retain references to it. This can be +demonstrated if the service is spawned as a result of the following +command: +$ gdbus call \ + --session \ + --dest com.intel.dleyna-renderer \ + --object-path /com/intel/dLeynaRenderer \ + --method com.intel.dLeynaRenderer.Manager.Release + +This leads to the signal handlers being invoked with an invalid +dlr_upnp_t and the outcome is a crash. + +To avoid this, we should disconnect the callbacks listening to the +context manager and the control points belonging to it. +--- + libdleyna/renderer/upnp.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/libdleyna/renderer/upnp.c b/libdleyna/renderer/upnp.c +index 17cbda720bc1..707dc09aaf5a 100644 +--- a/libdleyna/renderer/upnp.c ++++ b/libdleyna/renderer/upnp.c +@@ -45,6 +45,7 @@ struct dlr_upnp_t_ { + void *user_data; + GHashTable *server_udn_map; + GHashTable *server_uc_map; ++ GList *cps; + dlr_host_service_t *host_service; + }; + +@@ -352,6 +353,7 @@ static void prv_on_context_available(GUPnPContextManager *context_manager, + + gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(cp), TRUE); + gupnp_context_manager_manage_control_point(upnp->context_manager, cp); ++ upnp->cps = g_list_prepend (upnp->cps, g_object_ref (cp)); + g_object_unref(cp); + } + +@@ -390,10 +392,28 @@ dlr_upnp_t *dlr_upnp_new(dleyna_connector_id_t connection, + void dlr_upnp_delete(dlr_upnp_t *upnp) + { + if (upnp) { ++ GList *l; ++ ++ for (l = upnp->cps; l != NULL; l = l->next) { ++ GUPnPControlPoint *cp = GUPNP_CONTROL_POINT (l->data); ++ ++ g_signal_handlers_disconnect_by_func (cp, ++ prv_server_available_cb, ++ upnp); ++ g_signal_handlers_disconnect_by_func (cp, ++ prv_server_unavailable_cb, ++ upnp); ++ } ++ ++ g_signal_handlers_disconnect_by_func (upnp->context_manager, ++ prv_on_context_available, ++ upnp); ++ + dlr_host_service_delete(upnp->host_service); + g_object_unref(upnp->context_manager); + g_hash_table_unref(upnp->server_udn_map); + g_hash_table_unref(upnp->server_uc_map); ++ g_list_free_full (upnp->cps, g_object_unref); + + g_free(upnp); + } +-- +2.9.5 + diff --git a/167.patch b/167.patch new file mode 100644 index 0000000..7ead9f9 --- /dev/null +++ b/167.patch @@ -0,0 +1,942 @@ +From 594015eac2757f629a32d043c9a9b10ff6c5f95f Mon Sep 17 00:00:00 2001 +From: Jens Georg +Date: Mon, 5 Nov 2018 22:07:09 +0100 +Subject: [PATCH 1/4] Use english for logging + +--- + libdleyna/renderer/device.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libdleyna/renderer/device.c b/libdleyna/renderer/device.c +index 7acef89..032d394 100644 +--- a/libdleyna/renderer/device.c ++++ b/libdleyna/renderer/device.c +@@ -1201,7 +1201,7 @@ static void prv_add_actions(dlr_device_t *device, + continue; + } + +- DLEYNA_LOG_DEBUG("DLNA version ≥ 1.50 pour %s", ++ DLEYNA_LOG_DEBUG("DLNA version ≥ 1.50 for %s", + device->path); + timeseek_missing = TRUE; + g_free(dlna_device_class); + +From a588dd11e4c6d2ff6a7c1789fad913ab9c2519b5 Mon Sep 17 00:00:00 2001 +From: Jens Georg +Date: Sat, 21 Sep 2019 20:36:04 +0200 +Subject: [PATCH 2/4] Do service introspection on device creation + +Fixes #104 +Fixes #164 +--- + libdleyna/renderer/Makefile.am | 2 + + libdleyna/renderer/device.c | 411 ++++++++++++++++--------------- + libdleyna/renderer/gasync-task.c | 135 ++++++++++ + libdleyna/renderer/gasync-task.h | 57 +++++ + libdleyna/renderer/manager.c | 1 - + libdleyna/renderer/upnp.c | 14 +- + 6 files changed, 419 insertions(+), 201 deletions(-) + create mode 100644 libdleyna/renderer/gasync-task.c + create mode 100644 libdleyna/renderer/gasync-task.h + +diff --git a/libdleyna/renderer/Makefile.am b/libdleyna/renderer/Makefile.am +index ca601c7..ce4dc41 100644 +--- a/libdleyna/renderer/Makefile.am ++++ b/libdleyna/renderer/Makefile.am +@@ -22,6 +22,7 @@ libdleyna_renderer_1_0_la_LDFLAGS = -version-info $(DLEYNA_RENDERER_VERSION) \ + libdleyna_renderer_1_0_la_SOURCES = $(libdleyna_rendererinc_HEADERS) \ + async.c \ + device.c \ ++ gasync-task.c \ + host-service.c \ + manager.c \ + server.c \ +@@ -53,6 +54,7 @@ sysconf_DATA = dleyna-renderer-service.conf + EXTRA_DIST = $(sysconf_DATA) \ + async.h \ + device.h \ ++ gasync-task.h \ + host-service.h \ + prop-defs.h \ + manager.h \ +diff --git a/libdleyna/renderer/device.c b/libdleyna/renderer/device.c +index 032d394..73b3dd3 100644 +--- a/libdleyna/renderer/device.c ++++ b/libdleyna/renderer/device.c +@@ -26,15 +26,16 @@ + + #include + #include ++#include + #include + + #include + #include + #include +-#include + + #include "async.h" + #include "device.h" ++#include "gasync-task.h" + #include "prop-defs.h" + #include "server.h" + +@@ -675,21 +676,30 @@ static void prv_process_protocol_info(dlr_device_t *device, + DLEYNA_LOG_DEBUG("Exit"); + } + +-static void prv_get_protocol_info_cb(GUPnPServiceProxy *proxy, +- GUPnPServiceProxyAction *action, ++static void prv_get_protocol_info_cb(GObject *target, ++ GAsyncResult *res, + gpointer user_data) + { + gchar *result = NULL; + gboolean end; + GError *error = NULL; + prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data; ++ GUPnPServiceProxyAction *action; + + DLEYNA_LOG_DEBUG("Enter"); + + priv_t->dev->construct_step++; + +- end = gupnp_service_proxy_end_action(proxy, action, &error, "Sink", +- G_TYPE_STRING, &result, NULL); ++ action = gupnp_service_proxy_call_action_finish(GUPNP_SERVICE_PROXY(target), res, &error); ++ ++ if (action == NULL || (error != NULL)) { ++ DLEYNA_LOG_WARNING("GetProtocolInfo operation failed: %s", ++ ((error != NULL) ? error->message ++ : "Invalid result")); ++ goto on_error; ++ } ++ ++ end = gupnp_service_proxy_action_get_result (action, &error, "Sink", G_TYPE_STRING, &result, NULL); + if (!end || (result == NULL)) { + DLEYNA_LOG_WARNING("GetProtocolInfo operation failed: %s", + ((error != NULL) ? error->message +@@ -701,6 +711,10 @@ static void prv_get_protocol_info_cb(GUPnPServiceProxy *proxy, + + on_error: + ++ if (action) { ++ gupnp_service_proxy_action_unref(action); ++ } ++ + if (error) + g_error_free(error); + +@@ -709,53 +723,193 @@ static void prv_get_protocol_info_cb(GUPnPServiceProxy *proxy, + DLEYNA_LOG_DEBUG("Exit"); + } + +-static GUPnPServiceProxyAction *prv_get_protocol_info( +- dleyna_service_task_t *task, +- GUPnPServiceProxy *proxy, +- gboolean *failed) ++static void prv_introspection_wrap_cb (GUPnPServiceInfo *info, ++ GUPnPServiceIntrospection *introspection, ++ const GError *error, ++ gpointer user_data) ++{ ++ if (error != NULL) { ++ g_task_return_error (G_TASK (user_data), ++ g_error_copy (error)); ++ } else { ++ g_task_return_pointer (G_TASK (user_data), ++ introspection, ++ g_object_unref); ++ } ++ ++ g_object_unref (G_OBJECT (user_data)); ++} ++ ++void prv_introspect_async (GUPnPServiceInfo *info, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ GTask *task = g_task_new (info, cancellable, callback, user_data); ++ ++ gupnp_service_info_get_introspection_async_full (info, ++ prv_introspection_wrap_cb, ++ cancellable, ++ task); ++} ++ ++static GUPnPServiceIntrospection *prv_introspect_finish ++ (GUPnPServiceInfo *info, ++ GAsyncResult *res, ++ GError **error) ++{ ++ g_return_val_if_fail (g_task_is_valid (res, info), NULL); ++ ++ return g_task_propagate_pointer (G_TASK (res), error); ++} ++ ++static gint compare_speeds(gconstpointer a, gconstpointer b); ++ ++static void prv_introspect_av_cb (GObject *target, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data; ++ GError *error = NULL; ++ GUPnPServiceIntrospection *introspection; ++ const GUPnPServiceStateVariableInfo *svi; ++ GList *allowed_values; ++ GVariant *speeds = NULL; ++ const GUPnPServiceActionInfo *sai; ++ ++ DLEYNA_LOG_DEBUG("Enter"); ++ ++ priv_t->dev->construct_step++; ++ ++ introspection = prv_introspect_finish (GUPNP_SERVICE_INFO (target), res, &error); ++ ++ if (introspection == NULL || (error != NULL)) { ++ DLEYNA_LOG_WARNING("GetProtocolInfo operation failed: %s", ++ ((error != NULL) ? error->message ++ : "Invalid result")); ++ goto on_error; ++ } ++ ++ svi = gupnp_service_introspection_get_state_variable( ++ introspection, ++ "TransportPlaySpeed"); ++ ++ if (svi && svi->allowed_values) { ++ allowed_values = svi->allowed_values; ++ ++ allowed_values = g_list_sort(allowed_values, compare_speeds); ++ ++ prv_get_rates_values(allowed_values, &speeds, ++ &priv_t->dev->transport_play_speeds, ++ &priv_t->dev->min_rate, ++ &priv_t->dev->max_rate); ++ ++ priv_t->dev->mpris_transport_play_speeds = g_variant_ref_sink(speeds); ++ } ++ ++ sai = gupnp_service_introspection_get_action( ++ introspection, ++ "X_DLNA_GetBytePositionInfo"); ++ ++ priv_t->dev->can_get_byte_position = (sai != NULL); ++ ++on_error: ++ g_clear_object(&introspection); ++ ++ g_clear_error(&error); ++ ++ DLEYNA_LOG_DEBUG("Exit"); ++} ++ ++static void prv_introspect_rc_cb (GObject *target, ++ GAsyncResult *res, ++ gpointer user_data) ++{ ++ prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data; ++ GError *error = NULL; ++ GUPnPServiceIntrospection *introspection; ++ const GUPnPServiceStateVariableInfo *svi; ++ ++ DLEYNA_LOG_DEBUG("Enter"); ++ ++ priv_t->dev->construct_step++; ++ ++ introspection = prv_introspect_finish (GUPNP_SERVICE_INFO (target), res, &error); ++ ++ if (introspection == NULL || (error != NULL)) { ++ DLEYNA_LOG_WARNING("GetProtocolInfo operation failed: %s", ++ ((error != NULL) ? error->message ++ : "Invalid result")); ++ goto on_error; ++ } ++ ++ svi = gupnp_service_introspection_get_state_variable(introspection, ++ "Volume"); ++ if (svi != NULL) ++ priv_t->dev->max_volume = g_value_get_uint(&svi->maximum); ++ ++on_error: ++ g_clear_object(&introspection); ++ ++ g_clear_error(&error); ++ ++ DLEYNA_LOG_DEBUG("Exit"); ++} ++ ++static gboolean prv_get_protocol_info( ++ dleyna_gasync_task_t *task, ++ GObject *target) + { +- *failed = FALSE; ++ GUPnPServiceProxyAction *action; + +- return gupnp_service_proxy_begin_action( +- proxy, "GetProtocolInfo", +- dleyna_service_task_begin_action_cb, +- task, NULL); ++ action = gupnp_service_proxy_action_new("GetProtocolInfo", NULL); ++ ++ gupnp_service_proxy_call_action_async(GUPNP_SERVICE_PROXY (target), action, ++ dleyna_gasync_task_get_cancellable (task), ++ dleyna_gasync_task_ready_cb, ++ task); ++ ++ return FALSE; ++} ++ ++static gboolean prv_introspect(dleyna_gasync_task_t *task, GObject *target) ++{ ++ prv_introspect_async (GUPNP_SERVICE_INFO (target), ++ dleyna_gasync_task_get_cancellable (task), ++ dleyna_gasync_task_ready_cb, ++ task); ++ ++ return FALSE; + } + +-static GUPnPServiceProxyAction *prv_subscribe(dleyna_service_task_t *task, +- GUPnPServiceProxy *proxy, +- gboolean *failed) ++static gboolean prv_subscribe(dleyna_gasync_task_t *task, GObject *target) + { + dlr_device_t *device; + + DLEYNA_LOG_DEBUG("Enter"); + +- device = (dlr_device_t *)dleyna_service_task_get_user_data(task); ++ device = (dlr_device_t *)dleyna_gasync_task_get_user_data(task); + + device->construct_step++; + prv_device_subscribe_context(device); + +- *failed = FALSE; +- + DLEYNA_LOG_DEBUG("Exit"); + +- return NULL; ++ return FALSE; + } + +-static GUPnPServiceProxyAction *prv_declare(dleyna_service_task_t *task, +- GUPnPServiceProxy *proxy, +- gboolean *failed) ++static gboolean prv_declare(dleyna_gasync_task_t *task, ++ GObject *target) + { + unsigned int i; + dlr_device_t *device; + prv_new_device_ct_t *priv_t; + const dleyna_connector_dispatch_cb_t *table; ++ gboolean result = FALSE; + + DLEYNA_LOG_DEBUG("Enter"); + +- *failed = FALSE; +- +- priv_t = (prv_new_device_ct_t *)dleyna_service_task_get_user_data(task); ++ priv_t = (prv_new_device_ct_t *)dleyna_gasync_task_get_user_data(task); + device = priv_t->dev; + device->construct_step++; + +@@ -770,16 +924,16 @@ static GUPnPServiceProxyAction *prv_declare(dleyna_service_task_t *task, + table + i); + + if (!device->ids[i]) { +- *failed = TRUE; ++ result = TRUE; + goto on_error; + } + } + + on_error: + +-DLEYNA_LOG_DEBUG("Exit"); ++ DLEYNA_LOG_DEBUG("Exit"); + +- return NULL; ++ return result; + } + + static void prv_free_rc_event(gpointer user_data) +@@ -800,6 +954,9 @@ void dlr_device_construct( + { + prv_new_device_ct_t *priv_t; + GUPnPServiceProxy *s_proxy; ++ GUPnPServiceProxy *av_proxy; ++ GUPnPServiceProxy *rc_proxy; ++ GCancellable *cancellable; + + DLEYNA_LOG_DEBUG("Current step: %d", dev->construct_step); + +@@ -809,19 +966,42 @@ void dlr_device_construct( + priv_t->dispatch_table = dispatch_table; + + s_proxy = context->service_proxies.cm_proxy; ++ cancellable = g_cancellable_new (); + + if (dev->construct_step < 1) +- dleyna_service_task_add(queue_id, prv_get_protocol_info, +- s_proxy, prv_get_protocol_info_cb, +- NULL, priv_t); ++ dleyna_gasync_task_add(queue_id, ++ prv_get_protocol_info, ++ G_OBJECT(s_proxy), ++ prv_get_protocol_info_cb, ++ cancellable, ++ NULL, priv_t); ++ ++ av_proxy = context->service_proxies.av_proxy; ++ if (dev->construct_step < 2) ++ dleyna_gasync_task_add(queue_id, ++ prv_introspect, ++ G_OBJECT(av_proxy), ++ prv_introspect_av_cb, ++ cancellable, ++ NULL, priv_t); ++ ++ rc_proxy = context->service_proxies.rc_proxy; ++ if (dev->construct_step < 3) ++ dleyna_gasync_task_add(queue_id, ++ prv_introspect, ++ G_OBJECT(rc_proxy), ++ prv_introspect_rc_cb, ++ cancellable, ++ NULL, priv_t); ++ + + /* The following task should always be completed */ +- dleyna_service_task_add(queue_id, prv_subscribe, s_proxy, +- NULL, NULL, dev); ++ dleyna_gasync_task_add(queue_id, prv_subscribe, G_OBJECT(s_proxy), ++ NULL, NULL, NULL, dev); + +- if (dev->construct_step < 3) +- dleyna_service_task_add(queue_id, prv_declare, s_proxy, +- NULL, g_free, priv_t); ++ if (dev->construct_step < 5) ++ dleyna_gasync_task_add(queue_id, prv_declare, G_OBJECT(s_proxy), ++ NULL, NULL, g_free, priv_t); + + dleyna_task_queue_start(queue_id); + +@@ -2121,133 +2301,6 @@ static void prv_get_rates_values(GList *allowed_tp_speeds, + return; + } + +-static gboolean prv_get_av_service_states_values(GUPnPServiceProxy *av_proxy, +- GVariant **mpris_tp_speeds, +- GPtrArray **upnp_tp_speeds, +- double *min_rate, +- double *max_rate, +- gboolean *can_get_byte_pos) +-{ +- const GUPnPServiceStateVariableInfo *svi; +- const GUPnPServiceActionInfo *sai; +- GUPnPServiceIntrospection *introspection; +- GError *error = NULL; +- GVariant *speeds = NULL; +- GList *allowed_values; +- gpointer weak_ref = NULL; +- gboolean device_alive = TRUE; +- +- /* TODO: this weak_ref hack is needed as +- gupnp_service_info_get_introspection iterates the main loop. +- This can result in our device getting deleted before this +- function returns. Ultimately, this code needs to be re-written +- to use gupnp_service_info_get_introspection_async but this cannot +- really be done until GUPnP provides a way to cancel this function. */ +- +- weak_ref = av_proxy; +- g_object_add_weak_pointer(G_OBJECT(av_proxy), &weak_ref); +- +- introspection = gupnp_service_info_get_introspection( +- GUPNP_SERVICE_INFO(av_proxy), +- &error); +- +- if (!weak_ref) { +- DLEYNA_LOG_WARNING("Lost device during introspection call"); +- device_alive = FALSE; +- goto exit; +- } +- +- g_object_remove_weak_pointer(G_OBJECT(av_proxy), &weak_ref); +- +- if (error != NULL) { +- DLEYNA_LOG_DEBUG( +- "failed to fetch AV service introspection file"); +- +- g_error_free(error); +- +- goto exit; +- } +- +- svi = gupnp_service_introspection_get_state_variable( +- introspection, +- "TransportPlaySpeed"); +- +- if (svi && svi->allowed_values) { +- allowed_values = svi->allowed_values; +- +- allowed_values = g_list_sort(allowed_values, compare_speeds); +- +- prv_get_rates_values(allowed_values, &speeds, upnp_tp_speeds, +- min_rate, max_rate); +- +- *mpris_tp_speeds = g_variant_ref_sink(speeds); +- } +- +- sai = gupnp_service_introspection_get_action( +- introspection, +- "X_DLNA_GetBytePositionInfo"); +- +- *can_get_byte_pos = (sai != NULL); +- +- g_object_unref(introspection); +- +-exit: +- +- return device_alive; +-} +- +-static gboolean prv_get_rc_service_states_values(GUPnPServiceProxy *rc_proxy, +- guint *max_volume) +-{ +- const GUPnPServiceStateVariableInfo *svi; +- GUPnPServiceIntrospection *introspection; +- GError *error = NULL; +- gpointer weak_ref = NULL; +- gboolean device_alive = TRUE; +- +- /* TODO: this weak_ref hack is needed as +- gupnp_service_info_get_introspection iterates the main loop. +- This can result in our device getting deleted before this +- function returns. Ultimately, this code needs to be re-written +- to use gupnp_service_info_get_introspection_async but this cannot +- really be done until GUPnP provides a way to cancel this function. */ +- +- weak_ref = rc_proxy; +- g_object_add_weak_pointer(G_OBJECT(rc_proxy), &weak_ref); +- +- introspection = gupnp_service_info_get_introspection( +- GUPNP_SERVICE_INFO(rc_proxy), +- &error); +- +- if (!weak_ref) { +- DLEYNA_LOG_WARNING("Lost device during introspection call"); +- device_alive = FALSE; +- goto exit; +- } +- +- g_object_remove_weak_pointer(G_OBJECT(rc_proxy), &weak_ref); +- +- if (error != NULL) { +- DLEYNA_LOG_DEBUG( +- "failed to fetch RC service introspection file"); +- +- g_error_free(error); +- +- goto exit; +- } +- +- svi = gupnp_service_introspection_get_state_variable(introspection, +- "Volume"); +- if (svi != NULL) +- *max_volume = g_value_get_uint(&svi->maximum); +- +- g_object_unref(introspection); +- +-exit: +- +- return device_alive; +-} +- + static void prv_update_device_props(GUPnPDeviceInfo *proxy, GHashTable *props) + { + GVariant *val; +@@ -2378,34 +2431,6 @@ static gboolean prv_props_update(dlr_device_t *device, dlr_task_t *task) + + service_proxies = &context->service_proxies; + +- /* TODO: We should not retrieve these values here. They should be +- retrieved during device construction. */ +- +- if (service_proxies->av_proxy) +- if (!prv_get_av_service_states_values( +- service_proxies->av_proxy, +- &device->mpris_transport_play_speeds, +- &device->transport_play_speeds, +- &device->min_rate, +- &device->max_rate, +- &device->can_get_byte_position)) { +- DLEYNA_LOG_DEBUG("Lost Device AV"); +- +- device_alive = FALSE; +- goto on_lost_device; +- } +- +- /* TODO: We should not retrieve these values here. They should be +- retrieved during device construction. */ +- +- if (service_proxies->rc_proxy) +- if (!prv_get_rc_service_states_values(service_proxies->rc_proxy, +- &device->max_volume)) { +- DLEYNA_LOG_DEBUG("Lost Device RC"); +- device_alive = FALSE; +- goto on_lost_device; +- } +- + changed_props_vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}")); + + prv_add_player_speed_props(device->props.player_props, +diff --git a/libdleyna/renderer/gasync-task.c b/libdleyna/renderer/gasync-task.c +new file mode 100644 +index 0000000..47a0ad5 +--- /dev/null ++++ b/libdleyna/renderer/gasync-task.c +@@ -0,0 +1,135 @@ ++/* ++ * dLeyna ++ * ++ * Copyright (c) 2019 Jens Georg ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU Lesser General Public License, ++ * version 2.1, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#include "gasync-task.h" ++#include ++ ++struct dleyna_gasync_task_t_ { ++ dleyna_task_atom_t base; ++ dleyna_gasync_task_action action; ++ GObject *target; ++ GAsyncReadyCallback callback; ++ GCancellable *cancellable; ++ GDestroyNotify free_func; ++ gpointer cb_user_data; ++}; ++ ++const char *dleyna_gasync_task_create_source(void) ++{ ++ static unsigned int cpt = 1; ++ static char source[27]; ++ ++ g_snprintf(source, 27, "gasync-source-%d", cpt); ++ cpt++; ++ ++ return source; ++} ++ ++void dleyna_gasync_task_add(const dleyna_task_queue_key_t *queue_id, ++ dleyna_gasync_task_action action, ++ GObject *target, ++ GAsyncReadyCallback callback, ++ GCancellable *cancellable, ++ GDestroyNotify free_func, ++ gpointer cb_user_data) ++{ ++ dleyna_gasync_task_t *task; ++ ++ task = g_new0(dleyna_gasync_task_t, 1); ++ ++ task->action = action; ++ task->callback = callback; ++ task->cancellable = cancellable; ++ task->free_func = free_func; ++ task->cb_user_data = cb_user_data; ++ task->target = target; ++ ++ if (target != NULL) { ++ g_object_add_weak_pointer (target, (gpointer *)(&task->target)); ++ } ++ ++ dleyna_task_queue_add_task(queue_id, &task->base); ++} ++ ++void dleyna_gasync_task_ready_cb(GObject *source, GAsyncResult *res, gpointer user_data) ++{ ++ dleyna_gasync_task_t *task = (dleyna_gasync_task_t *)user_data; ++ ++ task->callback(source, res, task->cb_user_data); ++ ++ dleyna_task_queue_task_completed(task->base.queue_id); ++} ++ ++void dleyna_gasync_task_process_cb(dleyna_task_atom_t *atom, ++ gpointer user_data) ++{ ++ gboolean failed = FALSE; ++ ++ dleyna_gasync_task_t *task = (dleyna_gasync_task_t *)atom; ++ ++ failed = task->action(task, task->target); ++ ++ if (failed) { ++ dleyna_task_processor_cancel_queue(task->base.queue_id); ++ dleyna_task_queue_task_completed(task->base.queue_id); ++ } ++ ++ if (task->callback == NULL) { ++ dleyna_task_queue_task_completed(task->base.queue_id); ++ } ++} ++ ++void dleyna_gasync_task_cancel_cb(dleyna_task_atom_t *atom, ++ gpointer user_data) ++{ ++ dleyna_gasync_task_t *task = (dleyna_gasync_task_t *)atom; ++ ++ if (task->cancellable) { ++ g_cancellable_cancel (task->cancellable); ++ task->cancellable = NULL; ++ ++ dleyna_task_queue_task_completed(task->base.queue_id); ++ } ++} ++ ++void dleyna_gasync_task_delete_cb(dleyna_task_atom_t *atom, ++ gpointer user_data) ++{ ++ dleyna_gasync_task_t *task = (dleyna_gasync_task_t *)atom; ++ ++ if (task->free_func != NULL) ++ task->free_func(task->cb_user_data); ++ ++ if (task->target != NULL) { ++ g_object_remove_weak_pointer(task->target, (gpointer *)&task->target); ++ } ++ ++ g_free(task); ++} ++ ++gpointer dleyna_gasync_task_get_user_data(dleyna_gasync_task_t *task) ++{ ++ return task->cb_user_data; ++} ++ ++GCancellable *dleyna_gasync_task_get_cancellable(dleyna_gasync_task_t *task) ++{ ++ return task->cancellable; ++} +diff --git a/libdleyna/renderer/gasync-task.h b/libdleyna/renderer/gasync-task.h +new file mode 100644 +index 0000000..629e48c +--- /dev/null ++++ b/libdleyna/renderer/gasync-task.h +@@ -0,0 +1,57 @@ ++/* ++ * dLeyna ++ * ++ * Copyright (c) 2019 Jens Georg ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU Lesser General Public License, ++ * version 2.1, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public License ++ * along with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ */ ++ ++#pragma once ++ ++#include ++ ++#include ++#include ++ ++typedef struct dleyna_gasync_task_t_ dleyna_gasync_task_t; ++ ++typedef gboolean (*dleyna_gasync_task_action) ++ (dleyna_gasync_task_t *task, ++ GObject *target); ++ ++const char *dleyna_gasync_task_create_source(void); ++ ++void dleyna_gasync_task_add(const dleyna_task_queue_key_t *queue_id, ++ dleyna_gasync_task_action action, ++ GObject *target, ++ GAsyncReadyCallback callback, ++ GCancellable *cancellable, ++ GDestroyNotify free_func, ++ gpointer cb_user_data); ++ ++void dleyna_gasync_task_ready_cb(GObject *source, GAsyncResult *res, gpointer user_data); ++ ++void dleyna_gasync_task_process_cb(dleyna_task_atom_t *atom, ++ gpointer user_data); ++ ++void dleyna_gasync_task_cancel_cb(dleyna_task_atom_t *atom, ++ gpointer user_data); ++ ++void dleyna_gasync_task_delete_cb(dleyna_task_atom_t *atom, ++ gpointer user_data); ++ ++gpointer dleyna_gasync_task_get_user_data(dleyna_gasync_task_t *task); ++ ++GCancellable *dleyna_gasync_task_get_cancellable(dleyna_gasync_task_t *task); +diff --git a/libdleyna/renderer/manager.c b/libdleyna/renderer/manager.c +index 74052f5..bea9935 100644 +--- a/libdleyna/renderer/manager.c ++++ b/libdleyna/renderer/manager.c +@@ -25,7 +25,6 @@ + + #include + #include +-#include + #include + + #include "async.h" +diff --git a/libdleyna/renderer/upnp.c b/libdleyna/renderer/upnp.c +index 17cbda7..0e9d483 100644 +--- a/libdleyna/renderer/upnp.c ++++ b/libdleyna/renderer/upnp.c +@@ -28,10 +28,10 @@ + + #include + #include +-#include + + #include "async.h" + #include "device.h" ++#include "gasync-task.h" + #include "host-service.h" + #include "prop-defs.h" + #include "upnp.h" +@@ -116,12 +116,12 @@ static const dleyna_task_queue_key_t *prv_create_device_queue( + + queue_id = dleyna_task_processor_add_queue( + dlr_renderer_service_get_task_processor(), +- dleyna_service_task_create_source(), ++ dleyna_gasync_task_create_source(), + DLR_RENDERER_SINK, + DLEYNA_TASK_QUEUE_FLAG_AUTO_REMOVE, +- dleyna_service_task_process_cb, +- dleyna_service_task_cancel_cb, +- dleyna_service_task_delete_cb); ++ dleyna_gasync_task_process_cb, ++ dleyna_gasync_task_cancel_cb, ++ dleyna_gasync_task_delete_cb); + dleyna_task_queue_set_finally(queue_id, prv_device_chain_end); + dleyna_task_queue_set_user_data(queue_id, *priv_t); + +@@ -243,8 +243,8 @@ static void prv_server_unavailable_cb(GUPnPControlPoint *cp, + + udn = gupnp_device_info_get_udn((GUPnPDeviceInfo *)proxy); + +- ip_address = gupnp_context_get_host_ip( +- gupnp_control_point_get_context(cp)); ++ ip_address = gssdp_client_get_host_ip( ++ GSSDP_CLIENT(gupnp_control_point_get_context(cp))); + + if (!udn || !ip_address) + goto on_error; + +From 79593067cf40ed58a3bd95311c7fa108feafcb46 Mon Sep 17 00:00:00 2001 +From: Jens Georg +Date: Sat, 21 Sep 2019 20:37:33 +0200 +Subject: [PATCH 3/4] Move to GUPnP 1.2 + +Fixes #166 +--- + configure.ac | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 271ee92..364659d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -38,8 +38,8 @@ LT_LANG([C]) + PKG_PROG_PKG_CONFIG(0.16) + PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28]) + PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.28]) +-PKG_CHECK_MODULES([GSSDP], [gssdp-1.0 >= 0.13.2]) +-PKG_CHECK_MODULES([GUPNP], [gupnp-1.0 >= 0.20.5]) ++PKG_CHECK_MODULES([GSSDP], [gssdp-1.2 >= 1.2.0]) ++PKG_CHECK_MODULES([GUPNP], [gupnp-1.2 >= 1.2.0]) + PKG_CHECK_MODULES([GUPNPAV], [gupnp-av-1.0 >= 0.11.5]) + PKG_CHECK_MODULES([GUPNPDLNA], [gupnp-dlna-2.0 >= 0.9.4]) + PKG_CHECK_MODULES([SOUP], [libsoup-2.4 >= 2.28.2]) + +From 66e755a89cdcd7f10a535131a340c3f3ab371194 Mon Sep 17 00:00:00 2001 +From: Jens Georg +Date: Mon, 23 Sep 2019 00:08:38 +0200 +Subject: [PATCH 4/4] Protect introspection calls against missing proxies + +--- + libdleyna/renderer/device.c | 38 +++++++++++++++++++++++-------------- + 1 file changed, 24 insertions(+), 14 deletions(-) + +diff --git a/libdleyna/renderer/device.c b/libdleyna/renderer/device.c +index 73b3dd3..525a23d 100644 +--- a/libdleyna/renderer/device.c ++++ b/libdleyna/renderer/device.c +@@ -977,22 +977,32 @@ void dlr_device_construct( + NULL, priv_t); + + av_proxy = context->service_proxies.av_proxy; +- if (dev->construct_step < 2) +- dleyna_gasync_task_add(queue_id, +- prv_introspect, +- G_OBJECT(av_proxy), +- prv_introspect_av_cb, +- cancellable, +- NULL, priv_t); ++ if (dev->construct_step < 2) { ++ if (av_proxy == NULL) { ++ dev->construct_step++; ++ } else { ++ dleyna_gasync_task_add(queue_id, ++ prv_introspect, ++ G_OBJECT(av_proxy), ++ prv_introspect_av_cb, ++ cancellable, ++ NULL, priv_t); ++ } ++ } + + rc_proxy = context->service_proxies.rc_proxy; +- if (dev->construct_step < 3) +- dleyna_gasync_task_add(queue_id, +- prv_introspect, +- G_OBJECT(rc_proxy), +- prv_introspect_rc_cb, +- cancellable, +- NULL, priv_t); ++ if (dev->construct_step < 3) { ++ if (rc_proxy == NULL) { ++ dev->construct_step++; ++ } else { ++ dleyna_gasync_task_add(queue_id, ++ prv_introspect, ++ G_OBJECT(rc_proxy), ++ prv_introspect_rc_cb, ++ cancellable, ++ NULL, priv_t); ++ } ++ } + + + /* The following task should always be completed */ diff --git a/dleyna-renderer-0.6.0.tar_2.gz b/dleyna-renderer-0.6.0.tar_2.gz new file mode 100644 index 0000000..5d34781 Binary files /dev/null and b/dleyna-renderer-0.6.0.tar_2.gz differ diff --git a/dleyna-renderer-Fix-the-cancellation-of-dleyna_gasync_task_t-tasks.patch b/dleyna-renderer-Fix-the-cancellation-of-dleyna_gasync_task_t-tasks.patch new file mode 100644 index 0000000..79372dc --- /dev/null +++ b/dleyna-renderer-Fix-the-cancellation-of-dleyna_gasync_task_t-tasks.patch @@ -0,0 +1,385 @@ +From cc2e5d5f51e354e43c80ae0bbaf96f8bb4c9a3aa Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Fri, 4 Dec 2020 20:54:47 +0100 +Subject: [PATCH] Fix the cancellation of dleyna_gasync_task_t tasks + +When a GAsyncResult-based asynchronous operation is cancelled, by +convention, it will always asynchronously invoke the +GAsyncReadyCallback with the error set to G_IO_ERROR_CANCELLED. +However, when the queues in a dleyna_task_processor_t are cancelled, +the tasks within them are immediately destroyed. This means that a +GAsyncReadyCallback shouldn't try to access a task after cancellation +because it would be pointing to invalid memory. + +Here's an example: + +%0 prv_introspect_rc_cb (target=0x556be880d9f0, + res=0x556be8840280, + user_data=0x556be88fef70) + at device.c:835 +%1 dleyna_gasync_task_ready_cb (source=, + res=, + user_data=0x556be89a0ac0) + at gasync-task.c:75 +%2 g_task_return_now (task=0x556be8840280) at ../gio/gtask.c:1215 +%3 complete_in_idle_cb (task=task@entry=0x556be8840280) + at ../gio/gtask.c:1229 +%4 g_idle_dispatch (source=source@entry=0x556be8844e40, + callback=0x7f87cd82b380 , + user_data=0x556be8840280) + at ../glib/gmain.c:5836 +%5 g_main_dispatch (context=0x556be87e6be0) at ../glib/gmain.c:3325 +%6 g_main_context_dispatch (context=0x556be87e6be0) + at ../glib/gmain.c:4043 +%7 g_main_context_iterate.constprop.0 (context=0x556be87e6be0, + block=block@entry=1, + dispatch=dispatch@entry=1, + self=) + at ../glib/gmain.c:4119 +%8 g_main_loop_run (loop=0x556be8828130) at ../glib/gmain.c:4317 +%9 dleyna_main_loop_start (server=, + control_point=, + user_data=) + at libdleyna/core/main-loop.c:154 +%10 __libc_start_main (main=0x556be79fe0d0
, + argc=1, argv=0x7ffeb4610d98, + init=, + fini=, + rtld_fini=, + stack_end=0x7ffeb4610d88) + at ../csu/libc-start.c:314 +%11 0x0000556be79fe14e in _start () + +Till now, dleyna_gasync_task_ready_cb was being used as the common +GAsyncReadyCallback for all tasks. However, it doesn't support +cancellation because that requires the use of the 'finish' counterpart +of the specific asynchronous operation in question. Therefore, instead +of a common GAsyncReadyCallback, each task needs to provide its own. + +Secondly, when cancelling a dleyna_gasync_task_t through +dleyna_gasync_task_cancel_cb, dleyna_task_queue_task_completed should +be called only if the task was current. Calling it for tasks that were +waiting in the queue breaks the semantics of the processor because the +running_tasks counter is an unsigned integer and can't accommodate +negative values. + +https://bugzilla.redhat.com/show_bug.cgi?id=1900645 +https://github.com/phako/dleyna-renderer/pull/4 +--- + libdleyna/renderer/device.c | 83 ++++++++++++++++++++++++-------- + libdleyna/renderer/gasync-task.c | 30 ++---------- + libdleyna/renderer/gasync-task.h | 1 - + 3 files changed, 69 insertions(+), 45 deletions(-) + +diff --git a/libdleyna/renderer/device.c b/libdleyna/renderer/device.c +index 525a23d978c7..4c0acb79c284 100644 +--- a/libdleyna/renderer/device.c ++++ b/libdleyna/renderer/device.c +@@ -683,15 +683,23 @@ static void prv_get_protocol_info_cb(GObject *target, + gchar *result = NULL; + gboolean end; + GError *error = NULL; +- prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data; ++ dleyna_gasync_task_t *task = NULL; ++ prv_new_device_ct_t *priv_t = NULL; + GUPnPServiceProxyAction *action; + + DLEYNA_LOG_DEBUG("Enter"); + +- priv_t->dev->construct_step++; +- + action = gupnp_service_proxy_call_action_finish(GUPNP_SERVICE_PROXY(target), res, &error); + ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { ++ DLEYNA_LOG_WARNING("GetProtocolInfo operation cancelled"); ++ goto on_error; ++ } ++ ++ task = (dleyna_gasync_task_t *) user_data; ++ priv_t = (prv_new_device_ct_t *) dleyna_gasync_task_get_user_data (task); ++ priv_t->dev->construct_step++; ++ + if (action == NULL || (error != NULL)) { + DLEYNA_LOG_WARNING("GetProtocolInfo operation failed: %s", + ((error != NULL) ? error->message +@@ -711,6 +719,9 @@ static void prv_get_protocol_info_cb(GObject *target, + + on_error: + ++ if (task) ++ dleyna_task_queue_task_completed (((dleyna_task_atom_t *) task)->queue_id); ++ + if (action) { + gupnp_service_proxy_action_unref(action); + } +@@ -769,7 +780,8 @@ static void prv_introspect_av_cb (GObject *target, + GAsyncResult *res, + gpointer user_data) + { +- prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data; ++ dleyna_gasync_task_t *task = NULL; ++ prv_new_device_ct_t *priv_t = NULL; + GError *error = NULL; + GUPnPServiceIntrospection *introspection; + const GUPnPServiceStateVariableInfo *svi; +@@ -779,10 +791,17 @@ static void prv_introspect_av_cb (GObject *target, + + DLEYNA_LOG_DEBUG("Enter"); + +- priv_t->dev->construct_step++; +- + introspection = prv_introspect_finish (GUPNP_SERVICE_INFO (target), res, &error); + ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { ++ DLEYNA_LOG_WARNING("GetProtocolInfo operation cancelled"); ++ goto on_error; ++ } ++ ++ task = (dleyna_gasync_task_t *) user_data; ++ priv_t = (prv_new_device_ct_t *) dleyna_gasync_task_get_user_data (task); ++ priv_t->dev->construct_step++; ++ + if (introspection == NULL || (error != NULL)) { + DLEYNA_LOG_WARNING("GetProtocolInfo operation failed: %s", + ((error != NULL) ? error->message +@@ -814,6 +833,9 @@ static void prv_introspect_av_cb (GObject *target, + priv_t->dev->can_get_byte_position = (sai != NULL); + + on_error: ++ if (task) ++ dleyna_task_queue_task_completed (((dleyna_task_atom_t *) task)->queue_id); ++ + g_clear_object(&introspection); + + g_clear_error(&error); +@@ -825,17 +847,25 @@ static void prv_introspect_rc_cb (GObject *target, + GAsyncResult *res, + gpointer user_data) + { +- prv_new_device_ct_t *priv_t = (prv_new_device_ct_t *)user_data; ++ dleyna_gasync_task_t *task = NULL; ++ prv_new_device_ct_t *priv_t = NULL; + GError *error = NULL; + GUPnPServiceIntrospection *introspection; + const GUPnPServiceStateVariableInfo *svi; + + DLEYNA_LOG_DEBUG("Enter"); + +- priv_t->dev->construct_step++; +- + introspection = prv_introspect_finish (GUPNP_SERVICE_INFO (target), res, &error); + ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { ++ DLEYNA_LOG_WARNING("GetProtocolInfo operation cancelled"); ++ goto on_error; ++ } ++ ++ task = (dleyna_gasync_task_t *) user_data; ++ priv_t = (prv_new_device_ct_t *) dleyna_gasync_task_get_user_data (task); ++ priv_t->dev->construct_step++; ++ + if (introspection == NULL || (error != NULL)) { + DLEYNA_LOG_WARNING("GetProtocolInfo operation failed: %s", + ((error != NULL) ? error->message +@@ -849,6 +879,9 @@ static void prv_introspect_rc_cb (GObject *target, + priv_t->dev->max_volume = g_value_get_uint(&svi->maximum); + + on_error: ++ if (task) ++ dleyna_task_queue_task_completed (((dleyna_task_atom_t *) task)->queue_id); ++ + g_clear_object(&introspection); + + g_clear_error(&error); +@@ -866,17 +899,27 @@ static gboolean prv_get_protocol_info( + + gupnp_service_proxy_call_action_async(GUPNP_SERVICE_PROXY (target), action, + dleyna_gasync_task_get_cancellable (task), +- dleyna_gasync_task_ready_cb, ++ prv_get_protocol_info_cb, + task); + + return FALSE; + } + +-static gboolean prv_introspect(dleyna_gasync_task_t *task, GObject *target) ++static gboolean prv_introspect_av(dleyna_gasync_task_t *task, GObject *target) + { + prv_introspect_async (GUPNP_SERVICE_INFO (target), + dleyna_gasync_task_get_cancellable (task), +- dleyna_gasync_task_ready_cb, ++ prv_introspect_av_cb, ++ task); ++ ++ return FALSE; ++} ++ ++static gboolean prv_introspect_rc(dleyna_gasync_task_t *task, GObject *target) ++{ ++ prv_introspect_async (GUPNP_SERVICE_INFO (target), ++ dleyna_gasync_task_get_cancellable (task), ++ prv_introspect_rc_cb, + task); + + return FALSE; +@@ -893,6 +936,8 @@ static gboolean prv_subscribe(dleyna_gasync_task_t *task, GObject *target) + device->construct_step++; + prv_device_subscribe_context(device); + ++ dleyna_task_queue_task_completed (((dleyna_task_atom_t *) task)->queue_id); ++ + DLEYNA_LOG_DEBUG("Exit"); + + return FALSE; +@@ -924,6 +969,7 @@ static gboolean prv_declare(dleyna_gasync_task_t *task, + table + i); + + if (!device->ids[i]) { ++ dleyna_task_processor_cancel_queue (((dleyna_task_atom_t *) task)->queue_id); + result = TRUE; + goto on_error; + } +@@ -931,6 +977,8 @@ static gboolean prv_declare(dleyna_gasync_task_t *task, + + on_error: + ++ dleyna_task_queue_task_completed (((dleyna_task_atom_t *) task)->queue_id); ++ + DLEYNA_LOG_DEBUG("Exit"); + + return result; +@@ -972,7 +1020,6 @@ void dlr_device_construct( + dleyna_gasync_task_add(queue_id, + prv_get_protocol_info, + G_OBJECT(s_proxy), +- prv_get_protocol_info_cb, + cancellable, + NULL, priv_t); + +@@ -982,9 +1029,8 @@ void dlr_device_construct( + dev->construct_step++; + } else { + dleyna_gasync_task_add(queue_id, +- prv_introspect, ++ prv_introspect_av, + G_OBJECT(av_proxy), +- prv_introspect_av_cb, + cancellable, + NULL, priv_t); + } +@@ -996,9 +1042,8 @@ void dlr_device_construct( + dev->construct_step++; + } else { + dleyna_gasync_task_add(queue_id, +- prv_introspect, ++ prv_introspect_rc, + G_OBJECT(rc_proxy), +- prv_introspect_rc_cb, + cancellable, + NULL, priv_t); + } +@@ -1007,11 +1052,11 @@ void dlr_device_construct( + + /* The following task should always be completed */ + dleyna_gasync_task_add(queue_id, prv_subscribe, G_OBJECT(s_proxy), +- NULL, NULL, NULL, dev); ++ NULL, NULL, dev); + + if (dev->construct_step < 5) + dleyna_gasync_task_add(queue_id, prv_declare, G_OBJECT(s_proxy), +- NULL, NULL, g_free, priv_t); ++ NULL, g_free, priv_t); + + dleyna_task_queue_start(queue_id); + +diff --git a/libdleyna/renderer/gasync-task.c b/libdleyna/renderer/gasync-task.c +index 47a0ad567cc2..0c65a22b6235 100644 +--- a/libdleyna/renderer/gasync-task.c ++++ b/libdleyna/renderer/gasync-task.c +@@ -25,9 +25,9 @@ struct dleyna_gasync_task_t_ { + dleyna_task_atom_t base; + dleyna_gasync_task_action action; + GObject *target; +- GAsyncReadyCallback callback; + GCancellable *cancellable; + GDestroyNotify free_func; ++ gboolean current; + gpointer cb_user_data; + }; + +@@ -45,7 +45,6 @@ const char *dleyna_gasync_task_create_source(void) + void dleyna_gasync_task_add(const dleyna_task_queue_key_t *queue_id, + dleyna_gasync_task_action action, + GObject *target, +- GAsyncReadyCallback callback, + GCancellable *cancellable, + GDestroyNotify free_func, + gpointer cb_user_data) +@@ -55,7 +54,6 @@ void dleyna_gasync_task_add(const dleyna_task_queue_key_t *queue_id, + task = g_new0(dleyna_gasync_task_t, 1); + + task->action = action; +- task->callback = callback; + task->cancellable = cancellable; + task->free_func = free_func; + task->cb_user_data = cb_user_data; +@@ -68,32 +66,13 @@ void dleyna_gasync_task_add(const dleyna_task_queue_key_t *queue_id, + dleyna_task_queue_add_task(queue_id, &task->base); + } + +-void dleyna_gasync_task_ready_cb(GObject *source, GAsyncResult *res, gpointer user_data) +-{ +- dleyna_gasync_task_t *task = (dleyna_gasync_task_t *)user_data; +- +- task->callback(source, res, task->cb_user_data); +- +- dleyna_task_queue_task_completed(task->base.queue_id); +-} +- + void dleyna_gasync_task_process_cb(dleyna_task_atom_t *atom, + gpointer user_data) + { +- gboolean failed = FALSE; +- + dleyna_gasync_task_t *task = (dleyna_gasync_task_t *)atom; + +- failed = task->action(task, task->target); +- +- if (failed) { +- dleyna_task_processor_cancel_queue(task->base.queue_id); +- dleyna_task_queue_task_completed(task->base.queue_id); +- } +- +- if (task->callback == NULL) { +- dleyna_task_queue_task_completed(task->base.queue_id); +- } ++ task->current = TRUE; ++ task->action(task, task->target); + } + + void dleyna_gasync_task_cancel_cb(dleyna_task_atom_t *atom, +@@ -105,7 +84,8 @@ void dleyna_gasync_task_cancel_cb(dleyna_task_atom_t *atom, + g_cancellable_cancel (task->cancellable); + task->cancellable = NULL; + +- dleyna_task_queue_task_completed(task->base.queue_id); ++ if (task->current) ++ dleyna_task_queue_task_completed(task->base.queue_id); + } + } + +diff --git a/libdleyna/renderer/gasync-task.h b/libdleyna/renderer/gasync-task.h +index 629e48ce35a3..443c44153098 100644 +--- a/libdleyna/renderer/gasync-task.h ++++ b/libdleyna/renderer/gasync-task.h +@@ -36,7 +36,6 @@ const char *dleyna_gasync_task_create_source(void); + void dleyna_gasync_task_add(const dleyna_task_queue_key_t *queue_id, + dleyna_gasync_task_action action, + GObject *target, +- GAsyncReadyCallback callback, + GCancellable *cancellable, + GDestroyNotify free_func, + gpointer cb_user_data); +-- +2.28.0 + diff --git a/dleyna-renderer.spec b/dleyna-renderer.spec new file mode 100644 index 0000000..6ff6735 --- /dev/null +++ b/dleyna-renderer.spec @@ -0,0 +1,70 @@ +%global api 1.0 + +Name: dleyna-renderer +Version: 0.6.0 +Release: 1 +Summary: Service for interacting with Digital Media Renderers +License: LGPLv2 +URL: https://01.org/dleyna/ +Source0: https://01.org/sites/default/files/downloads/dleyna/%{name}-%{version}.tar_2.gz +Patch0: 0001-UPnP-Disconnect-signal-handlers-during-destruction.patch +Patch1: 167.patch +Patch2: 0001-Add-a-manual.patch +Patch3: dleyna-renderer-Fix-the-cancellation-of-dleyna_gasync_task_t-tasks.patch + +BuildRequires: autoconf automake docbook-style-xsl libtool libxslt pkgconfig pkgconfig(dleyna-core-1.0) +BuildRequires: pkgconfig(gio-2.0) pkgconfig(glib-2.0) pkgconfig(gssdp-1.2) pkgconfig(gupnp-1.2) +BuildRequires: pkgconfig(gupnp-av-1.0) pkgconfig(gupnp-dlna-2.0) pkgconfig(libsoup-2.4) make + +Requires: dbus dleyna-connector-dbus + +%description +D-Bus service for clients to discover and manipulate DLNA Digital Media +Renderers (DMRs). + +%package devel +Summary: Development files for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +The %{name}-devel package contains libraries and header files for +developing applications that use %{name}. + +%prep +%autosetup -p1 + +%build +autoreconf -f -i +%configure \ + --disable-silent-rules \ + --disable-static + +sed --in-place --expression 's! -shared ! -Wl,--as-needed\0!g' libtool + +%make_build + +%install +%make_install +find $RPM_BUILD_ROOT -name '*.la' -delete + +rm -rf $RPM_BUILD_ROOT/%{_includedir} +rm -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/libdleyna-renderer-%{api}.so + +%files +%license COPYING +%doc AUTHORS +%doc ChangeLog +%doc README +%{_datadir}/dbus-1/services/com.intel.%{name}.service +%{_datadir}/man/man5/%{name}-service.conf.5* +%dir %{_libdir}/%{name} +%{_libdir}/%{name}/libdleyna-renderer-%{api}.so.* +%{_libexecdir}/%{name}-service +%config(noreplace) %{_sysconfdir}/%{name}-service.conf + +%files devel +%{_libdir}/pkgconfig/dleyna-renderer-service-%{api}.pc + +%changelog +* Fri Jul 02 2021 weijin deng - 0.6.0-1 +- Package init with 0.6.0