オーディオ機能

Android TV デバイスでは、同時に複数のオーディオ出力を接続できます。 テレビのスピーカー、HDMI で接続したホーム シネマ、Bluetooth ヘッドフォンなど。 これらのオーディオ出力デバイスは、さまざまなオーディオ機能をサポートできます。 エンコード(ドルビー デジタル+、DTS、PCM)、サンプルレート、チャンネルなど)が含まれます。 たとえば、HDMI 接続テレビは多数のエンコードに対応しています。 接続されている Bluetooth ヘッドフォンは通常、PCM のみに対応しています。

利用可能なオーディオ機器のリストと、ルーティングされたオーディオ機器のリストも変更できます HDMI デバイスのホットプラグ接続、Bluetooth ヘッドフォンの接続や切断 ユーザーが音声設定を変更することです。オーディオ出力機能は アプリがメディアを再生している場合でも変化するため、アプリはこうした変化に適切に適応する必要がある 変更された新しいオーディオ機器とそのデバイスで 提供します間違った音声形式を出力すると、エラーや 音が再生されない。

アプリは、同じコンテンツを複数のエンコードで出力することができる オーディオ機器に応じて最適なオーディオ エクスペリエンスを実現します。 提供します例: ドルビー デジタルでエンコードされた音声ストリームが再生される PCM オーディオ ストリームはより広くサポートされている ドルビー デジタルに対応していない場合に選択します。組み込みの Android デバイスのリスト オーディオ ストリームを PCM に変換するために使用されるデコーダは、 サポートされているメディア形式

再生時に、ストリーミング アプリは 「AudioTrack」と「最も良い」 出力でサポートされている AudioFormat 接続します。

適切な形式でトラックを作成する

アプリは AudioTrack を作成して再生を開始し、 getRoutedDevice() 音声を再生するデフォルトのオーディオ機器を指定します。 たとえば、安全な短い無音の PCM エンコードされたトラックで、 ルーティングされたデバイスとそのオーディオ機能を特定します。

サポートされているエンコードを取得する

使用 getAudioProfiles() (API レベル 31 以降)または getEncodings() (API レベル 23 以降)を使用して、 接続します

サポートされているオーディオ プロファイルと形式を確認する

AudioProfile を使用する (API レベル 31 以降)または isDirectPlaybackSupported() (API レベル 29 以降)を使用して、サポートされている形式、 チャンネル数、サンプルレートが含まれます。

一部の Android デバイスは、他のエンコードに対応しています。 対応している必要がありますこれらの追加フォーマットは isDirectPlaybackSupported() で検出されました。この場合、音声データは 出力オーディオ機器でサポートされている形式に再エンコードされます。使用 isDirectPlaybackSupported(): 目的の形式のサポートを適切に確認します。 getEncodings() が返すリストに存在しない場合でも同じです。

予測音声ルート

Android 13(API レベル 33)では、予測オーディオ ルートが導入されました。Google Chat では デバイスのオーディオ属性のサポートを予測し、アクティブな 接続します。次を使用: getDirectPlaybackSupport() 現在ルーティングしている音声が直接再生をサポートしているかどうかを確認する 1 対 1 のメッセージを送信します。

Kotlin

val format = AudioFormat.Builder()
    .setEncoding(AudioFormat.ENCODING_E_AC3)
    .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
    .setSampleRate(48000)
    .build()
val attributes = AudioAttributes.Builder()
    .setUsage(AudioAttributes.USAGE_MEDIA)
    .build()

if (AudioManager.getDirectPlaybackSupport(format, attributes) !=
    AudioManager.DIRECT_PLAYBACK_NOT_SUPPORTED
) {
    // The format and attributes are supported for direct playback
    // on the currently active routed audio path
} else {
    // The format and attributes are NOT supported for direct playback
    // on the currently active routed audio path
}

Java

AudioFormat format = new AudioFormat.Builder()
        .setEncoding(AudioFormat.ENCODING_E_AC3)
        .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
        .setSampleRate(48000)
        .build();
AudioAttributes attributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build();

if (AudioManager.getDirectPlaybackSupport(format, attributes) !=
        AudioManager.DIRECT_PLAYBACK_NOT_SUPPORTED) {
    // The format and attributes are supported for direct playback
    // on the currently active routed audio path
} else {
    // The format and attributes are NOT supported for direct playback
    // on the currently active routed audio path
}

または、ダイレクト メディアでサポートされているプロファイルをクエリすることもできます。 オーディオ機器で再生します。プロファイルは除外されます たとえば、Android でコード変換されない動画や、 フレームワーク:

Kotlin

private fun findBestAudioFormat(audioAttributes: AudioAttributes): AudioFormat {
    val preferredFormats = listOf(
        AudioFormat.ENCODING_E_AC3,
        AudioFormat.ENCODING_AC3,
        AudioFormat.ENCODING_PCM_16BIT,
        AudioFormat.ENCODING_DEFAULT
    )
    val audioProfiles = audioManager.getDirectProfilesForAttributes(audioAttributes)
    val bestAudioProfile = preferredFormats.firstNotNullOf { format ->
        audioProfiles.firstOrNull { it.format == format }
    }
    val sampleRate = findBestSampleRate(bestAudioProfile)
    val channelMask = findBestChannelMask(bestAudioProfile)
    return AudioFormat.Builder()
        .setEncoding(bestAudioProfile.format)
        .setSampleRate(sampleRate)
        .setChannelMask(channelMask)
        .build()
}

Java

private AudioFormat findBestAudioFormat(AudioAttributes audioAttributes) {
    Stream<Integer> preferredFormats = Stream.<Integer>builder()
            .add(AudioFormat.ENCODING_E_AC3)
            .add(AudioFormat.ENCODING_AC3)
            .add(AudioFormat.ENCODING_PCM_16BIT)
            .add(AudioFormat.ENCODING_DEFAULT)
            .build();
    Stream<AudioProfile> audioProfiles =
            audioManager.getDirectProfilesForAttributes(audioAttributes).stream();
    AudioProfile bestAudioProfile = (AudioProfile) preferredFormats.map(format ->
            audioProfiles.filter(profile -> profile.getFormat() == format)
                    .findFirst()
                    .orElseThrow(NoSuchElementException::new)
    );
    Integer sampleRate = findBestSampleRate(bestAudioProfile);
    Integer channelMask = findBestChannelMask(bestAudioProfile);
    return new AudioFormat.Builder()
            .setEncoding(bestAudioProfile.getFormat())
            .setSampleRate(sampleRate)
            .setChannelMask(channelMask)
            .build();
}

この例の preferredFormats は、 AudioFormat インスタンス。順序付けられている 優先度の高い順に、最も優先度の低いものが作成されます。 getDirectProfilesForAttributes() サポートされている 次のエンティティに対する AudioProfile 個のオブジェクト: オーディオ機器の接続先オーディオ機器が AudioAttributes。リスト 優先される AudioFormat アイテムは、マッチングがサポートされるまで反復処理されます。 AudioProfile が見つかりました。この AudioProfilebestAudioProfile として保存されます。 最適なサンプルレートとチャンネル マスクは bestAudioProfile から決定されます。 最後に、適切な AudioFormat 作成されます。

音声トラックを作成

この情報は、アプリの AudioTrack を デフォルトのオーディオ機器でサポートされる最高品質 AudioFormat (選択したコンテンツで利用できます)。

オーディオ機器の変更をインターセプトする

オーディオ機器の変更をインターセプトして対応するには、アプリで以下のことを行う必要があります。

  • API レベルが 24 以上の場合は、 OnRoutingChangedListener を使用して、オーディオ機器の変更(HDMI、Bluetooth など)を監視できます。
  • API レベル 23 の場合は、 AudioDeviceCallback 使用可能なオーディオ機器のリストで変更を受信します。
  • API レベル 21 と 22 の場合は、 HDMI プラグに関するイベント ブロードキャストの追加データを使用します
  • モニタリングする BroadcastReceiver も登録する BluetoothDevice の状態変化 API 23 より前のデバイスでは、 AudioDeviceCallback は次の条件に一致しない: サポートされていません。

AudioTrack でオーディオ機器の変更が検出されると、アプリは 更新されたオーディオ機能を確認し、必要に応じて 異なる AudioFormat を持つ AudioTrack。この方法は、高品質または低品質の サポートされるようになったか、以前使用したエンコードが サポートされなくなりました

サンプルコード

Kotlin

// audioPlayer is a wrapper around an AudioTrack
// which calls a callback for an AudioTrack write error
audioPlayer.addAudioTrackWriteErrorListener {
    // error code can be checked here,
    // in case of write error try to recreate the audio track
    restartAudioTrack(findDefaultAudioDeviceInfo())
}

audioPlayer.audioTrack.addOnRoutingChangedListener({ audioRouting ->
    audioRouting?.routedDevice?.let { audioDeviceInfo ->
        // use the updated audio routed device to determine
        // what audio format should be used
        if (needsAudioFormatChange(audioDeviceInfo)) {
            restartAudioTrack(audioDeviceInfo)
        }
    }
}, handler)

Java

// audioPlayer is a wrapper around an AudioTrack
// which calls a callback for an AudioTrack write error
audioPlayer.addAudioTrackWriteErrorListener(new AudioTrackPlayer.AudioTrackWriteError() {
    @Override
    public void audioTrackWriteError(int errorCode) {
        // error code can be checked here,
        // in case of write error try to recreate the audio track
        restartAudioTrack(findDefaultAudioDeviceInfo());
    }
});

audioPlayer.getAudioTrack().addOnRoutingChangedListener(new AudioRouting.OnRoutingChangedListener() {
    @Override
    public void onRoutingChanged(AudioRouting audioRouting) {
        if (audioRouting != null && audioRouting.getRoutedDevice() != null) {
            AudioDeviceInfo audioDeviceInfo = audioRouting.getRoutedDevice();
            // use the updated audio routed device to determine
            // what audio format should be used
            if (needsAudioFormatChange(audioDeviceInfo)) {
                restartAudioTrack(audioDeviceInfo);
            }
        }
    }
}, handler);