[NDK] Colorspace Conversion 관련 삽질기

2019. 7. 4. 18:44Programming/Android

반응형

이상하게 요즘 삽질을 계속해서 하는 느낌이다. 이번에는 구글의 정책으로 인해 64bits를 지원하도록 NDK 라이브러리를 수정하는 과정에서, Colorspace Conversion의 문제로 삽질을 하게 됐다. 일단 현재 GPU를 사용하지 않고 Colorspace Conversion을 처리하기때문에 성능상에 문제가 있기는 하지만, 어느정도 안정적으로 처리하는 것 까지만 확인하고 삽질기를 작성하기로 했다.

2019년 8월 1일부터 64bits를 지원하지 않는 앱은, 구글 플레이에 업데이트 할 수 없다. NDK를 사용하지 않는다면 상관이 없겠지만, 영상 스트리밍과 관련된 기능을 사용하고 있었기 때문에 필요한 라이브러리를 ARM64-v8a 칩셋에 맞게끔 다시 빌드할 필요가 있었다. 대표적인 예가 바로 FFmpeg로, [삽질기록] RAW H.264(AVC)에 MP4컨테이너 씌우기 #2 / FFmpeg 빌드에서 얼마나 삽질을 했는지 알 수 있다.

 

FFmpeg의 경우 삽질을 많이 하기는 했지만 잘 알려진 라이브러리라서 크게 문제될 것은 없었다. 지금같은 상황에서 가장 곤란한 것은 역시, '잘 알려지지 않은 라이브러리'에서 발생하는 문제들이다. 사용하는 사람들도 많이 없어서 자료를 발견하기도 어려울뿐만 아니라, 코드가 라이브러리에 의존적으로 작성되어있으면 작성되어있을수록 난항을 겪을수밖에 없다. ^ ㅈ^) 누가 알았겠는가. 하드웨어 가속을 지원하기 위해서 OpenGL을 사용하는 게 아니라, [YUV2RGB, Pinknoise Production]을 사용하고 있을 줄이야. 가장 최신버전인 0.3을 다운받아서 소스를 까보면, 상당히 지저분하게 작성되어있다. yuv2rgb.h 파일 내부에는 yuv2rgb565_table외에도 yuv2bgr565_table도 선언되어있는데, 실제로 소스를 찾아보면 yuv2bgr565_table은 존재하지 않는다. ^ ㅈ^ 대체 뭐람...

 

여기에 한술 더 떠서 기본으로 라이브러리에서 제공되는 파일이 아닌, yuv420rgb8888.neon.S라는 어셈블리 파일이 사용되고 있는 것이 아닌가. 심지어 빌드 에러가 발생했다. 대체제를 찾으면서 github를 미친듯이 헤매다가 동일한 내용의 파일을 발견하기는 했지만 현재는 사용하는 사람이 없는지, ARM64-v8a용으로 컨버팅한 버전은 아무데도 없었다. 까짓거 인스트럭션 셋을 찾아서 컨버팅하면 되겠지, 라고 생각했지만 생각보다 간단한 작업이 아니었다. ARMv8 Instruction Set Overview.pdf, ARM 메뉴얼을 확인해보니 기존에 사용되던 VMOV, PUSH, VPUSH같은 명령어들이 대거 삭제된 것이다. ^ ㅈ^) 하긴, 명령어가 유지되거나 추가됐으면 갑자기 빌드에러가 날 일도 없었겠지. 인스트럭션 셋이 있으면 어떻게든 되겠거니 했지만, 단기간에 해결할 수는 없을 것 같았다.

 

일단은 동작하게끔 만들어보자, 라는 생각에 기존에 사용하던 YUV2RGB의 yuv420_2_rgb8888를 호출해봤다. 검은 화면만 나온다. 아무래도 YUV420 색상값이 ARGB8888로 정상 변환이 되지 않는 듯 했다. 혹시 포멧이 잘못된건가 싶어서 yuv420_2_rgb888, yuv420_2_rgb565등을 호출해봤더니 깨질지언정 영상을 처리하기는 하더라. 아무래도 yuv420_2_rgb8888의 문제가 아닐까 싶었다. ffmpegsws_scale을 이용하는 레거시 코드가 주석처리되어있길래 사용해봤지만, 이번에는 메모리를 잘못 참조해서 죽는 사태가 발생했다. ^ ㅈ^) 하루종일 YUV -> RGB 변환을 검색해보고, 코드를 작성하거나 라이브러리를 적용했다가 실패하기를 몇일째 반복했다. RGB와 YUV에 대한 설명이 필요한 분은 RGB와 YUV color format, 그림그리는 개발자 글을 참조하도록 하자. 잘 설명되어있다.

 

AndroidBitmap_lockPixels 함수를 호출한 뒤 얻어올 수 있는 *void를 조작하여, YUV420을 RGB8888로 변경하는 수식을 적용해봤더니 4분할 된 영상이 두 개가 출력되고 있었다. 심지어 그 와중에도 메모리를 잘못 참조하여 사망하는 경우가 다수 발생됐다. 아무래도 이대로면 사회초년생때 하나쯤 들어둬야 한다고 해서 가입했던, 암 보험금을 다음날 쯤 수령하러 갈 수 있겠거니 하고 생각할 찰나 TensorFlow의 example에 YUV420을 ARGB8888로 컨버팅해주는 함수를 사용하고 있는 걸 발견했다. TensorFlow의 example에서 사용하고 있던 yuv2rgb 라이브러리, github

 

일단 몇 번의 시행착오 끝에, 영상이 정상적으로 표시되는 걸 확인할 수 있었다.

일단 나의 경우에는 Bitmap을 SurfaceView에 바로 그려주는 형식도 아니었다. Bitmap의 컬러스페이스만 YUV420에서 ARGB8888로 변경하는 구조를 유지하지 않으면, 전체적인 어플리케이션의 구조를 변경해야했기 때문에 이런 식으로 처리를 했다. 일반적인 경우라면 OpenGL을 사용하여 컬러스페이스 컨버전을 끝낸 후, SurfaceView로 프레임을 그려주는 구조가 되지 않을까싶었다. 일단 영상이 나오는 것까지는 확인했으며, ARM64칩셋에서만 해당 코드가 동작하도록 수정한 뒤 천천히 최적화를 고민해볼 생각이다. 휴우 ' ㅅ')-3

 

* 이런식으로 컬러스페이스 컨버전을 할 경우 CPU만 사용하기 때문에, 처리속도가 문제가 될 소지가 있다. 주의하도록 하자.

반응형