List<Surface> outputSurfaces = new ArrayList<Surface>(2); outputSurfaces.add(reader.getSurface()); outputSurfaces.add(new Surface(mTextureView.getSurfaceTexture()));
2가지 surface를 담았는데 하나는 ImageReader의 surface이고 다른 하나는 textureView의 surface이다.
※ (의문점) textureView의 surface는 이미 프리뷰를 위해서 사용했었다. 그런데 왜 또 사용하는 것일까?
만약 createCaptureSession()의 첫 번째 인자로 ImageReader의 surface만 넘겨주면 사진 촬영 시 이미지가 뿌옇게 나오는 현상이 있다. 현재 카메라 session의 출력을 ImageReader의 surface에 출력하고 저장한 것인데 왜 그런지 모르겠다. 추측하자면 CaptureRequest가 순간적으로 전환되면서 더 이상 프리뷰에 대한 캡처가 아닌 순간적인 캡처만 빠르게 일어난 후 그 이미지 정보가 ImageReader로 넘어간 것이 아닐까 싶다.
신기한 것은 하나는 textureView의 surface와 같이 넘겨주면 화면에 보이는 그대로 사진이 찍힌다는 것이다. 구글 예제를 보면 createCaptureSession() 자체를 한 번만 호출하면서 프리뷰를 위한 textureView와 ImageReader를 모두 넘겨주고 session 정보를 멤버 변수로 관리하여 좀 더 명확히 이해가 된다. 따라서 이 부분은 구글 예제를 보고 이해하는 편이 낫다. 그렇지만 의문점은 역시 계속해서 궁금하다.
프리뷰와 다르게 촬영을 위해서 CaptureRequest를 생성할 때 CameraDevice.TEMPLATE_STILL_CAPTURE을 인자로 사용한다.
final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(reader.getSurface());
세션이 구성되면 다음과 같이 capture() 함수를 호출한다. 첫 번째 인자는 앞서 와 같이 설정한 CaptureRequest이다. 두 번째 인자는 촬영 후 콜백이다. 세 번째는 UI(프리뷰)를 계속해서 보여주기 위한 핸들러이다.
private Runnable mDelayPreviewRunnable = new Runnable() { @Override public void run() { startPreview(); } };
촬영된 사진의 저장은 다음과 같이 이뤄진다. 덮어쓰기 방지를 위해서 파일명을 현재 날짜와 시간 정보를 가져와 구성하도록 수정했다. ImageReader에 setOnImageAvailableListener를 설정하여 카메라 session으로부터 데이터를 획득하면 해당 정보를 바이트 정보로 가져온 후 파일로 쓰는 것이다.
※ (주의) 이렇게 생성된 jpg 파일은 기본 갤러리로 확인이 되지 않는다. 하지만 파일 탐색기로 저장 경로에 가면 촬영된 사진 파일이 있다. 이때 갤러리에 나타나지 않는 현상을 해결하기 위해서는 미디어 스캔을 해야한다. 미디어 스캔을 하려면 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE 인텐트 날려주면 된다. (참고 : https://brunch.co.kr/@mystoryg/96)
Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss");
final File file = new File(Environment.getExternalStorageDirectory() + "/DCIM", "pic_" + dateFormat.format(date) + ".jpg");
final Handler backgroudHandler = new Handler(thread.getLooper()); reader.setOnImageAvailableListener(readerListener, backgroudHandler);