From 47cba68fe66983f50bf785a4e60ec77ac8535c2f Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Mon, 12 Nov 2018 00:55:01 -0800 Subject: binding: mediaplayer: add avrcp support Proxy avrcp metadata from bluetooth binding to mediaplayer service subscribers. Also allow transparent access to local and avrcp controls. Bug-AGL: SPEC-1630 Change-Id: I75cfd71ee62976a9312474b81469b8eb13a06015 Signed-off-by: Matt Ranostay --- binding/afm-common.c | 19 +++++- binding/afm-common.h | 3 +- binding/afm-mediaplayer-binding.c | 134 ++++++++++++++++++++++++++++++-------- conf.d/wgt/config-4a.xml.in | 1 + conf.d/wgt/config.xml.in | 1 + 5 files changed, 129 insertions(+), 29 deletions(-) diff --git a/binding/afm-common.c b/binding/afm-common.c index ec68b8d..93e6f54 100644 --- a/binding/afm-common.c +++ b/binding/afm-common.c @@ -19,7 +19,7 @@ #include "afm-common.h" -const char *control_commands[] = { +const char *gstreamer_control_commands[] = { "play", "pause", "previous", @@ -33,6 +33,21 @@ const char *control_commands[] = { "stop", }; +/* NULLs signal this functional isn't available */ +const char *avrcp_control_commands[] = { + "Play", + "Pause", + "Previous", + "Next", + NULL, + "FastForward", + "Rewind", + NULL, + NULL, + NULL, + "Stop", +}; + int get_command_index(const char *name) { int i; @@ -41,7 +56,7 @@ int get_command_index(const char *name) return -EINVAL; for (i = 0; i < NUM_CMDS; i++) { - if (!strcasecmp(control_commands[i], name)) + if (!strcasecmp(gstreamer_control_commands[i], name)) return i; } diff --git a/binding/afm-common.h b/binding/afm-common.h index 34258da..0439aa9 100644 --- a/binding/afm-common.h +++ b/binding/afm-common.h @@ -53,7 +53,8 @@ enum { NUM_CMDS }; -const char *control_commands[NUM_CMDS]; +const char *avrcp_control_commands[NUM_CMDS]; +const char *gstreamer_control_commands[NUM_CMDS]; int get_command_index(const char *name); GList *find_media_index(GList *list, long int index); void g_free_playlist_item(void *ptr); diff --git a/binding/afm-mediaplayer-binding.c b/binding/afm-mediaplayer-binding.c index d8efe37..6e85fa1 100644 --- a/binding/afm-mediaplayer-binding.c +++ b/binding/afm-mediaplayer-binding.c @@ -48,6 +48,9 @@ typedef struct _CustomData { long int volume; gint64 position; gint64 duration; + + /* avrcp */ + gboolean avrcp_connected; } CustomData; CustomData data = { @@ -328,34 +331,47 @@ static int seek_track(int cmd) return 0; } -/* @value can be one of the following values: - * play - go to playing transition - * pause - go to pause transition - * previous - skip to previous track - * next - skip to the next track - * seek - go to position (in milliseconds) - * - * fast-forward - skip forward in milliseconds - * rewind - skip backward in milliseconds - * - * pick-track - select track via index number - * volume - set volume between 0 - 100% - * loop - set looping of playlist (true or false) - */ +static void avrcp_controls(afb_req_t request) +{ + const char *value = afb_req_value(request, "value"); + const char *action = NULL; + afb_api_t api = afb_req_get_api(request); + int cmd = get_command_index(value), ret; + json_object *response, *jresp = NULL; -static void controls(afb_req_t request) + if (cmd < 0) { + afb_req_fail(request, "failed", "unknown command"); + return; + } + + action = avrcp_control_commands[cmd]; + if (!action) { + afb_req_fail(request, "failed", "command not supported"); + return; + } + + jresp = json_object_new_object(); + json_object_object_add(jresp, "action", json_object_new_string(action)); + + ret = afb_api_call_sync(api, "Bluetooth-Manager", + "avrcp_controls", jresp, &response, NULL, NULL); + json_object_put(response); + + if (ret < 0) { + afb_req_fail(request, "failed", "cannot request avrcp_control"); + return; + } + + afb_req_success(request, NULL, NULL); +} + +static void gstreamer_controls(afb_req_t request) { const char *value = afb_req_value(request, "value"); const char *position = afb_req_value(request, "position"); int cmd = get_command_index(value); json_object *jresp = NULL; - if (!value) { - afb_req_fail(request, "failed", "no value was passed"); - return; - } - - pthread_mutex_lock(&mutex); errno = 0; switch (cmd) { @@ -368,7 +384,6 @@ static void controls(afb_req_t request) if (current_track && current_track->data) set_media_uri(current_track->data); else { - pthread_mutex_unlock(&mutex); afb_req_fail(request, "failed", "No playlist"); return; } @@ -407,7 +422,6 @@ static void controls(afb_req_t request) if (idx == 0 && errno) { afb_req_fail(request, "failed", "invalid index"); - pthread_mutex_unlock(&mutex); return; } @@ -419,7 +433,6 @@ static void controls(afb_req_t request) current_track = list; } else { afb_req_fail(request, "failed", "couldn't find index"); - pthread_mutex_unlock(&mutex); return; } @@ -431,7 +444,6 @@ static void controls(afb_req_t request) if (volume == 0 && errno) { afb_req_fail(request, "failed", "invalid volume"); - pthread_mutex_unlock(&mutex); return; } @@ -458,11 +470,41 @@ static void controls(afb_req_t request) break; default: afb_req_fail(request, "failed", "unknown command"); - pthread_mutex_unlock(&mutex); return; } afb_req_success(request, jresp, NULL); +} + +/* @value can be one of the following values: + * play - go to playing transition + * pause - go to pause transition + * previous - skip to previous track + * next - skip to the next track + * seek - go to position (in milliseconds) + * + * fast-forward - skip forward in milliseconds + * rewind - skip backward in milliseconds + * + * pick-track - select track via index number + * volume - set volume between 0 - 100% + * loop - set looping of playlist (true or false) + */ + +static void controls(afb_req_t request) +{ + const char *value = afb_req_value(request, "value"); + + if (!value) { + afb_req_fail(request, "failed", "no value was passed"); + return; + } + + pthread_mutex_lock(&mutex); + if (data.avrcp_connected) + avrcp_controls(request); + else + gstreamer_controls(request); pthread_mutex_unlock(&mutex); } @@ -587,11 +629,31 @@ static void metadata(afb_req_t request) afb_req_success(request, jresp, "Metadata results"); } +static int bluetooth_subscribe(afb_api_t api) +{ + json_object *response, *query; + int ret; + + query = json_object_new_object(); + json_object_object_add(query, "value", json_object_new_string("media")); + + ret = afb_api_call_sync(api, "Bluetooth-Manager", "subscribe", query, &response, NULL, NULL); + json_object_put(response); + + if (ret < 0) { + AFB_ERROR("Cannot subscribe to Bluetooth media event"); + return ret; + } + + return 0; +} + static void subscribe(afb_req_t request) { const char *value = afb_req_value(request, "value"); if (!strcasecmp(value, "metadata")) { + afb_api_t api = afb_req_get_api(request); json_object *jresp = NULL; afb_req_subscribe(request, metadata_event); @@ -603,6 +665,8 @@ static void subscribe(afb_req_t request) afb_event_push(metadata_event, jresp); + bluetooth_subscribe(api); + return; } else if (!strcasecmp(value, "playlist")) { json_object *jresp = json_object_new_object(); @@ -860,6 +924,24 @@ static void onevent(afb_api_t api, const char *event, struct json_object *object if (current_track == NULL) current_track = g_list_first(playlist); + } else if (!g_ascii_strcasecmp(event, "Bluetooth-Manager/media")) { + json_object *val; + + pthread_mutex_lock(&mutex); + + if (json_object_object_get_ex(object, "connected", &val)) { + gboolean state = json_object_get_boolean(val); + data.avrcp_connected = state; + + if (state) + gst_element_set_state(data.playbin, GST_STATE_PAUSED); + } + + pthread_mutex_unlock(&mutex); + + afb_event_push(metadata_event, object); + + return; } else { AFB_ERROR("Invalid event: %s", event); return; diff --git a/conf.d/wgt/config-4a.xml.in b/conf.d/wgt/config-4a.xml.in index 9393652..daf5acf 100644 --- a/conf.d/wgt/config-4a.xml.in +++ b/conf.d/wgt/config-4a.xml.in @@ -19,6 +19,7 @@ + diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in index f4ffa76..3e8447a 100644 --- a/conf.d/wgt/config.xml.in +++ b/conf.d/wgt/config.xml.in @@ -18,6 +18,7 @@ + -- cgit 1.2.3-korg