Android 拍摄 录制

# Android 拍摄 录制

Android 移动开发 拍摄照片 录制视频页面 分两大块 如果业务不是很复杂 对拍摄录制要求没有定制化 ps: 4:3 16:9 照片质量 720P 1080P 不需要实现自己的拍照逻辑 只要借助 Android 原生的 相机 实现拍照录像即可 如果实现定制化开发 ps:照片尺寸 质量 连拍?等 则需要自行开发

# 申请必要的权限

Android 6.0以上 相机权限 录制权限 需要自行申请 这里不再讲明方法

  <uses-feature android:name="android.hardware.camera" />

  <uses-feature android:name="android.hardware.camera.autofocus" />

  <uses-permission android:name="android.permission.CAMERA" />

  <uses-permission android:name="android.permission.RECORD_AUDIO" />
  
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

# 调用系统相机->拍摄照片

   try {
                //检测相机 权限
                CameraPermissionsUtils.checkCameraPermissions();
                //设置拍摄照片保存位置
                File file = getStorgeFile();
                imgSavePath = file.getPath();
            
                Intent intentFromCapture = new Intent(
                        MediaStore.ACTION_IMAGE_CAPTURE);

                 //添加这一句表示对目标应用临时授权该Uri所代表的文件
                intentFromCapture.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
              
                intentFromCapture.putExtra(MediaStore.EXTRA_OUTPUT,getUriForFile(getActivity(), file));
                startActivityForResult(intentFromCapture,
                        FLAG_CAMERA);
       } catch (Exception e) {
          ToastUtils.shortToastMessage(BaseApplication.getContext(), "请检查下您是否禁用了照相权限!");
          //执行申请 权限 代码 ...
       }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                //相机拍摄
                case FLAG_CAMERA:
                    File file = new File(imgSavePath);
                    //裁剪 拍摄的照片
                    cropImageUri(getUriForFile(getActivity(), file), 800, 800, FLAG_CLIP, FLAG_CAMERA);
                    break;
            }
        }
    }

# 调用系统相机->录制视频

static final int REQUEST_VIDEO_CAPTURE = 1;

private void dispatchTakeVideoIntent() {
    Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
        Uri videoUri = intent.getData();
        videoView.setVideoURI(videoUri);
    }
}

# 自定义拍摄 录制

  • 设置Camera 参数
  • 初始化 SurfaceView
  • MediaRecorder 参数设置

# 拍摄 录制流程图

camera_flow.png

# 设置相机参数

预览比例 保存比例 本机是否支持 该比例?
举个栗子:预览图片希望是4:3 拍照保存也是4:3 手机的屏幕却是 16:9 或者 18:9
如何设置?

  • 按屏幕宽设置 预览View的高

  • 按照比例设置 预览View的宽高 screenHeight = screenWith/3*4

  • 按照预览的比例去轮询 相机支持的参数 设置宽高比

      /**
       * doStartPreview
       * @param holder
       * @param screenProp H/W
       */
      public void doStartPreview(SurfaceHolder holder, float screenProp) {
          if (isPreviewing) {
              LogUtil.i("doStartPreview isPreviewing");
          }
          if (this.screenProp < 0) {
              this.screenProp = screenProp;
          }
          if (holder == null) {
              return;
          }
          this.mHolder = holder;
          if (mCamera != null) {
              try {
                  mParams = mCamera.getParameters();
    
                  mParams.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                  //闪关灯配置
                  //自动模式,当光线较暗时自动打开闪光灯;
                  mParams.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
                  //对焦模式
                  //自动模式
                  mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_MACRO);
    
                
                  mParams = mCamera.getParameters();
                  Camera.Size previewSize = CameraParamUtil.getInstance().getPreviewSize(mParams
                          .getSupportedPreviewSizes(), DisplayUtils.getScreenMetrics(BaseApp.getContext()).x, screenProp);
                  Camera.Size pictureSize = CameraParamUtil.getInstance().getPictureSize(mParams
                          .getSupportedPictureSizes(), DisplayUtils.getScreenMetrics(BaseApp.getContext()).x, screenProp);
                  mParams.setPreviewSize(previewSize.width, previewSize.height);
                  preview_width = previewSize.width;
                  preview_height = previewSize.height;
                  mParams.setPictureSize(pictureSize.width, pictureSize.height);
    
                  //设置聚焦方式
                  if (CameraParamUtil.getInstance().isSupportedFocusMode(
                          mParams.getSupportedFocusModes(), android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                      mParams.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                  }
                  // 设置图片保存
                  if (CameraParamUtil.getInstance().isSupportedPictureFormats(mParams.getSupportedPictureFormats(),
                          ImageFormat.JPEG)) {
                      mParams.setPictureFormat(ImageFormat.JPEG);
                      mParams.setJpegQuality(100);
                  }
    
                  mCamera.setParameters(mParams);
                  mParams = mCamera.getParameters();
                  mCamera.setPreviewDisplay(holder);  //SurfaceView
                  mCamera.setDisplayOrientation(cameraAngle);//浏览角度
                  mCamera.setPreviewCallback(this); //每一帧回调
                  mCamera.startPreview();//启动浏览
                  isPreviewing = true;
                  Log.i(TAG, "=== Start Preview ===");
              } catch (Exception e) {
                  e.printStackTrace();
              }
              }
          }
    
  • 按照比例 查询最优 尺寸

       public Camera.Size getPreviewSize(List<Camera.Size> list, int th, float rate) {
          Collections.sort(list, sizeComparator);
          int i = 0;
          for (Camera.Size s : list) {
              if ((s.width > th) && equalRate(s, rate)) {
                  Log.i(TAG, "MakeSure Preview :w = " + s.width + " h = " + s.height);
                  break;
              }
              i++;
          }
          if (i == list.size()) {
              return getBestSize(list, rate);
          } else {
              return list.get(i);
          }
      }
      
      //排序
      private class CameraSizeComparator implements Comparator<Camera.Size> {
                   public int compare(Camera.Size lhs, Camera.Size rhs) {
              if (lhs.width == rhs.width) {
                  return 0;
              } else if (lhs.width > rhs.width) {
                  return 1;
              } else {
                  return -1;
              }
          }
      }
    
      //换算 比例差别 
      //如果差别 上下不超过 0.2 则为最佳尺寸 
      private boolean equalRate(Camera.Size s, float rate) {
              float r = (float) (s.width) / (float) (s.height);
              return Math.abs(r - rate) <= 0.2;
       }
    

# 拍照方法 很简单这里不再描述

/**
 * Equivalent to <pre>takePicture(Shutter, raw, null, jpeg)</pre>.
 *
 * @see #takePicture(ShutterCallback, PictureCallback, PictureCallback, PictureCallback)
 */
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
        PictureCallback jpeg) {
    takePicture(shutter, raw, null, jpeg);
}

# 录制

# 设置录制参数:

//启动录像
public void startRecord(Surface surface, float screenProp, ErrorCallback callback) {
    mCamera.setPreviewCallback(null);
    final int nowAngle = (angle + 90) % 360;
    //获取第一帧图片
    Camera.Parameters parameters = mCamera.getParameters();
    int width = parameters.getPreviewSize().width;
    int height = parameters.getPreviewSize().height;
    YuvImage yuv = new YuvImage(firstFrame_data, parameters.getPreviewFormat(), width, height, null);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    yuv.compressToJpeg(new Rect(0, 0, width, height), 50, out);
    byte[] bytes = out.toByteArray();
    videoFirstFrame = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    Matrix matrix = new Matrix();
    if (SELECTED_CAMERA == CAMERA_POST_POSITION) {
        matrix.setRotate(nowAngle);
    } else if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
        matrix.setRotate(270);
    }
    videoFirstFrame = createBitmap(videoFirstFrame, 0, 0, videoFirstFrame.getWidth(), videoFirstFrame
            .getHeight(), matrix, true);

    if (isRecorder) {
        return;
    }
    if (mCamera == null) {
        openCamera(SELECTED_CAMERA);
    }
    if (mediaRecorder == null) {
        mediaRecorder = new MediaRecorder();
    }
    if (mParams == null) {
        mParams = mCamera.getParameters();
    }
    List<String> focusModes = mParams.getSupportedFocusModes();
    if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
        mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
    }
    mCamera.setParameters(mParams);
    mCamera.unlock();
    mediaRecorder.reset();
    mediaRecorder.setCamera(mCamera);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); //视频采集来源

    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //输出格式
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); //编码格式
    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); //音频模式

    //设置视频输出 尺寸比例
    Camera.Size videoSize;
    if (mParams.getSupportedVideoSizes() == null) {
        videoSize = CameraParamUtil.getInstance().getPreviewSize(mParams.getSupportedPreviewSizes(),
                DisplayUtils.getScreenMetrics(BaseApp.getContext()).x, screenProp);
    } else {
        //            videoSize = CameraParamUtil.getInstance().getPreviewSize(mParams.getSupportedVideoSizes(), 600,
        //                    screenProp);
        videoSize = CamParaUtil.getInstance().getOptimalSize(mParams.getSupportedVideoSizes(),
                DisplayUtils.getScreenMetrics(BaseApp.getContext()).x, DisplayUtils.getScreenMetrics(BaseApp.getContext()).y);
    }
    Log.i(TAG, "setVideoSize    width = " + videoSize.width + "height = " + videoSize.height);
    if (videoSize.width == videoSize.height) {
        mediaRecorder.setVideoSize(preview_width, preview_height);
    } else {
        mediaRecorder.setVideoSize(videoSize.width, videoSize.height);
    }

    //        if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
    //            mediaRecorder.setOrientationHint(270);
    //        } else {
    //            mediaRecorder.setOrientationHint(nowAngle);
    ////            mediaRecorder.setOrientationHint(90);
    //        }
    if (SELECTED_CAMERA == CAMERA_FRONT_POSITION) {
        //手机预览倒立的处理
        if (cameraAngle == 270) {
            //横屏
            if (nowAngle == 0) {
                mediaRecorder.setOrientationHint(180);
            } else if (nowAngle == 270) {
                mediaRecorder.setOrientationHint(270);
            } else {
                mediaRecorder.setOrientationHint(90);
            }
        } else {
            if (nowAngle == 90) {
                mediaRecorder.setOrientationHint(270);
            } else if (nowAngle == 270) {
                mediaRecorder.setOrientationHint(90);
            } else {
                mediaRecorder.setOrientationHint(nowAngle);
            }
        }
    } else {
        mediaRecorder.setOrientationHint(nowAngle);
    }


    //        if (DeviceUtil.isHuaWeiRongyao()) {
    //            mediaRecorder.setVideoEncodingBitRate(4 * 100000);
    //        } else {
    //            mediaRecorder.setVideoEncodingBitRate(mediaQuality);
    //        }
    
    //设置帧率
    mediaRecorder.setVideoFrameRate(24);
    //设置比特率
    mediaRecorder.setVideoEncodingBitRate(mediaQuality);
    mediaRecorder.setPreviewDisplay(surface);
    
    //文件保存文位置 设置
    videoFileName = "video_" + System.currentTimeMillis() + ".mp4";
    if (saveVideoPath.equals("")) {
        saveVideoPath = Environment.getExternalStorageDirectory().getPath();
    }
    //        videoFileAbsPath = saveVideoPath + File.separator + videoFileName;
    videoFileAbsPath = FileUtil.initPath() + videoFileName;
    mediaRecorder.setOutputFile(videoFileAbsPath);

    try {
        mediaRecorder.prepare();
        mediaRecorder.start();
        isRecorder = true;
    } catch (IllegalStateException e) {
        e.printStackTrace();
        Log.i("CJT", "startRecord IllegalStateException");
        if (this.errorLisenter != null) {
            this.errorLisenter.onError();
        }
    } catch (IOException e) {
        e.printStackTrace();
        Log.i("CJT", "startRecord IOException");
        if (this.errorLisenter != null) {
            this.errorLisenter.onError();
        }
    } catch (RuntimeException e) {
        Log.i("CJT", "startRecord RuntimeException");
    }
}

 //停止录像
public void stopRecord(boolean isShort, StopRecordCallback callback) {
    if (!isRecorder) {
        return;
    }
    if (mediaRecorder != null) {
        mediaRecorder.setOnErrorListener(null);
        mediaRecorder.setOnInfoListener(null);
        mediaRecorder.setPreviewDisplay(null);
        try {
            mediaRecorder.stop();
        } catch (RuntimeException e) {
            e.printStackTrace();
            mediaRecorder = null;
            mediaRecorder = new MediaRecorder();
        } finally {
            if (mediaRecorder != null) {
                mediaRecorder.release();
            }
            mediaRecorder = null;
            isRecorder = false;
        }
        if (isShort) {
            if (FileUtil.deleteFile(videoFileAbsPath)) {
                callback.recordResult(null, null);
            }
            return;
        }
        doStopPreview();
        String fileName = FileUtil.initPath() + videoFileName;
        callback.recordResult(fileName, videoFirstFrame);
    }
}

# surfceView 设置

mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
mHolder = mSurfaceView.getHolder();
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mHolder.addCallback(this);

@Override
public void surfaceCreated(SurfaceHolder holder) {
    //开启相机
    new Thread() {
        @Override
        public void run() {
            CameraInterface.getInstance().doOpenCamera(CameraShootActivity.this);
        }
    }.start();
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    //释放相机
    LogUtil.i("JCameraView SurfaceDestroyed");
    CameraInterface.getInstance().doDestroyCamera();
}

# Activity 生命周期

@Override
protected void onResume() {
    super.onResume();
    CameraInterface.getInstance().registerSensorManager(this);
    if (screenProp > 1.4) {
        isSwitchRecording = true;
        CameraInterface.getInstance().doStopPreview();
        //如果当前屏占比 大于1.8 不是 16:9 采用 16:9设置 小于16:9 采用原先屏占比
        CameraInterface.getInstance().doStartPreview(mSurfaceView.getHolder(), DisplayUtils.getScreenRate(this) > 1.8
                ? 16f / 9f : DisplayUtils.getScreenRate(this));
        screenProp = DisplayUtils.getScreenRate(this) > 1.8
                ? 16f / 9f : DisplayUtils.getScreenRate(this);
        measureVideoSize();
    } else {
        isSwitchRecording = false;
        CameraInterface.getInstance().doStopPreview();
        screenProp = 4f / 3f;
        CameraInterface.getInstance().doStartPreview(mSurfaceView.getHolder(), 4f / 3f);
        measureCameraSize();
    }
}

 @Override
protected void onPause() {
    super.onPause();
    CameraInterface.getInstance().isPreview(false);
    CameraInterface.getInstance().unregisterSensorManager(this);
    CameraInterface.getInstance().doStopPreview();
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容