`

摄像头

 
阅读更多

 

 

Android框架包含了对多种摄像头和摄像特性的支持,应用程序可以进行图片和视频的捕获。本文讨论了一种快速、简便的捕获图像和视频的方法,并简述了一种更高级的可为用户创建自定义摄像功能的方法。

 

 

需要考虑的问题

在让应用程序使用Android设备的摄像头之前,应该考虑一些期望如何使用此硬件的问题。

·       摄像头需求 —— 摄像头的使用对于应用程序是否确实如此重要,以至于在没有摄像头的设备上就不期望安装此应用了?如果确实如此,应该manifest中声明摄像头需求

·       快速拍照还是自定义摄像 —— 应用程序如何使用摄像头?仅仅是对快速拍照和视频片段感兴趣,还是要提供一种使用摄像头的新方式?对于快速拍照和摄像而言,可以考虑使用内置的摄像应用。为了开发一种定制的摄像头功能,请查看创建摄像一节。

·       存储 —— 应用程序产生的图像和视频是否期望仅对自身可见,还是可以共享——以便相册或其它媒体应用也能够使用?当应用程序被卸载后,还期望图像和视频可用么?请查看保存媒体文件一节来了解如何实现这些选项。

 

 

概述

通过Camera API 或摄像头意图IntentAndroid框架为图像和视频捕获提供支持。下面列出了有关的类:

Camera

此类是控制摄像头的主要API。在创建摄像头应用程序时,此类用于拍摄照片或视频。

SurfaceView

此类用于向用户提供摄像头实时预览功能。

MediaRecorder

此类用于从摄像头录制视频。

Intent

动作类型为MediaStore.ACTION_IMAGE_CAPTURE MediaStore.ACTION_VIDEO_CAPTURE 的意图,可在不直接使用Camera对象的情况下捕获图像和视频。

 

 

Manifest声明

开始开发摄像头 API的应用之前,应该确保已经在manifest中正确声明了对摄像头的使用及其它相关的feature

·       Camera权限——应用程序必须对请求摄像头的使用权限。

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

注意:如果是通过意图来使用摄像头的,应用程序就不必请求本权限。

·       Camera Feature——应用程序必须同时声明对camera feature的使用,例如:

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

关于摄像头feature的清单,参阅manifest Feature参考

manifest中加入camera feature,将会使得Android Market在没有摄像头或不支持指定feature的设备上禁止安装该应用程序。关于Android Market基于feature过滤的使用详情,请参阅Android Market和基于Feature的过滤

如果应用程序可能用到摄像头或摄像头feature,但却不是必需的,则应在manifest中指定包含android:required属性的feature,并将该属性设为false

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

·       存储权限——如果应用程序要把图像或视频保存到设备的外部存储上(SD卡),则还必须在manifest中指定如下权限。

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

·       录音权限——要用音频捕获来录音,应用程序必须请求音频捕获权限。

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

 

 

使用内置的摄像头应用程序

有一种快捷的方法可以让应用程序不用额外编写很多代码就能实现拍照或摄像,这就是用意图Intent来调用内置的Android摄像头应用程序。摄像头意图(intent)会请求通过内置摄像应用来捕获图像或视频,并把控制权返回给应用程序。本节展示了如何用这种方法来捕获图像。

通常按以下步骤来提交一个摄像头 intent

1. 构建一个摄像头 Intent —— 用以下意图类型之一,创建一个请求图像或视频的Intent 

o    MediaStore.ACTION_IMAGE_CAPTURE —— 向内置摄像头程序请求图像的意图活动类型。

o    MediaStore.ACTION_VIDEO_CAPTURE —— 向内置摄像头程序请求视频的意图活动类型。

2. 启动摄像头 Intent ——startActivityForResult()方法执行摄像头 intent。启动完毕后摄像头应用的用户界面就会显示在屏幕上,用户就可以拍照或摄像了。

3. 接收Intent结果 —— 在应用程序中设置onActivityResult()方法,用于接收从摄像头 intent返回的数据。当用户拍摄完毕后(或者取消操作),系统会调用此方法。

 

捕获图像的intent

如果希望程序以最少的代码实现拍照功能,利用摄像头intent捕获图像是一条捷径。图像捕捉intent还可以包含以下附加信息:

·       MediaStore.EXTRA_OUTPUT ——本设置需要一个Uri对象,用于指定存放图片的路径和文件名。本设置是可选项,但强烈建议使用。如果未指定本设置值,那么摄像应用将会把所请求的图片以默认文件名和路径进行保存,并将数据置入intentIntent.getData()部分返回。

以下例子演示了如何构建并执行一个图像捕获intent。此例中的getOutputMediaFileUri() 方法引自保存媒体文件中的例程代码。.

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 intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 

 

fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);

// 创建保存图片的文件

intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

// 设置图片文件名

 

    // 启动图像捕获Intent 

    startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE); 

}

startActivityForResult() 方法执行完毕后,用户将看到内置摄像头应用程序的界面。用户拍照完毕(或取消操作)后,用户界面返回应用程序,这时必须截获onActivityResult()方法来接收intent的返回结果并执行后续操作。关于如何接收完整的intent,请参阅接收摄像头 Intent的结果

 

捕获视频的intent

如果希望程序以最少的代码实现摄像功能,利用摄像头 intent捕获视频是一条捷径。视频捕捉intent可以包含以下附带信息:

·       MediaStore.EXTRA_OUTPUT —— 本设置需要一个Uri用于指定保存视频的路径和文件名。本设置是可选项,但强烈建议使用。如果未指定本设置值,那么摄像应用将会把所请求的视频以默认文件名和路径进行保存,并将数据置入intentIntent.getData()部分返回。

·       MediaStore.EXTRA_VIDEO_QUALITY ——本值用0表示最低品质及最小的文件尺寸,用1表示最高品质和较大的文件尺寸。

·       MediaStore.EXTRA_DURATION_LIMIT ——本值用于限制所捕获视频的长度,以秒为单位。

·       MediaStore.EXTRA_SIZE_LIMIT —— 本值用于限制所捕获视频的文件尺寸,以字节为单位。

以下例子演示了如何构建并执行一个视频捕获intent。本例中的getOutputMediaFileUri()方法引自保存媒体文件中的例程代码。

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 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);

// 设置视频的品质为高

 

    // 启动视频捕获Intent 

    startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE); 

}

startActivityForResult() 方法执行完毕后,用户将看到一个改动过的摄像程序界面。用户摄像完毕(或取消操作)后,用户界面返回应用程序,这时必须监听onActivityResult()方法来接收intent的返回结果并执行后续操作。关于如何接收完整的intent,请参阅下一节。

 

接收摄像头 intent的结果

一旦已构建并运行了图像或视频的摄像头intent,应用程序就必须进行设置,以接收intent返回的结果。本节展示了如何监听摄像头intent的回调方法,以便应用程序对捕获到的图片及视频进行进一步的处理。

要接收intent的返回结果,必须覆盖启动intentactivity中的onActivityResult()方法。以下例子演示了如何覆盖onActivityResult()来获取上述章节例程中的图像捕获intent视频捕获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 { 

            // 视频捕获失败,提示用户

        } 

    } 

}

一旦activity接收到成功的结果,就说明捕获到的图像或视频已保存到指定位置了,应用程序就可对其进行访问。

 

 

创建摄像头应用程序

有些开发人员可能需要自定义外观的摄像头用户界面,或者需要提供特殊的功能。相比使用intent而言,创建定制的摄像activity需要编写更多的代码,不过也能向用户提供更吸引人的使用感受。

通常按照以下步骤创建一个定制的摄像界面:

·       检测并访问摄像头 —— 创建代码以检查摄像头存在与否并请求访问。

·       创建预览类 —— 创建继承自SurfaceView 并实现SurfaceHolder 接口的摄像预览类。此类能预览摄像的实时图像。

·       建立预览布局Preview Layout —— 一旦有了摄像预览类,即可创建一个view layout,用于把预览画面与设计好的用户界面控件融合在一起。

·       为捕获设置侦听器Listener —— 将用户界面控件连接到listener,使其能响应用户操作开始捕获图像或视频,比如按下按钮。

·       捕获并保存文件 —— 建立捕获图片或视频并保存到输出文件的代码。

·       释放摄像头 —— 摄像头使用完毕后,应用程序必须正确地将其释放,便于其它程序的使用。

摄像头硬件是一个共享资源,必须对其进行细心的管理,因此需要使用它的应用程序之间不能发生冲突。下一节将会讨论如何检测摄像头硬件、如何请求访问摄像头、使用完毕如何释放。

警告:应用程序用完摄像头后,请记得调用Camera.release()释放Camera对象!如果某应用程序未能正确释放摄像头,所有后续访问摄像头的尝试(包括此应用程序自身)都将会失败,并可能导致程序被强行关闭。

 

检测摄像头硬件

如果应用程序未利用manifest声明对摄像头需求进行特别指明,则应该在运行时检查一下摄像头是否可用。可用PackageManager.hasSystemFeature()方法来进行这种检查,代码示例如下:

/** 检查设备是否提供摄像头 */ 

private boolean checkCameraHardware(Context context) { 

    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){ 

        // 摄像头存在 

        return true; 

    } else { 

        // 摄像头不存在 

        return false; 

    } 

}

Android设备可能拥有多个摄像头,比如向后的摄像头用于拍照、向前的摄像头用于摄像。Android 2.3 (API Level 9) 以上版本允许利用Camera.getNumberOfCameras()方法来检查设备可用摄像头的数量。

 

访问摄像头

如果在运行程序的设备上已经检测到了摄像头,则必须通过获取一个Camera的实例来请求对其访问(除非使用了用于访问摄像头的intent)。

可用Camera.open()方法来访问主摄像头,并确保捕获全部的异常,示例代码如下:

/** 安全获取Camera对象实例的方法*/ 

public static Camera getCameraInstance(){ 

    Camera c = null; 

    try { 

        c = Camera.open(); // 试图获取Camera实例

    } 

    catch (Exception e){ 

        // 摄像头不可用(正被占用或不存在)

    } 

    return c; // 不可用则返回null

}

警告: 每次使用Camera.open()时都要检查异常。如果摄像头被占用或者不存在,未检查异常将会导致应用程序被系统强行关闭。

在运行Android 2.3 (API Level 9) 以上版本的设备上,可以用Camera.open(int)访问指定的摄像头。在拥有多于一个摄像头的设备上,以上示例代码将会访问第一个也即朝后的那个摄像头。

 

检查摄像头feature

一旦获得了摄像头的访问权,就可以通过Camera.getParameters()方法来获取更多信息,检查返回的Camera.Parameters对象可查看摄像头所支持的feature。如果正在使用API Level 9以上版本,可用Camera.getCameraInfo()来确定摄像头朝前还是朝后以及图像的方向。

 

创建预览类

为了方便拍照或摄像,用户必须能看到摄像头所拍摄的画面。摄像头预览类就是一种能够显示摄像头实时数据的SurfaceView,用户可以调整并捕获图片和视频。

以下示例代码演示了如何创建一个基本的摄像头预览类,它可被嵌入一个View布局中。为了捕捉view创建和销毁时的回调事件,此类实现了SurfaceHolder.Callback,这在指定摄像头预览的输入时需要用到。

/** 基本的摄像头预览类 */ 

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { 

    private SurfaceHolder mHolder; 

    private Camera mCamera; 

 

    public CameraPreview(Context context, Camera camera) { 

        super(context); 

        mCamera = camera; 

 

        // 安装一个SurfaceHolder.Callback

        // 这样创建和销毁底层surface时能够获得通知。

        mHolder = getHolder(); 

        mHolder.addCallback(this); 

        // 已过期的设置,但版本低于3.0Android还需要

        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

    } 

 

    public void surfaceCreated(SurfaceHolder holder) { 

        // surface已被创建,现在把预览画面的位置通知摄像头

        try { 

            mCamera.setPreviewDisplay(holder); 

            mCamera.startPreview(); 

        } catch (IOException e) { 

            Log.d(TAG, "Error setting camera preview: " + e.getMessage()); 

        } 

    } 

 

    public void surfaceDestroyed(SurfaceHolder holder) { 

        // 空代码。注意在activity中释放摄像头预览对象    } 

 

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 

        // 如果预览无法更改或旋转,注意此处的事件

        // 确保在缩放或重排时停止预览

 

        if (mHolder.getSurface() == null){ 

          // 预览surface不存在

          return; 

        } 

 

        // 更改时停止预览 

        try { 

            mCamera.stopPreview(); 

        } catch (Exception e){ 

          // 忽略:试图停止不存在的预览

        } 

 

        // 在此进行缩放、旋转和重新组织格式

 

        // 以新的设置启动预览

        try { 

            mCamera.setPreviewDisplay(mHolder); 

            mCamera.startPreview(); 

 

        } catch (Exception e){ 

            Log.d(TAG, "Error starting camera preview: " + e.getMessage()); 

        } 

    } 

}

 

将预览画面置入layout

上节例程所述的摄像预览类必须被放入一个activitylayout中,连同其它用户界面控件一起,实现拍照或摄像功能。本节展示了如何为预览创建一个简单的layoutactivity

以下layout代码提供了一个非常简单的view,用于显示一个摄像预览画面。在此例中,FrameLayout元素用于容纳摄像预览类。利用此类layout,可以把附加的图片信息或控件叠加到实时预览画面上。

<?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>

在大多数设备上,缺省的摄像预览方向是横向的。此例中的layout指定了横向(landscape)布局,下面的代码还把应用程序的方向也改为了横向。为了简化摄像预览画面的刷新,应该在manifest中增加如下内容,把应用程序的预览activity也改为横向显示。

<activity android:name=".CameraActivity" 

          android:label="@string/app_name" 

 

          android:screenOrientation="landscape"> 

          <!-- configure this activity to use landscape orientation --> 

 

          <intent-filter> 

        <action android:name="android.intent.action.MAIN" /> 

        <category android:name="android.intent.category.LAUNCHER" /> 

    </intent-filter> 

</activity>

注意: 摄像预览画面并不是一定要横向显示。自Android 2.2 (API Level 8) 开始,可以利用setDisplayOrientation() 方法来旋转预览画面。为了让预览方向跟随手机方向的变化而改变,可以在预览类的surfaceChanged()方法中实现,先用Camera.stopPreview()停止预览,改变方向后再用Camera.startPreview()开启预览。

在摄像view activity中,请把预览类添加到上述的FrameLayout元素中。当摄像头暂停使用或者关闭时,摄像activity还必须确保将其释放。以下例子展示了如何修改摄像activity,加入创建预览类所述的预览类。

public class CameraActivity extends Activity { 

 

    private Camera mCamera; 

    private CameraPreview mPreview; 

 

    @Override 

    public void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        setContentView(R.layout.main); 

 

        // 创建Camera实例 

        mCamera = getCameraInstance(); 

 

        // 创建Preview view并将其设为activity中的内容

        mPreview = new CameraPreview(this, mCamera); 

        FrameLayout preview = (FrameLayout) findViewById(id.camera_preview); 

        preview.addView(mPreview); 

    } 

}

注意:上例中的getCameraInstance()方法引用了访问摄像头中的方法示例。

 

捕获图像

一旦创建了预览类和显示它的view layout,就可以开始在程序中捕获图片了。必须在程序代码中为用户界面控件设置listener,使其可响应用户操作进行拍照。

可以通过Camera.takePicture()方法来获取图片,此方法用到三个参数并从摄像头接收数据。如果要以JPEG的格式接收数据,必须实现Camera.PictureCallback接口,以接收图片数据并写入文件。以下代码展示了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.takePicture()方法,触发器捕获了一张图片。 以下例程展示了如何在按钮View.OnClickListener的中调用此方法。

// Capture按钮中加入listener 

Button captureButton = (Button) findViewById(id.button_capture); 

    captureButton.setOnClickListener( 

        new View.OnClickListener() { 

        @Override 

        public void onClick(View v) { 

            // 从摄像头获取图片

            mCamera.takePicture(null, null, mPicture); 

        } 

    } 

);

注意:下文例程中的mPicture 成员将会引用上述代码。

警告:当应用程序使用完摄像头之后,请记得调用Camera.release()释放Camera 对象! 关于如何释放摄像头的详情,请参阅释放摄像头

 

捕获视频

Android框架的视频捕捉需要对Camera对象进行仔细的管理,还要与MediaRecorder类一起协同工作。使用Camera录制视频时,必须管理好Camera.lock()Camera.unlock()的调用,使得MediaRecorder能够顺利访问摄像头硬件,并且还要进行Camera.open()Camera.release()调用。

注意:Android 4.0 (API level 14) 开始,Camera.lock()  Camera.unlock() 调用由系统自动管理。

与用摄像头拍照不同,视频捕获必需十分精确地按顺序进行调用。必须按照特定的顺序来执行,应用程序才能成功地准备并捕获视频,详细步骤如下。

1.       打开摄像头 —— Camera.open() 来获得一个camera对象的实例。

2.       连接预览 —— Camera.setPreviewDisplay()camera连接到一个SurfaceView ,准备实时预览。

3.       开始预览 —— 调用 Camera.startPreview() 开始显示实时摄像画面。

4.       开始录制视频 —— 严格按照以下顺序执行才能成功录制视频:

a.   解锁Camera —— 调用Camera.unlock()解锁,便于MediaRecorder 使用摄像头。

b. 配置MediaRecorder —— 按照如下顺序调用MediaRecorder 中的方法。详情请参阅MediaRecorder 参考文档。

1.   setCamera() —— 用当前Camera实例将摄像头用途设置为视频捕捉。

2.   setAudioSource() —— MediaRecorder.AudioSource.CAMCORDER设置音频源。

3.   setVideoSource() —— MediaRecorder.VideoSource.CAMERA设置视频源。

4.   设置视频输出格式和编码格式。Android 2.2 (API Level 8) 以上版本使用MediaRecorder.setProfile 方法,并用CamcorderProfile.get()来获取一个profile实例。对于Android prior to 2.2以上版本,必须设置视频输出格式和编码参数:

i.       setOutputFormat() —— 设置输出格式,指定缺省设置或MediaRecorder.OutputFormat.MPEG_4

ii.      setAudioEncoder() —— 设置声音编码类型。指定缺省设置或MediaRecorder.AudioEncoder.AMR_NB

iii.     setVideoEncoder() —— 设置视频编码类型,指定缺省设置或者 MediaRecorder.VideoEncoder.MPEG_4_SP

5.   setOutputFile() —— getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()设置输出文件,见保存媒体文件一节中的方法示例。

6.   setPreviewDisplay() —— 用上面连接预览中设置的对象来指定应用程序的SurfaceView 预览layout元素。

警告: 必须按照如下顺序调用MediaRecorder的下列配置方法,否则应用程序将会引发错误,录像也将失败。

c.   准备MediaRecorder —— 调用MediaRecorder.prepare()设置配置,准备好MediaRecorder 

d. 启动MediaRecorder —— 调用MediaRecorder.start()开始录制视频。

5.       停止录制视频 —— 按照顺序调用以下方法,才能成功完成视频录制:

a.   停止MediaRecorder —— 调用MediaRecorder.stop()停止录制视频。

b. 重置MediaRecorder —— 这是可选步骤,调用MediaRecorder.reset()recorder中的配置信息。

c.   释放MediaRecorder —— 调用MediaRecorder.release()释放MediaRecorder

d. 锁定摄像头 —— Camera.lock()锁定摄像头,使得以后MediaRecorder session能够使用它。自Android 4.0 (API level 14)开始,不再需要本调用了,除非MediaRecorder.prepare()调用失败。

6.       停止预览 —— activity使用完摄像头后,应用Camera.stopPreview()止预览。

7.       释放摄像头 —— 使用Camera.release()放摄像头,使其它应用程序可以使用它。

注意: 也可以不必先创建摄像头预览就使用MediaRecorder并跳过本节开始的几步。不过,因为用户一般都希望在开始录像前看到预览画面,这里就不讨论那类过程了。

 

配置MediaRecorder

在使用 MediaRecorder 类进行录像时,必须先按照特定顺序进行配置,然后调用MediaRecorder.prepare()方法检查并执行这些配置。以下例程演示了如何为录像正确配置并准备MediaRecorder 类。

private boolean prepareVideoRecorder(){ 

 

    mCamera = getCameraInstance(); 

    mMediaRecorder = new MediaRecorder(); 

 

    // 1步:解锁并将摄像头指向MediaRecorder

    mCamera.unlock(); 

    mMediaRecorder.setCamera(mCamera); 

 

    // 2步:指定源

    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); 

    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 

 

    // 3步:指定CamcorderProfile(需要API Level 8以上版本)

    mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); 

 

    // 4步:指定输出文件

    mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()); 

 

    // 5步:指定预览输出

    mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface()); 

 

    // 6步:根据以上配置准备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; 

}

如果是Android 2.2 (API Level 8) 之前的版本,则必须直接指定输出格式和编码格式,而不是使用CamcorderProfile以下代码演示了这种方式:

    // 3步:设置输出格式和编码格式(针对低于API Level 8版本)

    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); 

    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); 

    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);

MediaRecorder中以下有关视频录制的参数都给出了缺省值,当然也可以在应用程序中修改这些设置:

·       setVideoEncodingBitRate()

·       setVideoSize()

·       setVideoFrameRate()

·       setAudioEncodingBitRate()

·       setAudioChannels()

·       setAudioSamplingRate()

 

开始和停止MediaRecorder

使用MediaRecorder类开始和停止视频录制时,必须遵循以下特定顺序。

1.     Camera.unlock()解锁摄像头

2.     上代码所示配置MediaRecorder

3.     MediaRecorder.start()开始录制

4.     记录视频

5.     MediaRecorder.stop()停止录制

6.     MediaRecorder.release()释放media recorder

7.     Camera.lock()锁定摄像头

以下例程演示了如何触发按钮并用cameraMediaRecorder类正确地开始和停止视频录制。

注意: 视频录制完毕后请不要释放camera,否则预览将会停止。

private boolean isRecording = false; 

 

// Capture按钮加入listener

Button captureButton = (Button) findViewById(id.button_capture); 

captureButton.setOnClickListener( 

    new View.OnClickListener() { 

        @Override 

        public void onClick(View v) { 

            if (isRecording) { 

                // 停止录像并释放camera 

                mMediaRecorder.stop(); // 停止录像

                releaseMediaRecorder(); // 释放MediaRecorder对象

                mCamera.lock();         // 将控制权从MediaRecorder 交回camera

 

                // 通知用户录像已停止

                setCaptureButtonText("Capture"); 

                isRecording = false; 

            } else { 

                // 初始化视频camera 

                if (prepareVideoRecorder()) { 

                    // Camera已可用并解锁,MediaRecorder已就绪,

                    // 现在可以开始录像

                    mMediaRecorder.start(); 

 

                    // 通知用户录像已开始

                    setCaptureButtonText("Stop"); 

                    isRecording = true; 

                } else { 

                    // 准备未能完成,释放camera 

                    releaseMediaRecorder(); 

                    // 通知用户

                } 

            } 

        } 

    } 

);

注意: 在上例中,prepareVideoRecorder() 方法引用了配置MediaRecorder中的示例代码。此方法实现了锁定camera、配置和准备MediaRecorder 实例。

 

释放摄像头

摄像头是设备上所有应用程序共享使用的资源。应用程序可以在获得Camera实例后使用摄像头,停止使用后请务必注意释放摄像头对象,应用程序暂停时Activity.onPause())也是如此。如果某应用程序未能正确地释放摄像头,则所有后续访问摄像头的尝试(包括该应用程序自身)都将会失败,并可能导致应用程序被强行关闭。

Camera.release()方法可以释放Camera对象的实例,代码示例如下。

public class CameraActivity extends Activity { 

    private Camera mCamera; 

    private SurfaceView mPreview; 

    private MediaRecorder mMediaRecorder; 

 

    ... 

     

    @Override 

    protected void onPause() { 

        super.onPause(); 

        releaseMediaRecorder(); // 如果正在使用MediaRecorder,首先需要释放它。

        releaseCamera();         // 在暂停事件中立即释放摄像头

    } 

 

    private void releaseMediaRecorder(){ 

        if (mMediaRecorder != null) { 

            mMediaRecorder.reset(); // 清除recorder配置

            mMediaRecorder.release(); // 释放recorder对象

            mMediaRecorder = null; 

            mCamera.lock();           // 为后续使用锁定摄像头

        } 

    } 

 

    private void releaseCamera(){ 

        if (mCamera != null){ 

            mCamera.release();        // 为其它应用释放摄像头

            mCamera = null; 

        } 

    } 

}

警告:如果某应用程序未能正确释放摄像头,所有后续访问摄像头的尝试(包括该应用程序自身)都将会失败,并可能会导致应用程序被强行关闭。

 

 

保存媒体文件

诸如图片和视频这些由用户创建的媒体文件,应该保存到设备外部存储的目录中(SD卡)去,以节省系统空间,并使用户离开设备时也能访问这些文件。设备上有很多可用于存储媒体文件的目录,但作为开发人员只应考虑两个标准的位置:

·       Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) —— 本方法返回标准的、共享的、系统建议的存放位置,用于存放图片和视频文件。本目录是共享的(public),因此其它应用程序可以很容易地查找、读取、修改、删除存于此处的文件。即使应用程序被用户卸载,存于此处的媒体文件也不会被删除。为了避免与已有的图片和视频相冲突,应该在此目录下为自己的媒体文件创建一个子目录,如下代码所示。本方法自Android 2.2 (API Level 8) 起启用,更早API版本的也有类似的调用,请参阅保存共享文件

·       Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) —— 本方法返回一个标准的、用于存放图片和视频的位置,该存放位置与应用程序相关联。如果应用程序被卸载,则存于此处的文件将会被删除。对存于此处的文件不会增加访问权限控制,其它应用程序也可以读取、修改、删除文件。

以下例程演示了如何为媒体文件创建一个FileUri存放位置,通过Intent调用摄像头时可以使用该文件,创建摄像应用时也可以使用它。

public static final int MEDIA_TYPE_IMAGE = 1; 

public static final int MEDIA_TYPE_VIDEO = 2; 

 

/** 为保存图片或视频创建文件Uri */ 

private static Uri getOutputMediaFileUri(int type){ 

      return Uri.fromFile(getOutputMediaFile(type)); 

} 

 

/** 为保存图片或视频创建File */ 

private static Uri getOutputMediaFile(int type){ 

    // 安全起见,在使用前应该

    // Environment.getExternalStorageState()检查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; 

}

注意: Environment.getExternalStoragePublicDirectory() Android 2.2 (API Level 8) 版本启用。如果目标设备使用较早期版本的Android,请用Environment.getExternalStorageDirectory() 代替。详情请参阅保存共享文件

关于在Android设备上保存文件的详细信息,请参阅数据存储

 

 

分享到:
评论

相关推荐

    摄像头udp传输 摄像头udp传输

    摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输摄像头udp传输...

    pb11.5调摄像头_pb调用摄像头_pb11.5调用摄像头_

    在本文中,我们将深入探讨如何在PowerBuilder 11.5中调用摄像头这一主题。PowerBuilder是一款强大的、基于事件的编程环境,主要用于开发企业级应用程序。随着技术的发展,集成多媒体功能,如摄像头访问,成为了现代...

    海康摄像头抓拍图片,摄像头信息从数据库读取

    在IT行业中,海康摄像头广泛应用于视频监控领域,其功能丰富且可高度集成。本话题主要涉及海康摄像头与数据库的交互,以及如何利用数据库来管理摄像头信息进行图片抓拍。我们将深入探讨以下几个方面: 1. **数据库...

    usb摄像头ocx控件

    USB摄像头OCX控件是一种特殊的软件组件,它允许开发者在应用程序中集成USB摄像头的功能。在Windows操作系统中,OCX(ActiveX Control)是基于OLE技术的控件,用于提供图形用户界面元素,如按钮、文本框等,也可以...

    html5网页打开摄像头PC端摄像头

    描述中提到的“有5个例子”,可能包括了针对不同浏览器的兼容性解决方案,以及各种功能的实现,如调整摄像头分辨率、切换摄像头设备、拍照或录制视频等。这些功能可以通过`MediaStreamTrack`对象和`MediaRecorder` ...

    网络摄像头上位机代码(TCP)_摄像头_TCP上位机驱动_tcp图像上位机_网络摄像头_stm32_

    在IT领域,网络摄像头的开发是一项复杂而关键的工作,尤其涉及到STM32微控制器和TCP/IP通信协议。本文将深入探讨标题和描述中提到的知识点,包括STM32驱动摄像头、TCP上位机驱动、TCP图像上位机以及网络摄像头的相关...

    手机摄像头电路原理及故障维修

    手机摄像头是现代智能手机的核心组成部分,它使得用户可以轻松地拍摄照片和录制视频。手机摄像头的电路原理涉及到多个组件,包括中央处理器(CPU)和摄像头模块。CPU不仅负责处理整体操作系统,还集成有视频处理系统...

    C#操作摄像头(打开、关闭、截图)_C#操作摄像头_

    在C#编程环境中,操作摄像头涉及到了多媒体编程和设备访问技术。本文将深入探讨如何使用C#来实现摄像头的打开、关闭以及截图功能,并提供一个完整的程序代码示例。 首先,要操作摄像头,我们需要引入一个名为`...

    dicretshow开发的虚拟摄像头源码

    《dicretshow开发的虚拟摄像头源码解析与应用探索》 在现代的多媒体技术中,虚拟摄像头已经成为一种不可或缺的工具,尤其在远程会议、在线教学、直播等领域中扮演着重要角色。本文将深入探讨由dicretshow开发的虚拟...

    AndroidUSB摄像头含拍照

    在Android平台上,USB摄像头的集成和使用是一项技术性较强的任务,尤其涉及到硬件接口的检测、设备识别以及拍照功能的实现。本项目"Android USB摄像头含拍照"提供了一个完整的解决方案,帮助开发者实现Android设备...

    vb控制USB摄像头

    标题中的“vb控制USB摄像头”指的是使用Visual Basic(VB)6.0编程语言来操作和控制连接到计算机的USB摄像头设备。VB 6.0是Microsoft推出的一种面向对象的编程工具,广泛应用于Windows应用程序开发。 在描述中提到...

    C# 控制虚拟摄像头

    虚拟摄像头,也称为软件摄像头,是一种能够模拟真实物理摄像头功能的程序,它可以从本地视频文件、网络流或其他数据源提供视频输入。 在C#中实现虚拟摄像头的控制,主要涉及以下几个关键技术点: 1. **DirectShow*...

    ffmpeg调取摄像头录像

    在"ffmpeg调取摄像头录像"这一主题中,我们将深入探讨如何利用FFmpeg来捕获并录制来自摄像头的实时视频流。 首先,FFmpeg通过libavdevice库与各种输入/输出设备进行交互,包括摄像头。在Linux系统中,摄像头通常被...

    opencv 打开多个摄像头

    在OpenCV库中,可以轻松地处理多个摄像头的视频流,这对于进行立体视觉、多视图几何或同时监控多个视角的应用来说是至关重要的。在这个示例中,我们将深入探讨如何使用OpenCV 2.4.6版本和Visual C++ 2008编译器来...

    简单易用的摄像头OCX控件

    在IT领域,摄像头OCX控件是一种用于软件开发的组件,它使得程序员能够轻松地集成摄像头功能到他们的应用程序中。OCX(OLE Control Extension)是微软在早期Windows系统中引入的一种控件技术,它是ActiveX控件的一种...

    多路摄像头拍照系统(多路摄像头拍摄软件)

    多路摄像头拍照系统(多路摄像头拍摄软件)是一款十分方便实用的多路摄像头拍摄拍照软件。多路摄像头拍照系统功能强大,简单易操作,高清拍照和高清录像功能让监控画质更加的稳定清晰,给用户更好的监控体验。软件提供...

    win7显示并美化摄像头

    在Windows 7操作系统中,摄像头功能的使用可能会遇到一些挑战,尤其是在旧版系统或未安装相应驱动的情况下,摄像头图标可能无法正常显示。这个问题对于需要进行视频通话、录制视频或者使用其他依赖摄像头的应用程序...

    win7win10摄像头补丁.zip

    标题中的“win7win10摄像头补丁.zip”指的是针对Windows 7和Windows 10操作系统的摄像头功能增强补丁。这个补丁主要是为了解决或优化与摄像头相关的某些问题,比如在资源管理器中不显示摄像头图标或者使用外接摄像头...

    VC 打开摄像头 (使用DirectShow)

    在VC++编程环境中,使用DirectShow库可以实现与摄像头交互的功能,包括实时预览和捕获图像。DirectShow是微软提供的一种高级多媒体流处理框架,适用于视频和音频的捕获、处理以及播放。本篇文章将深入探讨如何在MFC...

Global site tag (gtag.js) - Google Analytics