İşlem içi kod çözücüler kullanarak ses performansını optimize etme

Android 17 (API düzeyi 37) ile başlayıp Google Play Sistem Güncellemeleri (Mainline APEX modülleri) aracılığıyla güncellenen Android, Opus ve AAC dahil olmak üzere sıkıştırılmış ses biçimleri için işlem içi yazılım ses kod çözücüleri sunar.

Ses kod çözücüleri ayrı bir korumalı alan sistem sürecinde değil, doğrudan uygulamanın sürecinde çalıştırarak uygulamalar ses kod çözme gecikmesini yaklaşık%40 azaltabilir, CPU kullanımını düşürebilir ve sürekli medya oynatma, sesli mesajlaşma ve oyun oynama sırasında pil ömrünü uzatabilir.

Ayrı işlemci alanında kod çözme ve aynı işlemci alanında kod çözme

Geçmişte Android, uygulamaları ve OS'yi hatalı medya bit akışlarından korumak için tüm platform yazılımı medya kod çözücülerini özel bir korumalı alan sistem arka plan programı (mediaswcodec) içinde çalıştırıyordu. Ancak işlem dışı korumalı alan, ölçülebilir bir performans yükü getirir:

  • İşlemler arası iletişim (IPC) gecikmesi: Kod çözücüye sıraya alınan her ses girişi çerçevesi ve döndürülen her çözülmüş PCM ses arabelleği, işlemler arası Binder IPC serileştirme ve bağlam değiştirme gerektirir.
  • Paylaşılan bellek ek yükü: Süreçler arası arabellek aktarımları, paylaşılan bellek (C2Buffer) senkronizasyonu ve önbellek yönetimi gerektirir.
  • Planlama anlaşmazlığı: CPU'nun yoğun yük altında olduğu durumlarda, uygulama işlemi ile mediaswcodec arasındaki iş parçacığı planlama anlaşmazlığı, düşük gecikmeli ses işlem hatlarında (ör. DAW'lar, VoIP iletişimi ve etkileşimli oyunlar) arabellek yetersizliğine ve sesin kesintili olarak oynatılmasına neden olabilir.

Medya kod çözücülerini işlem içinde çalıştırmak, bu performans darboğazlarını doğrudan çözer:

  • IPC gecikmesini ortadan kaldırır: Binder IPC işlemlerini ve bağlam geçişlerini atlayarak uçtan uca kod çözme gecikmesini yaklaşık %40 azaltır.
  • Arabellek ek yükünü en aza indirir: İşlemler arası paylaşılan bellek eşlemeleri gerektirmeden ses arabelleklerini doğrudan yerel uygulama bellek alanında geçirir.
  • Planlama dalgalanmasını önler: Uygulamanın kendi öncelikli iş parçacıklarında (ör. gerçek zamanlı AAudio veya Oboe iş parçacıkları) kod çözme işlemini gerçekleştirerek ağır sistem yükü altında ses kesintilerini ve arabellek yetersizliklerini önler.

Rust ile bellek güvenliği

Android, geçmişte yazılım kod çözücülerini ayrı bir işlemde (mediaswcodec) yalıtıyordu. Bunun nedeni, kod çözücülerin harici medya dosyalarından gelen güvenilmeyen bit akışlarını işlemesi ve bu durumun, arabellek taşması gibi bellek bozulmasıyla ilgili güvenlik açıklarını önemli bir güvenlik sorunu haline getirmesiydi.

Android, yazılım kod çözücüleri doğrudan arayan uygulama işlemine güvenli bir şekilde taşıyabilir. Bunu, Rust gibi bellek açısından güvenli dillerde uygulayarak veya Lightweight Fault Isolation (LFI) ile yalıtarak yapar.

Android, AAC (c2.android.inproc.aac.decoder) için işlem dışı korumalı alan ek yükünü güvenli bir şekilde ortadan kaldırmak amacıyla pure Rust ile uygulanmış veya güvenli Rust Foreign Function Interface (FFI) koşumlarıyla korunmuş bir kod çözücü sağlar.

Rust, aşağıdaki nedenlerle işlem içi kod çözme için güvenli kabul edilir:

  • Derleme zamanında bellek güvenliği: Rust'ın sıkı sahiplik, ödünç alma ve yaşam süresi kontrolü, bellek serbest bırakıldıktan sonra veya paylaşılırken değiştirildikten sonra erişilememesini sağlar.
  • Yaygın güvenlik açıklarının ortadan kaldırılması: Rust, derleme zamanında yığın arabellek taşmalarını, yığın parçalanmalarını, sınırların dışındaki dizi okuma ve yazma işlemlerini, boşaltıldıktan sonra kullanma hatalarını ve çift boşaltma güvenlik açıklarını tamamen önler.
  • Çalışma zamanında sıfır korumalı alan vergisi: Bellek güvenliği derleme zamanında matematiksel olarak kanıtlandığı ve sınır kontrolüyle doğrulandığı için Rust kod çözücüleri, sayfa tablosu geçişleri veya donanım korumalı alan bağlamları gerektirmeden uygulama sürecinde yerel hızda yürütülür.

Hafif Hata Yalıtımı (LFI) ile bellek güvenliği

Henüz Rust'ta yeniden yazılmamış karmaşık eski C/C++ ses kod çözücüleri (özellikle Opus kod çözücü (c2.android.inproc.opus.decoderlibopus ile desteklenir)) için Android, Lightweight Fault Isolation (LFI) kullanarak işlem içi güvenlik sağlar.

LFI, aşağıdaki nedenlerle C/C++ kod çözücüler için güvenli kabul edilir:

  • Donanımla korunan doğrusal bellek sanal alanı: LFI, C/C++ codec kitaplığını, donanımla korunan 4 GB'lık doğrusal bellek yuvasıyla sınırlı, WebAssembly'den türetilmiş doğrusal makine koduna derler.
  • Linux bellek koruma anahtarları (pku): LFI, adres alanı izinlerini bölmek için Linux bellek koruma anahtarlarını (pku / pkey_mprotect) kullanır. Yalıtılmış kitaplık, kendisine ayrılan 4 GB'lık sanal alan yuvasının dışındaki belleği okuyamaz veya belleğe yazamaz.
  • Sistem çağrısı yakalama: Sandbox içinde ana makine işletim sistemi sistem çağrılarına (ör. dosya G/Ç, ağ erişimi veya süreç kontrolü) izin verilmez. Desteklenmeyen tüm kitaplık çağrıları, minimal sahte saplamalara (lfi_stubs.c) yönlendirilir.
  • Güvenli hata kurtarma: Kötü biçimlendirilmiş veya saldırgan bir Opus bit akışı, doğrusal korumalı alan içinde sınırların dışında okuma veya yazma işlemi yapmaya çalışırsa LFI çalışma zamanı, hatayı kullanıcı alanında güvenli bir şekilde yakalar ve ana makine uygulamasını kilitlenmeden temiz bir C2_CORRUPTED kod çözme hatası döndürür.

LFI için desteklenen mimariler ve cihazlar

LFI için özel CPU donanım talimatları gerekmez ve ARM64 (aarch64) ve x86_64 işlemci mimarilerinde desteklenir:

  • ARM64 (aarch64) cihazlar: Cep telefonları (Google Tensor SoC'li Pixel cihazlar, Qualcomm Snapdragon ve MediaTek SoC'ler gibi), katlanabilir cihazlar, tabletler ve otomotiv bilgi-eğlence birimleri dahil olmak üzere çoğu tüketici mobil cihazı ARM64 işlemcilerde çalışır ve LFI işlem içi kod çözücüleri destekler.
  • x86_64 cihazlar: Intel veya AMD CPU'ları olan geliştirici iş istasyonlarında çalışan Android emülatörleri ve ARC (ChromeOS için Android Çalışma Zamanı) kullanarak Android uygulamalarını çalıştıran ChromeOS Chromebook'lar x86_64 mimarilerinde yürütülür ve LFI korumalı alanını destekler.

Kullanılabilir işlem içi ses kod çözücüleri

Ses biçimi MIME türü İşlemdeki bileşen adı Uygulama
Opus audio/opus c2.android.inproc.opus.decoder LFI ile C/C++ libopus
AAC audio/mp4a-latm c2.android.inproc.aac.decoder Bellek açısından güvenli Rust (C2ApexAacDec)
  • AAC (c2.android.inproc.aac.decoder): ADTS akışlarını (dinamik ADTS başlığı ayrıştırmasıyla) ve ham erişim birimlerini (AU) doğru sunum zaman damgası izlemeyle (AAC_PACKAGING_RAW durumunda yalnızca tek kareli AU işleme) işler.
  • Opus (c2.android.inproc.opus.decoder): Standart Ogg Opus veya Matroska kapsayıcı paketlerini 48 kHz PCM'ye dahili yeniden örneklemeyle kodunu çözer.

Devam eden kod çözücüleri etkinleştirme

Şu anda, MediaCodec.createDecoderByType() aranırken devam eden kod çözücüler varsayılan sistem değildir. Platform, ekosistem doğrulaması sırasında eski işlem dışı kod çözücüler için daha yüksek Codec 2.0 seçim önceliğini korur.

Düşük gecikmeli oynatma veya test için devam eden bir kod çözücüye bugün kaydolmak istiyorsanız MediaCodec.createByCodecName() kullanarak codec'i bileşen adına göre açıkça oluşturun.

Bileşen adına göre örnekleme

Aşağıdaki örnekte, işlem içi AAC kod çözücünün nasıl açıkça oluşturulacağı ve işlem içi bileşen cihazda kullanılamıyorsa varsayılan kod çözücüye nasıl geri dönüleceği gösterilmektedir:

Kotlin

val INPROC_AAC_CODEC = "c2.android.inproc.aac.decoder"

fun createAudioDecoder(): MediaCodec {
    return try {
        // Attempt to opt in to the low-latency in-process AAC decoder
        MediaCodec.createByCodecName(INPROC_AAC_CODEC)
    } catch (e: IllegalArgumentException) {
        // Fall back to the default platform AAC decoder
        MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC)
    }
}

Java

private static final String INPROC_AAC_CODEC = "c2.android.inproc.aac.decoder";

public MediaCodec createAudioDecoder() throws IOException {
    try {
        // Attempt to opt in to the low-latency in-process AAC decoder
        return MediaCodec.createByCodecName(INPROC_AAC_CODEC);
    } catch (IllegalArgumentException e) {
        // Fall back to the default platform AAC decoder
        return MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
    }
}

Jetpack Media3 veya ExoPlayer'ı yapılandırma

Uygulamanızda Jetpack Media3 veya ExoPlayer kullanılıyorsa cihazda kullanılabilir olduğunda işlem içi kod çözücülere öncelik vermek için özel bir MediaCodecSelector oluşturabilirsiniz:

Kotlin

class InprocPreferredMediaCodecSelector : MediaCodecSelector {
    override fun getDecoderInfos(
        mimeType: String,
        requiresSecureDecoder: Boolean,
        requiresTunnelingDecoder: Boolean
    ): List<MediaCodecInfo> {
        val defaultInfos = MediaCodecUtil.getDecoderInfos(
            mimeType,
            requiresSecureDecoder,
            requiresTunnelingDecoder
        )
        val inprocNames = setOf(
            "c2.android.inproc.opus.decoder",
            "c2.android.inproc.aac.decoder"
        )
        // Sort in-process decoders to the top of the selection list
        return defaultInfos.sortedByDescending { it.name in inprocNames }
    }
}

Java

public class InprocPreferredMediaCodecSelector implements MediaCodecSelector {
    private static final Set<String> INPROC_NAMES = new HashSet<>(Arrays.asList(
        "c2.android.inproc.opus.decoder",
        "c2.android.inproc.aac.decoder"
    ));

    @Override
    public List<MediaCodecInfo> getDecoderInfos(
            String mimeType,
            boolean requiresSecureDecoder,
            boolean requiresTunnelingDecoder) throws MediaCodecUtil.DecoderQueryException {
        List<MediaCodecInfo> defaultInfos = new ArrayList<>(
            MediaCodecUtil.getDecoderInfos(mimeType, requiresSecureDecoder, requiresTunnelingDecoder)
        );
        // Sort in-process decoders to the top of the selection list
        defaultInfos.sort((a, b) -> {
            boolean aInproc = INPROC_NAMES.contains(a.name);
            boolean bInproc = INPROC_NAMES.contains(b.name);
            return Boolean.compare(bInproc, aInproc);
        });
        return defaultInfos;
    }
}

Varsayılan sistem kod çözücülerine yönelik yol haritası

Android, bu bellek açısından güvenli işlem içi kod çözücüleri, yaklaşan platform sürümlerinde (Android 18'de Opus LFI ve AAC Rust ile veya yaklaşan Mainline modülü güncellemeleriyle başlayarak) ilgili ses MIME türleri için varsayılan sistem kod çözücüleri haline getirmeye hazırlanıyor.

İşlem içi kod çözücü, MIME türü için varsayılan Codec 2.0 bileşeni haline geldiğinde MediaCodec.createDecoderByType(...) için yapılan standart çağrılar otomatik olarak işlem içi uygulamaya yönlendirilir. Bu sayede, uygulama kodunda herhangi bir değişiklik yapılmasına gerek kalmadan yaklaşık% 40 daha düşük kod çözme gecikmesi sağlanır.