[모두의 메모] 기능 구현 과정 - boostcampwm-2021/android05-boomerang GitHub Wiki
동영상 메모
사용자에게 보여주기
동영상 메모는 위와 같이 재생되고 있는 동영상 위에 사용자가 그림을 그리는 등 메모를 작성하면 그림이 그려진 동영상이 저장되고, 이를 공유할 수 있는 기능입니다.
이 기능을 세가지로 나누어서 생각해보면
- 동영상을 보여주기
- 동영상에 그림을 그리기
- 그려진 동영상을 저장
이렇게 나눌 수 있을 것입니다.
그런데 이 방법을 순서대로 따라가면 한 가지 문제점이 있습니다. 동영상을 재생하면서 사용자에게 보여주면, 그려진 그림은 어떻게 보여줄 수 있을까요? 이미 원본 동영상은 재생되고 있고, 거기에 그림을 그려 저장한다면 그림이 그려진 동영상은 저장되지만 그 그림을 사용자가 바로 확인할 수가 없습니다.
즉, 사용자는 자신이 어떤 그림을 그리는지 확인하지 못한다는 것입니다! 그리고 있는 그림을 저장이 되고 나서야 확인할 수 있다면, 아무래도 썩 좋은 사용자 경험은 아닐 것 같습니다.
그래서 순서를 바꿔서 생각하였습니다.
- 동영상에 그림을 그리기
- 동영상을 보여주기
- 그려진 동영상을 저장
이 방법에서 고민할 점은 어떤 동영상에 그림을 그려야 하는지 1번에서 어떻게 알 수 있을까요? 우선 그림을 그리기 전에 그릴 대상이 존재해야 할 것입니다. 그래서 처음에 과정을 하나 더하였습니다.
- 동영상을 재생하기 (사용자에게 보여지지는 않음!)
- 동영상에 그림을 그리기
- 동영상을 보여주기
- 그려진 동영상을 저장
이렇게 한다면 우선 동영상을 재생하고, 그곳에 그림을 그린 후 그 결과를 사용자에게 보여주게 될 것입니다. 그리고 그 동영상을 저장합니다.
동영상에 그림 그리기
그렇다면 동영상에 그림을 어떻게 그릴 수 있을까요? 처음에 떠오른 두 가지 방법이 있습니다.
- 동영상을 재생시키고 그 위에 Canvas로 그림을 그리기
- 프레임 단위로 나누어서 각 프레임마다 그림을 그리고 다시 합치기
첫 번째 방법이 더 찾기 쉬워보입니다. 하지만 그렇다면 어떻게 저장을 할 수 있을까요? 물론 MediaProjection 클래스를 사용하여 화면 전체를 녹화할 수 있습니다. 하지만 이 클래스는 SurfaceFlinger까지 거치고 난 뒤의 사용자에게 표시되는 화면 그 자체를 녹화해 상태바를 포함한 모든 UI가 녹화되는 기능입니다. 이렇게 한 뒤 동영상 부분만 잘라서도 저장할 수 있을 것 같지만, 더 좋은 방법이 있을 것 같아 두 번째 방법도 생각해보았습니다.
두 번째 방법은 동영상을 프레임 단위로 나누어 하나씩 그림을 그리고 다시 합쳐서 동영상으로 만들어 저장하는 것입니다. 이 방법을 사용하는 것은 꽤 어려워 보였습니다. 자료가 많지 않아서 어떻게 접근해야 하는지조차 알기 어려웠습니다.
AOSP 그래픽 문서, Google에서 Android 프레임워크의 그래픽 관련 기능들을 구현한 프로젝트 Grafika에서 큰 도움을 얻을 수 있었습니다.
간단히 정리하자면 이미지 스트림 생성자는 이미지 스트림 소비자에게 표시할 이미지의 버퍼를 Surface라는 클래스를 통해 전달할 수 있습니다.
동영상을 재생하는 MediaPlayer도 이미지 스트림 생성자 중 하나입니다. 이미지 스트림 소비자는 일반적으로 화면에 표시될 UI들을 합성하는 SurfaceFlinger지만 우리는 바로 사용자에게 보여주지 않고 그림을 그린 뒤에 보여주기로 하였습니다. 그렇다면 다른 이미지 스트림 소비자에 연결되어야 할 것입니다.
OpenGL ES를 사용하는 클래스들도 이미지 스트림 소비자가 될 수 있습니다. OpenGL ES는 그래픽 렌더링 라이브러리로, Canvas와 같은 다른 안드로이드 프레임워크와 다르게 하드웨어 가속 렌더링을 수행할 수 있어 성능에 큰 장점을 가지고 있습니다.
그래서 MediaPlayer를 OpenGL ES 렌더링을 수행할 수 있는 SurfaceTexture 클래스와 연결시켰습니다. 그리고 SurfaceTexture에서는 프레임에 그림을 더하고, 그 결과를 화면에 표시하고 다시 동영상으로 저장할 수 있을 것입니다.
정리
위 내용들을 간단히 정리하면 위 그림처럼 나타낼 수 있습니다. 모두의 메모는 이러한 과정을 거쳐서 작동됩니다.