MediaProjection 和 MediaProjectionManager 是 Android 5.0 开放的屏幕截图与录制视频的接口,它可以用来对 surfaceview 进行截图,解决以前 surfaceview 截图出现黑屏的问题(就是问了这个问题来的,5.0以下没找到方法)。
MediaProjectionManager 是一个系统级的服务,可以通过 getSystemService 来获取实例:
MediaProjectionManager mMediaProjectionManager = (MediaProjectionManager)
getSystemService(Context.MEDIA_PROJECTION_SERVICE);
然后调用mMediaProjectionManager.createScreenCaptureIntent(),这里会弹出授权的dialog:
startActivityForResult(
mMediaProjectionManager.createScreenCaptureIntent(),
REQUEST_MEDIA_PROJECTION);
重写onActivityResult()方法,获取授权结果:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_MEDIA_PROJECTION) {
if (resultCode != Activity.RESULT_OK) {
Log.d(TAG, "User cancelled");
Toast.makeText(this, "User cancelled", Toast.LENGTH_SHORT).show();
return;
}
if (this == null) {
return;
}
Log.d(TAG, "Starting screen capture");
mResultCode = resultCode;
mResultData = data;
setUpMediaProjection();
setUpVirtualDisplay();
//继续执行截图或者录屏操作
// do somthing...
}
}
然后创建MediaProjection 的实例:
private void setUpMediaProjection() {
mMediaProjection = mMediaProjectionManager.getMediaProjection(mResultCode, mResultData);
}
接着创建VirtualDisplay实例:
private void setUpVirtualDisplay() {
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
mWindowWidth, mWindowHeight, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
}
此处是截图时的代码,所以上面surface参数传入的是: mImageReader.getSurface()
ImageReader 的创建:
ImageReader mImageReader = ImageReader.newInstance(mWindowWidth, mWindowHeight, 0x1, 2);
截图是通过ImageReader.acquireLatestImage() 获取当前屏幕的Image, 然后再获取Bitmap保存:
private void startCapture() {
mImageName = System.currentTimeMillis() + ".png";
Log.i(TAG, "image name is : " + mImageName);
Image image = mImageReader.acquireLatestImage();
if (image == null) {
Log.e(TAG, "image is null.");
return;
}
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * width;
mBitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
mBitmap.copyPixelsFromBuffer(buffer);
mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, width, height);
image.close();
if (mBitmap != null) {
// 保存或者显示...
}
}
录屏是通过MediaCadec, 初始化MediaCadec:
private void configureMedia() {
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", mWindowWidth, mWindowHeight);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 6000000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);
try {
mMediaCodec = MediaCodec.createEncoderByType("video/avc");
} catch (IOException e) {
e.printStackTrace();
}
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface = mMediaCodec.createInputSurface();
mMediaCodec.start();
}
创建VirtualDisplay实例:
mVirtualDisplay = mMediaProjection.createVirtualDisplay("record_screen",
mWindowWidth, mWindowHeight, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mSurface, null, null);
此处与截图方法出入的surface参数不一样。
开始录屏:
private void startRecord() {
try {
mMuxer = new MediaMuxer(mVideoPath + System.currentTimeMillis() + ".mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
recordVirtualDisplay();
} catch (IOException e) {
e.printStackTrace();
} finally {
release();
}
}
private void recordVirtualDisplay() {
while (!mIsQuit.get()) {
int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 10000);
Log.d(TAG, "dequeue output buffer index=" + index);
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {//后续输出格式变化
resetOutputFormat();
} else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {//请求超时
Log.d(TAG, "retrieving buffers time out!");
try {
// wait 10ms
Thread.sleep(10);
} catch (InterruptedException e) {
}
} else if (index >= 0) {//有效输出
if (!mMuxerStarted) {
throw new IllegalStateException("MediaMuxer dose not call addTrack(format) ");
}
encodeToVideoTrack(index);
mMediaCodec.releaseOutputBuffer(index, false);
}
}
}
private void resetOutputFormat() {
// should happen before receiving buffers, and should only happen once
if (mMuxerStarted) {
throw new IllegalStateException("output format already changed!");
}
MediaFormat newFormat = mMediaCodec.getOutputFormat();
Log.d(TAG, "output format changed.\n new format: " + newFormat.toString());
mVideoTrackIndex = mMuxer.addTrack(newFormat);
mMuxer.start();
mMuxerStarted = true;
Log.i(TAG, "started media muxer, videoIndex=" + mVideoTrackIndex);
}
private void encodeToVideoTrack(int index) {
ByteBuffer encodedData = mMediaCodec.getOutputBuffer(index);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {//是编码需要的特定数据,不是媒体数据
// The codec config data was pulled out and fed to the muxer when we got
// the INFO_OUTPUT_FORMAT_CHANGED status.
// Ignore it.
Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
mBufferInfo.size = 0;
}
if (mBufferInfo.size == 0) {
Log.d(TAG, "info.size == 0, drop it.");
encodedData = null;
} else {
Log.d(TAG, "got buffer, info: size=" + mBufferInfo.size
+ ", presentationTimeUs=" + mBufferInfo.presentationTimeUs
+ ", offset=" + mBufferInfo.offset);
}
if (encodedData != null) {
encodedData.position(mBufferInfo.offset);
encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);//写入
Log.i(TAG, "sent " + mBufferInfo.size + " bytes to muxer...");
}
}
private void recordStop() {
mIsQuit.set(true);
}
private void release() {
mIsQuit.set(false);
mMuxerStarted = false;
Log.i(TAG, " release() ");
if (mMediaCodec != null) {
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
if (mMuxer != null) {
mMuxer.stop();
mMuxer.release();
mMuxer = null;
}
}
这样录屏文件就会保存的指定路径。
部分代码参考他人,不喜可以喷~~
Github:Demo
Android MediaProjection截屏与录屏(ScreenCapture Library 的使用)(二)