Android 13 录屏流程

简介: Android 13 录屏流程

在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帧中的最大有效帧作为预览图。这种设计的好处是避免了第一帧是黑色或者不清楚,从而使得用户看起来比较清晰,更好辨别出所录的视频内容;

相关文章
|
16天前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
66 6
|
15天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
17天前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
22 3
|
18天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
19 2
|
1月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
33 3
|
17天前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
14 0
|
2月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
49 4
|
2月前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
46 0
|
3月前
|
Android开发
我的Android进阶修炼:安卓启动流程之init(1)
本文深入分析了Android系统中的init进程,包括其源码结构、主要功能以及启动流程的详细注解,旨在帮助读者理解init作为用户空间的1号进程在Android启动过程中的关键作用。
56 1
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
149 0