อินพุตเสียงมักมาจากไมโครโฟนในตัว ไมโครโฟนภายนอก หรืออินเทอร์เฟซเสียงที่เชื่อมต่อกับอุปกรณ์ อินพุตเสียงอาจมาจากการสนทนาทางโทรศัพท์ด้วย
บางครั้งแอปตั้งแต่ 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 จะแชร์เสียงอินพุตตามกฎต่อไปนี้
- หากแอปใดแอปหนึ่งไม่เกี่ยวข้องกับความเป็นส่วนตัว แอปที่มี UI อยู่ด้านบนจะได้รับเสียง หากไม่มีแอปใดมีแอป 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
ที่เปิดและปิดใช้ความสามารถในการบันทึกเสียงพร้อมกัน ไม่ว่าจะใช้ Use Case ใดก็ตาม
โดยวิธีการใหม่มีดังนี้
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()