W tym wpisie przedstawiałem, w jaki sposób zrobić zrzuty ekranu aplikacji bez dodatkowych bibliotek. Wspomniałem tam, aby wykonać pełny zrzut ekranu trzeba skorzystać z MediaProjection. W tym artykule właśnie pokaże Ci dokładnie, w jaki sposób to wykonać. Przechwytywanie ekranu za pomocą MediaProjection nie jest zbyt skomplikowane. Za jego pomocą możemy nagrywać obraz, jak i wykonać zdjęcie, zatem zapraszam do lektury 🙂
1. Robienie screenshot’a.
Na początku przyda nam się funkcja, która zmierzy nam wymiary ekranu:
private void getSize(){ DisplayMetrics metrics = activity.getResources().getDisplayMetrics(); screenWidth = metrics.widthPixels; screenHeight = metrics.heightPixels; screenDensity = metrics.densityDpi; }
Metoda do pobierania obrazu:
@Override public void onImageAvailable(ImageReader reader) { Image image; image = reader.acquireLatestImage(); if (image == null) return; final Image.Plane[] planes = image.getPlanes(); final Buffer buffer = planes[0].getBuffer().rewind(); int pixelStride = planes[0].getPixelStride(); int rowStride = planes[0].getRowStride(); int rowPadding = rowStride - pixelStride * screenWidth; // create bitmap bitmap = Bitmap.createBitmap(screenWidth+rowPadding/pixelStride, screenHeight, Bitmap.Config.ARGB_8888); bitmap.copyPixelsFromBuffer(buffer); if (bitmap != null) { image.close(); clean(); } }
Główna metoda może wyglądać tak:
public void takescreenshot(){ mMediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE); if (mMediaProjection != null) { try { getSize(); mImageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 2); mMediaProjection.createVirtualDisplay( "Screenshot", screenWidth, screenHeight, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mImageReader.getSurface(), null, null ); mImageReader.setOnImageAvailableListener( this, null); } catch (Exception e) { e.printStackTrace(); } } else { Intent intent = mMediaProjectionManager.createScreenCaptureIntent(); activity.startActivityForResult(intent,REQUEST_CODE_CAPTURE_IMAGE); } }
Na początku musimy wywołać metodę getSystemService z parametrem MEDIA_PROJECTION_SERVICE, która zapewnia uzyskiwania pozwolenia na przechwytywanie obrazu. Dalej mMediaProjectionManager.createScreenCaptureIntent() jest stosowana, aby uruchomić intencji.
Metoda onActivityResult obsługuje wywołania zwrotne i może wyglądać następująco:
public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != REQUEST_CODE_CAPTURE_IMAGE) { Log.e(TAG, "Unknown request code: " + requestCode); return; } if (resultCode != Activity.RESULT_OK) { Log.e(TAG, "Screen Cast Permission Denied" ); return; } mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data); takescreenshot(); }
Po udostępnieniu mMediaProjection możliwe jest teraz utworzenie wirtualnego ekranu (złożonego ze wszystkich widocznych powierzchni) i wskazanie Androidowi, na którą powierzchnię chcesz skopiować ekran – w naszym przypadku jest to mImageReader.getSurface(). Do przechwytywania ekranu i zapisaniu do obrazu posłuży nam wywowałanie – mImageReader.setOnImageAvailableListener( this, null); Na koniec metoda czyszcząca:
public void clean(){ if (mMediaProjectionManager != null) { mMediaProjectionManager = null; } if (mMediaProjection != null) { mMediaProjection.stop(); } if (mImageReader != null) { mImageReader.setOnImageAvailableListener(null, null); mImageReader.close(); } if (bitmap != null) { bitmap = null; } }
2. Nagrywanie ekranu.
Do nagrywania obrazu skorzystamy z MediaRecorder. Metoda główna zmieni się na coś takiego:
public void startRecord(){ mMediaProjectionManager = (MediaProjectionManager) activity.getSystemService(Context.MEDIA_PROJECTION_SERVICE); if (mMediaProjection != null) { try { getSize(); mMediaRecorder = new MediaRecorder(); initRecorder(); mMediaProjection.createVirtualDisplay( "Recording Screen", screenWidth, screenHeight, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mMediaRecorder.getSurface(), null, null); mMediaRecorder.start(); } catch (Exception e) { e.printStackTrace(); } } else { Intent intent = mMediaProjectionManager.createScreenCaptureIntent(); activity.startActivityForResult(intent,REQUEST_CODE_CAPTURE_RECORDING); } }
Kod jest prosty i podobny do wcześniejszego zamiast ImageReader mamy MediaRecoder. Funkcja do zatrzymywania nagrywania może wyglądać tak:
public void stopRecord(){ if (mMediaRecorder != null) { try{ SystemClock.sleep(500); mMediaRecorder.stop(); mMediaRecorder.reset(); mMediaRecorder.release(); mMediaRecorder = null; }catch(RuntimeException e){ e.printStackTrace(); } }
Zastosowałem tutaj celowo SystemClock.sleep(500), ponieważ gdy rozpoczniemy i od razu zatrzymamy nagrywanie, dostaniemy wyjątek. Błąd spowodowany jest tym, że mediarecoder nie otrzymał żadnych informacji o ekranie.
Metoda do zainicjowania mediarecodera:
private void initRecorder() { try { mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mMediaRecorder.setOutputFile(getPath(".mp4")); mMediaRecorder.setVideoSize(screenWidth, screenHeight); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mMediaRecorder.setVideoEncodingBitRate(512 * 1000); mMediaRecorder.setVideoFrameRate(30); mMediaRecorder.prepare(); } catch (IOException e) { e.printStackTrace(); } }
Jeżeli nie chcesz nagrywać dźwięku, możesz usunąć wpisy związane z audio. Dźwięk jest zbierany przez mikrofon, także otoczenie też się zarejestruje, Ostatnia metoda, która pobiera lokalizację do zapisu pliku wideo:
private String getPath(String format ) { String path = Environment.getExternalStorageDirectory() + "/Recorded Screen/"; File dir = new File(path); if (!dir.exists()) { dir.mkdirs(); } String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault()).format(new Date()); file = path + date + format; return file; }
3. Podsumowanie.
Krótko i na temat. Powyższy kod można też zmienić i nie koniecznie musisz korzystać z tego rozwiązania, które przedstawiłem. Na przykład, zamiast MediaRecorder można zastosować MediaMuxer + MediaCodec, ale uznałem, że powyższy sposób jest prostszy i też skuteczny. Przykładową aplikację można znaleźć tutaj.