public final class

DefaultAudioOffloadSupportProvider

extends java.lang.Object

implements DefaultAudioSink.AudioOffloadSupportProvider

 java.lang.Object

↳androidx.media3.exoplayer.audio.DefaultAudioOffloadSupportProvider

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-exoplayer:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Provides the AudioOffloadSupport capabilities for a Format and AudioAttributes.

Summary

Constructors
publicDefaultAudioOffloadSupportProvider()

Creates an instance.

publicDefaultAudioOffloadSupportProvider(Context context)

Creates an instance.

Methods
public AudioOffloadSupportgetAudioOffloadSupport(Format format, AudioAttributes audioAttributes)

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public DefaultAudioOffloadSupportProvider()

Creates an instance.

public DefaultAudioOffloadSupportProvider(Context context)

Creates an instance.

Parameters:

context: The context used to retrieve the AudioManager parameters for checking offload variable rate support.

Methods

public AudioOffloadSupport getAudioOffloadSupport(Format format, AudioAttributes audioAttributes)

Source

/*
 * Copyright 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 androidx.media3.exoplayer.audio;

import static androidx.media3.common.util.Assertions.checkNotNull;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;

/**
 * Provides the {@link AudioOffloadSupport} capabilities for a {@link Format} and {@link
 * AudioAttributes}.
 */
@UnstableApi
public final class DefaultAudioOffloadSupportProvider
    implements DefaultAudioSink.AudioOffloadSupportProvider {

  /** AudioManager parameters key for retrieving support of variable speeds during offload. */
  private static final String OFFLOAD_VARIABLE_RATE_SUPPORTED_KEY = "offloadVariableRateSupported";

  @Nullable private final Context context;

  /**
   * Whether variable speeds are supported during offload. If {@code null} then it has not been
   * attempted to retrieve value from {@link AudioManager}.
   */
  private @MonotonicNonNull Boolean isOffloadVariableRateSupported;

  /** Creates an instance. */
  public DefaultAudioOffloadSupportProvider() {
    this(/* context= */ null);
  }

  /**
   * Creates an instance.
   *
   * @param context The context used to retrieve the {@link AudioManager} parameters for checking
   *     offload variable rate support.
   */
  public DefaultAudioOffloadSupportProvider(@Nullable Context context) {
    this.context = context;
  }

  @Override
  public AudioOffloadSupport getAudioOffloadSupport(
      Format format, AudioAttributes audioAttributes) {
    checkNotNull(format);
    checkNotNull(audioAttributes);

    if (Util.SDK_INT < 29 || format.sampleRate == Format.NO_VALUE) {
      return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
    }

    // isOffloadVariableRateSupported is lazily-loaded instead of being initialized in
    // the constructor so that the platform will be queried from the playback thread.
    boolean isOffloadVariableRateSupported = isOffloadVariableRateSupported(context);

    @C.Encoding
    int encoding = MimeTypes.getEncoding(checkNotNull(format.sampleMimeType), format.codecs);
    if (encoding == C.ENCODING_INVALID
        || Util.SDK_INT < Util.getApiLevelThatAudioFormatIntroducedAudioEncoding(encoding)) {
      // Example: AudioFormat.ENCODING_OPUS is supported only from API 30.
      return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
    }

    int channelConfig = Util.getAudioTrackChannelConfig(format.channelCount);
    if (channelConfig == AudioFormat.CHANNEL_INVALID) {
      return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
    }

    AudioFormat audioFormat;
    try {
      audioFormat = Util.getAudioFormat(format.sampleRate, channelConfig, encoding);
    } catch (IllegalArgumentException e) {
      return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
    }

    if (Util.SDK_INT >= 31) {
      return Api31.getOffloadedPlaybackSupport(
          audioFormat,
          audioAttributes.getAudioAttributesV21().audioAttributes,
          isOffloadVariableRateSupported);
    }
    return Api29.getOffloadedPlaybackSupport(
        audioFormat,
        audioAttributes.getAudioAttributesV21().audioAttributes,
        isOffloadVariableRateSupported);
  }

  private boolean isOffloadVariableRateSupported(@Nullable Context context) {
    if (isOffloadVariableRateSupported != null) {
      return isOffloadVariableRateSupported;
    }

    if (context != null) {
      AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
      if (audioManager != null) {
        String offloadVariableRateSupportedKeyValue =
            audioManager.getParameters(/* keys= */ OFFLOAD_VARIABLE_RATE_SUPPORTED_KEY);
        isOffloadVariableRateSupported =
            offloadVariableRateSupportedKeyValue != null
                && offloadVariableRateSupportedKeyValue.equals(
                    OFFLOAD_VARIABLE_RATE_SUPPORTED_KEY + "=1");
      } else {
        isOffloadVariableRateSupported = false;
      }
    } else {
      isOffloadVariableRateSupported = false;
    }
    return isOffloadVariableRateSupported;
  }

  @RequiresApi(29)
  private static final class Api29 {
    private Api29() {}

    @DoNotInline
    public static AudioOffloadSupport getOffloadedPlaybackSupport(
        AudioFormat audioFormat,
        android.media.AudioAttributes audioAttributes,
        boolean isOffloadVariableRateSupported) {
      if (!AudioManager.isOffloadedPlaybackSupported(audioFormat, audioAttributes)) {
        return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
      }
      return new AudioOffloadSupport.Builder()
          .setIsFormatSupported(true)
          .setIsSpeedChangeSupported(isOffloadVariableRateSupported)
          .build();
    }
  }

  @RequiresApi(31)
  private static final class Api31 {
    private Api31() {}

    @DoNotInline
    public static AudioOffloadSupport getOffloadedPlaybackSupport(
        AudioFormat audioFormat,
        android.media.AudioAttributes audioAttributes,
        boolean isOffloadVariableRateSupported) {
      int playbackOffloadSupport =
          AudioManager.getPlaybackOffloadSupport(audioFormat, audioAttributes);
      if (playbackOffloadSupport == AudioManager.PLAYBACK_OFFLOAD_NOT_SUPPORTED) {
        return AudioOffloadSupport.DEFAULT_UNSUPPORTED;
      }
      AudioOffloadSupport.Builder audioOffloadSupport = new AudioOffloadSupport.Builder();
      // (b/191950723) Gapless is not supported pre-API 33 due to playback position
      // issue upon transition of gapless tracks
      boolean isGaplessSupported =
          Util.SDK_INT > 32
              && playbackOffloadSupport == AudioManager.PLAYBACK_OFFLOAD_GAPLESS_SUPPORTED;
      return audioOffloadSupport
          .setIsFormatSupported(true)
          .setIsGaplessSupported(isGaplessSupported)
          .setIsSpeedChangeSupported(isOffloadVariableRateSupported)
          .build();
    }
  }
}