天天看点

录制视频(Surfaceview+MediaRecorder)

欢迎转载和提问,转载请注明出处

http://blog.csdn.net/ning_gg/article/details/54314315

这个是自己写的录制视频,为了简单,全写在一个Activity中,可以切换照相机,没有带聚焦,需要聚焦自己写个OnTouch即可。在我的测试机上跑起来畅通无阻,我只有3个测试机。如果你使用了我的有问题可以告诉我。

这个demo比网上什么的写死录像尺寸强多了,全部自匹配尺寸。如果你的跑不起可以第一时间检测这个尺寸设置。我的思路是获取手机屏幕尺寸,得到高/宽的比 比如1280/720 或者1920/1080这样的。然后遍历Camera的预览尺寸,得到一个跟手机尺寸最合适的那一个,这样的话预览的时候图像不会变形,然后获取手机录像的尺寸,这里必须说下不是每个手机都提供这个录像尺寸,像华为mate7就不提供,不提供的另说,提供的会遍历这个尺寸,得到相同比例(比如预览是1920/1080=1.77777)最小的一个(800/450),这样的话比大部分提供的写死一个尺寸强多了,而且录制出来的东西不会变形。为什么要找最小的是因为我们的需求录制的视频最小越好。对于那些不提供录像尺寸的,直接把预览尺寸设成录像尺寸就可以了。mate7就是这样跑通的。

附上代码

import android.hardware.Camera;
import android.media.MediaRecorder;
import android.os.Environment;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.imapedia.ykdemo.R;
import com.imapedia.ykdemo.base.BaseActivity;
import com.imapedia.ykdemo.utils.L;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import butterknife.BindView;
import butterknife.OnClick;

/**
 * Created by ning_gg on 2017/1/9.
 * <p/>
 * 录制视频,切换摄像头,尺寸匹配
 */
public class RecordVideoActivity extends BaseActivity implements MediaRecorder.OnErrorListener ,SurfaceHolder.Callback{
    private String TAG=RecordVideoActivity.class.getSimpleName();
    @BindView(R.id.activity_main_toolbar)
    Toolbar activity_main_toolbar;
    @BindView(R.id.recordvideo_sv)
    SurfaceView recordvideo_sv;
    private SurfaceHolder mSurfaceHolder;
    @BindView(R.id.toolbar_change_tv)
    TextView toolbar_change_tv;
    @OnClick (R.id.toolbar_change_tv)
    void toolbar_change_tvOnClick(){

        if(recording){
            try {
                stopRecord();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (cameraPosition == Camera.CameraInfo.CAMERA_FACING_BACK) {
            cameraPosition = Camera.CameraInfo.CAMERA_FACING_FRONT;
        } else {
            cameraPosition = Camera.CameraInfo.CAMERA_FACING_BACK;
        }

        if (isOpenCamera) {
            mCamera.stopPreview();//停掉原来摄像头的预览
            mCamera.release();//释放资源
            mCamera = null;//取消原来摄像头
            mCamera = Camera.open(cameraPosition);
            try {
                setCameraParms();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @BindView(R.id.recordvideo_btn)
    Button recordvideo_btn;

    @OnClick(R.id.recordvideo_btn)
    void recordvideo_btnOnClick() {
        if (!recording) {
            try {
                startRecord();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } else {

            try {
                stopRecord();

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
    private int screenHeight;//屏幕的高度
    private int screenWidth;//屏幕的宽度
    private Camera mCamera;
    private int videoWidth = ;// 视频分辨率宽度
    private int videoHeight = ;// 视频分辨率高度
    public  Camera.Size pictureSize;//照片分辨率
    private Camera.Size previewSize;//预览分辨率
    private boolean isOpenCamera;// 是否已经打开摄像头
    private int cameraPosition = Camera.CameraInfo.CAMERA_FACING_FRONT;//当前摄像头
    private MediaRecorder mMediaRecorder;
    private String FilePath = Environment
            .getExternalStorageDirectory().getPath() + File.separator + "ninggegetest" + File.separator;
    private File mVecordFile;
    //是否正在录制中
    private boolean recording;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_recordvideo;
    }

    @Override
    protected void init() {
        DisplayMetrics dm = getResources().getDisplayMetrics();
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels;
        mSurfaceHolder=recordvideo_sv.getHolder();
        mSurfaceHolder.addCallback(this);
    }

    /**
     * 开始录制
     */
    private void startRecord() throws IOException {
        createRecordDir();
        initRecord();
        recording=true;
        recordvideo_btn.setBackgroundResource(R.drawable.stoprecord);
    }

    /**
     * 创建文件
     */
    private void createRecordDir() throws IOException {
        File sampleDir = new File(FilePath);
        if (!sampleDir.exists()) {
            sampleDir.mkdirs();
        }
        File vecordDir = sampleDir;
        // 创建文件
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy年MM月dd日_HH_mm_ss");
        Date curDate = new Date(System.currentTimeMillis());//获取当前时间
        String str = formatter.format(curDate);
        mVecordFile = File.createTempFile(str, ".mp4", vecordDir);//mp4格式
    }

    /**
     * 初始化
     *
     * @throws IOException
     * @author ning_gg
     * @date 2015-3-16
     */
    private void initRecord() throws IOException {
        //必须解锁  不然会报错
        mCamera.unlock();
        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.reset();
        if (mCamera != null)
            mMediaRecorder.setCamera(mCamera);
        mMediaRecorder.setOnErrorListener(this);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        //后置摄像头是旋转90度
        if (cameraPosition == ) {
            mMediaRecorder.setOrientationHint();// 输出旋转90度,保持竖屏录制
        } else {
            //前置摄像头是旋转270度
            mMediaRecorder.setOrientationHint();
        }
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 视频输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);// 音频格式
        //H264用得最广  基本每个手机都支持  反倒其他的没有每个都支持
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);

        mMediaRecorder.setVideoSize(videoWidth, videoHeight);// 设置分辨率
        mMediaRecorder.setVideoEncodingBitRate( *  * );// 设置帧频率,然后就清晰了

        // 设置录制的视频帧率。必须放在设置编码和格式的后面,否则报错
        // mMediaRecorder.setVideoFrameRate(20);

        // mediaRecorder.setMaxDuration(Constant.MAXVEDIOTIME * 1000);
        mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());

        mMediaRecorder.prepare();
        mMediaRecorder.start();
    }

    /**
     * 停止录制
     */
    private void stopRecord() throws Exception {
        if (mMediaRecorder != null) {
            // 设置后不会崩
            mMediaRecorder.setOnErrorListener(null);
            mMediaRecorder.setPreviewDisplay(null);
            mMediaRecorder.stop();
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
        recording=false;
        recordvideo_btn.setBackgroundResource(R.drawable.startrecord);
    }

    @Override
    public void onError(MediaRecorder mediaRecorder, int i, int i1) {

    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        if (mCamera != null) {
            mCamera.stopPreview();//停掉原来摄像头的预览
            mCamera.release();//释放资源
            mCamera = null;//取消原来摄像头
        }
            int i = Camera.getNumberOfCameras();
            //如果只有一个摄像头
            if (i == ) {
                mCamera = Camera.open();
                toolbar_change_tv.setVisibility(View.GONE);
            } else {
                mCamera = Camera.open(cameraPosition);
            }
        isOpenCamera=true;
        try {
            setCameraParms();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        try {
            stopRecord();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    private void setCameraParms() throws IOException{
        mCamera.setPreviewDisplay(mSurfaceHolder);
        Camera.Parameters myParam = mCamera.getParameters();
        List<String> flashModes = myParam.getSupportedFlashModes();
        String flashMode = myParam.getFlashMode();
        // Check if camera flash exists
        if (flashModes!=null&&!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
            // Turn off the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_OFF)) {
                myParam.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
            }
        }

        float percent = calcPreviewPercent();
        List<Camera.Size> supportedPreviewSizes = myParam.getSupportedPreviewSizes();
        //因为有些手机videoSizes不提供  比如华为mate7  那样的话就只能拿照相机预览数据了 当然预览数据尺寸不知道能不能当视频录制尺寸
        List<Camera.Size> videoSizes = mCamera.getParameters().getSupportedVideoSizes();
        previewSize = getPreviewMaxSize(supportedPreviewSizes, percent);
        L.e(TAG, "预览尺寸w===" + previewSize.width + ",h==="
                + previewSize.height);
        if (videoSizes != null) {
            for (Camera.Size _s : videoSizes) {
                float videoS = (float) _s.width / _s.height;
                if (videoS == percent && _s.width < previewSize.width && _s.height < previewSize.height) {
                    videoWidth = _s.width;
                    videoHeight = _s.height;
                    continue;
                }
            }
        }
        if (videoWidth ==  && videoHeight == ) {
            //如果没有拿到值  那就是当前的预览数据了
            videoWidth = previewSize.width;
            videoHeight = previewSize.height;
        }
        L.e(TAG, "录像尺寸w===" + videoWidth + ",h==="
                + videoHeight);
        // 获取摄像头支持的各种分辨率
        List<Camera.Size> supportedPictureSizes = myParam.getSupportedPictureSizes();
        pictureSize = findSizeFromList(supportedPictureSizes, previewSize);
        if (pictureSize == null) {
            pictureSize = getPictureMaxSize(supportedPictureSizes, previewSize);
        }
        L.e(TAG, "照片尺寸w===" + pictureSize.width + ",h==="
                + pictureSize.height);
        // 设置照片分辨率,注意要在摄像头支持的范围内选择
        myParam.setPictureSize(pictureSize.width, pictureSize.height);
        // 设置预浏尺寸,注意要在摄像头支持的范围内选择
        myParam.setPreviewSize(previewSize.width, previewSize.height);
        myParam.setJpegQuality();
        mCamera.setParameters(myParam);
        mCamera.setDisplayOrientation();
        mCamera.startPreview();
        mCamera.cancelAutoFocus();
    }

    private float calcPreviewPercent() {
        float d = screenHeight;
        return d / screenWidth;
    }

    private Camera.Size findSizeFromList(List<Camera.Size> supportedPictureSizes, Camera.Size size) {
        Camera.Size s = null;
        if (supportedPictureSizes != null && !supportedPictureSizes.isEmpty()) {
            for (Camera.Size su : supportedPictureSizes) {
                if (size.width == su.width && size.height == su.height) {
                    s = su;
                    break;
                }
            }
        }
        return s;
    }

    // 根据摄像头的获取与屏幕分辨率最为接近的一个分辨率
    private Camera.Size getPictureMaxSize(List<Camera.Size> l, Camera.Size size) {
        Camera.Size s = null;
        for (int i = ; i < l.size(); i++) {
            if (l.get(i).width >= size.width && l.get(i).height >= size.width
                    && l.get(i).height != l.get(i).width) {
                if (s == null) {
                    s = l.get(i);
                } else {
                    if (s.height * s.width > l.get(i).width * l.get(i).height) {
                        s = l.get(i);
                    }
                }
            }
        }
        return s;
    }

    // 获取预览的最大分辨率
    private Camera.Size getPreviewMaxSize(List<Camera.Size> l, float j) {
        int idx_best = ;
        int best_width = ;
        float best_diff = f;
        for (int i = ; i < l.size(); i++) {
            int w = l.get(i).width;
            int h = l.get(i).height;
            if (w * h < screenHeight * screenWidth)
                continue;
            float previewPercent = (float) w / h;
            float diff = Math.abs(previewPercent - j);
            if (diff < best_diff) {
                idx_best = i;
                best_diff = diff;
                best_width = w;
            } else if (diff == best_diff && w > best_width) {
                idx_best = i;
                best_diff = diff;
                best_width = w;
            }
        }
        return l.get(idx_best);
    }


    @Override
    public void onBackPressed() {
        super.onBackPressed();
        try {
            stopRecord();
        } catch (Exception e) {
            e.printStackTrace();
        }
        finish();
    }
}
           

TextureView+MediaRecorder录像原理跟这个是一样的,只是把SurfaceView替换成TextureView就行了,怎么使用TextureView在这里

http://blog.csdn.net/ning_gg/article/details/54134918

欢迎转载和提问,转载请注明出处

http://blog.csdn.net/ning_gg/article/details/54314315