MediaRecorder结合SurfaceView录制视频
手机一般都有麦克风和摄像头,而Android系统就可以利用这些硬件来录制音视频了。为了增加对录制音视频的支持,Android系统提供了一个MediaRecorder的类。使用MediaRecorder结合SurfaceView录制视频。下面先简单了解一下MediaRecorder这个类。
MediaRecorder应用实例
- 使用MediaRecorder录制音乐
- 使用MediaRecorder录制视频
MediaRecorder功能设置(方法/作用)
- getAudioSourceMax() 获取音频信号源的最高值。
- getMaxAmplitude() 最后调用这个方法采样的时候返回最大振幅的绝对值
- getMetrics() 返回当前Mediacorder测量的数据
- getSurface() 当使用Surface作为视频源的时候,返回Sufrace对象
- pause() 暂停录制
- prepare() 准备录制
- resume() 恢复录制
- release() 释放与此MediaRecorder对象关联的资源
- reset() 重新启动mediarecorder到空闲状态
- setAudioChannels(int numChannels) 设置录制的音频通道数
- setAudioEncoder(int audio_encoder) 设置audio的编码格式
- setAudioEncodingBitRate(int bitRate) 设置录制的音频编码比特率
- setAudioSamplingRate(int samplingRate) 设置录制的音频采样率
- setAudioSource(int audio_source) 设置用于录制的音源
- setAuxiliaryOutputFile(String path) 辅助时间的推移视频文件的路径传递
- setAuxiliaryOutputFile(FileDescriptor fd) 在文件描述符传递的辅助时间的推移视频
- setCamera(Camera c) 设置一个recording的摄像头,此方法在API21被遗弃,被getSurface替代
- setCaptureRate(double fps) 设置视频帧的捕获率
- setInputSurface(Surface surface) 设置持续的视频数据来源
- setMaxDuration(int max_duration_ms) 设置记录会话的最大持续时间(毫秒)
- setMaxFileSize(long max_filesize_bytes) 设置记录会话的最大大小(以字节为单位)
- setOutputFile(FileDescriptor fd) 传递要写入的文件的文件描述符
- setOutputFile(String path) 设置输出文件的路径
- setOutputFormat(int output_format) 设置在录制过程中产生的输出文件的格式
- setPreviewDisplay(Surface sv) 表面设置显示记录媒体(视频)的预览
- setVideoEncoder(int video_encoder) 设置视频编码器,用于录制
- setVideoEncodingBitRate(int bitRate) 设置录制的视频编码比特率
- setVideoFrameRate(int rate) 设置要捕获的视频帧速率
- setVideoSize(int width, int height) 设置要捕获的视频的宽度和高度
- setVideoSource(int video_source) 开始捕捉和编码数据到setOutputFile(指定的文件)
- setLocation(float latitude, float longitude) 设置并存储在输出文件中的地理数据(经度和纬度)
- setProfile(CamcorderProfile profile) 指定CamcorderProfile对象
- setOrientationHint(int degrees) 设置输出的视频播放的方向提示
- setOnErrorListener(MediaRecorder.OnErrorListener l) 注册一个用于记录录制时出现的错误的监听器
- setOnInfoListener(MediaRecorder.OnInfoListener listener) 注册一个用于记录录制时出现的信息事件
MediaRecorder内的嵌套类
- MediaRecorder.AudioEncoder
- MediaRecorder.AudioSource
- MediaRecorder.VideoSource
- MediaRecorder.OutputFormat
MediaRecorder.AudioEncoder
大家都知道在录音的时候都要调用setAudioEncoder()方法,这个方法里面总有不同的参数,这个类就是参数的值,这里说一下各个不同值的区别:
default: 默认值。
AAC: 高级音频编码,简单说下优缺点:
AAC优点:相对于mp3,AAC格式的音质更佳,文件更小。
AAC不足:AAC属于有损压缩的格式,与时下流行的APE、FLAC等无损格式相比音质存在”本质上”的差距。加之,传输速度更快的USB3.0和16G以上大容量MP3正在加速普及,也使得AAC头上”小巧”的光环不复存在。
HE_AAC: HE-AAC混合了AAC与SBR技术。
AAC_ELD: 低延时的AAC音频编解码器。
AMR_NB: 编码的是无视频纯声音3gp文件就是amr,他的文件比AAC的小,音乐效果没ACC的好。
AMR_WB: VMR-WB 是新型可变速率多模式宽带语音编解码器,专为无线 CDMA 2000标准而设计,目的在于在 50 至 7000 HZ 的频带上进行语音编码,采样率为 16 KHZ。VMR-WB 基于 3GPP AMR-WB (G722.2) 编解码器,在每秒速率12.65 Kbit 上可实现互操作。
VORBIS: Vorbis是一种新的音频压缩格式,类似于MP3等现有的音乐格式。但有一点不同的是,它是完全免费、开放和没有专利限制的。OGG Vorbis有一个很出众的特点,就是支持多声道,随着它的流行,以后用随身听来听DTS编码的多声道作品将不会是梦想。
MediaRecorder.AudioSource
这个类对应setAudioSource(int) 方法,主要用来设置音频源; MediaRecorder.AudioSource音频参数说明如下:
MediaRecorder.AudioSource.CAMCORDER 设定录音来源于同方向的相机麦克风相同,若相机无内置相机或无法识别,则使用预设的麦克风
MediaRecorder.AudioSource.DEFAULT 默认音频源
MediaRecorder.AudioSource.MIC 设定录音来源为主麦克风。
MediaRecorder.AudioSource.VOICE_CALL设定录音来源为语音拨出的语音与对方说话的声音
MediaRecorder.AudioSource.VOICE_COMMUNICATION 摄像头旁边的麦克风
MediaRecorder.AudioSource.VOICE_DOWNLINK 下行声音
MediaRecorder.AudioSource.VOICE_RECOGNITION 语音识别
MediaRecorder.AudioSource.VOICE_UPLINK 上行声音
MediaRecorder.VideoEncoder
通过setVideoEncoder(int)来设置视频编码格式。
default: 默认编码
H263: H.263 多用于视频传输,其优点是压缩后体积小,占用带宽少;
MPEG_4_SP: 码率低代表它无需高码率即可有很好的视频效果,H264就更好了
H264 也是用于网络视频传输,优点也和H263差不多;再是H264会比前两者更优秀一点,不过一般用在标清或者高清压缩比较多。
VP8: 据说比H264优秀。
HEVC: 一种新的视频压缩标准。可以替代H.264/ AVC编码标准。它将在H.264标准2至4倍的复杂度基础上,将压缩效率提升一倍以上。
MediaRecorder.VideoSource
通过setVideoSource(int)方法,设置视频的来源。
CAMERA: 视频数据来源摄像头
DEFAULT: 系统默认
SURFACE: 视频数据来源于Surface
MediaRecorder.OutputFormat
通过setOutputFormat(int)方法来控制视频输出的格式:同理列举下各个参数的说明:
AAC_ADTS: ADTS的全称是Audio Data Transport Stream。是AAC音频的传输流格式。是AAC的一种非常常见的传输格式,
AMR_NB: 编码的是无视频纯声音3gp文件就是amr,他的文件比AAC的小,他的音乐效果没ACC的好
AMR_WB: VMR-WB 是新型可变速率多模式宽带语音编解码器,专为无线 CDMA 2000标准而设计,目的在于在 50 至 7000 HZ 的频带上进行语音编码,采样率为 16 KHZ。VMR-WB 基于 3GPP AMR-WB (G722.2) 编解码器,在每秒速率12.65 Kbit 上可实现互操作。
DEFAULT: 默认输出
MPEG_4: 这将指定录制的文件为mpeg-4格式,可以保护Audio和Video
RAW_AMR: 录制原始文件,这只支持音频录制,同时要求音频编码为AMR_NB
THREE_GPP: 录制后文件是一个3gp文件,支持音频和视频录制
WEBM: 编码为VP8/VORBIS的输出格式。
输出格式,大同小异,这里也没有做特别详细的讲解,将一下基本用法就可以了。一般情况下使用输出格式为MPEG_4的即可。
上述主要介绍了MediaRecorder的方法,作用以及部分参数的定义,下面进行MediaRecorder与SurfaceView结合使用并进行录制视频,视频格式为mp4。
MediaRecorder结合SurfaceView录制视频的步骤
-
SurfaceView与Camera进行绑定
- 实现SurfaceHolder.Callback(他的生命周期有三个)回调
- 在surfaceCreated中做相机的初始化操作
- 在surfaceChanged中设置相机的相关参数
- 在surfaceDestroyed中释放相机资源
-
创建存放录制视频的相关路径
-
初始化MediaRecorder
- 释放Camera锁(Camera.unlock()),并设置MediaRecorder与Camera进行绑定
- 设置MediaRecorder的相关参数
- 录制前的准备
-
停止录制
SurfaceView与Camera进行绑定
在SurfaceView与Camera进行绑定前一定要先取得holder即mSurfaceHolder = mSurfaceView.getHolder();并且保证屏幕常亮mSurfaceHolder.setKeepScreenOn(true)。
实现SurfaceHolder.Callback回调(mSurfaceHolder.addCallback(SurfaceHolder.Callback)),其回调有两种实现方式:
- 直接在类上实现SurfaceHolder.Callback接口
- 自定义一个Callback类去实现SurfaceHolder.Callback接口
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
if (mCamera == null) {
openCamera();
}
if (null != mCamera) {
mCamera.setPreviewDisplay(mSurfaceHolder);//Camera屏幕通过SurfaceHolder与SurfaceView 进行绑定
mCamera.startPreview();//开始预览
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(MainActivity.this, "打开相机失败", Toast.LENGTH_SHORT).show();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mScreenWidth = width;
mScreenHeight = height;
setCameraParameters();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCameraResource();
}
/**
* 打开相机
*/
private void openCamera() {
if (null != mCamera) {
releaseCameraResource();
}
try {
if (!checkCameraFacing(0) && !checkCameraFacing(1)) {
Toast.makeText(MainActivity.this, "未发现有可用摄像头", Toast.LENGTH_SHORT).show();
return;
}
if (!checkCameraFacing(mCameraPosition)) {
Toast.makeText(MainActivity.this, mCameraPosition == 0 ? "后置摄像头不可用" : "前置摄像头不可用", Toast.LENGTH_SHORT).show();
return;
}
mCamera = Camera.open(mCameraPosition);
// mCamera = Camera.open(0);
} catch (Exception e) {
e.printStackTrace();
releaseCameraResource();
}
}
/**
* 检查是否有摄像头
*
* @param facing 前置还是后置
* @return
*/
private boolean checkCameraFacing(int facing) {
int cameraCount = Camera.getNumberOfCameras();
Camera.CameraInfo info = new Camera.CameraInfo();
for (int i = 0; i < cameraCount; i++) {
Camera.getCameraInfo(i, info);
if (facing == info.facing) {
return true;
}
}
return false;
}
//设置相机参数
private void setCameraParameters(){
try {
// mCamera = Camera.open();// 打开摄像头
if (mCamera == null)
return;
// mCamera.setDisplayOrientation(90);//将展示方向旋转90度
// mCamera.setPreviewDisplay(mSurfaceHolder);//Surface 预览
//可以通过获取相机的参数实例,设置里面各种效果,包括刚刚的预览图,前置摄像头,闪光灯等
mParameters = mCamera.getParameters();// 获得相机参数
// //设置图片格式
// mParameters.setPictureFormat(ImageFormat.JPEG);
// mParameters.setJpegQuality(100);
// mParameters.setJpegThumbnailQuality(100);
// mParameters.setPictureFormat(PixelFormat.JPEG);//设定图片格式为JPEG 默认为NV21
// mParameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);//设置预览版式为YCbCr_420_SP 默认为NV21
//该方法返回了SurfaceView的宽与高,根据给出的尺寸与宽高比例,获取一个最适配的预览尺寸
List<Camera.Size> mSupportedPreviewSizes = mParameters.getSupportedPreviewSizes();
List<Camera.Size> mSupportedVideoSizes = mParameters.getSupportedVideoSizes();
mOptimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,
mSupportedPreviewSizes, mScreenWidth, mScreenHeight);
//该方法是获取最佳的预览与摄像尺寸。然后设置预览图像大小
mParameters.setPreviewSize(mOptimalSize.width, mOptimalSize.height); // 设置预览图像大小
mCamera.setDisplayOrientation(getDegree());
// if(this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE){
// //如果是竖屏
//// mParameters.set("orientation", "portrait");
// Log.e("lu","我是竖屏............");
// //在2.2以上可以使用
// mCamera.setDisplayOrientation(90);
// }else{
//// mParameters.set("orientation", "landscape");
// Log.e("lu","我是横屏............");
// //在2.2以上可以使用
// mCamera.setDisplayOrientation(0);
// }
List<String> focusModes = mParameters.getSupportedFocusModes();
if (focusModes.contains("continuous-video")) {
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
mFpsRange = mParameters.getSupportedPreviewFpsRange();
List<String> modes = mParameters.getSupportedFocusModes();
if (modes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
//支持自动聚焦模式
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
mCamera.setParameters(mParameters);// 设置相机参数
// mCamera.startPreview();// 开始预览
//假设要支持自动对焦功能,则在需要的情况下,或者在上述surfaceChanged调用完startPreview函数后,可以调用Camera::autoFocus函数来设置自动对焦回调函数,该步是可选操作,有些设备可能不支持,可以通过Camera::getFocusMode函数查询。代码可以参考如下:
// 自动对焦
// mCamera.autoFocus(new Camera.AutoFocusCallback(){
// @Override
// public void onAutoFocus(boolean success, Camera camera){
// if (success){
// // success为true表示对焦成功,改变对焦状态图像
// ivFocus.setImageResource(R.drawable.focus2);
// }
// }
// });
}catch (Exception io){
io.printStackTrace();
}
}
private int getDegree() {
//获取当前屏幕旋转的角度
int rotating = this.getWindowManager().getDefaultDisplay().getRotation();
int degree = 0;//度数
//根据手机旋转的角度,来设置surfaceView的显示的角度
switch (rotating) {
case Surface.ROTATION_0:
degree = 90;
break;
case Surface.ROTATION_90:
degree = 0;
break;
case Surface.ROTATION_180:
degree = 270;
break;
case Surface.ROTATION_270:
degree = 180;
break;
}
return degree;
}
/**
* 释放摄像头资源
*/
private void releaseCameraResource() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.lock();
mCamera.release();
mCamera = null;
}
}
最优尺寸
/**
* 这两个队列分别是 该相机支持的 预览大小(一般就是拍照时照片的大小),另外一个就是支持适配的大小,
* 因为都是队列,说明相机支持很多组尺寸,而且,照片的尺寸与视频的尺寸是不一样的。我debug看了几款手机,
* 通常摄像支持的尺寸少一点,照片会多一些。这样,我们就要通过刚刚方法给出的宽高,
* 获取一个最佳匹配的预览尺寸.
*
* @param supportedVideoSizes Supported camera video sizes.
* @param previewSizes Supported camera preview sizes.
* @param w The width of the view.
* @param h The height of the view.
* @return Best match camera video size to fit in the view.
*/
public static Camera.Size getOptimalVideoSize(List<Camera.Size> supportedVideoSizes,
List<Camera.Size> previewSizes, int w, int h) {
// Use a very small tolerance because we want an exact match.
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) w / h;
// Supported video sizes list might be null, it means that we are allowed to use the preview
// sizes
List<Camera.Size> videoSizes;
if (supportedVideoSizes != null) {
videoSizes = supportedVideoSizes;
} else {
videoSizes = previewSizes;
}
Camera.Size optimalSize = null;
// Start with max value and refine as we iterate over available video sizes. This is the
// minimum difference between view and camera height.
double minDiff = Double.MAX_VALUE;
// Target view height
int targetHeight = h;
// Try to find a video size that matches aspect ratio and the target view size.
// Iterate over all available sizes and pick the largest size that can fit in the view and
// still maintain the aspect ratio.
for (Camera.Size size : videoSizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find video size that matches the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : videoSizes) {
if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
上述就是SurfaceView与Camera绑定,Camera初始化,Camera参数设置,Camera资源释放。
保存录制视频的路径
/**
* 创建目录与文件
*/
private void createRecordDir() {
mDirName = String.valueOf(System.currentTimeMillis()) + String.valueOf( new Random().nextInt(1000));
File FileDir = new File(BASE_PATH + mDirName);
if (!FileDir.exists()) {
FileDir.mkdirs();
}
// 创建文件
try {
mVecordFile = new File(FileDir.getAbsolutePath() + "/" + Utils.getDateNumber() +".mp4");
Log.e("Path:", mVecordFile.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
初始化MediaRecorder 并 设置MediaRecorder的参数
/**
* 录制前,初始化
*/
private void initRecord() {
try {
//进入一个预览的拍摄页面了,该页面其实也可以用来做拍照。
// 要想做拍摄,还要实例化MediaRecorder,然后传入camera并初始化相应的参数。
if(mMediaRecorder == null){
mMediaRecorder = new MediaRecorder();
}
if(mCamera != null){
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
}
mMediaRecorder.setOnErrorListener(this);
// mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//音频源 麦克风
// mMediaRecorder.setAudioChannels(1);//单声道
// mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//音频格式
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);//音频源 麦克风
// 设置录制视频源为Camera(相机)
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//视频源
// mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//视频输出格式
// mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);//视频录制格式
mMediaRecorder.setOrientationHint(90);//视频旋转90度
// Use the same size for recording profile.
CamcorderProfile mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
mProfile.videoFrameWidth = mOptimalSize.width;
mProfile.videoFrameHeight = mOptimalSize.height;
//
mMediaRecorder.setProfile(mProfile);
//该设置是为了抽取视频的某些帧,真正录视频的时候,不要设置该参数
// mMediaRecorder.setCaptureRate(mFpsRange.get(0)[0]);//获取最小的每一秒录制的帧数
// // 设置视频录制的分辨率。必须放在设置编码和格式的后面,否则报错,而且这个值要适配
// //手机,不然也会在后面stop方法报错!
// mMediaRecorder.setVideoSize(1280,720);
// // 设置录制的视频帧率。必须放在设置编码和格式的后面,否则报错,这样设置变清晰
// mMediaRecorder.setVideoEncodingBitRate(10*1024*1024);
mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
e.printStackTrace();
releaseRecord();
}
}
开始录制视频
开始录制视频时并计时,到达制定时间就停止录制。
/**
* 开始录制视频
*/
public void startRecord(final OnRecordFinishListener onRecordFinishListener) {
this.mOnRecordFinishListener = onRecordFinishListener;
isStarting = true;
lay_tool.setVisibility(View.INVISIBLE);
tag_start.setVisibility(View.VISIBLE);
anim.start();
createRecordDir();
try {
initRecord();
mTimeCount = 0;// 时间计数器重新赋值
mTimer = new Timer();
timerTask = new TimerTask() {
@Override
public void run() {
mTimeCount++;
mProgressBar.setProgress(mTimeCount);
if (mTimeCount == mRecordMaxTime) {// 达到指定时间,停止拍摄
runOnUiThread(new Runnable() {
@Override
public void run() {
stop();
if (mOnRecordFinishListener != null){
mOnRecordFinishListener.onRecordFinish();
}
}
});
}
}
};
mTimer.schedule(timerTask, 0, 100);
} catch (Exception e) {
e.printStackTrace();
}
}
停止录制视频
停止录制视频时一定要释放视频资源及相机资源
/**
* 停止拍摄
*/
public void stop() {
stopRecord();
releaseRecord();
releaseCameraResource();
}
/**
* 停止录制
*/
public void stopRecord() {
mProgressBar.setProgress(0);
isStarting = false;
tag_start.setVisibility(View.GONE);
anim.stop();
lay_tool.setVisibility(View.VISIBLE);
if(timerTask != null)
timerTask.cancel();
if (mTimer != null)
mTimer.cancel();
if (mMediaRecorder != null) {
try {
mMediaRecorder.stop();
mMediaRecorder.reset();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
释放MediaRecorder资源
/**
* 释放资源
*/
private void releaseRecord() {
if (mMediaRecorder != null) {
mMediaRecorder.setPreviewDisplay(null);
mMediaRecorder.setOnErrorListener(null);
try {
mMediaRecorder.release();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
mMediaRecorder = null;
}
闪光灯关闭与开启
//闪光灯关闭与开启
private void flashLightToggle(){
try {
if(isFlashLightOn){
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);//关闭闪光灯
mCamera.setParameters(mParameters);
isFlashLightOn = false;
}else {
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//开启闪光灯
mCamera.setParameters(mParameters);
isFlashLightOn = true;
}
} catch (Exception e) {
e.printStackTrace();
}
}
前后摄像头切换
前后摄像头切换的代码还可以简化的,这里是没有简化的,大家看得懂就可以了
//前后摄像头切换,就要重新初始化 camera实例
private void switchCamera(){
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
int cameraCount = Camera.getNumberOfCameras();//得到摄像头的个数
for(int i = 0; i < cameraCount; i++ ) {
Camera.getCameraInfo(i, cameraInfo);//得到每一个摄像头的信息
if(mCameraPosition == 1) {
//现在是后置,变更为前置
if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
mCamera.stopPreview();//停掉原来摄像头的预览
mCamera.release();//释放资源
mCamera = null;//取消原来摄像头
mCamera = Camera.open(i);//打开当前选中的摄像头
try {
mCamera.setDisplayOrientation(90);// 打开摄像头并将展示方向旋转90度
mCamera.setPreviewDisplay(mSurfaceHolder);//通过surfaceview显示取景画面
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.setParameters(mParameters);// 设置相机参数
mCamera.startPreview();//开始预览
mCameraPosition = 0;
break;
}
} else {
//现在是前置, 变更为后置
if(cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {//代表摄像头的方位,CAMERA_FACING_FRONT前置 CAMERA_FACING_BACK后置
mCamera.stopPreview();//停掉原来摄像头的预览
mCamera.release();//释放资源
mCamera = null;//取消原来摄像头
mCamera = Camera.open(i);//打开当前选中的摄像头
try {
mCamera.setDisplayOrientation(90);// 打开摄像头并将展示方向旋转90度
mCamera.setPreviewDisplay(mSurfaceHolder);//通过surfaceview显示取景画面
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.setParameters(mParameters);// 设置相机参数
mCamera.startPreview();//开始预览
mCameraPosition = 1;
break;
}
}
}
}
添加权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
总结
- 要注意让SurfaceView保持常亮状态mSurfaceHolder.setKeepScreenOn(true)
- Camera屏幕通过SurfaceHolder与SurfaceView 进行绑定
- MediaRecorder设置参数时一定要注意部分参数设置的顺序,不然会报错。
- 如果兼容横竖屏,注意相机方向与SufaceView的方向,视频旋转角度问题
- 音频设置时首选AAC就行了,如果录音被抢占了释放掉或者选default就不会出现这种问题。
- 一定不要忘记添加权限
- 视频编码格式:default,H263,H264,MPEG_4_SP
- 获得视频资源:default,CAMERA
- 音频编码格式:default,AAC,AMR_NB,AMR_WB,
- 获得音频资源:defalut,camcorder,mic,voice_call,voice_communication,voice_downlink, voice_recognition, voice_uplink;
- 输出方式:amr_nb,amr_wb,default,mpeg_4,raw_amr,three_gpp.