Các thiết bị Android TV có thể kết nối cùng lúc nhiều đầu ra âm thanh: Loa TV, rạp chiếu phim tại nhà có kết nối HDMI, tai nghe Bluetooth, v.v. Các thiết bị đầu ra âm thanh này có thể hỗ trợ nhiều chức năng âm thanh, chẳng hạn như mã hoá (Dolby Digital+, DTS và PCM), tốc độ lấy mẫu và kênh. Ví dụ: TV có kết nối HDMI hỗ trợ nhiều chế độ mã hoá trong khi tai nghe Bluetooth được kết nối thường chỉ hỗ trợ PCM.
Danh sách các thiết bị âm thanh có sẵn và thiết bị âm thanh được định tuyến cũng có thể thay đổi bằng cách cắm nóng thiết bị HDMI, kết nối hoặc ngắt kết nối tai nghe Bluetooth, hoặc người dùng thay đổi chế độ cài đặt âm thanh. Vì chức năng đầu ra âm thanh có thể thay đổi ngay cả khi ứng dụng đang phát nội dung nghe nhìn, các ứng dụng cần phải thích ứng linh hoạt với thay đổi và tiếp tục phát trên thiết bị âm thanh được định tuyến mới và các chức năng khác nhau. Việc xuất định dạng âm thanh không chính xác có thể dẫn đến lỗi hoặc không có âm thanh nào đang phát.
Các ứng dụng có thể xuất cùng một nội dung bằng nhiều kiểu mã hoá nhằm mang lại cho người dùng trải nghiệm âm thanh tốt nhất tuỳ thuộc vào thiết bị âm thanh các chức năng khác nhau. Ví dụ: một luồng âm thanh được mã hoá theo định dạng Dolby Digital được phát TV có hỗ trợ công nghệ này hay không, trong khi luồng âm thanh PCM được hỗ trợ rộng rãi hơn đã chọn khi không có hỗ trợ Dolby Digital. Danh sách các thiết bị Android tích hợp sẵn Bạn có thể tìm thấy bộ giải mã dùng để chuyển đổi luồng âm thanh thành PCM trong Định dạng nội dung nghe nhìn được hỗ trợ.
Tại thời điểm phát lại, ứng dụng truyền trực tuyến sẽ tạo một
AudioTrack
ứng dụng tốt nhất
AudioFormat
được hỗ trợ bởi dữ liệu đầu ra
thiết bị âm thanh.
Tạo bản nhạc có định dạng phù hợp
Các ứng dụng sẽ tạo một AudioTrack
, bắt đầu phát và gọi
getRoutedDevice()
để xác định thiết bị âm thanh mặc định dùng để phát âm thanh.
Ví dụ: đó có thể là một bản nhạc được mã hoá PCM tắt tiếng và an toàn, chỉ dùng để
xác định thiết bị được định tuyến và khả năng âm thanh của thiết bị.
Nhận các kiểu mã hoá được hỗ trợ
Sử dụng
getAudioProfiles()
(API cấp 31 trở lên) hoặc
getEncodings()
(API cấp 23 trở lên) để xác định các định dạng âm thanh có trên
thiết bị âm thanh mặc định.
Kiểm tra cấu hình và định dạng âm thanh được hỗ trợ
Sử dụng AudioProfile
(API cấp 31 trở lên) hoặc
isDirectPlaybackSupported()
(API cấp 29 trở lên) để kiểm tra các kiểu kết hợp định dạng được hỗ trợ,
số lượng kênh và tốc độ lấy mẫu.
Một số thiết bị Android có khả năng hỗ trợ các phương thức mã hoá ngoài các phương thức mã hoá
được thiết bị âm thanh đầu ra hỗ trợ. Những định dạng bổ sung này sẽ
được phát hiện qua isDirectPlaybackSupported()
. Trong những trường hợp này, dữ liệu âm thanh
được mã hoá lại thành định dạng mà thiết bị âm thanh đầu ra hỗ trợ. Sử dụng
isDirectPlaybackSupported()
để kiểm tra xem có hỗ trợ định dạng mong muốn không
ngay cả khi không có trong danh sách do getEncodings()
trả về.
Định tuyến âm thanh mong đợi
Android 13 (API cấp 33) đã ra mắt các tuyến âm thanh dự kiến. Bạn có thể
dự đoán sẽ hỗ trợ thuộc tính âm thanh của thiết bị và chuẩn bị các bản nhạc cho phiên bản đang hoạt động
thiết bị âm thanh. Bạn có thể sử dụng
getDirectPlaybackSupport()
để kiểm tra xem tính năng phát trực tiếp có được hỗ trợ cho âm thanh hiện được định tuyến hay không
cho một định dạng và thuộc tính nhất định:
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 }
Ngoài ra, bạn có thể truy vấn xem hồ sơ nào được hỗ trợ cho nội dung nghe nhìn trực tiếp phát qua thiết bị âm thanh hiện được định tuyến. Không bao gồm mọi trang doanh nghiệp không được hỗ trợ hoặc sẽ được chuyển mã bởi Android khung:
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(); }
Trong ví dụ này, preferredFormats
là danh sách
AudioFormat
thực thể. Đã sắp xếp
với ứng dụng được ưa thích nhất đầu tiên trong danh sách và ứng dụng được ưu tiên ít nhất cuối cùng.
getDirectProfilesForAttributes()
sẽ trả về danh sách các
AudioProfile
cho phần tử
được định tuyến với
AudioAttributes
. Danh sách
mục AudioFormat
ưu tiên sẽ được lặp lại cho đến khi kết quả phù hợp được hỗ trợ
Đã tìm thấy AudioProfile
. AudioProfile
này được lưu trữ dưới dạng bestAudioProfile
.
Tốc độ lấy mẫu tối ưu và mặt nạ kênh được xác định từ ngày bestAudioProfile
.
Cuối cùng, hãy sử dụng AudioFormat
thích hợp
thực thể được tạo.
Tạo bản âm thanh
Ứng dụng nên sử dụng thông tin này để tạo một AudioTrack
cho
AudioFormat
có chất lượng cao nhất được thiết bị âm thanh mặc định hỗ trợ
(và có sẵn cho nội dung đã chọn).
Chặn các thay đổi đối với thiết bị âm thanh
Để chặn và phản ứng với những thay đổi của thiết bị âm thanh, ứng dụng nên:
- Đối với các cấp độ API bằng hoặc lớn hơn 24, hãy thêm
OnRoutingChangedListener
để giám sát các thay đổi đối với thiết bị âm thanh (HDMI, Bluetooth, v.v.). - Đối với API cấp 23, hãy đăng ký
AudioDeviceCallback
để nhận các thay đổi trong danh sách thiết bị âm thanh có sẵn. - Đối với API cấp 21 và 22, hãy giám sát Sự kiện với giắc cắm HDMI và sử dụng dữ liệu bổ sung từ thông báo truyền tin.
- Đồng thời đăng ký
BroadcastReceiver
để giám sát Thay đổi về trạng thái củaBluetoothDevice
đối với các thiết bị thấp hơn API 23, vìAudioDeviceCallback
không khớp chưa được hỗ trợ.
Khi phát hiện thay đổi về thiết bị âm thanh đối với AudioTrack
, ứng dụng
nên kiểm tra các chức năng âm thanh được cập nhật và nếu cần, hãy tạo lại
AudioTrack
với một AudioFormat
khác. Hãy thực hiện việc này nếu quảng cáo có chất lượng cao hơn
phương thức mã hoá hiện đã được hỗ trợ hoặc phương thức mã hoá sử dụng trước đó
không còn được hỗ trợ.
Mã mẫu
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);