Feature: add screen recording

pull/209/head
xufuji456 3 years ago
parent e288658223
commit ec75aee0f8
  1. 39
      AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaProjectionController.kt
  2. 67
      AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaRecordController.kt

@ -6,6 +6,7 @@ import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.ImageReader import android.media.ImageReader
import android.media.projection.MediaProjection import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager import android.media.projection.MediaProjectionManager
@ -23,16 +24,23 @@ import java.lang.Exception
* @author frank * @author frank
* @date 2022/3/25 * @date 2022/3/25
*/ */
open class MediaProjectionController(screenshot: Boolean) { open class MediaProjectionController(type: Int) {
private var screenshot = true companion object {
private val requestCode = 1234 const val TYPE_SCREEN_SHOT = 0
const val TYPE_SCREEN_RECORD = 1
const val TYPE_SCREEN_LIVING = 2
}
private var type = TYPE_SCREEN_SHOT
private val requestCode = 123456
private var virtualDisplay: VirtualDisplay? = null
private var displayMetrics: DisplayMetrics? = null private var displayMetrics: DisplayMetrics? = null
private var mediaProjection: MediaProjection? = null private var mediaProjection: MediaProjection? = null
private var mediaProjectionManager: MediaProjectionManager? = null private var mediaProjectionManager: MediaProjectionManager? = null
init { init {
this.screenshot = screenshot this.type = type
} }
fun startScreenRecord(context: Context) { fun startScreenRecord(context: Context) {
@ -44,6 +52,13 @@ open class MediaProjectionController(screenshot: Boolean) {
(context as Activity).startActivityForResult(intent, requestCode) (context as Activity).startActivityForResult(intent, requestCode)
} }
fun createVirtualDisplay(surface: Surface) {
virtualDisplay = mediaProjection?.createVirtualDisplay("hello", displayMetrics!!.widthPixels,
displayMetrics!!.heightPixels, displayMetrics!!.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, null, null)
}
private fun saveBitmap(bitmap: Bitmap?, path: String) { private fun saveBitmap(bitmap: Bitmap?, path: String) {
if (path.isEmpty() || bitmap == null) if (path.isEmpty() || bitmap == null)
return return
@ -58,12 +73,6 @@ open class MediaProjectionController(screenshot: Boolean) {
} }
} }
private fun createVirtualDisplay(surface: Surface) {
mediaProjection?.createVirtualDisplay("hello", displayMetrics!!.widthPixels, displayMetrics!!.heightPixels,
displayMetrics!!.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, null, null)
}
private fun getBitmap() { private fun getBitmap() {
val imageReader = ImageReader.newInstance(displayMetrics!!.widthPixels, val imageReader = ImageReader.newInstance(displayMetrics!!.widthPixels,
displayMetrics!!.heightPixels, PixelFormat.RGBA_8888, 3) displayMetrics!!.heightPixels, PixelFormat.RGBA_8888, 3)
@ -76,7 +85,8 @@ open class MediaProjectionController(screenshot: Boolean) {
val pixelStride = planes[0].pixelStride val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride val rowStride = planes[0].rowStride
val rowPadding = rowStride - pixelStride * image.width val rowPadding = rowStride - pixelStride * image.width
val bitmap = Bitmap.createBitmap(image.width + rowPadding / pixelStride, image.height, Bitmap.Config.ARGB_8888) val bitmap = Bitmap.createBitmap(image.width + rowPadding / pixelStride,
image.height, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(buffer) bitmap.copyPixelsFromBuffer(buffer)
val filePath = Environment.getExternalStorageDirectory().path + "/hello.jpg" val filePath = Environment.getExternalStorageDirectory().path + "/hello.jpg"
saveBitmap(bitmap, filePath) saveBitmap(bitmap, filePath)
@ -87,14 +97,19 @@ open class MediaProjectionController(screenshot: Boolean) {
fun onActivityResult(resultCode: Int, data: Intent) { fun onActivityResult(resultCode: Int, data: Intent) {
mediaProjection = mediaProjectionManager?.getMediaProjection(resultCode, data) mediaProjection = mediaProjectionManager?.getMediaProjection(resultCode, data)
if (screenshot) { if (type == TYPE_SCREEN_SHOT) {
getBitmap() getBitmap()
} }
} }
fun getRequestCode(): Int {
return requestCode
}
fun stopScreenRecord() { fun stopScreenRecord() {
mediaProjection?.stop() mediaProjection?.stop()
virtualDisplay?.release()
} }
} }

@ -1,5 +1,7 @@
package com.frank.androidmedia.controller package com.frank.androidmedia.controller
import android.content.Context
import android.content.Intent
import android.hardware.Camera import android.hardware.Camera
import android.media.CamcorderProfile import android.media.CamcorderProfile
import android.media.MediaRecorder import android.media.MediaRecorder
@ -16,17 +18,22 @@ open class MediaRecordController {
private val usingProfile = true private val usingProfile = true
private var mCamera: Camera? = null private var mCamera: Camera? = null
private var mOutputPath: String? = null
private var mMediaRecorder: MediaRecorder? = null private var mMediaRecorder: MediaRecorder? = null
private var mMediaProjectionController: MediaProjectionController? = null
private fun initMediaRecord(surface: Surface, outputPath: String) { private fun initMediaRecord(videoSource: Int, surface: Surface?, outputPath: String) {
if (videoSource == MediaRecorder.VideoSource.CAMERA
|| videoSource == MediaRecorder.VideoSource.DEFAULT) {
// open camera // open camera
mCamera = Camera.open() mCamera = Camera.open()
mCamera!!.setDisplayOrientation(90) mCamera!!.setDisplayOrientation(90)
mCamera!!.unlock() mCamera!!.unlock()
// Note: pay attention to calling order
mMediaRecorder?.setCamera(mCamera) mMediaRecorder?.setCamera(mCamera)
}
// Note: pay attention to calling order
mMediaRecorder?.setVideoSource(videoSource)
mMediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC) mMediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
mMediaRecorder?.setVideoSource(MediaRecorder.VideoSource.CAMERA)
if (usingProfile) { if (usingProfile) {
// QUALITY_480P QUALITY_720P QUALITY_1080P QUALITY_2160P // QUALITY_480P QUALITY_720P QUALITY_1080P QUALITY_2160P
val profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P) val profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P)
@ -42,34 +49,74 @@ open class MediaRecordController {
mMediaRecorder?.setAudioSamplingRate(48000) mMediaRecorder?.setAudioSamplingRate(48000)
} }
mMediaRecorder?.setOutputFile(outputPath) mMediaRecorder?.setOutputFile(outputPath)
if (surface != null && (videoSource == MediaRecorder.VideoSource.CAMERA
|| videoSource == MediaRecorder.VideoSource.DEFAULT)) {
mMediaRecorder?.setPreviewDisplay(surface) mMediaRecorder?.setPreviewDisplay(surface)
} }
fun startRecord(surface: Surface, outputPath: String) {
if (mMediaRecorder == null) {
mMediaRecorder = MediaRecorder()
} }
initMediaRecord(surface, outputPath) private fun startRecordInternal(videoSource: Int, surface: Surface?, outputPath: String) {
initMediaRecord(videoSource, surface, outputPath)
try { try {
mMediaRecorder?.prepare() mMediaRecorder?.prepare()
if (videoSource == MediaRecorder.VideoSource.SURFACE) {
mMediaProjectionController?.createVirtualDisplay(mMediaRecorder?.surface!!)
}
mMediaRecorder?.start() mMediaRecorder?.start()
} catch (e: Exception) { } catch (e: Exception) {
Log.e("MediaRecorder", "start recorder error=$e") Log.e("MediaRecorder", "start recorder error=$e")
} }
}
/**
* Start record camera or screen
* @param videoSource the source of video, see {@link MediaRecorderVideoSource.CAMERA}
* or {@link MediaRecorder.VideoSource.SURFACE}
* @param surface the Surface to preview, when videoSource = MediaRecorderVideoSource.CAMERA
* @param context the Context of Activity
* @param outputPath the output path to save media file
*/
fun startRecord(videoSource: Int, surface: Surface?, context: Context, outputPath: String) {
if (mMediaRecorder == null) {
mMediaRecorder = MediaRecorder()
}
if (videoSource == MediaRecorder.VideoSource.SURFACE) {
mOutputPath = outputPath
mMediaProjectionController = MediaProjectionController(MediaProjectionController.TYPE_SCREEN_RECORD)
mMediaProjectionController?.startScreenRecord(context)
return
}
startRecordInternal(videoSource, surface, outputPath)
Log.i("MediaRecorder", "startRecord...") Log.i("MediaRecorder", "startRecord...")
} }
/**
* Stop recording camera or screen,
* and release everything.
*/
fun stopRecord() { fun stopRecord() {
if (mMediaRecorder != null) { if (mMediaRecorder != null) {
mMediaRecorder?.stop() mMediaRecorder?.stop()
mMediaRecorder?.reset() mMediaRecorder?.reset()
} }
mCamera?.stopPreview() if (mCamera != null) {
mCamera!!.stopPreview()
}
if (mMediaProjectionController != null) {
mMediaProjectionController!!.stopScreenRecord()
}
Log.i("MediaRecorder", "stopRecord...") Log.i("MediaRecorder", "stopRecord...")
} }
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
if (requestCode == mMediaProjectionController?.getRequestCode()) {
mMediaProjectionController?.onActivityResult(resultCode, data)
startRecordInternal(MediaRecorder.VideoSource.SURFACE, mMediaRecorder?.surface, mOutputPath!!)
}
}
fun release() { fun release() {
if (mMediaRecorder != null) { if (mMediaRecorder != null) {
mMediaRecorder?.release() mMediaRecorder?.release()

Loading…
Cancel
Save