MediaProjection 캡처 정리에 이어서 녹화도 정리하려고 합니다.
(원래 한 게시글에 정리하고 싶었지만.. 너무 지저분해지는 것 같아서 나눴습니다ㅠㅠ)
이전 글과 중복되는 내용은 생략해서 정리하려고 해요..!
이해를 위해서 이전글을 확인해주시면 좋을 것 같습니다.
[안드로이드] MediaProjection 캡처 예제&정리 (ForegroundService, PermissionOnce)
1. 예제 다운로드
해당 예제는 MediaProjection 화면 캡처/녹화 가 포함되어있습니다.
https://github.com/bictoselfdev/MediaProjectionEx
2. MediaProjection 권한 받기
startActivityForResult 를 통해서 권한 요청을 합니다.
이때, prevIntentData / prevResultCode 를 통해 이전값을 재사용해서 최초 한번만 권한을 받도록 하였습니다.
fun screenRecording(activity: Activity, action: Action?) {
startRecordCompletedAction = action
if (prevIntentData != null) {
// If you have received permission even once, proceed without requesting permission
getMediaProjectionRecord(activity, prevResultCode, prevIntentData)
} else {
// permission request
projectionManager = activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
activity.startActivityForResult(projectionManager?.createScreenCaptureIntent(), mediaScreenRecord)
}
}
정상적으로 권한을 받았다면 MediaProjectionManager 를 통해서 MediaProjection 을 가져올 수 있습니다.
fun getMediaProjectionRecord(activity: Activity, resultCode: Int, intentData: Intent?) {
projectionRecord = projectionManager?.getMediaProjection(resultCode, intentData!!)
if (projectionRecord != null) {
prevIntentData = intentData
prevResultCode = resultCode
// Create virtualDisplay
createVirtualDisplayRecord(activity)
// MediaRecorder Start
if (virtualDisplayRecord != null) {
startRecording(activity)
}
}
}
3. Create Virtual Display
녹화 데이터를 가져오기 위해서 MediaRecorder 의 surface 를 사용합니다.
MediaRecorder 를 활용해서 녹화를 Start/Stop 하기 위해서 prepare() 가 선행되어야 합니다.
private fun createVirtualDisplayRecord(activity: Activity) {
val metrics = activity.resources?.displayMetrics!!
val density = metrics.densityDpi
val flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
width = metrics.widthPixels
height = metrics.heightPixels
// MediaRecorder Prepare
mediaRecorder = MediaRecorder()
prepareRecording(activity)
// MediaRecorder Surface rendering
virtualDisplayRecord = projectionRecord?.createVirtualDisplay(
"screenRecord", width, height, density, flags,
mediaRecorder?.surface, null, null
)
}
녹화 시 기본적인 설정들을 해줍니다.
이중에 저장되는 파일(setOutputFile)은 MediaStore 공용폴더에 저장되도록 하였습니다.
private fun prepareRecording(activity: Activity) {
fileDescriptor = createFile(activity) // Create file to save
mediaRecorder?.apply {
setOutputFile(fileDescriptor?.fileDescriptor)
setVideoSource(MediaRecorder.VideoSource.SURFACE)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setVideoEncodingBitRate(5 * 1024 * 1000)
setVideoFrameRate(30)
setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)
setVideoSize(width, height)
prepare()
}
}
fun createFile(activity: Activity): ParcelFileDescriptor? {
val contentValues = ContentValues()
val currentTime = Date(System.currentTimeMillis())
val currentTimeStamp = SimpleDateFormat("yyyyMMddHHmmss", Locale.KOREA).format(currentTime)
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "MediaProjectionEx$currentTimeStamp.mp4")
contentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
contentValues.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis());
val contentResolver = activity.contentResolver
val collectionUri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)
return contentResolver.openFileDescriptor(collectionUri!!, "w")
}
Virtual Display 를 정상적으로 생성하였고, MediaRecorder prepare 가 선행되었다면
언제든지 startRecording 을 호출하여 녹화를 시작할 수 있습니다.
저 같은 경우는 Virtual Display 가 생성되면 바로 시작하도록 하였습니다.
private fun startRecording(activity: Activity) {
if (mediaRecorder != null) {
try {
mediaRecorder?.start()
isRecording.value = true
startRecordCompletedAction?.run()
Toast.makeText(activity, "screenRecording...", Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
System.err.println("[MediaProjection] start error : $e")
}
}
}
stopRecording 을 호출하게 되면 미리 설정해둔 경로에 파일이 저장되는 것을 확인 할 수 있습니다.
fun stopRecording(activity: Activity, action: Action?) {
if (mediaRecorder != null) {
try {
mediaRecorder?.stop()
mediaRecorder?.reset()
virtualDisplayRecord?.release()
projectionRecord?.stop()
isRecording.value = false
fileDescriptor?.close()
action?.run()
Toast.makeText(activity, "stopRecording, File has been saved.", Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
System.err.println("[MediaProjection] start error : $e")
}
}
}
[Related Post]
0 comments: