From e44c73d498e6d49cdfdb60f003bf65a0f7ee4e7f Mon Sep 17 00:00:00 2001 From: weijin-deng Date: Fri, 2 Jul 2021 10:54:19 +0800 Subject: [PATCH] Package init with 0.6.0 --- 0001-Add-a-manual.patch | 264 +++++ ...t-signal-handlers-during-destruction.patch | 77 ++ 167.patch | 942 ++++++++++++++++++ dleyna-renderer-0.6.0.tar_2.gz | Bin 0 -> 65566 bytes ...lation-of-dleyna_gasync_task_t-tasks.patch | 385 +++++++ dleyna-renderer.spec | 70 ++ 6 files changed, 1738 insertions(+) create mode 100644 0001-Add-a-manual.patch create mode 100644 0001-UPnP-Disconnect-signal-handlers-during-destruction.patch create mode 100644 167.patch create mode 100644 dleyna-renderer-0.6.0.tar_2.gz create mode 100644 dleyna-renderer-Fix-the-cancellation-of-dleyna_gasync_task_t-tasks.patch create mode 100644 dleyna-renderer.spec 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 0000000000000000000000000000000000000000..5d347815f08a5748e45992b8c6743fad4f3aebd0 GIT binary patch literal 65566 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

&cAR|l5u-va)x>EcB%tlALEL*ZN=CY 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 r;ZHh3R>%Y!)Cg27 +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