Android9.0 Camera App的视频拍摄用到了/android/frameworks/base/media/java/android/media/MediaRecorder.java类

的api

/** * Used to record audio and video. The recording control is based on a * simple state machine (see below). * * 

*

* *

A common case of using MediaRecorder to record audio works as follows: * *

MediaRecorder recorder = new MediaRecorder(); * recorder.setAudioSource(MediaRecorder.AudioSource.MIC); * recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); * recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); * recorder.setOutputFile(PATH_NAME); * recorder.prepare(); * recorder.start();   // Recording is now started * ... * recorder.stop(); * recorder.reset();   // You can reuse the object by going back to setAudioSource() step * recorder.release(); // Now the object cannot be reused * 
* *

Applications may want to register for informational and error * events in order to be informed of some internal update and possible * runtime errors during recording. Registration for such events is * done by setting the appropriate listeners (via calls * (to {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener and/or * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener). * In order to receive the respective callback associated with these listeners, * applications are required to create MediaRecorder objects on threads with a * Looper running (the main UI thread by default already has a Looper running). * *

Note: Currently, MediaRecorder does not work on the emulator. * * *

Developer Guides

*

For more information about how to use MediaRecorder for recording video, read the * Camera developer guide. * For more information about how to use MediaRecorder for recording sound, read the * Audio Capture developer guide.

* */public class MediaRecorder implements AudioRouting{ .....}

怎么用注释写的清清楚楚

好,接下来从VideoModule的init()方法开始讲起

@Override    public void init(CameraActivity activity, View root) {        mActivity = activity;        mUI = new VideoUI(activity, this, root);        mPreferences = ComboPreferences.get(mActivity);        if (mPreferences == null) {            mPreferences = new ComboPreferences(mActivity);        }        CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal(), activity);        mCameraId = getPreferredCameraId(mPreferences);        mPreferences.setLocalId(mActivity, mCameraId);        CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());        mOrientationManager = new OrientationManager(mActivity);        /*         * To reduce startup time, we start the preview in another thread.         * We make sure the preview is started at the end of onCreate.         */        CameraOpenThread cameraOpenThread = new CameraOpenThread();        cameraOpenThread.start();//开启线程CameraOpenThread打开相机,这里的相机是取CameraHolder里面的Camera对象,这样就不用重复去实例化对象了        mContentResolver = mActivity.getContentResolver();//SD卡工具类        Storage.setSaveSDCard(            mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1"));        mSaveToSDCard = Storage.isSaveSDCard();        // Surface texture is from camera screen nail and startPreview needs it.        // This must be done before startPreview.        mIsVideoCaptureIntent = isVideoCaptureIntent();        initializeSurfaceView();//初始化SurfaceView        // Make sure camera device is opened.        try {            cameraOpenThread.join();            if (mCameraDevice == null) {                return;            }        } catch (InterruptedException ex) {            // ignore        }//读取视频配置参数        readVideoPreferences();        mUI.setPrefChangedListener(this);        mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);        mLocationManager = new LocationManager(mActivity, this);        mUI.setOrientationIndicator(0, false);        setDisplayOrientation();//视频显示方向        mUI.showTimeLapseUI(mCaptureTimeLapse);        initializeVideoSnapshot();        resizeForPreviewAspectRatio();        initializeVideoControl();//视频控制        mPendingSwitchCameraId = -1;    }

接这看下预览方法

private void startPreview() {        Log.v(TAG, "startPreview");        mStartPrevPending = true;        SurfaceHolder sh = null;        Log.v(TAG, "startPreview: SurfaceHolder (MDP path)");        sh = mUI.getSurfaceHolder();        if (!mPreferenceRead || mPaused == true || mCameraDevice == null) {            mStartPrevPending = false;            return;        }        mErrorCallback.setActivity(mActivity);        mCameraDevice.setErrorCallback(mErrorCallback);        if (mPreviewing == true) {            stopPreview();        }        setDisplayOrientation();        mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);        setCameraParameters(true);        try {//关键代码在这里,绑定SurfaceView,熟悉的配方熟悉的套路            mCameraDevice.setPreviewDisplay(sh);            mCameraDevice.setOneShotPreviewCallback(mHandler,                new CameraManager.CameraPreviewDataCallback() {                    @Override                    public void onPreviewFrame(byte[] data, CameraProxy camera) {                        mUI.hidePreviewCover();                    }                });            mCameraDevice.startPreview();//开始预览            mPreviewing = true;            onPreviewStarted();        } catch (Throwable ex) {            closeCamera();            throw new RuntimeException("startPreview failed", ex);        }        mStartPrevPending = false;    }

这里要讲一下的是当视频的配置参数有变时是需要重新加载预览的

 @Override    public void onSharedPreferenceChanged() {        // ignore the events after "onPause()" or preview has not started yet        if (mPaused) {            return;        }        synchronized (mPreferences) {            // If mCameraDevice is not ready then we can set the parameter in            // startPreview().            if (mCameraDevice == null) return;            boolean recordLocation = RecordLocationPreference.get(mPreferences,                    CameraSettings.KEY_RECORD_LOCATION);            mLocationManager.recordLocation(recordLocation);            readVideoPreferences();            mUI.showTimeLapseUI(mCaptureTimeLapse);            // We need to restart the preview if preview size is changed.            Size size = mParameters.getPreviewSize();            if (size.width != mDesiredPreviewWidth                    || size.height != mDesiredPreviewHeight || mRestartPreview) {                stopPreview();                resizeForPreviewAspectRatio();                startPreview(); // Parameters will be set in startPreview().            } else {                setCameraParameters(false);            }            mRestartPreview = false;            mUI.updateOnScreenIndicators(mParameters, mPreferences);            Storage.setSaveSDCard(                mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1"));            mActivity.updateStorageSpaceAndHint();        }    }

我们接着讲视频拍摄按钮按下后的代码流程

private boolean startVideoRecording() {        ...        initializeRecorder();//初始化MediaRecorder        ...        requestAudioFocus();//关闭其他应用的音乐等多媒体        try {            mMediaRecorder.start(); // 开始拍摄        } catch (RuntimeException e) {           ...        }        // Make sure the video recording has started before announcing        // this in accessibility.        AccessibilityUtils.makeAnnouncement(mUI.getShutterButton(),                mActivity.getString(R.string.video_recording_started));        // The parameters might have been altered by MediaRecorder already.        // We need to force mCameraDevice to refresh before getting it.        mCameraDevice.refreshParameters();//刷新参数        // The parameters may have been changed by MediaRecorder upon starting        // recording. We need to alter the parameters if we support camcorder        // zoom. To reduce latency when setting the parameters during zoom, we        // update mParameters here once.        mParameters = mCameraDevice.getParameters();           ......        return true;    }// Prepares media recorder.    private void initializeRecorder() {        ....        if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) {            // Set the SurfaceView to visible so the surface gets created.            // surfaceCreated() is called immediately when the visibility is            // changed to visible. Thus, mSurfaceViewReady should become true            // right after calling setVisibility().            mUI.showSurfaceView();//展示视频拍摄的实时画面        }        ....        mMediaRecorder = new MediaRecorder();//实例化MediaRecorder        // Unlock the camera object before passing it to media recorder.        mCameraDevice.unlock();        mMediaRecorder.setCamera(mCameraDevice.getCamera());//绑定一个Camera实例用于拍摄        ....        //到这里已经很熟悉了,就是开篇讲的MediaRecorder的用法        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);        mProfile.videoCodec = mVideoEncoder;        mProfile.audioCodec = mAudioEncoder;        mProfile.duration = mMaxVideoDurationInMs;        if ((mProfile.audioCodec == MediaRecorder.AudioEncoder.AMR_NB) &&            !mCaptureTimeLapse && !isHFR) {            mProfile.fileFormat = MediaRecorder.OutputFormat.THREE_GPP;        }        // Set params individually for HFR case, as we do not want to encode audio        if ((isHFR || isHSR) && captureRate > 0) {            if (isHSR) {                Log.i(TAG, "Enabling audio for HSR");                mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);            }            mMediaRecorder.setOutputFormat(mProfile.fileFormat);            mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate);            mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);            mMediaRecorder.setVideoEncoder(mProfile.videoCodec);            if (isHSR) {                Log.i(TAG, "Configuring audio for HSR");                mMediaRecorder.setAudioEncodingBitRate(mProfile.audioBitRate);                mMediaRecorder.setAudioChannels(mProfile.audioChannels);                mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate);                mMediaRecorder.setAudioEncoder(mProfile.audioCodec);            }        } else {            if (!mCaptureTimeLapse) {                mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);            }            mMediaRecorder.setProfile(mProfile);        }        mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);        mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);        if (mCaptureTimeLapse) {            double fps = 1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs;            setCaptureRate(mMediaRecorder, fps);        } else if (captureRate > 0) {            Log.i(TAG, "Setting capture-rate = " + captureRate);            mMediaRecorder.setCaptureRate(captureRate);            // for HSR, encoder's target-framerate = capture-rate            // for HFR, encoder's taget-framerate = 30fps (from profile)            int targetFrameRate = isHSR ? captureRate :                    isHFR ? 30 : mProfile.videoFrameRate;            Log.i(TAG, "Setting target fps = " + targetFrameRate);            mMediaRecorder.setVideoFrameRate(targetFrameRate);            // Profiles advertizes bitrate corresponding to published framerate.            // In case framerate is different, scale the bitrate            int scaledBitrate = getHighSpeedVideoEncoderBitRate(mProfile, targetFrameRate);            Log.i(TAG, "Scaled Video bitrate : " + scaledBitrate);            mMediaRecorder.setVideoEncodingBitRate(scaledBitrate);        }        setRecordLocation();        // Set output file.        // Try Uri in the intent first. If it doesn't exist, use our own        // instead.        if (mVideoFileDescriptor != null) {            mMediaRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor());        } else {            generateVideoFilename(mProfile.fileFormat);            mMediaRecorder.setOutputFile(mVideoFilename);        }        // Set maximum file size.        long maxFileSize = mActivity.getStorageSpaceBytes() - Storage.LOW_STORAGE_THRESHOLD_BYTES;        if (requestedSizeLimit > 0 && requestedSizeLimit < maxFileSize) {            maxFileSize = requestedSizeLimit;        }        if (Storage.isSaveSDCard() && maxFileSize > SDCARD_SIZE_LIMIT) {            maxFileSize = SDCARD_SIZE_LIMIT;        }        try {            mMediaRecorder.setMaxFileSize(maxFileSize);        } catch (RuntimeException exception) {            // We are going to ignore failure of setMaxFileSize here, as            // a) The composer selected may simply not support it, or            // b) The underlying media framework may not handle 64-bit range            // on the size restriction.        }        // See android.hardware.Camera.Parameters.setRotation for        // documentation.        // Note that mOrientation here is the device orientation, which is the opposite of        // what activity.getWindowManager().getDefaultDisplay().getRotation() would return,        // which is the orientation the graphics need to rotate in order to render correctly.        int rotation = 0;        if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {            CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];            if (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT) {                rotation = (info.orientation - mOrientation + 360) % 360;            } else {  // back-facing camera                rotation = (info.orientation + mOrientation) % 360;            }        }        mMediaRecorder.setOrientationHint(rotation);        setupMediaRecorderPreviewDisplay();        try {            mMediaRecorder.prepare();        } catch (IOException e) {            Log.e(TAG, "prepare failed for " + mVideoFilename, e);            releaseMediaRecorder();            throw new RuntimeException(e);        }        mMediaRecorder.setOnErrorListener(this);        mMediaRecorder.setOnInfoListener(this);    }

当用户按下拍摄按钮后再此点击停止拍摄

private boolean stopVideoRecording() {        Log.v(TAG, "stopVideoRecording");        mStopRecPending = true;        mUI.setSwipingEnabled(true);        if (!isVideoCaptureIntent()) {            mUI.showSwitcher();        }        boolean fail = false;        if (mMediaRecorderRecording) {            boolean shouldAddToMediaStoreNow = false;            try {                mMediaRecorder.setOnErrorListener(null);                mMediaRecorder.setOnInfoListener(null);                mMediaRecorder.stop();//停止记录                shouldAddToMediaStoreNow = true;                mCurrentVideoFilename = mVideoFilename;                Log.v(TAG, "stopVideoRecording: Setting current video filename: "                        + mCurrentVideoFilename);                AccessibilityUtils.makeAnnouncement(mUI.getShutterButton(),                        mActivity.getString(R.string.video_recording_stopped));            } catch (RuntimeException e) {                Log.e(TAG, "stop fail",  e);                if (mVideoFilename != null) deleteVideoFile(mVideoFilename);                fail = true;            }            mMediaRecorderRecording = false;            //If recording stops while snapshot is in progress, we might not get jpeg callback            //because cameraservice will disable picture related messages. Hence reset the            //flag here so that we can take liveshots in the next recording session.            mSnapshotInProgress = false;            showVideoSnapshotUI(false);            // If the activity is paused, this means activity is interrupted            // during recording. Release the camera as soon as possible because            // face unlock or other applications may need to use the camera.            if (mPaused) {                closeCamera();            }            mUI.showRecordingUI(false);            if (!mIsVideoCaptureIntent) {                mUI.enableCameraControls(true);            }            // The orientation was fixed during video recording. Now make it            // reflect the device orientation as video recording is stopped.            mUI.setOrientationIndicator(0, true);            keepScreenOnAwhile();            if (shouldAddToMediaStoreNow && !fail) {                if (mVideoFileDescriptor == null) {                    saveVideo();                } else if (mIsVideoCaptureIntent) {                    // if no file save is needed, we can show the post capture UI now                    showCaptureResult();//显示视频拍摄结果界面                }            }        }        // release media recorder        releaseMediaRecorder();        releaseAudioFocus();        if (!mPaused) {            mCameraDevice.lock();            if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) {                stopPreview();                mUI.hideSurfaceView();                // Switch back to use SurfaceTexture for preview.                startPreview();//开启预览            }        }        // Update the parameters here because the parameters might have been altered        // by MediaRecorder.        if (!mPaused) mParameters = mCameraDevice.getParameters();        UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,                fail ? UsageStatistics.ACTION_CAPTURE_FAIL :                        UsageStatistics.ACTION_CAPTURE_DONE, "Video",                mMediaRecorderPausing ? mRecordingTotalTime :                        SystemClock.uptimeMillis() - mRecordingStartTime + mRecordingTotalTime);        mStopRecPending = false;        return fail;    }

那么视频文件是如何保存的呢?请看saveVideo() 方法

private void saveVideo() {        if (mVideoFileDescriptor == null) {            File origFile = new File(mCurrentVideoFilename);            if (!origFile.exists() || origFile.length() <= 0) {                Log.e(TAG, "Invalid file");                mCurrentVideoValues = null;                return;            }            long duration = 0L;            MediaMetadataRetriever retriever = new MediaMetadataRetriever();            try {                retriever.setDataSource(mCurrentVideoFilename);                duration = Long.valueOf(retriever.extractMetadata(                            MediaMetadataRetriever.METADATA_KEY_DURATION));            } catch (IllegalArgumentException e) {                Log.e(TAG, "cannot access the file");            }            retriever.release();//这里调用MediaSaveService服务来保存视频文件            mActivity.getMediaSaveService().addVideo(mCurrentVideoFilename,                    duration, mCurrentVideoValues,                    mOnVideoSavedListener, mContentResolver);        }        mCurrentVideoValues = null;    }//MediaSaveService类的方法public void addVideo(String path, long duration, ContentValues values,            OnMediaSavedListener l, ContentResolver resolver) {        // We don't set a queue limit for video saving because the file        // is already in the storage. Only updating the database.        new VideoSaveTask(path, duration, values, l, resolver).execute();    }//开启异步任务保存视频文件private class VideoSaveTask extends AsyncTask  {        private String path;        private long duration;        private ContentValues values;        private OnMediaSavedListener listener;        private ContentResolver resolver;        public VideoSaveTask(String path, long duration, ContentValues values,                OnMediaSavedListener l, ContentResolver r) {            this.path = path;            this.duration = duration;            this.values = new ContentValues(values);            this.listener = l;            this.resolver = r;        }        @Override        protected Uri doInBackground(Void... v) {            values.put(Video.Media.SIZE, new File(path).length());            values.put(Video.Media.DURATION, duration);            Uri uri = null;            try {                Uri videoTable = Uri.parse(VIDEO_BASE_URI);                uri = resolver.insert(videoTable, values);                // Rename the video file to the final name. This avoids other                // apps reading incomplete data.  We need to do it after we are                // certain that the previous insert to MediaProvider is completed.                String finalName = values.getAsString(                        Video.Media.DATA);                if (new File(path).renameTo(new File(finalName))) {                    path = finalName;                }                resolver.update(uri, values, null, null);            } catch (Exception e) {                // We failed to insert into the database. This can happen if                // the SD card is unmounted.                Log.e(TAG, "failed to add video to media store", e);                uri = null;            } finally {                Log.v(TAG, "Current video URI: " + uri);            }            return uri;        }        @Override        protected void onPostExecute(Uri uri) {            if (listener != null) listener.onMediaSaved(uri);        }    }}

这里的视频文件是放到了ContentProvider 里面供其他应用使用。

博文到这就告一段落了

谢谢大家,祝你生活愉快。

更多相关文章

  1. 第17天android:《android从零开始》视频(1-5)
  2. Android(安卓)关于获取摄像头帧数据
  3. 【Android】如何用MediaPlayer实现一个简单的音视频播放器
  4. android音频、视频、拍照基础操作
  5. Android(安卓)拍照及相册选图的那些坑
  6. android 照相源码
  7. android视频截图
  8. android获取本地视频路径
  9. android视频录制(调用系统视频录制),生成缩略图

随机推荐

  1. 一位程序员的爱情故事
  2. 数据库-范式
  3. Java内存模型-JMM简介
  4. SQL语句类别
  5. 软件架构师之基本素质
  6. 数据库-关系代数
  7. 谈谈架构师的职责
  8. 从100PV到1亿级PV网站架构演变
  9. 2021.1.17——指针和结构体的初步认识
  10. 从100PV到1亿级PV网站架构演变-知识结构