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 = http://www.ithao123.cn/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 = http://www.ithao123.cn/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 = http://www.ithao123.cn/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
Tags: Android public return null 接口
文章來源: