欢迎转载和提问,转载请注明出处
http://blog.csdn.net/ning_gg/article/details/54314315
这个是自己写的录制视频,为了简单,全写在一个Activity中,可以切换照相机,没有带聚焦,需要聚焦自己写个OnTouch即可。在我的测试机上跑起来畅通无阻,我只有3个测试机。如果你使用了我的有问题可以告诉我。
这个demo比网上什么的写死录像尺寸强多了,全部自匹配尺寸。如果你的跑不起可以第一时间检测这个尺寸设置。我的思路是获取手机屏幕尺寸,得到高/宽的比 比如1280/720 或者1920/1080这样的。然后遍历Camera的预览尺寸,得到一个跟手机尺寸最合适的那一个,这样的话预览的时候图像不会变形,然后获取手机录像的尺寸,这里必须说下不是每个手机都提供这个录像尺寸,像华为mate7就不提供,不提供的另说,提供的会遍历这个尺寸,得到相同比例(比如预览是1920/1080=1.77777)最小的一个(800/450),这样的话比大部分提供的写死一个尺寸强多了,而且录制出来的东西不会变形。为什么要找最小的是因为我们的需求录制的视频最小越好。对于那些不提供录像尺寸的,直接把预览尺寸设成录像尺寸就可以了。mate7就是这样跑通的。
附上代码
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.os.Environment;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.imapedia.ykdemo.R;
import com.imapedia.ykdemo.base.BaseActivity;
import com.imapedia.ykdemo.utils.L;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import butterknife.BindView;
import butterknife.OnClick;
/**
* Created by ning_gg on 2017/1/9.
* <p/>
* 录制视频,切换摄像头,尺寸匹配
*/
public class RecordVideoActivity extends BaseActivity implements MediaRecorder.OnErrorListener ,SurfaceHolder.Callback{
private String TAG=RecordVideoActivity.class.getSimpleName();
@BindView(R.id.activity_main_toolbar)
Toolbar activity_main_toolbar;
@BindView(R.id.recordvideo_sv)
SurfaceView recordvideo_sv;
private SurfaceHolder mSurfaceHolder;
@BindView(R.id.toolbar_change_tv)
TextView toolbar_change_tv;
@OnClick (R.id.toolbar_change_tv)
void toolbar_change_tvOnClick(){
if(recording){
try {
stopRecord();
} catch (Exception e) {
e.printStackTrace();
}
}
if (cameraPosition == Camera.CameraInfo.CAMERA_FACING_BACK) {
cameraPosition = Camera.CameraInfo.CAMERA_FACING_FRONT;
} else {
cameraPosition = Camera.CameraInfo.CAMERA_FACING_BACK;
}
if (isOpenCamera) {
mCamera.stopPreview();//停掉原来摄像头的预览
mCamera.release();//释放资源
mCamera = null;//取消原来摄像头
mCamera = Camera.open(cameraPosition);
try {
setCameraParms();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@BindView(R.id.recordvideo_btn)
Button recordvideo_btn;
@OnClick(R.id.recordvideo_btn)
void recordvideo_btnOnClick() {
if (!recording) {
try {
startRecord();
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
stopRecord();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private int screenHeight;//屏幕的高度
private int screenWidth;//屏幕的宽度
private Camera mCamera;
private int videoWidth = 0;// 视频分辨率宽度
private int videoHeight = 0;// 视频分辨率高度
public Camera.Size pictureSize;//照片分辨率
private Camera.Size previewSize;//预览分辨率
private boolean isOpenCamera;// 是否已经打开摄像头
private int cameraPosition = Camera.CameraInfo.CAMERA_FACING_FRONT;//当前摄像头
private MediaRecorder mMediaRecorder;
private String FilePath = Environment
.getExternalStorageDirectory().getPath() + File.separator + "ninggegetest" + File.separator;
private File mVecordFile;
//是否正在录制中
private boolean recording;
@Override
protected int getLayoutId() {
return R.layout.activity_recordvideo;
}
@Override
protected void init() {
DisplayMetrics dm = getResources().getDisplayMetrics();
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
mSurfaceHolder=recordvideo_sv.getHolder();
mSurfaceHolder.addCallback(this);
}
/**
* 开始录制
*/
private void startRecord() throws IOException {
createRecordDir();
initRecord();
recording=true;
recordvideo_btn.setBackgroundResource(R.drawable.stoprecord);
}
/**
* 创建文件
*/
private void createRecordDir() throws IOException {
File sampleDir = new File(FilePath);
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
File vecordDir = sampleDir;
// 创建文件
SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日_HH_mm_ss");
Date curDate = new Date(System.currentTimeMillis());//获取当前时间
String str = formatter.format(curDate);
mVecordFile = File.createTempFile(str, ".mp4", vecordDir);//mp4格式
}
/**
* 初始化
*
* @throws IOException
* @author ning_gg
* @date 2015-3-16
*/
private void initRecord() throws IOException {
//必须解锁 不然会报错
mCamera.unlock();
mMediaRecorder = new MediaRecorder();
mMediaRecorder.reset();
if (mCamera != null)
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setOnErrorListener(this);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
//后置摄像头是旋转90度
if (cameraPosition == 0) {
mMediaRecorder.setOrientationHint(90);// 输出旋转90度,保持竖屏录制
} else {
//前置摄像头是旋转270度
mMediaRecorder.setOrientationHint(270);
}
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 视频输出格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 音频格式
//H264用得最广 基本每个手机都支持 反倒其他的没有每个都支持
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setVideoSize(videoWidth, videoHeight);// 设置分辨率
mMediaRecorder.setVideoEncodingBitRate(2 * 1024 * 1024);// 设置帧频率,然后就清晰了
// 设置录制的视频帧率。必须放在设置编码和格式的后面,否则报错
// mMediaRecorder.setVideoFrameRate(20);
// mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());
mMediaRecorder.prepare();
mMediaRecorder.start();
}
/**
* 停止录制
*/
private void stopRecord() throws Exception {
if (mMediaRecorder != null) {
// 设置后不会崩
mMediaRecorder.setOnErrorListener(null);
mMediaRecorder.setPreviewDisplay(null);
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
}
recording=false;
recordvideo_btn.setBackgroundResource(R.drawable.startrecord);
}
@Override
public void onError(MediaRecorder mediaRecorder, int i, int i1) {
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
if (mCamera != null) {
mCamera.stopPreview();//停掉原来摄像头的预览
mCamera.release();//释放资源
mCamera = null;//取消原来摄像头
}
int i = Camera.getNumberOfCameras();
//如果只有一个摄像头
if (i == 1) {
mCamera = Camera.open();
toolbar_change_tv.setVisibility(View.GONE);
} else {
mCamera = Camera.open(cameraPosition);
}
isOpenCamera=true;
try {
setCameraParms();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
try {
stopRecord();
} catch (Exception e) {
e.printStackTrace();
}
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
private void setCameraParms() throws IOException{
mCamera.setPreviewDisplay(mSurfaceHolder);
Camera.Parameters myParam = mCamera.getParameters();
List<String> flashModes = myParam.getSupportedFlashModes();
String flashMode = myParam.getFlashMode();
// Check if camera flash exists
if (flashModes!=null&&!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
// Turn off the flash
if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
myParam.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
}
}
float percent = calcPreviewPercent();
List<Camera.Size> supportedPreviewSizes = myParam.getSupportedPreviewSizes();
//因为有些手机videoSizes不提供 比如华为mate7 那样的话就只能拿照相机预览数据了 当然预览数据尺寸不知道能不能当视频录制尺寸
List<Camera.Size> videoSizes = mCamera.getParameters().getSupportedVideoSizes();
previewSize = getPreviewMaxSize(supportedPreviewSizes, percent);
L.e(TAG, "预览尺寸w===" + previewSize.width + ",h==="
+ previewSize.height);
if (videoSizes != null) {
for (Camera.Size _s : videoSizes) {
float videoS = (float) _s.width / _s.height;
if (videoS == percent && _s.width < previewSize.width && _s.height < previewSize.height) {
videoWidth = _s.width;
videoHeight = _s.height;
continue;
}
}
}
if (videoWidth == 0 && videoHeight == 0) {
//如果没有拿到值 那就是当前的预览数据了
videoWidth = previewSize.width;
videoHeight = previewSize.height;
}
L.e(TAG, "录像尺寸w===" + videoWidth + ",h==="
+ videoHeight);
// 获取摄像头支持的各种分辨率
List<Camera.Size> supportedPictureSizes = myParam.getSupportedPictureSizes();
pictureSize = findSizeFromList(supportedPictureSizes, previewSize);
if (pictureSize == null) {
pictureSize = getPictureMaxSize(supportedPictureSizes, previewSize);
}
L.e(TAG, "照片尺寸w===" + pictureSize.width + ",h==="
+ pictureSize.height);
// 设置照片分辨率,注意要在摄像头支持的范围内选择
myParam.setPictureSize(pictureSize.width, pictureSize.height);
// 设置预浏尺寸,注意要在摄像头支持的范围内选择
myParam.setPreviewSize(previewSize.width, previewSize.height);
myParam.setJpegQuality(70);
mCamera.setParameters(myParam);
mCamera.setDisplayOrientation(90);
mCamera.startPreview();
mCamera.cancelAutoFocus();
}
private float calcPreviewPercent() {
float d = screenHeight;
return d / screenWidth;
}
private Camera.Size findSizeFromList(List<Camera.Size> supportedPictureSizes, Camera.Size size) {
Camera.Size s = null;
if (supportedPictureSizes != null && !supportedPictureSizes.isEmpty()) {
for (Camera.Size su : supportedPictureSizes) {
if (size.width == su.width && size.height == su.height) {
s = su;
break;
}
}
}
return s;
}
// 根据摄像头的获取与屏幕分辨率最为接近的一个分辨率
private Camera.Size getPictureMaxSize(List<Camera.Size> l, Camera.Size size) {
Camera.Size s = null;
for (int i = 0; i < l.size(); i++) {
if (l.get(i).width >= size.width && l.get(i).height >= size.width
&& l.get(i).height != l.get(i).width) {
if (s == null) {
s = l.get(i);
} else {
if (s.height * s.width > l.get(i).width * l.get(i).height) {
s = l.get(i);
}
}
}
}
return s;
}
// 获取预览的最大分辨率
private Camera.Size getPreviewMaxSize(List<Camera.Size> l, float j) {
int idx_best = 0;
int best_width = 0;
float best_diff = 100.0f;
for (int i = 0; i < l.size(); i++) {
int w = l.get(i).width;
int h = l.get(i).height;
if (w * h < screenHeight * screenWidth)
continue;
float previewPercent = (float) w / h;
float diff = Math.abs(previewPercent - j);
if (diff < best_diff) {
idx_best = i;
best_diff = diff;
best_width = w;
} else if (diff == best_diff && w > best_width) {
idx_best = i;
best_diff = diff;
best_width = w;
}
}
return l.get(idx_best);
}
@Override
public void onBackPressed() {
super.onBackPressed();
try {
stopRecord();
} catch (Exception e) {
e.printStackTrace();
}
finish();
}
}
TextureView+MediaRecorder录像原理跟这个是一样的,只是把SurfaceView替换成TextureView就行了,怎么使用TextureView在这里
http://blog.csdn.net/ning_gg/article/details/54134918
欢迎转载和提问,转载请注明出处
http://blog.csdn.net/ning_gg/article/details/54314315