Android 实现屏幕录制

  1. 添加权限和服务声明

    xml 复制代码
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <service
                android:name=".ScreenService"
                android:enabled="true"
                android:exported="true"
                android:foregroundServiceType="mediaProjection"></service>
  2. 创建屏幕录制的 Service

    kotlin 复制代码
    import android.app.*
    import android.content.Context
    import android.content.Intent
    import android.graphics.BitmapFactory
    import android.hardware.display.DisplayManager
    import android.hardware.display.VirtualDisplay
    import android.media.CamcorderProfile
    import android.media.MediaRecorder
    import android.media.projection.MediaProjection
    import android.media.projection.MediaProjectionManager
    import android.os.Build
    import android.os.IBinder
    import android.util.DisplayMetrics
    import android.util.Log
    import android.view.WindowManager
    import androidx.core.app.NotificationCompat
    import java.io.IOException
    
    class ScreenService : Service() {
    
        private var mContext:Context?=null
        private var projectionManager:MediaProjectionManager?=null
        private var mMediaProjection:MediaProjection?=null
        override fun onBind(intent: Intent): IBinder {
            TODO("Return the communication channel to the service.")
        }
    
        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            mContext = this
            var resultCode = intent?.getIntExtra("resultCode", -1)
            var path = intent?.getStringExtra("path")
            var resultData: Intent? = intent?.getParcelableExtra("data")
            startNotification();
            projectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
            mMediaProjection = resultCode?.let { resultData?.let { it1 -> projectionManager?.getMediaProjection(it, it1) } }
            path?.let { startRecording(it) }
            return super.onStartCommand(intent, flags, startId)
        }
    
        private var NOTIFICATION_CHANNEL_ID="id";
        private var NOTIFICATION_CHANNEL_NAME="channel";
        private var NOTIFICATION_CHANNEL_DESC="desc";
        private fun startNotification() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                var notificationIntent = Intent(mContext, ScreenService::class.java)
                var pendingIntent: PendingIntent?=null
                pendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
                    PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
                } else {
                    PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
                }
                var  notificationBuilder = mContext?.let {
                    NotificationCompat.Builder(it, NOTIFICATION_CHANNEL_ID)
                        .setLargeIcon(BitmapFactory.decodeResource(mContext!!.resources, R.drawable.ic_launcher_foreground))
                        .setSmallIcon(R.drawable.ic_launcher_foreground)
                        .setContentTitle("start record")
                        .setContentText("=== start record ===")
                        .setContentIntent(pendingIntent)
                };
                var  notification = notificationBuilder?.build();
                var  channel = NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
                channel.description = NOTIFICATION_CHANNEL_DESC;
                var  notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                notificationManager.createNotificationChannel(channel)
                startForeground(1, notification);
            }
        }
    
    
        private var isScreenRecoding = false
        private var  mMediaRecorder: MediaRecorder?=null
        private var mVirtualDisplay: VirtualDisplay? = null
        private fun startRecording(filePath:String) {
            if (!isScreenRecoding){
                try {
                    // 创建 MediaRecorder 并设置参数
                    val metrics = DisplayMetrics()
                    val windowManager: WindowManager = mContext?.getSystemService(WINDOW_SERVICE) as WindowManager
                    windowManager.defaultDisplay.getMetrics(metrics)
                    mMediaRecorder = MediaRecorder()
                    mMediaRecorder?.setVideoSource(MediaRecorder.VideoSource.SURFACE)
                    mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
                    mMediaRecorder?.setOutputFile(filePath)
                    mMediaRecorder?.setVideoSize(metrics.widthPixels, metrics.heightPixels)
                    mMediaRecorder?.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
                    mMediaRecorder?.setVideoEncodingBitRate(1920*1080 * 3)
                    mMediaRecorder?.setVideoFrameRate(30)
    
                    // 准备 MediaRecorder
                    mMediaRecorder?.prepare()
    
                    // 创建 VirtualDisplay 以获取屏幕内容
                    mVirtualDisplay = mMediaProjection?.createVirtualDisplay(
                        "ScreenRecord",
                        metrics.widthPixels, metrics.heightPixels, metrics.densityDpi,
                        DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                        mMediaRecorder?.surface, null, null
                    )
    
                    // 开始录制
                    mMediaRecorder?.start()
                    isScreenRecoding = true
                    Log.i(ScreenUtil.TAG,"开始录屏 $filePath")
                } catch (e: IOException) {
                    Log.e(ScreenUtil.TAG, "录屏失败: " + e.message)
                    e.printStackTrace()
                }
            }
        }
        public fun stopRecording() {
            if (isScreenRecoding) {
                try {
                    // 停止录制
                    mMediaRecorder?.stop()
                    mMediaRecorder?.reset()
                    mMediaRecorder?.release()
                    mMediaRecorder = null
    
                    // 停止 VirtualDisplay
                    mVirtualDisplay?.release()
    
                    // 停止 MediaProjection
                    mMediaProjection?.stop()
                    Log.i(ScreenUtil.TAG,"结束录屏")
                } catch (e: Exception) {
                    Log.e(ScreenUtil.TAG, "停止录屏失败: " + e.message)
                    e.printStackTrace()
                }
                isScreenRecoding = false
            }
        }
    
        override fun onDestroy() {
            stopRecording()
            super.onDestroy()
        }
    }
  3. 启动和停止录制

    kotlin 复制代码
    private var mProjectionManager: MediaProjectionManager? = null
    var screenService:Intent?=null
    fun start(){
    	mProjectionManager = getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
    	// 请求录屏权限
    	startActivityForResult(mProjectionManager?.createScreenCaptureIntent(), 500);
    }
    fun stop(){
    	stopService(screenService)
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 500){
            screenService = Intent(mConext, ScreenService::class.java)
            screenService?.let {
                it.putExtra("resultCode", resultCode);
                it.putExtra("data", data);
                it.putExtra("path", "screen.mp4");
                startForegroundService(it)
            }
        }
    }
相关推荐
晨春计28 分钟前
【git】
android·linux·git
标标大人1 小时前
c语言中的局部跳转以及全局跳转
android·c语言·开发语言
木鬼与槐2 小时前
MySQL高阶1783-大满贯数量
android·数据库·mysql
iofomo2 小时前
【Abyss】Android 平台应用级系统调用拦截框架
android·开发工具·移动端
AirDroid_cn5 小时前
在家找不到手机?除了语音助手,还可以用远程控制!
android·智能手机·远程控制·手机使用技巧·远程控制手机
Good_tea_h13 小时前
Android中如何处理运行时权限?
android
冬田里的一把火313 小时前
[Android][Reboot/Shutdown] 重启/关机 分析
android·gitee
大海..13 小时前
Android 系统开发人员的权限说明文档
android
技术无疆16 小时前
ButterKnife:Android视图绑定的简化专家
android·java·android studio·android-studio·androidx·butterknife·视图绑定
JohnsonXin17 小时前
【兼容性记录】video标签在 IOS 和 安卓中的问题
android·前端·css·ios·h5·兼容性