summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Guiraud <christophe.guiraud@intel.com>2013-03-29 15:14:13 +0100
committerChristophe Guiraud <christophe.guiraud@intel.com>2013-04-05 18:15:49 +0200
commite5dea5f6d883da1e09faab18be95ca64e10d6d0d (patch)
tree2d4bec663537a80d52b4f4639d064bc51bcb6bcc
parent1fbb7baa1066720fb9782900bf70c0de55e0f944 (diff)
downloaddleyna-renderer-e5dea5f6d883da1e09faab18be95ca64e10d6d0d.tar.gz
[Device] Add X_DLNA_PS transport action support
- Add the X_DLNA_PS transport action support to allow the playspeeds provided by the renderer once we have provided it a URI (SetAVTransportURI()) in addition to the renderer default playspeeds retrieved via the introspection. MINIMUM_RATE, MAXIMUM_RATE and TRANSPORT_PLAY_SPEEDS properties will be upddated. when SetAVTransportURI() action is called, MINIMUM_RATE, MAXIMUM_RATE and TRANSPORT_PLAY_SPEEDS properties will be reset with the renderer default playspeeds. - The renderer default playspeeds are stored in the dlr_device_t_ structure to be re-used without having to be re-computed. - When the application set the property RATE, the new rate value is checked with either the renderer default allowed playspeeds or the X_DLNA_PS allowed playspeeds depending on the availability of the X_DLNA_PS. - We now emit a property changed signal when application set the DLR_INTERFACE_PROP_MINIMUM_RATE property with a value not equal to the current one. - For the parsing of the action string containing the X_DLNA_PS content, thanks to regexp API, the sequence "\," are replaced by "*" ("\," is used to separated the X_DLNA_PS speeds), then the action string is splitted by "," to separate each actions token. For getting the X_DLNA_PS playspeeds we then just have to split the string with "*" action string = Play,Stop,Seek,X_DLNA_PS=-8\,2\,4\,8 Signed-off-by: Christophe Guiraud <christophe.guiraud@intel.com>
-rw-r--r--libdleyna/renderer/device.c314
-rw-r--r--libdleyna/renderer/device.h4
2 files changed, 266 insertions, 52 deletions
diff --git a/libdleyna/renderer/device.c b/libdleyna/renderer/device.c
index 5d9e649..8c44cff 100644
--- a/libdleyna/renderer/device.c
+++ b/libdleyna/renderer/device.c
@@ -67,6 +67,18 @@ static void prv_rc_last_change_cb(GUPnPServiceProxy *proxy,
static void prv_props_update(dlr_device_t *device, dlr_task_t *task);
+static void prv_get_rates_values(GList *allowed_tp_speeds,
+ GVariant **mpris_tp_speeds,
+ GPtrArray **upnp_tp_speeds,
+ double *min_rate, double *max_rate);
+
+static void prv_add_player_speed_props(GHashTable *player_props,
+ double min_rate, double max_rate,
+ GVariant *mpris_transport_play_speeds,
+ GVariantBuilder *changed_props_vb);
+
+static gint prv_compare_rationals(const gchar *a, const gchar *b);
+
static void prv_unref_variant(gpointer variant)
{
GVariant *var = variant;
@@ -369,6 +381,10 @@ void dlr_device_delete(void *device)
if (dev->transport_play_speeds != NULL)
g_ptr_array_free(dev->transport_play_speeds, TRUE);
+ if (dev->dlna_transport_play_speeds != NULL)
+ g_ptr_array_free(dev->dlna_transport_play_speeds, TRUE);
+ if (dev->mpris_transport_play_speeds)
+ g_variant_unref(dev->mpris_transport_play_speeds);
g_free(dev->rate);
g_free(dev);
}
@@ -970,6 +986,52 @@ on_error:
return retval;
}
+static gint compare_speeds(gconstpointer a, gconstpointer b)
+{
+ return prv_compare_rationals((const gchar *)a, (const gchar *)b);
+}
+
+static void prv_add_dlna_speeds(dlr_device_t *device,
+ gchar **dlna_speeds,
+ GVariantBuilder *changed_props_vb)
+{
+ GList *allowed_tp_speeds = NULL;
+ double min_rate = 0;
+ double max_rate = 0;
+ GVariant *mpris_tp_speeds = NULL;
+ unsigned int i = 0;
+ gchar *speed;
+
+ if (dlna_speeds == NULL)
+ goto exit;
+
+ allowed_tp_speeds = g_list_prepend(allowed_tp_speeds, g_strdup("1"));
+
+ while (dlna_speeds[i]) {
+ speed = g_strstrip(g_strdup(dlna_speeds[i]));
+ allowed_tp_speeds = g_list_prepend(allowed_tp_speeds, speed);
+ ++i;
+ }
+
+ allowed_tp_speeds = g_list_sort(allowed_tp_speeds, compare_speeds);
+
+ prv_get_rates_values(allowed_tp_speeds,
+ &mpris_tp_speeds,
+ &device->dlna_transport_play_speeds,
+ &min_rate, &max_rate);
+
+ prv_add_player_speed_props(device->props.player_props,
+ min_rate, max_rate,
+ mpris_tp_speeds,
+ changed_props_vb);
+
+exit:
+ if (allowed_tp_speeds != NULL)
+ g_list_free_full(allowed_tp_speeds, g_free);
+
+ return;
+}
+
static void prv_add_actions(dlr_device_t *device,
const gchar *actions,
GVariantBuilder *changed_props_vb)
@@ -984,8 +1046,15 @@ static void prv_add_actions(dlr_device_t *device,
gboolean next = FALSE;
gboolean previous = FALSE;
GVariant *val;
+ GRegex *regex;
+ gchar *tmp_str;
+ gchar **speeds;
- parts = g_strsplit(actions, ",", 0);
+ regex = g_regex_new("\\\\,", 0, 0, NULL);
+ tmp_str = g_regex_replace_literal(regex, actions, -1, 0, "*", 0, NULL);
+ parts = g_strsplit(tmp_str, ",", 0);
+ g_free(tmp_str);
+ g_regex_unref(regex);
true_val = g_variant_ref_sink(g_variant_new_boolean(TRUE));
false_val = g_variant_ref_sink(g_variant_new_boolean(FALSE));
@@ -993,16 +1062,23 @@ static void prv_add_actions(dlr_device_t *device,
while (parts[i]) {
g_strstrip(parts[i]);
- if (!strcmp(parts[i], "Play"))
+ if (!strcmp(parts[i], "Play")) {
play = TRUE;
- else if (!strcmp(parts[i], "Pause"))
+ } else if (!strcmp(parts[i], "Pause")) {
ppause = TRUE;
- else if (!strcmp(parts[i], "Seek"))
+ } else if (!strcmp(parts[i], "Seek")) {
seek = TRUE;
- else if (!strcmp(parts[i], "Next"))
+ } else if (!strcmp(parts[i], "Next")) {
next = TRUE;
- else if (!strcmp(parts[i], "Previous"))
+ } else if (!strcmp(parts[i], "Previous")) {
previous = TRUE;
+ } else if (!strncmp(parts[i], "X_DLNA_PS=",
+ strlen("X_DLNA_PS="))) {
+ speeds = g_strsplit(parts[i] + strlen("X_DLNA_PS="),
+ "*", 0);
+ prv_add_dlna_speeds(device, speeds, changed_props_vb);
+ g_strfreev(speeds);
+ }
++i;
}
@@ -1604,7 +1680,7 @@ static gint prv_compare_rationals(const gchar *a, const gchar *b)
return (a_numerator * b_denominator) - (b_numerator * a_denominator);
}
-static void prv_get_rates_values(const GUPnPServiceStateVariableInfo *svi,
+static void prv_get_rates_values(GList *allowed_tp_speeds,
GVariant **mpris_tp_speeds,
GPtrArray **upnp_tp_speeds,
double *min_rate, double *max_rate)
@@ -1616,12 +1692,12 @@ static void prv_get_rates_values(const GUPnPServiceStateVariableInfo *svi,
GVariantBuilder vb;
const double precision = 0.01;
- if ((svi == NULL) || (svi->allowed_values == NULL))
+ if (allowed_tp_speeds == NULL)
goto exit;
g_variant_builder_init(&vb, G_VARIANT_TYPE("ad"));
- list = svi->allowed_values;
+ list = allowed_tp_speeds;
min_rate_str = list->data;
max_rate_str = min_rate_str;
@@ -1666,6 +1742,8 @@ static void prv_get_av_service_states_values(GUPnPServiceProxy *av_proxy,
const GUPnPServiceStateVariableInfo *svi;
GUPnPServiceIntrospection *introspection;
GError *error = NULL;
+ GVariant *speeds = NULL;
+ GList *allowed_values;
introspection = gupnp_service_info_get_introspection(
GUPNP_SERVICE_INFO(av_proxy),
@@ -1683,9 +1761,16 @@ static void prv_get_av_service_states_values(GUPnPServiceProxy *av_proxy,
introspection,
"TransportPlaySpeed");
- prv_get_rates_values(svi,
- mpris_tp_speeds, upnp_tp_speeds,
- min_rate, max_rate);
+ 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);
+ }
g_object_unref(introspection);
@@ -1786,6 +1871,35 @@ static void prv_update_device_props(GUPnPDeviceInfo *proxy, GHashTable *props)
}
+static void prv_add_player_speed_props(GHashTable *player_props,
+ double min_rate, double max_rate,
+ GVariant *mpris_transport_play_speeds,
+ GVariantBuilder *changed_props_vb)
+{
+ GVariant *val;
+
+ if (min_rate != 0) {
+ val = g_variant_ref_sink(g_variant_new_double(min_rate));
+ prv_change_props(player_props,
+ DLR_INTERFACE_PROP_MINIMUM_RATE,
+ val, changed_props_vb);
+ }
+
+ if (max_rate != 0) {
+ val = g_variant_ref_sink(g_variant_new_double(max_rate));
+ prv_change_props(player_props,
+ DLR_INTERFACE_PROP_MAXIMUM_RATE,
+ val, changed_props_vb);
+ }
+
+ if (mpris_transport_play_speeds != NULL) {
+ val = g_variant_ref_sink(mpris_transport_play_speeds);
+ prv_change_props(player_props,
+ DLR_INTERFACE_PROP_TRANSPORT_PLAY_SPEEDS,
+ val, changed_props_vb);
+ }
+}
+
static void prv_props_update(dlr_device_t *device, dlr_task_t *task)
{
GVariant *val;
@@ -1795,9 +1909,6 @@ static void prv_props_update(dlr_device_t *device, dlr_task_t *task)
dlr_props_t *props = &device->props;
GVariantBuilder *changed_props_vb;
GVariant *changed_props;
- double min_rate = 0;
- double max_rate = 0;
- GVariant *mpris_transport_play_speeds = NULL;
context = dlr_device_get_context(device);
@@ -1830,36 +1941,21 @@ static void prv_props_update(dlr_device_t *device, dlr_task_t *task)
service_proxies = &context->service_proxies;
if (service_proxies->av_proxy)
- prv_get_av_service_states_values(service_proxies->av_proxy,
- &mpris_transport_play_speeds,
- &device->transport_play_speeds,
- &min_rate,
- &max_rate);
+ 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);
if (service_proxies->rc_proxy)
prv_get_rc_service_states_values(service_proxies->rc_proxy,
&device->max_volume);
- if (min_rate != 0) {
- val = g_variant_ref_sink(g_variant_new_double(min_rate));
- prv_change_props(device->props.player_props,
- DLR_INTERFACE_PROP_MINIMUM_RATE, val,
- changed_props_vb);
- }
-
- if (max_rate != 0) {
- val = g_variant_ref_sink(g_variant_new_double(max_rate));
- prv_change_props(device->props.player_props,
- DLR_INTERFACE_PROP_MAXIMUM_RATE,
- val, changed_props_vb);
- }
-
- if (mpris_transport_play_speeds != NULL) {
- val = g_variant_ref_sink(mpris_transport_play_speeds);
- prv_change_props(device->props.player_props,
- DLR_INTERFACE_PROP_TRANSPORT_PLAY_SPEEDS,
- val, changed_props_vb);
- }
+ prv_add_player_speed_props(device->props.player_props,
+ device->min_rate, device->max_rate,
+ device->mpris_transport_play_speeds,
+ changed_props_vb);
prv_add_all_actions(device, changed_props_vb);
device->props.synced = TRUE;
@@ -1950,28 +2046,36 @@ static GVariant *prv_get_rate_value_from_double(GVariant *params,
gchar **upnp_rate,
dlr_async_task_t *cb_data)
{
+ dlr_device_t *dev = cb_data->device;
GVariant *val = NULL;
GVariant *tps;
GVariantIter iter;
double tps_value;
double mpris_rate;
- GPtrArray *upnp_tp_speeds;
+ GPtrArray *tp_speeds;
int i;
- tps = g_hash_table_lookup(cb_data->device->props.player_props,
- DLR_INTERFACE_PROP_TRANSPORT_PLAY_SPEEDS);
+ if (dev->dlna_transport_play_speeds != NULL) {
+ tps = g_hash_table_lookup(dev->props.player_props,
+ DLR_INTERFACE_PROP_TRANSPORT_PLAY_SPEEDS);
+
+ tp_speeds = dev->dlna_transport_play_speeds;
+ } else {
+ tps = dev->mpris_transport_play_speeds;
+
+ tp_speeds = dev->transport_play_speeds;
+ }
if (tps == NULL) {
- cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
- DLEYNA_ERROR_OPERATION_FAILED,
- "TransportPlaySpeeds list is empty");
+ cb_data->error =
+ g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "TransportPlaySpeeds list is empty");
goto exit;
}
mpris_rate = g_variant_get_double(params);
- upnp_tp_speeds = cb_data->device->transport_play_speeds;
-
i = 0;
g_variant_iter_init(&iter, tps);
@@ -1981,7 +2085,7 @@ static GVariant *prv_get_rate_value_from_double(GVariant *params,
val = g_variant_ref_sink(
g_variant_new_double(tps_value));
- *upnp_rate = g_ptr_array_index(upnp_tp_speeds, i);
+ *upnp_rate = g_ptr_array_index(tp_speeds, i);
break;
}
@@ -2002,6 +2106,8 @@ exit:
static void prv_set_rate(GVariant *params, dlr_async_task_t *cb_data)
{
+ GVariantBuilder *changed_props_vb;
+ GVariant *changed_props;
GVariant *val;
gchar *rate;
@@ -2018,14 +2124,29 @@ static void prv_set_rate(GVariant *params, dlr_async_task_t *cb_data)
if (val == NULL)
goto exit;
+ DLEYNA_LOG_INFO("Set device rate to %s", rate);
+
+ if (!strcmp(cb_data->device->rate, rate)) {
+ g_variant_unref(val);
+
+ goto exit;
+ }
+
g_free(cb_data->device->rate);
cb_data->device->rate = g_strdup(rate);
- DLEYNA_LOG_INFO("Set device rate to %s", cb_data->device->rate);
+ changed_props_vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
prv_change_props(cb_data->device->props.player_props,
- DLR_INTERFACE_PROP_RATE, val, NULL);
+ DLR_INTERFACE_PROP_RATE, val, changed_props_vb);
+ changed_props = g_variant_ref_sink(
+ g_variant_builder_end(changed_props_vb));
+ prv_emit_signal_properties_changed(cb_data->device,
+ DLR_INTERFACE_PLAYER,
+ changed_props);
+ g_variant_unref(changed_props);
+ g_variant_builder_unref(changed_props_vb);
exit:
return;
@@ -2260,6 +2381,95 @@ void dlr_device_previous(dlr_device_t *device, dlr_task_t *task,
prv_simple_command(device, task, "Previous", cb);
}
+static void prv_reset_transport_speed_props(dlr_device_t *device)
+{
+ GVariantBuilder *changed_props_vb;
+ GVariant *changed_props;
+ GVariant *val;
+ double min_rate;
+ double max_rate;
+ gboolean props_changed = FALSE;
+
+ changed_props_vb = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+ if (device->dlna_transport_play_speeds != NULL) {
+ g_ptr_array_free(device->dlna_transport_play_speeds, TRUE);
+ device->dlna_transport_play_speeds = NULL;
+ }
+
+ val = g_hash_table_lookup(device->props.player_props,
+ DLR_INTERFACE_PROP_TRANSPORT_PLAY_SPEEDS);
+ if (!val ||
+ !g_variant_equal(val, device->mpris_transport_play_speeds)) {
+ min_rate = 0;
+ val = g_hash_table_lookup(device->props.player_props,
+ DLR_INTERFACE_PROP_MINIMUM_RATE);
+ if (!val || (g_variant_get_double(val) != device->min_rate))
+ min_rate = device->min_rate;
+
+ max_rate = 0;
+ val = g_hash_table_lookup(device->props.player_props,
+ DLR_INTERFACE_PROP_MAXIMUM_RATE);
+ if (!val || (g_variant_get_double(val) != device->max_rate))
+ max_rate = device->max_rate;
+
+ prv_add_player_speed_props(device->props.player_props,
+ min_rate, max_rate,
+ device->mpris_transport_play_speeds,
+ changed_props_vb);
+
+ props_changed = TRUE;
+ }
+
+ if (!device->rate || g_strcmp0(device->rate, "1") != 0) {
+ g_free(device->rate);
+ device->rate = g_strdup("1");
+
+ val = g_variant_ref_sink(g_variant_new_double(
+ prv_map_transport_speed(device->rate)));
+ prv_change_props(device->props.player_props,
+ DLR_INTERFACE_PROP_RATE, val,
+ changed_props_vb);
+
+ props_changed = TRUE;
+ }
+
+ changed_props = g_variant_ref_sink(
+ g_variant_builder_end(changed_props_vb));
+ if (props_changed)
+ prv_emit_signal_properties_changed(device,
+ DLR_INTERFACE_PLAYER,
+ changed_props);
+ g_variant_unref(changed_props);
+ g_variant_builder_unref(changed_props_vb);
+}
+
+static void prv_open_uri_cb(GUPnPServiceProxy *proxy,
+ GUPnPServiceProxyAction *action,
+ gpointer user_data)
+{
+ dlr_async_task_t *cb_data = user_data;
+ GError *upnp_error = NULL;
+
+ if (!gupnp_service_proxy_end_action(cb_data->proxy, cb_data->action,
+ &upnp_error, NULL)) {
+ cb_data->error = g_error_new(DLEYNA_SERVER_ERROR,
+ DLEYNA_ERROR_OPERATION_FAILED,
+ "Operation failed: %s",
+ upnp_error->message);
+ g_error_free(upnp_error);
+
+ goto exit;
+ }
+
+ prv_reset_transport_speed_props(cb_data->device);
+
+exit:
+
+ (void) g_idle_add(dlr_async_task_complete, cb_data);
+ g_cancellable_disconnect(cb_data->cancellable, cb_data->cancel_id);
+}
+
void dlr_device_open_uri(dlr_device_t *device, dlr_task_t *task,
dlr_upnp_task_complete_t cb)
{
@@ -2287,7 +2497,7 @@ void dlr_device_open_uri(dlr_device_t *device, dlr_task_t *task,
cb_data->action =
gupnp_service_proxy_begin_action(cb_data->proxy,
"SetAVTransportURI",
- prv_simple_call_cb,
+ prv_open_uri_cb,
cb_data,
"InstanceID", G_TYPE_INT, 0,
"CurrentURI", G_TYPE_STRING,
diff --git a/libdleyna/renderer/device.h b/libdleyna/renderer/device.h
index a0c1bf0..0e43884 100644
--- a/libdleyna/renderer/device.h
+++ b/libdleyna/renderer/device.h
@@ -73,7 +73,11 @@ struct dlr_device_t_ {
guint timeout_id;
guint max_volume;
GPtrArray *transport_play_speeds;
+ GPtrArray *dlna_transport_play_speeds;
+ GVariant *mpris_transport_play_speeds;
gchar *rate;
+ double min_rate;
+ double max_rate;
};
dlr_device_t *dlr_device_new(