转载请注明出处: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. PhpStorm中如何配置SVN,详细操作方法

    1.简介: PhpStorm是一个轻量级且便捷的PHP IDE,其提供的智能代码补全,快速导航以及即时错误检查等功能大大提高了编码效率.它是一款商业的 PHP 集成开发工具,以其独特的开发便利性,短时 ...

  2. 生产环境下JAVA进程高CPU占用故障排查---temp

    问题描述:生产环境下的某台tomcat7服务器,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高. 问题分析:1,程序属于CPU密集型,和开发沟通过, ...

  3. 编写 DockerFile

    编写 DockerFile 本节内容简介 在前面的实验中我们多次用到的 Dockerfile,在本实验里我们将通过完成一个实例来学习Dockerfile的编写. 本节中,我们需要依次完成下面几项任务: ...

  4. js事件触发器fireEvent和dispatchEvent

    转自:https://www.cnblogs.com/tiger95/p/6962059.html 事件触发器就是用来触发某个元素下的某个事件,IE下fireEvent方法,高级浏览器(chrome, ...

  5. WebBrowser内嵌页面的跨域调用问题

    很早之前我写过一篇Blog:网页通过External接口与WebBrowser交互,文中的交互其实只介绍了JS调用C++的部分,而C++调用JS由于微软自己的例子太多,那篇文章就没介绍,不过我最近遇到 ...

  6. 【217】◀▶ IDL 控制语句说明

    参考:Statements Routines —— 控制语句关键字 01   FOR 循环语句. 02   FOREACH 循环语句. 03   WHILE...DO 循环语句. 04   IF... ...

  7. 11.ClientCredential模式总结

    11.ClientCredential模式总结 服务端定义的Resource叫做api Resource API默认是被保护的 第三方的客户端先去请求 Server拿到access token.带着t ...

  8. java 中判断字符串相等

    今天写Java代码时遇到一个问题,就是关于判断两个字符串是否相等的问题.(刚尝试用SSH框架) 在大多编程中,通常比较两个字符串是否相同的表达式是“==”,但在java中不能这么写.在java中,用的 ...

  9. 【转】Visual Studio 选择相同变量高亮

    转:http://www.cnblogs.com/stone_w/p/3613760.html 前段时间一直在使用matlab,今天需要使用vs2008,而用惯了matlab,习惯了其中一项选中变量高 ...

  10. C/C++内存检测工具Valgrind

    内存检测Valgrind简介 Valgrind是运行在Linux上一套基于仿真技术的程序调试和分析工具,作者是获得过Google-O'Reilly开源大奖的Julian Seward, 它包含一个内核 ...