@Tyhj
        
        2017-04-21T06:40:57.000000Z
        字数 6272
        阅读 1990
    Android
原文:https://www.zybuluo.com/Tyhj/note/730409 
直播、视频聊天这种东西遍地都是,但是实现起来真的不是很容易。最近养了一只狗,想试试用一台手机一直拍摄,另一只手机来接收视频。 
首先找了找怎么实现,并没有找到完整的教程。但是想法有了,就是先打开手机摄像头,然后获取摄像头的每一帧的数据,然后发送给服务器,服务器再给另一台手机,手机收到视频后再显示到SurfaceView上面。逻辑应该是没有问题的,我试了也可以,就是数据传输有问题,但是我一个搞Android的服务器菜一点就菜一点,先实现了,优化再谈。
首先打开手机摄像头,将摄像头的数据展示到SurfaceView上面。
void initCamera(boolean what){try {int position=0;int rotate=0;if(what) {//what就是指摄像头的正反position = 1;//选择打开的摄像头rotate=180;//这里就是让显示的画面旋转一下}destroyCamera();camera=Camera.open(position);camera.setPreviewDisplay(mSurfaceHolder);Camera.Parameters parameter=camera.getParameters();parameter.setPreviewFormat(ImageFormat.NV21);List<Camera.Size> sizeList=parameter.getSupportedPreviewSizes();for(int i=0;i<sizeList.size();i++){Log.e("支持的尺寸大小","x:"+sizeList.get(i).width+" y"+sizeList.get(i).height);}parameter.setPreviewSize(320,240);//设置预览方向camera.setDisplayOrientation(90);//设置拍照之后图片方向parameter.setRotation(rotate);camera.setParameters(parameter);camera.setPreviewCallback(back);camera.startPreview();} catch (IOException e) {e.printStackTrace();}}
//这里是处理每一帧的图象然后发送出去,可以看见在这里可以获取到每一帧的bitmap,干很多事情,什么美颜呀,水印呀都可以玩,然后我是拿到数据直接转化为Base64字符串发送出去的,这样应该是很蠢的,但是技术有限,加上我之前有个写好的后台程序就可以发字符串,就将就一下。
//处理每一帧Camera.PreviewCallback back=new Camera.PreviewCallback() {@Overridepublic void onPreviewFrame(byte[] data, Camera camera) {if(i/30!=0){return;}Camera.Size size=camera.getParameters().getPreviewSize();YuvImage image=new YuvImage(data,ImageFormat.NV21,size.width,size.height,null);Bitmap bitmap=null;String string=null;if(image!=null){try {ByteArrayOutputStream stream=new ByteArrayOutputStream();image.compressToJpeg(new Rect(0,0,size.width,size.height),20,stream);//bitmap= BitmapFactory.decodeByteArray(stream.toByteArray(),0,stream.size());//加水印//bitmap= BitmapUtils.addLogo(MainActivity.this,bitmap,"小萨专用",R.drawable.watermark);byte[] bytes = stream.toByteArray();string = Base64.encodeToString(bytes, Base64.NO_WRAP);string.replace("\n","小萨");sendMsg(string);stream.flush();stream.close();} catch (Exception e) {e.printStackTrace();}}}};
当切换摄像头的时候要先关闭摄像头
private void destroyCamera(){if(camera!=null) {camera.setPreviewCallback(null);camera.stopPreview();camera.release();camera = null;}}
还有其他一些问题:
对焦后拍照:
void fab(){camera.autoFocus(new Camera.AutoFocusCallback() {@Overridepublic void onAutoFocus(boolean success, Camera camera) {if(success){camera.takePicture(null,null,JpgmPicture);}}});}//保存拍摄的图片private Camera.PictureCallback JpgmPicture=new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {File file=getOutputMediaFile();if(file==null)return;try {FileOutputStream fos=new FileOutputStream(file);fos.write(data);fos.close();} catch (IOException e) {e.printStackTrace();}camera.startPreview();}};//创建一个文件来保存拍摄的图片private static File getOutputMediaFile(){// To be safe, you should check that the SDCard is mounted// using Environment.getExternalStorageState() before doing this.File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");// This location works best if you want the created images to be shared// between applications and persist after your app has been uninstalled.// Create the storage directory if it does not existif (! mediaStorageDir.exists()){if (! mediaStorageDir.mkdirs()){Log.d("MyCameraApp", "failed to create directory");return null;}}// Create a media file nameString timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());File mediaFile;mediaFile = new File(mediaStorageDir.getPath() + File.separator +"IMG_"+ timeStamp + ".jpg");return mediaFile;}
添加水印
//添加水印public static Bitmap addLogo(Context context, Bitmap bitmap, String markText, int markBitmapId){// 当水印文字与水印图片都没有的时候,返回原图if (TextUtils.isEmpty(markText) && markBitmapId == 0) {return bitmap;}// 获取图片的宽高int bitmapWidth = bitmap.getWidth();int bitmapHeight = bitmap.getHeight();// 创建一个和图片一样大的背景图Bitmap bmp = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(bmp);// 画背景图canvas.drawBitmap(bitmap, 0, 0, null);//-------------开始绘制文字-------------------------------// 文字开始的坐标,默认为左上角float textX = 0;float textY = 0;if (!TextUtils.isEmpty(markText)) {// 创建画笔Paint mPaint = new Paint();// 文字矩阵区域Rect textBounds = new Rect();// 获取屏幕的密度,用于设置文本大小//float scale = context.getResources().getDisplayMetrics().density;// 水印的字体大小//mPaint.setTextSize((int) (11 * scale));mPaint.setTextSize(20);// 文字阴影mPaint.setShadowLayer(0.5f, 0f, 1f, Color.BLACK);// 抗锯齿mPaint.setAntiAlias(true);// 水印的区域mPaint.getTextBounds(markText, 0, markText.length(), textBounds);// 水印的颜色mPaint.setColor(Color.WHITE);//当图片大小小于文字水印大小的3倍的时候,不绘制水印if (textBounds.width() > bitmapWidth / 3 || textBounds.height() > bitmapHeight / 3) {return bitmap;}// 文字开始的坐标textX = bitmapWidth - textBounds.width() - 10;//这里的-10和下面的+6都是微调的结果textY = bitmapHeight - textBounds.height() + 6;// 画文字canvas.drawText(markText, textX, textY, mPaint);}//------------开始绘制图片-------------------------if (markBitmapId != 0) {// 载入水印图片Bitmap markBitmap = BitmapFactory.decodeResource(context.getResources(), markBitmapId);// 如果图片的大小小于水印的3倍,就不添加水印if (markBitmap.getWidth() > bitmapWidth / 3 || markBitmap.getHeight() > bitmapHeight / 3) {return bitmap;}int markBitmapWidth = markBitmap.getWidth();int markBitmapHeight = markBitmap.getHeight();// 图片开始的坐标float bitmapX = (float) (bitmapWidth - markBitmapWidth - 10);//这里的-10和下面的-20都是微调的结果float bitmapY = (float) (textY - markBitmapHeight - 20);// 画图canvas.drawBitmap(markBitmap, bitmapX, bitmapY, null);}//保存所有元素canvas.save(Canvas.ALL_SAVE_FLAG);canvas.restore();return bmp;}
然后那个接收视频信息
//收到信息后展示public void sendBordcast(String messge) {Log.e("长度:", messge.length() + "");Bitmap bitmap = stringtoBitmap(messge);if (bitmap == null)return;int orientation = bitmap.getHeight() < bitmap.getWidth() ? 90 : 0;Canvas canvas = mSurfaceHolder.lockCanvas();if (orientation != 0){Matrix matrix = new Matrix();matrix.postRotate(orientation);bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, true);}canvas.drawBitmap(bitmap, 0, 0, new Paint());mSurfaceHolder.unlockCanvasAndPost(canvas);}//把我发送的字符串转成bitmappublic Bitmap stringtoBitmap(String string) {string.replace("小萨", "\n");// 将字符串转换成Bitmap类型Bitmap bitmap = null;try {byte[] bitmapArray = null;bitmapArray = Base64.decode(string, Base64.NO_WRAP);bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0,bitmapArray.length);} catch (Exception e) {e.printStackTrace();}return bitmap;}
基本上就是这样子,然后数据传输肯定很菜的,要优化,我也把图象数据压缩了,压缩方法也是最垃圾的,然后获取的图象我也是设置成了最小尺寸,还不是获取每一帧的数据,故意丢了一些数据。这样的话我的那个学生优惠的阿里服务器也可以带的动,也没什么问题,延迟有一点。就是图片实在是太小了。对了,声音也是没有的。有时间再改。
求建议,tyhj5@qq.com