Skip to content

Commit 38b9970

Browse files
committed
hook up device events
1 parent c586646 commit 38b9970

13 files changed

+427
-17
lines changed

app/src/control_msg.h

+10
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ enum sc_control_msg_type {
4141
SC_CONTROL_MSG_TYPE_UHID_INPUT,
4242
SC_CONTROL_MSG_TYPE_UHID_DESTROY,
4343
SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS,
44+
SC_CONTROL_MSG_TYPE_MEDIA_STATE,
45+
SC_CONTROL_MSG_TYPE_MEDIA_SEEK,
4446
};
4547

4648
enum sc_screen_power_mode {
@@ -110,6 +112,14 @@ struct sc_control_msg {
110112
struct {
111113
uint16_t id;
112114
} uhid_destroy;
115+
struct {
116+
uint16_t player_id;
117+
uint64_t position;
118+
} media_seek;
119+
struct {
120+
uint16_t player_id;
121+
uint8_t state;
122+
} media_state;
113123
};
114124
};
115125

app/src/device_msg.c

+48-16
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@
77
#include "util/binary.h"
88
#include "util/log.h"
99

10+
static int read_message(uint8_t **target, const uint8_t *src, const uint16_t size) {
11+
uint8_t *data = malloc(size + 1);
12+
if (!data) {
13+
LOG_OOM();
14+
return -1;
15+
}
16+
if (size) {
17+
data[size] = '\0';
18+
memcpy(data, src, size);
19+
}
20+
*target = data;
21+
return 0;
22+
}
23+
1024
ssize_t
1125
sc_device_msg_deserialize(const uint8_t *buf, size_t len,
1226
struct sc_device_msg *msg) {
@@ -25,17 +39,10 @@ sc_device_msg_deserialize(const uint8_t *buf, size_t len,
2539
if (clipboard_len > len - 5) {
2640
return 0; // no complete message
2741
}
28-
char *text = malloc(clipboard_len + 1);
29-
if (!text) {
30-
LOG_OOM();
42+
if (read_message((uint8_t **)&msg->clipboard.text, &buf[5], clipboard_len) == -1) {
3143
return -1;
3244
}
33-
if (clipboard_len) {
34-
memcpy(text, &buf[5], clipboard_len);
35-
}
36-
text[clipboard_len] = '\0';
3745

38-
msg->clipboard.text = text;
3946
return 5 + clipboard_len;
4047
}
4148
case DEVICE_MSG_TYPE_ACK_CLIPBOARD: {
@@ -56,21 +63,43 @@ sc_device_msg_deserialize(const uint8_t *buf, size_t len,
5663
if (size < len - 5) {
5764
return 0; // not available
5865
}
59-
uint8_t *data = malloc(size);
60-
if (!data) {
61-
LOG_OOM();
66+
67+
msg->uhid_output.id = id;
68+
msg->uhid_output.size = size;
69+
if (read_message(&msg->uhid_output.data, &buf[5], size) == -1) {
6270
return -1;
6371
}
64-
if (size) {
65-
memcpy(data, &buf[5], size);
72+
73+
return 5 + size;
74+
case DEVICE_MSG_TYPE_MEDIA_UPDATE: {
75+
if (len < 5) {
76+
// at least id + size
77+
return 0; // not available
78+
}
79+
uint16_t id = sc_read16be(&buf[1]);
80+
size_t size = sc_read16be(&buf[3]);
81+
if (size < len - 5) {
82+
return 0; // not available
6683
}
6784

68-
msg->uhid_output.id = id;
69-
msg->uhid_output.size = size;
70-
msg->uhid_output.data = data;
85+
msg->media_update.id = id;
86+
msg->media_update.size = size;
87+
if (read_message(&msg->media_update.data, &buf[5], size) == -1) {
88+
return -1;
89+
}
7190

7291
return 5 + size;
7392
}
93+
case DEVICE_MSG_TYPE_MEDIA_REMOVE: {
94+
if (len < 3) {
95+
// at least id
96+
return 0; // not available
97+
}
98+
uint16_t id = sc_read16be(&buf[1]);
99+
msg->media_remove.id = id;
100+
return 3;
101+
}
102+
}
74103
default:
75104
LOGW("Unknown device message type: %d", (int) msg->type);
76105
return -1; // error, we cannot recover
@@ -86,6 +115,9 @@ sc_device_msg_destroy(struct sc_device_msg *msg) {
86115
case DEVICE_MSG_TYPE_UHID_OUTPUT:
87116
free(msg->uhid_output.data);
88117
break;
118+
case DEVICE_MSG_TYPE_MEDIA_UPDATE:
119+
free(msg->media_update.data);
120+
break;
89121
default:
90122
// nothing to do
91123
break;

app/src/device_msg.h

+10
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ enum sc_device_msg_type {
1515
DEVICE_MSG_TYPE_CLIPBOARD,
1616
DEVICE_MSG_TYPE_ACK_CLIPBOARD,
1717
DEVICE_MSG_TYPE_UHID_OUTPUT,
18+
DEVICE_MSG_TYPE_MEDIA_UPDATE,
19+
DEVICE_MSG_TYPE_MEDIA_REMOVE,
1820
};
1921

2022
struct sc_device_msg {
@@ -31,6 +33,14 @@ struct sc_device_msg {
3133
uint16_t size;
3234
uint8_t *data; // owned, to be freed by free()
3335
} uhid_output;
36+
struct {
37+
uint16_t id;
38+
uint16_t size;
39+
uint8_t *data; // owned, to be freed by free()
40+
} media_update;
41+
struct {
42+
uint16_t id;
43+
} media_remove;
3444
};
3545
};
3646

app/src/receiver.c

+22
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,22 @@ task_uhid_output(void *userdata) {
7474
free(data);
7575
}
7676

77+
static void
78+
dump_media_update(const struct sc_device_msg* msg) {
79+
uint8_t msg_type = 0;
80+
uint8_t *msg_ptr = NULL;
81+
for (int i = 0; i < msg->media_update.size; i++) {
82+
if (msg_ptr == NULL) {
83+
msg_type = msg->media_update.data[i];
84+
msg_ptr = &msg->media_update.data[i + 1];
85+
} else if (msg->media_update.data[i] == 0) {
86+
LOGI("Media update: %i, %s", (int)msg_type, msg_ptr);
87+
msg_ptr = NULL;
88+
msg_type = 0;
89+
}
90+
}
91+
}
92+
7793
static void
7894
process_msg(struct sc_receiver *receiver, struct sc_device_msg *msg) {
7995
switch (msg->type) {
@@ -150,6 +166,12 @@ process_msg(struct sc_receiver *receiver, struct sc_device_msg *msg) {
150166
return;
151167
}
152168

169+
break;
170+
case DEVICE_MSG_TYPE_MEDIA_UPDATE:
171+
dump_media_update(msg);
172+
break;
173+
case DEVICE_MSG_TYPE_MEDIA_REMOVE:
174+
LOGI("Media remove: %i", msg->media_remove.id);
153175
break;
154176
}
155177
}

server/src/main/java/com/genymobile/scrcpy/Options.java

+5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public class Options {
2121
private int scid = -1; // 31-bit non-negative value, or -1
2222
private boolean video = true;
2323
private boolean audio = true;
24+
private boolean mediaControl = true;
2425
private int maxSize;
2526
private VideoCodec videoCodec = VideoCodec.H264;
2627
private AudioCodec audioCodec = AudioCodec.OPUS;
@@ -81,6 +82,10 @@ public boolean getAudio() {
8182
return audio;
8283
}
8384

85+
public boolean getMediaControls() {
86+
return mediaControl;
87+
}
88+
8489
public int getMaxSize() {
8590
return maxSize;
8691
}

server/src/main/java/com/genymobile/scrcpy/Server.java

+41
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
import com.genymobile.scrcpy.video.SurfaceCapture;
2424
import com.genymobile.scrcpy.video.SurfaceEncoder;
2525
import com.genymobile.scrcpy.video.VideoSource;
26+
import com.genymobile.scrcpy.wrappers.MediaManager;
2627

28+
import android.media.MediaMetadata;
29+
import android.media.session.PlaybackState;
2730
import android.os.BatteryManager;
2831
import android.os.Build;
2932

@@ -139,6 +142,7 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
139142
boolean control = options.getControl();
140143
boolean video = options.getVideo();
141144
boolean audio = options.getAudio();
145+
boolean media = options.getMediaControls();
142146
boolean sendDummyByte = options.getSendDummyByte();
143147
boolean camera = video && options.getVideoSource() == VideoSource.CAMERA;
144148

@@ -162,6 +166,40 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
162166
controller.getSender().send(msg);
163167
});
164168
asyncProcessors.add(controller);
169+
170+
if (media) {
171+
MediaManager mediaManager = MediaManager.create();
172+
173+
mediaManager.setMediaChangeListener(new MediaManager.MediaChange() {
174+
@Override
175+
public void onMetadataChange(int id, MediaMetadata metadata) {
176+
Ln.i("onMetadataChange " + id);
177+
byte[] data = MediaManager.mediaMetadataSerialize(metadata);
178+
DeviceMessage msg = DeviceMessage.createMediaUpdate(id, data);
179+
controller.getSender().send(msg);
180+
}
181+
182+
@Override
183+
public void onPlaybackStateChange(int id, PlaybackState playbackState) {
184+
Ln.i("onPlaybackStateChange " + id);
185+
int state = MediaManager.create().playbackStateSerialize(playbackState);
186+
if(state < 0) {
187+
return;
188+
}
189+
DeviceMessage msg = DeviceMessage.createMediaState(id, state);
190+
controller.getSender().send(msg);
191+
}
192+
193+
@Override
194+
public void onRemove(int id) {
195+
Ln.i("onRemove " + id);
196+
DeviceMessage msg = DeviceMessage.createMediaRemove(id);
197+
controller.getSender().send(msg);
198+
}
199+
});
200+
201+
mediaManager.start();
202+
}
165203
}
166204

167205
if (audio) {
@@ -200,6 +238,9 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
200238
asyncProcessors.add(surfaceEncoder);
201239
}
202240

241+
242+
243+
203244
Completion completion = new Completion(asyncProcessors.size());
204245
for (AsyncProcessor asyncProcessor : asyncProcessors) {
205246
asyncProcessor.start((fatalError) -> {

server/src/main/java/com/genymobile/scrcpy/control/ControlMessage.java

+29
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public final class ControlMessage {
2323
public static final int TYPE_UHID_INPUT = 13;
2424
public static final int TYPE_UHID_DESTROY = 14;
2525
public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15;
26+
public static final int TYPE_MEDIA_STATE = 16;
27+
public static final int TYPE_MEDIA_SEEK = 17;
2628

2729
public static final long SEQUENCE_INVALID = 0;
2830

@@ -48,6 +50,9 @@ public final class ControlMessage {
4850
private long sequence;
4951
private int id;
5052
private byte[] data;
53+
private int mediaState;
54+
private long mediaSeek;
55+
5156

5257
private ControlMessage() {
5358
}
@@ -155,6 +160,22 @@ public static ControlMessage createUhidDestroy(int id) {
155160
return msg;
156161
}
157162

163+
public static ControlMessage createMediaState(int receiverId, byte state) {
164+
ControlMessage msg = new ControlMessage();
165+
msg.type = TYPE_MEDIA_STATE;
166+
msg.id = receiverId;
167+
msg.mediaState = state;
168+
return msg;
169+
}
170+
171+
public static ControlMessage createMediaSeek(int receiverId, long position) {
172+
ControlMessage msg = new ControlMessage();
173+
msg.type = TYPE_MEDIA_STATE;
174+
msg.id = receiverId;
175+
msg.mediaSeek = position;
176+
return msg;
177+
}
178+
158179
public int getType() {
159180
return type;
160181
}
@@ -226,4 +247,12 @@ public int getId() {
226247
public byte[] getData() {
227248
return data;
228249
}
250+
251+
public long getMediaSeek() {
252+
return mediaSeek;
253+
}
254+
255+
public int getMediaState() {
256+
return mediaState;
257+
}
229258
}

server/src/main/java/com/genymobile/scrcpy/control/ControlMessageReader.java

+16
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,27 @@ public ControlMessage read() throws IOException {
5353
return parseUhidInput();
5454
case ControlMessage.TYPE_UHID_DESTROY:
5555
return parseUhidDestroy();
56+
case ControlMessage.TYPE_MEDIA_STATE:
57+
return parseMediaPlayStateRequest();
58+
case ControlMessage.TYPE_MEDIA_SEEK:
59+
return parseMediaSeekRequest();
5660
default:
5761
throw new ControlProtocolException("Unknown event type: " + type);
5862
}
5963
}
6064

65+
private ControlMessage parseMediaSeekRequest() throws IOException {
66+
int receiverId = dis.readUnsignedShort();
67+
long position = dis.readLong();
68+
return ControlMessage.createMediaSeek(receiverId, position);
69+
}
70+
71+
private ControlMessage parseMediaPlayStateRequest() throws IOException {
72+
int receiverId = dis.readUnsignedShort();
73+
byte state = dis.readByte();
74+
return ControlMessage.createMediaState(receiverId, state);
75+
}
76+
6177
private ControlMessage parseInjectKeycode() throws IOException {
6278
int action = dis.readUnsignedByte();
6379
int keycode = dis.readInt();

server/src/main/java/com/genymobile/scrcpy/control/Controller.java

+14
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,27 @@ private boolean handleEvent() throws IOException {
221221
case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
222222
openHardKeyboardSettings();
223223
break;
224+
case ControlMessage.TYPE_MEDIA_STATE:
225+
mediaUpdateState(msg.getId(), msg.getMediaState());
226+
break;
227+
case ControlMessage.TYPE_MEDIA_SEEK:
228+
mediaSeek(msg.getId(), msg.getMediaSeek());
229+
break;
224230
default:
225231
// do nothing
226232
}
227233

228234
return true;
229235
}
230236

237+
private void mediaUpdateState(int id, int mediaState) {
238+
// TODO
239+
}
240+
241+
private void mediaSeek(int id, long mediaSeek) {
242+
// TODO
243+
}
244+
231245
private boolean injectKeycode(int action, int keycode, int repeat, int metaState) {
232246
if (keepPowerModeOff && action == KeyEvent.ACTION_UP && (keycode == KeyEvent.KEYCODE_POWER || keycode == KeyEvent.KEYCODE_WAKEUP)) {
233247
schedulePowerModeOff();

0 commit comments

Comments
 (0)