avdtpsrc: Add support for AVRCP metadata
Metadata from AVRCP is emitted as tags, and the duration from AVRCP is used in queries by avdtpsrc.
This commit is contained in:
parent
6e749f17da
commit
967046d3a5
@ -5,7 +5,8 @@ libgstbluez_la_SOURCES = \
|
|||||||
gsta2dpsink.c \
|
gsta2dpsink.c \
|
||||||
gstavdtpsink.c \
|
gstavdtpsink.c \
|
||||||
gstavdtpsrc.c \
|
gstavdtpsrc.c \
|
||||||
gstavdtputil.c
|
gstavdtputil.c \
|
||||||
|
gstavrcputil.c
|
||||||
|
|
||||||
nodist_libgstbluez_la_SOURCES = \
|
nodist_libgstbluez_la_SOURCES = \
|
||||||
$(BUILT_SOURCES)
|
$(BUILT_SOURCES)
|
||||||
|
@ -68,6 +68,8 @@ static void gst_avdtp_src_set_property (GObject * object, guint prop_id,
|
|||||||
const GValue * value, GParamSpec * pspec);
|
const GValue * value, GParamSpec * pspec);
|
||||||
|
|
||||||
static GstCaps *gst_avdtp_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter);
|
static GstCaps *gst_avdtp_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter);
|
||||||
|
static gboolean gst_avdtp_src_query (GstPad * pad, GstObject * parent,
|
||||||
|
GstQuery * query);
|
||||||
static gboolean gst_avdtp_src_start (GstBaseSrc * bsrc);
|
static gboolean gst_avdtp_src_start (GstBaseSrc * bsrc);
|
||||||
static gboolean gst_avdtp_src_stop (GstBaseSrc * bsrc);
|
static gboolean gst_avdtp_src_stop (GstBaseSrc * bsrc);
|
||||||
static GstFlowReturn gst_avdtp_src_create (GstBaseSrc * bsrc, guint64 offset,
|
static GstFlowReturn gst_avdtp_src_create (GstBaseSrc * bsrc, guint64 offset,
|
||||||
@ -117,9 +119,14 @@ gst_avdtp_src_init (GstAvdtpSrc * avdtpsrc)
|
|||||||
{
|
{
|
||||||
avdtpsrc->poll = gst_poll_new (TRUE);
|
avdtpsrc->poll = gst_poll_new (TRUE);
|
||||||
|
|
||||||
|
avdtpsrc->duration = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
gst_base_src_set_format (GST_BASE_SRC (avdtpsrc), GST_FORMAT_TIME);
|
gst_base_src_set_format (GST_BASE_SRC (avdtpsrc), GST_FORMAT_TIME);
|
||||||
gst_base_src_set_live (GST_BASE_SRC (avdtpsrc), TRUE);
|
gst_base_src_set_live (GST_BASE_SRC (avdtpsrc), TRUE);
|
||||||
gst_base_src_set_do_timestamp (GST_BASE_SRC (avdtpsrc), TRUE);
|
gst_base_src_set_do_timestamp (GST_BASE_SRC (avdtpsrc), TRUE);
|
||||||
|
|
||||||
|
gst_pad_set_query_function (GST_BASE_SRC_PAD (avdtpsrc),
|
||||||
|
GST_DEBUG_FUNCPTR (gst_avdtp_src_query));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -169,6 +176,35 @@ gst_avdtp_src_set_property (GObject * object, guint prop_id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_avdtp_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
|
||||||
|
{
|
||||||
|
GstAvdtpSrc *avdtpsrc = GST_AVDTP_SRC (gst_pad_get_parent_element (pad));
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
switch (GST_QUERY_TYPE (query)) {
|
||||||
|
case GST_QUERY_DURATION:{
|
||||||
|
GstFormat format;
|
||||||
|
|
||||||
|
if (avdtpsrc->duration != GST_CLOCK_TIME_NONE) {
|
||||||
|
gst_query_parse_duration (query, &format, NULL);
|
||||||
|
|
||||||
|
if (format == GST_FORMAT_TIME) {
|
||||||
|
gst_query_set_duration (query, format, (gint64) avdtpsrc->duration);
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = gst_pad_query_default (pad, parent, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static GstCaps *
|
static GstCaps *
|
||||||
gst_avdtp_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
|
gst_avdtp_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
|
||||||
{
|
{
|
||||||
@ -247,6 +283,54 @@ gst_avdtp_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
avrcp_metadata_cb (GstAvrcpConnection * avrcp, GstTagList * taglist,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GstAvdtpSrc *src = GST_AVDTP_SRC (user_data);
|
||||||
|
guint64 duration;
|
||||||
|
|
||||||
|
if (gst_tag_list_get_uint64 (taglist, GST_TAG_DURATION, &duration)) {
|
||||||
|
src->duration = duration;
|
||||||
|
gst_element_post_message (GST_ELEMENT (src),
|
||||||
|
gst_message_new_duration_changed (GST_OBJECT (src)));
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_pad_push_event (GST_BASE_SRC_PAD (src),
|
||||||
|
gst_event_new_tag (gst_tag_list_copy (taglist)));
|
||||||
|
gst_element_post_message (GST_ELEMENT (src),
|
||||||
|
gst_message_new_tag (GST_OBJECT (src), taglist));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_avdtp_src_start_avrcp (GstAvdtpSrc * src)
|
||||||
|
{
|
||||||
|
gchar *path, **strv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Strip out the /fdX in /org/bluez/dev_.../fdX */
|
||||||
|
strv = g_strsplit (src->conn.transport, "/", -1);
|
||||||
|
|
||||||
|
for (i = 0; strv[i]; i++);
|
||||||
|
g_return_if_fail (i > 0);
|
||||||
|
|
||||||
|
g_free (strv[i - 1]);
|
||||||
|
strv[i - 1] = NULL;
|
||||||
|
|
||||||
|
path = g_strjoinv ("/", strv);
|
||||||
|
g_strfreev (strv);
|
||||||
|
|
||||||
|
src->avrcp = gst_avrcp_connection_new (path, avrcp_metadata_cb, src, NULL);
|
||||||
|
|
||||||
|
g_free (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_avdtp_src_stop_avrcp (GstAvdtpSrc * src)
|
||||||
|
{
|
||||||
|
gst_avrcp_connection_free (src->avrcp);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_avdtp_src_start (GstBaseSrc * bsrc)
|
gst_avdtp_src_start (GstBaseSrc * bsrc)
|
||||||
{
|
{
|
||||||
@ -291,6 +375,8 @@ gst_avdtp_src_start (GstBaseSrc * bsrc)
|
|||||||
|
|
||||||
g_atomic_int_set (&avdtpsrc->unlocked, FALSE);
|
g_atomic_int_set (&avdtpsrc->unlocked, FALSE);
|
||||||
|
|
||||||
|
gst_avdtp_src_start_avrcp (avdtpsrc);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -306,6 +392,7 @@ gst_avdtp_src_stop (GstBaseSrc * bsrc)
|
|||||||
gst_poll_remove_fd (avdtpsrc->poll, &avdtpsrc->pfd);
|
gst_poll_remove_fd (avdtpsrc->poll, &avdtpsrc->pfd);
|
||||||
gst_poll_set_flushing (avdtpsrc->poll, TRUE);
|
gst_poll_set_flushing (avdtpsrc->poll, TRUE);
|
||||||
|
|
||||||
|
gst_avdtp_src_stop_avrcp (avdtpsrc);
|
||||||
gst_avdtp_connection_release (&avdtpsrc->conn);
|
gst_avdtp_connection_release (&avdtpsrc->conn);
|
||||||
|
|
||||||
if (avdtpsrc->dev_caps) {
|
if (avdtpsrc->dev_caps) {
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/base/gstbasesrc.h>
|
#include <gst/base/gstbasesrc.h>
|
||||||
#include "gstavdtputil.h"
|
#include "gstavdtputil.h"
|
||||||
|
#include "gstavrcputil.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
#define GST_TYPE_AVDTP_SRC \
|
#define GST_TYPE_AVDTP_SRC \
|
||||||
@ -54,9 +55,13 @@ struct _GstAvdtpSrc
|
|||||||
GstAvdtpConnection conn;
|
GstAvdtpConnection conn;
|
||||||
GstCaps *dev_caps;
|
GstCaps *dev_caps;
|
||||||
|
|
||||||
|
GstAvrcpConnection *avrcp;
|
||||||
|
|
||||||
GstPoll *poll;
|
GstPoll *poll;
|
||||||
GstPollFD pfd;
|
GstPollFD pfd;
|
||||||
volatile gint unlocked;
|
volatile gint unlocked;
|
||||||
|
|
||||||
|
GstClockTime duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_avdtp_src_get_type (void);
|
GType gst_avdtp_src_get_type (void);
|
||||||
|
333
sys/bluez/gstavrcputil.c
Normal file
333
sys/bluez/gstavrcputil.c
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Arun Raghavan <git@arunraghavan.net>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that 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 library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gstavrcputil.h"
|
||||||
|
#include "bluez.h"
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define BLUEZ_NAME "org.bluez"
|
||||||
|
#define BLUEZ_PATH "/"
|
||||||
|
#define BLUEZ_MEDIA_PLAYER_IFACE BLUEZ_NAME ".MediaPlayer1"
|
||||||
|
|
||||||
|
struct _GstAvrcpConnection
|
||||||
|
{
|
||||||
|
GMainContext *context;
|
||||||
|
GMainLoop *mainloop;
|
||||||
|
GThread *thread;
|
||||||
|
|
||||||
|
gchar *dev_path;
|
||||||
|
GDBusObjectManager *manager;
|
||||||
|
BluezMediaPlayer1 *player;
|
||||||
|
|
||||||
|
GstAvrcpMetadataCb cb;
|
||||||
|
gpointer user_data;
|
||||||
|
GDestroyNotify user_data_free_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
tag_from_property (const char *name)
|
||||||
|
{
|
||||||
|
if (g_str_equal (name, "Title"))
|
||||||
|
return GST_TAG_TITLE;
|
||||||
|
else if (g_str_equal (name, "Artist"))
|
||||||
|
return GST_TAG_ARTIST;
|
||||||
|
else if (g_str_equal (name, "Album"))
|
||||||
|
return GST_TAG_ALBUM;
|
||||||
|
else if (g_str_equal (name, "Genre"))
|
||||||
|
return GST_TAG_GENRE;
|
||||||
|
else if (g_str_equal (name, "NumberOfTracks"))
|
||||||
|
return GST_TAG_TRACK_COUNT;
|
||||||
|
else if (g_str_equal (name, "TrackNumber"))
|
||||||
|
return GST_TAG_TRACK_NUMBER;
|
||||||
|
else if (g_str_equal (name, "Duration"))
|
||||||
|
return GST_TAG_DURATION;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstTagList *
|
||||||
|
tag_list_from_variant (GVariant * properties, gboolean track)
|
||||||
|
{
|
||||||
|
const gchar *name, *s;
|
||||||
|
GVariant *value;
|
||||||
|
GVariantIter *iter;
|
||||||
|
GstTagList *taglist = NULL;
|
||||||
|
|
||||||
|
iter = g_variant_iter_new (properties);
|
||||||
|
|
||||||
|
if (track)
|
||||||
|
taglist = gst_tag_list_new_empty ();
|
||||||
|
|
||||||
|
/* The properties are in two levels -- at the top level we have the position
|
||||||
|
* and the 'track'. The 'track' is another level of {sv} so we recurse one
|
||||||
|
* level to pick up the actual track data. We get the taglist from the
|
||||||
|
* recursive call, and ignore the position for now. */
|
||||||
|
|
||||||
|
while (g_variant_iter_next (iter, "{&sv}", &name, &value)) {
|
||||||
|
if (!track && g_str_equal (name, "Track")) {
|
||||||
|
/* Top level property */
|
||||||
|
taglist = tag_list_from_variant (value, TRUE);
|
||||||
|
|
||||||
|
} else if (track) {
|
||||||
|
/* If we get here, we are in the recursive call and we're dealing with
|
||||||
|
* properties under "Track" */
|
||||||
|
GType type;
|
||||||
|
const gchar *tag;
|
||||||
|
guint i;
|
||||||
|
guint64 i64;
|
||||||
|
|
||||||
|
tag = tag_from_property (name);
|
||||||
|
if (!tag)
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
type = gst_tag_get_type (tag);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case G_TYPE_STRING:
|
||||||
|
s = g_variant_get_string (value, NULL);
|
||||||
|
if (s && s[0] != '\0')
|
||||||
|
gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, s, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case G_TYPE_UINT:
|
||||||
|
i = g_variant_get_uint32 (value);
|
||||||
|
if (i > 0)
|
||||||
|
gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag, i, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case G_TYPE_UINT64:
|
||||||
|
/* If we're here, the tag is 'duration' */
|
||||||
|
i64 = g_variant_get_uint32 (value);
|
||||||
|
if (i64 > 0 && i64 != (guint32) (-1)) {
|
||||||
|
gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, tag,
|
||||||
|
i64 * GST_MSECOND, NULL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
GST_WARNING ("Unknown property: %s", name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
g_variant_unref (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_variant_iter_free (iter);
|
||||||
|
|
||||||
|
if (taglist && gst_tag_list_is_empty (taglist)) {
|
||||||
|
gst_tag_list_unref (taglist);
|
||||||
|
taglist = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return taglist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
player_property_changed_cb (GDBusProxy * proxy, GVariant * properties,
|
||||||
|
GStrv invalid, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstAvrcpConnection *avrcp = (GstAvrcpConnection *) user_data;
|
||||||
|
GstTagList *taglist;
|
||||||
|
|
||||||
|
taglist = tag_list_from_variant (properties, FALSE);
|
||||||
|
|
||||||
|
if (taglist)
|
||||||
|
avrcp->cb (avrcp, taglist, avrcp->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstTagList *
|
||||||
|
player_get_taglist (BluezMediaPlayer1 * player)
|
||||||
|
{
|
||||||
|
GstTagList *taglist = NULL;
|
||||||
|
GVariant *track;
|
||||||
|
|
||||||
|
track = bluez_media_player1_get_track (player);
|
||||||
|
if (track)
|
||||||
|
taglist = tag_list_from_variant (track, TRUE);
|
||||||
|
|
||||||
|
return taglist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_avrcp_connection_set_player (GstAvrcpConnection * avrcp,
|
||||||
|
BluezMediaPlayer1 * player)
|
||||||
|
{
|
||||||
|
GstTagList *taglist;
|
||||||
|
|
||||||
|
if (avrcp->player)
|
||||||
|
g_object_unref (avrcp->player);
|
||||||
|
|
||||||
|
if (!player) {
|
||||||
|
avrcp->player = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
avrcp->player = g_object_ref (player);
|
||||||
|
|
||||||
|
g_signal_connect (player, "g-properties-changed",
|
||||||
|
G_CALLBACK (player_property_changed_cb), avrcp);
|
||||||
|
|
||||||
|
taglist = player_get_taglist (avrcp->player);
|
||||||
|
|
||||||
|
if (taglist)
|
||||||
|
avrcp->cb (avrcp, taglist, avrcp->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BluezMediaPlayer1 *
|
||||||
|
media_player_from_dbus_object (GDBusObject * object)
|
||||||
|
{
|
||||||
|
return (BluezMediaPlayer1 *) g_dbus_object_get_interface (object,
|
||||||
|
BLUEZ_MEDIA_PLAYER_IFACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GType
|
||||||
|
manager_proxy_type_func (GDBusObjectManagerClient * manager,
|
||||||
|
const gchar * object_path, const gchar * interface_name, gpointer user_data)
|
||||||
|
{
|
||||||
|
if (!interface_name)
|
||||||
|
return G_TYPE_DBUS_OBJECT_PROXY;
|
||||||
|
|
||||||
|
if (g_str_equal (interface_name, BLUEZ_MEDIA_PLAYER_IFACE))
|
||||||
|
return BLUEZ_TYPE_MEDIA_PLAYER1_PROXY;
|
||||||
|
|
||||||
|
return G_TYPE_DBUS_PROXY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
manager_object_added_cb (GDBusObjectManager * manager,
|
||||||
|
GDBusObject * object, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstAvrcpConnection *avrcp = (GstAvrcpConnection *) user_data;
|
||||||
|
BluezMediaPlayer1 *player;
|
||||||
|
|
||||||
|
if (!(player = media_player_from_dbus_object (object)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_avrcp_connection_set_player (avrcp, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
manager_object_removed_cb (GDBusObjectManager * manager,
|
||||||
|
GDBusObject * object, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstAvrcpConnection *avrcp = (GstAvrcpConnection *) user_data;
|
||||||
|
BluezMediaPlayer1 *player;
|
||||||
|
|
||||||
|
if (!(player = media_player_from_dbus_object (object)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (player == avrcp->player)
|
||||||
|
gst_avrcp_connection_set_player (avrcp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
manager_ready_cb (GObject * object, GAsyncResult * res, gpointer user_data)
|
||||||
|
{
|
||||||
|
GstAvrcpConnection *avrcp = (GstAvrcpConnection *) user_data;
|
||||||
|
GList *objects, *i;
|
||||||
|
GError *err = NULL;
|
||||||
|
|
||||||
|
avrcp->manager = g_dbus_object_manager_client_new_for_bus_finish (res, &err);
|
||||||
|
if (!avrcp->manager) {
|
||||||
|
GST_WARNING ("Could not create ObjectManager proxy: %s", err->message);
|
||||||
|
g_error_free (err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_signal_connect (avrcp->manager, "object-added",
|
||||||
|
G_CALLBACK (manager_object_added_cb), avrcp);
|
||||||
|
g_signal_connect (avrcp->manager, "object-removed",
|
||||||
|
G_CALLBACK (manager_object_removed_cb), avrcp);
|
||||||
|
|
||||||
|
objects = g_dbus_object_manager_get_objects (avrcp->manager);
|
||||||
|
|
||||||
|
for (i = objects; i; i = i->next) {
|
||||||
|
BluezMediaPlayer1 *player =
|
||||||
|
media_player_from_dbus_object (G_DBUS_OBJECT (i->data));
|
||||||
|
|
||||||
|
if (player && g_str_equal (avrcp->dev_path,
|
||||||
|
bluez_media_player1_get_device (player))) {
|
||||||
|
gst_avrcp_connection_set_player (avrcp, player);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free_full (objects, g_object_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
GstAvrcpConnection *
|
||||||
|
gst_avrcp_connection_new (const gchar * dev_path, GstAvrcpMetadataCb cb,
|
||||||
|
gpointer user_data, GDestroyNotify user_data_free_cb)
|
||||||
|
{
|
||||||
|
GstAvrcpConnection *avrcp;
|
||||||
|
|
||||||
|
avrcp = g_new0 (GstAvrcpConnection, 1);
|
||||||
|
|
||||||
|
avrcp->cb = cb;
|
||||||
|
avrcp->user_data = user_data;
|
||||||
|
avrcp->user_data_free_cb = user_data_free_cb;
|
||||||
|
|
||||||
|
avrcp->context = g_main_context_new ();
|
||||||
|
avrcp->mainloop = g_main_loop_new (avrcp->context, FALSE);
|
||||||
|
|
||||||
|
avrcp->dev_path = g_strdup (dev_path);
|
||||||
|
|
||||||
|
g_main_context_push_thread_default (avrcp->context);
|
||||||
|
|
||||||
|
g_dbus_object_manager_client_new_for_bus (G_BUS_TYPE_SYSTEM,
|
||||||
|
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, BLUEZ_NAME, BLUEZ_PATH,
|
||||||
|
manager_proxy_type_func, NULL, NULL, NULL, manager_ready_cb, avrcp);
|
||||||
|
|
||||||
|
g_main_context_pop_thread_default (avrcp->context);
|
||||||
|
|
||||||
|
avrcp->thread = g_thread_new ("gstavrcp", (GThreadFunc) g_main_loop_run,
|
||||||
|
avrcp->mainloop);
|
||||||
|
|
||||||
|
return avrcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gst_avrcp_connection_free (GstAvrcpConnection * avrcp)
|
||||||
|
{
|
||||||
|
g_main_loop_quit (avrcp->mainloop);
|
||||||
|
g_main_loop_unref (avrcp->mainloop);
|
||||||
|
|
||||||
|
g_main_context_unref (avrcp->context);
|
||||||
|
|
||||||
|
g_thread_join (avrcp->thread);
|
||||||
|
|
||||||
|
if (avrcp->player)
|
||||||
|
g_object_unref (avrcp->player);
|
||||||
|
|
||||||
|
if (avrcp->manager)
|
||||||
|
g_object_unref (avrcp->manager);
|
||||||
|
|
||||||
|
if (avrcp->user_data_free_cb)
|
||||||
|
avrcp->user_data_free_cb (avrcp->user_data);
|
||||||
|
|
||||||
|
g_free (avrcp->dev_path);
|
||||||
|
g_free (avrcp);
|
||||||
|
}
|
36
sys/bluez/gstavrcputil.h
Normal file
36
sys/bluez/gstavrcputil.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Arun Raghavan <git@arunraghavan.net>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that 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 library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GST_AVRCP_UTIL_H
|
||||||
|
#define __GST_AVRCP_UTIL_H
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
|
||||||
|
typedef struct _GstAvrcpConnection GstAvrcpConnection;
|
||||||
|
|
||||||
|
typedef void (*GstAvrcpMetadataCb) (GstAvrcpConnection *, GstTagList *,
|
||||||
|
gpointer);
|
||||||
|
|
||||||
|
GstAvrcpConnection *
|
||||||
|
gst_avrcp_connection_new (const gchar * dev_path, GstAvrcpMetadataCb cb,
|
||||||
|
gpointer user_data, GDestroyNotify user_data_free_cb);
|
||||||
|
|
||||||
|
void gst_avrcp_connection_free (GstAvrcpConnection * avrcp);
|
||||||
|
|
||||||
|
#endif /* __GST_AVRCP_UTIL_H */
|
@ -26,4 +26,28 @@
|
|||||||
<property name="Delay" type="q" access="read"></property>
|
<property name="Delay" type="q" access="read"></property>
|
||||||
<property name="Volume" type="q" access="readwrite"></property>
|
<property name="Volume" type="q" access="readwrite"></property>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
|
<interface name="org.bluez.MediaPlayer1">
|
||||||
|
<method name="Play"/>
|
||||||
|
<method name="Pause"/>
|
||||||
|
<method name="Stop"/>
|
||||||
|
<method name="Next"/>
|
||||||
|
<method name="Previous"/>
|
||||||
|
<method name="FastForward"/>
|
||||||
|
<method name="Rewind"/>
|
||||||
|
<property name="Name" type="s" access="read"/>
|
||||||
|
<property name="Type" type="s" access="read"/>
|
||||||
|
<property name="Subtype" type="s" access="read"/>
|
||||||
|
<property name="Position" type="u" access="read"/>
|
||||||
|
<property name="Status" type="s" access="read"/>
|
||||||
|
<property name="Equalizer" type="s" access="readwrite"/>
|
||||||
|
<property name="Repeat" type="s" access="readwrite"/>
|
||||||
|
<property name="Shuffle" type="s" access="readwrite"/>
|
||||||
|
<property name="Scan" type="s" access="readwrite"/>
|
||||||
|
<property name="Track" type="a{sv}" access="read"/>
|
||||||
|
<property name="Device" type="o" access="read"/>
|
||||||
|
<property name="Browsable" type="b" access="read"/>
|
||||||
|
<property name="Searchable" type="b" access="read"/>
|
||||||
|
<property name="Playlist" type="o" access="read"/>
|
||||||
|
</interface>
|
||||||
</node>
|
</node>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user