转载请注明出处:http://blog.csdn.net/woshizisezise/article/details/51878566

这两天产品经理向我丢来一个新需求,需要在项目里添加一个视频录制的功能,正好是我没做过的,于是研究了一番。在网上搜索了一些案例,但是都是不完整的,要不就是分辨率有问题的,要不就是声音有问题的,要不就是实现了视频录制但是没有播放功能的,所以我就想自己做一个,整合一下,来个较完整版的。

PM的要求如下:实现录像功能,录完后可以预览播放,视频清晰并且大小不能大,支持删除视频功能……

好吧,开始干活了,首先来分析一下原理,现在安卓手机实现录像的功能无非就两种方式,第一是实用系统自带的照相机/摄像机进行录制,然后通过回调的方式将源返回,例如:

Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
//设置视频录制的最长时间
intent.putExtra (MediaStore.EXTRA_DURATION_LIMIT,);
//设置视频录制的画质
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, );
startActivityForResult (intent, VIDEO_WITH_CAMERA);

回调如下所示:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
try{
if (resultCode == Activity.RESULT_OK && requestCode == VIDEO_WITH_CAMERA){
Uri uri = data.getData();
Log.e(TAG, "onActivityResult: " + uri.toString());
}
}catch (Exception e){
e.printStackTrace();
}
}

这种方式是直接调用手机的摄像功能,所以就和你打开相机摄像是一模一样的,但是这样就产生问题了,现在的手机摄像头像素越来越高,拍摄效果越来越清晰,很多都达到了720p甚至是1080p,这样短暂的10s时长内存占用就达到了20M,显然这样是不可能的,并且intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);这行代码,在设置EXTRA_VIDEO_QUALITY为1的情况下,视频格式保存为mp4,然而无论怎么修改EXTRA_VIDEO_QUALITY为0.几的时候,视频保存格式为3gp,并且视频录像效果很差,所以后来我放弃了这种方式而改用第二种方式。

第二种方法就是利用安卓自带的MediaRecorder来录制视频,并制定视频保存路径,并且可以通过Camera来播放录制的视频,下面我们来具体讲解一下这种实现的方式。

  • 首先来看一下效果图吧,很粗糙的

布局很简单,一个开始录制/停止录制按钮,一个播放按钮,一个录制时间计数器,布局文件代码如下:

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"> <SurfaceView
android:id="@+id/surfaceview"
android:layout_width="match_parent"
android:layout_marginBottom="60dp"
android:layout_height="match_parent" /> <ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="60dp"
android:src="@drawable/ic_launcher"/> <Button
android:id="@+id/btnStartStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:text="Start"/> <Button
android:id="@+id/btnPlayVideo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toRightOf="@id/btnStartStop"
android:text="Play"
android:layout_marginLeft="20dp"/> <TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text=""
android:layout_alignParentBottom="true"
android:layout_marginBottom="12dp"
android:layout_marginLeft="20dp"/> </RelativeLayout>

下面是主要的activity界面代码,控制MediaRecorder工作的逻辑,代码如下:

  • MainActivity.java
package com.example.mediarecorder;

import android.app.Activity;
import android.hardware.Camera;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Environment;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView; import java.io.File;
import java.util.Calendar; public class MainActivity extends Activity implements SurfaceHolder.Callback { private static final String TAG = "MainActivity";
private SurfaceView mSurfaceview;
private Button mBtnStartStop;
private Button mBtnPlay;
private boolean mStartedFlg = false;//是否正在录像
private boolean mIsPlay = false;//是否正在播放录像
private MediaRecorder mRecorder;
private SurfaceHolder mSurfaceHolder;
private ImageView mImageView;
private Camera camera;
private MediaPlayer mediaPlayer;
private String path;
private TextView textView;
private int text = ; private android.os.Handler handler = new android.os.Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
text++;
textView.setText(text+"");
handler.postDelayed(this,);
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main); mSurfaceview = (SurfaceView) findViewById(R.id.surfaceview);
mImageView = (ImageView) findViewById(R.id.imageview);
mBtnStartStop = (Button) findViewById(R.id.btnStartStop);
mBtnPlay = (Button) findViewById(R.id.btnPlayVideo);
textView = (TextView)findViewById(R.id.text);
mBtnStartStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mIsPlay) {
if (mediaPlayer != null) {
mIsPlay = false;
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}
}
if (!mStartedFlg) {
handler.postDelayed(runnable,);
mImageView.setVisibility(View.GONE);
if (mRecorder == null) {
mRecorder = new MediaRecorder();
} camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
if (camera != null) {
camera.setDisplayOrientation();
camera.unlock();
mRecorder.setCamera(camera);
} try {
// 这两项需要放在setOutputFormat之前
mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // Set output file format
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 这两项需要放在setOutputFormat之后
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); mRecorder.setVideoSize(, );
mRecorder.setVideoFrameRate();
mRecorder.setVideoEncodingBitRate( * * );
mRecorder.setOrientationHint();
//设置记录会话的最大持续时间(毫秒)
mRecorder.setMaxDuration( * );
mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); path = getSDPath();
if (path != null) {
File dir = new File(path + "/recordtest");
if (!dir.exists()) {
dir.mkdir();
}
path = dir + "/" + getDate() + ".mp4";
mRecorder.setOutputFile(path);
mRecorder.prepare();
mRecorder.start();
mStartedFlg = true;
mBtnStartStop.setText("Stop");
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
//stop
if (mStartedFlg) {
try {
handler.removeCallbacks(runnable);
mRecorder.stop();
mRecorder.reset();
mRecorder.release();
mRecorder = null;
mBtnStartStop.setText("Start");
if (camera != null) {
camera.release();
camera = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
mStartedFlg = false;
}
}
}); mBtnPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mIsPlay = true;
mImageView.setVisibility(View.GONE);
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
mediaPlayer.reset();
Uri uri = Uri.parse(path);
mediaPlayer = MediaPlayer.create(MainActivity.this, uri);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDisplay(mSurfaceHolder);
try{
mediaPlayer.prepare();
}catch (Exception e){
e.printStackTrace();
}
mediaPlayer.start();
}
}); SurfaceHolder holder = mSurfaceview.getHolder();
holder.addCallback(this);
// setType必须设置,要不出错.
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
} @Override
protected void onResume() {
super.onResume();
if (!mStartedFlg) {
mImageView.setVisibility(View.VISIBLE);
}
} /**
* 获取系统时间
*
* @return
*/
public static String getDate() {
Calendar ca = Calendar.getInstance();
int year = ca.get(Calendar.YEAR); // 获取年份
int month = ca.get(Calendar.MONTH); // 获取月份
int day = ca.get(Calendar.DATE); // 获取日
int minute = ca.get(Calendar.MINUTE); // 分
int hour = ca.get(Calendar.HOUR); // 小时
int second = ca.get(Calendar.SECOND); // 秒 String date = "" + year + (month + ) + day + hour + minute + second;
Log.d(TAG, "date:" + date); return date;
} /**
* 获取SD path
*
* @return
*/
public String getSDPath() {
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState()
.equals(android.os.Environment.MEDIA_MOUNTED); // 判断sd卡是否存在
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();// 获取跟目录
return sdDir.toString();
} return null;
} @Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mSurfaceHolder = surfaceHolder;
} @Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
// 将holder,这个holder为开始在onCreate里面取得的holder,将它赋给mSurfaceHolder
mSurfaceHolder = surfaceHolder;
} @Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mSurfaceview = null;
mSurfaceHolder = null;
handler.removeCallbacks(runnable);
if (mRecorder != null) {
mRecorder.release();
mRecorder = null;
Log.d(TAG, "surfaceDestroyed release mRecorder");
}
if (camera != null) {
camera.release();
camera = null;
}
if (mediaPlayer != null){
mediaPlayer.release();
mediaPlayer = null;
}
}
}

同时,别忘了在AndroidManifest.xml文件中添加相应的权限:

<!--硬件支持-->
<uses-feature android:name="android.hardware.camera"/>
<uses-feature android:name="android.hardware.camera.autofocus"/>
<uses-permission android:name="android.permission.CAMERA" >
</uses-permission>
<uses-permission android:name="android.permission.RECORD_AUDIO" >
</uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
</uses-permission>

代码里我觉得有几个地方需要注意一下:

1.视频质量的问题

mRecorder.setVideoSize(, );
mRecorder.setVideoFrameRate();
mRecorder.setVideoEncodingBitRate( * * );
  • mRecorder.setVideoSize(640, 480);

这是设置视频的分辨率,在手机上看不出什么区别,可能在大屏幕上投影或者电脑上观看的时候就有差距了。

  • mRecorder.setVideoFrameRate(30);

这是设置视频录制的帧率,即1秒钟30帧。

  • mRecorder.setVideoEncodingBitRate(3 * 1024 * 1024);

这个属性很重要,这个也直接影响到视频录制的大小,这个设置的越大,视频越清晰,我做了简单的比较,可以参考下表: 

所以大家可以根据自己的实际情况具体选择设置了。

2.视频录制时预览界面角度问题

camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
if (camera != null) {
camera.setDisplayOrientation();
camera.unlock();
mRecorder.setCamera(camera);
}

这个是在开发过程中实际遇到的问题,我网上找了一下,刚开始以为是哪个参数没有设置导致的,但是看网友也有类似的反应,明明是竖屏录制的,但是界面确实横屏,在给mediarecorder设置mRecorder.setOrientationHint(90);也无济于事,后来,必须得设置Camera的预览角度才行,也就是这行代码:camera.setDisplayOrientation(90);这样再进行测试,摄像机的预览角度终于是竖屏的了,并且保存的文件播放时也是竖屏的。


最后,一个简单的视频录制及播放的案例写完了,测试没有其他的问题,完成的功能也比较完整,如有不当之处,欢迎大家留言。

案例下载

Android使用MediaRecorder和Camera实现视频录制及播放功能整理的更多相关文章

  1. 【Android】20.3 拍照和视频录制

    分类:C#.Android.VS2015: 创建日期:2016-03-13 一.简介 Android提供的Camera有两个典型的版本,一个是在Android 5.0以前提供的,称为Camera:另一 ...

  2. Android 中使用MediaRecorder进行录像详解(视频录制)

    在这里给出自己的一个测试DEMO,里面注释很详细.简单的视频录制功能. package com.video; import java.io.IOException; import android.ap ...

  3. Android实现视频录制

    安卓实现视频录制,有两种方法,一种是调用自带的视频功能,一种是使用MediaRecorder. 每种方法都有自己的优缺点.接下来,把两种方法的代码写出来. 先说第一种方法,也是最简单的方法,那就是直接 ...

  4. Android视频录制

    public class MainActivity extends Activity { private MediaRecorder videoRecorder=null; private Butto ...

  5. 【转】开源视频录制库LandscapeVideoCamera

    非常强大的android 视频录制库,可以选择视频尺寸以及视频质量,只允许横屏录制. 使用Android自带的Camera应用可以录制视频,只需发送MediaStore.ACTION_VIDEO_CA ...

  6. 错误:Camera录制视频(6.0错误),5.1正常,7.1正常 (java.lang.RuntimeException: start failed.at android.media.MediaRecorder.native_start(Native Method))

    Process: com.example.mycamera2, PID: 24086 java.lang.RuntimeException: start failed. at android.medi ...

  7. Android音视频之MediaRecorder音视频录制

    前言: 公司产品有很多地方都需要上传音频视频,今天抽空总结一下音频视频的录制.学习的主角是MediaRecorder类. MediaRecorder类介绍: MediaRecorder类是Androi ...

  8. Android多媒体录制--MediaRecorder视频录制

    Android使用MediaRecorder类进行视频的录制. 需要注意,使用MediaRecorder 录音录像 的设置代码步骤一定要按照API指定的顺序来设置,否则报错 步骤为: 1.设置视频源, ...

  9. Android 开发 MediaRecorder使用Camera1配合录制视频

    前言 MediaRecorder可以不依靠Camera API 实现视频的录制,但是如果需要切换摄像头/设置对焦/选择分辨率等等就需要Camera来参与配合录制视频.这篇博客将介绍使用Camera1来 ...

随机推荐

  1. PHP/Javascript 数组定义 及JSON中的使用 ---OK

    PHP数组定义 一维数组: 1.$a=array(1,2,4,5,6); 2.$a= Array("cec"=>"cecValue","logo ...

  2. PBE加密算法

    这是我参加全国信息安全大赛的设计的加密系统中的一个加密算法,虽然比赛的结果不是非常理想但是,我还是学到了很多东西,现在和大家分享一下,比赛收获的东西. 基于口令加密 PBE(Password Base ...

  3. CPU、内存、硬盘分区的检测.py

    cpu_mem_directories.py   CPU.内存.硬盘分区的检测 #!/usr/bin/env python #coding:utf-8 import psutil import tim ...

  4. 1.Win7中判断当前端口是否被占用

    以Win7为例,可以用如下方法找出某个端口是否被其他进程占用:netstat -aon|findstr "8081" 发现8081端口被PID为5900的进程占用, tasklis ...

  5. 通过上一节部署出来的 Windows instance 有时候会发现操作系统时间总是慢 8 个小时,即使手工调整好时间和时区,下次 instance 重启后又会差 8 个小时

    这是 OpenStack 实施经验分享系列的第 3 篇. 问题描述 通过上一节部署出来的 Windows instance 有时候会发现操作系统时间总是慢 8 个小时,即使手工调整好时间和时区,下次 ...

  6. Windows 下有什么软件能够极大地提高工作效率

    Windows 下有什么软件能够极大地提高工作效率?修改 可以推荐一些好的应用或者有趣的程序,能提升工作效率或者能让人眼前一亮的.修改 举报1 条评论 分享 • 邀请回答   按票数排序按时间排序 2 ...

  7. mongodb和mysql语法对比

    MySQL: SELECT * FROM user Mongo: db.user.find() —————————————— MySQl: SELECT * FROM user WHERE name ...

  8. g2o扩展,然后重新编译生成新库。

    orb作者有g2o扩展,g2o原作者也有g2o扩展,等各项基本功扎实以后,考虑把他们整合在一起,再加上高博扩展的g2o,统一cmake,make,然后能make install 正常使用,就最好了.

  9. 利用python数据分析panda学习笔记之DataFrame

    2 DataFrame a:通过传入一个等长的列表构成DataFrame 自动加上索引 data={'state':['ohio','ohio','ohio','Nevada','Nevada'], ...

  10. Flutter实战视频-移动电商-19.首页_火爆专区界面布局编写

    19.首页_火爆专区界面布局编写 看一下图片的效果 一个标题栏,下面是多行两列.里面可以用column布局,外面用Warp流式布局 有得小伙伴说这里可以用网格布局,网格布局的话还是有一定的效率问题.这 ...