2.服务端通过Camera的setPreviewCallback方法回调采集摄像头视频数据,将一张张图片数据数据压缩,发送到客户端,因为是图片,需要在图片开头加个标记,然后指定数据长度发送。
public class MainActivity extends Activity {
TextView tv;
SurfaceView surfv;
SurfaceHolder surfaceHolder;
int screenWidth=300, screenHeight=300;
public Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case CamConstant.INIT_CAMERA:
/*if(CamConstant.hadInit){
return;
}
Camera camera = CameraUtil.initCamera(surfaceHolder);
if( null != camera){
CamConstant.hadInit = true;
}*/
break;
case CamConstant.RECYCLE_CAMERA:
CameraUtil.recycleCamera();
CamConstant.hadInit = false;
break;
default:
//tv.setText((CharSequence) msg.obj);
tv.append((CharSequence) msg.obj+"\n");
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
super.onCreate(savedInstanceState);
MainApplication.mHandler = this.mHandler;
tv = (TextView) findViewById(R.id.textView);
surfv = (SurfaceView) findViewById(R.id.surview);
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
screenWidth = dm.widthPixels;// 获取屏幕分辨率宽度
screenHeight = dm.heightPixels;
surfaceHolder = surfv.getHolder();
surfaceHolder.setFixedSize(screenWidth, screenHeight/4*2);
surfaceHolder.addCallback(new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
CameraUtil.initCamera(surfaceHolder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
});
//tv.setMovementMethod(ScrollingMovementMethod.getInstance());
startService();
};
public void startService(){
Intent intent = new Intent(MainActivity.this, CamThreadService.class);
startService(intent);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
CameraUtil.recycleCamera();
CamConstant.hadInit = false;
if(ServiceUtil.isServiceRunning(this, "CamService")){
stopService(new Intent(this, CamService.class));
}
super.onDestroy();
}
}
public class CamThreadService extends Service {
Socket mClintSocket;
ServerSocket serverSocket;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
L.e("启动11111");
// TODO Auto-generated method stub
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
L.e("accept000");
sendMSG("accept1111");
serverSocket = new ServerSocket(CamConstant.Camera_Port);
while(true){
L.e("accept0001111");
L.e("accept11111");
sendMSG("accept222");
mClintSocket = serverSocket.accept();
L.e("accept22222");
L.e("accept3333");
new Thread(){
public void run() {
if(mClintSocket != null){
sendMSG("accept3333");
//初始化
//MainApplication.mHandler.sendEmptyMessage(CamConstant.INIT_CAMERA);
L.e("accept4444");
boolean flag = true;
while(flag){
if(CameraUtil.mCamera != null){
try {
CameraUtil.mCamera.setPreviewCallback(new StreamIt(mClintSocket));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
flag = false;
mClintSocket = null;
}
}
}
};
}.start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
L.e("之行报错 停止");
e.printStackTrace();
}
}
}).start();
super.onCreate();
}
/**
* 视频流数据相关回调
*/
private class StreamIt implements PreviewCallback {
private OutputStream out;
private InputStream in;
private int times = 0;
public StreamIt(Socket s) throws IOException {
// TODO Auto-generated constructor stub
this.out = s.getOutputStream();
this.in = s.getInputStream();
sendMSG("压缩后的数据11");
int[] textures = new int[1];
L.e("压缩后的数据1");
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
// TODO Auto-generated method stub
Size size = camera.getParameters().getPreviewSize();
try {
L.e("压缩后的数据1.5");
if(times == 0){
YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);
if(image != null){
//这是源数据
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, size.width, size.height), 100, outputStream);
byte[] srcData = outputStream.toByteArray();
int len = srcData.length;
Bitmap src = BitmapFactory.decodeByteArray(srcData, 0, len);
//压缩后的数据
byte[] outdata = transImage(src, 640, 480);
L.e("压缩后的数据2");
sendMSG("压缩后的数据22");
int datalen = outdata.length;
out.write((byte) 0xA0);
out.write(intTOBytes(datalen));
out.write(outdata, 0, datalen);
if(!src.isRecycled()){
src.recycle();
}
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
/**
* 数据转换,将bitmap转换为byte
*/
private byte[] transImage(Bitmap bitmap, int width, int height){
try{
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
//缩放图片的尺寸
float scaleWidth = (float) width/bitmapWidth;
float scaleHeight = (float) height/bitmapHeight;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
//产生缩放后的Bitmap对象
Bitmap resizeBitemp = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, false);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
resizeBitemp.compress(CompressFormat.JPEG, 85, outputStream);
byte[] byteArray = outputStream.toByteArray();
outputStream.close();
if(!bitmap.isRecycled()){
bitmap.recycle();
}
if(!resizeBitemp.isRecycled()){
resizeBitemp.recycle();
}
return byteArray;
}catch(Exception ex){
ex.printStackTrace();
MainApplication.mHandler.sendEmptyMessage(CamConstant.RECYCLE_CAMERA);
}
return null;
}
/**
* 将int 数值转换为4个字节
*/
private byte[] intTOBytes(int value){
Log.e("cam",value+"d");
byte[] src = new byte[4];
src[3] = (byte) ((value >> 24) & 0xFF);
src[2] = (byte)((value >> 16) & 0xFF);
src[1] = (byte)((value >> 8) & 0xFF);
src[0] = (byte)(value & 0xFF);
return src;
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
try {
if(mClintSocket !=null){
mClintSocket.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MainApplication.mHandler.sendEmptyMessage(CamConstant.RECYCLE_CAMERA);
super.onDestroy();
}
public void sendMSG(String text){
Message msg = new Message();
msg.what = 908;
msg.obj = text;
MainApplication.mHandler.sendMessage(msg);
}
}
客户端代码:
public class MainActivity extends Activity {
RevImageThread revImageThread;
public static ImageView image;
private static TextView textView1;
private static Bitmap bitmap;
private static final int COMPLETED = 0x111;
private MyHandler handler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image=(ImageView)findViewById(R.id.imageView1);
textView1 = (TextView) findViewById(R.id.textView1);
textView1.setMovementMethod(ScrollingMovementMethod.getInstance());
handler = new MyHandler();
revImageThread = new RevImageThread(handler);
new Thread(revImageThread).start();
}
static class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){
if(msg.obj != null){
textView1.append(msg.obj.toString()+"\n");
}
if (msg.what == COMPLETED) {
bitmap = (Bitmap)msg.obj;
image.setImageBitmap(bitmap);
super.handleMessage(msg);
}
}
}
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
public class RevImageThread implements Runnable {
public Socket s;
public ServerSocket ss;
//向UI线程发送消息
private Handler handler;
private Bitmap bitmap;
private static final int COMPLETED = 0x111;
public RevImageThread(Handler handler){
this.handler = handler;
}
public void run()
{
try {
ss = new ServerSocket(ScoketIPandPort.SOCKTPORT);
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
while(true){
try {
s = ss.accept();
Log.e("strat","ljq");
new Thread(){
public void run() {
try {
byte [] buffer = new byte[1024];
InputStream ins = null;
ins = s.getInputStream();
Log.v("socket", "socketcc1111");
if(s != null){
Log.v("socket", "socketcc2222");
while(true){
int len = 0;
int i = 0;
int j = 0;
boolean flag = true;
/*while(flag){
i += ins.read();
Log.e("socket", ":"+i + " : " + j++);
}
*/
if(ins.read() == 0xA0){
byte[] src = new byte[4];
len = ins.read(src);
Log.v("sck", "src3:"+src[3]);
Log.v("sck", "src2:"+src[2]);
Log.v("sck", "src1:"+src[1]);
Log.v("sck", "src0:"+src[0]);
Log.v("sck", "src:"+src);
Log.v("socket", "socketcc55551:"+len);
Log.v("socket", "socketcc55552:"+src);
len = bytesToInt(src, 0);
Log.v("socket", "socketcc55553:"+src);
Log.v("socket", "socketcc55554:"+len);
//len = 4000000;
byte[] srcData = new byte[len];
sendMSG(len+":len");
int readc = 0;
ins.read(srcData, readc, len);
Log.v("socket", "srcData0:"+srcData[0]);
sendMSG("srcData0:"+srcData[0]);
Log.v("socket", "srcData0:"+srcData[1]);
Log.v("socket", "srcData0:"+srcData[2]);
Log.v("socket", "srcData0:"+srcData[3]);
Log.v("socket", "srcData0:"+srcData[4]);
Log.v("socket", "srcData0:"+srcData[5]);
Log.v("socket", "srcData0:"+srcData[6]);
Log.v("socket", "srcData0:"+srcData[7]);
Log.v("socket", "srcData0:"+srcData[len-1]);
sendMSG(srcData[len-1]+"L");
bitmap = BitmapFactory.decodeByteArray(srcData, 0, len);
Message msg =handler.obtainMessage();
msg.what = COMPLETED;
msg.obj = bitmap;
handler.removeMessages(COMPLETED);
handler.sendMessage(msg);
}
//Message msg = new Message();
/* ByteArrayOutputStream outStream = new ByteArrayOutputStream();
while( (len=ins.read(buffer)) != -1){
outStream.write(buffer, 0, len);
}
ins.close();byte data[] = outStream.toByteArray();
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
*/
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
sendMSG(e.toString()+"bao cuo " +e.getMessage());
}
};
}.start();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
/* if(!s.isClosed()){
s.close();
}*/
//Bitmap bitmap = BitmapFactory.decodeStream(ins);
}
}
public int bytesToInt(byte[] src, int offset){
int value;
value = (int)((src[offset] & 0xFF))
| ((src[offset+1] & 0xFF)<<8)
| ((src[offset+2] & 0xFF)<<16)
| ((src[offset+3] & 0xFF)<<24);
return value;
}
public int bytesToInt2(byte[] src, int offset){
int value;
value = (int)((src[offset] & 0xFF<<24)
| ((src[offset+1] & 0xFF)<<16)
| ((src[offset+2] & 0xFF)<<8)
| (src[offset+3] & 0xFF));
return value;
}
/* public static int bytesToInt2(byte[] src, int offset) {
int value;
value = (int) ( ((src[offset] & 0xFF)<<24)
|((src[offset+1] & 0xFF)<<16)
|((src[offset+2] & 0xFF)<<8)
|(src[offset+3] & 0xFF));
return value;
} */
public void sendMSG(String text){
Message msg = new Message();
msg.what = 908;
msg.obj = text;
handler.sendMessage(msg);
}
}
该代码也只做参考作用,代码中有漏洞,需要理解代码之后才能解决
DEMO源码
摄像头MediaCodec 编解码
MediaCodec编解码手机投屏功能的实现