先上張圖檔看看效果
簡單叙述下
- 首先通過Camera類調用系統相機
- 通過surfaceview繪制出來
- 通過MediaRecorder來錄制視訊
- 閃光燈 和 切換攝像頭 需要重新配置Camera的參數
- Camera預覽界面畫面拉升的原因是因為Surfaceview的大小與設定的比例不一緻的
**本次版本更新了 切換前置攝像頭錄制視訊問題
Android部分手機錄制視訊适配IOS手機問題 (OPPO手機部分不适配 目前啟用的H264格式)**
完整的代碼
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.tongxun.atongmu.dialog.CommonDialog;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class CircleViedoActivity extends Activity implements View.OnClickListener,MediaRecorder.OnErrorListener {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private ProgressBar mProgressBar;
private Button shoot_button;
private ImageView circle_light_btn;
private ImageView circle_change_camera_btn;
private TextView circle_camera_time;
private MediaRecorder mMediaRecorder;
private Camera mCamera;
private Timer mTimer;// 計時器
private OnRecordFinishListener mOnRecordFinishListener;// 錄制完成回調接口
private int mWidth;// 視訊分辨率寬度
private int mHeight;// 視訊分辨率高度
private boolean isOpenCamera;// 是否一開始就打開攝像頭
private int mRecordMaxTime;// 一次拍攝最長時間
private int mTimeCount;// 時間計數
private int mNumberOfCameras=;//手機攝像頭的數量
Bitmap bitmap;//圖檔縮略圖
private int screenWidth;
public static File mVecordFile = null;// 檔案
private boolean isOpenFlash=false;
private boolean isBackCamera=true;
private int mbackCamera;
private int mfrontCamera;
private CommonDialog commonDialog;
private static final int MOVICE_SUCCESS=;//錄制完成
private static final int MOVICE_FILE=;//錄制失敗
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_super_video);
init_datas();
init_view();
set_datas();
init_event();
}
private void init_datas() {
isOpenCamera=true;//預設一開始就打開相機
mRecordMaxTime=;//設定錄制的時間
//擷取手機攝像頭的數量
Camera.CameraInfo cameraInfo=new Camera.CameraInfo();
mNumberOfCameras=Camera.getNumberOfCameras();
for(int camidx=;camidx<mNumberOfCameras;camidx++){
Camera.getCameraInfo(camidx, cameraInfo);
if(cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_BACK){
mbackCamera=camidx;
}else if(cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_FRONT){
mfrontCamera=camidx;
}
}
}
private void init_view() {
mWidth=;
mHeight=;
//擷取螢幕的寬度
screenWidth = getWindowManager().getDefaultDisplay().getWidth();
mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);//預覽界面
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);//進度條
shoot_button = (Button) findViewById(R.id.shoot_button);//拍攝按鈕
circle_camera_time= (TextView) findViewById(R.id.circle_camera_time);
circle_change_camera_btn= (ImageView) findViewById(R.id.circle_change_camera_btn);//切換攝像頭
circle_light_btn= (ImageView) findViewById(R.id.circle_light_btn);//開啟閃光燈
ViewGroup.LayoutParams params=mSurfaceView.getLayoutParams();
params.height=(screenWidth*)/;
params.width=screenWidth;
mSurfaceView.setLayoutParams(params);
top_model2_left_img= (ImageView)findViewById(R.id.top_model2_left_img);
top_model2_left_img.setOnClickListener(this);
top_model2_right_img= (ImageView)findViewById(R.id.top_model2_right_img);
top_model2_right_img.setVisibility(View.INVISIBLE);
top_model2_txt= (TextView)findViewById(R.id.top_model2_txt);
top_model2_txt.setText("視訊錄制");
}
private void set_datas() {
mSurfaceHolder = mSurfaceView.getHolder();
mProgressBar.setMax(mRecordMaxTime);// 設定進度條最大量
mSurfaceHolder.setKeepScreenOn(true);//設定螢幕常亮
mSurfaceHolder.addCallback(new CustomCallBack());
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
circle_camera_time.setText("");
}
private void init_event() {
circle_light_btn.setOnClickListener(this);
circle_change_camera_btn.setOnClickListener(this);
shoot_button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
record(new OnRecordFinishListener() {
@Override
public void onRecordFinish() {
//錄制時間達到最大值
handler.sendEmptyMessage(MOVICE_SUCCESS);
}
});
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (mTimeCount > && mTimeCount<) { //防止達到最大值up事件
//錄制時間大于一秒
handler.sendEmptyMessage(MOVICE_SUCCESS);
} else if(mTimeCount<=) {
//删除小于一秒的視訊
if (getmVecordFile() != null) {
getmVecordFile().delete();
}
handler.sendEmptyMessage(MOVICE_FILE);
}
}
return true;
}
});
}
@Override
protected void onResume() {
super.onResume();
if(mCamera!=null){
freeCameraResource();
}
try {
initCamera();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// mRecorderView.stop();
}
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//停止拍攝
stopRecord();
switch (msg.what){
case MOVICE_SUCCESS:
String msgs = "确認上傳嗎?";
commonDialog = new CommonDialog(CircleViedoActivity.this, new CommonDialog.GoCommonDialog() {
@Override
public void go() {
finishActivity();
commonDialog.dismiss();
}
@Override
public void cancel() {
//删除沒有上傳的視訊
if (getmVecordFile() != null) {
getmVecordFile().delete();
}
commonDialog.dismiss();
}
}, msgs, "确定", "取消");
commonDialog.show();
break;
case MOVICE_FILE:
Toast.makeText(CircleViedoActivity.this, "視訊錄制時間太短", Toast.LENGTH_SHORT).show();
break;
}
}
};
private void finishActivity() {
stop();
Bitmap bitmap = null;
if (getmVecordFile() != null) {
//得到檔案 File類型
File mfile = getmVecordFile();
bitmap = getVideoThumbnail(mfile.toString());
}
----------
部分Android手機縮略圖拉不到,找到一種解決辦法不是太滿意 有沒有大神提供思路
----------
// VideoPlayerActivity.startActivity(this, mRecorderView.getVecordFile().toString());
Intent intent = new Intent();
intent.putExtra("bitmap",bitmap);
setResult(RESULT_OK,intent);
finish();
}
@Override
public void onError(MediaRecorder mr, int what, int extra) {
try {
if (mr != null)
mr.reset();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.circle_light_btn:
//開啟關閉閃光燈 預設關閉
if(isOpenFlash){
isOpenFlash=false;
circle_light_btn.setImageResource(R.drawable.camera_light);
}else {
isOpenFlash=true;
circle_light_btn.setImageResource(R.drawable.camera_no_light);
}
try {
initCamera();
} catch (IOException e) {
e.printStackTrace();
}
break;
case R.id.circle_change_camera_btn:
if(isBackCamera){
isBackCamera=false;
}else {
isBackCamera=true;
}
try {
initCamera();
} catch (IOException e) {
e.printStackTrace();
}
break;
case R.id.top_model2_left_img:
stop();
finish();
break;
}
}
private class CustomCallBack implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!isOpenCamera)
return;
try {
initCamera();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
/**
* 初始化攝像頭
*/
private void initCamera() throws IOException {
if (mCamera != null) {
freeCameraResource();
}
try {
if(isBackCamera){
mCamera = Camera.open(mbackCamera);//打開後攝像頭
setCameraParams(isOpenFlash);
}else {
mCamera=Camera.open(mfrontCamera);//打開前攝像頭
}
} catch (Exception e) {
e.printStackTrace();
freeCameraResource();
}
if (mCamera == null)
return;
mCamera.setDisplayOrientation();
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
mCamera.unlock();
}
/**
* 設定攝像頭為豎屏
*/
private void setCameraParams(Boolean isOpenFlash) {
if (mCamera != null) {
Camera.Parameters params = mCamera.getParameters();
//擷取預覽的各種分辨率
List<Camera.Size> supportedPreviewSizes = params.getSupportedPreviewSizes();
params.set("orientation", "portrait");//豎屏錄制
params.setPreviewSize(mWidth, mHeight);//預設640*480
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//持續對焦
if(isBackCamera){
if(isOpenFlash){
//開啟閃光燈
params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
}else {
//關閉閃光燈
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
}
}else {
}
mCamera.setParameters(params);
}
}
/**
* 釋放攝像頭資源
*/
private void freeCameraResource() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.lock();
mCamera.release();
mCamera = null;
}
}
private void createRecordDir() {
File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator + "ATOMImg/video/");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
File vecordDir = sampleDir;
// 建立檔案
try {
mVecordFile = File.createTempFile("recording", ".mp4", vecordDir);//mp4格式
} catch (IOException e) {
}
}
/**
* 初始化
*/
private void initRecord() throws IOException {
mMediaRecorder = new MediaRecorder();
mMediaRecorder.reset();
if (mCamera != null)
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setOnErrorListener(this);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);// 視訊源
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 音頻源
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 視訊輸出格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);// 音頻格式
mMediaRecorder.setVideoSize(mWidth, mHeight);// 設定分辨率:
// mMediaRecorder.setVideoFrameRate(16);// 這個我把它去掉了,感覺沒什麼用
mMediaRecorder.setVideoEncodingBitRate( * * );// 設定幀頻率,然後就清晰了
if(isBackCamera) {
mMediaRecorder.setOrientationHint();// 輸出旋轉90度,保持豎屏錄制
}else {
mMediaRecorder.setOrientationHint();
}mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);// 視訊錄制格式
----------
/**
這裡是配置攝像頭的重點 因為我們錄制視訊需要适配IOS開發和Android不同版本的手機 而IOS和Android通配的視訊格式不多 我們這裡采用H264格式 它的相容範圍更廣闊 主要解決的就是部分OPPO手機不适配問題
*/
----------
// mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());
mMediaRecorder.prepare();
try {
mMediaRecorder.start();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 錄制完成回調接口
*/
public interface OnRecordFinishListener {
public void onRecordFinish();
}
/**
* 錄制
* @param onRecordFinishListener
*/
public void record(final OnRecordFinishListener onRecordFinishListener) {
this.mOnRecordFinishListener = onRecordFinishListener;
createRecordDir();//建立目錄
try {
if (!isOpenCamera)// 如果未打開攝像頭,則打開
initCamera();//初始化攝像頭
initRecord();//初始化錄制參數
mTimeCount = ;// 時間計數器重新指派
mTimer = new Timer();//建立一個定時器
mTimer.schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
mTimeCount++;
mProgressBar.setProgress(mTimeCount);// 設定進度條
// handler.sendEmptyMessage(TIME_CHANGW);
runOnUiThread(new Runnable() {
@Override
public void run() {
circle_camera_time.setText(mTimeCount + "″");
}
});
if (mTimeCount == mRecordMaxTime) {// 達到指定時間,停止拍攝
//錄制完成調用錄制回調接口
if (mOnRecordFinishListener != null)
mOnRecordFinishListener.onRecordFinish();
}
}
}, , );
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 停止拍攝
*/
public void stop() {
stopRecord();
releaseRecord();
freeCameraResource();
}
/**
* 停止錄制
*/
public void stopRecord() {
mProgressBar.setProgress();
circle_camera_time.setText("");
if (mTimer != null)
mTimer.cancel();
if (mMediaRecorder != null) {
// 設定後不會崩
mMediaRecorder.setOnErrorListener(null);
mMediaRecorder.setPreviewDisplay(null);
try {
mMediaRecorder.stop();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (RuntimeException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 擷取的視訊的位置
* @return
*/
public File getmVecordFile() {
return mVecordFile;
}
/**
* 釋放資源
*/
private void releaseRecord() {
if (mMediaRecorder != null) {
mMediaRecorder.setOnErrorListener(null);
try {
mMediaRecorder.release();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
mMediaRecorder = null;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
stop();
finish();
return false;
}else {
return super.onKeyDown(keyCode, event);
}
}
/**
* 擷取視訊縮略圖
* @param filePath
* @return
*/
public Bitmap getVideoThumbnail(String filePath) {
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(filePath);
bitmap = retriever.getFrameAtTime();
}
catch(IllegalArgumentException e) {
e.printStackTrace();
}
catch (RuntimeException e) {
e.printStackTrace();
}
finally {
try {
retriever.release();
}
catch (RuntimeException e) {
e.printStackTrace();
}
}
return bitmap;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(bitmap!=null){
try {
bitmap.recycle();
} catch (Exception e) {
e.printStackTrace();
}
bitmap=null;
}
}
}