許多 Android 裝置的相機也有錄影功能,不過程式碼會稍微複雜一點。MediaRecorder API 針對聲音和影像提供了錄製控制功能,因此是你在 Android 上所有錄影功能的基礎。查看其 API 文件以了解其狀態機的完整示意圖。雖然 MediaRecorder 幫你做了許多工作,為了使其正常運作,有許多你得按照一定順序進行的步驟,此一教學稍後將加以解說。我們會使用預覽 XML 和來自先前教學的基本應用程式結構。
設定 MediaRecorder
首先,開啓 AndroidManifest.xml,在之前教學中設定的相機權限上,增加錄音權限:
<manifest .... > <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-feature android:name="android.hardware.camera" />
我們會在 MyCameraActivity.java 中撰寫獨立的方法來處理影片。第一步是為相機解鎖,接著建立新的 MediaRecorder。我們讓這個方法傳回真假布林值,好讓稍後我們要開始錄影前,可以檢查一切是否設定妥當,因此可避免不少可能的錯誤:
private MediaRecorder mr; protected boolean prepareForVideoRecording() { camera.unlock(); mr = new MediaRecorder(); mr.setCamera(camera); }
為相機解鎖這一步是必須的,可以讓 media 程序存取到相機。如果你只是拍照,Camera API 會自動幫你處理好,你只需要在錄影時做這件事。
接著,我們必須對 MediaRecorder 做各種設定:聲音與影像來源、配置 (profile)、輸出檔案、預覽顯示。
private static final int MEDIA_TYPE_VIDEO = 1; protected boolean prepareForVideoRecording() { camera.unlock(); mr = new MediaRecorder(); mr.setCamera(camera); mr.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mr.setVideoSource(MediaRecorder.VideoSource.CAMERA); mr.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); mr.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()); mr.setPreviewDisplay(preview.getHolder().getSurface()); }
AudioSource 與 VideoSource 就如名稱所示。聲音部分有許多選項,包含 VOICE_CALL 與 MIC,在此我們用 CAMCORDER。在影像上你只有 CAMERA 與 DEFAULT 可以選擇。
setProfile() 是個快捷功能(從 API 8 開始提供),可以讓你一次設定好一連串的錄影與錄音編碼和格式資訊。如果你想手動進行,不管是使用了之前的 API,或是你想要更為細緻的控制,以下是設定範例程式:
mr.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mr.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
更多選項請查看 MediaRecorder API 文件。有關哪些選項會被自動設定請見 CamcorderProfile API 文件。
你可以看到呼叫 getOutputMediaFile() 指定了影片輸出檔案類型,目前我們的方法沒有加以處理,以下是額外的程式碼:
private File getOutputMediaFile(int type) { // Get directory and timestamp as before if (type == MEDIA_TYPE_IMAGE) { return new File(dir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg"); } else if (type == MEDIA_TYPE_VIDEO) { return new File(dir.getPath() + File.separator + "VID_" + timeStamp + ".3gp"); } else { return null; } }
如果你使用了不同的編碼,你可能得更改結尾的檔案類型。最後,預覽就如之前我們所設定的一樣,我們只需呼叫適當方法以便取得所使用的 surface,好讓 MediaRecorder 與其溝通。
錄影
錄影前最後要做的事是準備好 MediaRecorder:
protected boolean prepareForVideoRecording() { // as above try { mr.prepare(); } catch (IllegalStateException e) { Log.e(TAG, "IllegalStateException when preparing MediaRecorder " + e.getMessage()); e.getStackTrace(); releaseMediaRecorder(); return false; } catch (IOException e) { Log.e(TAG, "IOException when preparing MediaRecorder " + e.getMessage()); e.getStackTrace(); releaseMediaRecorder(); return false; } return true; } private void releaseMediaRecorder() { if (mr != null) { mr.reset(); mr.release(); mr = null; camera.lock(); } }
很簡單,我們只是在我們的 MediaRecorder 上呼叫 API 的 prepare() 方法,並處理例外狀況。重要的是記得在任何例外發生時,釋放 MediaRecorder 並回傳 false。在重置並釋放 MediaRecorder 後,我們必須重新鎖住相機,好讓應用程式保留其控制權。記得你必須在暫停時釋放相機,我們在之前教學中已經示範過。
在開始錄影前,我們要在預覽面板 (preview pane) 上加入按鈕,並設定該按鈕來停止或開始錄影:
private Button recordVideoButton; private boolean isRecording = false; private void setUpLayout() { // as in previous tutorials setUpVideoButton(); } private void setUpVideoButton() { Button recordVideoButton = (Button) findViewById(R.id.button_video); setUpButton(recordVideoButton, "Start video"); recordVideoButton.setOnClickListener( new View.OnClickListener() { public void onClick(View v) { if (isRecording) { mr.stop(); releaseMediaRecorder(); camera.lock(); recordVideoButton.setText("Start video"); isRecording = false; } else { if (prepareForVideoRecording()) { mr.start(); recordVideoButton.setText("Stop video"); isRecording = true; } else { // Something has gone wrong! Release the camera releaseMediaRecorder(); Toast.makeText(MyCameraActivity.this, "Sorry: couldn't start video", Toast.LENGTH_LONG).show(); } } } } ); }
你也必須把按鈕加到 XML 中。上面的程式碼讓我們用同一個按鈕來開始與停止錄影。這表示不管是否正在錄影中,該按鈕都會顯示出來,這是個最大化用戶資訊,同時最小化螢幕空間使用的便利方式。如果應用程式已經在錄影,當按鈕被點選,表示用戶希望停止錄影。於是我們停止並釋放 MediaRecorder,重新鎖定相機,改變按鈕文字並重置 isRecording。
如果應用程式沒在錄影,我們希望開始錄影。假如設定方法傳回的是 true,也就是 MediaRecorder 已準備妥當,我們就開始錄影,改變按鈕文字並重設 isRecording。如果 MediaRecorder 沒設定好,我們再次將其釋放,顯示一個 Toast 訊息讓用戶得知問題所在。
和 Camera 相片儲存有所不同的是,對於 MediaRecorder,傳入儲存的檔案名稱就會自動處理好影片的儲存,你不需要自行處理儲存功能。不過,它所做的只是存入你所提供的檔名中。如果你希望對影片做其他處理,像是秀給用戶看或讓他們編輯,你必須進一步擴展應用程式以達成這些功能。