第一次写自己的技术博客,本人Android菜鸟一枚,写博客也只是为了记录自己学习过的东西,以便以后可以更容找到属于自己的资料。
最近一直在做Camera的开发,今天主要想将使用Camera录制视频的方法记录下来。
其实网上关于这方面的技术其实都有比较清晰的讲解,不过大部分都是讲一部分空一部分,所以就想着要整理综合一下。
这个布局基本上来说就是相对布局,只不过增加了一些显示功能而已~所以可以忽略不计,因为是功能测试的App,所以数据上也懒得用dimens来写了,就先将就这么看吧~哈哈~
好了,下面就可以进入主题,使用MediaRecorder进行视频的录制。
首先,我们需要使用Camera, 需要在AndroidManifest.xml中对使用权限进行申请,一般来说,使用camera功能都需要使用以下几个权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.HARDWARE" />
首先先是对Camera的设置,这里使用的是谷歌大大不推荐使用的Camera,而并非Camera2,童鞋们要注意咯~
public void preview() {
try {
camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); // 使用后置摄像头
camera.setDisplayOrientation(90); // 镜头顺时旋转90度
camera.setPreviewDisplay(VideoActivity.getHolder()); // 将SurfaceHolder给camera
Camera.Parameters parameters = camera.getParameters();
// 自动对焦
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes != null) {
for (String mode : focusModes) {
mode.contains("continuous-video");
parameters.setFocusMode("continuous-video");
}
}
camera.setParameters(parameters);
camera.startPreview();
} catch (Exception e) {
// TODO: handle exception
}
}
这里需要注意的有三点
1:在我们使用Camera的时候,无论是预览还是最终得到的照片,都会逆时针的旋转90度,尤其是在预览模式下,预览的效果还是失真的效果,所以,我们需要用到
camera.setDisplayOrientation(90); // 将预览效果旋转90度
recorder.setOrientationHint(90); // 将获得的视频结果旋转90度
setOrientationHint()是针对MediaRecorder属性的设置,我们后面也会说到。
2:自动对焦的问题。我们发现如果不加这段代码,也是可以录制视频的,但是无论是预览还是最终得到的视频,都非常模糊。因此,你肯定会说,那就加上就好了。坑就在这里了,因为如果用了这段代码,某些手机得到的视频结果就会莫名其妙的产生马赛克,如果说一直保持一个位置还好,如果运动拍摄的话,这个问题就会非常的明显。因为我手头只有4款Android测试机,所以没有办法说到底哪几种机型会出现这样的结果,不过就现有测试的结果来说,小米的机器就会出现这个问题,不知道有没有大神可以帮忙解决这个蛋疼的问题呢?
// 自动对焦
List<String> focusModes = parameters.getSupportedFocusModes();
if (focusModes != null) {
for (String mode : focusModes) {
mode.contains("continuous-video");
parameters.setFocusMode("continuous-video");
}
3:关于代码顺序的问题。在谷歌开发文档里面其实已经强调了这方面的问题,有关Camera属性设置,必须严格按照谷歌所给的顺序进行设置,否则会触发各种各样的BUG。
接下来就在Activity中将surfaceView设置好,并获得SurfaceHolder传给我们已经写好的函数中,完成Camera初始化。
具体的SurfaceHolder的设置这里就跳过了,不过还需要强调的一点是,我建议大家在设置SurfaceHolder的时候加入
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
之后就是在holder中添加CallBack,然后可以在surfaceChanged或者surfaceCreate中设置Camera的属性,即调用上方的函数即可。
private SurfaceHolder.Callback mSurfaceHolder = new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
videoManager.preview();
}
};
总算是要讲到今天的主题MediaRecorder了
recorder = new MediaRecorder();
recorder.reset();
recorder.setCamera(camera);
// 选择角度(视频结果)
recorder.setOrientationHint(90);
// 设置采集图像
recorder.setVideoSource(Camera.CameraInfo.CAMERA_FACING_BACK);
// 设置视频的输出格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
// 设置视频编码格式
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
// 设置视频质量
CamcorderProfile profile = CamcorderProfile
.get(CamcorderProfile.QUALITY_HIGH);
// 设置高质量录制, 改变码率
recorder.setVideoEncodingBitRate(5 * 1024 * 1024);
// 设置分辨率
recorder.setVideoSize(profile.videoFrameWidth,
profile.videoFrameHeight);
// 设置帧率
recorder.setVideoFrameRate(60);
// 保存到文件中(路径)
saveFile = new File(Environment.getExternalStorageDirectory()
.getCanonicalFile()
+ "/"
+ System.currentTimeMillis()
+ ".mp4");
recorder.setOutputFile(saveFile.getAbsolutePath());
// 设置预览显示区
recorder.setPreviewDisplay(VideoActivity.getHolder().getSurface());
recorder.prepare();
recorder.start();
基本上来说,每一个API我都加上了注释,看的还是应该比较清晰的,不过这里还是需要提及几点需要注意的点:
1:之前在将Camera的时候就有强调过代码顺序的问题,这里同样也是需要注意这个问题的,尤其是编码格式后面的几个属性,最好是不要随意的修改代码的顺序;
2:我看到很多博主都有写到
// 设置分辨率
recorder.setVideoSize(profile.videoFrameWidth,profile.videoFrameHeight);
这个API的问题,后面跟着的两个宽高值不外乎都是640,480,我也试过写大于这两个数值的参数,均会报错,因此一开始也是默认认为这两个参数是一个最大可选参数,可是亲们也看到了,最后我并没有指定这两个参数,而是使用CamcorderProfile获取宽高。这是由于我在打印日志的时候发现,这两个值是可以到达1080或者1280的,因此可以极大的提高视频的质量,可是具体是为什么宝宝也没有搞明白,是否有哪个大神出来解释一下捏,嘿嘿~
3:另外,对于帧率和码率来说,其实并没有必要设置的这么高,只是由于宝宝的这个App适用于图像识别处理,所以对图像质量的要求比较高,因此设置的比较高。
最后,当我们停止录制的时候,要将MediaRecorder清空重置,便于下次的使用
public void stopVideo() {
try {
releaseMedioRecorder();
} catch (Exception e) {
}
}
private void releaseMedioRecorder() {
if (recorder != null) {
recorder.reset();
recorder.release();
recorder = null;
camera.lock();
}
}
当然在整个程序关闭的时候,我们还需要将Camera释放,便于其他进程的使用
public void releaseCamera() {
if (camera != null) {
camera.release();
camera = null;
}
}
第一次写东西哈,有什么问题希望各位大大指正哈~嘿嘿~以后应该也是会不定期更新博客,希望可以多多和大家交流Android开发的经验~么么哒