การแก้ไขข้อบกพร่องของเสียง

บทความนี้อธิบายกลเม็ดเคล็ดลับบางอย่างในการแก้ไขข้อบกพร่องของเสียง Android

อ่างทีซิงก์

"อ่างของเสื้อยืด" เท่ากับ ฟีเจอร์การแก้ไขข้อบกพร่องของ AudioFlinger ซึ่งมีอยู่ในบิลด์ที่กำหนดเองเท่านั้น สำหรับเก็บส่วนย่อยของเสียงล่าสุดไว้เพื่อการวิเคราะห์ในภายหลัง ซึ่งช่วยให้เปรียบเทียบสิ่งที่เล่นหรือบันทึกจริงได้ เทียบกับที่คาดไว้

เพื่อความเป็นส่วนตัว ซิงก์เสื้อยืดจะปิดใช้อยู่โดยค่าเริ่มต้นทั้งในเวลาคอมไพล์และ รันไทม์ หากต้องการใช้ซิงก์ท้าย คุณจะต้องเปิดใช้ ด้วยการคอมไพล์อีกครั้ง และโดยการตั้งค่าพร็อพเพอร์ตี้ โปรดปิดใช้ฟีเจอร์นี้หลังจาก แก้ไขข้อบกพร่องเสร็จแล้ว ไม่ควรเปิดใช้ซิงก์เสื้อยืดในบิลด์เวอร์ชันที่ใช้งานจริง

วิธีการในส่วนนี้มีไว้สำหรับ Android 7.x ขึ้นไป สำหรับ Android 5.x และ 6.x แทนที่ /data/misc/audioserver ด้วย /data/misc/media นอกจากนี้คุณต้องใช้โปรแกรมแก้ไขข้อบกพร่องหรือ การสร้างภาษาอังกฤษ หากคุณใช้บิลด์ userdebug ให้ปิดใช้การยืนยันด้วย:

adb root && adb disable-verity && adb reboot

การตั้งค่าเวลาคอมไพล์

  1. cd frameworks/av/services/audioflinger
  2. แก้ไขConfiguration.h
  3. Uncomment #define TEE_SINK
  4. สร้าง libaudioflinger.so ใหม่
  5. adb root
  6. adb remount
  7. พุชหรือซิงค์ libaudioflinger.so ใหม่ไปยัง /system/lib ของอุปกรณ์

การตั้งค่ารันไทม์

  1. adb shell getprop | grep ro.debuggable
    ยืนยันว่าเอาต์พุตเป็น [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    ยืนยันว่าเอาต์พุตเป็นดังนี้

    drwx------ media media ... media
    

    ถ้าไม่มีไดเรกทอรี ให้สร้างดังนี้

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    โดยที่ค่า af.tee เป็นตัวเลขที่อธิบายไว้ด้านล่าง
  5. chmod 644 /data/local.prop
  6. reboot

ค่าสำหรับพร็อพเพอร์ตี้ af.tee

ค่าของ af.tee เป็นตัวเลขระหว่าง 0 ถึง 7 ซึ่งแสดง ผลรวมของหลายบิต หนึ่งบิตต่อฟีเจอร์ ดูรหัสที่ AudioFlinger::AudioFlinger() ใน AudioFlinger.cpp เพื่อดูคำอธิบายของแต่ละบิตโดยสังเขป

  • 1 = อินพุต
  • 2 = เอาต์พุต FastMixer
  • 4 = AudioRecord และ AudioTrack ต่อแทร็ก

ยังไม่มีบิตสำหรับบัฟเฟอร์แบบดีปหรือมิกเซอร์แบบปกติ แต่คุณจะได้รับผลลัพธ์ที่คล้ายกันโดยใช้ "4"

ทดสอบและได้ข้อมูล

  1. ทดสอบเสียง
  2. adb shell dumpsys media.audio_flinger
  3. มองหาบรรทัดในเอาต์พุต dumpsys เช่น
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    ไฟล์นี้เป็นไฟล์ PCM .wav
  4. จากนั้น adb pull ไฟล์ใดก็ได้ /data/misc/audioserver/*.wav ไฟล์ที่สนใจ โปรดทราบว่าชื่อไฟล์ Dump เฉพาะแทร็กจะไม่ปรากฏในส่วน เอาต์พุต dumpsys แต่ยังคงบันทึกลงใน /data/misc/audioserver เมื่อปิดแทร็ก
  5. ตรวจสอบไฟล์ Dump เพื่อหาข้อกังวลด้านความเป็นส่วนตัวก่อนที่จะแชร์กับผู้อื่น

คำแนะนำ

ลองใช้แนวคิดต่อไปนี้เพื่อให้ได้ผลลัพธ์ที่เป็นประโยชน์มากขึ้น

  • ปิดใช้เสียงแตะและการคลิกปุ่ม เพื่อลดการขัดจังหวะในเอาต์พุตทดสอบ
  • เพิ่มระดับเสียงสูงสุด
  • ปิดใช้แอปที่ส่งเสียงหรือบันทึกเสียงจากไมโครโฟน หากไม่สนใจการทดสอบ
  • ระบบจะบันทึกข้อมูล Dump เฉพาะแทร็กเมื่อแทร็กปิดอยู่เท่านั้น คุณอาจต้องบังคับปิดแอปเพื่อถ่ายโอนข้อมูลเฉพาะแทร็กของแอปนั้น
  • ทำdumpsysทันทีหลังจากการทดสอบ มีพื้นที่บันทึกจำกัด
  • เพื่อไม่ให้ไฟล์ Dump สูญหาย อัปโหลดไปยังโฮสต์ของคุณเป็นระยะ สามารถรักษาไฟล์ Dump ไว้ได้ในจำนวนจำกัด ระบบจะนำดัมพ์เก่าออกหลังจากถึงขีดจำกัดแล้ว

คืนค่า

ดังที่กล่าวไว้ข้างต้น ไม่ควรเปิดใช้ฟีเจอร์อ่างทีซิงก์ กู้คืนบิลด์และอุปกรณ์โดยทำดังนี้

  1. เปลี่ยนกลับการเปลี่ยนแปลงซอร์สโค้ดเป็น Configuration.h
  2. สร้าง libaudioflinger.so ใหม่
  3. พุชหรือซิงค์ libaudioflinger.so ที่กู้คืนแล้ว ไปยัง/system/libของอุปกรณ์
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

มาโคร ALOGx

API การบันทึกภาษา Java มาตรฐานใน Android SDK คือ android.util.Log

API ภาษา C ที่สอดคล้องกันใน Android NDK คือ __android_log_print ที่ประกาศใน<android/log.h>

ในส่วนเนทีฟของเฟรมเวิร์ก Android ต้องการมาโครชื่อ ALOGE, ALOGW ALOGI, ALOGV ฯลฯ ประกาศใน <utils/Log.h> และตามวัตถุประสงค์ของบทความนี้ เราจะเรียกรวมกันว่า ALOGx

API ทั้งหมดนี้ใช้งานง่ายและเข้าใจเป็นอย่างดี ทำให้มีแพร่หลาย ในทุกแพลตฟอร์ม Android โดยเฉพาะอย่างยิ่ง mediaserver ซึ่งรวมถึงเซิร์ฟเวอร์เสียง AudioFlinger จะใช้ ALOGx อย่างมาก

แต่มีข้อจำกัดบางประการสำหรับ ALOGx และเพื่อนดังนี้

  • มีความเสี่ยงต่อ "สแปมบันทึก": บัฟเฟอร์ไฟล์บันทึกเป็นทรัพยากรที่มีการแชร์ เพื่อให้สามารถใส่รายการเพิ่มเติมได้อย่างง่ายดาย เนื่องจากรายการบันทึกที่ไม่เกี่ยวข้อง ซึ่งส่งผลให้เกิด ข้อมูลที่ไม่ได้รับ ตัวแปร ALOGV ถูกปิดใช้ที่ เวลาคอมไพล์โดยค่าเริ่มต้น แต่แน่นอนว่าผลลัพธ์ที่ได้คือสแปมบันทึก หากเปิดใช้อยู่
  • การเรียกระบบเคอร์เนลที่อยู่เบื้องหลังอาจบล็อก ซึ่งอาจทำให้เกิด การกลับลำดับความสำคัญ และจากนั้น รบกวนการวัดและ ความไม่ถูกต้อง นี้ของ ข้อกังวลพิเศษสำหรับชุดข้อความที่ต้องคำนึงถึงเวลาเป็นสำคัญ เช่น FastMixer และ FastCapture
  • ถ้ามีการปิดใช้บันทึกใดเพื่อลดสแปมบันทึก ข้อมูลใดๆ ที่อาจจะบันทึกไว้โดยบันทึกนั้นจะสูญหายไป คุณไม่สามารถเปิดใช้งานบันทึกบางรายการย้อนหลังได้ หลังเป็นที่ประจักษ์ว่าบันทึกนั้นน่าสนใจ

NBLOG, media.log และ MediaLogService

API ของ NBLOG และ media.log ที่เชื่อมโยง กระบวนการและMediaLogService เข้าด้วยกัน ทำให้ระบบเก็บบันทึกใหม่ สำหรับสื่อ และ ที่ออกแบบมาเพื่อแก้ปัญหาข้างต้น โดยเราจะใช้คำว่า "media.log" จะอ้างอิงถึงทั้ง 3 อย่าง แต่พูดง่ายๆ ก็คือ NBLOG คือ API การบันทึกของ C++, media.log เป็นชื่อกระบวนการของ Linux และ MediaLogService เป็นบริการ Android Binder สำหรับตรวจสอบบันทึก

"ไทม์ไลน์" media.log เป็นซีรีส์ ของรายการบันทึกที่มีการเก็บรักษาตามลำดับที่เกี่ยวข้อง โดยปกติ แต่ละชุดข้อความควรใช้ไทม์ไลน์ของตัวเอง

ข้อดี

ประโยชน์ของระบบ media.log คือ

  • ไม่ส่งสแปมไปยังบันทึกหลัก เว้นแต่และจนกว่าจะจำเป็นต้องใช้
  • ตรวจสอบได้แม้ว่า mediaserver จะขัดข้องหรือค้าง
  • ไม่บล็อกต่อไทม์ไลน์
  • รบกวนประสิทธิภาพการทำงานน้อยกว่า (แน่นอนว่าการบันทึกทุกรูปแบบจะไม่เป็นการรบกวนแต่อย่างใด)

สถาปัตยกรรม

แผนภาพด้านล่างแสดงความสัมพันธ์ของกระบวนการ mediaserver และกระบวนการ init ก่อนการใช้ media.log

สถาปัตยกรรมก่อนมีเดีย.log

รูปที่ 1 สถาปัตยกรรมก่อนมีเดีย.log

คะแนนสำคัญ:

  • init แยกและผู้บริหาร mediaserver
  • init ตรวจพบการเสียชีวิตของ mediaserver และแยกออกตามความจำเป็น
  • ระบบไม่แสดงการบันทึก ALOGx

แผนภาพด้านล่างแสดงความสัมพันธ์ใหม่ของคอมโพเนนต์ หลังจากเพิ่ม media.log ในสถาปัตยกรรมแล้ว

สถาปัตยกรรมหลัง media.log

รูปที่ 2 สถาปัตยกรรมหลัง media.log

การเปลี่ยนแปลงที่สำคัญ

  • ไคลเอ็นต์ใช้ NBLOG API เพื่อสร้างรายการบันทึกและนำไปต่อท้าย บัฟเฟอร์รูปวงกลมในหน่วยความจำที่ใช้ร่วมกัน
  • MediaLogService ดัมพ์เนื้อหาของบัฟเฟอร์วงกลมได้ทุกเมื่อ
  • บัฟเฟอร์รูปวงกลมถูกออกแบบมาในลักษณะที่ทำให้ หน่วยความจำที่แชร์จะไม่ขัดข้อง MediaLogService และจะยังใช้งานต่อไปได้ เพื่อถ่ายโอนบัฟเฟอร์ที่ไม่ได้รับผลกระทบจากความเสียหายนั้นออกไปให้ได้มากที่สุด
  • บัฟเฟอร์รูปวงกลมไม่บล็อกและไม่มีการล็อกสำหรับทั้งการเขียน รายการใหม่ และการอ่านรายการที่มีอยู่
  • ไม่จำเป็นต้องมีการเรียกใช้ระบบเคอร์เนลเพื่อเขียนหรืออ่านจากบัฟเฟอร์วงกลม (นอกเหนือจากการประทับเวลาที่ไม่บังคับ)

ใช้ที่ใดได้บ้าง

ใน Android 4.4 มีเพียงจุดบันทึกไม่กี่จุดใน AudioFlinger ที่ใช้ระบบ media.log แม้ว่า API ใหม่จะไม่เหมือน ที่ใช้งานง่ายด้วย ALOGx แต่ก็ไม่ยากเท่าไหร่เช่นกัน เราขอแนะนำให้คุณศึกษาระบบการบันทึกใหม่สำหรับผู้ที่ ในช่วงเวลาที่สิ่งที่ขาดไม่ได้ โดยเฉพาะอย่างยิ่ง เหมาะสำหรับชุดข้อความ AudioFlinger ที่ต้อง ทำงานเป็นประจำ เป็นระยะๆ และไม่มีการบล็อก เช่น FastMixer และชุดข้อความ FastCapture รายการ

วิธีใช้

เพิ่มบันทึก

ขั้นแรก คุณต้องเพิ่มบันทึกลงในโค้ดของคุณ

ในชุดข้อความ FastMixer และ FastCapture ให้ใช้โค้ดดังนี้

logWriter->log("string");
logWriter->logf("format", parameters);
logWriter->logTimestamp();

เนื่องจากไทม์ไลน์ NBLog นี้ใช้โดย FastMixer และ FastCapture ชุดข้อความ ก็ไม่มีความจำเป็นที่จะต้องแยกออกจากกัน

ในชุดข้อความ AudioFlinger อื่นๆ ให้ใช้ mNBLogWriter:

mNBLogWriter->log("string");
mNBLogWriter->logf("format", parameters);
mNBLogWriter->logTimestamp();

สำหรับชุดข้อความที่ไม่ใช่ FastMixer และ FastCapture ไทม์ไลน์ NBLog ของชุดข้อความนั้นใช้ได้กับทั้งชุดข้อความนั้นๆ และ ตามการดำเนินการแฟ้ม NBLog::Writer ไม่ได้ระบุ การยกเว้นร่วมกันโดยนัยตามไทม์ไลน์ ดังนั้น โปรดตรวจสอบว่ามีบันทึกทั้งหมด ในบริบทที่มีการคงไว้ชั่วคราว mLock ของชุดข้อความอยู่

หลังจากที่เพิ่มบันทึกแล้ว ให้สร้าง AudioFlinger อีกครั้ง

ข้อควรระวัง: ต้องระบุไทม์ไลน์ของ NBLog::Writer แยกต่างหากต่อชุดข้อความ เพื่อความปลอดภัยของ Thread เนื่องจากไทม์ไลน์จะไม่มีการปิดเสียงตามที่ควรจะเป็น หากคุณ ต้องการให้ชุดข้อความมากกว่า 1 รายการใช้ไทม์ไลน์เดียวกัน คุณสามารถป้องกันด้วย Listener ที่มีอยู่ (ตามที่อธิบายไว้ข้างต้นสำหรับ mLock) หรือคุณสามารถ ให้ใช้ Wrapper NBLog::LockedWriter แทน NBLog::Writer อย่างไรก็ตาม การทำเช่นนี้ทำให้ประโยชน์หลักของ API นี้ลดลง ซึ่งก็คือการไม่บล็อก พฤติกรรมของคุณ

API ของ NBLog เต็มรูปแบบจะอยู่ที่ frameworks/av/include/media/nbaio/NBLog.h

เปิดใช้ media.log

media.log จะปิดใช้อยู่โดยค่าเริ่มต้น จะทำงานเมื่อพร็อพเพอร์ตี้ ro.test_harness คือ1 คุณสามารถเปิดใช้งานได้โดย:

adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot

การเชื่อมต่อจะหายไประหว่างรีบูต ดังนั้น

adb shell
คำสั่ง ps media จะแสดงกระบวนการ 2 รายการดังนี้
  • media.log
  • เซิร์ฟเวอร์สื่อ

จดรหัสกระบวนการของ mediaserver ไว้ใช้ภายหลัง

แสดงลำดับเวลา

คุณขอดัมพ์บันทึกด้วยตนเองได้ทุกเมื่อ คำสั่งนี้จะแสดงบันทึกจากไทม์ไลน์ที่ใช้งานอยู่และล่าสุดทั้งหมด จากนั้นจะล้างข้อมูลดังนี้

dumpsys media.log

โปรดทราบว่า ลำดับเวลาในการออกแบบเป็นอิสระ และไม่มีบริการ ในการรวมไทม์ไลน์

กู้คืนบันทึกหลังจากเซิร์ฟเวอร์สื่อไม่ทำงาน

ตอนนี้ให้ลองปิดกระบวนการ mediaserver: kill -9 # โดยที่ # คือ รหัสกระบวนการที่คุณจดไว้ก่อนหน้า คุณควรเห็นไฟล์ดัมพ์จาก media.log ใน logcat หลัก โดยแสดงบันทึกทั้งหมดที่นำไปสู่ข้อขัดข้อง

dumpsys media.log