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录制视频的一个简单状态机,展示了各个状态之间的转化。然后也给出了一个简单的调用方法,代码如下:
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
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>
View Code
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 }
View Code
运行效果图
至此,主要代码已经贴出,没什么技术含量,算是Android学习过程中的一个小结,Android在线API的一个阅读笔记。