在Manifest中的声明
1.相机权限声明:
- <!--如果是使用系统的相机拍照,可以不用声明此权限-->
- <uses-permission android:name="android.permission.CAMERA" />
2.功能声明:
- <!--只有带有摄像头的Android设备才能安装-->
- <uses-feature android:name="android.hardware.camera" />
- <!--不要求只有待有摄像头的Android设备才能安装-->
- <uses-feature android:name="android.hardware.camera" android:required="false" />
3.存储权限声明:
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
4.录制视频的权限声明:
- <uses-permission android:name="android.permission.RECORD_AUDIO" />
5.位置权限:
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
用系统的相机拍照或录制视频的步骤:
1.创建Intent
2.startActivityForResult()
3.onActivityResult()
拍照的Intent:
- private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
- private Uri fileUri;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
- startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
- }
视频的Intent:
- private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
- private Uri fileUri;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
- fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
- intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // 设置视频质量,从0到1由低到高,还可以设置时长(<code><a target=_blank href="http://developer.android.com/reference/android/provider/MediaStore.html#EXTRA_DURATION_LIMIT">MediaStore.EXTRA_DURATION_LIMIT</a></code>)和大小(<code><a target=_blank href="http://developer.android.com/reference/android/provider/MediaStore.html#EXTRA_SIZE_LIMIT">MediaStore.EXTRA_SIZE_LIMIT</a></code>)
- startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
- }
接收Intent:
- private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
- private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
- if (resultCode == RESULT_OK) {
- // 返回在Intent中指定的图片保存路径fileUri
- Toast.makeText(this, "Image saved to:\n" + data.getData(), Toast.LENGTH_LONG).show();
- } else if (resultCode == RESULT_CANCELED) {
- // 用户取消拍摄图像后
- } else {
- // 图像捕获失败
- }
- }
- if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
- if (resultCode == RESULT_OK) {
- // 返回在Intent中指定的视频保存路径fileUri
- Toast.makeText(this, "Video saved to:\n" + data.getData(), Toast.LENGTH_LONG).show();
- } else if (resultCode == RESULT_CANCELED) {
- // 用户取消录制后
- } else {
- // 视屏录制失败
- }
- }
- }
以上用到的:getOutputMediaFile(int type)
- private static File getOutputMediaFile(int type){
- // 检查SD卡
- File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");
- // 这个地方是个好位置,如果你想给别的应用程序共享图片和视频,或者你希望你的应用在卸载之后忍让能够使用图片和视频,不太明白这是什么意思,求指点
- // 是在这里把照片和视频加到设备的媒体库中吗?
- // 如果目录不存在就创建
- if (! mediaStorageDir.exists()){
- if (! mediaStorageDir.mkdirs()){
- Log.d("MyCameraApp", "failed to create directory");
- return null;
- }
- }
- // Create a media file name
- String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
- File mediaFile;
- if (type == MEDIA_TYPE_IMAGE){
- mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg");
- } else if(type == MEDIA_TYPE_VIDEO) {
- mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_"+ timeStamp + ".mp4");
- } else {
- return null;
- }
- return mediaFile;
- }
自定义相机的介绍:
第一步,先检查是否支持自定义相机
- /** Check if this device has a camera */
- private boolean checkCameraHardware(Context context) {
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
- // 设备有摄像头
- return true;
- } else {
- // 设备没有摄像头
- return false;
- }
- }
可以通过Camera.getParameters()或者Camera.Parameters获取摄像头的许多功能特性和参数
还可以通过Camera.getCameraInfo()获知摄像头的前、后属性和图片的方向
第二步,创建预览视图
- /** A basic Camera preview class */
- public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
- private SurfaceHolder mHolder;
- private Camera mCamera;
- public CameraPreview(Context context, Camera camera) {
- super(context);
- mCamera = camera;
- mHolder = getHolder();
- mHolder.addCallback(this);
- // 不建议使用,但在3.0之前要求调用
- mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
- }
- public void surfaceCreated(SurfaceHolder holder) {
- try {
- mCamera.setPreviewDisplay(holder);
- mCamera.startPreview();
- } catch (IOException e) {
- Log.d(TAG, "Error setting camera preview: " + e.getMessage());
- }
- }
- public void surfaceDestroyed(SurfaceHolder holder) {
- // 记得在onDestory中释放preview
- }
- <pre name="code" class="java"> // 注意preview的改变或者旋转,如果要改变大小或者格式,确保preview先stop
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- if (mHolder.getSurface() == null){
- return;
- }
- // 改变之前先stop
- try {
- mCamera.stopPreview();
- } catch (Exception e){
- }
- //设置新的大小,方向和格式
- // 重新打开预览
- try {
- mCamera.setPreviewDisplay(mHolder);
- mCamera.startPreview();
- } catch (Exception e){
- Log.d(TAG, "Error starting camera preview: " + e.getMessage());
- }
- }
- }
第三步,预览布局
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <FrameLayout
- android:id="@+id/camera_preview"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- />
- <Button
- android:id="@+id/button_capture"
- android:text="Capture"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- />
- </LinearLayout>
- public class CameraActivity extends Activity {
- private Camera mCamera;
- private CameraPreview mPreview;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mCamera = getCameraInstance();
- // Create our Preview view and set it as the content of our activity.
- mPreview = new CameraPreview(this, mCamera);
- FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
- preview.addView(mPreview);
- }
- }
- /** 以一种安全的方式获取相机实例,捕获异常信息为了防止相机获取不成功的情况 */
- public static Camera getCameraInstance(){
- Camera c = null;
- try {
- c = Camera.open();
- }
- catch (Exception e){
- // 摄像头不可用(正在使用/没有摄像头)
- }
- return c; // 如果摄像头不可用,则返回null
- }
第四步,拍照监听
- // Add a listener to the Capture button
- Button captureButton = (Button) findViewById(id.button_capture);
- captureButton.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mCamera.takePicture(null, null, mPicture);
- }
- }
- );
第五步,捕获和保存
Camera.PictureCallback();
- private PictureCallback mPicture = new PictureCallback() {
- @Override
- public void onPictureTaken(byte[] data, Camera camera) {
- File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
- if (pictureFile == null){
- Log.d(TAG, "Error creating media file, check storage permissions: " + e.getMessage());
- return;
- }
- try {
- FileOutputStream fos = new FileOutputStream(pictureFile);
- fos.write(data);
- fos.close();
- } catch (FileNotFoundException e) {
- Log.d(TAG, "File not found: " + e.getMessage());
- } catch (IOException e) {
- Log.d(TAG, "Error accessing file: " + e.getMessage());
- }
- }
- };
第六步,释放摄像头
- Camera.realse();
注:摄像头是共享的,所以在使用是必须小心的管理它,在使用完之后也要记得释放,否则下次调用时将会报错。
视频捕获
视频拍摄要求小心的管理Camera对象和MediaRecorder类,除了Camera.open()和Camera.close(),还要管理Camera.lock()和Camera.unlock()
1.打开摄像头
Camera.open()
2.连接预览
Camera.setPreviewDisplay()
3.打开预览
Camera.startPreview()
4.开始录制
a.解锁相机
Camera.unlock()
b.配置MediaRecorder
2.setAudioSource()
-->MediaRecorder.AudioSource.CAMCORDER
3.setVideoSource()
-->MediaRecorder.VideoSource.CAMERA
.
4.设置视频输出格式和编码:
2.2+:MediaRecorder.setProfile
,CamcorderProfile.get()
2.2-:
setOutputFormat()
-->MediaRecorder.OutputFormat.MPEG_4
setAudioEncoder()
-->MediaRecorder.AudioEncoder.AMR_NB
setVideoEncoder()
-->MediaRecorder.VideoEncoder.MPEG_4_SP
c.准备MediaRecorder
d.开始MediaRcorder
- private boolean prepareVideoRecorder(){
- mCamera = getCameraInstance();
- mMediaRecorder = new MediaRecorder();
- // Step 1: Unlock and set camera to MediaRecorder
- mCamera.unlock();
- mMediaRecorder.setCamera(mCamera);
- // Step 2: Set sources
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
- mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
- // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
- mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
- // Step 4: Set output file
- mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());
- // Step 5: Set the preview output
- mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());
- // Step 6: Prepare configured MediaRecorder
- try {
- mMediaRecorder.prepare();
- } catch (IllegalStateException e) {
- Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
- releaseMediaRecorder();
- return false;
- } catch (IOException e) {
- Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
- releaseMediaRecorder();
- return false;
- }
- return true;
- }
在2.2之前
- // Step 3: Set output format and encoding (for versions prior to API Level 8)
- mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
- mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
还可以配置以下参数
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
5.停止录制
6.停止预览
7.释放摄像头
开始和停止
- Unlock the camera with
Camera.unlock()
- Configure
MediaRecorder
as shown in the code example above - Start recording using
MediaRecorder.start()
- Record the video
- Stop recording using
MediaRecorder.stop()
- Release the media recorder with
MediaRecorder.release()
- Lock the camera using
Camera.lock()
- private boolean isRecording = false;
- // Add a listener to the Capture button
- Button captureButton = (Button) findViewById(id.button_capture);
- captureButton.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (isRecording) {
- // stop recording and release camera
- mMediaRecorder.stop(); // stop the recording
- releaseMediaRecorder(); // release the MediaRecorder object
- mCamera.lock(); // take camera access back from MediaRecorder
- // inform the user that recording has stopped
- setCaptureButtonText("Capture");
- isRecording = false;
- } else {
- // initialize video camera
- if (prepareVideoRecorder()) {
- // Camera is available and unlocked, MediaRecorder is prepared,
- // now you can start recording
- mMediaRecorder.start();
- // inform the user that recording has started
- setCaptureButtonText("Stop");
- isRecording = true;
- } else {
- // prepare didn't work, release the camera
- releaseMediaRecorder();
- // inform user
- }
- }
- }
- }
- );
释放摄像头
- public class CameraActivity extends Activity {
- private Camera mCamera;
- private SurfaceView mPreview;
- private MediaRecorder mMediaRecorder;
- ...
- @Override
- protected void onPause() {
- super.onPause();
- releaseMediaRecorder(); // if you are using MediaRecorder, release it first
- releaseCamera(); // release the camera immediately on pause event
- }
- private void releaseMediaRecorder(){
- if (mMediaRecorder != null) {
- mMediaRecorder.reset(); // clear recorder configuration
- mMediaRecorder.release(); // release the recorder object
- mMediaRecorder = null;
- mCamera.lock(); // lock camera for later use
- }
- }
- private void releaseCamera(){
- if (mCamera != null){
- mCamera.release(); // release the camera for other applications
- mCamera = null;
- }
- }
- }
保存
两个标准路径保存文件:
Environment.getExternalStoragePublicDirectory
(Environment.DIRECTORY_PICTURES
):共享路径,当应用程序卸载后不会被删除
Context.getExternalFilesDir
(Environment.DIRECTORY_PICTURES
):当应用程序卸载后会被删除
- public static final int MEDIA_TYPE_IMAGE = 1;
- public static final int MEDIA_TYPE_VIDEO = 2;
- private static Uri getOutputMediaFileUri(int type){
- return Uri.fromFile(getOutputMediaFile(type));
- }
- private static File getOutputMediaFile(int type){
- //应该先检查SD卡的状态
- File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyCameraApp");
- if (! mediaStorageDir.exists()){
- if (! mediaStorageDir.mkdirs()){
- Log.d("MyCameraApp", "failed to create directory");
- return null;
- }
- }
- String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
- File mediaFile;
- if (type == MEDIA_TYPE_IMAGE){
- mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg");
- } else if(type == MEDIA_TYPE_VIDEO) {
- mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_"+ timeStamp + ".mp4");
- } else {
- return null;
- }
- return mediaFile;
- }
摄像头的功能
Android支持许多你可以控制的摄像头功能,比如图片格式,闪光模式,焦点设置等等。以下列举通用的摄像头功能和描述怎么使用它们。许多功能是通过Camera.Parameters
对象设置的。
Feature | API Level | Description |
---|---|---|
Face Detection | 14 | 人脸识别 |
Metering Areas | 14 | 为一张图片制定一个或多个白平衡调节 |
Focus Areas | 14 | 为一张图片摄者一个或多个焦点区域 |
White Balance Lock | 14 | 自动调节白平衡 |
Exposure Lock | 14 | 自动调节曝光 |
Video Snapshot | 14 | 捕获图片 |
Time Lapse Video | 11 | 延时视频 |
Multiple Cameras | 9 | 支持多个摄像头,包含前置和后置谁想头 |
Focus Distance | 9 | 摄像头和拍摄物之间的距离 |
Zoom | 8 | 设置图片的放大率 |
ExposureCompensation | 8 | 增减曝光值 |
GPS Data | 5 | 包含或忽略图片的地理位置信息 |
White Balance | 5 | 设置平衡模式,影响图片的颜色值 |
Focus Mode | 5 | 设置焦点:automatic, fixed, macro or infinity |
Scene Mode | 5 | 预设场景,比如夜晚,海滩,雪景或者烛光晚餐 |
JPEG Quality | 5 | 图片质量 |
Flash Mode | 5 | 闪光模式 |
Color Effects | 5 | 对图片使用颜色 |
Anti-Banding | 5 | 减小由于JPEG压缩对颜色渐变的影响 |
Picture Format | 1 | 指定图片的格式 |
Picture Size | 1 | 指定保存图片的尺寸 |
注:由于硬件和软件实现的不同,这些功能不是所有的设备都支持。所以在使用的时候需要现检查一下是否支持特定的功能。
比如支持是否自动对焦:
- // get Camera parameters
- Camera.Parameters params = mCamera.getParameters();
- List<String> focusModes = params.getSupportedFocusModes();
- if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
- // Autofocus mode is supported
- }
你也可以使用
Camera.Parameters
中的getSupported...(),isSupported...(),或者getMax...()方法去判断某些特定的摄像头功能。
最后,可以在Manifest中声明指定的摄像头相关功能,这样Google Play会限制没有该功能的设备不予下载使用应用程序。
摄像头功能的使用:
- // get Camera parameters
- Camera.Parameters params = mCamera.getParameters();
- // set the focus mode
- params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
- // set Camera parameters
- mCamera.setParameters(params);
大多数摄像头的功能都可以通过如上方式设置,但是有一些功能不能被随便设置,比如拍摄图片大小和方向,必须要先停止预览,改变之后再重新预览,4.0之后,想要改变方向可以不用重启预览。
还有一些功能需要编写更多的代码才能实现,比如:
- Metering and focus areas
- Face detection
- Time lapse video
测光和调距:
在一些摄影场景中,自定对焦和测光不能满足预期的效果,从4.0(api14)开始,允许自己定义这些功能属性:
- // Create an instance of Camera
- mCamera = getCameraInstance();
- // set Camera parameters
- Camera.Parameters params = mCamera.getParameters();
- if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported
- List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
- Rect areaRect1 = new Rect(-100, -100, 100, 100); // specify an area in center of image
- meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60%
- Rect areaRect2 = new Rect(800, -1000, 1000, -800); // specify an area in upper right of image
- meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40%
- params.setMeteringAreas(meteringAreas);
- }
- mCamera.setParameters(params);
人脸识别功能:
人脸识别开启式,setWhiteBalance(String)
,setFocusAreas(List)
和setMeteringAreas(List)
不起作用。
第一步:检验是否支持人脸识别功能;
第二步:创建人脸识别监听;
- class MyFaceDetectionListener implements Camera.FaceDetectionListener {
- @Override
- public void onFaceDetection(Face[] faces, Camera camera) {
- if (faces.length > 0){
- Log.d("FaceDetection", "face detected: "+ faces.length +
- " Face 1 Location X: " + faces[0].rect.centerX() +
- "Y: " + faces[0].rect.centerY() );
- }
- }
- }
第三步:想camera中添加监听;
- mCamera.setFaceDetectionListener(new MyFaceDetectionListener());
第四步:开始识别
- public void startFaceDetection(){
- // Try starting Face Detection
- Camera.Parameters params = mCamera.getParameters();
- // start face detection only *after* preview has started
- if (params.getMaxNumDetectedFaces() > 0){
- // camera supports face detection, so can start it:
- mCamera.startFaceDetection();
- }
- }
每次开始预览的时候都必须打开人脸识别,在surfaceCreated()和surfaceChanged中都要调用startFaceDetection()
- public void surfaceCreated(SurfaceHolder holder) {
- try {
- mCamera.setPreviewDisplay(holder);
- mCamera.startPreview();
- startFaceDetection(); // start face detection feature
- } catch (IOException e) {
- Log.d(TAG, "Error setting camera preview: " + e.getMessage());
- }
- }
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- if (mHolder.getSurface() == null){
- // preview surface does not exist
- Log.d(TAG, "mHolder.getSurface() == null");
- return;
- }
- try {
- mCamera.stopPreview();
- } catch (Exception e){
- // ignore: tried to stop a non-existent preview
- Log.d(TAG, "Error stopping camera preview: " + e.getMessage());
- }
- try {
- mCamera.setPreviewDisplay(mHolder);
- mCamera.startPreview();
- startFaceDetection(); // re-start face detection feature
- } catch (Exception e){
- // ignore: tried to stop a non-existent preview
- Log.d(TAG, "Error starting camera preview: " + e.getMessage());
- }
- }
注:不要相机界面Activity的onCreate()中调用人脸识别方法,这个时候的preview还不可用,要在starPreview()之后调用人脸识别。
延时摄影:
延时摄运行用户把几张图片合成一个几秒或者几分钟的视频剪辑。这个功能需要使用MediaRecorder对象来记录图像的延时序列。
要用MediaRecorder对象来记录延时视频,要想录制普通视频一样,必须要先配置,例如把每秒采集的帧数设置到较小,并使用一个延时品质设置
- // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
- mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH));
- ...
- // Step 5.5: Set the video capture rate to a low number
- mMediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds