[关闭]
@Tyhj 2017-04-21T14:40:57.000000Z 字数 6272 阅读 1720

初探Android平台直播

Android


原文:https://www.zybuluo.com/Tyhj/note/730409
直播、视频聊天这种东西遍地都是,但是实现起来真的不是很容易。最近养了一只狗,想试试用一台手机一直拍摄,另一只手机来接收视频。
首先找了找怎么实现,并没有找到完整的教程。但是想法有了,就是先打开手机摄像头,然后获取摄像头的每一帧的数据,然后发送给服务器,服务器再给另一台手机,手机收到视频后再显示到SurfaceView上面。逻辑应该是没有问题的,我试了也可以,就是数据传输有问题,但是我一个搞Android的服务器菜一点就菜一点,先实现了,优化再谈。

首先打开手机摄像头,将摄像头的数据展示到SurfaceView上面。

  1. void initCamera(boolean what){
  2. try {
  3. int position=0;
  4. int rotate=0;
  5. if(what) {//what就是指摄像头的正反
  6. position = 1;//选择打开的摄像头
  7. rotate=180;//这里就是让显示的画面旋转一下
  8. }
  9. destroyCamera();
  10. camera=Camera.open(position);
  11. camera.setPreviewDisplay(mSurfaceHolder);
  12. Camera.Parameters parameter=camera.getParameters();
  13. parameter.setPreviewFormat(ImageFormat.NV21);
  14. List<Camera.Size> sizeList=parameter.getSupportedPreviewSizes();
  15. for(int i=0;i<sizeList.size();i++){
  16. Log.e("支持的尺寸大小","x:"+sizeList.get(i).width+" y"+sizeList.get(i).height);
  17. }
  18. parameter.setPreviewSize(320,240);
  19. //设置预览方向
  20. camera.setDisplayOrientation(90);
  21. //设置拍照之后图片方向
  22. parameter.setRotation(rotate);
  23. camera.setParameters(parameter);
  24. camera.setPreviewCallback(back);
  25. camera.startPreview();
  26. } catch (IOException e) {
  27. e.printStackTrace();
  28. }
  29. }

//这里是处理每一帧的图象然后发送出去,可以看见在这里可以获取到每一帧的bitmap,干很多事情,什么美颜呀,水印呀都可以玩,然后我是拿到数据直接转化为Base64字符串发送出去的,这样应该是很蠢的,但是技术有限,加上我之前有个写好的后台程序就可以发字符串,就将就一下。

  1. //处理每一帧
  2. Camera.PreviewCallback back=new Camera.PreviewCallback() {
  3. @Override
  4. public void onPreviewFrame(byte[] data, Camera camera) {
  5. if(i/30!=0){
  6. return;
  7. }
  8. Camera.Size size=camera.getParameters().getPreviewSize();
  9. YuvImage image=new YuvImage(data,ImageFormat.NV21,size.width,size.height,null);
  10. Bitmap bitmap=null;
  11. String string=null;
  12. if(image!=null){
  13. try {
  14. ByteArrayOutputStream stream=new ByteArrayOutputStream();
  15. image.compressToJpeg(new Rect(0,0,size.width,size.height),20,stream);
  16. //bitmap= BitmapFactory.decodeByteArray(stream.toByteArray(),0,stream.size());
  17. //加水印
  18. //bitmap= BitmapUtils.addLogo(MainActivity.this,bitmap,"小萨专用",R.drawable.watermark);
  19. byte[] bytes = stream.toByteArray();
  20. string = Base64.encodeToString(bytes, Base64.NO_WRAP);
  21. string.replace("\n","小萨");
  22. sendMsg(string);
  23. stream.flush();
  24. stream.close();
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }
  30. };

当切换摄像头的时候要先关闭摄像头

  1. private void destroyCamera(){
  2. if(camera!=null) {
  3. camera.setPreviewCallback(null);
  4. camera.stopPreview();
  5. camera.release();
  6. camera = null;
  7. }
  8. }

还有其他一些问题:

对焦后拍照:

  1. void fab(){
  2. camera.autoFocus(new Camera.AutoFocusCallback() {
  3. @Override
  4. public void onAutoFocus(boolean success, Camera camera) {
  5. if(success){
  6. camera.takePicture(null,null,JpgmPicture);
  7. }
  8. }
  9. });
  10. }
  11. //保存拍摄的图片
  12. private Camera.PictureCallback JpgmPicture=new Camera.PictureCallback() {
  13. @Override
  14. public void onPictureTaken(byte[] data, Camera camera) {
  15. File file=getOutputMediaFile();
  16. if(file==null)
  17. return;
  18. try {
  19. FileOutputStream fos=new FileOutputStream(file);
  20. fos.write(data);
  21. fos.close();
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. camera.startPreview();
  26. }
  27. };
  28. //创建一个文件来保存拍摄的图片
  29. private static File getOutputMediaFile(){
  30. // To be safe, you should check that the SDCard is mounted
  31. // using Environment.getExternalStorageState() before doing this.
  32. File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
  33. Environment.DIRECTORY_PICTURES), "MyCameraApp");
  34. // This location works best if you want the created images to be shared
  35. // between applications and persist after your app has been uninstalled.
  36. // Create the storage directory if it does not exist
  37. if (! mediaStorageDir.exists()){
  38. if (! mediaStorageDir.mkdirs()){
  39. Log.d("MyCameraApp", "failed to create directory");
  40. return null;
  41. }
  42. }
  43. // Create a media file name
  44. String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
  45. File mediaFile;
  46. mediaFile = new File(mediaStorageDir.getPath() + File.separator +
  47. "IMG_"+ timeStamp + ".jpg");
  48. return mediaFile;
  49. }

添加水印

  1. //添加水印
  2. public static Bitmap addLogo(Context context, Bitmap bitmap, String markText, int markBitmapId){
  3. // 当水印文字与水印图片都没有的时候,返回原图
  4. if (TextUtils.isEmpty(markText) && markBitmapId == 0) {
  5. return bitmap;
  6. }
  7. // 获取图片的宽高
  8. int bitmapWidth = bitmap.getWidth();
  9. int bitmapHeight = bitmap.getHeight();
  10. // 创建一个和图片一样大的背景图
  11. Bitmap bmp = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
  12. Canvas canvas = new Canvas(bmp);
  13. // 画背景图
  14. canvas.drawBitmap(bitmap, 0, 0, null);
  15. //-------------开始绘制文字-------------------------------
  16. // 文字开始的坐标,默认为左上角
  17. float textX = 0;
  18. float textY = 0;
  19. if (!TextUtils.isEmpty(markText)) {
  20. // 创建画笔
  21. Paint mPaint = new Paint();
  22. // 文字矩阵区域
  23. Rect textBounds = new Rect();
  24. // 获取屏幕的密度,用于设置文本大小
  25. //float scale = context.getResources().getDisplayMetrics().density;
  26. // 水印的字体大小
  27. //mPaint.setTextSize((int) (11 * scale));
  28. mPaint.setTextSize(20);
  29. // 文字阴影
  30. mPaint.setShadowLayer(0.5f, 0f, 1f, Color.BLACK);
  31. // 抗锯齿
  32. mPaint.setAntiAlias(true);
  33. // 水印的区域
  34. mPaint.getTextBounds(markText, 0, markText.length(), textBounds);
  35. // 水印的颜色
  36. mPaint.setColor(Color.WHITE);
  37. //当图片大小小于文字水印大小的3倍的时候,不绘制水印
  38. if (textBounds.width() > bitmapWidth / 3 || textBounds.height() > bitmapHeight / 3) {
  39. return bitmap;
  40. }
  41. // 文字开始的坐标
  42. textX = bitmapWidth - textBounds.width() - 10;//这里的-10和下面的+6都是微调的结果
  43. textY = bitmapHeight - textBounds.height() + 6;
  44. // 画文字
  45. canvas.drawText(markText, textX, textY, mPaint);
  46. }
  47. //------------开始绘制图片-------------------------
  48. if (markBitmapId != 0) {
  49. // 载入水印图片
  50. Bitmap markBitmap = BitmapFactory.decodeResource(context.getResources(), markBitmapId);
  51. // 如果图片的大小小于水印的3倍,就不添加水印
  52. if (markBitmap.getWidth() > bitmapWidth / 3 || markBitmap.getHeight() > bitmapHeight / 3) {
  53. return bitmap;
  54. }
  55. int markBitmapWidth = markBitmap.getWidth();
  56. int markBitmapHeight = markBitmap.getHeight();
  57. // 图片开始的坐标
  58. float bitmapX = (float) (bitmapWidth - markBitmapWidth - 10);//这里的-10和下面的-20都是微调的结果
  59. float bitmapY = (float) (textY - markBitmapHeight - 20);
  60. // 画图
  61. canvas.drawBitmap(markBitmap, bitmapX, bitmapY, null);
  62. }
  63. //保存所有元素
  64. canvas.save(Canvas.ALL_SAVE_FLAG);
  65. canvas.restore();
  66. return bmp;
  67. }

然后那个接收视频信息

  1. //收到信息后展示
  2. public void sendBordcast(String messge) {
  3. Log.e("长度:", messge.length() + "");
  4. Bitmap bitmap = stringtoBitmap(messge);
  5. if (bitmap == null)
  6. return;
  7. int orientation = bitmap.getHeight() < bitmap.getWidth() ? 90 : 0;
  8. Canvas canvas = mSurfaceHolder.lockCanvas();
  9. if (orientation != 0){
  10. Matrix matrix = new Matrix();
  11. matrix.postRotate(orientation);
  12. bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, true);
  13. }
  14. canvas.drawBitmap(bitmap, 0, 0, new Paint());
  15. mSurfaceHolder.unlockCanvasAndPost(canvas);
  16. }
  17. //把我发送的字符串转成bitmap
  18. public Bitmap stringtoBitmap(String string) {
  19. string.replace("小萨", "\n");
  20. // 将字符串转换成Bitmap类型
  21. Bitmap bitmap = null;
  22. try {
  23. byte[] bitmapArray = null;
  24. bitmapArray = Base64.decode(string, Base64.NO_WRAP);
  25. bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0,
  26. bitmapArray.length);
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. return bitmap;
  31. }

基本上就是这样子,然后数据传输肯定很菜的,要优化,我也把图象数据压缩了,压缩方法也是最垃圾的,然后获取的图象我也是设置成了最小尺寸,还不是获取每一帧的数据,故意丢了一些数据。这样的话我的那个学生优惠的阿里服务器也可以带的动,也没什么问题,延迟有一点。就是图片实在是太小了。对了,声音也是没有的。有时间再改。

求建议,tyhj5@qq.com

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注