到目前为止Android中还不能直接录制正方形的视频,虽然不能直接录但是我们也有一些方式来处理录制后的视频,之前我写过一篇文章 Android自定义Camera(一), 可以先了解一下如何做一个简单的自定义相机demo, 那录制视频也要开启相机预览, 有以下几个步骤和需要注意的地方:
1.获取相机实例
/**
* 获取Camera实例
*
* @return
*/
private Camera getCamera(int id) {
Camera camera = null;
try {
camera = Camera.open(id);
} catch(Exception e) {
}
return camera;
}```
#2.开启预览
要注意,开启预览要在Activity的onResume方法里面开启,然后在onPause方法里面释放相机资源,举一个简单的例子,如果你在预览的时候按下了home键,此时再次打开程序,如果你是在Oncreate方法里面开启相机,那么再次打开预览界面应该会卡住。
/**
* 预览相机
*/
private void startPreview(Camera camera, SurfaceHolder holder) {
try {
setupCamera(camera);
camera.setPreviewDisplay(holder);
//获取相机预览角度, 后面录制视频需要用
recorderRotation = CameraUtil.getInstance().getRecorderRotation(mCameraId);
CameraUtil.getInstance().setCameraDisplayOrientation(this, mCameraId, camera);
camera.startPreview();
} catch(IOException e) {
e.printStackTrace();
}
}
/**
* 设置
*/
private void setupCamera(Camera camera) {
if (camera != null) {
Camera.Parameters parameters = camera.getParameters();
List < String > focusModes = parameters.getSupportedFocusModes();
if (focusModes != null && focusModes.size() > 0) {
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
//设置自动对焦
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
}
List < Camera.Size > videoSiezes = null;
if (parameters != null) {
//获取相机所有支持尺寸
videoSiezes = parameters.getSupportedVideoSizes();
for (Camera.Size size: videoSiezes) {}
}
if (videoSiezes != null && videoSiezes.size() > 0) {
//拿到一个预览宽度最小为720像素的预览值
Camera.Size videoSize = CameraUtil.getInstance().getPropVideoSize(videoSiezes, 720);
video_width = videoSize.width;
video_height = videoSize.height;
LogUtils.i("video_width===" + video_width);
LogUtils.i("video_height===" + video_height);
}
//这里第三个参数为最小尺寸 getPropPreviewSize方法会对从最小尺寸开始升序排列 取出所有支持尺寸的最小尺寸
Camera.Size previewSize = CameraUtil.getInstance().getPropPreviewSize(parameters.getSupportedPreviewSizes(), video_width);
parameters.setPreviewSize(previewSize.width, previewSize.height);
Camera.Size pictrueSize = CameraUtil.getInstance().getPropPictureSize(parameters.getSupportedPictureSizes(), video_width);
parameters.setPictureSize(pictrueSize.width, pictrueSize.height);
camera.setParameters(parameters);
/**
* 设置surfaceView的尺寸 因为camera默认是横屏,所以取得支持尺寸也都是横屏的尺寸
* 我们在startPreview方法里面把它矫正了过来,但是这里我们设置设置surfaceView的尺寸的时候要注意 previewSize.height<previewSize.width
* previewSize.width才是surfaceView的高度
* 一般相机都是屏幕的宽度 这里设置为屏幕宽度 高度自适应 你也可以设置自己想要的大小
*/
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(screenWidth, (screenWidth * video_width) / video_height);
//这里当然可以设置拍照位置 比如居中 我这里就置顶了
surfaceView.setLayoutParams(params);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(screenWidth, screenheight - screenWidth);
layoutParams.addRule(RelativeLayout.BELOW, surfaceView.getId());
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
bottomLayout.setLayoutParams(layoutParams);
}
}```
3.开始录制
这里要注意的是MediaRecorder的相关方法的调用顺序时候不能乱的 * *,详细可以看官网api说明
接下来开启录制:
protected void start() {
try {
pathName = System.currentTimeMillis() + "";
//视频存储路径
file = new File(MyApplication.getInstance().getTempPath() + File.separator + pathName + AppConfig.MP4);
//如果没有要创建
BitmapUtils.makeDir(file);
//初始化一个MediaRecorder
if (mediaRecorder == null) {
mediaRecorder = new MediaRecorder();
} else {
mediaRecorder.reset();
}
mCamera.unlock();
mediaRecorder.setCamera(mCamera);
//设置视频输出的方向 很多设备在播放的时候需要设个参数 这算是一个文件属性
mediaRecorder.setOrientationHint(recorderRotation);
//视频源类型
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setAudioChannels(2);
// 设置视频图像的录入源
// 设置录入媒体的输出格式
// mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
// 设置音频的编码格式
// mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 设置视频的编码格式
// mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
}
/*else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
} */
else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);
} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HIGH)) {
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
} else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_LOW)) {
profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
}
if (profile != null) {
profile.audioCodec = MediaRecorder.AudioEncoder.AAC;
profile.audioChannels = 1;
profile.audioSampleRate = 16000;
profile.videoCodec = MediaRecorder.VideoEncoder.H264;
mediaRecorder.setProfile(profile);
}
//视频尺寸
mediaRecorder.setVideoSize(video_width, video_height);
//数值越大 视频质量越高
mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);
// 设置视频的采样率,每秒帧数
// mediaRecorder.setVideoFrameRate(5);
// 设置录制视频文件的输出路径
mediaRecorder.setOutputFile(file.getAbsolutePath());
mediaRecorder.setMaxDuration(2000);
// 设置捕获视频图像的预览界面
mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface());
mediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
@Override public void onError(MediaRecorder mr, int what, int extra) {
// 发生错误,停止录制
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.release();
mediaRecorder = null;
LogUtils.i("Error");
}
}
});
mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {@Override public void onInfo(MediaRecorder mr, int what, int extra) {
//录制完成
}
});
// 准备、开始
mediaRecorder.prepare();
mediaRecorder.start();
new Thread(new Runnable() {@Override public void run() {
for (int i = 0; i < PROGRESS_MAX; i++) {
try {
Thread.currentThread().sleep(20);
Message message = new Message();
message.what = 1;
message.obj = i;
handler.sendMessage(message);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
} catch(Exception e) {
e.printStackTrace();
}
}
4.录制成功后接下来的重点来了
使用ffmpeg对视频进行裁剪正方形,ffmpeg使用Shell命令的方式进行视频操作,执行效率也是非常好,那首先你要集成FFmpeg到Android项目里面,这里我下载好了一个library, 直接引入到项目即可,感兴趣的伙伴可以自己编译一个库,这里我把命令贴出来解释一下,
ffmpeg - threads 4 - y - i / storage / emulated / 0 / CustomCamera / temp / 1476598263062.mp4 - metadata: s: v rotate = "0" - vf transpose = 1, crop = width: height: x: y - preset ultrafast - tune zerolatency - r 25 - vcodec libx264 - acodec copy / storage / emulated / 0 / CustomCamera / VIDEO / 1476598263062.mp4
-y: 如果文件存在那么覆盖掉
-i:输入
metadata: s: v rotate = "0": 重新编码除去rotate, 因为默认录制出来的是横屏视频,这里重新编码
transpose = 1:0 = 90CounterCLockwise and Vertical Flip(
default)逆时针旋转90度并且垂直翻转,下面类推1 = 90Clockwise 2 = 90CounterClockwise 3 = 90Clockwise and Vertical Flip
crop = width: height: x: y,其中width和height表示裁剪后的尺寸,x: y表示裁剪区域的左上角坐标
-preset ultrafast - tune zerolatency: 加快效率
r 25:帧率
-vcodec libx264 - acodec:编码方式libx264如果想知道更多关于ffmpeg的东西可以访问官网https: //ffmpeg.org/
再贴一下我的代码调用:
try {
fc.compress_clipVideo(file.getAbsolutePath(), file2.getAbsolutePath(), mCameraId, video_width, video_height, 0, 0, new ShellUtils.ShellCallback() {
@Override public void shellOut(String shellLine) {}
@Override public void processComplete(int exitValue) {
dialog.dismiss();
if (exitValue != 0) {
ToastFactory.showLongToast(context, getResources().getString(R.string.state_compress_error));
mHandler.sendEmptyMessage(R.string.state_compress_error);
} else {
mHandler.sendEmptyMessage(R.string.state_compress_end);
}
}
});
} catch(Exception e) {
e.printStackTrace();
}
FFmpeg编译出来的Android库还是很大的,大约15M左右,无疑增加了apk的大小,如果你的产品方向是视频图片gif等等的格式转换类似的功能可以考虑使用 * *
如果你对这样的功能感兴趣可以详细查看源码: