Android Camera開發(一)之基礎知識

本文轉載自Watson的博客:Android Camera開發(一)之基礎知識


概述

Android手機關於Camera的使用,一是拍照,二是攝像,由於Android提供了強大的組件功能,爲此對於在Android手機系統上進行Camera的開發,我們可以使用兩類方法:一是藉助Intent和MediaStore調用系統Camera App程序來實現拍照和攝像功能,二是根據Camera API自寫Camera程序。

基礎知識

Android系統提供API來支持自定義相機拍照和系統拍照,以下是有關的類:

  • Camera
    該類提供基礎API來使用設備上的相機,且該類可以爲你的應用提供拍照和錄像相關的API。
  • SurfaceView
    該類用於顯示相機的預覽數據。如果你對SurfaceView還不熟悉,請參考Android SurfaceView的使用這篇文章。
  • MediaRecorder
    該類提供相機錄像相關的API。

注意事項

在你的應用程序能夠在Android設備上使用相機之前,你應該考慮幾個問題,那就是你的App打算如何使用相機拍照或者錄像?

  • Camera需求的聲明:
    使用相機功能對於你的應用程序來說是否很重要並且你不希望你的應用程序被安裝在沒有相機的機器上?如果是這樣,那麼你需要把相機需求聲明在配置文件裏。
  • 調用系統拍照還是自定義相機:
    你的應用程序該如何使用相機?你是否僅僅需要拍攝一張照片或者一個視頻,或者你的應用程序希望提供一種使用相機的新的方式?
  • 存儲:
    是否你的應用生成的圖片和視頻僅對你的應用可見,還是其他應用程序例如相冊或者其他的多媒體和社交App也可以使用它們?你是否希望你的應用程序被卸載後,這些照片和視頻仍然可用,還是一起被刪除?

權限申明

  • Camera Permission - 你的應用必須申請相機權限纔可以使用設備相機。
<uses-permission android:name="android.permission.CAMERA" />
  • 1

注意:如果你使用Intent調用系統相機,你的應用無需申請該權限。

  • Storage Permission - 如果你的應用需要保存照片或者視頻到設備存儲中,你必須在Manifest指定文件的寫權限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 1
  • Audio Recording Permission - 你必須申請錄音權限才能使用相機來錄像.
<uses-permission android:name="android.permission.RECORD_AUDIO" />
  • 1
  • Location Permission - 當然如果你需要拍攝的照片記錄地理位置,你同樣需要申請如下權限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  • 1

調用系統相機

你的應用可以通過發送一個Intent 到系統相機應用來實現抓取一張照片或者一段視頻剪輯,然後將它們返回給你的應用。
使用camera intent調用系統相機流程如下:
(1)Compose a Camera Intent - 創建一個Intent請求用來拍照或者錄像,有關的Intent類型如下:

  • MediaStore.ACTION_IMAGE_CAPTURE - 該Intent action 類型用於請求系統相機拍照。
  • MediaStore.ACTION_VIDEO_CAPTURE - 該Intent action 類型用於請求系統相機錄像。

(2)Start the Camera Intent - 調用activity的startActivityForResult()方法來發送camera intent請求拍照或者錄像,當發送camera intent 以後,當前應用會跳轉到系統相機應用app界面,讓用戶可以拍照或者錄像。
(3)Receive the Intent Result - 在你的應用中實現onActivityResult()回調方法去接收來自系統相機的拍攝結果。該方法在用戶完成拍照或者錄像以後由系統調用。

系統拍照

代碼如下,按上面的三步走:

button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
            }
        });

...

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        /**
         * 通過data取得數據
         */
        if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
            Bundle extras = data.getExtras();
            Bitmap bitmap = (Bitmap) extras.get("data");
            image.setImageBitmap(bitmap);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

但是,現在手機像素這麼高,萬一圖片特別大呢,會不會data過大而FC呢?放心,Android早就考慮到了,所以,data裏面壓根就不是完整的圖片,它只是一張縮略圖。所以,我們需要獲取到拍攝的原圖,就不能使用這種方法。但是我們可以這樣做,我們可以指定MediaStore類的一個EXTRA_OUTPUT來指定拍攝圖像保存的位置,相當於建立一個臨時文件。在onActivityResult中,我們不使用data來獲取圖像,而是直接去讀這個臨時文件即可。如果自己代碼指定了保存圖片的uri,data裏面就不會保存數據。

button1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                Uri fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
                startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
            }
        });
...

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        /**
         * 通過存儲Uri取得數據
         */
        if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                image.setImageURI(getOutputMediaFileUri(MEDIA_TYPE_IMAGE));
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

這樣我們就可以獲取到完整的拍攝圖片了。後面你可以讓圖像顯示出來。

下面來看看保存多媒體文件:
拍照或者錄像生成的多媒體文件需要保存到手機存儲目錄中(SD Card),所以在應用中必須有往手機中寫文件的權限。一般可以有多種本地路徑來保存多媒體文件,但是主要有如下兩種常用的路徑:

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

    /** 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){
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "WatsonCamera");

        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                return null;
            }
        }

        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_watson.jpg");
        } else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_watson.mp4");
        } else {
            return null;
        }

        return mediaFile;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

系統錄像

發送Intent錄像攜帶的外部數據extra的信息如下:

  • MediaStore.EXTRA_OUTPUT
    該關鍵字和拍照使用的關鍵字一樣,意思就是制定一個路徑和文件名來構建一個Uri對象來保存錄像結果。
  • MediaStore.EXTRA_VIDEO_QUALITY
    該關鍵字用於指定拍攝的錄像質量,參數0表示低質量,參數1表示高質量。
  • MediaStore.EXTRA_DURATION_LIMIT
    該關鍵之用於指定拍攝的錄像的時間限制,單位是秒。
  • MediaStore.EXTRA_SIZE_LIMIT
    該關鍵字用於指定拍攝的錄像文件大小限制,單位值byte。

代碼如下,按上面的三步走:

button2.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                Uri fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
                intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
                startActivityForResult(intent, CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE);
            }
        });

...

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
           if (resultCode == RESULT_OK) {
                image.setVisibility(View.VISIBLE);
                video.setVisibility(View.GONE);       
                image.setImageURI(getOutputMediaFileUri(MEDIA_TYPE_IMAGE));
            }
        } else if (requestCode == CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                video.setVisibility(View.VISIBLE);
                image.setVisibility(View.GONE);
                video.setVideoURI(getOutputMediaFileUri(MEDIA_TYPE_VIDEO));
                video.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                    @Override
                    public void onPrepared(MediaPlayer mp) {
                        video.start();
                    }
                });
                video.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer mp) {
                        if (null != video) {
                            video.stopPlayback();
                        }
                    }
                });
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

自定義相機

創建一個自定義的相機app基本遵循如下步驟:

  • 檢測和訪問相機:
    首先代碼檢測該設備相機是否存在,如果存在才能請求訪問設備相機。
  • 創建一個預覽來顯示相機圖像:
    在你的佈局中使用SurfaceView控件,然後在代碼中繼承SurfaceHolder.Callback接口並且實現接口中的方法來顯示來自相機的圖像信息。
  • 設置相機基本參數:
    根據需求設置相機預覽尺寸,圖片大小,預覽方向,圖片方向等。
  • 設置拍照錄像監聽:
    當用戶按下按鈕時調用Camera.takePicture()或者MediaRecorder.start()來進行拍照或錄像。
  • 文件保存:
    當拍照結束或者錄像視頻結束時,需要開啓一個後臺線程去保存圖片或者視頻文件。
  • 釋放相機資源:
    Camera硬件是一個共享資源,所以你必須小心的編寫你的應用代碼來管理相機資源。一般在Activity的生命週期的onResume中開啓相機,在onPause中釋放相機。

注意: 當你不在使用相機資源時,記得調用Camera.release()方法來釋放相機資源,否則其他應用甚至你自己的應用再次請求訪問設備相機時會失敗,並且crash。

檢測相機硬件是否存在

一般情況,我們會在運行代碼時檢測該設備是否有相機硬件,如果有相機硬件,才進一步去訪問相機,如下是檢測相機硬件是否存在是代碼示例:

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        return true;
    } else {
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Android 設備可以有多個相機硬件,現在一般手機都是前後兩個camera,因此我們在Android2.3以後也可以使用Camera.getNumberOfCameras()方法來獲得當前設備camera個數來判斷相機硬件是否存在。

創建Camera預覽

Camera預覽佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:id="@+id/record_navigation_bar"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="#F8F8F8" >

        <ImageView
            android:id="@+id/record_act_back"
            android:layout_width="25dp"
            android:layout_height="31dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:src="@drawable/icon_ll_back" />
    </RelativeLayout>

    <SurfaceView
        android:id="@+id/camera_preview"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_above="@+id/record_bottom_bar"
        android:layout_below="@+id/record_navigation_bar" />

    <RelativeLayout
        android:id="@+id/record_bottom_bar"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        android:background="@drawable/recording_bottom_bar_bg_interview" >

        <Button
            android:id="@+id/btn_start_recording"
            android:layout_width="58dp"
            android:layout_height="58dp"
            android:layout_centerInParent="true"
            android:background="@drawable/recording_act_vedio_start" />

        <Button
            android:id="@+id/btn_change_module"
            android:layout_width="45dp"
            android:layout_height="35dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dp"
            android:background="@drawable/change_module_photo" />
    </RelativeLayout>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="30dp"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/record_navigation_bar"
        android:layout_marginRight="10dp"
        android:layout_marginTop="10dp"
        android:gravity="center_vertical" >

        <View
            android:id="@+id/record_video_tip"
            android:layout_width="15dp"
            android:layout_height="15dp"
            android:layout_marginRight="10dp"
            android:background="@drawable/record_video_tip" />

        <TextView
            android:id="@+id/record_video_time"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:layout_marginRight="10dp"
            android:gravity="center_vertical"
            android:text="00:00"
            android:textColor="@android:color/white"
            android:textSize="17sp" />
    </LinearLayout>

</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

這裏寫圖片描述

然後,我們創建一個Activity,用來展示Camera的預覽,那麼在這個Activity裏面,我們需要做什麼呢?兩件事情:

  • 初始化相機
  • 將內容顯示到SurfaceView

Android的Camera是獨享的,如果多處調用,就會拋出異常,所以,我們需要將Camera的生命週期與SurfaceView的生命週期綁定:

  • surfaceCreated方法中初始化相機
  • surfaceDestroyed方法中釋放相機

初始化相機非常簡單:

private Camera getCamera() {
    Camera camera;
    try {
        camera = Camera.open();
    } catch (Exception e) {
        camera = null;
    }
    return camera;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

注意: 在調用Camera.open()方法時總是要去捕獲一個異常,以免打開相機設備失敗導致整個應用crash。在Android2.3以及更高api上,你可以使用Camera.open(int)來打開指定的相機。以上代碼示例總是默認打開後置camera,一般情況參數爲0表示打開後置camera,參數爲1表示打開前置camera。
釋放相機也非常簡單:

@Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (camera != null) {
            try {
                camera.setPreviewCallback(null);
                camera.stopPreview();
                camera.release();
                camera = null;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

那麼下面我們再來看如何把相機圖像設置到SurfaceView中進行預覽:

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            camera = getCamera();
            camera.setPreviewDisplay(holder); //camera關聯到SurfaceView
            camera.setDisplayOrientation(90); //旋轉90度
            camera.startPreview(); //開始預覽
        } catch (Exception e) {
            finish();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

是不是也非常簡單,camera的一個方法已經幫我們自動關聯了SurfaceView。
這裏需要注意下這個方法camera.setDisplayOrientation(90),通過這個方法,我們可以調整攝像頭的角度,不然默認是橫屏,圖像會顯示的比較奇怪。當然,即使你設置了90,圖像也有可能比較奇怪,這是因爲你沒有對圖像進行正確的縮放,比例不對。

通過上面的設置,我們已經可以正常預覽攝像頭的圖像內容了。
這裏寫圖片描述

拍照

一旦你創建了camera preview並且加載到佈局中可以實時顯示預覽畫面了,此時就可以進行拍照了。爲了配合拍照,我們需要做一些設置,設置拍照參數,當然你也可以不設置而使用默認參數,默認參數基本上就能滿足我們的要求。

Camera.Parameters params = mCamera.getParameters();
params.setPictureFormat(ImageFormat.JPEG);
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(params);
//自動聚焦
camera.autoFocus(new AutoFocusCallback() {
     public void onAutoFocus(boolean success, Camera camera) {
           if (success)
                System.out.println("聚焦成功 !");
           else
                System.out.println("聚焦失敗 !");
        }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在代碼中你應該實現一個監聽回調來捕獲用戶拍照的行爲。可以調用camera.takePciture()方法來進行拍照。

public final void takePicture(ShutterCallback shutter, PictureCallback raw, PictureCallback jpeg);
  • 1

該方法接受三個參數,第一個參數ShutterCallback響應快門的接口,第二個參數PictureCallback接收raw格式的圖片數據,第三個參數PictureCallback接收jpeg格式的圖片數據。爲了保存圖片數據,你可以根據需要實現以上三個接口。此處我們暫且實現第三個PictureCallback接口回調。示例代碼如下:

   //拍照
   if (camera != null) {
        camera.takePicture(null, null, mPictureCallback);
   }

   //第三個PictureCallback接口回調,通過data[]保持圖片數據信息
    private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            File pictureFile = MainActivity.getOutputMediaFile(MEDIA_TYPE_IMAGE);
            if (pictureFile == null){
                return;
            }
            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            Toast.makeText(RecordVedioAct.this, "圖像已保存", Toast.LENGTH_SHORT).show();
            camera.startPreview(); //拍完繼續預覽
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

錄像

Camera視頻錄像不僅涉及到Camera類還用到了MediaRecorder類。當你使用Camera錄像時,你應該調用Camera.lock()和Camera.unlock()來管理camera硬件,允許MediaRecorder訪問camera硬件。你應該在camera和MediaRecorder關聯之前調用Camera.unlock()來解鎖camera,允許MediaRecorder訪問Camera,在釋放MediaRecorder資源以後調用Camera.lock()來鎖定camera以保證camera硬件資源的共享性。
注:在Android4.0以後,系統會自動管理camera.unlock()以及camera.lock(),無需用戶自己管理。

啓動錄像流程需要一個指定調用順序,如下是詳細的步驟流程:
(1)Open Camera – 使用Camera.open()靜態方法來獲得camera對象實例。
(2)Connect Preview – 使用camera.setPreviewDiaplay(holder)方法將相機的預覽畫面顯示在SurfaceView控件上。
(3)Start Preview – 使用camera.startPreview()方法開始啓動預覽畫面。
(4)Start Recording Video – 必須完成以下步驟才能正常開始正常錄音:

  • Unlock the Camera - 調用camera.unlock()方法解鎖camera,使得MediaRecorder進程能訪問Camera硬件。
  • Configure MediaRecorder - 在這一步,分別調用MediaRecorder類中如下方法來配置MediaRecorder:
/**配置MediaRecorder*/
recorder.setCamera(camera); //設置camera用於錄像
recorder.setOutputFile(filePath); //設置輸出文件路徑
recorder.setAudioSource(MediaRecorder.AudioSource.MIC); //設置錄像音頻來源
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); //設置錄像視頻來源
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); //設置視頻的輸出格式
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); //設置視頻的編碼格式
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  //設置音頻的編碼格式
/**輸出格式和編碼格式,對於Android2.2或者更高版本使用MediaRecorder.setProfile方法即可,使用方法CamcorderProfile.get()來獲得一個配置信息*/
recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
setPreviewDisplay(holder.getSurface()) //爲MediaRecorder指定預覽顯示
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意:在這一步,你必須調用MediaRecorder類中的以上全部方法來配置MediaRecorder,否則你的應用將無法正常錄像並且報錯。

由於錄像默認設置了很多參數,無需用戶太關心更細節的參數設置,但是如果需要在你的應用中修改這些默認參數設置,你可以使用如下方法來修改默認參數:


recorder.setAudioEncodingBitRate(); //設置音頻編碼的字節率
recorder.setVideoEncodingBitRate(); //設置視頻編碼的字節率
recorder.setOrientationHint(tureAngle); //設置MediaRecorder旋轉角度
recorder.setAudioSamplingRate(); //設置音頻採樣率
recorder.setMaxDuration(5 * 60 * 1000); //設置最大錄製時間
recorder.setVideoSize(640, 480); //設置視頻尺寸大小,在setVideoSource()和setOutFormat()之後
recorder.setVideoFrameRate() //設置視頻幀率,在setVideoSource()和setOutFormat()之後
recorder.setAudioChannels(); //設置音頻的頻道數目,參數一般1/2
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • Prepare MediaRecorder - 在配置完MediaRecorder參數之後調用mediaRecorder.prepare()方法來準備MediaRecorder。
  • Start MediaRecorder - 調用mediaRecorder.start()方法啓動錄像。

(5)Stop Recording Video – 當你結束錄像時調用如下方法:

  • Stop MediaRecorder - 首先調用 mediaRecorder.stop()方法停止多媒體錄像。
  • Reset MediaRecorder - 調用mediaRecorder.reset()方法重置多媒體狀態,調用該方法之後之前的所有MediaRecorder configuration將被移除,你如果還想再次錄像,需要再次配置多媒體參數。
  • Release MediaRecorder - 調用 mediaRecorder.release()方法釋放多媒體資源。
  • Lock the Camera - 調用camera.lock()方法來給Camera硬件加鎖。在Android4.0及以後無需調用該方法,除非在調用mediaRecorder.prepare()失敗時,才需要再次調用該方法。

(6)Stop the Preview - 當你的Activity已經不再使用camera時,調用camera.stopPreview()方法來停止預覽。
(7)Release Camera - 當不再使用Camera時,調用camera.release()方法來釋放camera,以便其他應用可以使用camera資源。

注意: 當完成一段視頻錄像時,不要馬上去釋放camera資源或者停止當前預覽,因爲有可能用戶會再次啓動錄像操作。本文中將camera釋放操作放在surfaceDestroyed裏面。

如下代碼演示在button的點擊事件中去啓動和停止視頻錄像操作:

   /**錄像*/
   if (isRecording) {
      stopRecord();
   } else {
      startRecord();
   }

    // 開始錄像
    private void startRecord() {
        if (prepareVideoRecorder()) {
            mediaRecorder.start();
            //修改狀態
            isRecording = true;
            Toast.makeText(RecordVedioAct.this, "開始錄像", Toast.LENGTH_SHORT).show();
            btn_start_recording.setBackgroundResource(R.drawable.recording_act_vedio_stop);
            start_time = 0;
            timer = new Timer();
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    start_time++;
                    handler.sendEmptyMessage(0);
                }
            }, 0, 1000);
        } else {
            mediaRecorder.release();
            camera.lock();
        }
    }

    // 停止錄像
    private void stopRecord() {
        mediaRecorder.stop();
        mediaRecorder.reset();
        mediaRecorder.release();
        camera.lock();
        //修改狀態
        isRecording = false;
        btn_start_recording.setBackgroundResource(R.drawable.recording_act_vedio_start);
        timer.cancel();
        record_video_tip.setVisibility(View.VISIBLE);
        Toast.makeText(RecordVedioAct.this, "錄像已保存", Toast.LENGTH_SHORT).show();
    }

    private boolean prepareVideoRecorder(){
        mediaRecorder = new MediaRecorder();
        camera.unlock();
        mediaRecorder.setCamera(camera);
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
        mediaRecorder.setOutputFile(MainActivity.getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());
        mediaRecorder.setPreviewDisplay(myHolder.getSurface());
        try {
            mediaRecorder.prepare();
        } catch (Exception e) {
            mediaRecorder.release();
            camera.lock();
            return false;
        }
        return true;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

好了,這篇博客就講到這裏,回顧一下,本文主要講解了如何調用系統Camera應用來進行拍照和拍攝以及如何自定義自己的Camera應用實現拍照和拍攝功能。關於Camera的特性參數設置和開發過程中一些常見問題請參考我的下一篇博客Android Camera開發(二)之擴展知識

Demo下載地址

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