我正在为我的应用程序编写屏幕录制功能。因此,我需要将视频mp4文件保存到外部存储器中。我的功能在API 29及以下版本上运行正常,但在API 32及以上版本上无法工作。请向我展示解决此问题的步骤。
我遵循了这个源代码:https://github.com/Truiton/ScreenCapture
MainActivity.kt
class MainActivity : AppCompatActivity() {
private var mScreenDensity = 0
private var mProjectionManager: MediaProjectionManager? = null
private var mMediaProjection: MediaProjection? = null
private var mVirtualDisplay: VirtualDisplay? = null
private var mMediaProjectionCallback: MediaProjectionCallback? = null
private var mMediaRecorder: MediaRecorder? = null
private lateinit var binding: ActivityMainBinding
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val metrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(metrics)
mScreenDensity = metrics.densityDpi
mMediaRecorder = MediaRecorder()
mProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
binding.toggle.setOnClickListener { v: View? ->
if ((ContextCompat.checkSelfPermission(
this@MainActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) + ContextCompat
.checkSelfPermission(
this@MainActivity,
Manifest.permission.RECORD_AUDIO
) + ContextCompat.checkSelfPermission(
this@MainActivity,
MANAGE_EXTERNAL_STORAGE
))
!= PackageManager.PERMISSION_GRANTED
) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this@MainActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) ||
ActivityCompat.shouldShowRequestPermissionRationale(
this@MainActivity,
Manifest.permission.RECORD_AUDIO
) || ActivityCompat.shouldShowRequestPermissionRationale(
this@MainActivity,
MANAGE_EXTERNAL_STORAGE
)
) {
binding.toggle.isChecked = false
Snackbar.make(
findViewById(android.R.id.content), R.string.label_permissions,
Snackbar.LENGTH_INDEFINITE
).setAction("ENABLE",
View.OnClickListener { v1: View? ->
ActivityCompat.requestPermissions(
this@MainActivity,
arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO,
MANAGE_EXTERNAL_STORAGE
),
REQUEST_PERMISSIONS
)
}).show()
} else {
ActivityCompat.requestPermissions(
this@MainActivity,
arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.RECORD_AUDIO,
MANAGE_EXTERNAL_STORAGE
),
REQUEST_PERMISSIONS
)
}
} else {
onToggleScreenShare(v)
}
}
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != REQUEST_CODE) {
Log.e(TAG, "Unknown request code: $requestCode")
return
}
if (resultCode != RESULT_OK) {
Toast.makeText(
this,
"Screen Cast Permission Denied", Toast.LENGTH_SHORT
).show()
binding.toggle.isChecked = false
return
}
mMediaProjectionCallback = MediaProjectionCallback()
mMediaProjection = mProjectionManager!!.getMediaProjection(resultCode, data!!)
mMediaProjection?.registerCallback(mMediaProjectionCallback, null)
mVirtualDisplay = createVirtualDisplay()
mMediaRecorder!!.start()
}
fun onToggleScreenShare(view: View?) {
if ((view as ToggleButton?)!!.isChecked) {
initRecorder()
shareScreen()
} else {
mMediaRecorder!!.stop()
mMediaRecorder!!.reset()
Log.v(TAG, "Stopping Recording")
stopScreenSharing()
}
}
private fun shareScreen() {
if (mMediaProjection == null) {
startActivityForResult(mProjectionManager!!.createScreenCaptureIntent(), REQUEST_CODE)
return
}
mVirtualDisplay = createVirtualDisplay()
mMediaRecorder!!.start()
}
private fun createVirtualDisplay(): VirtualDisplay {
return mMediaProjection!!.createVirtualDisplay(
"MainActivity",
DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mMediaRecorder!!.surface, null /*Callbacks*/, null /*Handler*/
)
}
@SuppressLint("SimpleDateFormat")
private fun initRecorder() {
try {
mMediaRecorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
mMediaRecorder!!.setVideoSource(MediaRecorder.VideoSource.SURFACE)
mMediaRecorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
mMediaRecorder!!.setOutputFile(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.toString() + "/" + "Prank_Record_" + SimpleDateFormat("dd-MM-yyyy-hh_mm_ss").format(
Date()
) + ".mp4"
)
mMediaRecorder!!.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT)
mMediaRecorder!!.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
mMediaRecorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
mMediaRecorder!!.setVideoEncodingBitRate(512 * 1000)
mMediaRecorder!!.setVideoFrameRate(30)
val rotation = windowManager.defaultDisplay.rotation
val orientation = ORIENTATIONS[rotation + 90]
mMediaRecorder!!.setOrientationHint(orientation)
mMediaRecorder!!.prepare()
} catch (e: IOException) {
e.printStackTrace()
}
}
private inner class MediaProjectionCallback : MediaProjection.Callback() {
override fun onStop() {
if (binding.toggle.isChecked) {
binding.toggle.isChecked = false
mMediaRecorder!!.stop()
mMediaRecorder!!.reset()
Log.v(TAG, "Recording Stopped")
}
mMediaProjection = null
stopScreenSharing()
}
}
private fun stopScreenSharing() {
if (mVirtualDisplay == null) {
return
}
mVirtualDisplay!!.release()
//mMediaRecorder.release(); //If used: mMediaRecorder object cannot
// be reused again
destroyMediaProjection()
}
public override fun onDestroy() {
super.onDestroy()
destroyMediaProjection()
}
private fun destroyMediaProjection() {
if (mMediaProjection != null) {
mMediaProjection!!.unregisterCallback(mMediaProjectionCallback)
mMediaProjection!!.stop()
mMediaProjection = null
}
Log.i(TAG, "MediaProjection Stopped")
}
@RequiresApi(Build.VERSION_CODES.R)
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
REQUEST_PERMISSIONS -> {
if (true) {
onToggleScreenShare(binding.toggle)
} else {
binding.toggle.isChecked = false
Snackbar.make(
findViewById(android.R.id.content), R.string.label_permissions,
Snackbar.LENGTH_INDEFINITE
).setAction("ENABLE",
View.OnClickListener {
val intent = Intent()
intent.action = Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.data = Uri.parse("package:$packageName")
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
startActivity(intent)
}).show()
}
return
}
}
}
companion object {
private const val TAG = "MainActivity"
private const val REQUEST_CODE = 1000
private const val DISPLAY_WIDTH = 720
private const val DISPLAY_HEIGHT = 1280
private val ORIENTATIONS = SparseIntArray()
private const val REQUEST_PERMISSIONS = 10
init {
ORIENTATIONS.append(Surface.ROTATION_0, 90)
ORIENTATIONS.append(Surface.ROTATION_90, 0)
ORIENTATIONS.append(Surface.ROTATION_180, 270)
ORIENTATIONS.append(Surface.ROTATION_270, 180)
}
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />