使用 Android Camera API 完成音视频的采集、编码、封包成 mp4 输出
基于android.hardware.Camera,创建一个横屏应用,实时预览摄像头图像,实现录像并输出MP4的功能。
申请权限

在activity中动态申请权限

private static final String[] VIDEO_PERMISSIONS = {        Manifest.permission.CAMERA,        Manifest.permission.RECORD_AUDIO,        Manifest.permission.WRITE_EXTERNAL_STORAGE};

实现摄像头预览功能

使用SurfaceView来预览。新建CameraPreview类继承自SurfaceView并实现SurfaceHolder.Callback;
camera相关操作都放在这个View里。

surfaceCreated中获取Camera实例,启动预览;设置预览相关参数
surfaceDestroyed释放Camera

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {    private SurfaceHolder mHolder;    private Camera mCamera;    public static final int MEDIA_TYPE_IMAGE = 1;    public static final int MEDIA_TYPE_VIDEO = 2;    private static int mOptVideoWidth = 1920;  // 默认视频帧宽度    private static int mOptVideoHeight = 1080;    private Uri outputMediaFileUri;    private String outputMediaFileType;    public CameraPreview(Context context) {        super(context);        mHolder = getHolder();        mHolder.addCallback(this);    }    private static Camera getCameraInstance() {        Camera c = null;        try {            c = Camera.open();        } catch (Exception e) {            Log.d(TAG, "camera is not available");        }        return c;    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        mCamera = getCameraInstance();        try {            mCamera.setPreviewDisplay(holder);            mCamera.startPreview();            getCameraOptimalVideoSize(); // 找到最合适的分辨率        } catch (IOException e) {            Log.d(TAG, "Error setting camera preview: " + e.getMessage());        }    }    private void getCameraOptimalVideoSize() {        try {            Camera.Parameters parameters = mCamera.getParameters();            List mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();            List mSupportedVideoSizes = parameters.getSupportedVideoSizes();            Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,                    mSupportedPreviewSizes, getWidth(), getHeight());            mOptVideoWidth = optimalSize.width;            mOptVideoHeight = optimalSize.height;            Log.d(TAG, "prepareVideoRecorder: optimalSize:" + mOptVideoWidth + ", " + mOptVideoHeight);        } catch (Exception e) {            Log.e(TAG, "getCameraOptimalVideoSize: ", e);        }    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        mHolder.removeCallback(this);        mCamera.setPreviewCallback(null);        mCamera.stopPreview();        mCamera.release();        mCamera = null;    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {    }}

在Fragment中显示摄像头预览

预置一个FrameLayout,实例化一个CameraPreview添加进去

/** * 视频录制界面 * Created by Rust on 2018/5/17. */public class VideoRecordFragment extends Fragment {    private static final String TAG = "rustAppVideoFrag";    private Button mCaptureBtn;    private CameraPreview mCameraPreview;    public static VideoRecordFragment newInstance() {        return new VideoRecordFragment();    }    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "frag onCreate");    }    @Nullable    @Override    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        Log.d(TAG, "frag onCreateView");        return inflater.inflate(R.layout.frag_video_record, container, false);    }    @Override    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {        Log.d(TAG, "frag onViewCreated");        super.onViewCreated(view, savedInstanceState);        mCaptureBtn = view.findViewById(R.id.capture_btn);        //mCaptureBtn.setOnClickListener(mOnClickListener);// 录制键        mCameraPreview = new CameraPreview(getContext());        FrameLayout preview = view.findViewById(R.id.camera_preview);        preview.addView(mCameraPreview);    }}

使用MediaRecorder录制

给MediaRecorder指定参数后,调用start()开始录制,stop()结束录制

录制开始前,获取camera,mCamera.unlock()解锁;录制完毕后,清除MediaRecorder,mCamera.lock()

private MediaRecorder mMediaRecorder;    public boolean startRecording() {        if (prepareVideoRecorder()) {            mMediaRecorder.start();            return true;        } else {            releaseMediaRecorder();        }        return false;    }    public void stopRecording() {        if (mMediaRecorder != null) {            mMediaRecorder.stop();        }        releaseMediaRecorder();    }    public boolean isRecording() {        return mMediaRecorder != null;    }    private boolean prepareVideoRecorder() {        if (null == mCamera) {            mCamera = getCameraInstance();        }        mMediaRecorder = new MediaRecorder();        mCamera.unlock();        mMediaRecorder.setCamera(mCamera);        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);        mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));        mMediaRecorder.setVideoSize(mOptVideoWidth, mOptVideoHeight);        mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());        mMediaRecorder.setPreviewDisplay(mHolder.getSurface());        try {            mMediaRecorder.prepare();        } catch (IllegalStateException e) {            Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());            releaseMediaRecorder();            return false;        } catch (IOException e) {            Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());            releaseMediaRecorder();            return false;        }        return true;    }    private void releaseMediaRecorder() {        if (mMediaRecorder != null) {            mMediaRecorder.reset();            mMediaRecorder.release();            mMediaRecorder = null;            mCamera.lock();        }    }    private File getOutputMediaFile(int type) {        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(                Environment.DIRECTORY_PICTURES), TAG);        if (!mediaStorageDir.exists()) {            if (!mediaStorageDir.mkdirs()) {                Log.d(TAG, "failed to create directory");                return null;            }        }        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date());        File mediaFile;        if (type == MEDIA_TYPE_IMAGE) {            mediaFile = new File(mediaStorageDir.getPath() + File.separator +                    "IMG_" + timeStamp + ".jpg");            outputMediaFileType = "image/*";        } else if (type == MEDIA_TYPE_VIDEO) {            mediaFile = new File(mediaStorageDir.getPath() + File.separator +                    "VID_" + timeStamp + ".mp4");            outputMediaFileType = "video/*";        } else {            return null;        }        outputMediaFileUri = Uri.fromFile(mediaFile);        return mediaFile;    }

后台返回时预览黑屏的问题

CameraPreview是我们在Fragment创建时实例化并添加进去的。
应用退到后台后,CameraPreview已经被销毁。应用回到前台时,我们应该在onResume方法中进行操作。恢复CameraPreview。

在Fragment中,判断销毁和重建预览的时机。

@Overridepublic void onPause() {    super.onPause();    Log.d(TAG, "onPause: 销毁预览");    mCameraPreview = null;}@Overridepublic void onResume() {    super.onResume();    Log.d(TAG, "onResume: 回到前台");    if (null == mCameraPreview) {        initCameraPreview();    }}private void initCameraPreview() {    mCameraPreview = new CameraPreview(getContext());    FrameLayout preview = mRoot.findViewById(R.id.camera_preview);    preview.addView(mCameraPreview);}

相关代码请参阅: https://github.com/changhaismile/CameraRecorder

更多相关文章

  1. [置顶] Android之Fragment的前世今生(二)
  2. android 美颜滤镜效果的实现
  3. Android(安卓)音视频开发(三) -- Camera2 实现预览、拍照功能
  4. Android中Message传递参数实例
  5. Android图片预览效果,支持缩放、平移切换
  6. android widget的预览图
  7. 网络实时监测广播类
  8. android 视频录制 例子源码
  9. Activity销毁时返回数据

随机推荐

  1. Android各文件存储路径汇总
  2. android中添加通过内容提供者添加联系人
  3. android 从媒体库去数据
  4. An Asynchronous HTTP Library for Andro
  5. 怎样实现android http-post方法
  6. TextView本身可以加图片装饰
  7. 使用xml和java代码混合控制UI界面
  8. Android(安卓)Uri.getQueryParameter使用
  9. android自定义适配屏幕的ImageView
  10. android RadioButton单选按钮的使用