抱歉,你查看的文章不存在

音视频的采集、编码、封包成 mp4 输出

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

1、申请权限

<!-- 需要录制音视频权限和写外部存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

在activity中动态申请权限

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

2、实现摄像头预览功能

使用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<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
            List<Camera.Size> 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) {
    }
}

3、在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中,判断销毁和重建预览的时机。

@Override
public void onPause() {
    super.onPause();
    Log.d(TAG, "onPause: 销毁预览");
    mCameraPreview = null;
}

@Override
public 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);
}

相关代码,点击下方阅读原文

原创作者:CHSmile,原文链接:https://www.jianshu.com/p/b6386ba20d08

原文发布于微信公众号 - 何俊林(DriodDeveloper)

原文发表时间:2018-08-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

码农突围

0 篇文章93 人订阅

相关文章

来自专栏程序员叨叨叨

Android 5.0以上系统Toast不显示的解决方案

开发中我们经常会在适配5.0以后的机型遇到各种各样的问题,其中有一个不大不小的问题就是:Toast不显示问题

36830
来自专栏听雨堂

存储状态数据

保存偏好         @Override         protected void onPause(){                 super.o...

24890
来自专栏Android干货

Android项目实战(三):实现第一次进入软件的引导页

35650
来自专栏cloudskyme

android使用Activity

第一个例子,显示网址 首先创建工程 ? 按照提示填入 我使用的是2.3版本,所以Min SDK Version填10 修改/res/layout/下main....

39730
来自专栏木头编程 - moTzxx

安卓 —— 图灵机器人+讯飞语音设计实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/de...

31420
来自专栏蜉蝣禅修之道

Android之共享已安装的apk应用

80650
来自专栏Java后端生活

购物APP项目开发(1)——菜单开发

16340
来自专栏CodingBlock

Android学习笔记(六)Fragment的生命周期

  在上一篇博文中对Fragment做了简单的介绍,现在再来探讨一下Fragment的生命周期。 一、Fragment的几种状态:   与Activity类...

245100
来自专栏项勇

笔记83 | wifi解析使用

WifiManager是管理android WIFI的连接服务,它可以配置WIFI网络连接,管理当前wifi连接,扫描接入点,监视wifi连接状态;

10820
来自专栏指尖下的Android

Matisse预览图片黑屏,Glide内存溢出

项目中要到图片、视频选择的功能,然后google了一下,找到Matisse,知乎的图片选择框架,用的人还挺多的,果断依赖gradle,然后开始我的踩坑之旅。 ...

29410

扫码关注云+社区

领取腾讯云代金券