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)
            }
        }
    }
相关推荐
五味香2 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录2 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽4 小时前
Android实训九 数据存储和访问
android
aloneboyooo4 小时前
Android Studio安装配置
android·ide·android studio
Jacob程序员4 小时前
leaflet绘制室内平面图
android·开发语言·javascript
2401_897907865 小时前
10天学会flutter DAY2 玩转dart 类
android·flutter
m0_748233646 小时前
【PHP】部署和发布PHP网站到IIS服务器
android·服务器·php
Yeats_Liao7 小时前
Spring 定时任务:@Scheduled 注解四大参数解析
android·java·spring
雾里看山8 小时前
【MySQL】 库的操作
android·数据库·笔记·mysql
水瓶丫头站住17 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机