| /* |
| * Copyright 2017 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.google.android.exoplayer2.audio; |
| |
| import android.os.Bundle; |
| import androidx.annotation.DoNotInline; |
| import androidx.annotation.IntDef; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.RequiresApi; |
| import com.google.android.exoplayer2.Bundleable; |
| import com.google.android.exoplayer2.C; |
| import com.google.android.exoplayer2.util.Util; |
| import java.lang.annotation.Documented; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.reflect.Method; |
| |
| /** |
| * Attributes for audio playback, which configure the underlying platform {@link |
| * android.media.AudioTrack}. |
| * |
| * <p>To set the audio attributes, create an instance using the {@link Builder} and either pass it |
| * to the player or send a message of type {@code Renderer#MSG_SET_AUDIO_ATTRIBUTES} to the audio |
| * renderers. |
| * |
| * <p>This class is based on {@link android.media.AudioAttributes}, but can be used on all supported |
| * API versions. |
| */ |
| public final class AudioAttributes implements Bundleable { |
| |
| public static final AudioAttributes DEFAULT = new Builder().build(); |
| |
| /** Builder for {@link AudioAttributes}. */ |
| public static final class Builder { |
| |
| private @C.AudioContentType int contentType; |
| private @C.AudioFlags int flags; |
| private @C.AudioUsage int usage; |
| private @C.AudioAllowedCapturePolicy int allowedCapturePolicy; |
| private @C.SpatializationBehavior int spatializationBehavior; |
| |
| /** |
| * Creates a new builder for {@link AudioAttributes}. |
| * |
| * <p>By default the content type is {@link C#CONTENT_TYPE_UNKNOWN}, usage is {@link |
| * C#USAGE_MEDIA}, capture policy is {@link C#ALLOW_CAPTURE_BY_ALL} and no flags are set. |
| */ |
| public Builder() { |
| contentType = C.CONTENT_TYPE_UNKNOWN; |
| flags = 0; |
| usage = C.USAGE_MEDIA; |
| allowedCapturePolicy = C.ALLOW_CAPTURE_BY_ALL; |
| spatializationBehavior = C.SPATIALIZATION_BEHAVIOR_AUTO; |
| } |
| |
| /** @see android.media.AudioAttributes.Builder#setContentType(int) */ |
| public Builder setContentType(@C.AudioContentType int contentType) { |
| this.contentType = contentType; |
| return this; |
| } |
| |
| /** @see android.media.AudioAttributes.Builder#setFlags(int) */ |
| public Builder setFlags(@C.AudioFlags int flags) { |
| this.flags = flags; |
| return this; |
| } |
| |
| /** @see android.media.AudioAttributes.Builder#setUsage(int) */ |
| public Builder setUsage(@C.AudioUsage int usage) { |
| this.usage = usage; |
| return this; |
| } |
| |
| /** See {@link android.media.AudioAttributes.Builder#setAllowedCapturePolicy(int)}. */ |
| public Builder setAllowedCapturePolicy(@C.AudioAllowedCapturePolicy int allowedCapturePolicy) { |
| this.allowedCapturePolicy = allowedCapturePolicy; |
| return this; |
| } |
| |
| // TODO[b/190759307] Update javadoc to link to AudioAttributes.Builder#setSpatializationBehavior |
| // once compile SDK target is set to 32. |
| /** See AudioAttributes.Builder#setSpatializationBehavior(int). */ |
| public Builder setSpatializationBehavior(@C.SpatializationBehavior int spatializationBehavior) { |
| this.spatializationBehavior = spatializationBehavior; |
| return this; |
| } |
| |
| /** Creates an {@link AudioAttributes} instance from this builder. */ |
| public AudioAttributes build() { |
| return new AudioAttributes( |
| contentType, flags, usage, allowedCapturePolicy, spatializationBehavior); |
| } |
| } |
| |
| public final @C.AudioContentType int contentType; |
| public final @C.AudioFlags int flags; |
| public final @C.AudioUsage int usage; |
| public final @C.AudioAllowedCapturePolicy int allowedCapturePolicy; |
| public final @C.SpatializationBehavior int spatializationBehavior; |
| |
| @Nullable private android.media.AudioAttributes audioAttributesV21; |
| |
| private AudioAttributes( |
| @C.AudioContentType int contentType, |
| @C.AudioFlags int flags, |
| @C.AudioUsage int usage, |
| @C.AudioAllowedCapturePolicy int allowedCapturePolicy, |
| @C.SpatializationBehavior int spatializationBehavior) { |
| this.contentType = contentType; |
| this.flags = flags; |
| this.usage = usage; |
| this.allowedCapturePolicy = allowedCapturePolicy; |
| this.spatializationBehavior = spatializationBehavior; |
| } |
| |
| /** |
| * Returns a {@link android.media.AudioAttributes} from this instance. |
| * |
| * <p>Field {@link AudioAttributes#allowedCapturePolicy} is ignored for API levels prior to 29. |
| */ |
| @RequiresApi(21) |
| public android.media.AudioAttributes getAudioAttributesV21() { |
| if (audioAttributesV21 == null) { |
| android.media.AudioAttributes.Builder builder = |
| new android.media.AudioAttributes.Builder() |
| .setContentType(contentType) |
| .setFlags(flags) |
| .setUsage(usage); |
| if (Util.SDK_INT >= 29) { |
| Api29.setAllowedCapturePolicy(builder, allowedCapturePolicy); |
| } |
| if (Util.SDK_INT >= 32) { |
| Api32.setSpatializationBehavior(builder, spatializationBehavior); |
| } |
| audioAttributesV21 = builder.build(); |
| } |
| return audioAttributesV21; |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null || getClass() != obj.getClass()) { |
| return false; |
| } |
| AudioAttributes other = (AudioAttributes) obj; |
| return this.contentType == other.contentType |
| && this.flags == other.flags |
| && this.usage == other.usage |
| && this.allowedCapturePolicy == other.allowedCapturePolicy |
| && this.spatializationBehavior == other.spatializationBehavior; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = 17; |
| result = 31 * result + contentType; |
| result = 31 * result + flags; |
| result = 31 * result + usage; |
| result = 31 * result + allowedCapturePolicy; |
| result = 31 * result + spatializationBehavior; |
| return result; |
| } |
| |
| // Bundleable implementation. |
| |
| @Documented |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({ |
| FIELD_CONTENT_TYPE, |
| FIELD_FLAGS, |
| FIELD_USAGE, |
| FIELD_ALLOWED_CAPTURE_POLICY, |
| FIELD_SPATIALIZATION_BEHAVIOR |
| }) |
| private @interface FieldNumber {} |
| |
| private static final int FIELD_CONTENT_TYPE = 0; |
| private static final int FIELD_FLAGS = 1; |
| private static final int FIELD_USAGE = 2; |
| private static final int FIELD_ALLOWED_CAPTURE_POLICY = 3; |
| private static final int FIELD_SPATIALIZATION_BEHAVIOR = 4; |
| |
| @Override |
| public Bundle toBundle() { |
| Bundle bundle = new Bundle(); |
| bundle.putInt(keyForField(FIELD_CONTENT_TYPE), contentType); |
| bundle.putInt(keyForField(FIELD_FLAGS), flags); |
| bundle.putInt(keyForField(FIELD_USAGE), usage); |
| bundle.putInt(keyForField(FIELD_ALLOWED_CAPTURE_POLICY), allowedCapturePolicy); |
| bundle.putInt(keyForField(FIELD_SPATIALIZATION_BEHAVIOR), spatializationBehavior); |
| return bundle; |
| } |
| |
| /** Object that can restore {@link AudioAttributes} from a {@link Bundle}. */ |
| public static final Creator<AudioAttributes> CREATOR = |
| bundle -> { |
| Builder builder = new Builder(); |
| if (bundle.containsKey(keyForField(FIELD_CONTENT_TYPE))) { |
| builder.setContentType(bundle.getInt(keyForField(FIELD_CONTENT_TYPE))); |
| } |
| if (bundle.containsKey(keyForField(FIELD_FLAGS))) { |
| builder.setFlags(bundle.getInt(keyForField(FIELD_FLAGS))); |
| } |
| if (bundle.containsKey(keyForField(FIELD_USAGE))) { |
| builder.setUsage(bundle.getInt(keyForField(FIELD_USAGE))); |
| } |
| if (bundle.containsKey(keyForField(FIELD_ALLOWED_CAPTURE_POLICY))) { |
| builder.setAllowedCapturePolicy(bundle.getInt(keyForField(FIELD_ALLOWED_CAPTURE_POLICY))); |
| } |
| if (bundle.containsKey(keyForField(FIELD_SPATIALIZATION_BEHAVIOR))) { |
| builder.setSpatializationBehavior( |
| bundle.getInt(keyForField(FIELD_SPATIALIZATION_BEHAVIOR))); |
| } |
| return builder.build(); |
| }; |
| |
| private static String keyForField(@FieldNumber int field) { |
| return Integer.toString(field, Character.MAX_RADIX); |
| } |
| |
| @RequiresApi(29) |
| private static final class Api29 { |
| private Api29() {} |
| |
| @DoNotInline |
| public static void setAllowedCapturePolicy( |
| android.media.AudioAttributes.Builder builder, |
| @C.AudioAllowedCapturePolicy int allowedCapturePolicy) { |
| builder.setAllowedCapturePolicy(allowedCapturePolicy); |
| } |
| } |
| |
| @RequiresApi(32) |
| private static final class Api32 { |
| private Api32() {} |
| |
| @DoNotInline |
| public static void setSpatializationBehavior( |
| android.media.AudioAttributes.Builder builder, |
| @C.SpatializationBehavior int spatializationBehavior) { |
| try { |
| // TODO[b/190759307]: Remove reflection once compile SDK target is set to 32. |
| Method setSpatializationBehavior = |
| builder.getClass().getMethod("setSpatializationBehavior", Integer.TYPE); |
| setSpatializationBehavior.invoke(builder, spatializationBehavior); |
| } catch (Exception e) { |
| // Do nothing if reflection fails. |
| } |
| } |
| } |
| } |