Análisis de la reducción de sonido de mezcla de Android O

Prefacio

Se originó cuando la transmisión de voz de navegación y el reproductor de medios se mezclan, mientras se esté reproduciendo la transmisión de navegación, el volumen del reproductor de medios que aparece se baja de forma predeterminada. La dirección de seguimiento inicial es la consideración del mecanismo de enfoque de pato en AudioService , pero la situación real es que el encapsulado setwillPausewhenduck es solo un bit de bandera, y la aplicación no necesariamente llama. AudioService es la implementación de la interfaz de audio de AudioManager. Además de las llamadas de interfaz comunes, también hay un mecanismo de procesamiento de enfoque y monitoreo del estado de reproducción de audio.

análisis

Desde el seguimiento del flujo de código de AudioService, PlaybackActivityMonitor se inicializa en el método de construcción, que se explica por el nombre como un monitor de actividad de reproducción.

public AudioService(Context context) {
    ...
        //初始化PlaybackActivityMonitor的对象
        mPlaybackMonitor =
            new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
 
        mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
    ...
}

PlaybackActivityMonitor primero implementa las dos clases de interfaz PlayerDeathMonitor y PlayerFocusEnforcer del reproductor multimedia

, Actualmente solo necesito mencionar PlayerFocusEnforcer

Primero analice el proceso específico del método de interfaz duckPlayer

@Override
public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
    if (DEBUG) {
        Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d",
                winner.getClientUid(), loser.getClientUid()));
    }
    synchronized (mPlayerLock) {
        if (mPlayers.isEmpty()) {
            return true;
        }
        // check if this UID needs to be ducked (return false if not), and gather list of
        // eligible players to duck
        //确认当前UID的player是否需要被降音(若不需要降音则返回false),总起归纳有资格被降音player的列表AudioPlaybackConfiguration
        final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
        final ArrayList<AudioPlaybackConfiguration> apcsToDuck =
                new ArrayList<AudioPlaybackConfiguration>();
        while (apcIterator.hasNext()) {
            final AudioPlaybackConfiguration apc = apcIterator.next();
            if (!winner.hasSameUid(apc.getClientUid())
                    && loser.hasSameUid(apc.getClientUid())
                    && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
            {
                if (apc.getAudioAttributes().getContentType() ==
                        AudioAttributes.CONTENT_TYPE_SPEECH) {
                    // the player is speaking, ducking will make the speech unintelligible
                    // so let the app handle it instead
                    Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
                            + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
                            + " - SPEECH");
                    return false;
                } else if (ArrayUtils.contains(UNDUCKABLE_PLAYER_TYPES, apc.getPlayerType())) {//特殊定义排除不需要降音的palyer类型不做降音处理
                    Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
                            + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
                            + " due to type:"
                            + AudioPlaybackConfiguration.toLogFriendlyPlayerType(
                                    apc.getPlayerType()));
                    return false;
                }
                apcsToDuck.add(apc);//符合条件的player uid添加到
            }
        }
        // add the players eligible for ducking to the list, and duck them
        // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when
        //  players of the same uid start, they will be ducked by DuckingManager.checkDuck())
        mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);//最终在音频焦点中失败的uid的player被添加到降音列表,<<等待制裁...>>
                                                                  //下面还有一点需要注意的是,如果需要duck的列表为空时,相同uid的player开始播放时会先执行DuckingManager的checkDuck方法
    }
    return true;
}
La clase interna estática DuckingManager maneja la lógica que requiere agacharse
synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
    if (DEBUG) {  Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); }
    if (!mDuckers.containsKey(uid)) {
        mDuckers.put(uid, new DuckedApp(uid));
    }
    final DuckedApp da = mDuckers.get(uid);
    for (AudioPlaybackConfiguration apc : apcsToDuck) {
        da.addDuck(apc, false /*skipRamp*/);//满足条件Uid的player添加到AudioPlaybackConfiguration的list中
    }
}

Continuar rastreando hasta la clase interna DuckedApp

            // pre-conditions:
            //  * apc != null
            //  * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
//先决条件,audioplaybackconfiguration不为空,也就是说至少存在一个或以上的player在播放,而且还需处在播放状态
            void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
                final int piid = new Integer(apc.getPlayerInterfaceId());
                if (mDuckedPlayers.contains(piid)) {
                    if (DEBUG) { Log.v(TAG, "player piid:" + piid + " already ducked"); }
                    return;
                }
                try {
                    sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG));
                    apc.getPlayerProxy().applyVolumeShaper(//本文的核心处,就是降音曲线的调用
                            DUCK_VSHAPE,
                            skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
                    mDuckedPlayers.add(piid);
                } catch (Exception e) {
                    Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);
                }
            }

 

Declaración de inicialización de curva plana

private static final VolumeShaper.Configuration DUCK_VSHAPE =
        new VolumeShaper.Configuration.Builder()
            .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
            .setCurve(new float[] { 0.f, 1.f } /* times */,
                new float[] { 1.f, 0.2f  } /* volumes */)//被压低的播放器声音降低至0.2f
            .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
            .setDuration(MediaFocusControl.getFocusRampTimeMs(
                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
                        .build()))
            .build();

TODO

Siempre he querido asociar la relación con la curva de sonido de enrutamiento de las capas jni y hal. La capa jni todavía se puede notar directamente, pero el estado del enrutamiento de audio aún no ha encontrado el punto de obtención, así que continúe rastreando.

Supongo que te gusta

Origin blog.csdn.net/jeephao/article/details/108612076
Recomendado
Clasificación