| // Copyright 2017 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package dev.cobalt.media; |
| |
| import static dev.cobalt.media.Log.TAG; |
| |
| import android.annotation.TargetApi; |
| import android.content.Context; |
| import android.media.AudioDeviceInfo; |
| import android.media.AudioManager; |
| import android.os.Build; |
| import dev.cobalt.util.Log; |
| import dev.cobalt.util.UsedByNative; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** Creates and destroys AudioTrackBridge and handles the volume change. */ |
| public class AudioOutputManager implements CobaltMediaSession.UpdateVolumeListener { |
| private List<AudioTrackBridge> audioTrackBridgeList; |
| private Context context; |
| |
| public AudioOutputManager(Context context) { |
| this.context = context; |
| audioTrackBridgeList = new ArrayList<AudioTrackBridge>(); |
| } |
| |
| @Override |
| public void onUpdateVolume(float gain) { |
| for (AudioTrackBridge audioTrackBridge : audioTrackBridgeList) { |
| audioTrackBridge.setVolume(gain); |
| } |
| } |
| |
| @SuppressWarnings("unused") |
| @UsedByNative |
| AudioTrackBridge createAudioTrackBridge( |
| int sampleType, int sampleRate, int channelCount, int framesPerChannel) { |
| AudioTrackBridge audioTrackBridge = |
| new AudioTrackBridge(sampleType, sampleRate, channelCount, framesPerChannel); |
| if (!audioTrackBridge.isAudioTrackValid()) { |
| Log.e(TAG, "AudioTrackBridge has invalid audio track"); |
| return null; |
| } |
| audioTrackBridgeList.add(audioTrackBridge); |
| return audioTrackBridge; |
| } |
| |
| @SuppressWarnings("unused") |
| @UsedByNative |
| void destroyAudioTrackBridge(AudioTrackBridge audioTrackBridge) { |
| audioTrackBridge.release(); |
| audioTrackBridgeList.remove(audioTrackBridge); |
| } |
| |
| /** Returns the maximum number of HDMI channels. */ |
| @SuppressWarnings("unused") |
| @UsedByNative |
| int getMaxChannels() { |
| // The aac audio decoder on this platform will switch its output from 5.1 |
| // to stereo right before providing the first output buffer when |
| // attempting to decode 5.1 input. Since this heavily violates invariants |
| // of the shared starboard player framework, disable 5.1 on this platform. |
| // It is expected that we will be able to resolve this issue with Xiaomi |
| // by Android P, so only do this workaround for SDK versions < 27. |
| if (android.os.Build.MODEL.equals("MIBOX3") && android.os.Build.VERSION.SDK_INT < 27) { |
| return 2; |
| } |
| |
| if (Build.VERSION.SDK_INT >= 23) { |
| return getMaxChannelsV23(); |
| } |
| return 2; |
| } |
| |
| /** Returns the maximum number of HDMI channels for API 23 and above. */ |
| @TargetApi(23) |
| private int getMaxChannelsV23() { |
| AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); |
| AudioDeviceInfo[] deviceInfos = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); |
| int maxChannels = 2; |
| for (AudioDeviceInfo info : deviceInfos) { |
| int type = info.getType(); |
| if (type == AudioDeviceInfo.TYPE_HDMI || type == AudioDeviceInfo.TYPE_HDMI_ARC) { |
| int[] channelCounts = info.getChannelCounts(); |
| if (channelCounts.length == 0) { |
| // An empty array indicates that the device supports arbitrary channel masks. |
| return 8; |
| } |
| for (int count : channelCounts) { |
| maxChannels = Math.max(maxChannels, count); |
| } |
| } |
| } |
| return maxChannels; |
| } |
| } |