公司有个项目,有一个需求是录制视频上传到网上,原本准备使用系统的录制功能,发现界面的跳转不能满足需求。于是就只能用自己写的,然后我也不会,就找了很多网上的例子,发现总是有些问题,然后我总结了一下写成了一个工具类,现在分享出来。
import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.Size; import android.media.MediaRecorder; import android.text.TextUtils; import android.util.Log; import android.view.OrientationEventListener; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.widget.RelativeLayout; import android.widget.Toast; import com.vomont.fileloadsdk.ACache; /** * 用于录制视频的工具类 * * @author 街角慢嗨 * @email 773675907@qq.com * @time 2017-04-21 */ @SuppressWarnings({
"unused", "deprecation"})
public class MediaRecorderUtil {
private MediaRecorder mediaRecorder; // 录像camera private Camera camera; // 用于判断前置摄像头还是后置摄像头 private int cameraPosition = 1; // 判断前置摄像头 还是后置 private boolean isCheck; private SurfaceHolder.Callback callback; // 上下文 private Context context; // 容器 private SurfaceView surfaceView; private String filePath; OrientationEventListener orientationEventListener; boolean flagRecord = false;// 是否正在录像 int rotationFlag = 90; int rotationRecord = 90; int frontRotate; int frontOri; int camaraType = 0; Activity activity; private int m, n; /** * 旋转界面UI */ private void rotationUIListener() {
orientationEventListener = new OrientationEventListener(context) {
@Override public void onOrientationChanged(int rotation) {
if (!flagRecord) {
if (((rotation >= 0) && (rotation <= 30)) || (rotation >= 330)) {
// 竖屏拍摄 if (rotationFlag != 0) {
// 旋转logo // rotationAnimation(rotationFlag, 0); // 这是竖屏视频需要的角度 rotationRecord = 90; // 这是记录当前角度的flag rotationFlag = 0; }
} else if (((rotation >= 230) && (rotation <= 310))) {
// 横屏拍摄 if (rotationFlag != 90) {
// 旋转logo // rotationAnimation(rotationFlag, 90); // 这是正横屏视频需要的角度 rotationRecord = 0; // 这是记录当前角度的flag rotationFlag = 90; }
} else if (rotation > 30 && rotation < 95) {
// 反横屏拍摄 if (rotationFlag != 270) {
// 旋转logo // rotationAnimation(rotationFlag, 270); // 这是反横屏视频需要的角度 rotationRecord = 180; // 这是记录当前角度的flag rotationFlag = 270; }
}
}
}
}; orientationEventListener.enable(); }
/** * 创建录制环境 * <p> * 需要用到的surfaceview * * @param context 上下文 */ public void create(RelativeLayout relativeLayout, final Context context, Activity activity) {
this.context = context; this.activity = activity; relativeLayout.removeAllViews(); surfaceView = new SurfaceView(context); relativeLayout.addView(surfaceView); // this.surfaceView = surfaceView; surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceView.setKeepScreenOn(true); callback = new SurfaceHolder.Callback() {
// 在控件创建的时候,进行相应的初始化工作 public void surfaceCreated(SurfaceHolder holder) {
// 打开相机,同时进行各种控件的初始化mediaRecord等 if (camera != null) {
camera.stopPreview(); camera.release(); camera = null; }
try {
camera = Camera.open(); } catch (Exception e) {
Toast.makeText(context, "没有权限,请打开录像权限!", Toast.LENGTH_SHORT).show(); return; }
mediaRecorder = new MediaRecorder(); }
// 当控件发生变化的时候调用,进行surfaceView和camera进行绑定,可以进行画面的显示 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
doChange(surfaceView.getHolder()); }
@Override public void surfaceDestroyed(SurfaceHolder holder) {
holder.removeCallback(this); if (camera != null) {
camera.stopPreview(); camera.release(); camera = null; }
}
}; // 为SurfaceView设置回调函数 surfaceView.getHolder().addCallback(callback); rotationUIListener(); }
// 当我们的程序开始运行,即使我们没有开始录制视频,我们的surFaceView中也要显示当前摄像头显示的内容 private void doChange(SurfaceHolder holder) {
try {
if (camera != null) {
Camera.Parameters parameters = camera.getParameters(); m = getCloselyPreSize(surfaceView.getWidth(), surfaceView.getHeight(), parameters.getSupportedPreviewSizes()).width; n = getCloselyPreSize(surfaceView.getWidth(), surfaceView.getHeight(), parameters.getSupportedPreviewSizes()).height; if (camaraType == 0) {
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦 camera.cancelAutoFocus();// 2如果要实现连续的自动对焦,这一句必须加上 }
if (rotationRecord == 90) {
parameters.setPreviewSize(getCloselyPreSize(surfaceView.getWidth(), surfaceView.getHeight(), parameters.getSupportedPreviewSizes()).width, getCloselyPreSize(surfaceView.getWidth(), surfaceView.getHeight(), parameters.getSupportedPreviewSizes()).height); }
camera.setParameters(parameters); camera.setPreviewDisplay(holder); // 设置surfaceView旋转的角度,系统默认的录制是横向的画面,把这句话注释掉运行你就会发现这行代码的作用 if (camaraType == 1) {
frontCameraRotate(); camera.setDisplayOrientation(frontRotate); } else {
camera.setDisplayOrientation(90); }
camera.startPreview(); }
} catch (IOException e) {
e.printStackTrace(); }
}
/** * 旋转前置摄像头为正的 */ private void frontCameraRotate() {
Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(1, info); int degrees = getDisplayRotation(activity); int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; }
frontOri = info.orientation; frontRotate = result; }
/** * 获取旋转角度 */ private int getDisplayRotation(Activity activity) {
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); switch (rotation) {
case Surface.ROTATION_0:
return 0; case Surface.ROTATION_90:
return 90; case Surface.ROTATION_180:
return 180; case Surface.ROTATION_270:
return 270; }
return 0; }
/** * 切换摄像头 */ public void changeCamara() {
// 切换前后摄像头 int cameraCount = 0; CameraInfo cameraInfo = new CameraInfo(); cameraCount = Camera.getNumberOfCameras();// 得到摄像头的个数 for (int i = 0; i < cameraCount; i++) {
Camera.getCameraInfo(i, cameraInfo);// 得到每一个摄像头的信息 if (cameraPosition == 1) {
// 现在是后置,变更为前置 if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
// 代表摄像头的方位,CAMERA_FACING_FRONT前置 // CAMERA_FACING_BACK后置 camera.stopPreview();// 停掉原来摄像头的预览 camera.release();// 释放资源 camera = null;// 取消原来摄像头 frontCameraRotate();// 前置旋转摄像头度数 camera = Camera.open(i);// 打开当前选中的摄像头 camaraType = i; try {
camera.setPreviewDisplay(surfaceView.getHolder());// 通过surfaceview显示取景画面 camera.setDisplayOrientation(frontRotate); } catch (IOException e) {
e.printStackTrace(); }
camera.startPreview();// 开始预览 cameraPosition = 0; isCheck = true; break; }
} else {
// 现在是前置, 变更为后置 if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
// 代表摄像头的方位,CAMERA_FACING_FRONT前置 // CAMERA_FACING_BACK后置 camera.stopPreview();// 停掉原来摄像头的预览 camera.release();// 释放资源 camera = null;// 取消原来摄像头 camera = Camera.open(i);// 打开当前选中的摄像头 camaraType = i; cameraPosition = 1; isCheck = false; doChange(surfaceView.getHolder()); break; }
}
}
}
/** * 停止录制 */ public void stopRecord() {
// 当结束录制之后,就将当前的资源都释放 if (mediaRecorder != null) {
mediaRecorder.release(); }
if (camera != null) {
camera.stopPreview(); camera.release(); }
camera = null; mediaRecorder = null; }
/** * 重新开始的初始化方法 */ public void resCreate() {
deleteFile(filePath); if (cameraPosition == 0) {
camera = Camera.open(1); isCheck = true; camaraType = 1; } else {
camera = Camera.open(0); camaraType = 0; isCheck = false; }
// // 然后再重新初始化所有的必须的控件和对象 mediaRecorder = new MediaRecorder(); doChange(surfaceView.getHolder()); }
/** * 打开闪光灯的接口 */ @SuppressLint("NewApi")
public void openMode(boolean isOpen) {
Camera.Parameters p = camera.getParameters(); if (isOpen) {
p.setFlashMode(Parameters.FLASH_MODE_TORCH); } else {
p.setFlashMode(Parameters.FLASH_MODE_OFF); }
camera.setParameters(p); }
/** * 视频镜头的伸缩 * 改变zoom值 改变视频的伸缩距离 * * @param m 传入的值 不同手机支持的最大m不同 * @see [类、类#方法、类#成员] */ @SuppressLint("NewApi")
public void setZoom(int m) {
Camera.Parameters p = camera.getParameters(); if (m <= 0) {
p.setZoom(0); } else if (m >= p.getMaxZoom()) {
p.setZoom(p.getMaxZoom()); } else {
p.setZoom(m); }
camera.setParameters(p); }
/** * 是否支持视频镜头的伸缩 * * @return * @see [类、类#方法、类#成员] */ public boolean isSupportZoom() {
boolean isSuppport = true; if (camera.getParameters().isSmoothZoomSupported()) {
isSuppport = false; }
return isSuppport; }
/** * 不同的手机最大伸缩的值不一样 * * @return * @see [类、类#方法、类#成员] */ public int getMaxZoom() {
if (camera != null) {
Camera.Parameters p = camera.getParameters(); return p.getMaxZoom(); }
return 0; }
/** * 拍照 * * @param callBack * @return * @see [类、类#方法、类#成员] */ public boolean TakePicture(PictureCallback callBack) {
if (null == camera) {
return false; }
camera.takePicture(null, null, callBack); return true; }
public void deleteVideo() {
if (filePath != null) {
File file = new File(filePath); if (file != null && file.exists() && file.isDirectory()) {
file.delete(); }
}
}
public static void deleteFile(String filePaths) {
File file = new File(filePaths); if (file.exists() && file.isDirectory()) {
file.delete(); }
}
/** * 通过对比得到与宽高比最接近的尺寸(如果有相同尺寸,优先选择) * * @param surfaceWidth 需要被进行对比的原宽,surface view的宽度 * @param surfaceHeight 需要被进行对比的原高 surface view的高度 * @param preSizeList 得到的支持预览尺寸的list,parmeters.getSupportedPreviewSizes() * 需要对比的预览尺寸列表 * @return 得到与原宽高比例最接近的尺寸 */ protected Camera.Size getCloselyPreSize(int surfaceWidth, int surfaceHeight, List<Size> preSizeList) {
int ReqTmpWidth; int ReqTmpHeight; // 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高 if (rotationRecord == 90) {
ReqTmpWidth = surfaceHeight; ReqTmpHeight = surfaceWidth; } else {
ReqTmpWidth = surfaceWidth; ReqTmpHeight = surfaceHeight; }
// 先查找preview中是否存在与surfaceview相同宽高的尺寸 for (Camera.Size size : preSizeList) {
if ((size.width == ReqTmpWidth) && (size.height == ReqTmpHeight)) {
return size; }
}
// 得到与传入的宽高比最接近的size float reqRatio = ((float) ReqTmpWidth) / ReqTmpHeight; float curRatio, deltaRatio; float deltaRatioMin = Float.MAX_VALUE; Camera.Size retSize = null; for (Camera.Size size : preSizeList) {
curRatio = ((float) size.width) / size.height; deltaRatio = Math.abs(reqRatio - curRatio); if (deltaRatio < deltaRatioMin) {
deltaRatioMin = deltaRatio; retSize = size; }
}
return retSize; }
/** * 开始录制 * @param path 录制保存的路径 * @param name 文件名称 */ public int startRecord(String path, String name) {
// 先释放被占用的camera,在将其设置为mediaRecorder所用的camera // 解锁并将摄像头指向MediaRecorder if (camera != null)
camera.unlock(); if (camera == null) {
return 0; }
if (null == mediaRecorder) {
Toast.makeText(context, "请打开录音权限!", Toast.LENGTH_SHORT).show(); return 0; }
mediaRecorder.setCamera(camera); // 指定输入源 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // mediaRecorder.setCamera(camera)如果不用这个方式 那么下面这句话可以加入用来设置视频清晰度的 不然会产生冲突 // 报错 我也不知道是为什么 // mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); // 设置输出格式和编码格式 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置编码格式 mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); // 根据缓存 选择视频的比特率 从而改变清晰度 ACache aCache = ACache.get(context); String av = aCache.getAsString("videosetting"); if (!TextUtils.isEmpty(av)) {
if (av.equals("0")) {
// 如果只设置比特率 不设置分辨率 清晰度是没有用的 mediaRecorder.setVideoEncodingBitRate(56 * 8 * 1024); // 改成 640*480可以通用 但其他的 有些分辨率 有些手机是不支持的,所以最好不要定死,而是根据你录像窗口的 // 的大小 选择一个最靠近的分辨率 // 另外 应为camera的parameters.setPreviewSize()设置了大小,所以如果不统一,录制出来的画面会有拉伸 // 所以两边的分辨率最好统一起来 mediaRecorder.setVideoSize(m, n); } else if (av.equals("1")) {
mediaRecorder.setVideoEncodingBitRate(84 * 8 * 1024); mediaRecorder.setVideoSize(m, n); } else {
mediaRecorder.setVideoEncodingBitRate(256 * 8 * 1024); mediaRecorder.setVideoSize(m, n); }
} else {
mediaRecorder.setVideoEncodingBitRate(56 * 8 * 1024); mediaRecorder.setVideoSize(m, n); }
int frontRotation; if (rotationRecord == 180) {
// 反向的前置 frontRotation = 180; } else {
// 正向的前置 frontRotation = (rotationRecord == 0) ? 270 - frontOri : frontOri; // 录制下来的视屏选择角度,此处为前置 }
mediaRecorder.setOrientationHint((camaraType == 1) ? frontRotation : rotationRecord); // 设置视频录制的分辨率。必须放在设置编码和格式的后面,否则报错 // 还有个设置焦距的 发现没卵用 我就不加了 自己找去 // 保存的路径以及文件名称 filePath = path + "/" + name + ".mp4"; mediaRecorder.setOutputFile(filePath); // 预览输出 mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface()); // // 判断是前置摄像头还是后置摄像头 然后设置视频旋转 如果不加上 后置摄像头没有问题 但是前置摄像头录制的视频会导致上下翻转 // 开始录制 try {
mediaRecorder.prepare(); try {
mediaRecorder.start(); } catch (Exception e) {
Toast.makeText(context, "请打开录音权限!", Toast.LENGTH_SHORT).show(); return 0; }
} catch (IOException e) {
e.printStackTrace(); }
return 1; }
}
使用的demo
import com.example.videocamara.R;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
private Button button, button2, button3;
private SurfaceView surfaceView;
private String path;
private String name;
private MediaRecorderUtil util;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
initSurfaceView();
}
private void initSurfaceView() {
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
util = new MediaRecorderUtil();
util.create(surfaceView, this);
}
private void init() {
button = (Button) findViewById(R.id.button);
button2 = (Button) findViewById(R.id.button2);
button3 = (Button) findViewById(R.id.button3);
button.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).getAbsolutePath()
+ "/yundd_vedio";
name = "123213";
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
util.startRecord(path, name);
break;
case R.id.button2:
util.stopRecord();
break;
case R.id.button3:
util.changeCamara();
break;
}
}
}