Android 13 录屏流程

作者: 孤街酒客0911 | 来源:发表于2022-12-21 17:59 被阅读0次

    在Android13 中录屏有个专门的类 ScreenMediaRecorder.java。我们先看ScreenMediaRecorder#start()方法:

    // ScreenMediaRecorder.java
        void start() throws IOException, RemoteException, RuntimeException {
            Log.d(TAG, "start recording");
            // 准备录屏
            prepare();
            // 开始录屏
            mMediaRecorder.start();
            // 开始内部音频录制
            recordInternalAudio();
        }
    

    这个 ScreenMediaRecorder#start()RecordingService.java 的 onStartCommand() 方法里被调用。

    接着看 ScreenMediaRecorder#prepare() 准备录屏:

    // ScreenMediaRecorder.java
        private void prepare() throws IOException, RemoteException, RuntimeException {
            //Setup media projection
            // 拉起 MEDIA_PROJECTION_SERVICE
            IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE);
            IMediaProjectionManager mediaService =
                    IMediaProjectionManager.Stub.asInterface(b);
            IMediaProjection proj = null;
            proj = mediaService.createProjection(mUser, mContext.getPackageName(),
                        MediaProjectionManager.TYPE_SCREEN_CAPTURE, false);
            IBinder projection = proj.asBinder();
            mMediaProjection = new MediaProjection(mContext,
                    IMediaProjection.Stub.asInterface(projection));
    
            // 创建文件、设置格式
            File cacheDir = mContext.getCacheDir();
            cacheDir.mkdirs();
            mTempVideoFile = File.createTempFile("temp", ".mp4", cacheDir);
    
            // 设置媒体记录器
            // MediaRecorde 主要提供了一些方法用来支持录屏录音
            mMediaRecorder = new MediaRecorder();
    
            // 设置音频源
            if (mAudioSource == MIC) {
                // 设置声音来源,一般传入 MediaRecorder. AudioSource.MIC参数指定录制来自麦克风的声音。(这里如果只录屏可以不设置)
                mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
            }
            // 设置用于录制的视频来源。如屏幕等
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            // 设置所录制的音视频文件的格式。
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    
    
            // 设置视频
            DisplayMetrics metrics = new DisplayMetrics();
            WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
            wm.getDefaultDisplay().getRealMetrics(metrics);
            int refreshRate = (int) wm.getDefaultDisplay().getRefreshRate();
            int[] dimens = getSupportedSize(metrics.widthPixels, metrics.heightPixels, refreshRate);
            int width = dimens[0];
            int height = dimens[1];
            refreshRate = dimens[2];
            int vidBitRate = width * height * refreshRate / VIDEO_FRAME_RATE
                    * VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO;
            // 设置视频的编码格式
            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mMediaRecorder.setVideoEncodingProfileLevel(
                    MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
                    MediaCodecInfo.CodecProfileLevel.AVCLevel3);
            // 设置要拍摄的宽度和视频的高度,最高只能设置640x480
            mMediaRecorder.setVideoSize(width, height);
            // 设置录制视频的捕获帧速率
            mMediaRecorder.setVideoFrameRate(refreshRate);
            // 设置所录制视频的编码位率
            mMediaRecorder.setVideoEncodingBitRate(vidBitRate);
            // 设置录制会话的最长持续时间(以ms为单位)
            mMediaRecorder.setMaxDuration(MAX_DURATION_MS);
            // 设置最大文件大小
            mMediaRecorder.setMaxFileSize(MAX_FILESIZE_BYTES);
    
            // 设置音频
            if (mAudioSource == MIC) {
                // 设置音频编码格式
                mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
                mMediaRecorder.setAudioChannels(TOTAL_NUM_TRACKS);
                // 设置所录制视频的编码位率
                mMediaRecorder.setAudioEncodingBitRate(AUDIO_BIT_RATE);
                mMediaRecorder.setAudioSamplingRate(AUDIO_SAMPLE_RATE);
            }
            // 设置录制的音频文件的保存位置
            mMediaRecorder.setOutputFile(mTempVideoFile);
            // 准备录制
            mMediaRecorder.prepare();
            // Create surface
            mInputSurface = mMediaRecorder.getSurface();
            // VirtualDisplay类代表一个虚拟显示器,需要调用DisplayManager 类的 createVirtualDisplay()方法,
            // 将虚拟显示器的内容渲染在一个Surface控件上,即 捕捉屏幕了。
            mVirtualDisplay = mMediaProjection.createVirtualDisplay(
                    "Recording Display",    // 实际的流媒体显示实体名字,不能为null;
                    width,                  // 实际的流媒体显示实体的宽度,单位为像素,必须大于0
                    height,                 // 实际的流媒体显示实体的高度,单位为像素,必须大于0;
                    metrics.densityDpi,     // 实际的流媒体显示实体的像素密度,单位为dp,必须大于0;
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,    // 实际的流媒体显示实体标志的结合
                    mInputSurface,          // 播放流媒体的surface实例,可为null,
                    null,                   // 实际的流媒体显示实体状态改变时的回调方法,可能为null;
                    null);                  // 调用第 7 个参数回调方法的handler
    
            mMediaRecorder.setOnInfoListener(mListener);
            if (mAudioSource == INTERNAL ||
                    mAudioSource == MIC_AND_INTERNAL) {
                mTempAudioFile = File.createTempFile("temp", ".aac",
                        mContext.getCacheDir());
                mAudio = new ScreenInternalAudioRecorder(mTempAudioFile.getAbsolutePath(),
                        mMediaProjection, mAudioSource == MIC_AND_INTERNAL);
            }
        }
    

    上述 prepare() 方法,主要:进行了拉起后台服务,创建VirtualDisplay用于屏幕参数设定、设置录屏问文件路径等操作;

    再接着看ScreenMediaRecorder#end()方法:

    // ScreenMediaRecorder.java
        void end() {
            //jingtao.guo add try catch for TFBAAA-341 crash
            try {
                mMediaRecorder.stop();
                mMediaRecorder.release();
                mInputSurface.release();
                mVirtualDisplay.release();
                mMediaProjection.stop();
                mMediaRecorder = null;
                mMediaProjection = null;
                stopInternalAudioRecording();
    
                Log.d(TAG, "end recording");
            } catch (RuntimeException e) {
                Log.e(TAG, "end recording" + e.getMessage());
            }
        }
    

    end() 方法结束录屏,调用了mMediaRecorder的stop与release方法,stopInternalAudioRecording()方法中通过 mAudio.end()方法来停止录音。

    缩略图
    在谷歌原生设计中,录屏的缩略图并非是视频的第一帧,而是前20帧中的最大有效帧作为预览图。这种设计的好处是避免了第一帧是黑色或者不清楚,从而使得用户看起来比较清晰,更好辨别出所录的视频内容;

    相关文章

      网友评论

        本文标题:Android 13 录屏流程

        本文链接:https://www.haomeiwen.com/subject/tpemqdtx.html