Android的SDK在線API上對錄制視頻的方法、步驟都寫得非常清楚,但是如果沒有一點思路,寫起來也比較式費事。錄制視頻的全過程要打開閃光燈(可能是因為項目需要,或者特殊原因),則必須按照一定的順序進行開關,畢竟容易出錯。要實現錄制的同時開啟閃光燈也不難,官方API給出了一個大體的步驟.因為要采集點視頻數據,臨時寫了個簡單的Demo學習下,必要時再深度開發。
首先在工程中的AndroidManifest.xml中添加權限聲明,因為要使用到攝像頭,故需要添加Camera的相關權限,另外還需要寫SD卡的權限,如果同時需要錄制音頻,則還需要添加RECORD_AUDIO權限。
1 <uses-permission android:name="android.permission.CAMERA" />
2 <uses-feature android:name="android.hardware.camera" />
3 <uses-feature android:name="android.hardware.camera.autofocus" />
4 <uses-permission android:name="android.permission.RECORD_AUDIO"/>
5 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
再來分析下要使用到的類,錄制視頻使用的MediaRecorder類,官方給出了調用MediaRecorder錄制視頻的一個簡單狀態機,展示了各個狀態之間的轉化。然後也給出了一個簡單的調用方法,代碼如下:
View Code
錄制視頻是調用MediaRecorder類,但API中真正介紹如何錄制視頻的一般步驟卻被放在了Camera類中,在線API上有句話提示“For more information about how to use MediaRecorder for recording video, read the Camera developer guide.”。轉到Camera類去看看。
Camera類是用來控制照相機的,沒錯,就是這個類。照相機可以用來拍照,也可以用來錄制視頻(也叫捕捉視頻),但是錄制視頻需要按照一定的步驟來編寫程序,不然發生運行時錯誤是非常正常的。錄制視頻需要調用Camera和MediaRecorder類,下面說說一般步驟。
1) 打開照相機。直接調用Camera.open()來獲取一個Camera的實例。
2) 設置預覽控件。一般是設置在SurfaceView上面,通過調用Camera.setPreviewDisplay()來完成,但是這一步也可以放到MediaRecorder類DataSourceConfigured步驟中完成。
3) 開啟預覽。調用Camera.startPreview()。
4) 開始錄制視頻。為了確保你錄制成功,請務必按要求完成下面的步驟。
A. 解鎖照相機。通過調用Camera.unlock()解鎖照相機,以便照相機被MediaRecorder使用。
B. 設置MediaRecorder。
這裡有一系列的設置,根據需要設置吧。比如說,你只需要錄制視頻,就不必設置音頻的輸入源,也就不用設置音頻的編碼方式。對應於MediaRecorder state diagram中的Initialized和DataSourceConfigured。具體方法調用可以查看Android在線API的MediaRecorder類,上文已經將主要的代碼貼出,下文還會貼出實例代碼,這裡就不詳細介紹了。
C. 准備MediaRecorder。在調用MediaRecorder.prepare()之前一定要先設置好MediaRecorder對象的各項屬性,後面設置會引發運行時錯誤。
D. 開始MediaRecorder。調用MediaRecorder.start()之後,就開始錄制視頻了。
5) 停止錄制。
A. 停止MediaRecorder。調用MediaRecorder.stop()停止錄制。
B. 恢復MediaRecorder的默認設置。調用MediaRecorder.reset()來取消你對MediaRecorder所做的設置,但調用玩之後,MediaRecorder對象還是可以再次使用的。
C. 釋放MediaRecorder對象。調用MediaRecorder.release()釋放資源,之後該MediaRecorder對象銷毀了,再調用會出錯。
D. 給Camera上鎖。為了後面的MediaRecorder對象可以再次使用,需要調用Camera.lock(),Android 4.0以後,這個操作並不是必須的,除非MediaRecorder.prepare()調用失敗。
6) 停止預覽,調用Camera.stopPreview()。
7) 釋放照相機資源,調用Camera.release()。
以上就是打開照相機錄制視頻的一般步驟,當然你可以可以在錄制之前實現預覽,決定什麼時間開始錄制,這個其實可以先開啟照相機進行預覽即可然後,需要錄制時調用Camera.unlock(),然後按流程接入MediaRecorder進行錄制。現在考慮第一種情況,直接開始錄制。
權限要求已經貼出來了,再貼個布局文件,recordvideo.xml。
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="fill_parent"
4 android:layout_height="fill_parent"
5 android:background="#ffffff"
6 android:orientation="vertical" >
7
8 <SurfaceView
9 android:id="@+id/surfaceView"
10 android:layout_width="fill_parent"
11 android:layout_height="220dip" />
12
13 <LinearLayout
14 android:layout_width="fill_parent"
15 android:layout_height="wrap_content"
16 android:layout_marginLeft="5dp"
17 android:layout_marginRight="5dp"
18 android:layout_marginTop="20dp"
19 android:gravity="right"
20 android:orientation="horizontal" >
21
22 <EditText
23 android:id="@+id/rv_testusername"
24 android:layout_width="156dp"
25 android:layout_height="wrap_content"
26 android:layout_weight="0.27"
27 android:ems="10"
28 android:hint="輸入姓名或標識" />
29
30 <Button
31 android:id="@+id/rv_record"
32 style="@style/mainactivitybtnstyle"
33 android:layout_width="wrap_content"
34 android:layout_height="wrap_content"
35 android:minHeight="40dp"
36 android:minWidth="70dp"
37 android:text="錄制" />
38
39 <Button
40 android:id="@+id/rv_stop"
41 style="@style/mainactivitybtnstyle"
42 android:layout_width="wrap_content"
43 android:layout_height="wrap_content"
44 android:layout_marginLeft="10dip"
45 android:minHeight="40dp"
46 android:minWidth="70dp"
47 android:text="停止" />
48 </LinearLayout>
49
50 <LinearLayout
51 android:layout_width="fill_parent"
52 android:layout_height="fill_parent"
53 android:gravity="center_horizontal"
54 android:orientation="vertical" >
55
56 <ProgressBar
57 android:id="@+id/rv_schedule"
58 style="?android:attr/progressBarStyleHorizontal"
59 android:layout_width="fill_parent"
60 android:layout_height="wrap_content" />
61
62 <TextView
63 android:id="@+id/rv_record_time"
64 android:layout_width="fill_parent"
65 android:layout_height="wrap_content"
66 android:gravity="center"
67 android:text="00:00:000"
68 android:textColor="#FF750000"
69 android:textSize="24sp"
70 android:textStyle="bold" />
71 </LinearLayout>
72
73 </LinearLayout>
復制代碼
Activity代碼,因為非常簡單,就沒有封裝多線程什麼的。
復制代碼
1 package com.ict.phonedoctor.camerasign;
2
3 import java.io.File;
4 import java.text.SimpleDateFormat;
5
6 import android.content.Context;
7 import android.content.pm.FeatureInfo;
8 import android.content.pm.PackageManager;
9 import android.hardware.Camera;
10 import android.media.MediaRecorder;
11 import android.os.Bundle;
12 import android.os.Environment;
13 import android.os.Handler;
14 import android.os.Message;
15 import android.support.v7.app.ActionBarActivity;
16 import android.util.Log;
17 import android.view.SurfaceHolder;
18 import android.view.SurfaceView;
19 import android.view.View;
20 import android.widget.Button;
21 import android.widget.EditText;
22 import android.widget.ProgressBar;
23 import android.widget.TextView;
24 import android.widget.Toast;
25
26 import com.ict.phonedoctor.R;
27 import com.ict.util.IOUtil;
28
29 public class RecordVideoActivity extends ActionBarActivity {
30 private static final String TAG = "RecordVideo";
31 private SurfaceView surfaceView;
32 private MediaRecorder mediaRecorder;
33 private boolean record;
34 private TextView testusername;
35 private Camera camera;
36
37 // 計時器相關
38 private MyChronograph myChronograph;
39 private TextView chronograph = null;
40
41 private ProgressBar schedule;
42 private boolean recordOver = false;
43
44 @Override
45 protected void onCreate(Bundle savedInstanceState) {
46 // TODO Auto-generated method stub
47 super.onCreate(savedInstanceState);
48 setContentView(R.layout.recordvideo);
49 setTitle("錄制視頻");
50 mediaRecorder = new MediaRecorder();
51 surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView);
52 this.surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
53 this.surfaceView.getHolder().setFixedSize(320, 240);//設置分辨率
54
55 testusername = (EditText)findViewById(R.id.rv_testusername);
56 chronograph = (TextView)findViewById(R.id.rv_record_time);
57 schedule = (ProgressBar)findViewById(R.id.rv_schedule);
58 schedule.setMax(60);
59 ButtonClickListener listener = new ButtonClickListener();
60 Button stopButton = (Button) this.findViewById(R.id.rv_stop);
61 Button recordButton = (Button) this.findViewById(R.id.rv_record);
62 stopButton.setOnClickListener(listener);
63 recordButton.setOnClickListener(listener);
64 }
65
66 @Override
67 protected void onDestroy() {
68 // TODO Auto-generated method stub
69 if(mediaRecorder!=null)
70 mediaRecorder.release();
71 super.onDestroy();
72 }
73
74 @Override
75 protected void onPause() {
76 // TODO Auto-generated method stub
77 super.onPause();
78 }
79
80 @Override
81 protected void onResume() {
82 // TODO Auto-generated method stub
83 super.onResume();
84 }
85 private final class ButtonClickListener implements View.OnClickListener{
86 @Override
87 public void onClick(View v) {
88 if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
89 Toast.makeText(RecordVideoActivity.this, "木有檢測到SD擴展卡", 1).show();
90 return ;
91 }
92 try {
93 switch (v.getId()) {
94 case R.id.rv_record:
95 // 要求輸入用戶名
96 String testuser;
97 if(testusername.getText()==null || testusername.getText().toString().equals("")){
98 Toast.makeText(RecordVideoActivity.this, "請輸入測試者姓名", Toast.LENGTH_LONG).show();
99 return;
100 }
101 Log.i(TAG,"檢測通過");
102 recordOver = false;
103 testuser = testusername.getText().toString();
104 testuser = android.os.Build.MODEL + "-" + testuser;
105 mediaRecorder.reset();
106 if(isSurportFlashlight(RecordVideoActivity.this)){
107 if (camera == null)
108 camera = Camera.open();
109 Camera.Parameters myParameters = camera.getParameters();
110 myParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
111 camera.setParameters(myParameters);
112 camera.startPreview();
113 camera.unlock();
114 mediaRecorder.setCamera(camera);
115 }
116 mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
117 //mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
118 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
119 mediaRecorder.setVideoSize(320, 240);
120 mediaRecorder.setVideoFrameRate(30); //每秒30幀
121 mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
122 //mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
123 SimpleDateFormat ff = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
124 String recordTimeString = String.valueOf(ff.format(System.currentTimeMillis()));
125 File videoFile = IOUtil.CreateNewFile(Environment.getExternalStorageDirectory().getPath()+"/phonedoctor/video",
126 testuser + "-" + recordTimeString+".3gp",null);
127 mediaRecorder.setOutputFile(videoFile.getAbsolutePath());
128 mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface());
129 mediaRecorder.prepare();
130 mediaRecorder.start(); // 開始錄制
131 // 開啟計時線程
132 myChronograph = new MyChronograph(mHandler,60000);
133 myChronograph.start();
134 Toast.makeText(RecordVideoActivity.this, "開始錄制視頻!", Toast.LENGTH_SHORT).show();
135 record = true;
136 ((Button)findViewById(R.id.rv_record)).setEnabled(false);
137 break;
138
139 case R.id.rv_stop:
140 if(record){
141 record = false;
142 mediaRecorder.stop();
143 mediaRecorder.reset();
144 Log.i(TAG,"TAG-1");
145 if(camera!=null){
146 camera.lock();
147 camera.stopPreview();
148 Camera.Parameters myParameters = camera.getParameters();
149 myParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
150 camera.setParameters(myParameters);
151 camera.release();
152 camera = null;
153 }
154 // 秒表線程控制
155 if(myChronograph!=null){
156 myChronograph.exit();
157 myChronograph = null;
158 }
159 ((Button)findViewById(R.id.rv_record)).setEnabled(true);
160 }
161 break;
162 }
163 } catch (Exception e) {
164 Toast.makeText(RecordVideoActivity.this, "發生異常", 1).show();
165 e.printStackTrace();
166 }
167 }
168
169 }
170
171 private Handler mHandler = new Handler(){
172
173 @Override
174 public void handleMessage(Message msg) {
175 String[] strMsg;
176 switch (msg.what) {
177 case MsgNumber.UPTIME_UI:
178 strMsg = (String[]) msg.obj;
179 chronograph.setText(strMsg[0]);
180 if(!recordOver){
181 int percent = Integer.parseInt(strMsg[1]);
182 if(percent==-1){
183 recordOver = true;
184 schedule.setProgress(60);
185 Toast.makeText(RecordVideoActivity.this, "已錄制一分鐘!", Toast.LENGTH_SHORT).show();
186 return;
187 }
188 percent = percent>60?60:percent;
189 schedule.setProgress(percent);
190 }
191 break;
192
193 default:
194 break;
195 }
196 }
197
198 };
199
200 // 閃光燈判斷
201 public boolean isSurportFlashlight(Context context) {
202 boolean flag = false;
203 PackageManager pm = context.getPackageManager();
204 FeatureInfo[] features = pm.getSystemAvailableFeatures();
205 for (FeatureInfo f : features) {
206 if (PackageManager.FEATURE_CAMERA_FLASH.equals(f.name)) {
207 flag = true;
208 break;
209 }
210 }
211 return flag;
212 }
213 }