정보공간_1

[7기 강북 전소현] 안드로이드 오디오 출력 장치 제어 본문

IT 놀이터/Elite Member Tech & Talk

[7기 강북 전소현] 안드로이드 오디오 출력 장치 제어

알 수 없는 사용자 2015. 4. 18. 19:18

안녕하세요. 강북멤버십 23-1기 전소현입니다.

이번 게시물에서는 안드로이드의 오디오 출력 장치를 제어하는 방법에 대해 설명하겠습니다.

[이 게시물은 안드로이드 KitKat(4.4.2)을 기준으로 직접 분석한 내용을 정리한 것입니다.]

 

안드로이드에서 사용할 수 있는 오디오 입출력 장치는 아래 표와 같이 다양하게 있습니다.  

이렇게 상황에 따라 사용되는 오디오 입출력 장치가 나뉘어 있기 때문에 스마트폰을 통해 같은 일을 하더라도 사용되는 입출력 장치는 달라집니다.

예를 들어, 일반적인 경우 전화 통화 시에는 스마트폰의 수화기인 이어피스를 통해 소리가 출력되지만, 유선 헤드셋을 연결한 뒤 전화 통화를 할 때는 이어피스가 아닌 연결된 유선 헤드셋을 통해 소리가 출력됩니다.

 

안드로이드 오디오 시스템의 오디오 출력 장치 제어 구조는 아래 그림과 같습니다. 

외부 프레임워크에서 전달받은 입출력 장치의 변경, 어플리케이션에서 요청한 강제 설정 변경 등은 오디오 서비스를 통해서 오디오 정책 서비스로 전달되고, 어플리케이션에서 오디오 트랙을 시작하면 오디오 플링거 서비스를 통해 트랙 정보를 전달 받게 됩니다. 오디오 정책 HAL은 이렇게 외부에서 전달받은 다양한 정보를 바탕으로 오디오 출력 장치를 결정한 후 다시 오디오 정책 서비스의 콜백 함수를 호출하고, 콜백 함수에서는 오디오 플링거 서비스에 요청해서 하드웨어에 반영해 줍니다.

따라서 오디오 정책 서비스와 오디오 정책 HAL은 오디오 출력 장치 제어에서 가장 핵심이 되는 부분입니다.

 

그럼 이제 오디오 정책 서비스와 오디오 정책 HAL에서 출력 장치를 결정하는 방식에 대해 좀 더 자세히 알아보겠습니다.

출력 스트림을 안드로이드 시스템에서 소리를 내 보내는 통로에 비유한다면, 출력 장치는 그 통로의 끝이 어디인지를 나타냅니다. 출력 스트림에 따라 출력 장치의 변경이 없는 출력 스트림도 있고(하나의 출력 장치만 지원한다는 의미), 여러 가지 출력 장치를 지원하는 출력 스트림도 있습니다.

오디오 정책 서비스는 출력 장치를 선택하기 위해 다양한 정보를 사용하며, 여기에는 오디오 서비스를 통해 전달된 시스템 상태 정보, 사용자 설정, 그리고 현재 사용중인 오디오 스트림 타입 등이 있습니다.

다양한 출력 장치 선택 기준 중 오디오 스트림을 이용하여 출력 장치가 결정되는 방식에 대해 분석해 보겠습니다.

이는 AudioPolicyManagerBase.cpp getDevicesForStream() 함수를 통해 결정됩니다. 함수의 내용은 아래와 같습니다.

getDevicesForStream() 함수를 분석해보면 먼저 getStrategy() 함수를 통해 현재 출력되고 있는 오디오 스트림 타입의 전략(Strategy)을 얻습니다.

 

그럼 여기서 말한 전략(Strategy)은 무엇을 의미할까요?

안드로이드에서 각각의 소리는 어떤 오디오 스트림 타입이냐에 따라 출력 장치 (스피커, 이어피스, 이어폰, 블루투스 헤드셋 등)와 볼륨이 결정됩니다. 하지만 모든 오디오 스트림 타입이 각각 다른 다른 방식으로 출력 장치를 관리하지는 않고, 몇몇 스트림 타입은 출력되는 볼륨은 서로 다르지만 출력 장치를 관리하는 방식은 동일한 경우가 많습니다.

예를 들어, 벨소리에 사용하는 오디오 스트림 타입인 STREAM_RING과 알람을 위한 오디오 스트림 타입인 STREAM_ALARM은 서로 다른 볼륨을 사용하지만, 출력 장치를 선택하는 기준은 동일합니다.

이렇게 몇몇 스트림 타입이 그룹을 이루고 이를 기준으로 출력 장치가 제어됩니다. 오디오 정책 서비스에서 이러한 그룹을 전략(strategy)이라고 부르며, 현재 안드로이드에는 6개의 전략이 있습니다.

각각의 전략과 그에 속한 오디오 스트림 타입은 아래 표와 같습니다.

이렇게 출력되는 오디오 스트림 타입의 전략을 얻은 뒤에, getDeviceForStrategy() 함수를 통해 그 전략을 위한 출력장치를 얻습니다. 이 함수가 출력 장치를 선택하는 부분에서 가장 중요한 함수입니다.

getDeviceForStrategy() 함수는 아래와 같습니다. (모든 함수 내용을 담을 수 없어 일부만 담았습니다.)

 

getDeviceForStrategy() 함수는 인자로 전달된 전략에 따라 다른 코드가 실행되며, 전략 별로 정해진 출력 장치의 우선순위에 따라 차례로 검사하면서 우선순위가 높은 출력 장치를 우선적으로 사용하도록 설정되어 있습니다.

예를 들어, STRATEGY_MEDIA의 경우 유선 혹은 블루투스 헤드셋이 연결된 경우의 우선순위가 아무것도 연결되지 않은 일반적인 경우의 우선순위보다 높습니다. 따라서 헤드셋 연결 시에는 헤드셋으로, 헤드셋이 연결되지 않은 경우에는 일반 스피커가 출력 장치로 선택됩니다.

이렇게 우선순위에 맞춰 결정된 출력 장치를 getDevicesForStream()으로 돌려주어 해당 오디오 스트림 타입에 맞는 출력 장치를 결정하게 됩니다.

 

오늘 제가 설명한 오디오 스트림을 이용하여 출력 장치를 결정하는 방식 외에도 다양한 방식으로 출력 장치를 결정할 수 있습니다.

이러한 내용들은 안드로이드 프레임워크 소스의 hardware/libhardware_legacy/audio 하위의 소스 파일들에 정의되어 있으니 이 부분들을 참고하면 도움이 될 것 같습니다.

 

위 내용은 아래 자료를 참고하여 작성하였습니다.

 - 안드로이드 미디어 프레임워크 (김태연 외 4명 공저)