blob: 42c5205455caecfc480cd458e5b5f2a40b1505a4 [file] [log] [blame]
/*
* Copyright 2022 The Android Open Source Project
*
* 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 com.android.server.audio;
import static android.media.AudioManager.GET_DEVICES_OUTPUTS;
import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_4;
import static android.media.LoudnessCodecInfo.CodecMetadataType.CODEC_METADATA_TYPE_MPEG_D;
import static android.media.MediaFormat.KEY_AAC_DRC_EFFECT_TYPE;
import static android.media.MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION;
import static android.media.MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL;
import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_LARGE;
import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_MEDIUM;
import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_SMALL;
import static com.android.server.audio.LoudnessCodecHelper.SPL_RANGE_UNKNOWN;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.ILoudnessCodecUpdatesDispatcher;
import android.media.LoudnessCodecInfo;
import android.media.PlayerBase;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.server.audio.LoudnessCodecHelper.DeviceSplRange;
import com.android.server.audio.LoudnessCodecHelper.LoudnessCodecInputProperties;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RunWith(AndroidJUnit4.class)
@Presubmit
public class LoudnessCodecHelperTest {
private static final String TAG = "LoudnessCodecHelperTest";
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
private LoudnessCodecHelper mLoudnessHelper;
@Mock
private AudioService mAudioService;
@Mock
private ILoudnessCodecUpdatesDispatcher.Default mDispatcher;
private final int mInitialApcPiid = 1;
@Before
public void setUp() throws Exception {
mLoudnessHelper = new LoudnessCodecHelper(mAudioService);
when(mAudioService.getActivePlaybackConfigurations()).thenReturn(
getApcListForPiids(mInitialApcPiid));
when(mDispatcher.asBinder()).thenReturn(Mockito.mock(IBinder.class));
}
@Test
public void registerDispatcher_sendsInitialUpdateOnStart() throws Exception {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_4)));
verify(mDispatcher).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid), any());
}
@Test
public void unregisterDispatcher_noInitialUpdateOnStart() throws Exception {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.unregisterLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/false,
CODEC_METADATA_TYPE_MPEG_D)));
verify(mDispatcher, times(0)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
}
@Test
public void addCodecInfo_sendsInitialUpdateAfterStart() throws Exception {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_4)));
mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_D));
verify(mDispatcher, times(2)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
}
@Test
public void addCodecInfoForUnstartedPiid_noUpdateSent() throws Exception {
final int newPiid = 2;
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_4)));
mLoudnessHelper.addLoudnessCodecInfo(newPiid,
getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_D));
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
}
@Test
public void updateCodecParameters_updatesOnlyStartedPiids() throws Exception {
final int newPiid = 2;
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid,
List.of(getLoudnessInfo(/*mediaCodecHash=*/111, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_4)));
//does not trigger dispatch since active apc list does not contain newPiid
mLoudnessHelper.startLoudnessCodecUpdates(newPiid,
List.of(getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_D)));
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
// triggers dispatch for new active apc with newPiid
mLoudnessHelper.updateCodecParameters(getApcListForPiids(newPiid));
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(newPiid), any());
}
@Test
public void updateCodecParameters_noStartedPiids_noDispatch() throws Exception {
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.addLoudnessCodecInfo(mInitialApcPiid,
getLoudnessInfo(/*mediaCodecHash=*/222, /*isDownmixing=*/true,
CODEC_METADATA_TYPE_MPEG_D));
mLoudnessHelper.updateCodecParameters(getApcListForPiids(mInitialApcPiid));
// no dispatch since mInitialApcPiid was not started
verify(mDispatcher, times(0)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
}
@Test
public void updateCodecParameters_removedCodecInfo_noDispatch() throws Exception {
final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
mLoudnessHelper.removeLoudnessCodecInfo(mInitialApcPiid, info);
mLoudnessHelper.updateCodecParameters(getApcListForPiids(mInitialApcPiid));
// no second dispatch since codec info was removed for updates
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
}
@Test
public void updateCodecParameters_stoppedPiids_noDispatch() throws Exception {
final LoudnessCodecInfo info = getLoudnessInfo(/*mediaCodecHash=*/111,
/*isDownmixing=*/true, CODEC_METADATA_TYPE_MPEG_4);
mLoudnessHelper.registerLoudnessCodecUpdatesDispatcher(mDispatcher);
mLoudnessHelper.startLoudnessCodecUpdates(mInitialApcPiid, List.of(info));
mLoudnessHelper.stopLoudnessCodecUpdates(mInitialApcPiid);
mLoudnessHelper.updateCodecParameters(getApcListForPiids(mInitialApcPiid));
// no second dispatch since piid was removed for updates
verify(mDispatcher, times(1)).dispatchLoudnessCodecParameterChange(eq(mInitialApcPiid),
any());
}
@Test
public void checkParcelableBundle_forMpeg4CodecInputProperties() {
PersistableBundle loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
SPL_RANGE_SMALL).createLoudnessParameters();
assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
SPL_RANGE_SMALL).createLoudnessParameters();
assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
SPL_RANGE_MEDIUM).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
SPL_RANGE_MEDIUM).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
SPL_RANGE_LARGE).createLoudnessParameters();
assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
SPL_RANGE_LARGE).createLoudnessParameters();
assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/true,
SPL_RANGE_UNKNOWN).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(1, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_4, /*isDownmixing*/false,
SPL_RANGE_UNKNOWN).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(0, loudnessParameters.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
}
@Test
public void checkParcelableBundle_forMpegDCodecInputProperties() {
PersistableBundle loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
SPL_RANGE_SMALL).createLoudnessParameters();
assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
SPL_RANGE_SMALL).createLoudnessParameters();
assertEquals(64, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(3, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
SPL_RANGE_MEDIUM).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
SPL_RANGE_MEDIUM).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
SPL_RANGE_LARGE).createLoudnessParameters();
assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
SPL_RANGE_LARGE).createLoudnessParameters();
assertEquals(124, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/true,
SPL_RANGE_UNKNOWN).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
loudnessParameters = createInputProperties(
CODEC_METADATA_TYPE_MPEG_D, /*isDownmixing*/false,
SPL_RANGE_UNKNOWN).createLoudnessParameters();
assertEquals(96, loudnessParameters.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
assertEquals(6, loudnessParameters.getInt(KEY_AAC_DRC_EFFECT_TYPE));
}
private List<AudioPlaybackConfiguration> getApcListForPiids(int... piids) {
final ArrayList<AudioPlaybackConfiguration> apcList = new ArrayList<>();
AudioDeviceInfo[] devicesStatic = AudioManager.getDevicesStatic(GET_DEVICES_OUTPUTS);
assumeTrue(devicesStatic.length > 0);
int index = new Random().nextInt(devicesStatic.length);
Log.d(TAG, "Out devices number " + devicesStatic.length + ". Picking index " + index);
int deviceId = devicesStatic[index].getId();
for (int piid : piids) {
PlayerBase.PlayerIdCard idCard = Mockito.mock(PlayerBase.PlayerIdCard.class);
AudioPlaybackConfiguration apc =
new AudioPlaybackConfiguration(idCard, piid, /*uid=*/1, /*pid=*/1);
apc.handleStateEvent(PLAYER_UPDATE_DEVICE_ID, deviceId);
apcList.add(apc);
}
return apcList;
}
private static LoudnessCodecInputProperties createInputProperties(
int metadataType, boolean isDownmixing, @DeviceSplRange int splRange) {
return new LoudnessCodecInputProperties.Builder().setMetadataType(
metadataType).setIsDownmixing(isDownmixing).setDeviceSplRange(splRange).build();
}
private static LoudnessCodecInfo getLoudnessInfo(int mediaCodecHash, boolean isDownmixing,
int metadataType) {
LoudnessCodecInfo info = new LoudnessCodecInfo();
info.isDownmixing = isDownmixing;
info.mediaCodecHashCode = mediaCodecHash;
info.metadataType = metadataType;
return info;
}
}