summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMart Raudsepp <leio@gentoo.org>2020-02-21 22:41:37 +0200
committerMart Raudsepp <leio@gentoo.org>2020-02-21 22:56:37 +0200
commitd0fe0c7cc316dcb6f7150e84c657f100c4a22ad0 (patch)
tree8d553d9b796942f54e4111551f17c24bd01cd556 /net-libs/dleyna-renderer
parentnet-libs/dleyna-core: bump to 0.6.0 and use gupnp-1.2 (diff)
downloadgentoo-d0fe0c7cc316dcb6f7150e84c657f100c4a22ad0.tar.gz
gentoo-d0fe0c7cc316dcb6f7150e84c657f100c4a22ad0.tar.bz2
gentoo-d0fe0c7cc316dcb6f7150e84c657f100c4a22ad0.zip
net-libs/dleyna-renderer: bump to 0.6.0 and use gupnp-1.2
Package-Manager: Portage-2.3.84, Repoman-2.3.20 Signed-off-by: Mart Raudsepp <leio@gentoo.org>
Diffstat (limited to 'net-libs/dleyna-renderer')
-rw-r--r--net-libs/dleyna-renderer/Manifest1
-rw-r--r--net-libs/dleyna-renderer/dleyna-renderer-0.6.0.ebuild45
-rw-r--r--net-libs/dleyna-renderer/files/0.6.0-gupnp-1.2.patch944
3 files changed, 990 insertions, 0 deletions
diff --git a/net-libs/dleyna-renderer/Manifest b/net-libs/dleyna-renderer/Manifest
index bfde6702fbc3..96110f36ad4e 100644
--- a/net-libs/dleyna-renderer/Manifest
+++ b/net-libs/dleyna-renderer/Manifest
@@ -1 +1,2 @@
DIST dleyna-renderer-0.5.0.tar.gz 378208 BLAKE2B 4a1b9e28310c2dd13baa9dccc1f35b2190b6af852da5a3a7ee77766affd7c52312e68b34a881652f3dbf6f71f0e25a69988ed2ff6ce897dc4f74c60793e06359 SHA512 28802057d6e0b7da24fd8abb11748faeb78ea6ef15ce85d83907497ff252c86cec1072a919e64bab6df0a375e0092c1281569802515fb87d9dc083098f1d4820
+DIST dleyna-renderer-0.6.0.tar.gz 65566 BLAKE2B cfd2b5a08b2e5c6e15006e722c4878281bf7f4b6d8a9bd317ec5b27fc1568a2ca891af2ac2fc3c230f65e9920bd524b4fbaf43631192b079b2aea051783d2b8a SHA512 f4cb6c658bfaf620962eb6b84ab6b41be1bb8455ac1ff358db7fcf3db7f5c4e2130bb7b0e5201005c738e892aabf25b3cf2a3e1cc1f4a9ef9d834789b87c0183
diff --git a/net-libs/dleyna-renderer/dleyna-renderer-0.6.0.ebuild b/net-libs/dleyna-renderer/dleyna-renderer-0.6.0.ebuild
new file mode 100644
index 000000000000..d8d20888cdb8
--- /dev/null
+++ b/net-libs/dleyna-renderer/dleyna-renderer-0.6.0.ebuild
@@ -0,0 +1,45 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=7
+
+inherit autotools
+
+DESCRIPTION="library implementing services that allow clients to discover and manipulate DLNA renderers"
+HOMEPAGE="https://01.org/dleyna/"
+SRC_URI="https://01.org/sites/default/files/downloads/dleyna/${P}.tar_2.gz -> ${P}.tar.gz"
+
+LICENSE="LGPL-2.1"
+SLOT="0"
+KEYWORDS="~amd64"
+IUSE=""
+
+DEPEND="
+ >=dev-libs/glib-2.28:2
+ >=media-libs/gupnp-dlna-0.9.4:2.0
+ >=net-libs/dleyna-core-0.6.0
+ >=net-libs/gssdp-1.2.0:0=
+ >=net-libs/gupnp-1.2.0:0=
+ >=net-libs/gupnp-av-0.11.5
+ >=net-libs/libsoup-2.28.2:2.4
+"
+RDEPEND="${DEPEND}
+ net-libs/dleyna-connector-dbus
+"
+BDEPEND="
+ virtual/pkgconfig
+"
+
+PATCHES=(
+ "${FILESDIR}"/${PV}-gupnp-1.2.patch
+)
+
+src_prepare() {
+ default
+ eautoreconf
+}
+
+src_install() {
+ default
+ find "${ED}" -name "*.la" -delete || die
+}
diff --git a/net-libs/dleyna-renderer/files/0.6.0-gupnp-1.2.patch b/net-libs/dleyna-renderer/files/0.6.0-gupnp-1.2.patch
new file mode 100644
index 000000000000..2fddca199444
--- /dev/null
+++ b/net-libs/dleyna-renderer/files/0.6.0-gupnp-1.2.patch
@@ -0,0 +1,944 @@
+https://github.com/intel/dleyna-renderer/pull/167
+
+From 594015eac2757f629a32d043c9a9b10ff6c5f95f Mon Sep 17 00:00:00 2001
+From: Jens Georg <mail@jensge.org>
+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 <mail@jensge.org>
+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 <libsoup/soup.h>
+ #include <libgupnp/gupnp-control-point.h>
++#include <libgupnp/gupnp-service-proxy.h>
+ #include <libgupnp-av/gupnp-av.h>
+
+ #include <libdleyna/core/core.h>
+ #include <libdleyna/core/error.h>
+ #include <libdleyna/core/log.h>
+-#include <libdleyna/core/service-task.h>
+
+ #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 <mail@jensge.org>
++ *
++ * 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 <libdleyna/core/task-processor.h>
++
++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 <mail@jensge.org>
++ *
++ * 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 <libdleyna/core/task-atom.h>
++
++#include <glib.h>
++#include <gio/gio.h>
++
++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 <libdleyna/core/error.h>
+ #include <libdleyna/core/log.h>
+-#include <libdleyna/core/service-task.h>
+ #include <libdleyna/core/white-list.h>
+
+ #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 <libdleyna/core/error.h>
+ #include <libdleyna/core/log.h>
+-#include <libdleyna/core/service-task.h>
+
+ #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 <mail@jensge.org>
+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 <mail@jensge.org>
+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 */