zoukankan      html  css  js  c++  java
  • Android使用MediaRecorder和Camera实现视频录制及播放功能整理

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

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

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

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

    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
    //设置视频录制的最长时间
    intent.putExtra (MediaStore.EXTRA_DURATION_LIMIT,30);
    //设置视频录制的画质
    intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
    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="0"
            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 = 0;
    
        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,1000);
            }
        };
    
        @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,1000);
                        mImageView.setVisibility(View.GONE);
                        if (mRecorder == null) {
                            mRecorder = new MediaRecorder();
                        }
    
                        camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
                        if (camera != null) {
                            camera.setDisplayOrientation(90);
                            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(640, 480);
                            mRecorder.setVideoFrameRate(30);
                            mRecorder.setVideoEncodingBitRate(3 * 1024 * 1024);
                            mRecorder.setOrientationHint(90);
                            //设置记录会话的最大持续时间(毫秒)
                            mRecorder.setMaxDuration(30 * 1000);
                            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 + 1) + 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(640, 480); 
    mRecorder.setVideoFrameRate(30); 
    mRecorder.setVideoEncodingBitRate(3 * 1024 * 1024);
    • 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(90); 
    camera.unlock(); 
    mRecorder.setCamera(camera); 
    }

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


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

    案例下载

  • 相关阅读:
    【原】 POJ 1012 Joseph 约瑟夫 解题报告
    【原】 POJ 1001 Exponentiation 大整数乘法 解题报告
    POJ——1517
    毕业了,校园里走走看看——华中科技大学
    毕业答辩后
    CV编程常用的获取鼠标圈定区域的方法
    送走了GB
    POJ——2546
    奥巴马在YY21#524
    POJ——3517
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/9035883.html
Copyright © 2011-2022 走看看