บันทึกเสียงคุณภาพสูงที่มีบิตเรตต่ำด้วย xHE-AAC

ตั้งแต่ Android 17 QPR1 (ระดับ API 37.1 หรือ Build.VERSION_CODES_FULL.CINNAMON_BUN_1) เป็นต้นไป Android จะเปิดตัวการรองรับแพลตฟอร์ม สำหรับการเข้ารหัส Extended High Efficiency Advanced Audio Coding (xHE-AAC)

xHE-AAC ซึ่งกำหนดไว้ใน ISO/IEC 23003-3 (MPEG-D USAC) และ ISO/IEC 23003-4 (MPEG-D DRC) ผสานรวมการเข้ารหัสเสียงและคำพูดแบบรวมเข้ากับการปรับระดับเสียงตามข้อกำหนดและการควบคุมช่วงไดนามิกในตัว แม้ว่าเอกสารนิยามความเข้ากันได้ของ Android (CDD) จะรองรับและกำหนดให้ใช้การถอดรหัส xHE-AAC ในแพลตฟอร์มหลายเวอร์ชัน แต่การเพิ่มตัวเข้ารหัส xHE-AAC (c2.android.xheaac.encoder และการติดตั้งใช้งานฮาร์ดแวร์ OEM) จะช่วยให้คุณบันทึกข้อความเสียงและเสียงที่มีอัตราบิตต่ำ สตรีมเสียงสด และเพย์โหลดการสื่อสารด้วยความชัดเจนของเสียงพูดที่ยอดเยี่ยมและระดับเสียงที่สม่ำเสมอในอุปกรณ์ต่างๆ

ประโยชน์ของ xHE-AAC

โดยปกติแล้ว การบันทึกเสียงแบบเดิมในแอปส่งข้อความ Android จะใช้ AAC-LC (AAC Low Complexity) เวอร์ชันเก่าที่ 28 kbps ที่บิตเรตต่ำ (<32 kbps) AAC-LC แสดงให้เห็นถึงอาร์ติแฟกต์การจำกัดแถบความถี่ที่สังเกตได้ คุณภาพเสียงพูดที่ไม่ชัด และ ประสิทธิภาพการบีบอัดที่ไม่เหมาะสม สำหรับผู้ใช้ปลายทาง เสียงที่ได้จะอู้อี้ แหลมเล็ก และแยกพยัญชนะได้ยาก ทำให้ผู้ฟังต้อง เล่นข้อความซ้ำหรือตั้งใจฟังเป็นพิเศษเพื่อทำความเข้าใจคำพูดในสภาพแวดล้อมที่มีเสียงดัง นอกจากนี้ ระดับเสียงที่ไม่สอดคล้องกันในคลิปเสียงยังบังคับให้ผู้ใช้ต้อง ปรับระดับเสียงของอุปกรณ์บ่อยๆ

xHE-AAC ขจัดข้อจำกัดเหล่านี้ด้วยการใช้อัลกอริทึมการเข้ารหัสเสียงที่ซับซ้อนยิ่งขึ้น เช่น การสลับระหว่างเครื่องมือการเข้ารหัสเสียงพูดโดยเฉพาะ และเครื่องมือการเข้ารหัสเสียงทั่วไปแบบไดนามิก พร้อมทั้งขยายช่วงความถี่ด้วย Spectral Band Replication (eSBR) ที่ได้รับการปรับปรุง และมอบความสามารถในการเข้ารหัสสเตอริโอแบบพารามิเตอร์ โดยมีค่าใช้จ่ายในการเข้ารหัสเพิ่มเติมเพียงเล็กน้อย

MPEG-D DRC ในตัวจะให้การควบคุมระดับเสียงที่จำเป็นสำหรับ xHE-AAC เพื่อเล่นเนื้อหา ที่ระดับเสียงที่สม่ำเสมอ และมีการประมวลผลการควบคุมช่วงไดนามิก เพื่อมอบประสบการณ์การใช้งานที่ดีที่สุดเท่าที่จะเป็นไปได้สำหรับการฟังบนแพลตฟอร์มใดก็ตามและ ในทุกสภาพแวดล้อม

กรณีการใช้งานเป้าหมาย

  • Rich Communication Services (RCS) และการรับส่งข้อความเสียง: เป็นไปตาม GSMA RCC.71 Universal Profile เวอร์ชัน 3.1 ขึ้นไป ซึ่งกำหนดให้ใช้ xHE-AAC สำหรับการรับส่งข้อความเสียงที่มีบิตเรตต่ำ
  • การสตรีมเสียงสดและพอดแคสต์: ให้คุณภาพเสียงที่เสถียร ผ่านการเชื่อมต่อเครือข่ายมือถือหรือดาวเทียมที่เสื่อมสภาพ

ความพร้อมให้บริการของแพลตฟอร์ม (ระดับ API 37.1 ขึ้นไป)

การรองรับการเข้ารหัส xHE-AAC ของแพลตฟอร์มต้องใช้ API ระดับ 37.1 (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.CINNAMON_BUN_1 / SDK 3700001 ขึ้นไป)

สอบถามเกี่ยวกับการรองรับตัวเข้ารหัส xHE-AAC

คุณต้องไม่ถือว่าการเข้ารหัส xHE-AAC พร้อมใช้งานในทุกอุปกรณ์ โดยการเรียกใช้ MediaCodec.createByCodecName(...) ด้วย "c2.android.xheaac.encoder"

  • อุปกรณ์บางเครื่องอาจมีตัวเข้ารหัส xHE-AAC ที่เร่งด้วยฮาร์ดแวร์จากผู้จำหน่ายชิปเซ็ตแทนที่จะเป็นตัวเข้ารหัสซอฟต์แวร์ของแพลตฟอร์ม
  • ระบบปฏิบัติการเวอร์ชันเก่าหรือ ROM ที่ปรับแต่งเองไม่รองรับการเข้ารหัส xHE-AAC
  • ก่อนเข้ารหัสไฟล์แนบสำหรับการรับส่งข้อความแบบเพียร์ทูเพียร์ (เช่น RCS หรือ แชท) แอปควรตรวจสอบด้วยว่าไคลเอ็นต์ฝั่งผู้รับประกาศ การรองรับการถอดรหัส USAC/xHE-AAC หรือไม่ (เช่น ใช้การค้นพบความสามารถของ SDP profile-level-id=55 / object=42) เพื่อป้องกันไม่ให้เกิดความล้มเหลวในการทำงานร่วมกัน ในเครื่องรับรุ่นเดิม

ตรวจสอบความสามารถของ MediaCodecList

หากต้องการตรวจสอบว่าอุปกรณ์ปัจจุบันรองรับการเข้ารหัส xHE-AAC หรือไม่ ให้ตรวจสอบ profileLevels ของตัวเข้ารหัสที่รองรับ MediaFormat.MIMETYPE_AUDIO_AAC

Kotlin

import android.media.MediaCodecInfo.CodecProfileLevel
import android.media.MediaCodecList
import android.media.MediaFormat
import android.os.Build

/**
 * Checks if the current device supports xHE-AAC audio encoding.
 */
fun isXheAacEncodingSupported(): Boolean {
    // Verify API Level >= 37.1 (CINNAMON_BUN_1 / SDK 3700001)
    if (Build.VERSION.SDK_INT_FULL < Build.VERSION_CODES_FULL.CINNAMON_BUN_1) {
        return false
    }

    val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
    for (codecInfo in codecList.codecInfos) {
        if (!codecInfo.isEncoder) continue

        if (MediaFormat.MIMETYPE_AUDIO_AAC in codecInfo.supportedTypes) {
            val capabilities =
                codecInfo.getCapabilitiesForType(MediaFormat.MIMETYPE_AUDIO_AAC)
            // Check if AACObjectXHE (42) is present in profileLevels
            val supportsXhe = capabilities.profileLevels.any { profileLevel ->
                profileLevel.profile == CodecProfileLevel.AACObjectXHE
            }
            if (supportsXhe) {
                return true
            }
        }
    }
    return false
}

Java

import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.os.Build;

/**
 * Checks if the current device supports xHE-AAC audio encoding.
 */
public boolean isXheAacEncodingSupported() {
    // Verify API Level >= 37.1 (CINNAMON_BUN_1 / SDK 3700001)
    if (Build.VERSION.SDK_INT_FULL < Build.VERSION_CODES_FULL.CINNAMON_BUN_1) {
        return false;
    }

    MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
    for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
        if (!codecInfo.isEncoder()) continue;

        for (String type : codecInfo.getSupportedTypes()) {
            if (MediaFormat.MIMETYPE_AUDIO_AAC.equals(type)) {
                MediaCodecInfo.CodecCapabilities capabilities =
                        codecInfo.getCapabilitiesForType(MediaFormat.MIMETYPE_AUDIO_AAC);
                for (CodecProfileLevel profileLevel : capabilities.profileLevels) {
                    if (profileLevel.profile == CodecProfileLevel.AACObjectXHE) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

กำหนดค่าและสร้างอินสแตนซ์ของตัวเข้ารหัส xHE-AAC

หลังจากยืนยันการรองรับ xHE-AAC แล้ว ให้กำหนดค่า MediaFormat โดยตั้งค่า KEY_AAC_PROFILE เป็น MediaCodecInfo.CodecProfileLevel.AACObjectXHE (ค่า 42)

Kotlin

fun setupXheAacEncoder(): MediaCodec? {
    if (!isXheAacEncodingSupported()) {
        return null
    }

    val mimeType = MediaFormat.MIMETYPE_AUDIO_AAC
    val sampleRate = 48000
    val channelCount = 1 // Mono voice recording
    val bitRate = 20000  // 20 kbps delivers superior speech clarity

    val format = MediaFormat.createAudioFormat(mimeType, sampleRate, channelCount).apply {
        setInteger(MediaFormat.KEY_BIT_RATE, bitRate)
        setInteger(MediaFormat.KEY_AAC_PROFILE, CodecProfileLevel.AACObjectXHE)
    }

    // Find the hardware or software encoder supporting this format and profile
    val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
    val encoderName = codecList.findEncoderForFormat(format) ?: return null
    val encoder = MediaCodec.createByCodecName(encoderName)
    encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
    return encoder
}

Java

public MediaCodec setupXheAacEncoder() throws IOException {
    if (!isXheAacEncodingSupported()) {
        return null;
    }

    String mimeType = MediaFormat.MIMETYPE_AUDIO_AAC;
    int sampleRate = 48000;
    int channelCount = 1; // Mono voice recording
    int bitRate = 20000;  // 20 kbps delivers superior speech clarity

    MediaFormat format = MediaFormat.createAudioFormat(mimeType, sampleRate, channelCount);
    format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
    format.setInteger(MediaFormat.KEY_AAC_PROFILE, CodecProfileLevel.AACObjectXHE);

    // Find the hardware or software encoder supporting this format and profile
    MediaCodecList codecList =
            new MediaCodecList(MediaCodecList.REGULAR_CODECS);
    String encoderName = codecList.findEncoderForFormat(format);
    if (encoderName == null) {
        return null;
    }

    MediaCodec encoder = MediaCodec.createByCodecName(encoderName);
    encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
    return encoder;
}

ขีดจำกัดของซอฟต์แวร์เปลี่ยนไฟล์ที่รองรับ (c2.android.xheaac.encoder)

เมื่อใช้ตัวเข้ารหัส xHE-AAC ของซอฟต์แวร์ที่ระบบมีให้ (c2.android.xheaac.encoder) คอมโพเนนต์จะโฆษณาขีดจำกัดความสามารถต่อไปนี้

  • จำนวนช่อง: สูงสุด 2 ช่อง (โมโน / สเตอริโอ)
  • อัตราการสุ่มตัวอย่าง: 44100 Hz, 48000 Hz
  • ช่วงอัตราบิต: 12,000 bps ถึง 400,000 bps (12 kbps – 400 kbps)
  • การมัลติเพล็กซ์คอนเทนเนอร์: ใช้งานร่วมกับสตรีมพื้นฐาน MP4A ดิบและการมัลติเพล็กซ์คอนเทนเนอร์ MP4/M4A ของแพลตฟอร์ม (MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)