您的位置:首页 > 移动开发 > Android开发

Android自定义录像(一)之录像功能实现(附demo源码)

2016-08-06 16:54 645 查看

引言

最近在做一个项目,是有关用手机摄像头做图像实时识别的。所以里面需要自定义一个录像功能。该demo实现了录像录像后文件的保存查看,录像会实时自动对焦(AutoFocus)。根据功能分两篇讲述。这第一篇讲述录像基本实现思路和需要注意的点。后面附有github的demo源码下载链接。

思路

*Android实现录像主要依靠MediaRecorder和SurfaceView这两个类。另外,因为需要对摄像头参数做一些设定,所以也需要Camera类。它们的作用分别是:MediaRecorder通过控制录像音视频源和输出编码等;surfaceview则是作为View的存在提供用户界面,在surfaceview的不同生命周期实现不同的操作;camera类则用于对摄像头参数做一些设定,再调用MediaRecorder的
setCamera()
方法将camera对象带入。

代码

1.布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:baselineAligned="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<SurfaceView
android:id="@+id/capture_surfaceview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:baselineAligned="false">
<RelativeLayout
android:layout_gravity="left"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
android:layout_weight="5.0">
<TextView
android:textSize="15.0sp"
android:textColor="@color/red_overlay"
android:id="@+id/capture_textview_information"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10.0dip"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true" />
</RelativeLayout>
<RelativeLayout
android:layout_gravity="right"
android:background="@color/white_overlay"
android:padding="20.0dip"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
android:layout_weight="1.0"
android:alpha="0.3">

<ImageButton
android:id="@+id/capture_imagebutton_setting"
android:tag="setting"
android:background="@drawable/settings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:contentDescription="@string/imagedescription" />
<ImageButton
android:id="@+id/ib_stop"
android:tag="start"
android:background="@drawable/rec_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:contentDescription="@string/imagedescription" />

4000
<ImageButton
android:id="@+id/capture_imagebutton_showfiles"
android:tag="showfiles"
android:background="@drawable/folder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:contentDescription="@string/imagedescription" />
</RelativeLayout>
</LinearLayout>
</FrameLayout>


2.布局截图:



3.核心代码及注释:

package com.alanjet.videorecordertest;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.Toast;

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 ImageButton mBtnStartStop;
private ImageButton mBtnSet;
private ImageButton mBtnShowFile;
private boolean mStartedFlg = false;
private MediaRecorder mRecorder;
private SurfaceHolder mSurfaceHolder;
private Camera myCamera;
private Camera.Parameters myParameters;
private Camera.AutoFocusCallback mAutoFocusCallback=null;
private boolean isView = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//重写AutoFocusCallback接口
mAutoFocusCallback=new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if(success){
Log.i(TAG, "AutoFocus: success...");
}else {
Log.i(TAG, "AutoFocus: failure...");
}
}
};

initScreen();
setContentView(R.layout.activity_main);
mSurfaceview  = (SurfaceView)findViewById(R.id.capture_surfaceview);
mBtnStartStop = (ImageButton) findViewById(R.id.ib_stop);
mBtnSet= (ImageButton) findViewById(R.id.capture_imagebutton_setting);
mBtnShowFile= (ImageButton) findViewById(R.id.capture_imagebutton_showfiles);
mBtnShowFile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MainActivity.this,ShowVideoActivity.class);
startActivity(intent);
finish();
}
});
mBtnSet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"相机设置待开发~",Toast.LENGTH_SHORT).show();
}
});

SurfaceHolder holder = mSurfaceview.getHolder();// 取得holder

holder.addCallback(this); // holder加入回调接口

holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

}

/**
* 获取系统时间,保存文件以系统时间戳命名
*/
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 + 1 )+ day + hour + minute + second;
Log.d(TAG, "date:" + date);

return date;
}

/**
* 获取SD path
*/
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;
}

//初始化屏幕设置
public void initScreen(){
requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉标题栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏

// 设置横屏显示
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

// 选择支持半透明模式,在有surfaceview的activity中使用。
getWindow().setFormat(PixelFormat.TRANSLUCENT);
}

//初始化Camera设置
public void initCamera()
{
if(myCamera == null && !isView)
{
myCamera = Camera.open();
Log.i(TAG, "camera.open");
}
if(myCamera != null && !isView) {
try {
myParameters = myCamera.getParameters();
myParameters.setPreviewSize(1920, 1080);
myParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
myCamera.setParameters(myParameters);
myCamera.setPreviewDisplay(mSurfaceHolder);
myCamera.startPreview();
isView = true;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
Toast.makeText(MainActivity.this, "初始化相机错误",
Toast.LENGTH_SHORT).show();
}
}
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
mSurfaceHolder = holder;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
mSurfaceHolder = holder;
initCamera();
mBtnStartStop.setOnClickListener(new View.OnClickListener() {

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onClick(View v) {
// TODO Auto-generated method stub

if (!mStartedFlg) {
// Start
if (mRecorder == null) {
mRecorder = new MediaRecorder(); // Create MediaRecorder
}
try {
myCamera.unlock();
mRecorder.setCamera(myCamera);
// Set audio and video source and encoder
// 这两项需要放在setOutputFormat之前
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_720P));
mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());

// Set output file path
String path = getSDPath();
if (path != null) {

File dir = new File(path + "/VideoRecorderTest");
if (!dir.exists()) {
dir.mkdir();
}
path = dir + "/" + getDate() + ".mp4";
mRecorder.setOutputFile(path);
Log.d(TAG, "bf mRecorder.prepare()");
mRecorder.prepare();
Log.d(TAG, "af mRecorder.prepare()");
Log.d(TAG, "bf mRecorder.start()");
mRecorder.start();   // Recording is now started
Log.d(TAG, "af mRecorder.start()");
mStartedFlg = true;
mBtnStartStop.setBackground(getDrawable(R.drawable.rec_stop));
}
} catch (Exception e) {
e.printStackTrace();
}

b3c0
} else {
// stop
if (mStartedFlg) {
try {
mRecorder.stop();
mRecorder.reset();
mBtnStartStop.setBackground(getDrawable(R.drawable.rec_start));
} catch (Exception e) {
e.printStackTrace();
}
}
mStartedFlg = false;
}
}

});
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
// surfaceDestroyed的时候同时对象设置为null
mSurfaceview = null;
mSurfaceHolder = null;
if (mRecorder != null) {
mRecorder.release();
mRecorder = null;
}
}

@Override
protected void onRestart() {
super.onRestart();
}

@Override
protected void onResume() {
super.onResume();
}

@Override
protected void onStart() {
super.onStart();
}
}


4.manifest权限配置:

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />


5.注意点:

Surfaceview主要是通过
SurfaceHolder holder = mSurfaceview.getHolder();
中的SurfaceHolder来做控制的,SurfaceHolder加入回调接口
holder.addCallback(this)
来重写接口中的三个生命周期方法:
public void surfaceChanged(), public void surfaceCreated(), public void surfaceDestroyed().


实现自动对焦的关键在于重写
Camera.AutoFocusCallback()
接口。并且需要将camera的parameters设置对焦模式为
myParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)


6.运行截图:



7.demo源代码下载链接:

https://github.com/alanjet/VideoRecorderTest.git

后言

还有一篇博客《Android自定义录像(二)之录像文件保存查看实现(附demo源码)》将在近期推出!

在实践中求真知,在技术中寻快乐。这是自己在做项目中的一点小积累,希望能帮到有需要的Android开发小伙伴。这也是自己第一次写csdn博客。可能表达得并不是很到位,一些地方写的也比较稚嫩,欢迎大家在评论中多指点多交流,在技术之路上不断前进。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐