root 后的android 无线传屏(服务器端与客户端)
首先我得感谢我的大师兄以及我的师姐,还有网上的一个朋友,没有他们的帮忙我也做不出来。
进入正题吧,首先在android2.3.3/development里面新建一个文件夹(比如说test),然后在test里面建两个文件,Android.mk与screencap.cpp(截屏的一些基础知识请看我的博客 浅谈android截屏http://my.oschina.net/JumpLong/blog/75556
)
/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include <utils/Log.h>#include <errno.h>#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <binder/IPCThreadState.h>#include <binder/ProcessState.h>#include <binder/IServiceManager.h>#include <binder/IMemory.h>#include <surfaceflinger/ISurfaceComposer.h>#include <SkImageEncoder.h>#include <SkBitmap.h>#include <SkStream.h>using namespace android;long getCurrentTime(){struct timeval tv;gettimeofday(&tv,NULL);return tv.tv_sec * 1000 + tv.tv_usec / 1000 ;}long SocketPre,SocketAft,SocketTime;long CapPre,CapAft,CapTime;long SendPre,SendAft,SendTime;long EncodePre,EncodeAft,EncodeTime;int SetSockfd(char* strip,char* strport,int* psockfd){SocketPre = getCurrentTime();struct sockaddr_in server_addr;in_addr_t inaddrip; int portnumber;if((inaddrip=inet_addr(strip))==INADDR_NONE) { fprintf(stderr,"ip Error!\n");LOGD("ip error!"); return -1; } if((portnumber=atoi(strport))<0) { fprintf(stderr,"portnumber Error\n");LOGD("port error!"); return -1; }/* 客户程序开始建立 sockfd描述符 */ if((*psockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));LOGD("socketcreate error!"); return -1; } /* 客户程序填充服务端的资料 */ bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(portnumber);server_addr.sin_addr.s_addr=inaddrip; /* 客户程序发起连接请求 */ if(connect(*psockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));LOGD("connect error!"); return -1; }SocketAft = getCurrentTime();SocketTime = SocketAft - SocketPre;return 0; }char* CapScreen(int* len){const String16 name("SurfaceFlinger");sp<ISurfaceComposer> composer;getService(name, &composer);sp<IMemoryHeap> heap;uint32_t w, h;PixelFormat f;EncodePre = getCurrentTime();status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0);if (err != NO_ERROR) {fprintf(stderr, "screen capture failed: %s\n", strerror(-err));return NULL;}EncodeAft = getCurrentTime();EncodeTime = EncodeAft - EncodePre;printf("screen capture success: w=%u, h=%u, pixels=%p,heapsize = %zu,heapsize = %zx\n",w, h, heap->getBase() ,heap->getSize(),heap->getSize());SkBitmap b;b.setConfig(SkBitmap::kARGB_8888_Config, w, h);b.setPixels(heap->getBase());printf("size = %ld\n",sizeof(b));CapPre = getCurrentTime();SkDynamicMemoryWStream stream;SkImageEncoder::EncodeStream(&stream, b,SkImageEncoder::kJPEG_Type,SkImageEncoder::kDefaultQuality);*len=stream.getOffset(); char* buffer=new char[*len];memcpy(buffer,stream.getStream(),*len);CapAft = getCurrentTime();CapTime = CapAft - CapPre;return buffer;}int SendScreen(int sockfd){SendPre = getCurrentTime(); int jpglong;char* jpgbuffer=CapScreen(&jpglong);if(jpgbuffer==NULL){printf("CapScreen failed!\n");return -1;}//加jpg头和长度printf("screen size is %d byte\n",jpglong);char* pbuffer=new char[jpglong+6];pbuffer[0]='\r';pbuffer[1]='\n';memcpy(pbuffer+2,&jpglong,sizeof(int));memcpy(pbuffer+6,jpgbuffer,jpglong);delete jpgbuffer;if(send(sockfd,pbuffer,jpglong+6,0)<0) { LOGD("send error");printf("send error closed!\n");delete pbuffer;return -1; } SendAft = getCurrentTime();SendTime = SendAft - SendPre;delete pbuffer;return 0;}int main(int argc, char** argv){if(argc != 3){ fprintf(stderr,"Usage:%s ip portnumber\a\n",argv[0]); exit(1);}int sockfd;if(SetSockfd(argv[1],argv[2],&sockfd)==-1){fprintf(stderr,"setsockfd failed!\n");exit(1);} while(true){long pre = getCurrentTime();if(SendScreen(sockfd)!=0){printf("SendScreen failed!\n");close(sockfd); return -1;}long aft = getCurrentTime();long useTime = aft - pre;printf("total %ld,setsocket %ld,cap %ld,send %ld,encode %ld\n",useTime,SocketTime,CapTime,SendTime,EncodeTime);} close(sockfd);return 0;}
这里面主要是调用了screencap那个文件夹里面的screencap.cpp里面的截屏方法,然后加上了socket传输,getCurrentTime只是我用来测试截屏,传输,解码的所用时间的。这个程序运行的话需要三个参数,第一个参数就是执行嘛,第二个就是服务器的IP,第三个就是服务器的端口号。等下再说。。。。
大家都知道要想图片流畅,那么每秒显示的图片数量得有24幅,但是这个目前只能达到5幅,因为它截屏大概是60ms,但是编码得需要大概120ms,也就是平均得要0.2s,如果做成多线程的话,也就是说利用生产者与消费者的模式,生产者负责capture,消费者负责encode,那样的话至少每秒会产生10幅图片(尚未解决,求高手指点)
Android.mk
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES:= \screencap.cppLOCAL_SHARED_LIBRARIES := \libcutils \libutils \libbinder \libskia \ libui \ libsurfaceflinger_clientLOCAL_MODULE:= whilecapLOCAL_MODULE_TAGS := testsLOCAL_C_INCLUDES += \external/skia/include/core \external/skia/include/effects \external/skia/include/images \external/skia/src/ports \external/skia/include/utilsinclude $(BUILD_EXECUTABLE)有了这两个文件后,进入终端,转到test目录,然后mm,没错的话就会出现
Install: out/target/product/generic/system/bin/whilecap
然后将这个whilecap push到已经root的手机里面,注意不要随便放在一个文件里面,sdcard就不行,可以放在system里面,但是在push到system的时候会提示read-only system,这个时候就需要一下几步:adb shell ->su ->mount -o rw,remount -t ext3 /dev/block/mmcblk1p21 /system,然后再次adb push whilecap /system (当然这是你的whilecap已经被你移到home文件夹的情况下)。
现在假设服务器程序已经安装了,那么你怎么去执行呢?adb shell -> /system/whilecap 192.168.0.*** **** 这就是前面说的三个参数,第二个是IP,第三个是port。
当然我们想的不是每次都要去用命令行去执行,多麻烦,于是有了下一步的使用java去调用命令行语句。
public static int RunRootCommand(String paramString) { int i = 0; Process localProcess = null; try { localProcess = Runtime.getRuntime().exec("su"); OutputStreamWriter localOutputStreamWriter = new OutputStreamWriter(localProcess.getOutputStream(), "UTF-8"); // localOutputStreamWriter.write("export LD_LIBRARY_PATH=endorb:/systemb\n"); //localOutputStreamWriter.flush(); localOutputStreamWriter.write(paramString + "\n"); localOutputStreamWriter.flush(); localOutputStreamWriter.write("exit\n"); localOutputStreamWriter.flush(); localOutputStreamWriter.close(); localProcess.waitFor(); int j = localProcess.exitValue(); if (j == 0) i = 1; try { localProcess.destroy(); return i; } catch (Exception localException4) { System.out.println(localException4.getMessage()); while (true) localException4.printStackTrace(); } } catch (Exception localException2) { System.out.println(localException2.getMessage()); while (true) { localException2.printStackTrace(); try { localProcess.destroy(); } catch (Exception localException3) { localException3.printStackTrace(); } } } finally { } }
新建一个android程序,在mainActivity中调用此函数,传送的字符串参数就是我们刚才使用的三个参数合在一起的结果,比如
RunRootCommand("/system/whilecap 192.168.0.100 7787");
这样,客户端的程序就完成了。
下面是服务器端的程序,分为两个,一个是android版本的,一个是java project。
android版本:
package com.example.jumplong;import java.io.DataInputStream;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketException;import java.util.ArrayList;import java.util.Date;import java.util.concurrent.atomic.AtomicInteger;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.Menu;import android.view.MenuInflater;import android.view.MenuItem;import android.view.Window;import android.view.WindowManager;import android.widget.ImageView;import android.widget.Toast;public class TCA extends Activity {/** Called when the activity is first created. */Boolean state = false;Bitmap bitmap = null;ImageView iv = null;Thread severThread;static Handler mHandler;long pre,aft;int photo;static ServerSocket ss = null;static Socket cs = null;static InputStream in = null;static DataInputStream din = null;static CriticQueue cq;static imgReceiver ir;static imgShower is;static Thread tir;static Thread tis;static AtomicInteger imgCount;@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// TODO Auto-generated method stubMenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.menu, menu);return true;}@Overridepublic boolean onMenuItemSelected(int featureId, MenuItem item) {switch (item.getItemId()) {case R.id.start:Log.e("tc"," current Time ��"+new Date( System.currentTimeMillis()));start();return true;case R.id.stop:stop();return true;default:return super.onOptionsItemSelected(item);}}private void start() {Log.e("tc", "enter the TCLCA start()");state = true;iv = (ImageView) findViewById(R.id.showPhoto);//bitmapseverThread = new Thread(new ServeThread()," ServeThread() ");severThread.start();}private void stop() {// TODO Auto-generated method stubstate = false;try {ss.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}aft = System.currentTimeMillis();Toast.makeText(getApplicationContext(), "photo " + photo + " time " + ( aft - pre ), Toast.LENGTH_SHORT).show();// android.os.Process.killProcess((int)Thread.getId());finish();// ActivityManager manager =// (ActivityManager)getSystemService(ACTIVITY_SERVICE);// manager.killBackgroundProcesses(getPackageName());}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 去除标题栏 requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.main);}class ServeThread implements Runnable {public ServeThread() {mHandler = new Handler() {public void handleMessage(Message msg) {switch (msg.what) {case 1:iv.setImageBitmap(bitmap); // ��ImageView����ʾBitmapLog.e("tc", "setImageBitmap sucess");break;default:break;}super.handleMessage(msg);}};}@Overridepublic void run() {try {ss = new ServerSocket(12345);} catch (IOException e) {Log.e("tc", "get the ServerSocket(54321) failed ");}imgCount = new AtomicInteger();// 0while (true) {try {cs = ss.accept();pre = System.currentTimeMillis();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {in = cs.getInputStream();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}din = new DataInputStream(in);cq = new CriticQueue();ir = new imgReceiver(cq);is = new imgShower(cq);tir = new Thread(ir, "thread-p");tis = new Thread(is, "thread-c");tir.start();tis.start();}}//run end}// class ServeThread endclass imgInfo {private byte[] imglocation;public void setimglocation(byte[] imgloc) {this.imglocation = imgloc;}}class CriticQueue {ArrayList<imgInfo> imgInfos = new ArrayList<imgInfo>();public void produce(imgInfo imginfo) {if (imgInfos.add(imginfo)){imgCount.incrementAndGet();}}// produce endpublic synchronized void consume() throws IOException {while (imgInfos.size() == 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}imgInfo img = imgInfos.get(0);imgInfos.remove(0);// first stagebitmap = BitmapFactory.decodeByteArray(img.imglocation, 0,img.imglocation.length);photo++;Message amessage = new Message();amessage.what = 1;mHandler.sendMessage(amessage);img = null;imgCount.decrementAndGet();}}class imgReceiver extends Thread {private CriticQueue imgReceiver_crique;boolean streamIsEnd = false;imgReceiver(CriticQueue q) {this.imgReceiver_crique = q;}public void run() {try {// search for mark of the imageint s0 = 0;int s1 = 0;int imgsize = 0;int inter = 0;byte[] len = new byte[4];while ( !streamIsEnd ) {// search for 13s0 = din.read();while (s0 == -1) {s0 = din.read();}while (s0 != 13) {s0 = din.read();}if (din.read() == 10) {try {s1 = din.read();while ((s1 != -1) && (inter < 4)) {len[inter] = (byte) s1;inter++;s1 = din.read();}if (inter == 4) {imgsize = (len[0] & 0xff)| ((len[1] << 8) & 0xff00)| ((len[2] << 24) >>> 8)| (len[3] << 24);inter=0;}else{streamIsEnd = true;}if (!streamIsEnd) {byte[] img = new byte[imgsize];int readonce = 0;int totalread = 0;img[0] = (byte) s1;totalread++;while (totalread != imgsize) {readonce = din.read(img, totalread, imgsize- totalread);if (readonce == -1) {streamIsEnd = true;break;}totalread += readonce;if (totalread == imgsize) {break;}}if (!streamIsEnd) {imgInfo message = new imgInfo();message.setimglocation(img);imgReceiver_crique.produce(message);} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {}streamIsEnd = true;}} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {}streamIsEnd = true;}} catch (IOException e) {e.printStackTrace();}}// if 10 end}// while( !streamIsEnd ) end} catch (SocketException e) {try {din.close();in.close();cs.close();while (imgCount.get() > 0) {}} catch (IOException e1) {e1.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}// run end}// imgReceiver endclass imgShower extends Thread {private CriticQueue imgShower_crique = null;imgShower(CriticQueue q) {this.imgShower_crique = q;}public void run() {while (true) {if (state == true) {while (imgCount.get() < 1) {}try {imgShower_crique.consume();} catch (IOException e) {e.printStackTrace();}}}}}// imgShower end}// class TCA end
main.XML
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ImageView android:id="@+id/showPhoto" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </LinearLayout>
最后添加权限
<uses-permission android:name ="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name ="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name ="android.permission.CHANGE_NETWORK_STATE"/> <uses-permission android:name ="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.INTERNET" />
然后java project版本:
package com.jump.screencapclient;import java.awt.BorderLayout;import java.awt.Container;import java.awt.Graphics;import java.awt.Image;import java.awt.Toolkit;import java.awt.image.BufferedImage;import java.io.ByteArrayInputStream;import java.io.DataInputStream;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;import java.net.SocketException;import java.util.ArrayList;import java.util.concurrent.atomic.AtomicInteger;import javax.imageio.ImageIO;import javax.swing.ImageIcon;import javax.swing.JFrame;import javax.swing.JLabel;public class test{static JFrame frame = new JFrame();final static JFrame win = new JFrame("JumpLong"); ;static Graphics g;Image img;static long pre ;long aft;static int photo;static Boolean state = false;static ServeThread severThread;static ServerSocket ss = null;static Socket cs = null;static InputStream in = null;static DataInputStream din = null;static CriticQueue cq;static imgReceiver ir;static imgShower is;static Thread tir;static Thread tis;static AtomicInteger imgCount;static BufferedImage image;/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubContainer contentPane;win.setSize(480,900);win.setVisible(true);win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//關閉g = win.getGraphics();severThread = new ServeThread("tcl capture");Thread t = new Thread(severThread);t.start();}public static class ServeThread implements Runnable { String name;public ServeThread(String name){ this.name = name; }@Overridepublic void run() {System.out.println("run");try {ss = new ServerSocket(12345);System.out.println("open the socket ");} catch (IOException e) {System.out.println("get the ServerSocket(54321) failed ");}imgCount = new AtomicInteger();// 0while (true) {try {cs = ss.accept();pre = System.currentTimeMillis();System.out.println("accept ");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}try {in = cs.getInputStream();System.out.println("get the inputstream ");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}din = new DataInputStream(in);System.out.println("after din ");cq = new CriticQueue();System.out.println("after cq");ir = new imgReceiver(cq);System.out.println("after ir ");is = new imgShower(cq); System.out.println("after is ");tir = new Thread(ir, "thread-p");System.out.println("after tir ");tis = new Thread(is, "thread-c");System.out.println("after tis ");tir.start();System.out.println("after tir ");tis.start();System.out.println("after tis ");}}//run end}// class ServeThread endpublic static class imgInfo {private byte[] imglocation;public void setimglocation(byte[] imgloc) {this.imglocation = imgloc;}}public static class CriticQueue {ArrayList<imgInfo> imgInfos = new ArrayList<imgInfo>();public void produce(imgInfo imginfo) {if (imgInfos.add(imginfo)){imgCount.incrementAndGet();}}// produce endpublic synchronized void consume() throws IOException {System.out.println("consume");while (imgInfos.size() == 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}imgInfo img = imgInfos.get(0);imgInfos.remove(0);System.out.print("remove ");// first stageByteArrayInputStream in = new ByteArrayInputStream(img.imglocation); //将b作为输入流;image = ImageIO.read(in); //将in作为输入流,读取图片存入image中,而这里in可以为ByteArrayInputStream();System.out.print("image " + photo++ +"time " + ( System.currentTimeMillis()-pre ));g.drawImage(image,0,0, win);//JFrame frame = new JFrame();//frame.invalidate();//JLabel label = new JLabel(new ImageIcon(image));//System.out.print("show the image ");//frame.getContentPane().add(label, BorderLayout.CENTER);//frame.pack();//frame.setVisible(true); ////�رմ���--�˳�����//frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);img = null;imgCount.decrementAndGet();}}public static class imgReceiver extends Thread {private CriticQueue imgReceiver_crique;boolean streamIsEnd = false;imgReceiver(CriticQueue q) {this.imgReceiver_crique = q;}public void run() {try {// search for mark of the imageint s0 = 0;int s1 = 0;int imgsize = 0;int inter = 0;byte[] len = new byte[4];while ( !streamIsEnd ) {// search for 13s0 = din.read();System.out.println("din.read() ");while (s0 == -1) {s0 = din.read();System.out.println("s0 == -1");}while (s0 != 13) {s0 = din.read();System.out.println("s0 != 13 ");}if (din.read() == 10) {try {System.out.println("din.read() == 10");s1 = din.read();while ((s1 != -1) && (inter < 4)) {len[inter] = (byte) s1;inter++;s1 = din.read();}if (inter == 4) {imgsize = (len[0] & 0xff)| ((len[1] << 8) & 0xff00)| ((len[2] << 24) >>> 8)| (len[3] << 24);inter=0;}else{streamIsEnd = true;System.out.println("streamisend = true ");}if (!streamIsEnd) {System.out.println("!streamisend");byte[] img = new byte[imgsize];int readonce = 0;int totalread = 0;img[0] = (byte) s1;totalread++;System.out.println("titalread = " + totalread);while (totalread != imgsize) {readonce = din.read(img, totalread, imgsize- totalread);if (readonce == -1) {streamIsEnd = true;break;}totalread += readonce;if (totalread == imgsize) {break;}}if (!streamIsEnd) {imgInfo message = new imgInfo();message.setimglocation(img);imgReceiver_crique.produce(message);} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {}streamIsEnd = true;System.out.println("streamisend is true");}} else {din.close();in.close();cs.close();while (imgCount.get() > 0) {System.out.println("imgcount.get > 0 ");}streamIsEnd = true;}} catch (IOException e) {e.printStackTrace();}}// if 10 end}// while( !streamIsEnd ) end} catch (SocketException e) {try {din.close();in.close();cs.close();while (imgCount.get() > 0) {}} catch (IOException e1) {e1.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}// run end}// imgReceiver endpublic static class imgShower extends Thread {private CriticQueue imgShower_crique = null;imgShower(CriticQueue q) {this.imgShower_crique = q;}public void run() {while (true) {System.out.println("imgshower run while(true) ");if (!state) {System.out.println("state == true ");while (imgCount.get() < 1) {}try {System.out.println("imgcount.get() < 1 ");System.out.println("imgshower try function");imgShower_crique.consume();} catch (IOException e) {e.printStackTrace();}}}}}// imgShower end}
现在客户端与服务器端都写好,运行的步骤就是:
1:打开服务器程序:java project的话就直接运行,android project的话就是运行后点击手机的菜单按钮,就会出现两个按钮,点击左边的开启服务器端(端口号目前设置的是12345,假设服务器的IP地址是192.168.0.100)
2:运行客户端程序:你可以在android程序中添加一个editview来接受我们要传给RunRootCommand函数的参数,这个自己去写就是了,最终达到的效果就是如下
RunRootCommand("/system/whilecap 192.168.0.100 12345");
3:结束:点击服务器端的关闭服务器端,此时不再传送图片,但是客户端程序还在运行,需要去任务管理器中强行结束。因为此程序还没有优化,只是一个雏形。
到此,android的无线传屏就搞定了。
最后,还是希望大家帮帮忙,怎么去提升性能,怎么尽可能地增加每秒截取(截取与编码)的图片。
更多相关文章
- 如何为平板电脑开发android应用程序?
- Android(安卓)闹钟详解
- Android(安卓)NDK 是什么
- 05_Content Provider
- 在手机上开发程序之AIDE(Android集成开发环境)介绍
- Android(安卓)4.0的图形硬件加速及绘制技巧
- 应用程序基础(Application Fundamentals)
- [原]Android应用程序发送广播(sendBroadcast)的过程分析
- Android存储之SharePreferences