Android录制视频,仿微信小视频录制(一)
Android录制视频,第一部分自定义控件
简述
公司有一个录制视频并上传的功能,录制视频具体使用类如下:硬件控制使用Camera,视频录制的格式音频等具体配置与录制使用MediaRecorder,预览使用SurfaceView。在网上找了一个项目,后来经过自己加工完善,可以比较稳定的使用。内容较多分为几个篇幅来说吧,第一篇先说一下封装的录制控件,第二篇有具体的使用,第三篇讲一下其他一些扩展延伸。
具体实现
自定义了一个控件MovieRecorderView,封装了包括视频的录制、视频的预览、视频的保存、与录制进度监听等功能。具体可以参考代码,注释也是比较详细的。
代码
控件代码部分 MovieRecorderView.java
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.media.MediaRecorder;
import android.media.MediaRecorder.AudioEncoder;
import android.media.MediaRecorder.AudioSource;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OutputFormat;
import android.media.MediaRecorder.VideoEncoder;
import android.media.MediaRecorder.VideoSource;
import android.os.Build;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.widget.LinearLayout;import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;/*** 视频播放控件* Created by Wood on 2016/4/6.*/
public class MovieRecorderView extends LinearLayout implements OnErrorListener {private static final String LOG_TAG = "MovieRecorderView";private Context context;private SurfaceView surfaceView;private SurfaceHolder surfaceHolder;//private ProgressBar progressBar;private MediaRecorder mediaRecorder;private Camera camera;private Timer timer;//计时器private int mWidth;//视频录制分辨率宽度private int mHeight;//视频录制分辨率高度private boolean isOpenCamera;//是否一开始就打开摄像头private int recordMaxTime;//最长拍摄时间private int timeCount;//时间计数private File recordFile = null;//视频文件private long sizePicture = 0;public MovieRecorderView(Context context) {this(context, null);}public MovieRecorderView(Context context, AttributeSet attrs) {this(context, attrs, 0);}@TargetApi(Build.VERSION_CODES.HONEYCOMB)public MovieRecorderView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);this.context = context;TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MovieRecorderView, defStyle, 0);mWidth = a.getInteger(R.styleable.MovieRecorderView_record_width, 640);//默认640mHeight = a.getInteger(R.styleable.MovieRecorderView_record_height, 360);//默认360isOpenCamera = a.getBoolean(R.styleable.MovieRecorderView_is_open_camera, true);//默认打开摄像头recordMaxTime = a.getInteger(R.styleable.MovieRecorderView_record_max_time, 10);//默认最大拍摄时间为10sLayoutInflater.from(context).inflate(R.layout.movie_recorder_view, this);surfaceView = (SurfaceView) findViewById(R.id.surface_view);//TODO 需要用到进度条,打开此处,也可以自己定义自己需要的进度条,提供了拍摄进度的接口//progressBar = (ProgressBar) findViewById(R.id.progress_bar);//progressBar.setMax(recordMaxTime);//设置进度条最大量surfaceHolder = surfaceView.getHolder();surfaceHolder.addCallback(new CustomCallBack());surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);a.recycle();}/*** SurfaceHolder回调*/private class CustomCallBack implements Callback {@Overridepublic void surfaceCreated(SurfaceHolder holder) {if (!isOpenCamera)return;try {initCamera();} catch (IOException e) {e.printStackTrace();}}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {if (!isOpenCamera)return;freeCameraResource();}}/*** 初始化摄像头*/public void initCamera() throws IOException {if (camera != null) {freeCameraResource();}try {if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_FRONT)) {camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);//TODO 默认打开前置摄像头} else if (checkCameraFacing(Camera.CameraInfo.CAMERA_FACING_BACK)) {camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);}} catch (Exception e) {e.printStackTrace();freeCameraResource();((Activity) context).finish();}if (camera == null)return;setCameraParams();camera.setDisplayOrientation(90);camera.setPreviewDisplay(surfaceHolder);camera.startPreview();camera.unlock();}/*** 检查是否有摄像头** @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 setCameraParams() {if (camera != null) {Parameters params = camera.getParameters();params.set("orientation", "portrait");List<Camera.Size> supportedPictureSizes = params.getSupportedPictureSizes();for (Camera.Size size : supportedPictureSizes) {sizePicture = (size.height * size.width) > sizePicture ? size.height * size.width : sizePicture;}
// LogUtil.e(LOG_TAG,"手机支持的最大像素supportedPictureSizes===="+sizePicture);setPreviewSize(params);camera.setParameters(params);}}/*** 根据手机支持的视频分辨率,设置预览尺寸** @param params*/private void setPreviewSize(Parameters params) {if (camera == null) {return;}//获取手机支持的分辨率集合,并以宽度为基准降序排序List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();Collections.sort(previewSizes, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size lhs, Camera.Size rhs) {if (lhs.width > rhs.width) {return -1;} else if (lhs.width == rhs.width) {return 0;} else {return 1;}}});float tmp = 0f;float minDiff = 100f;float ratio = 3.0f / 4.0f;//TODO 高宽比率3:4,且最接近屏幕宽度的分辨率,可以自己选择合适的想要的分辨率Camera.Size best = null;for (Camera.Size s : previewSizes) {tmp = Math.abs(((float) s.height / (float) s.width) - ratio);Log.e(LOG_TAG, "setPreviewSize: width:" + s.width + "...height:" + s.height);
// LogUtil.e(LOG_TAG,"tmp:" + tmp);if (tmp < minDiff) {minDiff = tmp;best = s;}}
// LogUtil.e(LOG_TAG, "BestSize: width:" + best.width + "...height:" + best.height);
// List<int[]> range = params.getSupportedPreviewFpsRange();
// int[] fps = range.get(0);
// LogUtil.e(LOG_TAG,"min="+fps[0]+",max="+fps[1]);
// params.setPreviewFpsRange(3,7);params.setPreviewSize(best.width, best.height);//预览比率// params.setPictureSize(480, 720);//拍照保存比率Log.e(LOG_TAG, "setPreviewSize BestSize: width:" + best.width + "...height:" + best.height);//TODO 大部分手机支持的预览尺寸和录制尺寸是一样的,也有特例,有些手机获取不到,那就把设置录制尺寸放到设置预览的方法里面if (params.getSupportedVideoSizes() == null || params.getSupportedVideoSizes().size() == 0) {mWidth = best.width;mHeight = best.height;} else {setVideoSize(params);}}/*** 根据手机支持的视频分辨率,设置录制尺寸** @param params*/private void setVideoSize(Parameters params) {if (camera == null) {return;}//获取手机支持的分辨率集合,并以宽度为基准降序排序List<Camera.Size> previewSizes = params.getSupportedVideoSizes();Collections.sort(previewSizes, new Comparator<Camera.Size>() {@Overridepublic int compare(Camera.Size lhs, Camera.Size rhs) {if (lhs.width > rhs.width) {return -1;} else if (lhs.width == rhs.width) {return 0;} else {return 1;}}});float tmp = 0f;float minDiff = 100f;float ratio = 3.0f / 4.0f;//高宽比率3:4,且最接近屏幕宽度的分辨率Camera.Size best = null;for (Camera.Size s : previewSizes) {tmp = Math.abs(((float) s.height / (float) s.width) - ratio);Log.e(LOG_TAG, "setVideoSize: width:" + s.width + "...height:" + s.height);if (tmp < minDiff) {minDiff = tmp;best = s;}}Log.e(LOG_TAG, "setVideoSize BestSize: width:" + best.width + "...height:" + best.height);//设置录制尺寸mWidth = best.width;mHeight = best.height;}/*** 释放摄像头资源*/private void freeCameraResource() {try {if (camera != null) {camera.setPreviewCallback(null);camera.stopPreview();camera.lock();camera.release();camera = null;}} catch (Exception e) {e.printStackTrace();} finally {camera = null;}}/*** 创建视频文件*/private void createRecordDir() {File sampleDir = new File(Environment.getExternalStorageDirectory() + File.separator + "SampleVideo/video/");if (!sampleDir.exists()) {sampleDir.mkdirs();}try {//TODO 文件名用的时间戳,可根据需要自己设置,格式也可以选择3gp,在初始化设置里也需要修改recordFile = new File(sampleDir, System.currentTimeMillis() + ".mp4");
// recordFile = new File(sampleDir, System.currentTimeMillis() + ".mp4");
// File.createTempFile(AccountInfo.userId, ".mp4", sampleDir);
// LogUtil.e(LOG_TAG, recordFile.getAbsolutePath());} catch (Exception e) {e.printStackTrace();}}/*** 录制视频初始化*/private void initRecord() throws Exception {mediaRecorder = new MediaRecorder();mediaRecorder.reset();if (camera != null)mediaRecorder.setCamera(camera);mediaRecorder.setOnErrorListener(this);mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());mediaRecorder.setVideoSource(VideoSource.CAMERA);//视频源mediaRecorder.setAudioSource(AudioSource.MIC);//音频源mediaRecorder.setOutputFormat(OutputFormat.MPEG_4);//TODO 视频输出格式 也可设为3gp等其他格式mediaRecorder.setAudioEncoder(AudioEncoder.AMR_NB);//音频格式mediaRecorder.setVideoSize(mWidth, mHeight);//设置分辨率
// mediaRecorder.setVideoFrameRate(25);//TODO 设置每秒帧数 这个设置有可能会出问题,有的手机不支持这种帧率就会录制失败,这里使用默认的帧率,当然视频的大小肯定会受影响
// LogUtil.e(LOG_TAG,"手机支持的最大像素supportedPictureSizes===="+sizePicture);if (sizePicture < 3000000) {//这里设置可以调整清晰度mediaRecorder.setVideoEncodingBitRate(3 * 1024 * 512);} else if (sizePicture <= 5000000) {mediaRecorder.setVideoEncodingBitRate(2 * 1024 * 512);} else {mediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512);}mediaRecorder.setOrientationHint(270);//输出旋转90度,保持竖屏录制mediaRecorder.setVideoEncoder(VideoEncoder.H264);//视频录制格式//mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);mediaRecorder.setOutputFile(recordFile.getAbsolutePath());mediaRecorder.prepare();mediaRecorder.start();}/*** 开始录制视频** @param onRecordFinishListener 达到指定时间之后回调接口*/public void record(final OnRecordFinishListener onRecordFinishListener) {this.onRecordFinishListener = onRecordFinishListener;createRecordDir();try {//如果未打开摄像头,则打开if (!isOpenCamera)initCamera();initRecord();timeCount = 0;//时间计数器重新赋值timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {timeCount++;//progressBar.setProgress(timeCount);//设置进度条if (onRecordProgressListener != null) {onRecordProgressListener.onProgressChanged(recordMaxTime, timeCount);}//达到指定时间,停止拍摄if (timeCount == recordMaxTime) {stop();if (MovieRecorderView.this.onRecordFinishListener != null)MovieRecorderView.this.onRecordFinishListener.onRecordFinish();}}}, 0, 1000);} catch (Exception e) {e.printStackTrace();if (mediaRecorder != null) {mediaRecorder.release();}freeCameraResource();}}/*** 停止拍摄*/public void stop() {stopRecord();releaseRecord();freeCameraResource();}/*** 停止录制*/public void stopRecord() {//progressBar.setProgress(0);if (timer != null)timer.cancel();if (mediaRecorder != null) {mediaRecorder.setOnErrorListener(null);//设置后防止崩溃mediaRecorder.setPreviewDisplay(null);try {mediaRecorder.stop();} catch (Exception e) {e.printStackTrace();}}}/*** 释放资源*/private void releaseRecord() {if (mediaRecorder != null) {mediaRecorder.setOnErrorListener(null);try {mediaRecorder.release();} catch (Exception e) {e.printStackTrace();}}mediaRecorder = null;}/*** 获取当前录像时间** @return timeCount*/public int getTimeCount() {return timeCount;}/*** 设置最大录像时间** @param recordMaxTime*/public void setRecordMaxTime(int recordMaxTime) {this.recordMaxTime = recordMaxTime;}/*** 返回录像文件** @return recordFile*/public File getRecordFile() {return recordFile;}/*** 录制完成监听*/private OnRecordFinishListener onRecordFinishListener;/*** 录制完成接口*/public interface OnRecordFinishListener {void onRecordFinish();}/*** 录制进度监听*/private OnRecordProgressListener onRecordProgressListener;/*** 设置录制进度监听** @param onRecordProgressListener*/public void setOnRecordProgressListener(OnRecordProgressListener onRecordProgressListener) {this.onRecordProgressListener = onRecordProgressListener;}/*** 录制进度接口*/public interface OnRecordProgressListener {/*** 进度变化** @param maxTime 最大时间,单位秒* @param currentTime 当前进度*/void onProgressChanged(int maxTime, int currentTime);}@Overridepublic void onError(MediaRecorder mr, int what, int extra) {try {if (mr != null)mr.reset();} catch (Exception e) {e.printStackTrace();}}
}
xml布局部分 movie_recorder_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/background_dark"android:orientation="vertical"><SurfaceView
android:id="@+id/surface_view"android:layout_width="fill_parent"android:layout_height="0dp"android:layout_weight="1" /><!--下方提供进度条,已经隐藏,需要可以打开。Used to display progress,hidden by default --><ProgressBar
android:id="@+id/progress_bar"style="?android:attr/progressBarStyleHorizontal"android:layout_width="match_parent"android:layout_height="2dp"android:visibility="gone" /></LinearLayout>
其他资源链接
Android录制视频,仿微信小视频录制(二)
Android录制视频,仿微信小视频录制(三)
项目Demo地址
Android录制视频,仿微信小视频录制(一)相关推荐
- Android录制小视频(仿微信小视频)
Android录制小视频 一.概述 日常生活中,录制一些视频已经渐渐成为一种习惯,当然这对于我们技术来说并没有什么影响,因为无论大家用不用,你都需要开发,这只是需求制定者–PM应该关心的事情,我们需要 ...
- Android 仿微信小视频录制
Android 仿微信小视频录制 WechatShortVideo和WechatShortVideo文章
- Android仿微信小视频录制功能(二)
Android仿微信小视频录制功能(二) 接着上一篇,在完成了录制功能后,伟大的哲学家沃兹基索德曾经说过:"有录就有放.",那么紧接着就来实现播放功能,按照国际惯例,先上下效果图: ...
- 仿微信朋友圈,仿微信小视频 ,录制视频功能
https://github.com/Naoki2015/VCameraDemo 仿微信小视频 CircleDemo https://github.com/Naoki2015/CircleDe ...
- Android仿微信小视频的简单实现
一个可播放的网络视频url http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4 由于项目中用到了视频认证功能,所以想到了采用与微信小视频录制类似的界面功 ...
- android高仿微信小视频,Android仿微信录制小视频
本文实例为大家分享了Android仿微信录制小视频的具体代码,供大家参考,具体内容如下 先上张图片看看效果 简单叙述下 首先通过Camera类调用系统相机 通过surfaceview绘制出来 通过Me ...
- Android仿微信小视频录制功能
还没看完,应该还不错,先收藏,觉得可以开拓 https://blog.csdn.net/u012227600/article/details/50835633 -------------------- ...
- 抖音 vs 即刻小视频(微信小视频)-竞品对比
玩了一段时间的抖音,然后最近又有微信推出来'即刻视频',又顺便体验了一段时间的即刻视频,作为一个80后的老阿姨,写点自己的体验.不喜勿喷,哈哈哈 关注点 抖音 即刻视频 入口 抖音APP 微信-我,下 ...
- Android之---高仿微信录制小视频(拍摄和查看)
高仿微信录制小视频(拍摄和查看) Android仿微信小视频录制功能 http://blog.csdn.net/u012227600/article/details/50835633 Android仿 ...
最新文章
- python字符串内建函数详解
- 2017苏州太湖国际马拉松 半程成绩单
- java 抽样_beta分布的采样或抽样(java程序)
- Excel数据批量导入到数据库
- datagrip 导出数据库备份到本地
- python实现一个简单的【图像中物体坐标】标注小工具
- 烟雾传感器的matlab程序,单片机烟雾传感器proteus仿真+程序+PCB原理图
- visio2013剪除_Visio 2003 图形剪切合并 高级技巧
- Ubuntu安装jdk8
- 全球知名企业高管预测2019人工智能趋势
- 参考文献的序号怎么对齐_word参考文献怎么对齐
- 各个排序算法(^_^)
- GlusterFS 4.0开发计划解读
- 从红牛案看商业伦理和社会公义中的众生相
- 如何一次将蓝牙耳机连接到多个设备
- “李记餐厅”微信点餐小程序+后台管理系统
- 唐山校友会会长苏伟与徐飞校长的一次短信交流
- 解码快手新市井电商,新品牌流量多,大品牌政策好
- 活非光合细胞中ROS的主要从哪里细胞来源?
- 白云机场查获具有充电宝功能的时尚女包