1. 程式人生 > >Android5.0免Root截屏,錄屏

Android5.0免Root截屏,錄屏

manage png chan 編解碼 pixel make and plane file

http://blog.csdn.net/wds1181977/article/details/52174840

技術分享

MediaProjection介紹

MediaProjection可以用來捕捉屏幕,具體來說可以截取當前屏幕和錄制屏幕視頻。MediaProjection由MediaProjectionManager來管理和獲取。

使用步驟

首先獲取MediaProjectionManager,和其他的Manager一樣通過 Context.getSystemService() 傳入參數MEDIA_PROJECTION_SERVICE獲得實例。

接著調用MediaProjectionManager.createScreenCaptureIntent()彈出dialog詢問用戶是否授權應用捕捉屏幕,同時覆寫onActivityResult()獲取授權結果。

如果授權成功,通過MediaProjectionManager.getMediaProjection(int resultCode, Intent resultData)獲取MediaProjection實例,通過MediaProjection.createVirtualDisplay(String name, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler)創建VirtualDisplay實例。實際上在上述方法中傳入的surface參數,是真正用來截屏或者錄屏的。

截屏

截屏這裏用到ImageReader類,這個類的getSurface()方法獲取到surface直接傳入MediaProjection.createVirtualDisplay()方法中,此時就可以執行截取。通過ImageReader.acquireLatestImage()方法即可獲取當前屏幕的Image,經過簡單處理之後即可保存為Bitmap。

           private void startCapture() {

            mImageName = System.currentTimeMillis() + ".png";

            Log.e(TAG, "image name is : " + mImageName);

            Image image = mImageReader.acquireLatestImage();

            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;

            Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);

            bitmap.copyPixelsFromBuffer(buffer);

            bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);

            image.close();

            if (bitmap != null) {

                Log.e(TAG, "bitmap  create success ");

                try {

                    File fileFolder = new File(mImagePath);

                    if (!fileFolder.exists())

                        fileFolder.mkdirs();

                    File file = new File(mImagePath, mImageName);

                    if (!file.exists()) {

                        Log.e(TAG, "file create success ");

                        file.createNewFile();

                    }

                    FileOutputStream out = new FileOutputStream(file);

                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);

                    out.flush();

                    out.close();

                    Log.e(TAG, "file save success ");

                    Toast.makeText(this.getApplicationContext(), "截圖成功", Toast.LENGTH_SHORT).show();

                } catch (IOException e) {

                    Log.e(TAG, e.toString());

                    e.printStackTrace();

                }

            }

        }

錄屏

錄屏需要用到上篇博文中的MediaCadec,這個類將原始的屏幕數據編碼,在通過MediaMuxer分裝為mp4格式保存。MediaCodec.createInputSurface()獲取一個surface對象講起傳入MediaProjection.createVirtualDisplay()即可獲取屏幕原始多媒體數據,之後讀取MediaCodec編碼輸出數據經過MediaMuxer封裝處理為mp4即可播放,實現錄屏。

       private void recordVirtualDisplay() {//循環多去編解碼器輸出數據經過處理保存為mp4

        while (!mIsQuit.get()) {

            int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 10000);

            Log.i(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 encodeToVideoTrack(int index) {//輸出數據為mp4文件

        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 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.i(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);

    }

附錄參考

官方文檔

Android視頻錄制

Android 5.0截屏

Android錄屏代碼
本文Demo

Android5.0免Root截屏,錄屏