Android TV 기기는 여러 개의 오디오 출력을 동시에 연결할 수 있습니다. TV 스피커, HDMI 커넥티드 홈 시네마, 블루투스 헤드폰 등 이러한 오디오 출력 장치는 다양한 오디오 기능을 지원할 수 있습니다. 인코딩 (Dolby Digital+, DTS 및 PCM), 샘플링 레이트 및 채널 등입니다. 예를 들어 HDMI로 연결된 TV는 다양한 인코딩을 지원합니다. 연결된 블루투스 헤드폰은 일반적으로 PCM만 지원합니다.
사용 가능한 오디오 기기 및 라우팅된 오디오 기기 목록도 변경될 수 있습니다. HDMI 기기를 핫플러그하거나 블루투스 헤드폰을 연결하거나 연결 해제하거나 사용자가 오디오 설정을 변경할 수 없습니다. 오디오 출력 기능은 앱이 미디어를 재생하고 있을 때도 바뀌지 않지만, 앱은 이러한 환경에 맞춰 적절하게 적응해야 합니다. 변경하고 계속 재생할 수 있으며 기능을 제공합니다 잘못된 오디오 형식을 출력하면 오류가 발생하거나 소리가 나지 않습니다.
앱은 동일한 콘텐츠를 여러 인코딩으로 출력할 수 있음 최적의 오디오 환경을 제공하기 위해 기능을 제공합니다 예를 들어 Dolby Digital로 인코딩된 오디오 스트림이 더 광범위하게 지원되는 PCM 오디오 스트림은 Dolby Digital이 지원되지 않을 때 선택됩니다. 내장된 Android 기기 목록 오디오 스트림을 PCM으로 변환하는 데 사용되는 디코더는 지원되는 미디어 형식
스트리밍 앱은 재생 시
최고의 가격의 AudioTrack
출력에서 지원되는 AudioFormat
오디오 기기입니다.
올바른 형식으로 트랙 만들기
앱에서 AudioTrack
를 만들어 재생을 시작하고
getRoutedDevice()
를 사용하여 소리를 재생할 기본 오디오 기기를 결정합니다.
이것은 예를 들어, 잡음이 클 수 없으며
라우팅된 장치와 오디오 기능을 결정합니다.
지원되는 인코딩 가져오기
사용
getAudioProfiles()
드림
(API 수준 31 이상) 또는
getEncodings()
(API 수준 23 이상)를 사용하여
기본 오디오 기기입니다.
지원되는 오디오 프로필 및 형식 확인하기
AudioProfile
사용
(API 수준 31 이상) 또는
isDirectPlaybackSupported()
(API 수준 29 이상)에서 지원되는 형식 조합을 확인합니다.
샘플링 레이트를 선택할 수 있습니다.
일부 Android 기기는
출력 오디오 장치에서 지원해야 합니다. 이러한 추가 형식은
isDirectPlaybackSupported()
를 통해 감지되었습니다. 이러한 경우에는 오디오 데이터가
출력 오디오 기기에서 지원하는 형식으로 다시 인코딩됩니다. 사용
isDirectPlaybackSupported()
- 원하는 형식 지원 여부를 올바르게 확인
getEncodings()
에서 반환된 목록에 없는 경우에도 마찬가지입니다.
예상 오디오 경로
Android 13 (API 수준 33)에는 예상 오디오 경로가 도입되었습니다. 다음과 같은 작업을 할 수 있습니다.
기기 오디오 속성 지원을 예상하고 활성 상태의
오디오 기기입니다. 이때
getDirectPlaybackSupport()
드림
현재 라우팅된 오디오에서 직접 재생이 지원되는지 확인
사용할 수 있습니다.
Kotlin
val format = AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_E_AC3) .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1) .setSampleRate(48000) .build() val attributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .build() if (AudioManager.getDirectPlaybackSupport(format, attributes) != AudioManager.DIRECT_PLAYBACK_NOT_SUPPORTED ) { // The format and attributes are supported for direct playback // on the currently active routed audio path } else { // The format and attributes are NOT supported for direct playback // on the currently active routed audio path }
자바
AudioFormat format = new AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_E_AC3) .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1) .setSampleRate(48000) .build(); AudioAttributes attributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .build(); if (AudioManager.getDirectPlaybackSupport(format, attributes) != AudioManager.DIRECT_PLAYBACK_NOT_SUPPORTED) { // The format and attributes are supported for direct playback // on the currently active routed audio path } else { // The format and attributes are NOT supported for direct playback // on the currently active routed audio path }
또는 직접 미디어에 지원되는 프로필을 쿼리할 수 있습니다. 오디오를 통해 재생할 수 있습니다. 프로필이 제외됩니다. 지원되지 않거나 Android에서 트랜스코딩될 수 있는 파일 프레임워크:
Kotlin
private fun findBestAudioFormat(audioAttributes: AudioAttributes): AudioFormat { val preferredFormats = listOf( AudioFormat.ENCODING_E_AC3, AudioFormat.ENCODING_AC3, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_DEFAULT ) val audioProfiles = audioManager.getDirectProfilesForAttributes(audioAttributes) val bestAudioProfile = preferredFormats.firstNotNullOf { format -> audioProfiles.firstOrNull { it.format == format } } val sampleRate = findBestSampleRate(bestAudioProfile) val channelMask = findBestChannelMask(bestAudioProfile) return AudioFormat.Builder() .setEncoding(bestAudioProfile.format) .setSampleRate(sampleRate) .setChannelMask(channelMask) .build() }
자바
private AudioFormat findBestAudioFormat(AudioAttributes audioAttributes) { Stream<Integer> preferredFormats = Stream.<Integer>builder() .add(AudioFormat.ENCODING_E_AC3) .add(AudioFormat.ENCODING_AC3) .add(AudioFormat.ENCODING_PCM_16BIT) .add(AudioFormat.ENCODING_DEFAULT) .build(); Stream<AudioProfile> audioProfiles = audioManager.getDirectProfilesForAttributes(audioAttributes).stream(); AudioProfile bestAudioProfile = (AudioProfile) preferredFormats.map(format -> audioProfiles.filter(profile -> profile.getFormat() == format) .findFirst() .orElseThrow(NoSuchElementException::new) ); Integer sampleRate = findBestSampleRate(bestAudioProfile); Integer channelMask = findBestChannelMask(bestAudioProfile); return new AudioFormat.Builder() .setEncoding(bestAudioProfile.getFormat()) .setSampleRate(sampleRate) .setChannelMask(channelMask) .build(); }
이 예에서 preferredFormats
는
인스턴스 AudioFormat
개 순서가 지정됨
가장 선호되는 숫자가 가장 먼저 표시되고 가장 선호되지 않는 마지막 항목이 표시됩니다.
getDirectProfilesForAttributes()
드림
지원되는 목록의
AudioProfile
객체를
현재 라우팅된 오디오 기기를
AudioAttributes
: 목록의
선호하는 AudioFormat
항목은 일치하는 항목이 지원될 때까지 반복됩니다.
AudioProfile
을(를) 찾았습니다. 이 AudioProfile
은 bestAudioProfile
로 저장됩니다.
최적의 샘플링 레이트와 채널 마스크는 bestAudioProfile
에서 결정됩니다.
마지막으로 적절한 AudioFormat
인스턴스가 생성됩니다
오디오 트랙 만들기
앱은 이 정보를 사용하여AudioTrack
기본 오디오 기기에서 지원하는 최고 품질의 AudioFormat
(선택한 콘텐츠에서만 사용 가능)
오디오 기기 변경사항 가로채기
오디오 기기 변경사항을 가로채서 반응하려면 앱은 다음을 실행해야 합니다.
- API 수준이 24 이상인 경우
OnRoutingChangedListener
드림 을 사용하여 오디오 기기 변경사항 (HDMI, 블루투스 등)을 모니터링할 수 있습니다. - API 수준 23의 경우
AudioDeviceCallback
드림 사용 가능한 오디오 기기 목록의 변경사항을 수신합니다. - API 수준 21 및 22의 경우 HDMI 플러그 이벤트 브로드캐스트의 추가 데이터를 사용합니다.
- 모니터링할
BroadcastReceiver
도 등록BluetoothDevice
상태 변경 API 23보다 낮은 기기의 경우AudioDeviceCallback
이(가) 아닌 경우 아직 지원되지 않습니다.
AudioTrack
의 오디오 기기 변경이 감지되면 앱이
업데이트된 오디오 기능을 확인하고 필요한 경우
다른 AudioFormat
로 AudioTrack
를 반환합니다. 더 높은 품질의 동영상 광고가
인코딩이 지원되거나 이전에 사용된 인코딩이
더 이상 지원되지 않습니다
샘플 코드
Kotlin
// audioPlayer is a wrapper around an AudioTrack // which calls a callback for an AudioTrack write error audioPlayer.addAudioTrackWriteErrorListener { // error code can be checked here, // in case of write error try to recreate the audio track restartAudioTrack(findDefaultAudioDeviceInfo()) } audioPlayer.audioTrack.addOnRoutingChangedListener({ audioRouting -> audioRouting?.routedDevice?.let { audioDeviceInfo -> // use the updated audio routed device to determine // what audio format should be used if (needsAudioFormatChange(audioDeviceInfo)) { restartAudioTrack(audioDeviceInfo) } } }, handler)
Java
// audioPlayer is a wrapper around an AudioTrack // which calls a callback for an AudioTrack write error audioPlayer.addAudioTrackWriteErrorListener(new AudioTrackPlayer.AudioTrackWriteError() { @Override public void audioTrackWriteError(int errorCode) { // error code can be checked here, // in case of write error try to recreate the audio track restartAudioTrack(findDefaultAudioDeviceInfo()); } }); audioPlayer.getAudioTrack().addOnRoutingChangedListener(new AudioRouting.OnRoutingChangedListener() { @Override public void onRoutingChanged(AudioRouting audioRouting) { if (audioRouting != null && audioRouting.getRoutedDevice() != null) { AudioDeviceInfo audioDeviceInfo = audioRouting.getRoutedDevice(); // use the updated audio routed device to determine // what audio format should be used if (needsAudioFormatChange(audioDeviceInfo)) { restartAudioTrack(audioDeviceInfo); } } } }, handler);