公司现在的项目是一个在AndroidTV上开发的,要添加一个新的需求,录制视频。记录一下在AndroidTV上开发录制视频时遇到的小问题:
注: 对于只有一个摄像头的手机可能会有同样的问题,未测试过
- 无法获取到
Camera
对象 - 获取到Camera对象,在设置录制质量等一系列数据时
MediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));
时,捕获到空指针
- 无法获取到
Camera
对象(目标TV上只有一个摄像头,QQ在该TV上同样无法获取到摄像头)
注意的地方: 通常我们获取Camera
对象直接调用Camera.open()
Camera c = null;
try {
c = Camera.open();
} catch (Exception e) {
LogUtils.e("camera", "Open Camera Failed", e);
}
Camera
类中提供给我们两个获取Camera对象的方法(无参或有参):
/**
* Creates a new Camera object to access the first back-facing camera on the
* device. If the device does not have a back-facing camera, this returns
* null.
* @see #open(int)
*/
public static Camera open() {
int numberOfCameras = getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
return new Camera(i);
}
}
return null;
}
注意到该方法的提示信息If the device does not have a back-facing camera, this returns null.
,如果没有后置摄像头,那么返回值为 NULL
,即我们并没有获取到Camera对象。只有一个摄像头时,系统并没有获取到cameraInfo.facing
,Camera.open()
无参默认打开后置摄像头。讲到这里,你可能已经明白我没有获取到Camera
对象的原因了...
接下来继续看一下,Camera.open(number)
带参数的系统方法:
/**
* Creates a new Camera object to access a particular hardware camera. If
* the same camera is opened by other applications, this will throw a
* RuntimeException.
*
* You must call {@link #release()} when you are done using the camera,
* otherwise it will remain locked and be unavailable to other applications.
*
*
Your application should only have one Camera object active at a time
* for a particular hardware camera.
*
*
Callbacks from other methods are delivered to the event loop of the
* thread which called open(). If this thread has no event loop, then
* callbacks are delivered to the main application event loop. If there
* is no main application event loop, callbacks are not delivered.
*
*
Caution: On some devices, this method may
* take a long time to complete. It is best to call this method from a
* worker thread (possibly using {@link android.os.AsyncTask}) to avoid
* blocking the main application UI thread.
*
* @param cameraId the hardware camera to access, between 0 and
* {@link #getNumberOfCameras()}-1.
* @return a new Camera object, connected, locked and ready for use.
* @throws RuntimeException if opening the camera fails (for example, if the
* camera is in use by another process or device policy manager has
* disabled the camera).
* @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
*/
public static Camera open(int cameraId) {
return new Camera(cameraId);
}
注意到该方法返回我们输入参数获取对应的Camera对象,项目中我是通过Camera.open(0)
获取到Camera
对象的。
- 程序运行到
mediarecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));
捕获到空指针异常。
我们先来了解一下setProfile(getProfile())
方法有什么作用:
百度上的解释 设置录制文件质量,格式,分辨率之类
Camcorder.get()方法和Camera.open()相似系统同样有两个重载方法
- CamcorderProfile.get(int) 单参数
- CamcorderProfile.get(int,int) 双参数
我们看一下系统中两个方法的定义:
/**
* Returns the camcorder profile for the first back-facing camera on the
* device at the given quality level. If the device has no back-facing
* camera, this returns null.
* @param quality the target quality level for the camcorder profile
* @see #get(int, int)
*/
public static CamcorderProfile get(int quality) {
int numberOfCameras = Camera.getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
return get(i, quality);
}
}
return null;
}
**和Camera.open()
一样,并没有返回要设置的内容,而是返回一个NULL
,所以我们只能调用CamcorderProfile.get(int,int);
来设置录制质量,看一下双参数的系统方法,如下: **
/**
* Returns the camcorder profile for the given camera at the given
* quality level.
*
* A camcorder recording session with higher quality level usually has higher output
* bit rate, better video and/or audio recording quality, larger video frame
* resolution and higher audio sampling rate, etc, than those with lower quality
* level.
*
* @param cameraId the id for the camera
* @param quality the target quality level for the camcorder profile.
* @see #QUALITY_LOW
*/
public static CamcorderProfile get(int cameraId, int quality) {
if (!((quality >= QUALITY_LIST_START &&
quality <= QUALITY_LIST_END) ||
(quality >= QUALITY_TIME_LAPSE_LIST_START &&
quality <= QUALITY_TIME_LAPSE_LIST_END) ||
(quality >= QUALITY_HIGH_SPEED_LIST_START &&
quality <= QUALITY_HIGH_SPEED_LIST_END))) {
String errMessage = "Unsupported quality level: " + quality;
throw new IllegalArgumentException(errMessage);
}
return native_get_camcorder_profile(cameraId, quality);
}
setProfile(CamcorderProfile)方法也可以看一下具体设置那些东西
/**
* Uses the settings from a CamcorderProfile object for recording. This method should
* be called after the video AND audio sources are set, and before setOutputFile().
* If a time lapse CamcorderProfile is used, audio related source or recording
* parameters are ignored.
*
* @param profile the CamcorderProfile to use
* @see android.media.CamcorderProfile
*/
public void setProfile(CamcorderProfile profile) {
setOutputFormat(profile.fileFormat);
setVideoFrameRate(profile.videoFrameRate);
setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
setVideoEncodingBitRate(profile.videoBitRate);
setVideoEncoder(profile.videoCodec);
if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) {
// Nothing needs to be done. Call to setCaptureRate() enables
// time lapse video recording.
} else {
setAudioEncodingBitRate(profile.audioBitRate);
setAudioChannels(profile.audioChannels);
setAudioSamplingRate(profile.audioSampleRate);
setAudioEncoder(profile.audioCodec);
}
}