VideoModule.java
@Override
public void onCameraAvailable(CameraProxy cameraProxy) {
...
startPreview(true);
...
}
startPreview() 与 PhotoModule 基本一致,不做详解
开始录像
private void startVideoRecording() {
//更新 UI,判断存储是否低于设定的阈值
...
// 初始化 MediaRecorder
initializeRecorder();
...
try {
// 调用 requestAudioFocus 获取 STREAM_MUSIC 焦点,暂停正在播放的音频
// 调用 setRingerMode 设置情景模式为 RINGER_MODE_SILENT,保证录像期间没有声音和震动提示
...
// 启动录像
mMediaRecorder.start();
} catch (IllegalStateException exception) {
// 实现同 catch (RuntimeException e){}
return;
} catch (RuntimeException e) {
// Could not start media recorder(start failed)
...
// releasing media recorder
if (mMediaRecorder != null) {
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
}
// 调用 setRingerMode 恢复原有情景模式
if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
mAudioManager.setRingerMode(mOriginalRingerMode);
}
// If start fails, frameworks will not lock the camera for us.
mCameraDevice.lock();
return;
}
...
}
private MediaRecorder mMediaRecorder;
private static Class<?> mediaRecoderExClazz = null;
private static Class<?> mediaRecoderClazz = null;
private static boolean OLD_SDK_VERSION =
Build.VERSION.SDK_INT <= Build.VERSION_CODES.M ? true : false;
static {
try {
mediaRecoderClazz = Class.forName("android.media.MediaRecorder");
mediaRecoderExClazz = Class.forName("android.media.MediaRecorderEx");
} catch (Exception e) {
// get class of MediaRecorder or MediaRecorderEx error
}
}
// Prepares media recorder.
private void initializeRecorder() {
...
// 初始化 MediaRecorder 对象
try {
if (OLD_SDK_VERSION) {
mMediaRecorder = mediaRecoderClazz.newInstance();
} else {
mMediaRecorder = mediaRecoderExClazz.newInstance();
}
return mMediaRecorder;
} catch (Exception e) {
return null;
}
// *必要* 解锁 Camera,使得 MediaRecorder 进程能访问 Camera 硬件
mCameraDevice.unlock();
// We rely here on the fact that the unlock call above is synchronous
// and blocks until it occurs in the handler thread. Thereby ensuring
// that we are up to date with handler requests, and if this proxy had
// ever been released by a prior command, it would be null.
Camera camera = mCameraDevice.getCamera();
// If the camera device is null, the camera proxy is stale and recording
// should be ignored.
if (camera == null) {
Log.w(TAG, "null camera within proxy, not recording");
return;
}
// *必要* 设置 Camera 用于录像
mMediaRecorder.setCamera(camera);
...
Method method;
try {
if (mMediaRecorder != null) {
if (OLD_SDK_VERSION) {
method = mediaRecoderClazz.getMethod(
"setParam64BitFileOffset", boolean.class);
method.invoke(mMediaRecorder, use64BitFlag);
} else {
method = mediaRecoderExClazz.getMethod(
"setParam64BitFileOffset", boolean.class);
method.invoke(mMediaRecorder, use64BitFlag);
}
}
} catch (Exception e) {
// setParam64BitFileOffset error
}
// *必要* 设置录像音频来源, 使用麦克风 MediaRecorder.AudioSource.CAMCORDER 作为音频来源
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
// *必要* 设置录像视频来源, 使用Camera MediaRecorder.VideoSource.CAMERA 作为视频来源
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(mProfile);
// 设置视频尺寸大小,在 setVideoSource() 和 setOutFormat() 之后 prepare() 之前调用
mMediaRecorder.setVideoSize(mProfile.videoFrameWidth,
mProfile.videoFrameHeight);
// 设置录制会话的最长持续时间(以ms为单位)
mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
// 设置视频帧捕获率
mMediaRecorder.setCaptureRate(fps);
Location loc = mLocationManager.getCurrentLocation();
if (loc != null) {
// 设置并存储在输出文件中的地理数据(经度和纬度)
mMediaRecorder.setLocation((float) loc.getLatitude(),
(float) loc.getLongitude());
}
// *必要* 设置视频输出文件的路径
mMediaRecorder.setOutputFile(mVideoFilename);
// Set maximum file size.
try {
// 设置录制文件的最大文件大小
mMediaRecorder.setMaxFileSize(maxFileSize);
} catch (RuntimeException exception) {
// We are going to ignore failure of setMaxFileSize here, as
// a) The composer selected may simply not support it, or
// b) The underlying media framework may not handle 64-bit range
// on the size restriction.
}
// 输出旋转90度,保持竖屏录制
mMediaRecorder.setOrientationHint(90);
try {
// *必要* 准备录制
mMediaRecorder.prepare();
} catch (IOException e) {
// prepare failed
// releasing media recorder
if (mMediaRecorder != null) {
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
}
throw new RuntimeException(e);
}
// 注册一个用于记录录制时出现错误的监听器
mMediaRecorder.setOnErrorListener(this);
// 注册一个用于记录录制时出现的信息事件的监听器
mMediaRecorder.setOnInfoListener(this);
}
停止录像
private boolean stopVideoRecording(boolean shouldSaveVideo, boolean checkStorage) {
...
boolean fail = false;
try {
mMediaRecorder.setOnErrorListener(null);
mMediaRecorder.setOnInfoListener(null);
// *必要* 停止录制
mMediaRecorder.stop();
...
} catch (RuntimeException e) {
// stop fail delete Video File
if (mVideoFilename != null) {
deleteVideoFile(mVideoFilename);
}
fail = true;
}
...
// 保存录像
saveVideo();
if (mMediaRecorder != null) {
/**
* 必要
* 重置多媒体状态,调用该方法之后之前的所有 MediaRecorder configuration 将被移除
* 如果还想再次录像,需要再次配置 MediaRecorder configuration
*/
mMediaRecorder.reset();
// *必要* 释放多媒体资源
mMediaRecorder.release();
mMediaRecorder = null;
}
...
// *必要* 给 Camera 硬件加锁
mCameraDevice.lock();
...
return fail;
}