Android的SDK在線API上對錄製視頻的方法、步驟都寫得很是清楚,可是若是沒有一點思路,寫起來也比較式費事。錄製視頻的全過程要打開閃光燈(多是由於項目須要,或者特殊緣由),則必須按照必定的順序進行開關,畢竟容易出錯。要實現錄製的同時開啓閃光燈也不難,官方API給出了一個大致的步驟.由於要採集點視頻數據,臨時寫了個簡單的Demo學習下,必要時再深度開發。html
首先在工程中的AndroidManifest.xml中添加權限聲明,由於要使用到攝像頭,故須要添加Camera的相關權限,另外還須要寫SD卡的權限,若是同時須要錄製音頻,則還須要添加RECORD_AUDIO權限。java
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錄製視頻的一個簡單狀態機,展現了各個狀態之間的轉化。而後也給出了一個簡單的調用方法,代碼以下:android
1 MediaRecorder recorder = new MediaRecorder(); 2 recorder.setAudioSource(MediaRecorder.AudioSource.MIC); 3 recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 4 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 5 recorder.setOutputFile(PATH_NAME); 6 recorder.prepare(); 7 recorder.start(); // Recording is now started 8 ... 9 recorder.stop(); 10 recorder.reset(); // You can reuse the object by going back to setAudioSource() step 11 recorder.release(); // Now the object cannot be reused
錄製視頻是調用MediaRecorder類,但API中真正介紹如何錄製視頻的通常步驟卻被放在了Camera類中,在線API上有句話提示「For more information about how to use MediaRecorder for recording video, read the Camera developer guide.」。轉到Camera類去看看。多線程
Camera類是用來控制照相機的,沒錯,就是這個類。照相機能夠用來拍照,也能夠用來錄製視頻(也叫捕捉視頻),可是錄製視頻須要按照必定的步驟來編寫程序,否則發生運行時錯誤是很是正常的。錄製視頻須要調用Camera和MediaRecorder類,下面說說通常步驟。app
1) 打開照相機。直接調用Camera.open()來獲取一個Camera的實例。ide
2) 設置預覽控件。通常是設置在SurfaceView上面,經過調用Camera.setPreviewDisplay()來完成,可是這一步也能夠放到MediaRecorder類DataSourceConfigured步驟中完成。佈局
3) 開啓預覽。調用Camera.startPreview()。學習
4) 開始錄製視頻。爲了確保你錄製成功,請務必按要求完成下面的步驟。測試
A. 解鎖照相機。經過調用Camera.unlock()解鎖照相機,以便照相機被MediaRecorder使用。ui
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 import java.io.File; 2 import java.text.SimpleDateFormat; 3 4 import android.content.Context; 5 import android.content.pm.FeatureInfo; 6 import android.content.pm.PackageManager; 7 import android.hardware.Camera; 8 import android.media.MediaRecorder; 9 import android.os.Bundle; 10 import android.os.Environment; 11 import android.os.Handler; 12 import android.os.Message; 13 import android.support.v7.app.ActionBarActivity; 14 import android.util.Log; 15 import android.view.SurfaceHolder; 16 import android.view.SurfaceView; 17 import android.view.View; 18 import android.widget.Button; 19 import android.widget.EditText; 20 import android.widget.ProgressBar; 21 import android.widget.TextView; 22 import android.widget.Toast; 23 24 import com.ict.util.IOUtil; 25 26 public class RecordVideoActivity extends ActionBarActivity { 27 private static final String TAG = "RecordVideo"; 28 private SurfaceView surfaceView; 29 private MediaRecorder mediaRecorder; 30 private boolean record; 31 private TextView testusername; 32 private Camera camera; 33 34 // 計時器相關 35 private MyChronograph myChronograph; 36 private TextView chronograph = null; 37 38 private ProgressBar schedule; 39 private boolean recordOver = false; 40 41 @Override 42 protected void onCreate(Bundle savedInstanceState) { 43 // TODO Auto-generated method stub 44 super.onCreate(savedInstanceState); 45 setContentView(R.layout.recordvideo); 46 setTitle("錄製視頻"); 47 mediaRecorder = new MediaRecorder(); 48 surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView); 49 this.surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 50 this.surfaceView.getHolder().setFixedSize(320, 240);//設置分辨率 51 52 testusername = (EditText)findViewById(R.id.rv_testusername); 53 chronograph = (TextView)findViewById(R.id.rv_record_time); 54 schedule = (ProgressBar)findViewById(R.id.rv_schedule); 55 schedule.setMax(60); 56 ButtonClickListener listener = new ButtonClickListener(); 57 Button stopButton = (Button) this.findViewById(R.id.rv_stop); 58 Button recordButton = (Button) this.findViewById(R.id.rv_record); 59 stopButton.setOnClickListener(listener); 60 recordButton.setOnClickListener(listener); 61 } 62 63 @Override 64 protected void onDestroy() { 65 // TODO Auto-generated method stub 66 if(mediaRecorder!=null) 67 mediaRecorder.release(); 68 super.onDestroy(); 69 } 70 71 @Override 72 protected void onPause() { 73 // TODO Auto-generated method stub 74 super.onPause(); 75 } 76 77 @Override 78 protected void onResume() { 79 // TODO Auto-generated method stub 80 super.onResume(); 81 } 82 private final class ButtonClickListener implements View.OnClickListener{ 83 @Override 84 public void onClick(View v) { 85 if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ 86 Toast.makeText(RecordVideoActivity.this, "木有檢測到SD擴展卡", 1).show(); 87 return ; 88 } 89 try { 90 switch (v.getId()) { 91 case R.id.rv_record: 92 // 要求輸入用戶名 93 String testuser; 94 if(testusername.getText()==null || testusername.getText().toString().equals("")){ 95 Toast.makeText(RecordVideoActivity.this, "請輸入測試者姓名", Toast.LENGTH_LONG).show(); 96 return; 97 } 98 Log.i(TAG,"檢測經過"); 99 recordOver = false; 100 testuser = testusername.getText().toString(); 101 testuser = android.os.Build.MODEL + "-" + testuser; 102 mediaRecorder.reset(); 103 if(isSurportFlashlight(RecordVideoActivity.this)){ 104 if (camera == null) 105 camera = Camera.open(); 106 Camera.Parameters myParameters = camera.getParameters(); 107 myParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH); 108 camera.setParameters(myParameters); 109 camera.startPreview(); 110 camera.unlock(); 111 mediaRecorder.setCamera(camera); 112 } 113 mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); 114 //mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); 115 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); 116 mediaRecorder.setVideoSize(320, 240); 117 mediaRecorder.setVideoFrameRate(30); //每秒30幀 118 mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); 119 //mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); 120 SimpleDateFormat ff = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"); 121 String recordTimeString = String.valueOf(ff.format(System.currentTimeMillis())); 122 File videoFile = IOUtil.CreateNewFile(Environment.getExternalStorageDirectory().getPath()+"/phonedoctor/video", 123 testuser + "-" + recordTimeString+".3gp",null); 124 mediaRecorder.setOutputFile(videoFile.getAbsolutePath()); 125 mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface()); 126 mediaRecorder.prepare(); 127 mediaRecorder.start(); // 開始錄製 128 // 開啓計時線程 129 myChronograph = new MyChronograph(mHandler,60000); 130 myChronograph.start(); 131 Toast.makeText(RecordVideoActivity.this, "開始錄製視頻!", Toast.LENGTH_SHORT).show(); 132 record = true; 133 ((Button)findViewById(R.id.rv_record)).setEnabled(false); 134 break; 135 136 case R.id.rv_stop: 137 if(record){ 138 record = false; 139 mediaRecorder.stop(); 140 mediaRecorder.reset(); 141 Log.i(TAG,"TAG-1"); 142 if(camera!=null){ 143 camera.lock(); 144 camera.stopPreview(); 145 Camera.Parameters myParameters = camera.getParameters(); 146 myParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); 147 camera.setParameters(myParameters); 148 camera.release(); 149 camera = null; 150 } 151 // 秒錶線程控制 152 if(myChronograph!=null){ 153 myChronograph.exit(); 154 myChronograph = null; 155 } 156 ((Button)findViewById(R.id.rv_record)).setEnabled(true); 157 } 158 break; 159 } 160 } catch (Exception e) { 161 Toast.makeText(RecordVideoActivity.this, "發生異常", 1).show(); 162 e.printStackTrace(); 163 } 164 } 165 166 } 167 168 private Handler mHandler = new Handler(){ 169 170 @Override 171 public void handleMessage(Message msg) { 172 String[] strMsg; 173 switch (msg.what) { 174 case MsgNumber.UPTIME_UI: 175 strMsg = (String[]) msg.obj; 176 chronograph.setText(strMsg[0]); 177 if(!recordOver){ 178 int percent = Integer.parseInt(strMsg[1]); 179 if(percent==-1){ 180 recordOver = true; 181 schedule.setProgress(60); 182 Toast.makeText(RecordVideoActivity.this, "已錄製一分鐘!", Toast.LENGTH_SHORT).show(); 183 return; 184 } 185 percent = percent>60?60:percent; 186 schedule.setProgress(percent); 187 } 188 break; 189 190 default: 191 break; 192 } 193 } 194 195 }; 196 197 // 閃光燈判斷 198 public boolean isSurportFlashlight(Context context) { 199 boolean flag = false; 200 PackageManager pm = context.getPackageManager(); 201 FeatureInfo[] features = pm.getSystemAvailableFeatures(); 202 for (FeatureInfo f : features) { 203 if (PackageManager.FEATURE_CAMERA_FLASH.equals(f.name)) { 204 flag = true; 205 break; 206 } 207 } 208 return flag; 209 } 210 }
運行效果圖
至此,主要代碼已經貼出,沒什麼技術含量,算是Android學習過程當中的一個小結,Android在線API的一個閱讀筆記。