레이블이 화면녹화인 게시물을 표시합니다. 모든 게시물 표시
레이블이 화면녹화인 게시물을 표시합니다. 모든 게시물 표시

2019년 3월 18일 월요일

안드로이드 화면녹화 예제 - MediaProjection

- 안드로이드 API 21 이상
- WRITE_EXTERNAL_STORAGE, RECORD_AUDIO 권한 필요.


1. 기본코드
- MediaRecorder
MediaRecorder mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setOutputFile(videoFile);
DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
mediaRecorder.setVideoSize(displayMetrics.widthPixels, displayMetrics.heightPixels);
mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mediaRecorder.setVideoEncodingBitRate(512 * 1000);
mediaRecorder.setVideoFrameRate(30);
try {
mediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}


- MediaProjection
final MediaRecorder screenRecorder = createRecorder();
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
MediaProjection.Callback callback = new MediaProjection.Callback() {
@Override
public void onStop() {
super.onStop();
if (screenRecorder != null) {
screenRecorder.stop();
screenRecorder.reset();
screenRecorder.release();
}
mediaProjection.unregisterCallback(this);
mediaProjection = null;
}
};
mediaProjection.registerCallback(callback, null);

DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
mediaProjection.createVirtualDisplay(
"sample",
displayMetrics.widthPixels, displayMetrics.heightPixels, displayMetrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
screenRecorder.getSurface(), null, null);

screenRecorder.start();


> createVirtualDisplay 과정에서 MediaRecorder의 Surface를 가져와 버추얼 디스플레이로 녹화를 한다.
> 녹화를 위해서 오디오와 저장소 사용 권한을 허용해야 한다.


- 전체코드
public class MainActivity extends AppCompatActivity {
    private static final String videoFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/MediaProjection.mp4";

    private static final int REQUEST_CODE_PERMISSIONS = 100;
    private static final int REQUEST_CODE_MediaProjection = 101;

    private MediaProjection mediaProjection;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 퍼미션 확인
        checkSelfPermission();
    }

    @Override
    protected void onDestroy() {
        // 녹화중이면 종료하기
        if (mediaProjection != null) {
            mediaProjection.stop();
        }
        super.onDestroy();
    }

    @Override
    protected void onActivityResult(int requestCode, final int resultCode, @Nullable final Intent data) {
        // 미디어 프로젝션 응답
        if (requestCode == REQUEST_CODE_MediaProjection && resultCode == RESULT_OK) {
            screenRecorder(resultCode, data);
            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    /**
     * 음성녹음, 저장소 퍼미션
     *
     * @return
     */
    public boolean checkSelfPermission() {
        String temp = "";
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            temp += Manifest.permission.RECORD_AUDIO + " ";
        }
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            temp += Manifest.permission.WRITE_EXTERNAL_STORAGE + " ";
        }

        if (TextUtils.isEmpty(temp) == false) {
            ActivityCompat.requestPermissions(this, temp.trim().split(" "), REQUEST_CODE_PERMISSIONS);
            return false;
        } else {
            initView();
            return true;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_PERMISSIONS: {
                int length = permissions.length;
                for (int i = 0; i < length; i++) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        // 퍼미션 동의가 1개라도 부족하면 화면을 초기화 하지 않음
                        return;
                    }
                    initView();
                }
                return;
            }
            default:
                return;
        }
    }

    /**
     * 뷰 초기화
     */
    private void initView() {
        findViewById(R.id.actionRec).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 미디어 프로젝션 요청
                startMediaProjection();
            }
        });
    }

    /**
     * 화면녹화
     *
     * @param resultCode
     * @param data
     */
    private void screenRecorder(int resultCode, @Nullable Intent data) {
        final MediaRecorder screenRecorder = createRecorder();
        MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
        MediaProjection.Callback callback = new MediaProjection.Callback() {
            @Override
            public void onStop() {
                super.onStop();
                if (screenRecorder != null) {
                    screenRecorder.stop();
                    screenRecorder.reset();
                    screenRecorder.release();
                }
                mediaProjection.unregisterCallback(this);
                mediaProjection = null;
            }
        };
        mediaProjection.registerCallback(callback, null);

        DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
        mediaProjection.createVirtualDisplay(
                "sample",
                displayMetrics.widthPixels, displayMetrics.heightPixels, displayMetrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                screenRecorder.getSurface(), null, null);

        final Button actionRec = findViewById(R.id.actionRec);
        actionRec.setText("STOP REC");
        actionRec.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                actionRec.setText("START REC");
                if (mediaProjection != null) {
                    mediaProjection.stop();

                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setDataAndType(Uri.parse(videoFile), "video/mp4");
                    startActivity(intent);
                }
            }
        });
        screenRecorder.start();
    }

    /**
     * 미디어 프로젝션 요청
     */
    private void startMediaProjection() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
            startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE_MediaProjection);
        }
    }

    /**
     * 미디어 레코더
     *
     * @return
     */
    private MediaRecorder createRecorder() {
        MediaRecorder mediaRecorder = new MediaRecorder();
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mediaRecorder.setOutputFile(videoFile);
        DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
        mediaRecorder.setVideoSize(displayMetrics.widthPixels, displayMetrics.heightPixels);
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mediaRecorder.setVideoEncodingBitRate(512 * 1000);
        mediaRecorder.setVideoFrameRate(30);
        try {
            mediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mediaRecorder;
    }
}



2. 샘플코드
소스코드
APK









2019년 2월 28일 목요일

안드로이드 adb 명령어



1. 패키지 검색
c:\> adb pm list packages -f
shell> adb pm list packages -f


- 특정 패키지 검색
pm list packages -f | grep com.google
 > grep를 사용할 때는 shell 에서 사용해야 합니다.(리눅스 명령어)


















2. 파일 또는 디렉토리 가져오기
c:\> adb pull [가져올 디렉토리경로 또는 파일경로] [저장할 디렉토리경로 또는 파일경로]

- APK 파일 가져오기
adb pull /data/app/com.google.android.youtube-1ENzPRhJvNSoW2e8QWVfpQ==/base.apk /apk/youtube.apk




















3. 실행중인 프로세스 확인
shell> ps -A

- 특정 프로세스 동작 확인
ps -A | grep com.google.android.youtube

Youtube 앱 실행 상태에서 확인


















4. android.intent.action.MAIN 액티비티 찾기
shell> pm dump [패키지명] | grep -A 1 MAIN

dump로 확인하는 방식이므로 출력결과를 확인해야 함.

- 예
pm dump com.google.android.youtube | grep -A 1 MAIN

























































5. 액티비티 실행
shell> am start -a android.intent.action.MAIN -n [액티비티 경로]

- 예1
am start -a android.intent.action.MAIN -n com.google.android.youtube/com.google.android.apps.youtube.app.WatchWhileActivity
















- 예2
adb shell am start -a android.intent.action.VIEW -d "https://www.google.com"
브라우저 실행




















6. 스크린캡쳐
녹화방지 코드가 삽입된 부분은 캡쳐되지 않습니다.

c:\> adb shell screencap -p [저장경로]
shell> screencap -p [저장경로]


- 예
adb shell screencap -p /sdcard/DCIM/screencap.png

SD카드 DCIM 디렉토리에 저장됩니다.














7. mp4 화면녹화
사운드는 녹화되지 않습니다. 녹화방지 코드가 삽입된 부분은 녹화되지 않습니다.

c:\> adb shell screenrecord [파일경로]
shell> screenrecord [파일경로]


- 예

adb shell screenrecord /sdcard/DCIM/screenrecord.mp4


SD카드 DCIM 디렉토리에 저장됩니다.

















8. 키 입력(keyevent, text, tap)
- 키 입력
c:\> adb shell input keyevent [KeyCode]
shell> input keyevent [KeyCode]

레퍼런스 : https://developer.android.com/reference/android/view/KeyEvent


- 텍스트 입력
c:\> adb shell input text [텍스트]
shell> input text [텍스트]
문장 단위는 ''로 묶어서 처리


- 클릭
c:\> adb shell input tap [x] [y]
shell> input tap [x] [y]


- 예
adb shell input keyevent KEYCODE_HOME
adb shell input keyevent 3

adb shell input text android
adb shell input text 'hello android'

adb shell input tap 100 100