前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >短视频app源码开发,短视频录制的实现

短视频app源码开发,短视频录制的实现

作者头像
云豹科技程序员
修改2021-05-25 17:54:52
1.6K0
修改2021-05-25 17:54:52
举报
文章被收录于专栏:用户8528729的专栏

原理说明

利用SurfaceView预览视频 利用系统自带的MediaRecorder实现短视频app源码中短视频视频的录制

  1. 实例化
  2. 设置音频输入
  3. 设置输出格式
  4. 设置视频编码格式
  5. 设置输出路径
  6. 调用prepare()进行资源初始化
  7. 调用start()开始录制 注意: 这里的步骤先后顺序非常重要,如果对MediaRecorder不是那么熟悉,还是照着步骤写比较好

使用方法

代码语言:javascript
复制
    // 录制视频
    private void toRecordVideo() {
        RecordConfig
                .getInstance()
                .with(this)
                .setQuality(RecordConfig.Quality.QUALITY_480P)
                .setMaxDuration(6*1000)
                .setFocusMode(RecordConfig.FocusMode.FOCUS_MODE_CONTINUOUS_VIDEO)
                .setOutputPath("/smallvideo/")
                .obtainVideo(REQUEST_CODE_VIDEO);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode==REQUEST_CODE_VIDEO&&resultCode==RESULT_OK){
            //接收视频输出路径
            String videoPath=RecordConfig.obtainVideoPath(data);
            int duration=RecordConfig.obtainVideoDuration(data);
            Log.i(this.getClass().getSimpleName(),"obtainVideoPath="+videoPath+" duration="+generateTime(duration));
        }
    }
复制代码

实现视频录制

新建录制与播放界面

录制界面

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".RecordVideoActivity">
    <!--预览视频-->
    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <!--录制按钮-->
    <Button
        android:id="@+id/btnRecord"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginBottom="60dp"
        android:background="@drawable/selector_record_point"
        app:layout_constraintBottom_toBottomOf="@+id/surfaceView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent" />
    <!--录制进度条-->
    <com.junt.videorecorderlib.CustomProgressBar
        android:id="@+id/progressBar"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_margin="5dp"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/btnRecord"
        app:layout_constraintEnd_toEndOf="@+id/btnRecord"
        app:layout_constraintStart_toStartOf="@+id/btnRecord"
        app:layout_constraintTop_toTopOf="@+id/btnRecord"
        app:ringWidth="10"
        app:style="ring" />
    <!--取消按钮-->
    <ImageView
        android:id="@+id/ivBack"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:paddingStart="5dp"
        android:paddingTop="10dp"
        android:paddingEnd="5dp"
        android:paddingBottom="10dp"
        android:scaleType="fitXY"
        android:src="@drawable/down"
        app:layout_constraintBottom_toBottomOf="@+id/btnRecord"
        app:layout_constraintEnd_toStartOf="@+id/btnRecord"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/btnRecord" />

</androidx.constraintlayout.widget.ConstraintLayout>
复制代码

录制按钮动画效果 内部白色按钮缩小放大利用selector实现

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <layer-list>
            <item android:drawable="@drawable/record_bg_recording" android:gravity="center" />

            <item android:drawable="@drawable/record_fg_recording" android:gravity="center" />
        </layer-list>
    </item>

    <item>
        <layer-list>
            <item android:drawable="@drawable/record_bg_default" android:gravity="center" />

            <item android:drawable="@drawable/record_fg_default" android:gravity="center" />
        </layer-list>
    </item>

</selector>
复制代码

自定义圆形进度条: github.com/xiaojigugu/…

预览界面

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".PlayVideoActivity">
    
    <!--自定义View播放视频-->
    <com.junt.videorecorderlib.MediaPlayView
        android:id="@+id/playView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <!--取消-->
    <ImageButton
        android:id="@+id/btnGiveUp"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:background="@drawable/selector_video_play_button"
        android:src="@drawable/fanhui"
        app:layout_constraintBottom_toTopOf="@+id/guideline2"
        app:layout_constraintEnd_toStartOf="@+id/guideline"
        app:layout_constraintHorizontal_bias="0.44"
        app:layout_constraintStart_toStartOf="parent" />
    <!--确定-->
    <ImageButton
        android:id="@+id/btnConfirm"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:background="@drawable/selector_video_play_button"
        android:src="@drawable/duigou"
        app:layout_constraintBottom_toBottomOf="@+id/btnGiveUp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline" />
    <!--辅助线-->
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />
    <!--辅助线-->
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.9" />
</androidx.constraintlayout.widget.ConstraintLayout>  
复制代码

自定义MediaPlayView 继承SurfaceView,利用SurfaceHolder的回调方法进行MediaPlayer的初始化

新建相机管理类

这里直接copy Google官方示例中的代码

代码语言:javascript
复制
public class CameraHelper {

    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;

    /**
     * Iterate over supported camera video sizes to see which one best fits the
     * dimensions of the given view while maintaining the aspect ratio. If none can,
     * be lenient with the aspect ratio.
     *
     * @param supportedVideoSizes Supported camera video sizes.
     * @param previewSizes Supported camera preview sizes.
     * @param w     The width of the view.
     * @param h     The height of the view.
     * @return Best match camera video size to fit in the view.
     */
    public static Camera.Size getOptimalVideoSize(List<Camera.Size> supportedVideoSizes,
            List<Camera.Size> previewSizes, int w, int h) {
        // Use a very small tolerance because we want an exact match.
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;

        // Supported video sizes list might be null, it means that we are allowed to use the preview
        // sizes
        List<Camera.Size> videoSizes;
        if (supportedVideoSizes != null) {
            videoSizes = supportedVideoSizes;
        } else {
            videoSizes = previewSizes;
        }
        Camera.Size optimalSize = null;

        // Start with max value and refine as we iterate over available video sizes. This is the
        // minimum difference between view and camera height.
        double minDiff = Double.MAX_VALUE;

        // Target view height
        int targetHeight = h;

        // Try to find a video size that matches aspect ratio and the target view size.
        // Iterate over all available sizes and pick the largest size that can fit in the view and
        // still maintain the aspect ratio.
        for (Camera.Size size : videoSizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
                continue;
            if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find video size that matches the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : videoSizes) {
                if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    /**
     * @return the default camera on the device. Return null if there is no camera on the device.
     */
    public static Camera getDefaultCameraInstance() {
        return Camera.open();
    }


    /**
     * @return the default rear/back facing camera on the device. Returns null if camera is not
     * available.
     */
    public static Camera getDefaultBackFacingCameraInstance() {
        Log.i(CameraHelper.class.getSimpleName(),"getDefaultBackFacingCameraInstance");
        return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
    }

    /**
     * @return the default front facing camera on the device. Returns null if camera is not
     * available.
     */
    public static Camera getDefaultFrontFacingCameraInstance() {
        return getDefaultCamera(Camera.CameraInfo.CAMERA_FACING_FRONT);
    }


    /**
     *
     * @param position Physical position of the camera i.e Camera.CameraInfo.CAMERA_FACING_FRONT
     *                 or Camera.CameraInfo.CAMERA_FACING_BACK.
     * @return the default camera on the device. Returns null if camera is not available.
     */
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    private static Camera getDefaultCamera(int position) {
        // Find the total number of cameras available
        int  mNumberOfCameras = Camera.getNumberOfCameras();

        // Find the ID of the back-facing ("default") camera
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int i = 0; i < mNumberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == position) {
                return Camera.open(i);

            }
        }

        return null;
    }

    /**
     * Creates a media file in the {@code Environment.DIRECTORY_PICTURES} directory. The directory
     * is persistent and available to other applications like gallery.
     *
     * @param type Media type. Can be video or image.
     * @return A file object pointing to the newly created file.
     */
    public  static File getOutputMediaFile(int type){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.
        if (!Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
            return  null;
        }

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "CameraSample");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()) {
                Log.d("CameraSample", "failed to create directory");
                return null;
            }
        }

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "IMG_"+ timeStamp + ".jpg");
        } else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "VID_"+ timeStamp + ".mp4");
        } else {
            return null;
        }
        return mediaFile;
    }
}  
复制代码

新建MediaRecorder配置类

RecordConfig.java

代码语言:javascript
复制
public class RecordConfig {
    private static RecordConfig recordConfig;
    private Activity activity;

    public static final String RECORD_CONFIG_SP_NAME = "RECORD_CONFIG";
    private SharedPreferences sp;

    static final String CONFIG_QUALITY = "quality";
    static final String CONFIG_FOCUS_MODE = "focus_mode";
    static final String CONFIG_ENCODING_BIT_RATE = "bitRate";
    static final String CONFIG_FRAME_RATE = "frameRate";
    static final String CONFIG_OUTPUT_PATH = "outputPath";
    static final String CONFIG_MAX_DURATION = "duration";

    public static RecordConfig getInstance() {
        if (recordConfig == null) {
            recordConfig = new RecordConfig();
        }
        return recordConfig;
    }
    
    <!--接收上下文-->
    public RecordConfig with(Activity activity) {
        this.activity = activity;
        sp = activity.getSharedPreferences(RECORD_CONFIG_SP_NAME, Context.MODE_PRIVATE);
        return this;
    }
    
    <!--视频质量-->
    public RecordConfig setQuality(int quality) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:480P
        sp.edit().putInt(CONFIG_QUALITY, quality).commit();
        return this;
    }
    
    <!--对焦模式-->
    public RecordConfig setFocusMode(String focusMode) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:FOCUS_MODE_CONTINUOUS_VIDEO
        sp.edit().putString(CONFIG_FOCUS_MODE, focusMode).commit();
        return this;
    }

    <!--音频采样率-->
    public RecordConfig setEncodingBitRate(int encodingBitRate) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:5 * 1280 * 720
        sp.edit().putInt(CONFIG_ENCODING_BIT_RATE, encodingBitRate).commit();
        return this;
    }
    
    <!--视频帧率-->
    public RecordConfig setFrameRate(int frameRate) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:20
        sp.edit().putInt(CONFIG_FRAME_RATE, frameRate).commit();
        return this;
    }

    /**
     * 设置视频输出路径
     * @param outputPath 视频输出路径默认前缀为 /storage/emulated/0
     *                   eg:outputPath="/smallvideo/files/VID_timestamp.mp4",最终的文件路径为/storage/emulated/0/smallvideo/files/VID_timestamp.mp4
     * @return RecordConfig
     */
    public RecordConfig setOutputPath(String outputPath) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:/storage/emulated/0/junt/video/VID_timestamp.mp4
        String path = Environment.getExternalStorageDirectory() + outputPath;
        sp.edit().putString(CONFIG_OUTPUT_PATH, path).commit();
        return this;
    }

    <!--设置最大时长-->
    public RecordConfig setMaxDuration(int duration) {
        if (sp == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        //default:6*1000ms
        sp.edit().putInt(CONFIG_MAX_DURATION, duration).commit();
        return this;
    }

    /**
     * 获取视频路径
     */
    public static String obtainVideoPath(Intent data){
        if (data==null){
            throw new NullPointerException("data is NULL");
        }
        return data.getStringExtra("path");
    }

    /**
     * 获取视频长度
     */
    public static int obtainVideoDuration(Intent data){
        if (data==null){
            throw new NullPointerException("data is NULL");
        }
        return data.getIntExtra("duration",0);
    }
    
    <!--跳转至视频录制界面-->
    public void obtainVideo(int requestCode) {
        if (activity == null) {
            throw new NullPointerException("Please innovate method 'with()' first");
        }
        activity.startActivityForResult(new Intent(activity, RecordVideoActivity.class), requestCode);
    }
    
    <!--视频质量-->
    public static class Quality {
        public static int QUALITY_LOW = CamcorderProfile.QUALITY_LOW;
        public static int QUALITY_HIGH = CamcorderProfile.QUALITY_HIGH;
        public static int QUALITY_QCIF = CamcorderProfile.QUALITY_QCIF;
        public static int QUALITY_CIF = CamcorderProfile.QUALITY_CIF;
        public static int QUALITY_480P = CamcorderProfile.QUALITY_480P;
        public static int QUALITY_720P = CamcorderProfile.QUALITY_720P;
        public static int QUALITY_1080P = CamcorderProfile.QUALITY_1080P;
        public static int QUALITY_QVGA = CamcorderProfile.QUALITY_QVGA;
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public static int QUALITY_2160P = CamcorderProfile.QUALITY_2160P;
    }

    <!--对焦模式-->
    public static class FocusMode {
        public static String FOCUS_MODE_AUTO = Camera.Parameters.FOCUS_MODE_AUTO;
        public static String FOCUS_MODE_CONTINUOUS_PICTURE = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
        public static String FOCUS_MODE_CONTINUOUS_VIDEO = Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
        public static String FOCUS_MODE_EDOF = Camera.Parameters.FOCUS_MODE_EDOF;
        public static String FOCUS_MODE_INFINITY = Camera.Parameters.FOCUS_MODE_INFINITY;
        public static String FOCUS_MODE_FIXED = Camera.Parameters.FOCUS_MODE_FIXED;
        public static String FOCUS_MODE_MACRO = Camera.Parameters.FOCUS_MODE_MACRO;
    }
}
复制代码

新建RecordVideoActivity界面

!!!所有摄像头及视频录制的操作应该异步处理

  1. 初始化摄像头并预览
代码语言:javascript
复制
        mCamera = Camera.open();
        Log.d(TAG, "Camera.open");
        //获取摄像头参数
        Camera.Parameters parameters = mCamera.getParameters();
        //获取所有预览尺寸
        List<Camera.Size> mSupportedPreviewSizes = parameters.getSupportedPreviewSizes();
        //获取所有的视频尺寸
        List<Camera.Size> mSupportedVideoSizes = parameters.getSupportedVideoSizes();
        //获取适当的尺寸
        Camera.Size optimalSize = CameraHelper.getOptimalVideoSize(mSupportedVideoSizes,
                mSupportedPreviewSizes, surfaceView.getWidth(), surfaceView.getHeight());
        //设置预览竖屏方向
        mCamera.setDisplayOrientation(90);
        //获取系统默认配置
        CamcorderProfile profile = CamcorderProfile.get(sp.getInt(RecordConfig.CONFIG_QUALITY, CamcorderProfile.QUALITY_480P));
        //设置录制尺寸
        profile.videoFrameWidth = optimalSize.width;
        profile.videoFrameHeight = optimalSize.height;
        //设置视频码率
        profile.videoBitRate = sp.getInt(RecordConfig.CONFIG_ENCODING_BIT_RATE, 5 * 1280 * 720);
        //设置视频帧率
        profile.videoFrameRate = sp.getInt(RecordConfig.CONFIG_FRAME_RATE, 30);
        //设置预览尺寸
        parameters.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
        //设置对焦模式
        parameters.setFocusMode(sp.getString(RecordConfig.CONFIG_FOCUS_MODE, Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO));
        //设置闪光灯模式
        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
        mCamera.setParameters(parameters);
        try {
            //绑定SurfaceView
            mCamera.setPreviewDisplay(surfaceView.getHolder());
            //开始预览
            mCamera.startPreview();
            Log.d(TAG, "Camera初始化结束");
        } catch (IOException e) {
            Log.e(TAG, "Surface texture is unavailable or unsuitable" + e.getMessage());
        }  
复制代码
  1. 初始化MediaRecorder
代码语言:javascript
复制
        mMediaRecorder = new MediaRecorder();
        //释放摄像头,以便让MediaRecorder能够使用它
        // 该方法源码注释中明确表明必须提前调用   
        //* <p>This must be done before calling
        //* {@link android.media.MediaRecorder#setCamera(Camera)}. This cannot be
        //* called after recording starts.
        //
        mCamera.unlock();
        //绑定摄像头
        mMediaRecorder.setCamera(mCamera);
        //设置音频来源
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        //设置视频来源
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        //设置录制视频质量
        mMediaRecorder.setProfile(CamcorderProfile.get(sp.getInt(RecordConfig.CONFIG_QUALITY, CamcorderProfile.QUALITY_480P)));
        //设置视频码率
        mMediaRecorder.setVideoFrameRate(sp.getInt(RecordConfig.CONFIG_FRAME_RATE, 30));
        //设置视频帧率
        mMediaRecorder.setVideoEncodingBitRate(sp.getInt(RecordConfig.CONFIG_ENCODING_BIT_RATE, 5 * 1280 * 720));
        //设置视频输出文件
        String defaultPath = Environment.getExternalStorageDirectory() + "/junt/video/";
        String outputPath = sp.getString(RecordConfig.CONFIG_OUTPUT_PATH, defaultPath) + "VID_" + System.currentTimeMillis() + ".mp4";
        mOutputFile = new File(outputPath);
        if (!mOutputFile.getParentFile().exists()) {
            mOutputFile.getParentFile().mkdirs();
        }
        mMediaRecorder.setOutputFile(mOutputFile.getAbsolutePath());
        //设置录制视频为竖屏
        mMediaRecorder.setOrientationHint(90);
        //设置录制时的预览Surface
        mMediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface());
        //设置最大时长
        //当设置次项以后可以在 android.media.MediaRecorder.OnInfoListener 监听回调方法中接收结果
        // * After recording reaches the specified duration, a notification
        //* will be sent to the {@link android.media.MediaRecorder.OnInfoListener}
        //* with a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_DURATION_REACHED}
        //* and recording will be stopped.
        MAX_DURATION = sp.getInt(RecordConfig.CONFIG_MAX_DURATION, 6 * 1000);
        mMediaRecorder.setMaxDuration(MAX_DURATION);
        try {
            //设置录制监听
            mMediaRecorder.setOnInfoListener(this);
            //设置错误监听
            mMediaRecorder.setOnErrorListener(this);
            //完成初始化,等待录制
            mMediaRecorder.prepare();
        } catch (Exception e) {
            Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
            return false;
        }   
        
        //录制完成监听
        @Override
        public void onInfo(MediaRecorder mr, int what, int extra) {
            Log.d(TAG, "onInfo what=" + what + " extra=" + extra);
            if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED && isRecording) {
                recordComplete();
            }
    }
    ``` 
    注意:步骤1-2中的所有操作都是异步的  
``` java
        class MediaPrepareTask extends AsyncTask<Void, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Void... voids) {
            //初始化操作,包括初始化Camera、MediaRecorder
            boolean initResult = prepareVideoRecorder();
            Log.d(TAG, "doInBackground,init:" + initResult);
            if (initResult) {
                Log.d(TAG, "开始预览");
                return true;
            } else {
                Log.d(TAG, "初始化错误");
                //初始化错误则立即释放Camera、MediaRecorder
                releaseMediaRecorder();
                return false;
            }
        }
    }   
复制代码
  1. 录制按钮事件
代码语言:javascript
复制
    /**
     * 录制按钮触摸事件
     * 单击录制(再次单击停止录制)、按住录制(松手停止录制)
     */
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        int action = motionEvent.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            //按下
            long downTime=System.currentTimeMillis();
            if (downTime-clickTime>1000){
                if (!isRecording){
                    btnRecord.setPressed(true);
                    //开始录制
                    RecordTask recordTask = new RecordTask();
                    recordTask.execute();
                }else if (mMediaRecorder!=null){
                    recordComplete();
                }
            }
            clickTime=downTime;
        } else if (action == MotionEvent.ACTION_UP) {
            //抬起
            if (isRecording && mMediaRecorder != null) {
                //停止录制
                recordComplete();
            }
        }
        return true;
    }  
    
     /**
     * 开始录制任务
     */
    class RecordTask extends AsyncTask<Void, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Void... voids) {
            mMediaRecorder.start();
            return true;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            isRecording = true;
            //更新圆形进度条
            setProgressBar();
        }
    }
    
     /**
     * 停止录制
     */
    private void recordComplete() {
        isRecording = false;
        btnRecord.setPressed(false);
        endTime = System.currentTimeMillis();
        hideRecordController();
        //跳转到视频播放界面进行完整预览,在onActivityResult中接收是否使用该视频文件的结果
        Intent intent = new Intent(RecordVideoActivity.this, PlayVideoActivity.class);
        intent.putExtra(VIDEO_PATH, mOutputFile.getAbsolutePath());
        startActivityForResult(intent, REQUEST_CODE_TO_PLAY);
    } 
    
       /**
     * 设置进度条显示并调整其大小
     */
    private void setProgressBar() {
        progressBar.setProgress(0);
        ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) progressBar.getLayoutParams();
        params.width = btnRecord.getMeasuredWidth() + 20;
        params.height = btnRecord.getMeasuredHeight() + 20;
        progressBar.setLayoutParams(params);
        progressBar.setVisibility(View.VISIBLE);
        startTime = System.currentTimeMillis
        //利用计时器定时更新进度条
        progressHandler = new ProgressHandler();
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                if (progressHandler != null && isRecording) {
                    progressHandler.sendEmptyMessage(0);
                }
            }
        }, 0, 50);
    }
    
     /**
     * 更新进度条
     */
    class ProgressHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0) {
                int progress = (int) ((System.currentTimeMillis() - startTime) / (MAX_DURATION / 100));
                if (progress <= 100) {
                    progressBar.setProgress(progress);
                }
            }
        }
    }
    
     /**
     * 接收用户确认时事件
     * @param requestCode 跳转播放请求码
     * @param resultCode 结果码
     * @param data 传递了duration
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_TO_PLAY) {
            if (resultCode == RESULT_CANCELED) {
                //用户选择取消,重置视频录制界面(进度条、播放按钮)
                resetProgress();
                showRecordController();
                //删除被放弃的视频
                deleteFile();
            } else if (resultCode == RESULT_OK) {
                //用户选择了该视频,将结果返回给调用方
                Intent intent = new Intent();
                intent.putExtra("duration", data.getIntExtra("duration", 0));
                intent.putExtra("path", mOutputFile.getAbsolutePath());
                setResult(RESULT_OK, intent);
                finish();
            }
        }
    }
复制代码

播放界面

代码语言:javascript
复制
    /**
     * 放弃该视频
     */
    private void giveUp() {
        setResult(RESULT_CANCELED);
        finish();
    }

    /**
     * 选择该视频
     */
    private void chooseThisVideo() {
        Intent intent = new Intent();
        intent.putExtra("duration", mediaPlayView.getDuration());
        Log.i(TAG,"duration="+mediaPlayView.getDuration());
        setResult(RESULT_OK, intent);
        finish();
    }
复制代码

完结

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原理说明
  • 使用方法
  • 实现视频录制
    • 新建录制与播放界面
      • 新建相机管理类
        • 新建MediaRecorder配置类
          • 新建RecordVideoActivity界面
            • 播放界面
            • 完结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档