VideoModule.java
@Override
public void onCameraAvailable(CameraProxy cameraProxy) {
...
startPreview(true);
...
}
startPreview() 與 PhotoModule 基本一致壹蔓,不做詳解
開始錄像
private void startVideoRecording() {
//更新 UI轻庆,判斷存儲是否低于設(shè)定的閾值
...
// 初始化 MediaRecorder
initializeRecorder();
...
try {
// 調(diào)用 requestAudioFocus 獲取 STREAM_MUSIC 焦點粉楚,暫停正在播放的音頻
// 調(diào)用 setRingerMode 設(shè)置情景模式為 RINGER_MODE_SILENT错沽,保證錄像期間沒有聲音和震動提示
...
// 啟動錄像
mMediaRecorder.start();
} catch (IllegalStateException exception) {
// 實現(xiàn)同 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;
}
// 調(diào)用 setRingerMode 恢復(fù)原有情景模式
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;
}
// *必要* 設(shè)置 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
}
// *必要* 設(shè)置錄像音頻來源, 使用麥克風(fēng) MediaRecorder.AudioSource.CAMCORDER 作為音頻來源
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
// *必要* 設(shè)置錄像視頻來源, 使用Camera MediaRecorder.VideoSource.CAMERA 作為視頻來源
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(mProfile);
// 設(shè)置視頻尺寸大小芦疏,在 setVideoSource() 和 setOutFormat() 之后 prepare() 之前調(diào)用
mMediaRecorder.setVideoSize(mProfile.videoFrameWidth,
mProfile.videoFrameHeight);
// 設(shè)置錄制會話的最長持續(xù)時間(以ms為單位)
mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
// 設(shè)置視頻幀捕獲率
mMediaRecorder.setCaptureRate(fps);
Location loc = mLocationManager.getCurrentLocation();
if (loc != null) {
// 設(shè)置并存儲在輸出文件中的地理數(shù)據(jù)(經(jīng)度和緯度)
mMediaRecorder.setLocation((float) loc.getLatitude(),
(float) loc.getLongitude());
}
// *必要* 設(shè)置視頻輸出文件的路徑
mMediaRecorder.setOutputFile(mVideoFilename);
// Set maximum file size.
try {
// 設(shè)置錄制文件的最大文件大小
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.
}
// 輸出旋轉(zhuǎn)90度晶默,保持豎屏錄制
mMediaRecorder.setOrientationHint(90);
try {
// *必要* 準(zhǔn)備錄制
mMediaRecorder.prepare();
} catch (IOException e) {
// prepare failed
// releasing media recorder
if (mMediaRecorder != null) {
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
}
throw new RuntimeException(e);
}
// 注冊一個用于記錄錄制時出現(xiàn)錯誤的監(jiān)聽器
mMediaRecorder.setOnErrorListener(this);
// 注冊一個用于記錄錄制時出現(xiàn)的信息事件的監(jiān)聽器
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) {
/**
* 必要
* 重置多媒體狀態(tài),調(diào)用該方法之后之前的所有 MediaRecorder configuration 將被移除
* 如果還想再次錄像为流,需要再次配置 MediaRecorder configuration
*/
mMediaRecorder.reset();
// *必要* 釋放多媒體資源
mMediaRecorder.release();
mMediaRecorder = null;
}
...
// *必要* 給 Camera 硬件加鎖
mCameraDevice.lock();
...
return fail;
}