以下內容翻譯來源,Google官方開發文檔:https://developer.android.google.cn/guide/topics/media/camera.html
相關的類
- android.hardware.camera2
- Camera
- SurfaceView---這個類用于向用戶呈現實時相機預覽。
- MediaRecorder---這個類用于從攝像機錄制視頻。
- Intent---MediaStore.ACTION_IMAGE_CAPTURE或MediaStore.ACTION_VIDEO_CAPTURE可用于捕獲圖像或視頻,而無需直接使用Camera對象。
清單聲明
在使用Camera API開始開發應用程序之前,應確保您的清單具有適當的聲明,以允許使用相機硬件和其他相關功能。
-
相機權限 - 您的應用程序必須請求使用設備相機的權限。
<uses-permission android:name="android.permission.CAMERA" />
注意:如果您通過調用現有的攝像頭應用程序來使用攝像頭,則應用程序不需要請求此權限。
-
相機功能 - 您的應用程序還必須聲明使用相機功能,例如:
<uses-feature android:name="android.hardware.camera" />
-
存儲權限 - 如果應用程序將圖像或視頻保存到設備的外部存儲設備(SD卡),則還必須在清單中指定此選項。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
音頻錄制權限 - 對于使用視頻捕獲錄制音頻,應用程序必須請求獲取音頻捕獲權限。
<uses-permission android:name="android.permission.RECORD_AUDIO" />
創建自定義攝像頭
為應用程序創建自定義攝像頭界面的一般步驟如下:
- 檢測和訪問攝像機 - 創建代碼以檢查攝像機是否存在并請求訪問。
- 創建預覽類 - 創建擴展SurfaceView并實現SurfaceHolder界面的相機預覽類。這個類預覽來自相機的實時圖像。
- 構建預覽布局 - 一旦你擁有相機預覽類,創建一個包含預覽和你想要的用戶界面控件的視圖布局。
- Capture的監聽器 - 為您的接口控件連接偵聽器,以響應用戶操作(例如按下按鈕)開始捕獲圖像或視頻。
- 捕獲和保存文件 - 設置用于捕獲圖片或視頻和保存輸出的代碼。
- 釋放相機 - 使用相機后,應用程序必須正確釋放它以供其他應用程序使用。
相機硬件是一個共享資源,必須仔細管理,以便您的應用程序不會與其他可能也想使用它的應用程序沖突。以下部分討論如何檢測相機硬件,如何請求訪問相機,如何捕獲圖片或視頻以及如何在應用程序使用完成后釋放相機。
檢測相機硬件
如果您的應用程序沒有特別要求使用清單聲明的攝像機,則應檢查相機在運行時是否可用。要執行此檢查,請使用PackageManager.hasSystemFeature()方法,如下面的示例代碼所示:
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
} else {
// no camera on this device
return false;
}
}
Android設備可以具有多個攝像機,例如用于攝影的背面攝像機和用于視頻通話的前置攝像機。Android 2.3(API Level 9)及更高版本允許您使用Camera.getNumberOfCameras()方法檢查設備上可用的攝像機數量。
訪問相機
如果您確定運行應用程序的設備具有攝像頭,則必須通過獲取攝像頭實例來請求訪問它(除非您正在使用意圖來訪問一個攝像頭)。
要訪問主要攝像機,請使用Camera.open()方法,并確保捕獲任何異常,如下面的代碼所示:
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
注意:使用Camera.open()時始終檢查異常。如果相機正在使用或不存在,則無法檢查異常,將導致您的應用程序被系統關閉。
在運行Android 2.3(API Level 9)或更高版本的設備上,您可以使用Camera.open(int)訪問特定的攝像機。上面的示例代碼將訪問具有多個攝像頭的設備上的第一個后置攝像頭
檢查相機功能
獲取對攝像機的訪問權限后,您可以使用Camera.getParameters()方法獲取有關其功能的更多信息,并檢查返回的Camera.Parameters對象以獲取支持的功能。使用API?? Level 9或更高版本時,請使用Camera.getCameraInfo()確定相機是位于設備的正面還是背面,以及圖像的方向。
創建預覽類
為了讓用戶有效地拍攝照片或視頻,他們必須能夠看到設備相機看到的內容。相機預覽類是SurfaceView,可以顯示來自相機的實時圖像數據,因此用戶可以框架并捕獲圖片或視頻。
以下示例代碼演示了如何創建可包含在視圖布局中的基本攝像機預覽類。這個類實現了SurfaceHolder.Callback,以便捕獲用于創建和銷毀視圖的回調事件,這是分配攝像機預覽輸入所需要的。
/** 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;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
如果要為相機預覽設置特定大小,請在上述注釋中所述的surfaceChanged()方法中設置此大小。設置預覽大小時,必須使用getSupportedPreviewSizes()的值。不要在setPreviewSize()方法中設置任意值。
注意:隨著Android 7.0(API級別24)及更高版本中多窗口功能的引入,即使在調用setDisplayOrientation()之后,也不能再假設預覽的縱橫比與您的活動相同。根據窗口大小和寬高比,您可能必須使用信箱布局將寬相機預覽適配為縱向布局,反之亦然。
在布局中放置預覽
相機預覽類(例如上一部分中所示的示例)必須與用于拍攝圖片或視頻的其他用戶界面控件一起放置在活動的布局中。
<?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>
在大多數設備上,相機預覽的默認方向為橫向。此示例布局指定水平(橫向)布局,以下代碼將應用程序的方向固定為橫向。為了簡化渲染相機預覽,您應該將以下內容添加到清單中,以將應用程序的預覽活動方向更改為橫向。
<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級別8)開始,您可以使用setDisplayOrientation()方法設置預覽圖像的旋轉。為了在用戶重新定位手機時更改預覽方向,請在預覽類的surfaceChanged()方法中,首先使用Camera.stopPreview()停止預覽,更改方向,然后再次使用Camera.startPreview()啟動預覽。
在相機視圖的活動中,將預覽類添加到上面示例中所示的FrameLayout元素。您的相機活動還必須確保在暫停或關閉時釋放相機。以下示例顯示如何修改相機活動以附加創建預覽類中顯示的預覽類。
public class CameraActivity extends Activity {
private Camera mCamera;
private CameraPreview mPreview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create an instance of Camera
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);
}
}
注意:上面示例中的getCameraInstance()方法指的是訪問攝像機中顯示的示例方法。
捕獲圖片
一旦構建了預覽類和顯示它的視圖布局,就可以開始使用應用程序捕獲圖像。在應用程序代碼中,您必須為用戶界面控件設置偵聽器,以通過拍攝圖片來響應用戶操作。在應用程序代碼中,您必須為用戶界面控件設置偵聽器,以通過拍攝圖片來響應用戶操作。
為了檢索圖片,請使用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調用此方法。
// 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) {
// get an image from the camera
mCamera.takePicture(null, null, mPicture);
}
}
);
警告:記住,當您的應用程序使用它時,通過調用Camera.release()來釋放Camera對象!有關如何釋放相機的信息,請參閱釋放相機。
捕獲視頻
使用Android框架的視頻捕獲需要仔細管理Camera對象和與MediaRecorder類的協調。使用Camera錄制視頻時,除了Camera.open()和Camera.release()調用外,還必須管理Camera.lock()和Camera.unlock()調用,以允許MediaRecorder訪問攝像機硬件。
注意:從Android 4.0(API級別14)開始,Camera.lock()和Camera.unlock()調用會自動管理。
與使用設備相機拍攝照片不同,捕獲視頻需要非常特殊的通話順序。您必須遵循特定的執行順序,才能使用您的應用程序成功準備和捕獲視頻,如下所述。
- 打開相機 - 使用Camera.open()獲取相機對象的實例。
- 連接預覽 - 通過使用Camera.setPreviewDisplay()將SurfaceView連接到攝像機,準備實時的攝像機圖像預覽
- 開始預覽 - 調用Camera.startPreview()開始顯示實時攝像機圖像。
- 開始錄制視頻 - 必須完成以下步驟才能成功錄制視頻:
- 解鎖相機 - 通過調用Camera.unlock()解鎖相機以供MediaRecorder使用。
- 配置MediaRecorder - 按以下順序調用以下MediaRecorder方法。有關更多信息,請參閱MediaRecorder參考文檔。
- setCamera() - 設置要用于視頻捕獲的攝像機,使用應用程序的當前攝像機實例。
- setAudioSource() - 設置音頻源,使用MediaRecorder.AudioSource.CAMCORDER。
- setVideoSource() - 設置視頻源,使用MediaRecorder.VideoSource.CAMERA。
- 設置視頻輸出格式和編碼。對于Android 2.2(API Level 8)及更高版本,請使用MediaRecorder.setProfile方法,并使用CamcorderProfile.get()獲取配置文件實例。對于2.2之前的Android版本,必須設置視頻輸出格式和編碼參數:
- setOutputFormat() - 設置輸出格式,指定默認設置或MediaRecorder.OutputFormat.MPEG_4
- setAudioEncoder() - 設置聲音編碼類型,指定默認設置或MediaRecorder.AudioEncoder.AMR_NB。
- setVideoEncoder() - 設置視頻編碼類型,指定默認設置或MediaRecorder.VideoEncoder.MPEG_4_SP。
- 準備MediaRecorder - 通過調用MediaRecorder.prepare()為提供的配置設置準備MediaRecorder。
- 啟動MediaRecorder - 通過調用MediaRecorder.start()開始錄制視頻
- 停止錄制視頻 - 按順序調用以下方法,以成功完成視頻錄制:
- 停止MediaRecorder - 停止通過調用MediaRecorder.stop()錄制視頻。
- 重置MediaRecorder - (可選)通過調用MediaRecorder.reset()從記錄器中刪除配置設置。
- 釋放MediaRecorder - 通過調用MediaRecorder.release()釋放MediaRecorder。
- 鎖定相機 - 鎖定相機,以便將來的MediaRecorder會話可以通過調用Camera.lock()來使用它。從Android 4.0(API級別14)開始,不需要此調用,除非MediaRecorder.prepare()調用失敗。
- 停止預覽 - 當您的活動使用相機完成后,使用Camera.stopPreview()停止預覽。
- 釋放相機 - 釋放相機,以便其他應用程序可以通過調用Camera.release()來使用它。
注意:可以先使用MediaRecorder而不創建相機預覽,并跳過此過程的前幾個步驟。然而,由于用戶通常喜歡在開始記錄之前看到預覽,所以這里不討論該處理。
提示:如果您的應用程序通常用于錄制視頻,請在開始預覽之前將setRecordingHint(boolean)設置為true。此設置可以幫助減少開始錄制所需的時間。
配置MediaRecorder
當使用MediaRecorder類記錄視頻時,必須按特定順序執行配置步驟,然后調用MediaRecorder.prepare()方法來檢查和實現配置。以下示例代碼演示如何正確配置和準備MediaRecorder類進行視頻錄制。
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;
}
在Android 2.2(API級別8)之前,您必須直接設置輸出格式和編碼格式參數,而不是使用CamcorderProfile。這種方法在下面的代碼中演示:
// 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);
MediaRecorder的以下視頻錄制參數被賦予默認設置,但是,您可能想要為應用程序調整這些設置:
setVideoEncodingBitRate()
setVideoSize()
setVideoFrameRate()
setAudioEncodingBitRate()
setAudioChannels()
setAudioSamplingRate()
啟動和停止MediaRecorder
當使用MediaRecorder類啟動和停止視頻錄制時,必須遵循以下列出的特定順序。
- 使用Camera.unlock()解鎖相機
- 配置MediaRecorder,如上面的代碼示例所示
- 使用MediaRecorder.start()開始錄制
- 錄制視頻
- 使用MediaRecorder.stop()停止錄制
- 使用MediaRecorder.release()釋放媒體記錄器
- 使用Camera.lock()鎖定相機
以下示例代碼演示了如何連接一個按鈕,以使用相機和MediaRecorder類正確啟動和停止視頻錄制。
注意:完成視頻錄制時,請勿釋放相機,否則將停止預覽。
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
}
}
}
}
);
注意:在上面的示例中,prepareVideoRecorder()方法引用配置MediaRecorder中顯示的示例代碼。此方法負責鎖定相機,配置和準備MediaRecorder實例。(詳見例子)
釋放相機
攝像機是由設備上的應用程序共享的資源。您的應用程序可以在獲取相機實例后使用相機,并且您的應用程序停止使用時,以及應用程序暫停后(Activity.onPause()),您必須特別小心釋放相機對象。如果應用程序未正確釋放相機,則隨后嘗試訪問相機(包括您自己的應用程序)將會失敗,并可能導致您的或其他應用程序關閉。
要釋放Camera對象的實例,請使用Camera.release()方法,如下面的示例代碼所示。
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;
}
}
}
警告:如果您的應用程序未正確釋放相機,則隨后嘗試訪問相機(包括您自己的應用程序)將會失敗,并可能導致您的或其他應用程序關閉。
保存媒體文件
用戶創建的媒體文件(如圖片和視頻)應保存到設備的外部存儲目錄(SD卡),以節省系統空間,并允許用戶在沒有設備的情況下訪問這些文件。在設備上保存媒體文件有許多可能的目錄位置,但是您應該考慮作為開發人員只有兩個標準位置:
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) --此方法返回用于保存圖片和視頻的標準,共享和推薦的位置。此目錄是共享(公共),因此其他應用程序可以輕松地發現,讀取,更改和刪除保存在此位置的文件。如果用戶卸載了您的應用程序,則不會刪除保存到此位置的媒體文件。了避免干擾用戶現有的圖片和視頻,您應該為此目錄中的應用程序的媒體文件創建一個子目錄,如下面的代碼示例所示。此方法在Android 2.2(API級別8)中可用,適用于早期API版本中的等效調用,請參閱保存共享文件。
- Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) ---此方法返回用于保存與您的應用程序相關聯的圖片和視頻的標準位置。如果您的應用程序已卸載,則會刪除保存在此位置的所有文件。不對此位置中的文件強制執行安全性,其他應用程序可能會讀取,更改和刪除它們。
以下示例代碼演示了如何為媒體文件創建文件或Uri位置,當使用Intent或作為構建相機應用程序的一部分調用設備的攝像機時,可以使用該位置。
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
/** Create a file Uri for saving an image or video */
private static Uri getOutputMediaFileUri(int type){
return Uri.fromFile(getOutputMediaFile(type));
}
/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
// 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 exist
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;
}
注意:Environment.getExternalStoragePublicDirectory()在Android 2.2(API級別8)或更高版本中可用。如果您要使用早期版本的Android定位設備,請改用Environment.getExternalStorageDirectory()。有關詳細信息,請參閱保存共享文件。
有關在Android設備上保存文件的詳細信息,請參閱數據存儲。
相機功能(用來設置更加詳細的視屏,例如各種比例和白平衡等。后期再補充詳細)
Android支持您可以使用相機應用程序控制的各種相機功能,例如圖片格式,閃光模式,對焦設置等。本節列出常見的攝像機功能,并簡要討論如何使用它們。大多數攝像機功能可以使用通過Camera.Parameters對象訪問和設置。然而,有幾個重要的功能,需要的不僅是Camera.Parameters中的簡單設置。這些功能將在以下部分中介紹:
- 測光和對焦區域
- 面部檢測
- 時間流逝視頻