Android開發者指南-攝像頭-Camera[原創譯文]

Camera

版本:Android 4.0 r1

在本文中

需要考慮的問題

概述

Manifest聲明

使用內置的攝像頭應用程序

捕獲圖像的intent

捕獲視頻的intent

接收攝像頭intent的結果

創建攝像頭應用程序

檢測攝像頭硬件

訪問攝像頭

檢查攝像頭feature

創建預覽類

將預覽畫面置入layout

捕獲圖像

捕獲視頻

釋放攝像頭

保存媒體文件

關鍵類

Camera

SurfaceView

MediaRecorder

Intent

參閱

Camera

MediaRecorder

數據存儲

Android框架包含了對多種攝像頭和攝像特性的支持,應用程序可以進行圖片和視頻的捕獲。本文討論了一種快速、簡便的捕獲圖像和視頻的方法,並簡述了一種更高級的可爲用戶創建自定義攝像功能的方法。

需要考慮的問題

在讓應用程序使用Android設備的攝像頭之前,應該考慮一些期望如何使用此硬件的問題。

  • 攝像頭需求—— 攝像頭的使用對於應用程序是否確實如此重要,以至於在沒有攝像頭的設備上就不期望安裝此應用了?如果確實如此,應該 在manifest中聲明攝像頭需求
  • 快速拍照還是自定義攝像—— 應用程序如何使用攝像頭?僅僅是對快速拍照和視頻片段感興趣,還是要提供一種使用攝像頭的新方式?對於快速拍照和攝像而言,可以考慮 使用內置的攝像頭應用 。爲了開發一種定製的攝像頭功能,請查看 創建攝像頭應用 一節。
  • 存儲—— 應用程序產生的圖像和視頻是否期望僅對自身可見,還是可以共享——以便相冊或其它媒體應用也能夠使用?當應用程序被卸載後,還期望圖像和視頻可用麼?請查看 保存媒體文件 一節來了解如何實現這些選項。

概述

通過 Camera API或攝像頭意圖 Intent ,Android框架爲圖像和視頻捕獲提供支持。下面列出了有關的類:
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:

  • 構建一個攝像頭Intent——用以下意圖類型之一,創建一個請求圖像或視頻的 Intent
  • 啓動攝像頭Intent——用 startActivityForResult() 方法執行攝像頭intent。啓動完畢後攝像頭應用的用戶界面就會顯示在屏幕上,用戶就可以拍照或攝像了。
  • 接收Intent結果——在應用程序中設置 onActivityResult() 方法,用於接收從攝像頭intent返回的數據。當用戶拍攝完畢後(或者取消操作),系統會調用此方法。

捕獲圖像的intent

如果希望程序以最少的代碼實現拍照功能,利用攝像頭intent捕獲圖像是一條捷徑。圖像捕捉intent還可以包含以下附加信息:

  • MediaStore.EXTRA_OUTPUT——本設置需要一個 Uri對象,用於指定存放圖片的路徑和文件名。本設置是可選項,但強烈建議使用。如果未指定本設置值,那麼攝像應用將會把所請求的圖片以默認文件名和路徑進行保存,並將數據置入intent的 Intent.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 ,用於指定保存視頻的路徑和文件名。本設置是可選項,但強烈建議使用。如果未指定本設置值,那麼攝像應用將會把所請求的視頻以默認文件名和路徑進行保存,並將數據置入intent的 Intent.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 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的activity中的 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()方法來訪問主攝像頭,並確保捕獲全部的異常,示例代碼如下:

public   static Camera  getCameraInstance(){

Camera c=null;

   try {

        c=Camera.open();//試圖獲取Camera實例

    }

    catch(Exceptione){

       //攝像頭不可用(正被佔用或不存在)

    }

    returnc;//不可用則返回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.0的Android還需要用到。

        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(SurfaceHolderholder){

       //空代碼。注意在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"?> 

<LinearLayoutxmlns: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也改爲橫向顯示。

<activityandroid:name=".CameraActivity" 

          android:label="@string/app_name" 

          android:screenOrientation="landscape"> 

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

   <intent-filter> 

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

        <categoryandroid: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,加入創建預覽類所述的預覽類。

publicclassCameraActivityextendsActivity{ 

    privateCamera mCamera; 

    privateCameraPreview mPreview; 

     @Override 

    publicvoid onCreate(Bundle savedInstanceState){ 

        super.onCreate(savedInstanceState); 

        setContentView(R.layout.main); 

 

        // 創建Camera實例 

        mCamera = getCameraInstance(); 

 

        // 創建Preview view並將其設爲activity中的內容

        mPreview =newCameraPreview(this, mCamera); 

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

        preview.addView(mPreview); 

    } 

}

注意:上例中的getCameraInstance()方法引用了訪問攝像頭中的方法示例。

 

捕獲圖像

一旦創建了預覽類和顯示它的view layout,就可以開始在程序中捕獲圖片了。必須在程序代碼中爲用戶界面控件設置listener,使其可響應用戶操作進行拍照。

可以通過Camera.takePicture()方法來獲取圖片,此方法用到三個參數並從攝像頭接收數據。如果要以JPEG的格式接收數據,必須實現Camera.PictureCallback接口,以接收圖片數據並寫入文件。以下代碼展示了Camera.PictureCallback接口的簡單例子,實現了從攝像頭接收圖片並保存。

privatePictureCallback mPicture =newPictureCallback(){ 

 

    @Override 

    publicvoid 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 =newFileOutputStream(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( 

        newView.OnClickListener(){ 

        @Override 

        publicvoid 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()調用由系統自動管理。

與用攝像頭拍照不同,視頻捕獲必需十分精確地按順序進行調用。必須按照特定的順序來執行,應用程序才能成功地準備並捕獲視頻,詳細步驟如下。

 

配置MediaRecorder

在使用MediaRecorder類進行錄像時,必須先按照特定順序進行配置,然後調用MediaRecorder.prepare()方法檢查並執行這些配置。以下例程演示瞭如何爲錄像正確配置並準備MediaRecorder類。

privateboolean prepareVideoRecorder(){ 

 

    mCamera = getCameraInstance(); 

    mMediaRecorder =newMediaRecorder(); 

 

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

        returnfalse; 

    }catch(IOException e){ 

        Log.d(TAG,"IOException preparing MediaRecorder: "+ e.getMessage()); 

        releaseMediaRecorder(); 

        returnfalse; 

    } 

    returntrue; 

}

如果是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中以下有關視頻錄製的參數都給出了缺省值,當然也可以在應用程序中修改這些設置:

開始和停止MediaRecorder

使用MediaRecorder類開始和停止視頻錄製時,必須遵循以下特定順序。

以下例程演示瞭如何觸發按鈕並用cameraMediaRecorder類正確地開始和停止視頻錄製。

注意:視頻錄製完畢後請不要釋放camera,否則預覽將會停止。

privateboolean isRecording =false; 

 

// Capture按鈕加入listener

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

captureButton.setOnClickListener( 

    newView.OnClickListener(){ 

        @Override 

        publicvoid 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對象的實例,代碼示例如下。

publicclassCameraActivityextendsActivity{ 

    privateCamera mCamera; 

    privateSurfaceView mPreview; 

    privateMediaRecorder mMediaRecorder; 

    ...

@Override

   protected   void  onPause(){

       super.onPause(); 

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

        releaseCamera();         // 在暫停事件中立即釋放攝像頭

    } 

 

    privatevoid releaseMediaRecorder(){ 

        if(mMediaRecorder !=null){ 

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

            mMediaRecorder.release();// 釋放recorder對象

            mMediaRecorder =null; 

            mCamera.lock();           // 爲後續使用鎖定攝像頭

        } 

    } 

 

    privatevoid 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調用攝像頭時可以使用該文件,創建攝像應用時也可以使用它。

publicstaticfinalint MEDIA_TYPE_IMAGE =1; 

publicstaticfinalint MEDIA_TYPE_VIDEO =2; 

 

privatestaticUri getOutputMediaFileUri(int type){ 

      returnUri.fromFile(getOutputMediaFile(type)); 

} 

 

privatestaticUri getOutputMediaFile(int type){ 

    // 安全起見,在使用前應該

    // Environment.getExternalStorageState()檢查SD卡是否已裝入

 

    File mediaStorageDir =newFile(Environment.getExternalStoragePublicDirectory( 

              Environment.DIRECTORY_PICTURES),"MyCameraApp"); 

// 如果期望圖片在應用程序卸載後還存在、且能被其它應用程序共享,

// 則此保存位置最合適

 

    // 如果不存在的話,則創建存儲目錄

    if(! mediaStorageDir.exists()){ 

        if(! mediaStorageDir.mkdirs()){ 

            Log.d("MyCameraApp","failed to create directory"); 

            returnnull; 

        } 

    } 

    // 創建媒體文件名

    String timeStamp =newSimpleDateFormat("yyyyMMdd_HHmmss").format(newDate()); 

    File mediaFile; 

    if(type == MEDIA_TYPE_IMAGE){ 

        mediaFile =newFile(mediaStorageDir.getPath()+File.separator + 

        "IMG_"+ timeStamp +".jpg"); 

    }elseif(type == MEDIA_TYPE_VIDEO){ 

        mediaFile =newFile(mediaStorageDir.getPath()+File.separator + 

        "VID_"+ timeStamp +".mp4"); 

    }else{ 

        returnnull; 

    } 

    return mediaFile; 

}

注意:Environment.getExternalStoragePublicDirectory()Android 2.2 (API Level 8) 版本啓用。如果目標設備使用較早期版本的Android,請用Environment.getExternalStorageDirectory()代替。詳情請參閱保存共享文件

關於在Android設備上保存文件的詳細信息,請參閱數據存儲


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章