273 lines
11 KiB
Java
273 lines
11 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2023 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 android.media;
|
||
|
|
||
|
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 android.annotation.CallbackExecutor;
|
||
|
import android.media.LoudnessCodecController.OnLoudnessCodecUpdateListener;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.PersistableBundle;
|
||
|
import android.os.RemoteException;
|
||
|
import android.util.Log;
|
||
|
|
||
|
import androidx.annotation.GuardedBy;
|
||
|
import androidx.annotation.NonNull;
|
||
|
|
||
|
import java.util.HashMap;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.Map.Entry;
|
||
|
import java.util.Objects;
|
||
|
import java.util.Set;
|
||
|
import java.util.concurrent.Executor;
|
||
|
|
||
|
/**
|
||
|
* Class used to handle the loudness related communication with the audio service.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public class LoudnessCodecDispatcher implements CallbackUtil.DispatcherStub {
|
||
|
private static final String TAG = "LoudnessCodecDispatcher";
|
||
|
|
||
|
private static final boolean DEBUG = false;
|
||
|
|
||
|
private static final class LoudnessCodecUpdatesDispatcherStub
|
||
|
extends ILoudnessCodecUpdatesDispatcher.Stub {
|
||
|
private static LoudnessCodecUpdatesDispatcherStub sLoudnessCodecStub;
|
||
|
|
||
|
private final CallbackUtil.LazyListenerManager<OnLoudnessCodecUpdateListener>
|
||
|
mLoudnessListenerMgr = new CallbackUtil.LazyListenerManager<>();
|
||
|
|
||
|
private final Object mLock = new Object();
|
||
|
|
||
|
@GuardedBy("mLock")
|
||
|
private final HashMap<OnLoudnessCodecUpdateListener, LoudnessCodecController>
|
||
|
mConfiguratorListener = new HashMap<>();
|
||
|
|
||
|
public static synchronized LoudnessCodecUpdatesDispatcherStub getInstance() {
|
||
|
if (sLoudnessCodecStub == null) {
|
||
|
sLoudnessCodecStub = new LoudnessCodecUpdatesDispatcherStub();
|
||
|
}
|
||
|
return sLoudnessCodecStub;
|
||
|
}
|
||
|
|
||
|
private LoudnessCodecUpdatesDispatcherStub() {}
|
||
|
|
||
|
@Override
|
||
|
public void dispatchLoudnessCodecParameterChange(int sessionId, PersistableBundle params) {
|
||
|
if (DEBUG) {
|
||
|
Log.d(TAG, "dispatchLoudnessCodecParameterChange for sessionId " + sessionId
|
||
|
+ " persistable bundle: " + params);
|
||
|
}
|
||
|
mLoudnessListenerMgr.callListeners(listener -> {
|
||
|
synchronized (mLock) {
|
||
|
mConfiguratorListener.computeIfPresent(listener, (l, lcConfig) -> {
|
||
|
// send the appropriate bundle for the user to update
|
||
|
if (lcConfig.getSessionId() == sessionId) {
|
||
|
lcConfig.mediaCodecsConsume(mcEntry -> {
|
||
|
final LoudnessCodecInfo codecInfo = mcEntry.getKey();
|
||
|
final String infoKey = Integer.toString(codecInfo.hashCode());
|
||
|
Bundle bundle = null;
|
||
|
if (params.containsKey(infoKey)) {
|
||
|
bundle = new Bundle(params.getPersistableBundle(infoKey));
|
||
|
}
|
||
|
|
||
|
final Set<MediaCodec> mediaCodecs = mcEntry.getValue();
|
||
|
for (MediaCodec mediaCodec : mediaCodecs) {
|
||
|
final String mediaCodecKey = Integer.toString(
|
||
|
mediaCodec.hashCode());
|
||
|
if (bundle == null && !params.containsKey(mediaCodecKey)) {
|
||
|
continue;
|
||
|
}
|
||
|
boolean canBreak = false;
|
||
|
if (bundle == null) {
|
||
|
// key was set by media codec hash to update single codec
|
||
|
bundle = new Bundle(
|
||
|
params.getPersistableBundle(mediaCodecKey));
|
||
|
canBreak = true;
|
||
|
}
|
||
|
bundle =
|
||
|
LoudnessCodecUpdatesDispatcherStub.filterLoudnessParams(
|
||
|
l.onLoudnessCodecUpdate(mediaCodec,
|
||
|
bundle));
|
||
|
|
||
|
if (!bundle.isDefinitelyEmpty()) {
|
||
|
try {
|
||
|
mediaCodec.setParameters(bundle);
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.w(TAG, "Cannot set loudness bundle on media codec "
|
||
|
+ mediaCodec);
|
||
|
}
|
||
|
}
|
||
|
if (canBreak) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
return lcConfig;
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
private static Bundle filterLoudnessParams(Bundle bundle) {
|
||
|
Bundle filteredBundle = new Bundle();
|
||
|
|
||
|
if (bundle.containsKey(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL)) {
|
||
|
filteredBundle.putInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL,
|
||
|
bundle.getInt(KEY_AAC_DRC_TARGET_REFERENCE_LEVEL));
|
||
|
}
|
||
|
if (bundle.containsKey(KEY_AAC_DRC_HEAVY_COMPRESSION)) {
|
||
|
filteredBundle.putInt(KEY_AAC_DRC_HEAVY_COMPRESSION,
|
||
|
bundle.getInt(KEY_AAC_DRC_HEAVY_COMPRESSION));
|
||
|
}
|
||
|
if (bundle.containsKey(KEY_AAC_DRC_EFFECT_TYPE)) {
|
||
|
filteredBundle.putInt(KEY_AAC_DRC_EFFECT_TYPE,
|
||
|
bundle.getInt(KEY_AAC_DRC_EFFECT_TYPE));
|
||
|
}
|
||
|
|
||
|
return filteredBundle;
|
||
|
}
|
||
|
|
||
|
void addLoudnessCodecListener(@NonNull CallbackUtil.DispatcherStub dispatcher,
|
||
|
@NonNull LoudnessCodecController configurator,
|
||
|
@NonNull @CallbackExecutor Executor executor,
|
||
|
@NonNull OnLoudnessCodecUpdateListener listener) {
|
||
|
Objects.requireNonNull(configurator);
|
||
|
Objects.requireNonNull(executor);
|
||
|
Objects.requireNonNull(listener);
|
||
|
|
||
|
mLoudnessListenerMgr.addListener(
|
||
|
executor, listener, "addLoudnessCodecListener",
|
||
|
() -> dispatcher);
|
||
|
synchronized (mLock) {
|
||
|
mConfiguratorListener.put(listener, configurator);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void removeLoudnessCodecListener(@NonNull LoudnessCodecController configurator) {
|
||
|
Objects.requireNonNull(configurator);
|
||
|
|
||
|
OnLoudnessCodecUpdateListener listenerToRemove = null;
|
||
|
synchronized (mLock) {
|
||
|
Iterator<Entry<OnLoudnessCodecUpdateListener, LoudnessCodecController>> iterator =
|
||
|
mConfiguratorListener.entrySet().iterator();
|
||
|
while (iterator.hasNext()) {
|
||
|
Entry<OnLoudnessCodecUpdateListener, LoudnessCodecController> e =
|
||
|
iterator.next();
|
||
|
if (e.getValue() == configurator) {
|
||
|
final OnLoudnessCodecUpdateListener listener = e.getKey();
|
||
|
iterator.remove();
|
||
|
listenerToRemove = listener;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (listenerToRemove != null) {
|
||
|
mLoudnessListenerMgr.removeListener(listenerToRemove,
|
||
|
"removeLoudnessCodecListener");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@NonNull private final IAudioService mAudioService;
|
||
|
|
||
|
/** @hide */
|
||
|
public LoudnessCodecDispatcher(@NonNull IAudioService audioService) {
|
||
|
mAudioService = Objects.requireNonNull(audioService);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void register(boolean register) {
|
||
|
try {
|
||
|
if (register) {
|
||
|
mAudioService.registerLoudnessCodecUpdatesDispatcher(
|
||
|
LoudnessCodecUpdatesDispatcherStub.getInstance());
|
||
|
} else {
|
||
|
mAudioService.unregisterLoudnessCodecUpdatesDispatcher(
|
||
|
LoudnessCodecUpdatesDispatcherStub.getInstance());
|
||
|
}
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void addLoudnessCodecListener(@NonNull LoudnessCodecController configurator,
|
||
|
@NonNull @CallbackExecutor Executor executor,
|
||
|
@NonNull OnLoudnessCodecUpdateListener listener) {
|
||
|
LoudnessCodecUpdatesDispatcherStub.getInstance().addLoudnessCodecListener(this,
|
||
|
configurator, executor, listener);
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void removeLoudnessCodecListener(@NonNull LoudnessCodecController configurator) {
|
||
|
LoudnessCodecUpdatesDispatcherStub.getInstance().removeLoudnessCodecListener(configurator);
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void startLoudnessCodecUpdates(int sessionId) {
|
||
|
try {
|
||
|
mAudioService.startLoudnessCodecUpdates(sessionId);
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void stopLoudnessCodecUpdates(int sessionId) {
|
||
|
try {
|
||
|
mAudioService.stopLoudnessCodecUpdates(sessionId);
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void addLoudnessCodecInfo(int sessionId, int mediaCodecHash,
|
||
|
@NonNull LoudnessCodecInfo mcInfo) {
|
||
|
try {
|
||
|
mAudioService.addLoudnessCodecInfo(sessionId, mediaCodecHash, mcInfo);
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public void removeLoudnessCodecInfo(int sessionId, @NonNull LoudnessCodecInfo mcInfo) {
|
||
|
try {
|
||
|
mAudioService.removeLoudnessCodecInfo(sessionId, mcInfo);
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public Bundle getLoudnessCodecParams(@NonNull LoudnessCodecInfo mcInfo) {
|
||
|
Bundle loudnessParams = null;
|
||
|
try {
|
||
|
loudnessParams = new Bundle(mAudioService.getLoudnessParams(mcInfo));
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
return loudnessParams;
|
||
|
}
|
||
|
}
|