天天看点

android camera 录制视频 (前后摄像头)

android camera 录制视频 (前后摄像头)

系统录制

Intent intent=new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
   intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY,0);
   intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT,10485760L);
   //android 5.0 无效
   intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT,10);
   startActivityForResult(intent,VIDEO_CAPTURE);   
           

本篇介绍MediaRecorder 和 SurfaceView 录制方法

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.Observer;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.hardware.Camera;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

public class MainActivity extends Activity implements SurfaceHolder.Callback {
    private SurfaceView surfaceView;
    private ImageView recorderBtn;
    private ImageView recorder_btn_car;
    private ImageView recorder_btn_s;
    private ImageView iv_confirm;
    private ImageView iv_relase;
    private VideoFullScreen video_view;
    private RelativeLayout rl_bottom;
    private TextView tv_time;
    private MediaRecorder recorder;
    private boolean isStart = false;
    private boolean recorded = false;
    private Camera camera;
    private SurfaceHolder holder;
    private String path;
    private Camera.Size size;
    private int facingBack =  Camera.CameraInfo.CAMERA_FACING_BACK;
    private boolean isBack = true;
    private boolean isSource = true;
    private String mCurrentPath;
    private int mWidth = 1280;
    private int mHeight = 720;
    private static final int REQUEST_PERMISSION = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏
        //全屏(默认有个电量栏)
//        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
//        getWindow().setFormat((PixelFormat.TRANSLUCENT));
        setContentView(R.layout.activity_main);
        init();
        Display display = getWindowManager().getDefaultDisplay();
        mWidth = display.getHeight();
        mHeight= display.getWidth();
//        DisplayMetrics dm = getResources().getDisplayMetrics();
//         prevHeight = dm.widthPixels;
//         prevWidth =  prevHeight * mWidth/mHeight;

        recorderBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isStart) {
                    //未开始,点击开始
                    startTimer(16);
                    recorderBtn.setSelected(true);
                    recorder_btn_car.setVisibility(View.GONE);
                    recorder_btn_s.setVisibility(View.GONE);
                    startRecorder();
                    isStart = true;
                } else {
                    recorderBtn.setSelected(false);
                    recorder_btn_car.setVisibility(View.VISIBLE);
                    recorder_btn_s.setVisibility(View.GONE);
                    if (mDisposable !=null) {
                        mDisposable.cancel();
                        mDisposable = null;
                    }
                    //已开始,点击停止
                    stopRecorder();
                    startVideo();
                    isStart = false;
                }
            }
        });

        recorder_btn_car.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                isBack = !isBack;
                if (isBack) {
                    facingBack = Camera.CameraInfo.CAMERA_FACING_BACK;
                }else  {
                    facingBack =   Camera.CameraInfo.CAMERA_FACING_FRONT;
                }
                releaseCamera();
                initPreview();
//                stopRecorder();
//                startRecorder();
            }
        });
        recorder_btn_s.setSelected(true);
        recorder_btn_s.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                isSource = !isSource;
                if (isSource) {
                    recorder_btn_s.setSelected(true);
                }else  {
                    recorder_btn_s.setSelected(false);
                }
            }
        });

        iv_relase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                video_view.stopPlayback();
                video_view.setVisibility(View.GONE);
                recorder_btn_car.setVisibility(View.VISIBLE);
                recorder_btn_s.setVisibility(View.GONE);
                recorderBtn.setSelected(false);
                isStart = false;
                recorderBtn.setVisibility(View.VISIBLE);
                rl_bottom.setVisibility(View.GONE);
                updataTimeFormat(tv_time,0);
                initPreview();
            }
        });
        iv_confirm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.putExtra("data",mCurrentPath);
                setResult(-1,intent);
                finish();
            }
        });


    }

    private void init() {
        surfaceView = findViewById(R.id.surface_view);
        recorderBtn = findViewById(R.id.recorder_btn_container);
        recorder_btn_car = findViewById(R.id.recorder_btn_car);
        recorder_btn_s = findViewById(R.id.recorder_btn_s);
        iv_confirm = findViewById(R.id.iv_confirm);
        iv_relase = findViewById(R.id.iv_relase);
        tv_time = findViewById(R.id.tv_time);
        rl_bottom = findViewById(R.id.rl_bottom);
        video_view = findViewById(R.id.video_view);
        holder = surfaceView.getHolder();
//        holder.setSizeFromLayout();
        holder.setFixedSize(640,480);
        holder.addCallback(this);//将holder加入回调接口
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    }
    /**
     * 判断权限
     */
    public void permissionRead(){
        boolean permission = isPermission(Manifest.permission.CAMERA);
        boolean permissionRead = isPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
        boolean permissionWrite = isPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        boolean permissionAudio = isPermission(Manifest.permission.RECORD_AUDIO);
        if (!permissionRead){
            initPermission(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE});
        }else if(!permissionWrite) {
            initPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE});
        }else  if(!permission) {
            initPermission(new String[]{Manifest.permission.CAMERA});
        }else if (!permissionAudio){
            initPermission(new String[]{Manifest.permission.RECORD_AUDIO});
        }else {
            initPreview();
        }
    }

    /**
     * 初始化权限
     */
    public void initPermission(String[] permissionArr) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            boolean isGranted = true;
            for (String permission : permissionArr) {
                int result = ActivityCompat.checkSelfPermission(this, permission);
                if (result != PackageManager.PERMISSION_GRANTED) {
                    isGranted = false;
                    break;
                }
            }
            if (!isGranted) {
                // 还没有的话,去申请权限
                ActivityCompat.requestPermissions(this, permissionArr, REQUEST_PERMISSION);
            }
        }
    }
    /**
     * 判断是否有权限
     */
    public boolean isPermission(String permission) {
        boolean isGranted = true;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int result = ActivityCompat.checkSelfPermission(this, permission);
            if (result != PackageManager.PERMISSION_GRANTED) {
                isGranted = false;
            }
        }
        return isGranted;
    }


    private void openCamera(int in) {
        Log.e("VideoRecorderActivity", "openCamera()");
        camera = Camera.open(in);
        if (camera != null) {
            setCameraParameters();

            camera.unlock();
        }
    }

    private void setCameraParameters() {
        if (camera == null) {
            return;
        }

        Camera.Parameters parameters = camera.getParameters();
        camera.setDisplayOrientation(90);
        /**
         * 增加对聚焦模式的判断,没有它会很模糊
         */
        List<String> focusModesList = parameters.getSupportedFocusModes();
        if (focusModesList.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
        } else if (focusModesList.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
        }
        parameters.setRecordingHint(true);

        camera.setParameters(parameters);


        if (size == null) {
            //所有支持的宽高的集合
            List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
            //从小到大排序
            Collections.sort(mSupportedPreviewSizes, new Comparator<Camera.Size>() {
                @Override
                public int compare(Camera.Size o1, Camera.Size o2) {
                    if (o1.width == o2.width) {
                        return 0;
                    } else if (o1.width > o2.width) {
                        return 1;
                    } else {
                        return -1;
                    }

                }
            });
            //因为尺寸太大也不好,容量也就太大,所以只要满足要求就行,要求是宽大于1000或者高大于800的最小的一个
            for (int num = 0; num < mSupportedPreviewSizes.size(); num++) {
                Camera.Size size1 = mSupportedPreviewSizes.get(num);
                if (size1.width >= 1000 && size1.height >= 800) {
                    size = size1;
                    break;
                }
            }
        }
        if (facingBack == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            Point point = CameraPreviewUtils.getBestPreview(parameters, new Point(mWidth, mHeight));
            parameters.setPreviewSize(point.x, point.y);
            camera.setParameters(parameters);
        }else {
            parameters.setPreviewSize(640, 480);
        }
    }

    /**
     * 释放摄像头资源
     */
    private void releaseCamera() {
        Log.e("VideoRecorderActivity", "releaseCamera()");
        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.lock();
            camera.release();
            camera = null;
        }
    }


    private void startRecorder() {
        Log.e("VideoRecorderActivity", "initRecorder()");
        if (recorder == null) {
            recorder = new MediaRecorder();
        }
        recorderBtn.setVisibility(View.VISIBLE);

        openCamera(facingBack);
//        camera.cancelAutoFocus();
        recorder.setCamera(camera);
        recorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
            @Override
            public void onError(MediaRecorder mr, int what, int extra) {
                Log.e("VideoRecorderActivity","onError what = " +what +",extra = "+ extra );
                stopRecorder();
            }
        });
        recorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
            @Override
            public void onInfo(MediaRecorder mediaRecorder, int i, int i1) {
            }
        });

        //设置音视频源, 这两项需要放在setOutputFormat之前
        if (isSource) {
            recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);//音频源
        }
        recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//视频源

        //设置格式
        recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        //设置编码,这两项需要放在setOutputFormat之后
        recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        if (isSource) {
            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        }

        /**
         * 这里直译作编码比特率,一般叫视频码率又叫视频比特率,也叫码流率:是指视频文件在单位时间内使用的数据流量。码率越大,
         * 说明单位时间内取样率越大,数据流精度就越高,这样表现出来的的效果就是:视频画面更清晰画质更高。
         *
         */
        recorder.setVideoEncodingBitRate(2* 1024 * 1024);
        /**
         * 视频帧率:通常说一个视频的25帧,指的就是这个视频帧率,即1秒中会显示25帧;
         * 视频帧率影响的是画面流畅感,也就是说视频帧率超高,表现出来的效果就是:画面越显得流畅。
         * 你也可以这样理解,假设1秒只显1帧,那么一段视频看起来,就是有很明显的卡顿感,不流畅不连惯。
         * 当然视频帧率越高,意味着画面越多,也就相应的,这个视频文件的大小也会随之增加,
         * 占用存储空间也就增大了.一般25或30就可以
         */
        recorder.setVideoFrameRate(30);
//        if (isBack) {
            recorder.setOrientationHint(displayOrientation(this));
//        } else  {
//            recorder.setOrientationHint(270);
//        }
        if (facingBack == Camera.CameraInfo.CAMERA_FACING_BACK) {
            recorder.setOrientationHint(displayOrientation(this));
        }else {
            recorder.setOrientationHint(360 - displayOrientation(this));
        }

        recorder.setMaxDuration(15*1000);//设置最大持续时间
        /**
         * 视频分辨率:分辨率就是我们常说的600x400分辨率、1920x1080分辨率,分辨率影响视频图像的大小,
         * 与视频图像大小成正比:视频分辨率越高,图像越大,越清晰,对应的视频文件本身大小也会越大。
         */
//         recorder.setVideoSize( 640,480);//市面上百分之99的手机支持这个视频尺寸.也可以像下面这样生成想要的尺寸
        Log.d("生成的分辨率:","width="+size.width+"  height="+size.height);
//        if (size != null) recorder.setVideoSize(size.width, size.height);
        Display display = getWindowManager().getDefaultDisplay();
        int  Height = display.getHeight();
        int  Width = display.getWidth();
//        recorder.setVideoSize( prevWidth,prevHeight);
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
            recorder.setVideoSize(1920, 1080);
        }else {
            recorder.setVideoSize( Height,Width);
//            if (size != null) recorder.setVideoSize(size.width, size.height);
        }
        //这句话必须有,而且参数必须这么写,通过surfaceView得到,不然报错或者不显示拍摄内容
        recorder.setPreviewDisplay(surfaceView.getHolder().getSurface());

        try {
            mCurrentPath =  createRecordPath();
            recorder.setOutputFile(mCurrentPath);
            recorder.prepare();
            recorder.start();

        } catch (Exception e) {
            e.printStackTrace();
            Log.e("VideoRecorderActivity", "recorder.prepare()异常" );
            stopRecorder();
        }
    }

//    private void

    private int displayOrientation(Context context) {
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        int rotation = windowManager.getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
            default:
                degrees = 0;
                break;
        }
        int result = (0 - degrees + 360) % 360;
        if (APIUtils.hasGingerbread()) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(facingBack, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                result = (info.orientation + degrees) % 360;
                result = (360 - result) % 360;
            } else {
                result = (info.orientation - degrees + 360) % 360;
            }
        }
        return result;
    }
    private void stopRecorder() {
        if (recorder != null) {
            //这里录像停止如果recorder.setPreviewDisplay(null);那么结束后的预览就是摄像机的内容,
            //不停止就是录制最后一帧的画面
//            recorder.setPreviewDisplay(null);
//            recorder.setOnErrorListener(null);
            recorder.stop();
            recorder.reset();
            recorder.release();
            recorder = null;
            recorded = true;
//            choose.setVisibility(View.VISIBLE);
            recorderBtn.setVisibility(View.GONE);
            rl_bottom.setVisibility(View.VISIBLE);
        }
        releaseCamera();
    }

    /**
     * 预览功能就是当surface建立的时候,调用这个方法,在surfaceView里预览相机拍摄内容
     * surfaceView可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。
     * 如果你要查看 surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)
     * 和 surfaceDestroyed(SurfaceHolder)。
     *
     * @param holder
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.e("VideoRecorderActivity", "surfaceCreated");
        permissionRead();
    }

    /**
     * 重置预览
     */
    private void initPreview(){
        if (camera == null) {
            camera = Camera.open(facingBack);
        }

        try {
            setCameraParameters();
            camera.setPreviewDisplay(holder);
            camera.startPreview();//开始预览
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "打开相机失败", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.e("VideoRecorderActivity", "surfaceChanged  width =" + width);
        Log.e("VideoRecorderActivity", "surfaceChanged height = " + height);

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.e("VideoRecorderActivity", "surfaceDestroyed");
        releaseCamera();
    }

    private String createRecordPath() throws IOException {

        File sdDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
        if (sdDir != null) {
            File dir = new File(sdDir.getAbsolutePath());//新建子目录
            if (!dir.exists()) {
                dir.mkdirs();
            }
            //视频文件的路径
            String path = dir.getAbsolutePath() + "/" + System.currentTimeMillis() + ".mp4";
            return path;
        }
        return null;
    }

    private Timer mDisposable = null;
    private int number = 0;
    private Handler handler = null;
    public void startTimer(int second){
        if (mDisposable !=null) {
            return;
        }
        if (handler == null) {
            handler = new Handler(getMainLooper());
        }
        mDisposable = new Timer();
        mDisposable.schedule(new TimerTask() {
            @Override
            public void run() {

                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        number ++;
                        if (number >= second) {
                            updataTimeFormat(tv_time,Math.round(number * 1000));
                            stopRecorder();
                            startVideo();
                        }else  {
                            updataTimeFormat(tv_time,Math.round(number * 1000));
                        }
                    }
                });

            }
        },1000,1000);

    }
    private void startVideo(){
        if (video_view.isPlaying())
            return;
        video_view.setVisibility(View.VISIBLE);
        video_view.setVideoPath(mCurrentPath);
        video_view.start();
        //或者
        video_view.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                video_view.setVideoPath(mCurrentPath);
                //或 //mVideoView.setVideoPath(Uri.parse(_filePath));
                video_view.start();
            }
        });

    }
    /**
     * 时间格式化
     *
     * @param textView    时间控件
     * @param millisecond 总时间 毫秒
     */
    private void updataTimeFormat(TextView textView, int millisecond) {
        //将毫秒转换为秒
        int second = millisecond / 1000;
        //计算小时
        int hh = second / 3600;
        //计算分钟
        int mm = second % 3600 / 60;
        //计算秒
        int ss = second % 60;
        //判断时间单位的位数
        String str = null;
        if (hh != 0) {//表示时间单位为三位
            str = String.format("%02d:%02d:%02d", hh, mm, ss);
        } else {
            str = String.format("%02d:%02d", mm, ss);
        }
        //将时间赋值给控件
        textView.setText(str);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
       permissionRead();
    }
}


           

xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <SurfaceView
            android:id="@+id/surface_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></SurfaceView>
    </FrameLayout>



    <TextView
        android:id="@+id/tv_time"
        android:text="0:00"
        android:padding="@dimen/dp_10"
        android:textSize="18dp"
        android:layout_marginTop="@dimen/dp_10"
        android:textColor="@color/white"
        android:layout_centerHorizontal="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"></TextView>


    <ImageView
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="30dp"
        android:background="@drawable/collect_play_selector"
        android:src="@drawable/collect_play_selector"
        android:id="@+id/recorder_btn_container"
        android:layout_width="80dp"
        android:layout_height="80dp"></ImageView>
    <ImageView
        android:text="声音"
        android:visibility="gone"
        android:layout_marginTop="@dimen/dp_10"
        android:src="@drawable/collect_image_voice_selector"
        android:layout_toLeftOf="@+id/recorder_btn_car"
        android:id="@+id/recorder_btn_s"
        android:layout_marginRight="10dp"
        android:padding="@dimen/dp_10"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"> </ImageView>
    <ImageView
        android:text="切换"
        android:padding="@dimen/dp_10"
        android:src="@drawable/collect_image_ca_selector"
        android:layout_alignParentRight="true"
        android:layout_marginTop="@dimen/dp_10"
        android:id="@+id/recorder_btn_car"
        android:layout_marginRight="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"> </ImageView>

    <TextView

        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    </TextView>

    <com.hdketang.camera.VideoFullScreen
        android:id="@+id/video_view"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.hdketang.camera.VideoFullScreen>

    <RelativeLayout
        android:id="@+id/rl_bottom"
        android:visibility="gone"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_marginBottom="30dp"
        android:paddingLeft="@dimen/dp_20"
        android:paddingRight="@dimen/dp_20"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/iv_relase"
            android:layout_alignParentBottom="true"
            android:src="@mipmap/ic_relese"
            android:layout_width="40dp"
            android:layout_height="wrap_content">
        </ImageView>
        <ImageView
            android:id="@+id/iv_confirm"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:src="@mipmap/ic_config"
            android:layout_width="40dp"
            android:layout_height="wrap_content">

        </ImageView>
    </RelativeLayout>

</RelativeLayout>
           

源码路径 https://gitee.com/zqmyself/CameraDemo