[PATCH BlueZ] control: Add methods FastForward and Rewind

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx>

These method can be used to fast-forward and rewind the playback, their
action will keep active until another method is called.

The commands are reapeated every 2 seconds to conform with AVC spec.
---
 doc/control-api.txt      |  10 ++++
 profiles/audio/avctp.c   | 148 ++++++++++++++++++++++++++++++++++++++++++-----
 profiles/audio/control.c |  14 +++++
 3 files changed, 157 insertions(+), 15 deletions(-)

diff --git a/doc/control-api.txt b/doc/control-api.txt
index e0ed04a..55cebe8 100644
--- a/doc/control-api.txt
+++ b/doc/control-api.txt
@@ -40,6 +40,16 @@ Methods		void Play()
 
 			Adjust remote volume one step down
 
+		void FastForward()
+
+			Fast forward playback, this action is only stopped
+			when another method in this interface is called.
+
+		void Rewind()
+
+			Rewind playback, this action is only stopped
+			when another method in this interface is called.
+
 Properties
 
 		boolean Connected [readonly]
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index e48c257..8693784 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -162,6 +162,11 @@ struct avctp_channel {
 	guint process_id;
 };
 
+struct key_pressed {
+	uint8_t op;
+	guint timer;
+};
+
 struct avctp {
 	struct avctp_server *server;
 	bdaddr_t dst;
@@ -179,6 +184,7 @@ struct avctp {
 	struct avctp_channel *browsing;
 
 	uint8_t key_quirks[256];
+	struct key_pressed *key;
 };
 
 struct avctp_pdu_handler {
@@ -214,6 +220,9 @@ static GSList *servers = NULL;
 
 static void auth_cb(DBusError *derr, void *user_data);
 static gboolean process_queue(gpointer user_data);
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+					uint8_t subunit, uint8_t *operands,
+					size_t operand_count, void *user_data);
 
 static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
 {
@@ -409,6 +418,13 @@ static void avctp_disconnected(struct avctp *session)
 		session->auth_id = 0;
 	}
 
+	if (session->key != NULL) {
+		if (session->key->timer > 0)
+			g_source_remove(session->key->timer);
+		g_free(session->key);
+	}
+
+
 	if (session->uinput >= 0) {
 		char address[18];
 
@@ -1272,6 +1288,116 @@ static int avctp_send_req(struct avctp *session, uint8_t code,
 	return 0;
 }
 
+static char *op2str(uint8_t op)
+{
+	switch (op & 0x7f) {
+	case AVC_VOLUME_UP:
+		return "VOLUME UP";
+	case AVC_VOLUME_DOWN:
+		return "VOLUME DOWN";
+	case AVC_MUTE:
+		return "MUTE";
+	case AVC_PLAY:
+		return "PLAY";
+	case AVC_STOP:
+		return "STOP";
+	case AVC_PAUSE:
+		return "PAUSE";
+	case AVC_RECORD:
+		return "RECORD";
+	case AVC_REWIND:
+		return "REWIND";
+	case AVC_FAST_FORWARD:
+		return "FAST FORWARD";
+	case AVC_EJECT:
+		return "EJECT";
+	case AVC_FORWARD:
+		return "FORWARD";
+	case AVC_BACKWARD:
+		return "BACKWARD";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static int avctp_passthrough_press(struct avctp *session, uint8_t op)
+{
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	/* Button pressed */
+	operands[0] = op & 0x7f;
+	operands[1] = 0;
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				operands, sizeof(operands),
+				avctp_passthrough_rsp, NULL);
+}
+
+static int avctp_passthrough_release(struct avctp *session, uint8_t op)
+{
+	uint8_t operands[2];
+
+	DBG("%s", op2str(op));
+
+	/* Button released */
+	operands[0] = op | 0x80;
+	operands[1] = 0;
+
+	return avctp_send_req(session, AVC_CTYPE_CONTROL,
+				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+				operands, sizeof(operands),
+				NULL, NULL);
+}
+
+static gboolean repeat_timeout(gpointer user_data)
+{
+	struct avctp *session = user_data;
+	struct key_pressed *key = session->key;
+
+	avctp_passthrough_release(session, key->op);
+	avctp_passthrough_press(session, key->op);
+
+	return TRUE;
+}
+
+static void release_pressed(struct avctp *session)
+{
+	struct key_pressed *key = session->key;
+
+	avctp_passthrough_release(session, key->op);
+
+	if (key->timer > 0)
+		g_source_remove(key->timer);
+
+	g_free(key);
+	session->key = NULL;
+}
+
+static bool set_pressed(struct avctp *session, uint8_t op)
+{
+	struct key_pressed *key;
+
+	if (session->key != NULL) {
+		if (session->key->op == op)
+			return TRUE;
+		release_pressed(session);
+	}
+
+	if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+		return FALSE;
+
+	key = g_new0(struct key_pressed, 1);
+	key->op = op;
+	key->timer = g_timeout_add_seconds(2, repeat_timeout, session);
+
+	session->key = key;
+
+	return TRUE;
+}
+
 static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
 					uint8_t subunit, uint8_t *operands,
 					size_t operand_count, void *user_data)
@@ -1279,29 +1405,21 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
 	if (code != AVC_CTYPE_ACCEPTED)
 		return FALSE;
 
-	/* Button release */
-	operands[0] |= 0x80;
+	if (set_pressed(session, operands[0]))
+		return FALSE;
 
-	avctp_send_req(session, AVC_CTYPE_CONTROL,
-				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
-				operands, operand_count,
-				NULL, NULL);
+	avctp_passthrough_release(session, operands[0]);
 
 	return FALSE;
 }
 
 int avctp_send_passthrough(struct avctp *session, uint8_t op)
 {
-	uint8_t operands[2];
-
-	/* Button pressed */
-	operands[0] = op & 0x7f;
-	operands[1] = 0;
+	/* Auto release if key pressed */
+	if (session->key != NULL)
+		release_pressed(session);
 
-	return avctp_send_req(session, AVC_CTYPE_CONTROL,
-				AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
-				operands, sizeof(operands),
-				avctp_passthrough_rsp, NULL);
+	return avctp_passthrough_press(session, op);
 }
 
 int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
diff --git a/profiles/audio/control.c b/profiles/audio/control.c
index 0013f8d..7299b7e 100644
--- a/profiles/audio/control.c
+++ b/profiles/audio/control.c
@@ -238,6 +238,18 @@ static DBusMessage *control_previous(DBusConnection *conn, DBusMessage *msg,
 	return key_pressed(conn, msg, AVC_BACKWARD, data);
 }
 
+static DBusMessage *control_fast_forward(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_FAST_FORWARD, data);
+}
+
+static DBusMessage *control_rewind(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	return key_pressed(conn, msg, AVC_REWIND, data);
+}
+
 static gboolean control_property_get_connected(
 					const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
@@ -258,6 +270,8 @@ static const GDBusMethodTable control_methods[] = {
 	{ GDBUS_METHOD("Previous", NULL, NULL, control_previous) },
 	{ GDBUS_METHOD("VolumeUp", NULL, NULL, control_volume_up) },
 	{ GDBUS_METHOD("VolumeDown", NULL, NULL, control_volume_down) },
+	{ GDBUS_METHOD("FastForward", NULL, NULL, control_fast_forward) },
+	{ GDBUS_METHOD("Rewind", NULL, NULL, control_rewind) },
 	{ }
 };
 
-- 
1.7.11.7

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux