轉(zhuǎn)自:http://blog.csdn.net/coderyue/article/details/52830564?
github:https://github.com/jinguangyue/Android-CustomCamera
1. 獲取相機實例
/**? ? * 獲取Camera實例
? *? ? *@return
*/
privateCameragetCamera(intid) {? ? ? ?
Camera camera =null;
try{? ? ? ? ? ?
camera = Camera.open(id);? ? ??
}catch(Exception e) {? ? ??
}
returncamera;? ? }
2.? 開啟預(yù)覽
要注意, 開啟預(yù)覽要在 Activity 的 onResume 方法里面開啟, 然后在 onPause 方法里面釋放相機資源, 舉一個簡單的例子, 如果你在預(yù)覽的時候按下了home鍵, 此時再次打開程序, 如果你是在 Oncreate 方法里面開啟相機, 那么再次打開預(yù)覽界面應(yīng)該會卡住。
/**
* 預(yù)覽相機
*/
privatevoidstartPreview(Camera camera, SurfaceHolder holder) {
try{? ? ? ? ??
setupCamera(camera);? ??
camera.setPreviewDisplay(holder);
recorderRotation = CameraUtil.getInstance().getRecorderRotation(mCameraId);? ? ? ? ? ? CameraUtil.getInstance().setCameraDisplayOrientation(this, mCameraId, camera);? ? ? ? ? ? camera.startPreview();? ? ??
}catch(IOException e) {? ? ? ? ??
e.printStackTrace();? ? ?
? }??
}
/**
* 設(shè)置
*/
privatevoidsetupCamera(Camera camera) {
if(camera !=null) {? ? ? ? ? ?
Camera.Parameters parameters = camera.getParameters();? ? ? ? ??
List focusModes = parameters.getSupportedFocusModes();
if(focusModes !=null&& focusModes.size() >0) {
if(focusModes.contains(? ? ? ? ? ? ? ? ? ? ? ? Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);? ? ? ? ? ? ? ? }? ? ? ??
? }? ? ? ?
? ? List videoSiezes =null;if(parameters !=null) {? ? ? ? ? ? ? ??
? ? ? ? ? ? ? videoSiezes = parameters.getSupportedVideoSizes();
for(Camera.Size size : videoSiezes) {? ? ? ?
? ? ? ? }? ? ? ?
? ? }
if(videoSiezes !=null&& videoSiezes.size() >0) {? ? ? ??
? ? ? ? ? ? ? ? ? ? ? Camera.Size videoSize = CameraUtil.getInstance().getPropVideoSize(videoSiezes,720);? ? ?
? ? ? ? ? video_width = videoSize.width;? ??
? ? ? ? ? video_height = videoSize.height;? ? ??
? ? ? ? LogUtils.i("video_width==="+ video_width);? ? ?
? ? ? ? ? LogUtils.i("video_height==="+ video_height);? ? ?
? ? ? }? ? ? ? ? ?
? ? ? ? ? ? Camera.Size previewSize = CameraUtil.getInstance().getPropPreviewSize(parameters.getSupportedPreviewSizes(), video_width);? ??
? ? ? parameters.setPreviewSize(previewSize.width, previewSize.height);? ? ??
? ? Camera.Size pictrueSize = CameraUtil.getInstance().getPropPictureSize(parameters.getSupportedPictureSizes(), video_width);? ? ? ? ? ? parameters.setPictureSize(pictrueSize.width, pictrueSize.height);? ? ? ? ? ? camera.setParameters(parameters);
/**
* 設(shè)置surfaceView的尺寸 因為camera默認是橫屏,所以取得支持尺寸也都是橫屏的尺寸
* 我們在startPreview方法里面把它矯正了過來,但是這里我們設(shè)置設(shè)置surfaceView的尺寸的時候要注意 previewSize.height
* previewSize.width才是surfaceView的高度
* 一般相機都是屏幕的寬度 這里設(shè)置為屏幕寬度 高度自適應(yīng) 你也可以設(shè)置自己想要的大小
*/
FrameLayout.LayoutParams params =newFrameLayout.LayoutParams(screenWidth, (screenWidth * video_width) / video_height);? ? ? ? ?
? ? ? ? ? ? ? surfaceView.setLayoutParams(params);? ??
? ? ? RelativeLayout.LayoutParams layoutParams =newRelativeLayout.LayoutParams(screenWidth, screenheight - screenWidth);? ??
? ? ? layoutParams.addRule(RelativeLayout.BELOW, surfaceView.getId());? ? ? ? ? ? layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);? ? ? ? ? ? bottomLayout.setLayoutParams(layoutParams);??
? ? }?
? }
3. 開始錄制
這里要注意的是 MediaRecorder 的相關(guān)方法的調(diào)用順序時候不能亂的, 詳細可以看官網(wǎng)api說明
接下來開啟錄制:
protected void start() {? ? ? ? try {? ? ? ? ? ? pathName = System.currentTimeMillis() +""http://視頻存儲路徑? ? ? ? ? ? file = new File(MyApplication.getInstance().getTempPath() + File.separator+ pathName + AppConfig.MP4)? ? ? ? ? ? //如果沒有要創(chuàng)建? ? ? ? ? ? BitmapUtils.makeDir(file)? ? ? ? ? ? //初始化一個MediaRecorder? ? ? ? ? ? if (mediaRecorder == null) {? ? ? ? ? ? ? ? mediaRecorder = new MediaRecorder()? ? ? ? ? ? } else {? ? ? ? ? ? ? ? mediaRecorder.reset()? ? ? ? ? ? }? ? ? ? ? ? mCamera.unlock()? ? ? ? ? ? mediaRecorder.setCamera(mCamera)? ? ? ? ? ? //設(shè)置視頻輸出的方向 很多設(shè)備在播放的時候需要設(shè)個參數(shù) 這算是一個文件屬性? ? ? ? ? ? mediaRecorder.setOrientationHint(recorderRotation)? ? ? ? ? ? //視頻源類型? ? ? ? ? ? mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA)? ? ? ? ? ? mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)? ? ? ? ? ? mediaRecorder.setAudioChannels(2)? ? ? ? ? ? // 設(shè)置視頻圖像的錄入源? ? ? ? ? ? // 設(shè)置錄入媒體的輸出格式//? ? ? ? ? ? mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)? ? ? ? ? ? // 設(shè)置音頻的編碼格式//? ? ? ? ? ? mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)? ? ? ? ? ? // 設(shè)置視頻的編碼格式//? ? ? ? ? ? mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)? ? ? ? ? ? if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {? ? ? ? ? ? ? ? profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P)? ? ? ? ? ? }? else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {? ? ? ? ? ? ? ? profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P)? ? ? ? ? ? } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HIGH)) {? ? ? ? ? ? ? ? profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)? ? ? ? ? ? } else if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_LOW)) {? ? ? ? ? ? ? ? profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW)? ? ? ? ? ? }? ? ? ? ? ? if (profile != null) {? ? ? ? ? ? ? ? profile.audioCodec= MediaRecorder.AudioEncoder.AACprofile.audioChannels=1profile.audioSampleRate=16000profile.videoCodec= MediaRecorder.VideoEncoder.H264? ? ? ? ? ? ? ? mediaRecorder.setProfile(profile)? ? ? ? ? ? }? ? ? ? ? ? //視頻尺寸? ? ? ? ? ? mediaRecorder.setVideoSize(video_width, video_height)? ? ? ? ? ? //數(shù)值越大 視頻質(zhì)量越高? ? ? ? ? ? mediaRecorder.setVideoEncodingBitRate(5*1024*1024)? ? ? ? ? ? // 設(shè)置視頻的采樣率,每秒幀數(shù)//? ? ? ? ? ? mediaRecorder.setVideoFrameRate(5)? ? ? ? ? ? // 設(shè)置錄制視頻文件的輸出路徑? ? ? ? ? ? mediaRecorder.setOutputFile(file.getAbsolutePath())? ? ? ? ? ? mediaRecorder.setMaxDuration(2000)? ? ? ? ? ? // 設(shè)置捕獲視頻圖像的預(yù)覽界面? ? ? ? ? ? mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface())? ? ? ? ? ? mediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {? ? ? ? ? ? ? ? @Override? ? ? ? ? ? ? ? public void onError(MediaRecorder mr, int what, int extra) {? ? ? ? ? ? ? ? ? ? // 發(fā)生錯誤,停止錄制? ? ? ? ? ? ? ? ? ? if (mediaRecorder != null) {? ? ? ? ? ? ? ? ? ? ? ? mediaRecorder.stop()? ? ? ? ? ? ? ? ? ? ? ? mediaRecorder.release()? ? ? ? ? ? ? ? ? ? ? ? mediaRecorder = null? ? ? ? ? ? ? ? ? ? ? ? LogUtils.i("Error")? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? })? ? ? ? ? ? mediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {? ? ? ? ? ? ? ? @Override? ? ? ? ? ? ? ? public void onInfo(MediaRecorder mr, int what, int extra) {? ? ? ? ? ? ? ? ? ? //錄制完成? ? ? ? ? ? ? ? }? ? ? ? ? ? })? ? ? ? ? ? // 準備、開始? ? ? ? ? ? mediaRecorder.prepare()? ? ? ? ? ? mediaRecorder.start()? ? ? ? ? ? new Thread(new Runnable() {? ? ? ? ? ? ? ? @Override? ? ? ? ? ? ? ? public void run() {? ? ? ? ? ? ? ? ? ? for (int i =0try {? ? ? ? ? ? ? ? ? ? ? ? ? ? Thread.currentThread().sleep(20)? ? ? ? ? ? ? ? ? ? ? ? ? ? Message message = new Message()? ? ? ? ? ? ? ? ? ? ? ? ? ? message.what=1message.obj= i? ? ? ? ? ? ? ? ? ? ? ? ? ? handler.sendMessage(message)? ? ? ? ? ? ? ? ? ? ? ? } catch (InterruptedException e) {? ? ? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace()? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }).start()? ? ? ? } catch (Exception e) {? ? ? ? ? ? e.printStackTrace()? ? ? ? }? ? }
4. 錄制成功后接下來的重點來了
使用
ffmpeg 對視頻進行裁剪正方形, ffmpeg 使用Shell命令的方式進行視頻操作, 執(zhí)行效率也是非常好, 那首先你要集成FFmpeg
到Android 項目里面, 這里我下載好了一個library, 直接引入到項目即可, 感興趣的伙伴可以自己編譯一個庫,
這里我把命令貼出來解釋一下,
ffmpeg-threads4-y-i/storage/emulated/0/CustomCamera/temp/1476598263062.mp4-metadata:s:v rotate="0"-vftranspose=1, crop=width:height:x:y-presetultrafast-tunezerolatency-r25-vcodeclibx264-acodeccopy /storage/emulated/0/CustomCamera/VIDEO/1476598263062.mp4
1
1
-y: 如果文件存在那么覆蓋掉
-i: 輸入
metadata:s:v rotate=”0” : 重新編碼 除去rotate, 因為默認錄制出來的是橫屏視頻, 這里重新編碼
transpose=1 :
0 = 90CounterCLockwise and Vertical Flip (default) 逆時針旋轉(zhuǎn)90度并且垂直翻轉(zhuǎn), 下面類推
1 = 90Clockwise
2 = 90CounterClockwise
3 = 90Clockwise and Vertical Flip
crop=width:height:x:y,其中 width 和 height 表示裁剪后的尺寸,x:y 表示裁剪區(qū)域的左上角坐標
-preset ultrafast -tune zerolatency: 加快效率
r 25:幀率
-vcodec libx264 -acodec: 編碼方式libx264
如果想知道更多關(guān)于ffmpeg的東西可以訪問官網(wǎng)https://ffmpeg.org/
再貼一下我的代碼調(diào)用:
try{? ? ? ? ? ? ? ? ? ? fc.compress_clipVideo(file.getAbsolutePath(),? ? ? ? ? ? ? ? ? ? ? ? ? ? file2.getAbsolutePath(), mCameraId, video_width, video_height,0,0,newShellUtils.ShellCallback() {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @OverridepublicvoidshellOut(String shellLine) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @OverridepublicvoidprocessComplete(intexitValue) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dialog.dismiss();if(exitValue !=0) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ToastFactory.showLongToast(context, getResources().getString(R.string.state_compress_error));? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mHandler.sendEmptyMessage(R.string.state_compress_error);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mHandler.sendEmptyMessage(R.string.state_compress_end);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? });? ? ? ? ? ? ? ? }catch(Exception e) {? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? }
FFmpeg編譯出來的Android 庫還是很大的, 大約15M左右, 無疑增加了apk的大小, 如果你的產(chǎn)品方向是視頻圖片gif等等的格式轉(zhuǎn)換類似的功能可以考慮使用