仿微信的录制小视频功能
这里主要重点介绍录制的过程和录制完播放的过程,其次会简单介绍一下录制按钮的实现。
总纲简介
首先我们录制视频需要知道用到几个类 SurfaceView Camera MediaRecorder,这几个类配合实现录制播放流程,下面是简述:
首先我们需要camera获取底层的视频流数据(YUV格式),然后把视频流输出和SurfaceView的句柄进行绑定,这样就可以显示出来预览了(这里要注意预览变形的问题,可以参考我的另一篇文章),然后是MediaRecorder接收预览的视频流数据,最后是吧录制好的数据通过SurfaceView 和MediaPlayer进行播放(此处的SurfaceView 和camera的为同一个,实现录制和播放的无缝的衔接效果)。
下面介绍详细步骤
第一步首先需要实现camera的预览,具体的不多说了,直接上代码了,在我的另外博客有详细介绍。
//1.获取surfaceview的句柄,activity实现接受的回调方法 mHolder = cameraPreview.getHolder(); mHolder.setKeepScreenOn(true); mHolder.addCallback(this); //2.在surfaceCreated开启相机 openCamera(mHolder); //3.在surfaceChanged中进行尺寸的选择 mHolder = holder; //设置相机的一些尺寸 screenWidth = FSScreen2.getScreenWidth(); //包括虚拟按键的高度 screenHeight = FSScreen2.getHasVirtualKey(); Camera.Parameters parameters = mCamera.getParameters(); //获取支持的预览尺寸 List<Camera.Size> sizes = parameters.getSupportedPreviewSizes(); Camera.Size optimalPreviewSize = getOptimalPreviewSize(sizes, screenHeight, screenWidth); parameters.setPreviewSize(optimalPreviewSize.width, optimalPreviewSize.height); cameraPreview.setLayoutParams(new FrameLayout.LayoutParams(optimalPreviewSize.height, optimalPreviewSize.width)); //获取video的尺寸 List<Camera.Size> supportedVideoSizes1 = parameters.getSupportedVideoSizes(); mSize = getOptimalPreviewSize(supportedVideoSizes1, screenHeight, screenWidth); //获取照片的尺寸 sizes = parameters.getSupportedPictureSizes();//支持图片尺寸 optimalPreviewSize = getOptimalPreviewSize(sizes, optimalPreviewSize.width, optimalPreviewSize.height);//比值1.77 parameters.setPictureSize(optimalPreviewSize.width, optimalPreviewSize.height); Log.i(TAG, "surfaceChanged----视频尺寸"); parameters.setRotation(0); mCamera.setParameters(parameters);和设置 //4.在surfaceDestroyed中进行资源的释放工作 releaseMediaRecorder(); releaseCamera();
预览已经没有问题了,下面就是重要的一步,将预览数据和MediaRecorder关联起来,获取到录制的视频资料,这里有一个要注意的地方是,录制视频的大小,这里我们是可以自己调整分辨率,不过要想真正的更接近微信的话,还是要做视频的压缩操作(一般是需要的,后期我会把压缩视频的内容加上),下面上代码:
/** * 开始录制,这段代码来自网络,由于刚开始不能使用,所以我是做了一下修改 */ private void startRecord() { Log.i(TAG, "startRecord----开始录像了"); if (mRecorder == null) { mRecorder = new MediaRecorder(); // 创建MediaRecorder } if (mCamera != null) { mCamera.unlock(); mRecorder.setCamera(mCamera); } try { // 设置音频采集方式 mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); //设置视频的采集方式 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); //设置文件的输出格式 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//aac_adif, aac_adts, output_format_rtp_avp, output_format_mpeg2ts ,webm //设置audio的编码格式 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); //设置video的编码格式 mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); //设置录制的视频编码比特率,下面我会贴出一些自己测试在不同分辨率和格式下产生的视频大小的数据 mRecorder.setVideoEncodingBitRate(2 * 1024 * 1024);// 设置帧频率,然后就清晰了 // CamcorderProfile cProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); // mRecorder.setProfile(cProfile); //设置录制的视频帧率,注意文档的说明: mRecorder.setVideoFrameRate(30); //设置要捕获的视频的宽度和高度,这里msize是在surfaceChanged中进行获取并比较取得合适的尺寸 mHolder.setFixedSize(mSize.width, mSize.height);//最高只能设置640x480 mRecorder.setVideoSize(mSize.width, mSize.height);//最高只能设置640x480 //设置记录会话的最大持续时间(毫秒) mRecorder.setMaxDuration(60 * 1000); // 输出旋转90度,保持竖屏录制 mRecorder.setOrientationHint(90); //注释掉并无影响,猜测是camera设置过显示就可以了 // mRecorder.setPreviewDisplay(mHolder.getSurface()); videoPath = getSDPath(); if (videoPath != null) { File dir = new File(videoPath + "/videos"); if (!dir.exists()) { dir.mkdirs(); } videoPath = dir + "/" + System.currentTimeMillis() + ".mp4"; //设置输出文件的路径 mRecorder.setOutputFile(videoPath); Log.i(TAG, "startRecord----path" + videoPath); //准备录制 mRecorder.prepare(); //开始录制 mRecorder.start(); isRecording = true; } } catch (Exception e) { e.printStackTrace(); } }
上面我们可以自己写分辨率
mRecorder.setVideoEncodingBitRate(2 * 1024 * 1024);// 设置帧频率
也可以用系统里面预制的
CamcorderProfile cProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH); mRecorder.setProfile(cProfile);
下面是一份我自己整理的一份资料:
CamcorderProfile.QUALITY_QVGA //5秒 大小为448k 模糊 CamcorderProfile.QUALITY_QCIF //5秒 大小为370k 模糊 CamcorderProfile.QUALITY_480P//5秒 大小为1.81M 清晰 CamcorderProfile.QUALITY_CIF //未知原因不能用,并且导致相机连接没有断开,相机无法打开 CamcorderProfile.QUALITY_720P //5秒 大小为5.8M 很清晰 CamcorderProfile.QUALITY_TIME_LAPSE_480P //无法使用,导致相机崩溃 CamcorderProfile.QUALITY_HIGH //5秒 大小为8.47 非常清晰
这里可以完成了录制视频,我是仿微信的倒计时来做的录像,一会会简单介绍一下录制的倒计时操作,就是那个长按的按钮:
//我这里是自定义的控件,里面两个回调长按和点击,原来用其他的人的,不过和微信的差的比较远,就自己写了一个。 bnCapture.setOnProgressTouchListener(new LProgressBar.OnProgressTouchListener() { @Override public void onClick(LProgressBar progressBar) { if (!_isCapturing) { return; } _isCapturing = false; focuseView.setVisibility(View.INVISIBLE); //这里我们获取jpeg的回调 try { mCamera.takePicture(null, null, pictureCallback); } catch (RuntimeException e) { e.printStackTrace(); _isCapturing = false; } } @Override public void onLongClick(LProgressBar progressBar) { startRecord(); } @Override public void onLongClickUp(LProgressBar progressBar) { //抬起的话直接取消录制,并切删除录制的文件 stopRecord(); playVideo(mHolder); } });
这里介绍一下停止录制和播放的工作:
private void stopRecord() { Log.i(TAG, "stopRecord----停止录像了"); bnCapture.setVisibility(View.GONE); tv_confirm.setVisibility(View.VISIBLE); try { //停止录制 mRecorder.stop(); //重置 mRecorder.reset(); } catch (Exception e) { e.printStackTrace(); } isRecording = false; }
下面是播放视频的方法:
//播放的代码操作,主要是释放一些资源,holder重新绑定 private void playVideo(final SurfaceHolder mHolder) { releaseMediaRecorder(); releaseCamera(); mHolder.removeCallback(this); mMediaPlayer = new MediaPlayer(); mMediaPlayer.setDisplay(mHolder); mMediaPlayer.getCurrentPosition(); //设置显示视频显示在SurfaceView上 // String path = getSDPath() + "/videos/" + "1493888903914.mp4"; Log.d(TAG, videoPath); try { mMediaPlayer.setDataSource(videoPath); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.prepareAsync(); mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.start(); } }); mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { bnCapture.setClickable(true); } }); } catch (Exception e) { e.printStackTrace(); } }