橘子味的心
标题:6.7 Android录制视频

视频录制也可以通过 MediaRecorder 类完成,其步骤与音频录制基本相同,只是添加了一些对视频进行处理的操作。

视频录制的基本步骤如下:

1) 调用Camera.open()方法打开摄像头。

2) 调用 Camera.setPreviewDisplay() 连接预览窗口
以便将从摄像头获取的图像放置到预览窗口中显示出来。

3) 调用 Camera.startPreview()启动预览
显示摄像头拍摄到的图像。

4) 使用 MediaRecorder 进行视频录制。

1.使用 Camera.unlock() 方法解锁摄像头,以使 MediaRecorder 获得对摄像头的使用权。

2.配置 MediaRecorder。

1)建立 MediaRecorder 类的对象,并设置音频源和视频源:

MediaRecorder recorder=new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

2)设置视频的输出和编码格式。在 Android 2.2(API Level 8)以上版本的 SDK 中,可以直接调用 MediaRecorder.setProfile 方法进行相关配置:

recorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));

其中,MediaRecorder.setProfile() 方法为 Android 2.2(API Level 8)之后 MediaRecorder 类新提供的方法,通过 CamcorderProfile 对象可用于对 MediaRecorder 进行相关设置。

CamcorderProfile 为预先定义好的一组视频录制相关配置信息。

Android SDK 共定义了 14 种 CamcorderProfile 配置,如 CamcorderProfile. QUALITY_HIGH、CamcorderProfile. QUALITY_LOW、CamcorderProfile. QUALITY_TIME_LAPSE_1080P 等。其中,QUALITY_LOW 和 QUALITY_HIGH 两种配置是所有的摄像头都支持的,其他配置则根据硬件性能决定。

每一种配置都涉及文件输出格式、视频编码格式、视频比特率、视频帧率、视频的高和宽、音频编码格式、音频的比特率、音频采样率和音频录制的通道数几个方面。通过使用这些预定义配置能够降低代码复杂度,提高编码效率。

3)设置录制的视频文件的保存位置及文件名:

MediaRecorder.setOutputFile(PATH_NAME);


4)使用 MediaRecorder.setPreviewDisplay() 方法指定 MediaRecorder 的视频预览窗口。

需要注意的是,以上配置过程必须按照顺序进行,否则会发生错误。

3.将录像器置于准备状态:

MediaRecorder.prepare();


4.启动录像器:

MediaRecorder.start();


5.进行视频录制:

5) 视频录制完成后,可使用以下方法停止视频录制。

1.停止录像器:

MediaRecorder.stop();


2.重置录像器的相关配置:

MediaRecorder.reset()


3.释放录像器对象:

MediaRecorder.release();


4.调用 Camera.lock() 方法锁定摄像头。从 Android N 开始,该调用也不再必需,除非 MediaRecorder.prepare() 方法失败。

6) 调用Camera.stopPreview()方法停止预览。

7) 调用Camera.release()方法释放摄像头 。

另外,在 Android N 系统下,Camera.unlock() 方法和 Camera.lock() 方法可由 Android 框架来完成。

实例 VideoRecorderDemo 演示了使用 MediaRecorder 进行视频录制的过程,该实例的运行效果如图 1 所示。

VideoRecorderDemo
图 1  VideoRecorderDemo 

实例 VideoRecorderDemo 使用的布局文件 main.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:orientation="vertical">
  6.  
  7. <TextView
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:text="@string/hello" />
  11.  
  12. <SurfaceView
  13. android:id="@+id/surfaceView1"
  14. android:layout_width="fill_parent"
  15. android:layout_height="wrap_content"
  16. android:layout_weight="0.58" />
  17.  
  18. <LinearLayout
  19. android:id="@+id/linearLayout1"
  20. android:layout_width="match_parent"
  21. android:layout_height="wrap_content"
  22. android:gravity="center">
  23.  
  24. <Button
  25. android:id="@+id/button1"
  26. android:layout_width="wrap_content"
  27. android:layout_height="wrap_content"
  28. android:text="@string/opBtn" />
  29.  
  30. <Button
  31. android:id="@+id/button2"
  32. android:layout_width="wrap_content"
  33. android:layout_height="wrap_content"
  34. android:text="@string/play" />
  35.  
  36. <Button
  37. android:id="@+id/button3"
  38. android:layout_width="wrap_content"
  39. android:layout_height="wrap_content"
  40. android:text="@string/cloBtn" />
  41.  
  42. </LinearLayout>
  43.  
  44. </LinearLayout>
其对应的资源文件 strings.xml 的内容如下:
  1. <resources>
  2. <string name="hello">使用MediaRecorder进行视频录制实例</string>
  3. <string name="app_name">VideoRecorder</string>
  4. <string name="opBtn">打开摄像头</string>
  5. <string name="play">录制</string>
  6. <string name="cloBtn">关闭摄像头</string>
  7. </resources>
由于实例 VideoRecorderDemo 中涉及音频录制、使用摄像头、向 SD 卡写文件等操作,因此需要在该工程的 AndroidManifest.xml 文件中声明相应权限。该文件内容如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="introduction.android.mycamerademo"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6.  
  7. <uses-sdk android:minSdkVersion="14" />
  8. <uses-feature android:name="android.hardware.camera.autofocus" />
  9. <uses-feature android:name="android.permission.CAMERA"/>
  10. <uses-permission android:name="android.permission.CAMERA" />
  11. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  12.  
  13. <application
  14. android:allowBackup="true"
  15. android:icon="@mipmap/ic_launcher"
  16. android:label="@string/app_name"
  17. android:roundIcon="@mipmap/ic_launcher_round"
  18. android:supportsRtl="true"
  19. android:theme="@style/AppTheme">
  20. <activity android:name=".MainActivity">
  21. <intent-filter>
  22. <action android:name="android.intent.action.MAIN" />
  23.  
  24. <category android:name="android.intent.category.LAUNCHER" />
  25. </intent-filter>
  26. </activity>
  27. </application>
  28.  
  29. </manifest>
实例 VideoRecorderDemo 的主 Activity 为 VideoRecorderDemoActivity,其代码如下:
  1. package introduction.android.mycamerademo;
  2.  
  3. import java.io.BufferedOutputStream;
  4. import java.io.File;
  5.  
  6. import java.io.FileNotFoundException;
  7. import java.io.FileOutputStream;
  8. import java.io.IOException;
  9.  
  10. import android.app.Activity;
  11. import android.graphics.Bitmap;
  12. import android.graphics.BitmapFactory;
  13. import android.graphics.PixelFormat;
  14. import android.hardware.Camera;
  15. import android.hardware.Camera.Parameters;
  16. import android.hardware.Camera.PictureCallback;
  17. import android.media.MediaRecorder;
  18. import android.os.Bundle;
  19. import android.util.Log;
  20. import android.view.SurfaceHolder;
  21. import android.view.SurfaceView;
  22. import android.view.View;
  23. import android.view.View.OnClickListener;
  24. import android.widget.Button;
  25.  
  26. public class MainActivity extends Activity {
  27. private Button opbtn;
  28. private Button playbtn;
  29. private Button clobtn;
  30. private SurfaceView surfaceView;
  31. private SurfaceHolder surfaceHolder;
  32. private Camera camera;
  33. private MediaRecorder videoRecorder;
  34. private String myVideofilepath = "sdcard/myvideo.3gp";
  35.  
  36. /**
  37. * Called when the activity is first created.
  38. */
  39. @Override
  40. public void onCreate(Bundle savedInstanceState) {
  41. super.onCreate(savedInstanceState);
  42. setContentView(R.layout.activity_main);
  43. opbtn = (Button) this.findViewById(R.id.button1);
  44. playbtn = (Button) this.findViewById(R.id.button2);
  45. clobtn = (Button) this.findViewById(R.id.button3);
  46. videoRecorder = new MediaRecorder();
  47. surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView1);
  48. surfaceHolder = surfaceView.getHolder();
  49. surfaceHolder.addCallback(new SurfaceHolder.Callback() {
  50.  
  51. @Override
  52. public void surfaceDestroyed(SurfaceHolder holder) {
  53. // TODO Auto-generated method stub
  54. Log.i("camera", "surface destroyed.");
  55. surfaceHolder = null;
  56. stopRecording();
  57. releaseCamera();
  58. }
  59.  
  60. @Override
  61. public void surfaceCreated(SurfaceHolder holder) {
  62. // TODO Auto-generated method stub
  63. Log.i("camera", "surface destroyed");
  64. surfaceHolder = holder;
  65. }
  66.  
  67. @Override
  68. public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  69. // TODO Auto-generated method stub
  70. Log.i("camera", "surface changed.");
  71. surfaceHolder = holder;
  72. }
  73. });
  74. opbtn.setOnClickListener(new OnClickListener() {
  75. @Override
  76. public void onClick(View argO) {
  77. // TODO Auto-generated method stub
  78. openCamera();
  79. }
  80. });
  81. playbtn.setOnClickListener(new OnClickListener() {
  82. @Override
  83. public void onClick(View v) {
  84. // TODO Auto-generated method stub
  85. benginRecording();
  86. }
  87. });
  88. clobtn.setOnClickListener(new OnClickListener() {
  89. @Override
  90. public void onClick(View v) {
  91. // TODO Auto-generated method stub
  92. stopRecording();
  93. }
  94. });
  95. }
  96.  
  97. @Override
  98. protected void onPause() {
  99. // TODO Auto-generated method stub
  100. super.onPause();
  101. stopRecording();
  102. releaseCamera();
  103. }
  104.  
  105. protected void stopRecording() {
  106. // TODO Auto-generated method stub
  107. Log.i("videoRecorder", "stopRecording...");
  108. if (videoRecorder != null) {
  109. videoRecorder.stop();
  110. videoRecorder.reset();
  111. videoRecorder.release();
  112. videoRecorder = null;
  113. camera.lock();
  114. }
  115. }
  116.  
  117. private void releaseCamera() {
  118. if (camera != null) {
  119. camera.release();
  120. camera = null;
  121. }
  122. }
  123.  
  124. protected void benginRecording() {
  125. Log.i("videoRecorder", "beginRecording");
  126. //给摄像头解锁
  127. camera.unlock();
  128. //MediaRecorder获取到摄像头的访问权
  129. videoRecorder.setCamera(camera);
  130. //设置视频录制过程中所录制的音频来自手机的麦克风
  131. videoRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
  132. //设置视频源为摄像头
  133. videoRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
  134. //设置视频录制的输出文件为3gp文件
  135. videoRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
  136. //设置音频编码方式为AAC
  137. videoRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
  138. //设置录制的视频编码为H.264
  139. videoRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
  140. videoRecorder.setVideoSize(176, 144);
  141. videoRecorder.setVideoFrameRate(20);
  142. if (!checkSDCard()) {
  143. Log.e("videoRecorder", "未找到SD卡!");
  144. return;
  145. }
  146. videoRecorder.setOutputFile(myVideofilepath);
  147. videoRecorder.setPreviewDisplay(surfaceHolder.getSurface());
  148. try {
  149. videoRecorder.prepare();
  150. } catch (IllegalStateException e) {
  151. // TODO Auto-generated catch block
  152. e.printStackTrace();
  153. } catch (IOException e) {
  154. // TODO Auto-generated catch block
  155. e.printStackTrace();
  156. }
  157. videoRecorder.start();
  158. }
  159.  
  160. private void openCamera() {
  161.  
  162. // TODO Auto-generated method stub
  163. Log.i("videoRecorder", "openCamera.");
  164. try {
  165. camera = Camera.open(); // attempt to get a Camera instance
  166. } catch (Exception e) {
  167. // Camera is not available (in use or does not exist)
  168. Log.e("camera", "open camera error!");
  169. e.printStackTrace();
  170. return;
  171. }
  172.  
  173. try {
  174. camera.setPreviewDisplay(surfaceHolder);
  175. } catch (IOException e) {
  176. // TODO Auto-generated catch block
  177. Log.e("camera", "preview failed.");
  178. e.printStackTrace();
  179. }
  180. camera.startPreview();
  181. }
  182.  
  183. private boolean checkSDCard() {
  184. // 判断SD存储卡是否存在
  185. if (android.os.Environment.getExternalStorageState().equals(
  186. android.os.Environment.MEDIA_MOUNTED)) {
  187. return true;
  188. } else {
  189. return false;
  190. }
  191. }
  192. }
该实例中,在对 MediaRecorder 进行设置时,没有使用:

videoRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));

而是使用以下代码对 MediaRecorder 进行设置:
  1. //设置视频录制的输出文件为3gp文件
  2. videoRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
  3. //设置音频编码方式为AAC
  4. videoRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
  5. //设置录制的视频编码为H.264
  6. videoRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
  7. //设置视频录制的分辨率,必须放在设置编码和格式的后面,否则报错
  8. videoRecorder.setVideoSize(176, 144);
  9. //设置录制的视频的视频帧率,必须放在设置编码和格式的后面,否则报错
  10. videoRecorder.setVideoFrameRate(20);