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
录制视频是调用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 }
运行效果图
至此,主要代码已经贴出,没什么技术含量,算是Android学习过程中的一个小结,Android在线API的一个阅读笔记。