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录制视频,仿微信小视频录制(一)相关推荐

  1. Android录制小视频(仿微信小视频)

    Android录制小视频 一.概述 日常生活中,录制一些视频已经渐渐成为一种习惯,当然这对于我们技术来说并没有什么影响,因为无论大家用不用,你都需要开发,这只是需求制定者–PM应该关心的事情,我们需要 ...

  2. Android 仿微信小视频录制

    Android 仿微信小视频录制 WechatShortVideo和WechatShortVideo文章

  3. Android仿微信小视频录制功能(二)

    Android仿微信小视频录制功能(二) 接着上一篇,在完成了录制功能后,伟大的哲学家沃兹基索德曾经说过:"有录就有放.",那么紧接着就来实现播放功能,按照国际惯例,先上下效果图: ...

  4. 仿微信朋友圈,仿微信小视频 ,录制视频功能

    https://github.com/Naoki2015/VCameraDemo 仿微信小视频    CircleDemo  https://github.com/Naoki2015/CircleDe ...

  5. Android仿微信小视频的简单实现

    一个可播放的网络视频url http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4 由于项目中用到了视频认证功能,所以想到了采用与微信小视频录制类似的界面功 ...

  6. android高仿微信小视频,Android仿微信录制小视频

    本文实例为大家分享了Android仿微信录制小视频的具体代码,供大家参考,具体内容如下 先上张图片看看效果 简单叙述下 首先通过Camera类调用系统相机 通过surfaceview绘制出来 通过Me ...

  7. Android仿微信小视频录制功能

    还没看完,应该还不错,先收藏,觉得可以开拓 https://blog.csdn.net/u012227600/article/details/50835633 -------------------- ...

  8. 抖音 vs 即刻小视频(微信小视频)-竞品对比

    玩了一段时间的抖音,然后最近又有微信推出来'即刻视频',又顺便体验了一段时间的即刻视频,作为一个80后的老阿姨,写点自己的体验.不喜勿喷,哈哈哈 关注点 抖音 即刻视频 入口 抖音APP 微信-我,下 ...

  9. Android之---高仿微信录制小视频(拍摄和查看)

    高仿微信录制小视频(拍摄和查看) Android仿微信小视频录制功能 http://blog.csdn.net/u012227600/article/details/50835633 Android仿 ...

最新文章

  1. python字符串内建函数详解
  2. 2017苏州太湖国际马拉松 半程成绩单
  3. java 抽样_beta分布的采样或抽样(java程序)
  4. Excel数据批量导入到数据库
  5. datagrip 导出数据库备份到本地
  6. python实现一个简单的【图像中物体坐标】标注小工具
  7. 烟雾传感器的matlab程序,单片机烟雾传感器proteus仿真+程序+PCB原理图
  8. visio2013剪除_Visio 2003 图形剪切合并 高级技巧
  9. Ubuntu安装jdk8
  10. 全球知名企业高管预测2019人工智能趋势
  11. 参考文献的序号怎么对齐_word参考文献怎么对齐
  12. 各个排序算法(^_^)
  13. GlusterFS 4.0开发计划解读
  14. 从红牛案看商业伦理和社会公义中的众生相
  15. 如何一次将蓝牙耳机连接到多个设备
  16. “李记餐厅”微信点餐小程序+后台管理系统
  17. 唐山校友会会长苏伟与徐飞校长的一次短信交流
  18. 解码快手新市井电商,新品牌流量多,大品牌政策好
  19. 活非光合细胞中ROS的主要从哪里细胞来源?
  20. 白云机场查获具有充电宝功能的时尚女包

热门文章

  1. SVN设置为中文 最全面
  2. SSM上传,下载,在线播放视频
  3. 学习英语给你带来了哪些机会?
  4. 数智化巩固智慧变电站“防汛墙”
  5. 微信android手机中点击大图片会自动放大图片
  6. 修改数据库密码为无限期,自己看
  7. Cocos2dx客户端崩溃的几种情况
  8. 全国大学生物联网设计竞赛(华为杯)巡回技术讲座“八校联动”,HarmonyOS与校园开发者面对面
  9. VBA 函数计算date之间的天数工作日
  10. 解决H5在微信浏览器中保存联系人问题