diff options
author | Christophe Guiraud <christophe.guiraud@intel.com> | 2013-03-29 15:14:13 +0100 |
---|---|---|
committer | Christophe Guiraud <christophe.guiraud@intel.com> | 2013-04-05 18:15:49 +0200 |
commit | e5dea5f6d883da1e09faab18be95ca64e10d6d0d (patch) | |
tree | 2d4bec663537a80d52b4f4639d064bc51bcb6bcc | |
parent | 1fbb7baa1066720fb9782900bf70c0de55e0f944 (diff) | |
download | dleyna-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.c | 314 | ||||
-rw-r--r-- | libdleyna/renderer/device.h | 4 |
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( |