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)
            }
        }
    }
相关推荐
_extraordinary_2 分钟前
Linux权限(一)
android·linux·excel
人生!?1 小时前
给小米/红米手机root(工具基本为官方工具)——KernelSU篇
android·linux·智能手机
古苏2 小时前
Android输入事件传递流程系统源码级解析
android
生产队队长3 小时前
ThinkPHP:配置Redis并使用
android·数据库·redis
踏雪羽翼3 小时前
android 差值器的使用
android
Mr-Apple4 小时前
MySQL的Union和OR查询
android·数据库·mysql
yzpyzp4 小时前
如果后台的Long类型的数据返回是null,那么Android客户端的数据bean的kotlin的Long类型的字段接受到数据后是null空指针吗?
android·kotlin
hmywillstronger5 小时前
【Excel】【VBA】根据内容调整打印区域
android·excel
coooliang6 小时前
【Android】ViewPager的使用
android
xvch8 小时前
Kotlin 2.1.0 入门教程(二十五)类型擦除
android·kotlin