โดยปกติแล้ว อินพุตเสียงจะมาจากไมโครโฟนในตัว ไมโครโฟนภายนอก หรือ อินเทอร์เฟซเสียงที่เชื่อมต่อกับอุปกรณ์ นอกจากนี้ อินพุตเสียงยังมาจากการสนทนาทางโทรศัพท์ได้ด้วย
บางครั้งแอปตั้งแต่ 2 แอปขึ้นไปอาจต้องการ "บันทึก" เสียงอินพุตเดียวกัน โดยอาจทำงานที่แตกต่างกัน ตัวอย่างเช่น แอปบางแอปที่รับเสียงอาจ "บันทึก" เช่น เครื่องบันทึกเสียงอย่างง่าย ในขณะที่แอปอื่นๆ อาจ "ฟัง" เช่น Google Assistant หรือบริการการช่วยเหลือพิเศษที่ ตอบสนองต่อคำสั่งเสียง
ไม่ว่าจะในกรณีใดก็ตาม แอปเหล่านี้ต้องการรับข้อมูลเสียง ตลอดทั้งหน้านี้ เราจะใช้คำว่า "จับภาพ" ไม่ว่าแอปจะบันทึกหรือเพียงแค่ฟังก็ตาม
หากแอปตั้งแต่ 2 แอปขึ้นไปต้องการบันทึกเสียงพร้อมกัน อาจเกิดปัญหาในการ ส่งสัญญาณเสียงจากแหล่งเดียวกันไปยังแอปทั้งหมด หน้านี้อธิบาย วิธีที่ระบบ Android แชร์อินพุตเสียงระหว่างแอปหลายแอปที่บันทึกเสียง
ลักษณะการทำงานก่อน Android 10
ก่อน Android 10 สตรีมเสียงอินพุตจะบันทึกได้โดยแอปเดียวเท่านั้นในแต่ละครั้ง
หากแอปบางแอปบันทึกหรือฟังเสียงอยู่แล้ว แอปของคุณจะ
สร้างออบเจ็กต์ AudioRecord
ได้ แต่ระบบจะแสดงข้อผิดพลาดเมื่อคุณเรียกใช้
AudioRecord.startRecording()
และจะไม่เริ่มการบันทึก
ข้อยกเว้นหนึ่งของกฎนี้คือเมื่อแอปที่มีสิทธิ์ (เช่น Google Assistant หรือ
บริการการช่วยเหลือพิเศษ) มีสิทธิ์
android.permission.CAPTURE_AUDIO_HOTWORD
และใช้แหล่งที่มาของเสียงประเภท
HOTWORD
ในกรณีนี้ แอปอื่นอาจเริ่มบันทึกได้ เมื่อเกิดเหตุการณ์ดังกล่าว แอปที่มีสิทธิ์พิเศษจะสิ้นสุดการทำงานและแอปใหม่จะบันทึกอินพุต
นอกจากนี้ ใน Android 9 ยังมีการเปลี่ยนแปลงอีกอย่างหนึ่งคือ มีเพียงแอปที่ทำงานอยู่เบื้องหน้า (หรือบริการที่ทำงานอยู่เบื้องหน้า) เท่านั้นที่สามารถบันทึกเสียงอินพุตได้ เมื่อแอปที่ไม่มี บริการที่ทำงานอยู่เบื้องหน้าหรือคอมโพเนนต์ UI ที่ทำงานอยู่เบื้องหน้าเริ่มบันทึก แอปจะ ทำงานต่อไปแต่จะไม่มีเสียง แม้ว่าจะเป็นแอปเดียวที่บันทึก เสียงในขณะนั้นก็ตาม
ลักษณะการทำงานของ Android 10
ลักษณะการทำงานก่อน Android 10 คือ "มาก่อนได้รับสิทธิ์ก่อน" เมื่อแอปเริ่มจับเสียงแล้ว แอปอื่นๆ จะเข้าถึง อินพุตเสียงไม่ได้จนกว่าแอปที่จับเสียงจะหยุด
Android 10 กำหนดรูปแบบลำดับความสำคัญที่สามารถสลับสตรีมเสียงอินพุต ระหว่างแอปขณะที่แอปทำงานอยู่ ในกรณีส่วนใหญ่ หากแอปใหม่ได้รับอินพุตเสียง แอปที่บันทึกเสียงก่อนหน้านี้จะยังคงทำงานต่อไป แต่จะไม่มีเสียง ในบางกรณี ระบบอาจยังคงส่งเสียงไปยังทั้ง 2 แอปได้ เราได้อธิบายสถานการณ์การแชร์ต่างๆ ไว้ด้านล่าง
โดยรูปแบบนี้จะคล้ายกับวิธีที่โฟกัสเสียงจัดการแอปหลายแอป ที่แข่งขันกันเพื่อใช้เอาต์พุตเสียง อย่างไรก็ตาม การจัดการโฟกัสเสียงจะดำเนินการโดยคำขอแบบเป็นโปรแกรมเพื่อรับและปล่อยโฟกัส ขณะที่รูปแบบการสลับอินพุต ที่อธิบายไว้ที่นี่จะอิงตามนโยบายการจัดลําดับความสําคัญซึ่งจะนําไปใช้ โดยอัตโนมัติเมื่อใดก็ตามที่แอปใหม่เริ่มบันทึกเสียง
Android จะแยกแอปออกเป็น 2 ประเภทเพื่อวัตถุประสงค์ในการบันทึกเสียง
- ผู้ใช้เป็นผู้ติดตั้งแอป "ธรรมดา"
- แอปที่มีสิทธิ์จะติดตั้งมาล่วงหน้าในอุปกรณ์ ซึ่งรวมถึง Google Assistant และบริการการช่วยเหลือพิเศษทั้งหมด
นอกจากนี้ ระบบจะจัดการแอปแตกต่างกัน
หากแอปใช้แหล่งเสียงที่ "คำนึงถึงความเป็นส่วนตัว" ดังนี้
CAMCORDER
หรือ VOICE_COMMUNICATION
กฎการจัดลําดับความสําคัญสําหรับการใช้และแชร์อินพุตเสียงมีดังนี้
- แอปที่มีสิทธิ์จะมีลำดับความสำคัญสูงกว่าแอปทั่วไป
- แอปที่มี UI เบื้องหน้าที่มองเห็นได้จะมีลำดับความสำคัญสูงกว่าแอปในเบื้องหลัง
- แอปที่บันทึกเสียงจากแหล่งที่มาที่คำนึงถึงความเป็นส่วนตัวจะมีลำดับความสำคัญสูงกว่าแอปที่ไม่ได้บันทึกเสียงจากแหล่งที่มาดังกล่าว
- แอปทั่วไป 2 แอปจะบันทึกเสียงพร้อมกันไม่ได้
- ในบางสถานการณ์ แอปที่มีสิทธิ์พิเศษจะแชร์อินพุตเสียงกับแอปอื่นได้
- หากแอปพื้นหลัง 2 แอปที่มีลำดับความสำคัญเดียวกันกำลังบันทึกเสียง แอปที่เริ่มทำงานล่าสุดจะมีลำดับความสำคัญสูงกว่า
สถานการณ์การแชร์
เมื่อแอป 2 แอปพยายามบันทึกเสียง ทั้ง 2 แอปอาจรับสัญญาณอินพุตได้ หรือแอปใดแอปหนึ่งอาจได้รับ ความเงียบ
สถานการณ์หลักๆ มี 4 กรณีดังนี้
- Assistant + แอปทั่วไป
- บริการการช่วยเหลือพิเศษ + แอปทั่วไป
- แอปทั่วไป 2 แอป
- การโทรด้วยเสียง + แอปทั่วไป
Assistant + แอปทั่วไป
Assistant เป็นแอปที่มีสิทธิ์พิเศษเนื่องจากมีการติดตั้งไว้ล่วงหน้าและมีบทบาท RoleManager.ROLE_ASSISTANT
ระบบจะถือว่าแอปอื่นๆ ที่ติดตั้งไว้ล่วงหน้าซึ่งมีบทบาทนี้ในลักษณะเดียวกัน
Android จะแชร์เสียงที่ป้อนตามกฎต่อไปนี้
Assistant รับเสียงได้ (ไม่ว่าจะอยู่ในเบื้องหน้าหรือเบื้องหลัง) เว้นแต่แอปอื่นที่ใช้แหล่งเสียงที่คำนึงถึงความเป็นส่วนตัวจะบันทึกเสียงอยู่แล้ว
แอปจะรับเสียงได้เว้นแต่ Assistant จะมีคอมโพเนนต์ UI ที่มองเห็นได้ที่ด้านบนของหน้าจอ
โปรดทราบว่าทั้ง 2 แอปจะได้รับเสียงเฉพาะเมื่อ Assistant ทำงานในเบื้องหลัง และอีกแอปไม่ได้บันทึกจากแหล่งเสียงที่ละเอียดอ่อนด้านความเป็นส่วนตัว
บริการการช่วยเหลือพิเศษ + แอปทั่วไป
AccessibilityService
ต้องมีการประกาศที่เข้มงวด
Android จะแชร์เสียงที่ป้อนตามกฎต่อไปนี้
หาก UI ของบริการอยู่ด้านบน ทั้งบริการและแอปจะได้รับ อินพุตเสียง ลักษณะการทำงานนี้มีฟังก์ชันต่างๆ เช่น การควบคุมการโทรด้วยเสียงหรือการจับภาพวิดีโอ ด้วยคำสั่งเสียง
หากบริการไม่ได้อยู่ด้านบนสุด ระบบจะถือว่ากรณีนี้เป็นกรณีแอป 2 แอปธรรมดาตามที่ระบุไว้ด้านล่าง
แอปทั่วไป 2 แอป
เมื่อแอป 2 แอปบันทึกพร้อมกัน จะมีเพียงแอปเดียวเท่านั้นที่จะได้รับเสียง ส่วนอีกแอปจะไม่มีเสียง
Android จะแชร์เสียงที่ป้อนตามกฎต่อไปนี้
- หากทั้ง 2 แอปไม่ไวต่อความเป็นส่วนตัว แอปที่มี UI อยู่ด้านบนจะได้รับเสียง หากทั้ง 2 แอปไม่มี UI แอปที่เริ่มจับภาพล่าสุดจะได้รับเสียง
- หากแอปใดแอปหนึ่งมีความละเอียดอ่อนด้านความเป็นส่วนตัว แอปนั้นจะได้รับเสียงและ อีกแอปจะไม่มีเสียงแม้ว่าจะมี UI อยู่ด้านบนหรือเริ่มบันทึก เมื่อเร็วๆ นี้ก็ตาม
- หากทั้ง 2 แอปมีความละเอียดอ่อนด้านความเป็นส่วนตัว แอปที่เริ่มบันทึกเสียงล่าสุดจะได้รับเสียง ส่วนอีกแอปจะไม่ได้ยินเสียง
การโทรด้วยเสียง + แอปทั่วไป
การโทรด้วยเสียงจะทำงานอยู่หากโหมดเสียงที่ AudioManager.getMode()
แสดงผลเป็น MODE_IN_CALL
หรือ MODE_IN_COMMUNICATION
Android จะแชร์เสียงที่ป้อนตามกฎต่อไปนี้
- การโทรจะได้รับเสียงเสมอ
- แอปจะบันทึกเสียงได้หากเป็นบริการการช่วยเหลือพิเศษ
แอปจะบันทึกการโทรด้วยเสียงได้หากเป็นแอปที่มีสิทธิ์ (ติดตั้งมาล่วงหน้า) ที่มีสิทธิ์
CAPTURE_AUDIO_OUTPUT
หากต้องการบันทึกการอัปลิงก์ (TX), ดาวน์ลิงก์ (RX) หรือทั้ง 2 อย่างของการโทรด้วยเสียง แอปต้อง ระบุแหล่งที่มาของเสียง
MediaRecorder.AudioSource.VOICE_UPLINK
หรือMediaRecorder.AudioSource.VOICE_DOWNLINK
และ/หรือAudioDeviceInfo.TYPE_TELEPHONY
ของอุปกรณ์
ลักษณะการทำงานของ Android 11
Android 11 (API ระดับ 30) จะใช้รูปแบบลำดับความสำคัญของ Android 10
ที่อธิบายไว้ข้างต้น นอกจากนี้ ยังมีเมธอดใหม่ใน AudioRecord
, MediaRecorder
และ
AAudioStream
ที่เปิดและปิดใช้ความสามารถในการบันทึกเสียงพร้อมกัน
โดยไม่คำนึงถึงกรณีการใช้งานที่เลือก
วิธีการใหม่มีดังนี้
AudioRecord.Builder.setPrivacySensitive()
AudioRecord.isPrivacySensitive()
MediaRecorder.setPrivacySensitive()
MediaRecorder.isPrivacySensitive()
AAudioStreamBuilder_setPrivacySensitive()
AAudioStream_isPrivacySensitive()
เมื่อ setPrivacySensitive()
เป็น true
กรณีการใช้งานการจับภาพจะเป็นแบบส่วนตัวและแม้แต่
Assistant ที่มีสิทธิ์ก็ไม่สามารถจับภาพพร้อมกันได้ การตั้งค่านี้จะลบล้าง
ลักษณะการทำงานเริ่มต้นซึ่งขึ้นอยู่กับแหล่งที่มาของเสียง ตัวอย่างเช่น
VOICE_COMMUNICATION
จะเป็นแบบส่วนตัวโดยค่าเริ่มต้น แต่ UNPROCESSED
จะไม่เป็น
การเปลี่ยนแปลงการกำหนดค่า
เมื่อแอปหลายแอปบันทึกเสียงพร้อมกัน จะมีเพียง 1 หรือ 2 แอปเท่านั้นที่ "ทำงาน" (รับเสียง) ส่วนแอปอื่นๆ จะปิดเสียง (รับความเงียบ) เมื่อ แอปที่ใช้งานอยู่มีการเปลี่ยนแปลง เฟรมเวิร์กเสียงอาจกำหนดค่าเส้นทางเสียงใหม่ ตามกฎต่อไปนี้
- อุปกรณ์อินพุตเสียงสำหรับแต่ละแอปที่ใช้งานอยู่อาจเปลี่ยนแปลง (เช่น จาก ไมโครโฟนในตัวเป็นชุดหูฟังบลูทูธที่เชื่อมต่อ)
- ระบบจะเปิดใช้การประมวลผลล่วงหน้าที่เชื่อมโยงกับแอปที่ใช้งานอยู่ซึ่งมีลำดับความสำคัญสูงสุด ระบบจะไม่สนใจการประมวลผลล่วงหน้าอื่นๆ ทั้งหมด
เนื่องจากระบบอาจปิดเสียงแอปที่ทำงานอยู่เมื่อแอปที่มีลำดับความสำคัญสูงกว่าเริ่มทำงาน
คุณจึงลงทะเบียน AudioManager.AudioRecordingCallback
ในออบเจ็กต์ AudioRecord
หรือ MediaRecorder
เพื่อรับการแจ้งเตือนเมื่อมีการเปลี่ยนแปลงการกำหนดค่าได้
การเปลี่ยนแปลงที่อาจเกิดขึ้นมีดังนี้
- บันทึกเสียงที่ปิดหรือเปิดเสียง
- เปลี่ยนอุปกรณ์แล้ว
- มีการเปลี่ยนแปลงการประมวลผลล่วงหน้า
- มีการเปลี่ยนแปลงพร็อพเพอร์ตี้ของสตรีม (อัตราการสุ่มตัวอย่าง มาสก์ช่อง รูปแบบตัวอย่าง)
คุณต้องเรียกใช้
AudioRecord.registerAudioRecordingCallback()
ก่อนที่จะเริ่มการจับภาพ
ระบบจะเรียกใช้การเรียกกลับเมื่อแอปได้รับเสียงและมีการเปลี่ยนแปลงเท่านั้น
เมธอด onRecordingConfigChanged()
จะแสดงผล AudioRecordingConfiguration
ที่มีสถานะการบันทึกเสียงปัจจุบัน ใช้วิธีการต่อไปนี้เพื่อดูข้อมูลเกี่ยวกับการเปลี่ยนแปลง
isClientSilenced()
- แสดงผลเป็นจริงหากระบบปิดเสียงที่ส่งคืนไปยังไคลเอ็นต์เนื่องจากนโยบายการบันทึก
getAudioDevice()
- แสดงอุปกรณ์เสียงที่ใช้งานอยู่
getEffects()
- แสดงผลเอฟเฟกต์การประมวลผลล่วงหน้าที่ใช้งานอยู่ โปรดทราบว่าเอฟเฟกต์ที่ใช้งานอยู่อาจไม่เหมือนกับที่
getClientEffects()
แสดงผลหากไคลเอ็นต์ไม่ใช่แอปที่ใช้งานอยู่ที่มีลำดับความสำคัญสูงสุด getFormat()
- แสดงผลพร็อพเพอร์ตี้สตรีม โปรดทราบว่าข้อมูลเสียงจริงที่ไคลเอ็นต์ได้รับจะเป็นไปตามรูปแบบที่
getClientFormat()
ส่งกลับมาเสมอ เฟรมเวิร์กจะทำการเปลี่ยนการสุ่มตัวอย่าง ช่อง และรูปแบบที่จำเป็นโดยอัตโนมัติจากรูปแบบที่ใช้ในอินเทอร์เฟซฮาร์ดแวร์เป็นรูปแบบที่ไคลเอ็นต์ระบุ AudioRecord.getActiveRecordingConfiguration()
- แสดงผลการกำหนดค่าการบันทึกที่ใช้งานอยู่
คุณดูภาพรวมของการบันทึกที่ใช้งานอยู่ทั้งหมดในอุปกรณ์ได้โดยโทรไปที่
AudioManager.getActiveRecordingConfigurations()