From e288658223f8c41895d10682f49d8438d2784922 Mon Sep 17 00:00:00 2001 From: xufuji456 Date: Fri, 25 Mar 2022 21:09:03 +0800 Subject: [PATCH] Feature: add MediaProjectionController --- .../controller/MediaMuxController.kt | 23 ++-- .../controller/MediaProjectionController.kt | 100 ++++++++++++++++++ .../controller/MediaRecordController.kt | 10 +- 3 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaProjectionController.kt diff --git a/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaMuxController.kt b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaMuxController.kt index 840399d..fd27af6 100644 --- a/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaMuxController.kt +++ b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaMuxController.kt @@ -21,6 +21,7 @@ open class MediaMuxController { return false } var happenError = false + // 1、create MediaMuxer val mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) val mediaExtractor = MediaExtractor() try { @@ -28,7 +29,11 @@ open class MediaMuxController { var audioIndex = 0 var audioFormat: MediaFormat? = null var videoFormat: MediaFormat? = null + var finished = false + val bufferInfo = MediaCodec.BufferInfo() + val inputBuffer = ByteBuffer.allocate(2 * 1024 * 1024) mediaExtractor.setDataSource(inputPath) + // select track with mimetype for (i in 0 until mediaExtractor.trackCount) { val mediaFormat = mediaExtractor.getTrackFormat(i) val mimeType = mediaFormat.getString(MediaFormat.KEY_MIME) @@ -42,25 +47,24 @@ open class MediaMuxController { mediaExtractor.selectTrack(i) } } + // 2、add MediaFormat into track if (videoFormat != null) { mediaMuxer.addTrack(videoFormat) } if (audioFormat != null) { mediaMuxer.addTrack(audioFormat) } - - var finished = false - val bufferInfo = MediaCodec.BufferInfo() - val inputBuffer = ByteBuffer.allocate(2 * 1024 * 1024) - + // 3、start the muxer mediaMuxer.start() while (!finished) { + // demux media stream val sampleSize = mediaExtractor.readSampleData(inputBuffer, 0) if (sampleSize > 0) { bufferInfo.size = sampleSize bufferInfo.flags = mediaExtractor.sampleFlags bufferInfo.presentationTimeUs = mediaExtractor.sampleTime + // 4、call MediaMuxer to mux media stream if (mediaExtractor.sampleTrackIndex == videoIndex) { mediaMuxer.writeSampleData(videoIndex, inputBuffer, bufferInfo) } else if (mediaExtractor.sampleTrackIndex == audioIndex) { @@ -77,12 +81,9 @@ open class MediaMuxController { Log.e("MediaMuxController", "mux error=$e") happenError = true } finally { - try { - mediaMuxer.release() - mediaExtractor.release() - } catch (e1: Exception) { - Log.e("MediaMuxController", "release error=$e1") - } + // 5、release resource + mediaMuxer.release() + mediaExtractor.release() return !happenError } } diff --git a/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaProjectionController.kt b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaProjectionController.kt new file mode 100644 index 0000000..990e509 --- /dev/null +++ b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaProjectionController.kt @@ -0,0 +1,100 @@ +package com.frank.androidmedia.controller + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.PixelFormat +import android.hardware.display.DisplayManager +import android.media.ImageReader +import android.media.projection.MediaProjection +import android.media.projection.MediaProjectionManager +import android.os.Environment +import android.util.DisplayMetrics +import android.view.Surface +import android.view.WindowManager +import java.io.FileOutputStream +import java.lang.Exception + +/** + * Using MediaProjectionManager to screenshot, + * and using MediaProjection recording screen. + * + * @author frank + * @date 2022/3/25 + */ +open class MediaProjectionController(screenshot: Boolean) { + + private var screenshot = true + private val requestCode = 1234 + private var displayMetrics: DisplayMetrics? = null + private var mediaProjection: MediaProjection? = null + private var mediaProjectionManager: MediaProjectionManager? = null + + init { + this.screenshot = screenshot + } + + fun startScreenRecord(context: Context) { + val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + displayMetrics = DisplayMetrics() + windowManager.defaultDisplay.getMetrics(displayMetrics) + mediaProjectionManager = context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager + val intent = mediaProjectionManager?.createScreenCaptureIntent() + (context as Activity).startActivityForResult(intent, requestCode) + } + + private fun saveBitmap(bitmap: Bitmap?, path: String) { + if (path.isEmpty() || bitmap == null) + return + var outputStream: FileOutputStream? = null + try { + outputStream = FileOutputStream(path) + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) + } catch (e: Exception) { + + } finally { + outputStream?.close() + } + } + + 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() { + val imageReader = ImageReader.newInstance(displayMetrics!!.widthPixels, + displayMetrics!!.heightPixels, PixelFormat.RGBA_8888, 3) + createVirtualDisplay(imageReader.surface) + imageReader.setOnImageAvailableListener ({ reader: ImageReader -> + + val image = reader.acquireNextImage() + val planes = image.planes + val buffer = planes[0].buffer + val pixelStride = planes[0].pixelStride + val rowStride = planes[0].rowStride + val rowPadding = rowStride - pixelStride * image.width + val bitmap = Bitmap.createBitmap(image.width + rowPadding / pixelStride, image.height, Bitmap.Config.ARGB_8888) + bitmap.copyPixelsFromBuffer(buffer) + val filePath = Environment.getExternalStorageDirectory().path + "/hello.jpg" + saveBitmap(bitmap, filePath) + image.close() + imageReader.close() + }, null) + } + + fun onActivityResult(resultCode: Int, data: Intent) { + mediaProjection = mediaProjectionManager?.getMediaProjection(resultCode, data) + if (screenshot) { + getBitmap() + } + } + + + fun stopScreenRecord() { + mediaProjection?.stop() + } + +} \ No newline at end of file diff --git a/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaRecordController.kt b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaRecordController.kt index cb6693c..a2136e5 100644 --- a/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaRecordController.kt +++ b/AndroidMedia/src/main/java/com/frank/androidmedia/controller/MediaRecordController.kt @@ -35,11 +35,11 @@ open class MediaRecordController { mMediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) mMediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC) mMediaRecorder?.setVideoEncoder(MediaRecorder.VideoEncoder.H264) -// mMediaRecorder?.setVideoSize(640, 480) -// mMediaRecorder?.setVideoEncodingBitRate(5000 * 1000) -// mMediaRecorder?.setVideoFrameRate(25) -// mMediaRecorder?.setAudioChannels(2) -// mMediaRecorder?.setAudioSamplingRate(48000) + mMediaRecorder?.setVideoSize(640, 480) + mMediaRecorder?.setVideoEncodingBitRate(5000 * 1000) + mMediaRecorder?.setVideoFrameRate(25) + mMediaRecorder?.setAudioChannels(2) + mMediaRecorder?.setAudioSamplingRate(48000) } mMediaRecorder?.setOutputFile(outputPath) mMediaRecorder?.setPreviewDisplay(surface)