本文实例为大家分享了Android实现屏幕录制功能的具体代码,供大家参考,具体内容如下

1.效果图:

2.添加依赖

dependencies {

implementation fileTree(dir: "libs", include: ["*.jar"])

implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

implementation "androidx.appcompat:appcompat:1.1.0"

implementation "androidx.core:core-ktx:1.0.2"

implementation "androidx.constraintlayout:constraintlayout:1.1.3"

testImplementation "junit:junit:4.12"

androidTestImplementation "androidx.test.ext:junit:1.1.1"

androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0"

api "com.blankj:utilcode:1.24.4"

}

repositories {

mavenCentral()

}

3.注册权限:

4.主界面,

test.aac是录屏的时候配的音乐,可以随便找另外一个放到assets文件夹里面进行替换

package com.ufi.pdioms.ztkotlin

import android.content.Intent

import android.content.res.AssetFileDescriptor

import android.media.MediaPlayer

import android.os.Build

import androidx.appcompat.app.AppCompatActivity

import android.os.Bundle

import android.util.Log

import android.widget.Toast

import com.blankj.utilcode.util.PathUtils

import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

// https://github.com/fanqilongmoli/AndroidScreenRecord

private var screenRecordHelper: ScreenRecordHelper? = null

private val afdd:AssetFileDescriptor by lazy { assets.openFd("test.aac") }

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btnStart.setOnClickListener {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

if (screenRecordHelper == null) {

screenRecordHelper = ScreenRecordHelper(this, object : ScreenRecordHelper.OnVideoRecordListener {

override fun onBeforeRecord() {

}

override fun onStartRecord() {

play()

}

override fun onCancelRecord() {

releasePlayer()

}

override fun onEndRecord() {

releasePlayer()

}

}, PathUtils.getExternalStoragePath() + "/fanqilong")

}

screenRecordHelper?.apply {

if (!isRecording) {

// 如果你想录制音频(一定会有环境音量),你可以打开下面这个限制,并且使用不带参数的 stopRecord()

// recordAudio = true

startRecord()

}

}

} else {

Toast.makeText(this@MainActivity.applicationContext, "sorry,your phone does not support recording screen", Toast.LENGTH_LONG).show()

}

}

btnStop.setOnClickListener {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

screenRecordHelper?.apply {

if (isRecording) {

if (mediaPlayer != null) {

// 如果选择带参数的 stop 方法,则录制音频无效

stopRecord(mediaPlayer!!.duration.toLong(), 15 * 1000, afdd)

} else {

stopRecord()

}

}

}

}

}

}

private fun play() {

mediaPlayer = MediaPlayer()

try {

mediaPlayer?.apply {

this.reset()

this.setDataSource(afdd.fileDescriptor, afdd.startOffset, afdd.length)

this.isLooping = true

this.prepare()

this.start()

}

} catch (e: Exception) {

Log.d("fanqilong", "播放音乐失败")

} finally {

}

}

// 音频播放

private var mediaPlayer: MediaPlayer? = null

private fun releasePlayer() {

mediaPlayer?.apply {

stop()

release()

}

mediaPlayer = null

}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

super.onActivityResult(requestCode, resultCode, data)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && data != null) {

screenRecordHelper?.onActivityResult(requestCode, resultCode, data)

}

}

override fun onDestroy() {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

screenRecordHelper?.clearAll()

}

afdd.close()

super.onDestroy()

}

}

5.录屏代码

package com.ufi.pdioms.ztkotlin

import android.app.Activity

import android.content.Context

import android.content.Intent

import android.content.pm.PackageManager

import android.content.res.AssetFileDescriptor

import android.hardware.display.DisplayManager

import android.hardware.display.VirtualDisplay

import android.media.*

import android.media.projection.MediaProjection

import android.media.projection.MediaProjectionManager

import android.net.Uri

import android.os.Build

import android.os.Environment

import android.os.Handler

import android.util.DisplayMetrics

import android.util.Log

import android.widget.Toast

import androidx.annotation.RequiresApi

import com.blankj.utilcode.constant.PermissionConstants

import com.blankj.utilcode.util.PermissionUtils

import java.io.File

import java.lang.Exception

import java.nio.ByteBuffer

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)

class ScreenRecordHelper @JvmOverloads constructor(

private var activity: Activity,

private val listener: OnVideoRecordListener?,

private var savePath: String = Environment.getExternalStorageDirectory().absolutePath + File.separator

+ "DCIM" + File.separator + "Camera",

private val saveName: String = "record_${System.currentTimeMillis()}"

) {

private val mediaProjectionManager by lazy { activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as? MediaProjectionManager }

private var mediaRecorder: MediaRecorder? = null

private var mediaProjection: MediaProjection? = null

private var virtualDisplay: VirtualDisplay? = null

private val displayMetrics by lazy { DisplayMetrics() }

private var saveFile: File? = null

var isRecording = false

var recordAudio = false

init {

activity.windowManager.defaultDisplay.getMetrics(displayMetrics)

}

companion object {

private const val VIDEO_FRAME_RATE = 30

private const val REQUEST_CODE = 1024

private const val TAG = "ScreenRecordHelper"

}

fun startRecord() {

if (mediaProjectionManager == null) {

Log.d(TAG, "mediaProjectionManager == null,当前手机暂不支持录屏")

showToast(R.string.phone_not_support_screen_record)

return

}

PermissionUtils.permission(PermissionConstants.STORAGE, PermissionConstants.MICROPHONE)

.callback(object : PermissionUtils.SimpleCallback {

override fun onGranted() {

mediaProjectionManager?.apply {

listener?.onBeforeRecord()

val intent = this.createScreenCaptureIntent()

if (activity.packageManager.resolveActivity(

intent,

PackageManager.MATCH_DEFAULT_ONLY

) != null

) {

activity.startActivityForResult(intent, REQUEST_CODE)

} else {

showToast(R.string.phone_not_support_screen_record)

}

}

}

override fun onDenied() {

showToast(R.string.permission_denied)

}

}).request()

}

@RequiresApi(Build.VERSION_CODES.N)

fun resume() {

mediaRecorder?.resume()

}

@RequiresApi(Build.VERSION_CODES.N)

fun pause() {

mediaRecorder?.pause()

}

fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {

if (requestCode == REQUEST_CODE) {

if (resultCode == Activity.RESULT_OK) {

mediaProjection = mediaProjectionManager!!.getMediaProjection(resultCode, data)

// 部分手机录制视频的时候 会出现弹框

Handler().postDelayed({

if (initRecorder()) {

isRecording = true

mediaRecorder?.start()

listener?.onStartRecord()

} else {

showToast(R.string.phone_not_support_screen_record)

}

}, 150)

} else {

showToast(R.string.phone_not_support_screen_record)

}

}

}

fun cancelRecord(){

stopRecord()

saveFile?.delete()

saveFile = null

listener?.onCancelRecord()

}

fun stopRecord(videoDuration: Long = 0, audioDuration: Long = 0, afdd: AssetFileDescriptor? = null){

stop()

if (audioDuration != 0L && afdd != null) {

syntheticAudio(videoDuration, audioDuration, afdd)

} else {

// saveFile

if (saveFile != null) {

val newFile = File(savePath, "$saveName.mp4")

// 录制结束后修改后缀为 mp4

saveFile!!.renameTo(newFile)

refreshVideo(newFile)

}

saveFile = null

}

}

private fun refreshVideo(newFile: File) {

Log.d(TAG, "screen record end,file length:${newFile.length()}.")

if (newFile.length() > 5000) {

val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)

intent.data = Uri.fromFile(newFile)

activity.sendBroadcast(intent)

Log.e("TAG","refreshVideo: "+savePath)

showToast(R.string.save_to_album_success)

} else {

newFile.delete()

showToast(R.string.phone_not_support_screen_record)

Log.d(TAG, activity.getString(R.string.record_faild))

}

}

private fun stop() {

if (isRecording) {

isRecording = false

try {

mediaRecorder?.apply {

setOnErrorListener(null)

setOnInfoListener(null)

setPreviewDisplay(null)

stop()

Log.d(TAG, "stop success")

}

} catch (e: Exception) {

Log.e(TAG, "stopRecorder() error!${e.message}")

} finally {

mediaRecorder?.reset()

virtualDisplay?.release()

mediaProjection?.stop()

listener?.onEndRecord()

}

}

}

private fun initRecorder(): Boolean {

var result = true

val f = File(savePath)

if (!f.exists()) {

f.mkdir()

}

saveFile = File(savePath, "$saveName.tmp")

saveFile?.apply {

if (exists()) {

delete()

}

}

mediaRecorder = MediaRecorder()

val width = Math.min(displayMetrics.widthPixels, 1080)

val height = Math.min(displayMetrics.heightPixels, 1920)

mediaRecorder?.apply {

if (recordAudio) {

setAudioSource(MediaRecorder.AudioSource.MIC)

}

setVideoSource(MediaRecorder.VideoSource.SURFACE)

setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)

setVideoEncoder(MediaRecorder.VideoEncoder.H264)

if (recordAudio) {

setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)

}

setOutputFile(saveFile!!.absolutePath)

setVideoSize(width, height)

setVideoEncodingBitRate(8388608)

setVideoFrameRate(VIDEO_FRAME_RATE)

try {

prepare()

virtualDisplay = mediaProjection?.createVirtualDisplay(

"MainScreen", width, height, displayMetrics.densityDpi,

DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, surface, null, null

)

Log.d(TAG, "initRecorder 成功")

} catch (e: Exception) {

Log.e(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}")

e.printStackTrace()

result = false

}

}

return result

}

private fun showToast(resId: Int) {

Toast.makeText(activity.applicationContext, activity.applicationContext.getString(resId), Toast.LENGTH_SHORT)

.show()

}

fun clearAll() {

mediaRecorder?.release()

mediaRecorder = null

virtualDisplay?.release()

virtualDisplay = null

mediaProjection?.stop()

mediaProjection = null

}

/**

* https://stackoverflow.com/questions/31572067/android-how-to-mux-audio-file-and-video-file

*/

private fun syntheticAudio(audioDuration: Long, videoDuration: Long, afdd: AssetFileDescriptor) {

Log.d(TAG, "start syntheticAudio")

val newFile = File(savePath, "$saveName.mp4")

if (newFile.exists()) {

newFile.delete()

}

try {

newFile.createNewFile()

val videoExtractor = MediaExtractor()

videoExtractor.setDataSource(saveFile!!.absolutePath)

val audioExtractor = MediaExtractor()

afdd.apply {

audioExtractor.setDataSource(fileDescriptor, startOffset, length * videoDuration / audioDuration)

}

val muxer = MediaMuxer(newFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)

videoExtractor.selectTrack(0)

val videoFormat = videoExtractor.getTrackFormat(0)

val videoTrack = muxer.addTrack(videoFormat)

audioExtractor.selectTrack(0)

val audioFormat = audioExtractor.getTrackFormat(0)

val audioTrack = muxer.addTrack(audioFormat)

var sawEOS = false

var frameCount = 0

val offset = 100

val sampleSize = 1000 * 1024

val videoBuf = ByteBuffer.allocate(sampleSize)

val audioBuf = ByteBuffer.allocate(sampleSize)

val videoBufferInfo = MediaCodec.BufferInfo()

val audioBufferInfo = MediaCodec.BufferInfo()

videoExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)

audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC)

muxer.start()

// 每秒多少帧

// 实测 OPPO R9em 垃圾手机,拿出来的没有 MediaFormat.KEY_FRAME_RATE

val frameRate = if (videoFormat.containsKey(MediaFormat.KEY_FRAME_RATE)) {

videoFormat.getInteger(MediaFormat.KEY_FRAME_RATE)

} else {

31

}

// 得出平均每一帧间隔多少微妙

val videoSampleTime = 1000 * 1000 / frameRate

while (!sawEOS) {

videoBufferInfo.offset = offset

videoBufferInfo.size = videoExtractor.readSampleData(videoBuf, offset)

if (videoBufferInfo.size < 0) {

sawEOS = true

videoBufferInfo.size = 0

} else {

videoBufferInfo.presentationTimeUs += videoSampleTime

videoBufferInfo.flags = videoExtractor.sampleFlags

muxer.writeSampleData(videoTrack, videoBuf, videoBufferInfo)

videoExtractor.advance()

frameCount++

}

}

var sawEOS2 = false

var frameCount2 = 0

while (!sawEOS2) {

frameCount2++

audioBufferInfo.offset = offset

audioBufferInfo.size = audioExtractor.readSampleData(audioBuf, offset)

if (audioBufferInfo.size < 0) {

sawEOS2 = true

audioBufferInfo.size = 0

} else {

audioBufferInfo.presentationTimeUs = audioExtractor.sampleTime

audioBufferInfo.flags = audioExtractor.sampleFlags

muxer.writeSampleData(audioTrack, audioBuf, audioBufferInfo)

audioExtractor.advance()

}

}

muxer.stop()

muxer.release()

videoExtractor.release()

audioExtractor.release()

// 删除无声视频文件

saveFile?.delete()

} catch (e: Exception) {

Log.e(TAG, "Mixer Error:${e.message}")

// 视频添加音频合成失败,直接保存视频

saveFile?.renameTo(newFile)

} finally {

afdd.close()

Handler().post {

refreshVideo(newFile)

saveFile = null

}

}

}

interface OnVideoRecordListener {

/**

* 录制开始时隐藏不必要的UI

*/

fun onBeforeRecord()

/**

* 开始录制

*/

fun onStartRecord()

/**

* 取消录制

*/

fun onCancelRecord()

/**

* 结束录制

*/

fun onEndRecord()

}

}

6.布局

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

tools:context=".MainActivity">

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:textAllCaps="false"

android:text="start"/>

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:textAllCaps="false"

android:text="stop"/>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持云海天教程。

android 实现屏幕录制功能,Android实现屏幕录制功能相关推荐

  1. android屏幕录制功能,Android利用ADB进行屏幕录制

    前言 在写博客时,为了方便大家理解,我们经常需要把一些操作或动画录制成Gif,一般需要下载一个屏幕录制App将手机屏幕录制成视频(可能需要Root权限),然后导出到电脑,再转为Gif.今天就来教大家一 ...

  2. Android11灵敏度,Android 11即将上线!推出原生屏幕录制、触摸灵敏度等功能

    对于喜欢国产手机的用户而言,最期待的莫过于Android 11上线了.大家通过外媒已经了解到Android 11的消息了,Android 11被传的神乎其神,Android 11真的有那么厉害吗? A ...

  3. android 屏幕录制方案,Android录制屏幕的实现方法

    原文:Paul Kinlan 翻译:Agora.io 长久以来,我一直希望能够直接从Android屏幕上进行录制并将其编码为多种格式,以便将录制内容嵌入在任意位置,而不需要安装任何软件. 如今,我们已 ...

  4. Android opengles 实现触碰屏幕,根据运动轨迹画直线的功能

    Android opengles 实现触碰屏幕,根据运动轨迹画直线的功能 目录 引言 第一步,先自己学会绘制一条固定坐标的直线 第二步,动态的绘制一条直线 第三步,坐标转换 第四步,绘制多条直线 代码 ...

  5. android屏幕 录制检测,Android 录制屏幕的实现方法

    Android 录制屏幕的实现方法,长久以来,我一直希望能够直接从Android屏幕上进行录制并将其编码为多种格式,以便将录制内容嵌入在任意位置,而不需要安装任何软件. 如今,我们已经接近这个目标.C ...

  6. android手机录屏多少fps,如何在Android上以90fps或120fps的屏幕录制?

    [5G资讯网]Android智能手机每天都在开拓新的领域,应用程序也在以相同的速度发展.最近增加的90赫兹显示屏为智能手机上的更好游戏铺平了道路.在OnePlus 7T推出之前,90Hz显示屏仅限于游 ...

  7. android 屏幕录制代码,Android 录制屏幕的实现方法

    长久以来,我一直希望能够直接从Android屏幕上进行录制并将其编码为多种格式,以便将录制内容嵌入在任意位置,而不需要安装任何软件. 如今,我们已经接近这个目标.Chrome团队正在添加一种功能,可以 ...

  8. android 屏幕录制方案,Android录屏的三种解决方案

    本文总结三种用于安卓录屏的解决方案: adb shell命令screenrecord MediaRecorder, MediaProjection MediaProjection , MediaCod ...

  9. android 屏幕录制方案,Android录屏的三种方案

    本文总结三种用于安卓录屏的解决方案: adb shell命令screenrecord MediaRecorder, MediaProjection MediaProjection , MediaCod ...

最新文章

  1. shell编程系列7--shell中常用的工具find、locate、which、whereis
  2. 从零入门 FreeRTOS 操作系统之任务的概念
  3. jQuery 表格自动增加
  4. openjdk替换java_ubuntu中将java环境由安装版的openjdk替换为Oracle的jdk
  5. css 输入框 按钮 对齐,CSS让input button元素对齐的代码收集
  6. python install_[Python] Linux下python install
  7. 捷联惯导系统学习7.2(捷联惯导精对准 )
  8. java压缩图片thumbnails_Java压缩图片、减小图片文件体积大小,Thumbnails使用教程...
  9. VMware清理vmdk文件
  10. Katalon Studio:一款静候你使用的免费自动化测试工具
  11. 【mssql】SQL Server2012编程入门经典(第四版)(上) 读书笔记
  12. 秦汉考场科目三路线图_秦汉科目三考场考试攻略,附考场路线图
  13. 发票查验API,批量查验发票真伪
  14. 退出计算机控制如何恢复,在您退出大势至U盘禁用软件、电脑U口屏蔽软件之后如何取消U盘写保护功能、恢复向U盘复制文件的功能?...
  15. 433芯片的基本原理和对应优缺点理解
  16. 嵌入式新闻早班车-第16期
  17. 数据库mysql命令
  18. NSIS + QT 制作自定义界面安装包
  19. 立创EDA专业版,修改自带库中的元件
  20. 电控系统开发工作内容梳理

热门文章

  1. 电脑开机自检过程都有什么?
  2. 我彻底抛弃Windows,入坑MacBook
  3. [OpenCV4] 湖南大学数字图像处理实验1
  4. 怎么样给小孩取名字好听又准确?有它就可以搞定了
  5. php+中文分词scws+sphinx+mysql打造千万级数据全文搜索
  6. 【Android】自定义progressBar样式
  7. Windows 10下安装Elementary OS双系统
  8. 【云杂谈】之二《AT&T发布基于云存储的物联网产品》
  9. 4.0 ipu_soc,ipu_channel_t ,ipu_channel_params_t结构体详解
  10. 【Nexus】安装配置与使用