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

บทความนี้จะอธิบายเคล็ดลับและคำแนะนำบางประการสำหรับการดีบักเสียงของ Android

อ่างล้างจาน

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

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

คำแนะนำในส่วนนี้ใช้สำหรับ Android 7.x และสูงกว่า สำหรับ Android 5.x และ 6.x ให้แทนที่ /data/misc/audioserver ด้วย /data/misc/media นอกจากนี้ คุณต้องใช้ userdebug หรือ eng build หากคุณใช้บิลด์ userdebug ให้ปิดการใช้งาน verity ด้วย:

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

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

  1. cd frameworks/av/services/audioflinger
  2. แก้ไข Configuration.h
  3. ไม่แสดงความคิดเห็น #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 ใด ๆ ที่น่าสนใจ โปรดทราบว่าชื่อไฟล์ดัมพ์เฉพาะแทร็กจะไม่ปรากฏในเอาต์พุต dumpsys แต่ยังคงบันทึกลงใน /data/misc/audioserver เมื่อปิดแทร็ก
  5. ตรวจสอบไฟล์ดัมพ์เพื่อดูข้อกังวลด้านความเป็นส่วนตัวก่อนแชร์กับผู้อื่น

ข้อเสนอแนะ

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

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

คืนค่า

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

  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

สื่อ.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

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

"ไทม์ไลน์" media.log คือชุดของรายการบันทึกที่ยังคงลำดับที่เกี่ยวข้องไว้ ตามแบบแผน แต่ละเธรดควรใช้ไทม์ไลน์ของตัวเอง

ประโยชน์

ข้อดีของระบบ media.log คือ:

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

สถาปัตยกรรม

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

สถาปัตยกรรมก่อน media.log

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

ประเด็นเด่น:

  • init forks และ execs 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 ของเธรดสามารถใช้ได้ทั้งกับเธรดเอง และโดยการดำเนินการของ Binder NBLog::Writer ไม่มีการยกเว้นร่วมกันโดยนัยต่อไทม์ไลน์ ดังนั้นโปรดตรวจสอบให้แน่ใจว่าบันทึกทั้งหมดเกิดขึ้นภายในบริบทที่ mLock ของ mutex ของเธรดถูกเก็บไว้

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

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

NBLog API ฉบับเต็มอยู่ที่ 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 จะแสดงสองกระบวนการ:
  • สื่อ.log
  • มีเดียเซิร์ฟเวอร์

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

แสดงไทม์ไลน์

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

dumpsys media.log

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

กู้คืนบันทึกหลังจากเซิร์ฟเวอร์สื่อเสียชีวิต

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

dumpsys media.log