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

来源:互联网 发布:下单软件 编辑:程序博客网 时间:2024/04/28 08:35

引言

最近在做一个项目,是有关用手机摄像头做图像实时识别的。所以里面需要自定义一个录像功能。该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" />                <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();                        }                    } 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博客。可能表达得并不是很到位,一些地方写的也比较稚嫩,欢迎大家在评论中多指点多交流,在技术之路上不断前进。

8 0
原创粉丝点击