前言

  MediaRecorder可以不依靠Camera API 实现视频的录制,但是如果需要切换摄像头/设置对焦/选择分辨率等等就需要Camera来参与配合录制视频.这篇博客将介绍使用Camera1来实现视频录制.此篇博客不在重复一些细节和坑的介绍.如果你刚接触建议你看我另一篇博客https://www.cnblogs.com/guanxinjing/p/10980906.html这篇博客用更简单易懂的形式说明了MediaRecorder录制视频的步骤,并且有大量深坑的详解介绍,防止你也掉坑里.

实现流程

  1.   设置TextureView的硬件加速
  2.   获取权限
  3.   初始化TextureView的监听
  4.   初始化MediaRecorder
  5.   初始化Camera
  6.   初始化配置Camera
  7.   开始相机预览
  8.   配置MediaRecorder(每次录制之前都需要配置一次)
  9.   开始录制
  10.   停止录制
  11.   恢复相机预览

代码部分

  关键点有大量注释,就不做另外篇幅的介绍

  如果你对下面代码里的计算分辨率不甚理解,可以去我另一篇博客有更详细的讲解分辨率的计算,这篇博客虽然是拿Camera2 API来计算分辨率,但是原理是一样的. 传送门:https://www.cnblogs.com/guanxinjing/p/10943966.html

  如果你对Camera自动对焦不太理解,可以去我另一篇更详细的博客有介绍Camera1的自动对焦. 传送门:https://www.cnblogs.com/guanxinjing/p/10986249.html

import androidx.appcompat.app.AppCompatActivity;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.io.IOException;
import java.util.List; public class MedioRecorderCamera1Activity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = MedioRecorderCamera1Activity.class.getSimpleName();
private TextureView mTextureview;
private Button mBtnStart, mBtnFinish;
private MediaRecorder mMediaRecorder;
private Camera mCamera;
private Camera.Size mSelectSize;//记录当前选择的分辨率
private boolean isRecorder = false;//用于判断当前是否在录制视频
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0x01:
mCamera.autoFocus(new Camera.AutoFocusCallback() { //自动对焦
@Override
public void onAutoFocus(boolean success, Camera camera) { }
});
mHandler.sendEmptyMessageDelayed(0x01,2*1000);//2秒之后在对焦一次,一直重复自动对焦
break;
default:
break;
}
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_medio_recorder_camera1);
mTextureview = findViewById(R.id.textureview);
mBtnStart = findViewById(R.id.btn_start);
mBtnFinish = findViewById(R.id.btn_finish);
mBtnStart.setOnClickListener(this);
mBtnFinish.setOnClickListener(this);
initTextureViewListener();
initMediaRecorder(); } /**
* 初始化TextureView监听
*/
private void initTextureViewListener(){
mTextureview.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { //Textureview初始化启用回调
initCamera();
initCameraConfig();
try {
mCamera.setPreviewTexture(surface);
mCamera.startPreview();
mHandler.sendEmptyMessage(0x01);//启动对焦
} catch (IOException e) {
e.printStackTrace();
} } @Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
} @Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
});
} @Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_start:
startRecorder(); break;
case R.id.btn_finish:
stopRecorder(); break;
default:
break;
} } /**
* 初始化MediaRecorder
*/
private void initMediaRecorder(){
mMediaRecorder = new MediaRecorder();
} /**
* 选择摄像头
* @param isFacing true=前摄像头 false=后摄像头
* @return 摄像id
*/
private Integer selectCamera(boolean isFacing){
int cameraCount = Camera.getNumberOfCameras();
// CameraInfo.CAMERA_FACING_BACK 后摄像头
// CameraInfo.CAMERA_FACING_FRONT 前摄像头
int facing = isFacing ? Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK;
Log.e(TAG, "selectCamera: cameraCount="+cameraCount);
if (cameraCount == 0){
Log.e(TAG, "selectCamera: The device does not have a camera ");
return null;
}
Camera.CameraInfo info = new Camera.CameraInfo();
for (int i=0; i < cameraCount; i++){
Camera.getCameraInfo(i,info);
if (info.facing == facing){
return i;
} }
return null; } /**
* 初始化相机
*/
private void initCamera(){
mCamera = Camera.open(selectCamera(false));
mSelectSize = selectPreviewSize(mCamera.getParameters()); } /**
* 初始化相机配置
*/
private void initCameraConfig(){
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);//关闭闪光灯
parameters.setFocusMode(Camera.Parameters.FLASH_MODE_AUTO); //对焦设置为自动
parameters.setPreviewSize(mSelectSize.width,mSelectSize.height);//设置预览尺寸
parameters.setPictureSize(mSelectSize.width,mSelectSize.height);//设置图片尺寸 就拿预览尺寸作为图片尺寸,其实他们基本上是一样的
parameters.set("orientation", "portrait");//相片方向
parameters.set("rotation", 90); //相片镜头角度转90度(默认摄像头是横拍)
mCamera.setParameters(parameters);//添加参数
mCamera.setDisplayOrientation(90);//设置显示方向 } /**
* 计算获取预览尺寸
* @param parameters
* @return
*/
private Camera.Size selectPreviewSize(Camera.Parameters parameters){
List<Camera.Size> previewSizeList = parameters.getSupportedPreviewSizes();
if (previewSizeList.size() == 0){
Log.e(TAG, "selectPreviewSize: previewSizeList size is 0" );
return null; } Camera.Size currentSelectSize = null;
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int deviceWidth =displayMetrics.widthPixels;
int deviceHeight = displayMetrics.heightPixels;
for (int i = 1; i < 41 ; i++){
for(Camera.Size itemSize : previewSizeList){
// Log.e(TAG, "selectPreviewSize: itemSize 宽="+itemSize.width+"高"+itemSize.height);
if (itemSize.height > (deviceWidth - i*5) && itemSize.height < (deviceWidth + i*5)){
if (currentSelectSize != null){ //如果之前已经找到一个匹配的宽度
if (Math.abs(deviceHeight-itemSize.width) < Math.abs(deviceHeight - currentSelectSize.width)){ //求绝对值算出最接近设备高度的尺寸
currentSelectSize = itemSize;
continue;
}
}else {
currentSelectSize = itemSize;
} } }
}
Log.e(TAG, "selectPreviewSize: 当前选择的尺寸 宽="+currentSelectSize.width+"高"+currentSelectSize.height);
return currentSelectSize;
} /**
* 配置MedioRecorder
*/
private void configMedioRecorder(){
File saveRecorderFile = new File(getExternalCacheDir(),"CameraRecorder.mp4");
if (saveRecorderFile.exists()){
saveRecorderFile.delete();
}
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);//设置音频源
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);//设置视频源
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//设置音频输出格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频编码格式
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);//设置视频编码格式
mMediaRecorder.setVideoSize(mSelectSize.width,mSelectSize.height);//设置视频分辨率
mMediaRecorder.setVideoEncodingBitRate(8*1920*1080);//设置视频的比特率
mMediaRecorder.setVideoFrameRate(60);//设置视频的帧率
mMediaRecorder.setOrientationHint(90);//设置视频的角度
mMediaRecorder.setMaxDuration(60*1000);//设置最大录制时间
Surface surface = new Surface(mTextureview.getSurfaceTexture());
mMediaRecorder.setPreviewDisplay(surface);//设置预览
mMediaRecorder.setOutputFile(saveRecorderFile.getAbsolutePath());//设置文件保存路径
mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() { //录制异常监听
@Override
public void onError(MediaRecorder mr, int what, int extra) {
mMediaRecorder.stop();
mMediaRecorder.reset();
try {
mCamera.setPreviewTexture(mTextureview.getSurfaceTexture());
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
} }
}); } /**
* 开启录制视频
*/
private void startRecorder(){
if (!isRecorder) {//如果不在录制视频
mCamera.stopPreview();//暂停相机预览
configMedioRecorder();//再次配置MedioRecorder
try {
mMediaRecorder.prepare();//准备录制
} catch (IOException e) {
e.printStackTrace();
}
mMediaRecorder.start();//开始录制
isRecorder = true;
} } /**
* 停止录制视频
*/
private void stopRecorder(){
if (isRecorder){ //如果在录制视频
mMediaRecorder.stop();//暂停录制
mMediaRecorder.reset();//重置,将MediaRecorder调整为空闲状态
isRecorder = false;
try {
mCamera.setPreviewTexture(mTextureview.getSurfaceTexture());//重新设置预览SurfaceTexture
mCamera.startPreview(); //重新开启相机预览
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) { }
});
} catch (IOException e) {
e.printStackTrace();
}
} } @Override
protected void onDestroy() {
super.onDestroy();
if (mMediaRecorder != null){
if (isRecorder) {
mMediaRecorder.stop();
}
mMediaRecorder.release();
mMediaRecorder = null;
}
if (mCamera != null){
mCamera.stopPreview();
mCamera.release();
mCamera = null; }
}
}

这里说明一下为什么在配置MediaRecorder的时候大量使用DEFAULT 默认模式,是因为如果选定一个模式,并不一定所有机型都是适配的,所有为了安全适配所有机型这里最好选择默认.

        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);//设置音频源
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);//设置视频源
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//设置音频输出格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//设置音频编码格式
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);//设置视频编码格式

Android 开发 MediaRecorder使用Camera1配合录制视频的更多相关文章

  1. Android开发 MediaRecorder使用Camera2配合录制视频(暂时有异常抛出,无法使用)

    前言 这个博客本来是用来详细介绍MediaRecorder与Camera2,但是出乎预料之外,在获取mMediaRecorder.getSurface();的时候无论如何都是报错的,报错为Surfac ...

  2. Android 开发 MediaRecorder视频录制入门

    前言 MediaRecorder是Android SDK提供用于录制音视频,关于音频的录制在我另一篇博客里已经介绍.传送门: https://www.cnblogs.com/guanxinjing/p ...

  3. Android切换前后置摄像头并录制视频

    项目需要对微信的视频模块也看了一下,在此就对这块进行了一个开发.首先给出效果图 首先给出java代码 /** * RecordActivity.java * 版权所有(C) 2013 * 创建:cui ...

  4. Android 开发 MediaRecorder音频录制

    前言 MediaRecorder类是Android sdk提供的一个专门用于音视频录制,一般利用手机麦克风采集音频和摄像头采集图像.这个类是属于简单的音频录制类,录制音频简单容易但是对音频流的控制也比 ...

  5. 【Android】 Android实现录音、播音、录制视频功能

    智能手机操作系统IOS与Android平分天下(PS:WP与其他的直接无视了),而Android的免费招来了一大堆厂商分分向Android示好,故Android可能会有“较好”的前景. Android ...

  6. 【Android Developers Training】 49. 轻松录制视频

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. Android自定义view之仿微信录制视频按钮

    本文章只写了个类似微信的录制视频的按钮,效果图如下:             一.主要的功能: 1.长按显示进度条,单击事件,录制完成回调 2.最大时间和最小时间控制 3.进度条宽度,颜色设置 二.实 ...

  8. Android开发 MediaPlayer入门_播放本地视频

    前言 MediaPlayer,可以播放视频/音频,并且它支持本地和网络文件的播放.本片博客作为入门教程,先以最通俗的方式解释播放文件本地视频.(如果你嫌MediaPlayer还是太麻烦可以试试选择Vi ...

  9. Android开发所有视频教程汇总

    1.Mars的Android开发视频教程作者讲解的很详细,很全面,系统.以前出了两套视频,分别是<Java4Android视频教程>.<Android视频教程>,以及最新刚新出 ...

随机推荐

  1. NX二次开发-UFUN删除链表函数UF_MODL_delete_list

    NX9+VS2012 #include <uf.h> #include <uf_modl.h> #include <uf_obj.h> #include <u ...

  2. NX二次开发-UFUN输入对象tag获得part名字UF_OBJ_ask_owning_part

    NX11+VS2013 #include <uf.h> #include <uf_modl.h> #include <uf_part.h> #include < ...

  3. Delphi常用字符串函数

    Delphi常用字符串函数   一.字符转换函数1.ord(input[i])返回字符表达式 input 左端起第 I 字符的ASCII 码值.2.CHAR()将ASCII 码转换为字符.如果没有输入 ...

  4. NYOJ - 35 表达式求值 分类: NYOJ 2015-03-18 10:33 31人阅读 评论(0) 收藏

    #include<iostream> #include<string> #include<stack> #include<cstdio> using n ...

  5. n-map安装实操

    强烈建议大家从官网下载nmap,而不是其他的第三方.官网地址:https://nmap.org/download.html 打开是这样的,感觉有点阴森森的色调.BTW,谁能逃得过真香定律呢. wind ...

  6. 2019-2020 ICPC, Asia Jakarta Regional Contest (Online Mirror, ICPC Rules, Teams Preferred)

    2019-2020 ICPC, Asia Jakarta Regional Contest (Online Mirror, ICPC Rules, Teams Preferred) easy: ACE ...

  7. vue+nginx配置二级域名

    [1]修改路由文件 [2]修改配置文件 [3]修改本机nginx配置文件 [4]修改服务器nginx配置文件 [5]重启nginx文件,用二级域名访问 http://192.168.199.xxx:7 ...

  8. 现代软件工程HW2:结对编程-生成五则运算式-Core10组 [PB16110698+PB16120162]

    作业具体要求点 这里 Core组要求: 1.Calc() 这个Calc 函数接受字符串的输入(字符串里就是算术表达式,例如 “5*3.5”,“7/8 - 3/8 ”,“3 + 90 * 0.3”等等) ...

  9. Twain协议部分翻译

    转载:https://blog.csdn.net/a848691591/article/details/41006807 4.1 性能 应用程序与源进行性能协商的能力使人们能够控制TWAIN兼容的程序 ...

  10. 【学术篇】SDOI2010 古代猪文

    这里可能包含传送门 又双叒叕数论大杂烩... 定理什么我都不会证 题目很长很啰嗦 但是题意很显然... 化完式子之后就是这么个东东:\(G^{\sum_{k|n}C_k^{\frac{n}{k}}}\ ...