本人是一个小小码农,期间在CSDN上看了很多大神的文章,得到了很多帮助,避免了很多坑,所以我也想向大家分享一下我的开发的一些心得,给更多的萌新一些启发,避免走弯路,好了,废话不多说,直接进入正题...
先给大家看下布局:
很简单,背景使用SurfaceView,然后一个开始一个结束按钮,
然后进入代码片段:
这里开头一些findViewById我就不贴了,我习惯用ButterKnife
这是里面需要用到的一些参数:
private SurfaceHolder mSurfaceHolder;
private boolean isRecording;// 标记,判断当前是否正在录制
private long mRecordCurrentTime = 0;//录制时间间隔
// 存储文件
private File mVecordFile;
private Camera mCamera;
private MediaRecorder mediaRecorder;
private void initSurface(){
mSurfaceHolder = surfaceView.getHolder();
// 设置Surface不需要维护自己的缓冲区
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(mCallBack);
}
private SurfaceHolder.Callback mCallBack = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
initCamera();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
if (mSurfaceHolder.getSurface() == null) {
return;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
stopCamera();
}
};
//初始化摄像头
private void initCamera() {
mCamera = Camera.open(0); //①
mCamera.setDisplayOrientation(90);
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.cancelAutoFocus();//此句加上 可自动聚焦 必须加
Camera.Parameters parameters = mCamera.getParameters();
//查询摄像头支持的分辨率
parameters.getSupportedPreviewSizes();
for (int i = 0; i < parameters.getSupportedPreviewSizes().size(); i++) {
// Log.i("<><><><>Width", parameters.getSupportedPreviewSizes().get(i).width + "");
// Log.i("<><><><>Height", parameters.getSupportedPreviewSizes().get(i).height + "");
}
//设置分辨率
parameters.setPreviewSize(1280, 720);
//设置聚焦模式
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
//缩短Recording启动时间
parameters.setRecordingHint(true);
//是否支持影像稳定能力,支持则开启
if (parameters.isVideoStabilizationSupported())
parameters.setVideoStabilization(true);
mCamera.setParameters(parameters);
mCamera.startPreview();
} catch (IOException e) {
Log.i(TAG, "Error starting camera preview: " + e.getMessage());
}
}
中间的一段for循环是用来查看当前设备支持的分辨率,然后从中选出一个适合的分辨率,之前也有一段代码是自动挑选出合适的分辨率,但是我找不到, 而且我这个项目用的是开发板开发的,所以设备比较破旧,很多东西兼容性不好,一些代码就不适用,好了,录像前的准备工作做好了,接下来就可以开始录像了:
/**
* 创建视频文件
*
* @return
*/
private boolean createRecordDir() {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
Toast.makeText(this, getString(R.string.SDcardisxists), Toast.LENGTH_SHORT).show();
return false;
}
File sampleDir = new File("/sdcard/myVideo/");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
videoName = "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".mp4";
mVecordFile = new File(sampleDir, videoName);
return true;
}
private MediaRecorder.OnErrorListener onErrorListener = new MediaRecorder.OnErrorListener() {
@Override
public void onError(MediaRecorder mediaRecorder, int what, int extra) {
try {
if (mediaRecorder != null) {
mediaRecorder.reset();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* 配置MediaRecorder()
*/
private void setConfigRecord() {
mediaRecorder = new MediaRecorder();
mediaRecorder.reset();
mediaRecorder.setCamera(mCamera);
mediaRecorder.setOnErrorListener(onErrorListener);
//录像角度
mediaRecorder.setOrientationHint(270);
//使用SurfaceView预览
mediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
//1.设置采集声音
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置采集图像
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
//2.设置视频,音频的输出格式 mp4
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
//3.设置音频的编码格式
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//设置图像的编码格式
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
mediaRecorder.setAudioEncodingBitRate(44100);
if (mProfile.videoBitRate > 2 * 1024 * 1024) {
mediaRecorder.setVideoEncodingBitRate(2 * 1024 * 1024);
} else {
mediaRecorder.setVideoEncodingBitRate(1024 * 1024);
}
mediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);
mediaRecorder.setVideoSize(1280, 720);
mediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());
}
**
* 开始录制
*
* @throws IOException
*/
private void startRecord() throws IOException {
//这是是判断视频文件有没有创建,如果没有就返回
boolean creakOk = createRecordDir();
if (!creakOk) {
return;
}
mCamera.unlock();
setConfigRecord();
try {
mediaRecorder.prepare();
mediaRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
isRecording = true;
mRecordTime.start();
}
/**
* 停止录制
*/
private void stopRecord() {
if (isRecording && mediaRecorder != null) {
mediaRecorder.setOnErrorListener(null);
mediaRecorder.setPreviewDisplay(null);
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
mediaRecorder = null;
isRecording = false;
System.out.println(TAG ,""+ mVecordFile.toString());
}
}
/**
* 关闭摄像头
*/
private void stopCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
/**
* 压缩图片
*
* @param image
* @return
*/
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 90;
while (baos.toByteArray().length / 1024 > 100) {
baos.reset();
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
return bitmap;
}
/**
* 获取到视频第一帧
*/
class imageTask extends AsyncTask {
private int videoTime;
@Override
protected String doInBackground(File... files) {
//创建一个文件夹
String VideoImagePath = "/sdcard/myVideo/compress/";
String videoImageName = "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".png";
File dir = new File(VideoImagePath);
if (!dir.exists()) {
dir.mkdir();
}
videoImageFile = new File(VideoImagePath, videoImageName);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(mVecordFile.toString());
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); // 播放时长单位为毫秒
videoTime = (int) (Integer.parseInt(duration) * 0.001);
Log.i(TAG, duration);
// Bitmap bitmap = ImageUtils.compressImage(mmr.getFrameAtTime(), 50, 50);
Bitmap bitmap = compressImage(mmr.getFrameAtTime());
try {
FileOutputStream out = new FileOutputStream(videoImageFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return videoImageFile.toString();
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}
这里我比较喜欢用AsyncTask,虽然说完全没有必要开这个异步,但是我还是比较喜欢用,因为获得视频第一帧然后去压缩图片这个过程还是挺久的,如果放进异步的话这样用户体验感会好点;
这是因为 android6.0使用camera.open()时需要在onCreate()里面添加如下代码,否则会报错"Failed to connect to camera service":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {Manifest.permission.CAMERA}, 1);
}
}
这是调用麦克风录音的时候权限问题
第一种方法时在Manifest里面添加权限:
如果这样还不行的话就要打开手机设置>>>应用管理>>>找到你的应用>>>点开你申请的权限
然后就OK了!
package com.example.twj.test;
import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class Main4Activity extends AppCompatActivity {
@BindView(R.id.stop_button)
Button stopButton;
private String TAG = "--==>>";
@BindView(R.id.record_surfaceView)
SurfaceView surfaceView;
@BindView(R.id.record_control)
Button recordControl;
private SurfaceHolder mSurfaceHolder;
private boolean isRecording;// 标记,判断当前是否正在录制
// 存储文件
private File mVecordFile;
private Camera mCamera;
private MediaRecorder mediaRecorder;
private String videoName;
private File videoImageFile;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
ButterKnife.bind(this);
initSurface();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {Manifest.permission.CAMERA}, 1);
}
}
}
@OnClick({R.id.record_control, R.id.stop_button})
public void onViewClicked(View view) {
switch (view.getId()) {
//开始
case R.id.record_control:
//这是是判断视频文件有没有创建,如果没有就返回
boolean creakOk = createRecordDir();
if (!creakOk) {
return;
}
mCamera.unlock();
setConfigRecord();
try {
mediaRecorder.prepare();
mediaRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
isRecording = true;
break;
//停止
case R.id.stop_button:
stopRecord();
break;
}
}
private void initSurface() {
mSurfaceHolder = surfaceView.getHolder();
// 设置Surface不需要维护自己的缓冲区
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(mCallBack);
}
private SurfaceHolder.Callback mCallBack = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
initCamera();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
if (mSurfaceHolder.getSurface() == null) {
return;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
stopCamera();
}
};
//初始化摄像头
private void initCamera() {
mCamera = Camera.open(0); //①
mCamera.setDisplayOrientation(90);
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.cancelAutoFocus();//此句加上 可自动聚焦 必须加
Camera.Parameters parameters = mCamera.getParameters();
//查询摄像头支持的分辨率
parameters.getSupportedPreviewSizes();
for (int i = 0; i < parameters.getSupportedPreviewSizes().size(); i++) {
Log.i("<><><><>Width", parameters.getSupportedPreviewSizes().get(i).width + "");
Log.i("<><><><>Height", parameters.getSupportedPreviewSizes().get(i).height + "");
}
//设置分辨率
parameters.setPreviewSize(1280, 720);
//设置聚焦模式
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
//缩短Recording启动时间
parameters.setRecordingHint(true);
//是否支持影像稳定能力,支持则开启
if (parameters.isVideoStabilizationSupported())
parameters.setVideoStabilization(true);
mCamera.setParameters(parameters);
mCamera.startPreview();
} catch (IOException e) {
Log.i(TAG, "Error starting camera preview: " + e.getMessage());
}
}
/**
* 创建视频文件
*
* @return
*/
private boolean createRecordDir() {
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
Toast.makeText(this, "SD卡不存在!", Toast.LENGTH_SHORT).show();
return false;
}
File sampleDir = new File("/sdcard/myVideo/");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
videoName = "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".mp4";
mVecordFile = new File(sampleDir, videoName);
return true;
}
private MediaRecorder.OnErrorListener onErrorListener = new MediaRecorder.OnErrorListener() {
@Override
public void onError(MediaRecorder mediaRecorder, int what, int extra) {
try {
if (mediaRecorder != null) {
mediaRecorder.reset();
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
/**
* 配置MediaRecorder()
*/
private void setConfigRecord() {
mediaRecorder = new MediaRecorder();
mediaRecorder.reset();
mediaRecorder.setCamera(mCamera);
mediaRecorder.setOnErrorListener(onErrorListener);
//录像角度
mediaRecorder.setOrientationHint(270);
//使用SurfaceView预览
mediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
//1.设置采集声音
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置采集图像
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
//2.设置视频,音频的输出格式 mp4
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
//3.设置音频的编码格式
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//设置图像的编码格式
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
mediaRecorder.setAudioEncodingBitRate(44100);
if (mProfile.videoBitRate > 2 * 1024 * 1024) {
mediaRecorder.setVideoEncodingBitRate(2 * 1024 * 1024);
} else {
mediaRecorder.setVideoEncodingBitRate(1024 * 1024);
}
mediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);
mediaRecorder.setVideoSize(1280, 720);
mediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());
}
/**
* 停止录制
*/
private void stopRecord() {
if (isRecording && mediaRecorder != null) {
mediaRecorder.setOnErrorListener(null);
mediaRecorder.setPreviewDisplay(null);
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
mediaRecorder = null;
isRecording = false;
Log.i(TAG ,""+ mVecordFile.toString());
new imageTask().execute(mVecordFile);
}
}
/**
* 关闭摄像头
*/
private void stopCamera() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
/**
* 压缩图片
*
* @param image
* @return
*/
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 90;
while (baos.toByteArray().length / 1024 > 100) {
baos.reset();
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
return bitmap;
}
/**
* 获取到视频第一帧
*/
class imageTask extends AsyncTask {
@Override
protected String doInBackground(File... files) {
//创建一个文件夹
String VideoImagePath = "/sdcard/myVideo/compress/";
String videoImageName = "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".png";
File dir = new File(VideoImagePath);
if (!dir.exists()) {
dir.mkdir();
}
videoImageFile = new File(VideoImagePath, videoImageName);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(mVecordFile.toString());
String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); // 播放时长单位为毫秒
Log.i(TAG, duration);
// Bitmap bitmap = ImageUtils.compressImage(mmr.getFrameAtTime(), 50, 50);
Bitmap bitmap = compressImage(mmr.getFrameAtTime());
try {
FileOutputStream out = new FileOutputStream(videoImageFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return videoImageFile.toString();
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}
}
到这里项目一些基本的录像方面的要求就大致完成了,这是我第一次贴代码,如果有不足还请各位大神指出;