Scrcpy.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. package org.las2mile.scrcpy;
  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.Binder;
  5. import android.os.Bundle;
  6. import android.os.Handler;
  7. import android.os.IBinder;
  8. import android.os.Message;
  9. import android.os.Messenger;
  10. import android.os.RemoteException;
  11. import android.util.Log;
  12. import android.view.MotionEvent;
  13. import android.view.Surface;
  14. import org.las2mile.scrcpy.decoder.VideoDecoder;
  15. import org.las2mile.scrcpy.model.ByteUtils;
  16. import org.las2mile.scrcpy.model.MediaPacket;
  17. import org.las2mile.scrcpy.model.VideoPacket;
  18. import java.io.DataInputStream;
  19. import java.io.DataOutputStream;
  20. import java.io.EOFException;
  21. import java.io.IOException;
  22. import java.net.Socket;
  23. import java.util.concurrent.atomic.AtomicBoolean;
  24. import org.las2mile.okio.OkIoClient;
  25. import org.las2mile.okio.message.BaseMsg;
  26. import org.las2mile.okio.OkIoClient.OnOkIoMsgListener;
  27. public class Scrcpy extends Service {
  28. private String serverAdr;
  29. private Surface surface;
  30. private int screenWidth;
  31. private int screenHeight;
  32. private byte[] event = null;
  33. private VideoDecoder videoDecoder;
  34. private AtomicBoolean updateAvailable = new AtomicBoolean(false);
  35. private IBinder mBinder = new MyServiceBinder();
  36. private boolean first_time = true;
  37. private AtomicBoolean LetServceRunning = new AtomicBoolean(true);
  38. private ServiceCallbacks serviceCallbacks;
  39. private int[] remote_dev_resolution = new int[2];
  40. private boolean socket_status = false;
  41. OkIoClient mOkIoClient;
  42. @Override
  43. public IBinder onBind(Intent intent) {
  44. return mBinder;
  45. }
  46. public void setServiceCallbacks(ServiceCallbacks callbacks) {
  47. serviceCallbacks = callbacks;
  48. }
  49. public void setParms(Surface NewSurface, int NewWidth, int NewHeight) {
  50. this.screenWidth = NewWidth;
  51. this.screenHeight = NewHeight;
  52. this.surface = NewSurface;
  53. videoDecoder.start();
  54. updateAvailable.set(true);
  55. }
  56. public void start(Surface surface, String serverAdr, int screenHeight, int screenWidth) {
  57. this.videoDecoder = new VideoDecoder();
  58. videoDecoder.start();
  59. this.serverAdr = serverAdr;
  60. this.screenHeight = screenHeight;
  61. this.screenWidth = screenWidth;
  62. this.surface = surface;
  63. Thread thread = new Thread(new Runnable() {
  64. @Override
  65. public void run() {
  66. startConnection();
  67. }
  68. });
  69. thread.start();
  70. //手机-设备交互客户端
  71. mOkIoClient = new OkIoClient(serverAdr);
  72. mOkIoClient.setOnOkIoMsgListener(new OnOkIoMsgListener() {
  73. @Override
  74. public void onOkIoMsg(BaseMsg msg) {
  75. if (serviceCallbacks != null) {
  76. serviceCallbacks.onOkIoMsg(msg);
  77. }
  78. }
  79. });
  80. mOkIoClient.start();
  81. }
  82. public void downloadLog(){
  83. if(mOkIoClient != null){
  84. mOkIoClient.requestLogFile();
  85. }
  86. }
  87. public void pause() {
  88. videoDecoder.stop();
  89. }
  90. public void resume() {
  91. videoDecoder.start();
  92. updateAvailable.set(true);
  93. }
  94. public void StopService() {
  95. LetServceRunning.set(false);
  96. stopSelf();
  97. }
  98. public boolean touchevent(MotionEvent touch_event, int displayW, int displayH) {
  99. int[] buf = new int[]{touch_event.getAction(), touch_event.getButtonState(), (int) touch_event.getX() * screenWidth / displayW, (int) touch_event.getY() * screenHeight / displayH};
  100. final byte[] array = new byte[buf.length * 4]; // https://stackoverflow.com/questions/2183240/java-integer-to-byte-array
  101. for (int j = 0; j < buf.length; j++) {
  102. final int c = buf[j];
  103. array[j * 4] = (byte) ((c & 0xFF000000) >> 24);
  104. array[j * 4 + 1] = (byte) ((c & 0xFF0000) >> 16);
  105. array[j * 4 + 2] = (byte) ((c & 0xFF00) >> 8);
  106. array[j * 4 + 3] = (byte) (c & 0xFF);
  107. }
  108. event = array;
  109. return true;
  110. }
  111. public int[] get_remote_device_resolution(){
  112. return remote_dev_resolution;
  113. }
  114. public boolean check_socket_connection(){
  115. return socket_status;
  116. }
  117. public void sendKeyevent(int keycode) {
  118. int[] buf = new int[]{keycode};
  119. final byte[] array = new byte[buf.length * 4]; // https://stackoverflow.com/questions/2183240/java-integer-to-byte-array
  120. for (int j = 0; j < buf.length; j++) {
  121. final int c = buf[j];
  122. array[j * 4] = (byte) ((c & 0xFF000000) >> 24);
  123. array[j * 4 + 1] = (byte) ((c & 0xFF0000) >> 16);
  124. array[j * 4 + 2] = (byte) ((c & 0xFF00) >> 8);
  125. array[j * 4 + 3] = (byte) (c & 0xFF);
  126. }
  127. event = array;
  128. }
  129. private void startConnection() {
  130. videoDecoder = new VideoDecoder();
  131. videoDecoder.start();
  132. DataInputStream dataInputStream;
  133. DataOutputStream dataOutputStream;
  134. Socket socket = null;
  135. VideoPacket.StreamSettings streamSettings = null;
  136. int attempts = 50;
  137. while (attempts != 0) {
  138. try {
  139. Log.e("Scrcpy",attempts+" Connecting to screen push server port 7007:" + serverAdr);
  140. socket = new Socket(serverAdr, 7007);
  141. dataInputStream = new DataInputStream(socket.getInputStream());
  142. dataOutputStream = new DataOutputStream(socket.getOutputStream());
  143. byte[] packetSize;
  144. attempts = 0;
  145. byte[] buf = new byte[16];
  146. dataInputStream.read(buf, 0,16);
  147. for(int i =0; i<remote_dev_resolution.length; i++) {
  148. remote_dev_resolution[i] = (((int) (buf[i * 4]) << 24) & 0xFF000000) |
  149. (((int) (buf[i * 4 + 1]) << 16) & 0xFF0000) |
  150. (((int) (buf[i * 4 + 2]) << 8) & 0xFF00) |
  151. ((int) (buf[i * 4 + 3]) & 0xFF);
  152. }
  153. if (remote_dev_resolution[0] > remote_dev_resolution[1]){first_time = false;
  154. int i = remote_dev_resolution[0];
  155. remote_dev_resolution[0] = remote_dev_resolution[1];
  156. remote_dev_resolution[1] = i;
  157. }
  158. socket_status = true;
  159. // Log.e("Remote device res", String.valueOf(remote_dev_resolution[0]+" x "+remote_dev_resolution[1]));
  160. while (LetServceRunning.get()) {
  161. try {
  162. if (event != null) {
  163. dataOutputStream.write(event, 0, event.length);
  164. event = null;
  165. }
  166. if (dataInputStream.available() > 0) {
  167. packetSize = new byte[4];
  168. dataInputStream.readFully(packetSize, 0, 4);
  169. int size = ByteUtils.bytesToInt(packetSize);
  170. byte[] packet = new byte[size];
  171. dataInputStream.readFully(packet, 0, size);
  172. VideoPacket videoPacket = VideoPacket.fromArray(packet);
  173. if (videoPacket.type == MediaPacket.Type.VIDEO) {
  174. byte[] data = videoPacket.data;
  175. if (videoPacket.flag == VideoPacket.Flag.CONFIG || updateAvailable.get()) {
  176. if (!updateAvailable.get()) {
  177. streamSettings = VideoPacket.getStreamSettings(data);
  178. if (!first_time) {
  179. if (serviceCallbacks != null) {
  180. serviceCallbacks.loadNewRotation();
  181. }
  182. while (!updateAvailable.get()) {
  183. // Waiting for new surface
  184. try {
  185. Thread.sleep(100);
  186. } catch (InterruptedException e) {
  187. e.printStackTrace();
  188. }
  189. }
  190. }
  191. }
  192. updateAvailable.set(false);
  193. // first_time = false;
  194. videoDecoder.configure(surface, screenWidth, screenHeight, streamSettings.sps, streamSettings.pps);
  195. } else if (videoPacket.flag == VideoPacket.Flag.END) {
  196. // need close stream
  197. } else {
  198. videoDecoder.decodeSample(data, 0, data.length, 0, videoPacket.flag.getFlag());
  199. }
  200. first_time = false;
  201. }
  202. }
  203. } catch (IOException e) {
  204. e.printStackTrace();
  205. }
  206. }
  207. } catch (IOException e) {
  208. // e.printStackTrace();
  209. attempts = attempts - 1;
  210. if (attempts == 0){
  211. socket_status = false;
  212. return;
  213. }
  214. try {
  215. Thread.sleep(300);//连接比较耗时,适当延长重连时间。连接服务器7007端口,此端口用于推屏
  216. } catch (InterruptedException ignore) {
  217. }
  218. // Log.e("Scrcpy", e.getMessage());
  219. } finally {
  220. if (socket != null) {
  221. try {
  222. socket.close();
  223. } catch (IOException e) {
  224. e.printStackTrace();
  225. }
  226. }
  227. }
  228. }
  229. }
  230. public interface ServiceCallbacks {
  231. void loadNewRotation();
  232. void onOkIoMsg(BaseMsg msg);
  233. }
  234. public class MyServiceBinder extends Binder {
  235. public Scrcpy getService() {
  236. return Scrcpy.this;
  237. }
  238. }
  239. }