คำเตือน: OpenSL ES เลิกใช้งานแล้ว นักพัฒนาซอฟต์แวร์ควรใช้ไลบรารี Oboe แบบโอเพนซอร์สซึ่งมีอยู่ใน GitHub Oboe เป็น Wrapper ของ C++ ที่มี API ที่คล้ายกับ AAudio มาก Oboe จะเรียกใช้ AAudio เมื่อ AAudio พร้อมใช้งาน และจะใช้ OpenSL ES แทนหาก AAudio ไม่พร้อมใช้งาน
หมายเหตุในส่วนนี้เป็นข้อมูลเสริมสำหรับข้อกำหนดของ OpenSL ES 1.0.1
การสร้างวัตถุและอินเทอร์เฟซ
2 ด้านของรูปแบบการเขียนโปรแกรม OpenSL ES ที่นักพัฒนาแอปมือใหม่อาจไม่คุ้นเคยคือความแตกต่างระหว่างออบเจ็กต์กับอินเทอร์เฟซ และลําดับการเริ่มต้น
กล่าวโดยย่อ ออบเจ็กต์ OpenSL ES คล้ายกับแนวคิดออบเจ็กต์ในภาษาโปรแกรม เช่น Java และ C++ ยกเว้นออบเจ็กต์ OpenSL ES จะมองเห็นได้ผ่านอินเทอร์เฟซที่เกี่ยวข้องเท่านั้น
ซึ่งรวมถึงอินเทอร์เฟซเริ่มต้นของออบเจ็กต์ทั้งหมดที่เรียกว่า SLObjectItf
จะไม่มีแฮนเดิลสำหรับออบเจ็กต์เอง มีเพียงแฮนเดิลสำหรับอินเทอร์เฟซ SLObjectItf
ของออบเจ็กต์เท่านั้น
ระบบจะสร้างออบเจ็กต์ OpenSL ES ก่อน ซึ่งจะแสดงผล SLObjectItf
จากนั้นจึงสร้าง ซึ่งคล้ายกับรูปแบบการเขียนโปรแกรมทั่วไปของการสร้างออบเจ็กต์ครั้งแรก (ซึ่งไม่ควรล้มเหลวนอกเหนือจากการไม่มีหน่วยความจำหรือพารามิเตอร์ที่ไม่ถูกต้อง) จากนั้นจึงดำเนินการเริ่มต้นให้เสร็จสมบูรณ์ (ซึ่งอาจล้มเหลวเนื่องจากมีทรัพยากรไม่เพียงพอ) ขั้นตอนการสร้างเป็นขั้นตอนที่เหมาะสําหรับการจัดสรรทรัพยากรเพิ่มเติมหากจําเป็น
แอปพลิเคชันจะระบุอาร์เรย์ของอินเทอร์เฟซที่ต้องการซึ่งมีแผนที่จะนำมาใช้ในภายหลัง ซึ่งเป็นส่วนหนึ่งของ API ในการสร้างออบเจ็กต์ โปรดทราบว่าอาร์เรย์นี้ไม่ได้รับอินเทอร์เฟซโดยอัตโนมัติ เป็นเพียงการระบุความตั้งใจในอนาคตที่จะรับอินเทอร์เฟซเท่านั้น อินเทอร์เฟซจะแบ่งออกเป็นโดยนัยหรือชัดแจ้ง อินเทอร์เฟซที่ชัดเจนต้องแสดงอยู่ในอาร์เรย์หากจะรับในภายหลัง คุณไม่จำเป็นต้องแสดงอินเทอร์เฟซโดยนัยในอาร์เรย์การสร้างออบเจ็กต์ แต่ก็ไม่ได้เสียหายหากแสดง OpenSL ES มีอินเทอร์เฟซอีก 1 ประเภทที่เรียกว่า dynamic ซึ่งไม่จำเป็นต้องระบุไว้ในอาร์เรย์ออบเจ็กต์สร้างออบเจ็กต์และเพิ่มในภายหลังได้หลังจากที่สร้างออบเจ็กต์แล้ว การใช้ Android มอบฟีเจอร์อำนวยความสะดวกที่ช่วยหลีกเลี่ยงความซับซ้อนนี้ ซึ่งอธิบายไว้ในอินเทอร์เฟซแบบไดนามิกขณะสร้างออบเจ็กต์
หลังจากสร้างและทำให้ออบเจ็กต์เป็นจริงแล้ว แอปพลิเคชันควรรับอินเทอร์เฟซสำหรับแต่ละฟีเจอร์ที่ต้องการโดยใช้ GetInterface
ใน SLObjectItf
เริ่มต้น
สุดท้าย ออบเจ็กต์นี้พร้อมใช้งานผ่านอินเทอร์เฟซ แต่โปรดทราบว่าออบเจ็กต์บางรายการต้องมีการตั้งค่าเพิ่มเติม โดยเฉพาะอย่างยิ่ง เครื่องเล่นเสียงที่มีแหล่งข้อมูล URI จะต้องเตรียมการเพิ่มเติมเล็กน้อยเพื่อตรวจหาข้อผิดพลาดในการเชื่อมต่อ ดูรายละเอียดได้ในส่วนการอ่านล่วงหน้าของโปรแกรมเล่นเสียง
หลังจากแอปพลิเคชันใช้งานออบเจ็กต์เสร็จแล้ว คุณควรทำลายออบเจ็กต์อย่างชัดเจน ดูส่วนทำลายด้านล่าง
การอ่านล่วงหน้าของโปรแกรมเล่นเสียง
สำหรับโปรแกรมเล่นเสียงที่มีแหล่งข้อมูล URI Object::Realize
จะจัดสรรทรัพยากร แต่ไม่เชื่อมต่อกับแหล่งข้อมูล (เตรียม) หรือเริ่มดึงข้อมูลล่วงหน้า ซึ่งจะเกิดขึ้นเมื่อตั้งค่าสถานะผู้เล่นเป็น SL_PLAYSTATE_PAUSED
หรือ SL_PLAYSTATE_PLAYING
ข้อมูลบางอย่างอาจยังไม่ทราบจนกว่าจะเข้าสู่ช่วงท้ายของลำดับนี้ โดยเฉพาะอย่างยิ่ง ในช่วงแรก Player::GetDuration
จะแสดงผลเป็น SL_TIME_UNKNOWN
และ MuteSolo::GetChannelCount
จะแสดงผลสำเร็จโดยมีจํานวนช่องเป็น 0 หรือแสดงผลลัพธ์ข้อผิดพลาด SL_RESULT_PRECONDITIONS_VIOLATED
และ API ดังกล่าวจะแสดงผลค่าที่เหมาะสม
หลังจากทราบค่าแล้ว
พร็อพเพอร์ตี้อื่นๆ ที่ไม่รู้จักในตอนแรก ได้แก่ อัตราตัวอย่างและประเภทเนื้อหาสื่อจริง ซึ่งพิจารณาจากการตรวจสอบส่วนหัวของเนื้อหา (ไม่ใช่ประเภท MIME และประเภทคอนเทนเนอร์ที่แอปพลิเคชันระบุ) นอกจากนี้ ระบบยังพิจารณาผลลัพธ์เหล่านี้ในภายหลังระหว่างการเตรียม/ดึงข้อมูลล่วงหน้า แต่ไม่มี API ให้ดึงข้อมูล
อินเทอร์เฟซสถานะการเรียกข้อมูลล่วงหน้ามีประโยชน์ในการตรวจหาว่าข้อมูลทั้งหมดพร้อมใช้งานแล้ว หรือแอปพลิเคชันสามารถทำการสำรวจเป็นระยะๆ โปรดทราบว่าข้อมูลบางอย่าง เช่น ระยะเวลาของ MP3 สตรีมมิง อาจไม่เป็นที่รู้จักเลย
อินเทอร์เฟซสถานะการดึงข้อมูลล่วงหน้ายังเป็นประโยชน์ในการตรวจหาข้อผิดพลาดด้วย ลงทะเบียนการเรียกกลับ และเปิดใช้เหตุการณ์ SL_PREFETCHEVENT_FILLLEVELCHANGE
และ SL_PREFETCHEVENT_STATUSCHANGE
เป็นอย่างน้อย หากทั้ง 2 เหตุการณ์นี้ส่งพร้อมกัน และ PrefetchStatus::GetFillLevel
รายงานระดับ 0 และ PrefetchStatus::GetPrefetchStatus
รายงาน SL_PREFETCHSTATUS_UNDERFLOW
แสดงว่าแหล่งข้อมูลมีข้อผิดพลาดที่กู้คืนไม่ได้ ซึ่งรวมถึงกรณีที่เชื่อมต่อกับแหล่งข้อมูลไม่ได้เนื่องจากไม่มีชื่อไฟล์ในเครื่องหรือ URI ของเครือข่ายไม่ถูกต้อง
เราคาดว่า OpenSL ES เวอร์ชันถัดไปจะเพิ่มการรองรับที่ชัดเจนยิ่งขึ้นในการจัดการข้อผิดพลาดในแหล่งข้อมูล อย่างไรก็ตาม เราจะยังคงรองรับวิธีการปัจจุบันในการรายงานข้อผิดพลาดที่กู้คืนไม่ได้ต่อไปเพื่อรองรับการใช้งานไฟล์ไบนารีในอนาคต
โดยสรุป ลำดับโค้ดที่แนะนำคือ
Engine::CreateAudioPlayer
Object:Realize
Object::GetInterface
เป็นเวลาSL_IID_PREFETCHSTATUS
PrefetchStatus::SetCallbackEventsMask
PrefetchStatus::SetFillUpdatePeriod
PrefetchStatus::RegisterCallback
Object::GetInterface
เป็นเวลาSL_IID_PLAY
Play::SetPlayState
ถึงSL_PLAYSTATE_PAUSED
หรือSL_PLAYSTATE_PLAYING
หมายเหตุ: การเตรียมและการจัดเก็บข้อมูลล่วงหน้าจะเกิดขึ้นที่นี่ ในระหว่างนี้ระบบจะเรียกใช้การเรียกกลับพร้อมการอัปเดตสถานะเป็นระยะ
ทำลาย
อย่าลืมทำลายออบเจ็กต์ทั้งหมดเมื่อออกจากแอปพลิเคชันของคุณ คุณควรทำลายออบเจ็กต์ตามลำดับย้อนกลับของการสร้าง เนื่องจากการทำลายออบเจ็กต์ที่มีออบเจ็กต์ที่ขึ้นต่อกันไม่ปลอดภัย เช่น ทำลายตามลำดับต่อไปนี้ เครื่องเล่นและเครื่องบันทึกเสียง มิกซ์เอาต์พุต และสุดท้ายเครื่องยนต์
OpenSL ES ไม่รองรับการเก็บขยะอัตโนมัติหรือการนับอินเทอร์เฟซอ้างอิง หลังจากที่คุณเรียกใช้ Object::Destroy
อินเทอร์เฟซที่ยังเพิ่มขึ้นทั้งหมดซึ่งได้มาจากออบเจ็กต์ที่เชื่อมโยงจะไม่มีการกำหนด
การใช้งาน Android OpenSL ES ไม่ตรวจพบการใช้อินเทอร์เฟซดังกล่าวอย่างไม่ถูกต้อง การใช้อินเทอร์เฟซดังกล่าวต่อไปหลังจากที่วัตถุถูกทำลายอาจทำให้แอปพลิเคชันขัดข้องหรือทำงานในลักษณะที่คาดเดาไม่ได้
เราขอแนะนำให้คุณตั้งค่าทั้งอินเทอร์เฟซของออบเจ็กต์หลักและอินเทอร์เฟซที่เกี่ยวข้องทั้งหมดเป็น NULL
อย่างชัดเจน ซึ่งเป็นส่วนหนึ่งของลําดับการทำลายออบเจ็กต์ ซึ่งจะช่วยป้องกันการใช้แฮนเดิลอินเทอร์เฟซที่ล้าสมัยโดยไม่ตั้งใจ
การแพนกล้องแบบสเตอริโอ
เมื่อใช้ Volume::EnableStereoPosition
เพื่อเปิดใช้การปรับเสียงสเตอริโอของแหล่งที่มาแบบโมโน ระดับกำลังเสียงทั้งหมดจะลดลง 3 dB การดำเนินการนี้จำเป็นเพื่อให้ระดับกำลังเสียงทั้งหมดคงที่เมื่อมีการพากย์เสียงจากช่องหนึ่งไปยังอีกช่องหนึ่ง ดังนั้น ให้เปิดใช้การกำหนดตำแหน่งเสียงสเตอริโอเฉพาะเมื่อจำเป็นเท่านั้น ดูข้อมูลเพิ่มเติมได้ที่บทความใน Wikipedia เกี่ยวกับการปรับระดับเสียง
การติดต่อกลับและชุดข้อความ
โดยทั่วไปแล้วตัวแฮนเดิลการเรียกกลับจะเรียกใช้แบบซิงค์เมื่อการติดตั้งใช้งานตรวจพบเหตุการณ์ จุดนี้ไม่พร้อมกันในแง่ของแอปพลิเคชัน ดังนั้นคุณควรใช้กลไกการซิงค์แบบไม่บล็อกเพื่อควบคุมการเข้าถึงตัวแปรที่แชร์ระหว่างแอปพลิเคชันและเครื่องจัดการ Callback ในโค้ดตัวอย่าง เช่น สำหรับคิวบัฟเฟอร์ เราได้ละเว้นการซิงค์นี้หรือใช้การซิงค์แบบบล็อกเพื่อความเรียบง่าย อย่างไรก็ตาม การซิงค์ที่ไม่บล็อกอย่างเหมาะสมเป็นสิ่งสําคัญสําหรับโค้ดเวอร์ชันที่ใช้งานจริง
แฮนเดิลการเรียกกลับจะเรียกจากเธรดภายในที่ไม่ใช่แอปพลิเคชันซึ่งไม่ได้แนบอยู่กับรันไทม์ Android จึงไม่มีสิทธิ์ใช้ JNI เนื่องจากเทรดภายในเหล่านี้สำคัญต่อความสมบูรณ์ของการใช้งาน OpenSL ES ตัวแฮนเดิล Callback จึงไม่ควรบล็อกหรือทำงานหนักเกินไป
หากตัวแฮนเดิล Callback จำเป็นต้องใช้ JNI หรือเรียกใช้งานที่ไม่ใช่สัดส่วนกับ Callback ตัวแฮนเดิลควรโพสต์เหตุการณ์เพื่อให้เทรดอื่นประมวลผลแทน ตัวอย่างของภาระงานของ Callback ที่ยอมรับได้ ได้แก่ การแสดงผลและการจัดคิวบัฟเฟอร์เอาต์พุตถัดไป (สำหรับ AudioPlayer) การประมวลผลบัฟเฟอร์อินพุตที่ป้อนและจัดคิวบัฟเฟอร์ว่างถัดไป (สำหรับ AudioRecorder) หรือ API แบบง่าย เช่น กลุ่ม Get ส่วนใหญ่ ดูข้อมูลเกี่ยวกับปริมาณงานได้ที่ส่วนประสิทธิภาพด้านล่าง
โปรดทราบว่าการดำเนินการแบบย้อนกลับนั้นปลอดภัย นั่นคือ อนุญาตให้เธรดแอปพลิเคชัน Android ที่เข้าสู่ JNI เรียกใช้ OpenSL ES API ได้โดยตรง รวมถึง API ที่บล็อก อย่างไรก็ตาม ไม่แนะนำให้บล็อกการเรียกใช้จากเทรดหลัก เนื่องจากอาจส่งผลให้เกิดแอปพลิเคชันไม่ตอบสนอง (ANR)
การกำหนดเกี่ยวกับเธรดที่เรียกตัวแฮนเดิลการเรียกกลับนั้นขึ้นอยู่กับการใช้งานเป็นส่วนใหญ่ ความยืดหยุ่นนี้ช่วยให้เพิ่มประสิทธิภาพได้ในอนาคต โดยเฉพาะในอุปกรณ์แบบหลายแกน
ไม่มีการรับประกันว่าเธรดที่แฮนเดิลการเรียกกลับทํางานอยู่จะมีตัวตนเดียวกันในการเรียกที่แตกต่างกัน ดังนั้น อย่าคาดหวังว่า pthread_t
ที่ pthread_self()
แสดงหรือ pid_t
ที่ gettid()
แสดงจะสอดคล้องกันในทุกการเรียกใช้ ด้วยเหตุเดียวกันนี้ อย่าใช้ API พื้นที่เก็บข้อมูลในระบบของเธรด (TLS) เช่น pthread_setspecific()
และ pthread_getspecific()
จากคอลแบ็ก
การใช้งานจะรับประกันว่าจะไม่มีการทำ Callback พร้อมกันในประเภทเดียวกันสําหรับออบเจ็กต์เดียวกัน อย่างไรก็ตาม คุณสามารถเรียกกลับแบบต่างๆ สําหรับออบเจ็กต์เดียวกันพร้อมกันในชุดข้อความที่แตกต่างกันได้
ประสิทธิภาพ
เนื่องจาก OpenSL ES เป็น C API ดั้งเดิม เทรดของแอปพลิเคชันที่ไม่ใช่รันไทม์ซึ่งเรียกใช้ OpenSL ES จึงไม่มีค่าใช้จ่ายเพิ่มเติมที่เกี่ยวข้องกับรันไทม์ เช่น การหยุดชั่วคราวของการเก็บขยะ การใช้ OpenSL ES ไม่ได้ให้ประโยชน์ด้านประสิทธิภาพเพิ่มเติมนอกเหนือจากนี้ ยกเว้นกรณีเดียวที่อธิบายไว้ด้านล่าง โดยเฉพาะอย่างยิ่ง การใช้ OpenSL ES ไม่ได้รับประกันการปรับปรุงต่างๆ เช่น เวลาในการตอบสนองของเสียงที่ต่ำลงและลำดับความสำคัญของการจัดตารางเวลาสูงกว่าที่แพลตฟอร์มให้โดยทั่วไป ในทางกลับกัน เมื่อแพลตฟอร์ม Android และการใช้งานอุปกรณ์บางประเภทพัฒนาไปอย่างต่อเนื่อง แอปพลิเคชัน OpenSL ES ก็อาจได้รับประโยชน์จากการปรับปรุงประสิทธิภาพของระบบในอนาคต
หนึ่งในการเปลี่ยนแปลงดังกล่าวคือการรองรับเวลาในการตอบสนองของเอาต์พุตเสียงที่ลดลง
พื้นฐานในการลดเวลาในการตอบสนองของเอาต์พุตมีให้ใช้งานครั้งแรกใน Android 4.1 (API ระดับ 16) จากนั้นการพัฒนาก็ดำเนินต่อไปใน Android 4.2 (API ระดับ 17) การปรับปรุงเหล่านี้พร้อมใช้งานผ่าน OpenSL ES สําหรับการติดตั้งใช้งานอุปกรณ์ที่อ้างสิทธิ์ฟีเจอร์ android.hardware.audio.low_latency
หากอุปกรณ์ไม่ได้อ้างสิทธิ์ใช้ฟีเจอร์นี้แต่รองรับ Android 2.3 (API ระดับ 9) ขึ้นไป คุณยังคงใช้ OpenSL ES API ได้ แต่เวลาในการตอบสนองของเอาต์พุตอาจนานขึ้น
ระบบจะใช้เส้นทางเวลาในการตอบสนองของเอาต์พุตที่ต่ำลงก็ต่อเมื่อแอปพลิเคชันขอขนาดบัฟเฟอร์และอัตราตัวอย่างที่เข้ากันได้กับการกำหนดค่าเอาต์พุตดั้งเดิมของอุปกรณ์เท่านั้น พารามิเตอร์เหล่านี้จะเจาะจงอุปกรณ์และควรได้รับตามที่อธิบายไว้ด้านล่าง
ตั้งแต่ Android 4.2 (API ระดับ 17) เป็นต้นไป แอปพลิเคชันจะค้นหาอัตราการสุ่มตัวอย่างเอาต์พุตและขนาดบัฟเฟอร์แบบเนทีฟของแพลตฟอร์มหรือแบบเหมาะสมที่สุดสำหรับสตรีมเอาต์พุตหลักของอุปกรณ์ได้ เมื่อรวมกับการทดสอบฟีเจอร์ที่เพิ่งกล่าวถึง ตอนนี้แอปกำหนดค่าตัวเองได้อย่างเหมาะสมแล้วเพื่อลดเวลาในการตอบสนองในอุปกรณ์ที่กล่าวอ้างถึงการรองรับ
สำหรับ Android 4.2 (API ระดับ 17) และเวอร์ชันก่อนหน้า คุณต้องใช้บัฟเฟอร์อย่างน้อย 2 รายการเพื่อให้เวลาในการตอบสนองต่ำลง เริ่มตั้งแต่ Android 4.3 (API ระดับ 18) จำนวนบัฟเฟอร์ที่ 1 เพียงพอสำหรับเวลาในการตอบสนองที่ต่ำลง
อินเทอร์เฟซ OpenSL ES ทั้งหมดสำหรับเอฟเฟกต์เอาต์พุตจะปิดใช้เส้นทางที่มีเวลาในการตอบสนองต่ำ
ลำดับที่แนะนำมีดังนี้
- ตรวจสอบ API ระดับ 9 ขึ้นไปเพื่อยืนยันการใช้ OpenSL ES
- ตรวจสอบฟีเจอร์
android.hardware.audio.low_latency
โดยใช้โค้ด เช่นKotlin
import android.content.pm.PackageManager ... val pm: PackageManager = context.packageManager val claimsFeature: Boolean = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)
Java
import android.content.pm.PackageManager; ... PackageManager pm = getContext().getPackageManager(); boolean claimsFeature = pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);
- ตรวจสอบ API ระดับ 17 ขึ้นไปเพื่อยืนยันการใช้
android.media.AudioManager.getProperty()
- รับอัตราตัวอย่างเอาต์พุตดั้งเดิมหรือขนาดบัฟเฟอร์ที่เหมาะสมที่สุดสำหรับสตรีมเอาต์พุตหลักของอุปกรณ์นี้โดยใช้โค้ดต่อไปนี้
Kotlin
import android.media.AudioManager ... val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager val sampleRate: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE) val framesPerBuffer: String = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
Java
import android.media.AudioManager; ... AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
sampleRate
และframesPerBuffer
เป็นสตริง ก่อนอื่นให้ตรวจสอบว่าค่าเป็น null หรือไม่ จากนั้นแปลงเป็น int โดยใช้Integer.parseInt()
- ใช้ OpenSL ES เพื่อสร้าง AudioPlayer พร้อมตัวระบุตำแหน่งข้อมูลคิวบัฟเฟอร์ PCM
หมายเหตุ: คุณสามารถใช้แอปทดสอบ ขนาดบัฟเฟอร์เสียง เพื่อระบุขนาดบัฟเฟอร์ดั้งเดิมและอัตราการสุ่มตัวอย่างสำหรับแอปพลิเคชันเสียง OpenSL ES ในอุปกรณ์เสียงของคุณ คุณยังสามารถไปที่ GitHub เพื่อดูตัวอย่าง ขนาดบัฟเฟอร์เสียง
จำนวนโปรแกรมเล่นเสียงที่มีเวลาในการตอบสนองต่ำมีจำนวนจำกัด หากแอปพลิเคชันต้องใช้แหล่งที่มาของเสียงมากกว่า 2-3 รายการ ให้พิจารณาผสมเสียงที่ระดับแอปพลิเคชัน อย่าลืมปิดเพลเยอร์เสียงเมื่อกิจกรรมหยุดชั่วคราว เนื่องจากเป็นทรัพยากรส่วนกลางที่แชร์กับแอปอื่นๆ
แฮนเดิลการเรียกกลับคิวบัฟเฟอร์ต้องดำเนินการภายในกรอบเวลาขนาดเล็กที่คาดการณ์ได้เพื่อหลีกเลี่ยงข้อบกพร่องที่ได้ยิน ซึ่งโดยทั่วไปจะหมายถึงไม่มีการบล็อกแบบไม่จำกัดในมิวเทคส์ เงื่อนไข หรือการดำเนินการ I/O ให้ลองใช้try locks, ล็อกและรอโดยมีการหมดเวลา และ อัลกอริทึมที่ไม่มีการบล็อกแทน
การคำนวณที่จำเป็นในการแสดงผลบัฟเฟอร์ถัดไป (สำหรับ AudioPlayer) หรือใช้บัฟเฟอร์ก่อนหน้า (สำหรับ AudioRecord) ควรใช้เวลาประมาณเท่าเดิมสำหรับ Callback แต่ละรายการ หลีกเลี่ยงอัลกอริทึมที่ทำงานในระยะเวลาที่ไม่แน่นอนหรือทำงานเป็นช่วงๆ ในการคํานวณ การคำนวณการเรียกกลับจะเป็นจำนวนมากหากเวลา CPU ที่ใช้ใน Callback ที่กำหนดมากกว่าค่าเฉลี่ยอย่างมาก โดยสรุปแล้ว เวลาที่ใช้ในการประมวลผล CPU ของตัวแฮนเดิลควรมีความแปรปรวนใกล้กับ 0 และตัวแฮนเดิลไม่ควรบล็อกเป็นเวลานาน
เสียงที่มีเวลาในการตอบสนองต่ำใช้ได้กับเอาต์พุตต่อไปนี้เท่านั้น
- ลำโพงในอุปกรณ์
- หูฟังแบบมีสาย
- ชุดหูฟังแบบมีสาย
- เสียงออก
- เสียงดิจิทัลผ่าน USB
ในบางอุปกรณ์ เวลาในการตอบสนองของลำโพงจะสูงกว่าเส้นทางอื่นเนื่องจากมีการประมวลผลสัญญาณดิจิทัลเพื่อแก้ไขและป้องกันลำโพง
ตั้งแต่ Android 5.0 (API ระดับ 21) อุปกรณ์บางรุ่นรองรับอินพุตเสียงที่เวลาในการตอบสนองต่ำลง หากต้องการใช้ประโยชน์จากฟีเจอร์นี้ ก่อนอื่นให้ตรวจสอบว่าเอาต์พุตเวลาในการตอบสนองที่ต่ำกว่าพร้อมใช้งานตามที่อธิบายไว้ข้างต้น ความสามารถในการแสดงผลที่มีเวลาในการตอบสนองต่ำเป็นข้อกำหนดเบื้องต้นสำหรับคุณลักษณะอินพุตที่มีเวลาในการตอบสนองต่ำ จากนั้นสร้าง AudioRecorder ที่มีอัตราการสุ่มตัวอย่างและขนาดบัฟเฟอร์เดียวกับที่จะใช้สำหรับเอาต์พุต อินเทอร์เฟซ OpenSL ES สำหรับเอฟเฟกต์อินพุต
จะป้องกันเส้นทางเวลาในการตอบสนองที่ต่ำกว่า ค่าที่กำหนดล่วงหน้าสำหรับระเบียน SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
ต้องใช้เพื่อให้เวลาในการตอบสนองต่ำลง โดยค่าที่กำหนดล่วงหน้านี้จะปิดใช้การประมวลผลสัญญาณดิจิทัลเฉพาะอุปกรณ์ซึ่งอาจเพิ่มเวลาในการตอบสนองให้กับเส้นทางอินพุต
ดูข้อมูลเพิ่มเติมเกี่ยวกับค่ากำหนดล่วงหน้าของการบันทึกได้ที่ส่วนอินเทอร์เฟซการกําหนดค่า Android ด้านบน
สําหรับอินพุตและเอาต์พุตพร้อมกัน ระบบจะใช้ตัวแฮนเดิลการเสร็จสมบูรณ์ของคิวบัฟเฟอร์แยกต่างหากสําหรับแต่ละฝั่ง ไม่มีการรับประกันลำดับสัมพัทธ์ของคอลแบ็กเหล่านี้หรือการซิงค์นาฬิกาเสียง แม้ว่าทั้ง 2 ด้านจะใช้อัตราตัวอย่างเดียวกันก็ตาม แอปพลิเคชันควรบัฟเฟอร์ข้อมูลด้วยการซิงค์บัฟเฟอร์ที่เหมาะสม
ผลที่ตามมาอย่างหนึ่งของนาฬิกาเสียงที่อาจทำงานอิสระคือความจำเป็นในการแปลงอัตราตัวอย่างแบบไม่พร้อมกัน เทคนิคง่ายๆ (แต่ไม่ใช่วิธีที่ดีที่สุดสำหรับคุณภาพเสียง) สำหรับการเปลี่ยนอัตราการสุ่มตัวอย่างแบบไม่พร้อมกันคือการคัดลอกหรือทิ้งตัวอย่างตามที่จำเป็นใกล้กับจุดที่สัญญาณเป็น 0 Conversion ที่ซับซ้อนมากขึ้นก็เป็นไปได้
โหมดประสิทธิภาพ
ตั้งแต่ Android 7.1 (API ระดับ 25) เป็นต้นไป OpenSL ES ได้เปิดตัววิธีระบุโหมดประสิทธิภาพสำหรับเส้นทางเสียง ตัวเลือกมีดังนี้
SL_ANDROID_PERFORMANCE_NONE
: ไม่มีข้อกำหนดด้านประสิทธิภาพที่เฉพาะเจาะจง อนุญาตให้ใช้เอฟเฟกต์ฮาร์ดแวร์และซอฟต์แวร์SL_ANDROID_PERFORMANCE_LATENCY
: ให้ความสำคัญกับเวลาในการตอบสนอง ไม่มีเอฟเฟกต์ฮาร์ดแวร์หรือซอฟต์แวร์ ซึ่งเป็นโหมดเริ่มต้นSL_ANDROID_PERFORMANCE_LATENCY_EFFECTS
: ให้ความสำคัญกับความล่าช้าในขณะที่ยังคงใช้เอฟเฟกต์ฮาร์ดแวร์และซอฟต์แวร์SL_ANDROID_PERFORMANCE_POWER_SAVING
: ให้ความสำคัญกับการประหยัดพลังงาน อนุญาตให้ใช้เอฟเฟกต์ฮาร์ดแวร์และซอฟต์แวร์
หมายเหตุ: หากคุณไม่ต้องการใช้เส้นทางที่มีเวลาในการตอบสนองต่ำและต้องการใช้ประโยชน์จากเอฟเฟกต์เสียงในตัวของอุปกรณ์ (เช่น เพื่อปรับปรุงคุณภาพเสียงสำหรับการเล่นวิดีโอ) คุณต้องตั้งค่าโหมดประสิทธิภาพเป็น SL_ANDROID_PERFORMANCE_NONE
อย่างชัดเจน
หากต้องการตั้งค่าโหมดประสิทธิภาพ คุณต้องเรียก SetConfiguration
โดยใช้อินเทอร์เฟซการกําหนดค่า Android ดังที่แสดงด้านล่าง
// Obtain the Android configuration interface using a previously configured SLObjectItf. SLAndroidConfigurationItf configItf = nullptr; (*objItf)->GetInterface(objItf, SL_IID_ANDROIDCONFIGURATION, &configItf); // Set the performance mode. SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE; result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(performanceMode));
ความปลอดภัยและสิทธิ์
ตราบใดที่ใครทำอะไรได้ การรักษาความปลอดภัยใน Android จะทำกันในระดับกระบวนการ โค้ดภาษาโปรแกรม Java ไม่สามารถทําอะไรได้มากกว่าโค้ดเนทีฟ และโค้ดเนทีฟก็ทําอะไรได้มากกว่าโค้ดภาษาโปรแกรม Java ไม่ได้ ความแตกต่างเพียงอย่างเดียวระหว่าง 2 เวอร์ชันนี้คือ API ที่พร้อมใช้งาน
แอปพลิเคชันที่ใช้ OpenSL ES ต้องขอสิทธิ์ที่จําเป็นสําหรับ API ที่ไม่ใช่แบบเนทีฟที่คล้ายกัน เช่น หากแอปพลิเคชันบันทึกเสียง ก็จะต้องมีสิทธิ์ android.permission.RECORD_AUDIO
แอปพลิเคชันที่ใช้เอฟเฟกต์เสียงต้องมี
android.permission.MODIFY_AUDIO_SETTINGS
แอปพลิเคชันที่เล่นทรัพยากร URI ของเครือข่ายต้องใช้ android.permission.NETWORK
ดูข้อมูลเพิ่มเติมได้ที่หัวข้อการทํางานกับสิทธิ์ของระบบ
โปรแกรมแยกวิเคราะห์เนื้อหาสื่อและโปรแกรมเปลี่ยนรหัสซอฟต์แวร์อาจทำงานภายในบริบทของแอปพลิเคชัน Android ที่เรียกใช้ OpenSL ES (โปรแกรมเปลี่ยนรหัสฮาร์ดแวร์จะแยกออกมา แต่ขึ้นอยู่กับอุปกรณ์) ทั้งนี้ขึ้นอยู่กับเวอร์ชันแพลตฟอร์มและการใช้งาน เนื้อหาที่มีรูปแบบไม่ถูกต้องซึ่งออกแบบมาเพื่อใช้ประโยชน์จากช่องโหว่ของโปรแกรมแยกวิเคราะห์และตัวแปลงรหัสเป็นเวกเตอร์การโจมตีที่รู้จัก เราขอแนะนำให้คุณเล่นสื่อจากแหล่งที่มาที่เชื่อถือได้เท่านั้น หรือให้คุณแบ่งพาร์ติชันแอปพลิเคชันเพื่อให้โค้ดที่จัดการสื่อจากแหล่งที่มาที่ไม่น่าไว้วางใจทำงานในสภาพแวดล้อมที่แซนด์บ็อกซ์ที่ค่อนข้างสมบูรณ์ เช่น คุณอาจประมวลผลสื่อจากแหล่งที่มาที่ไม่น่าเชื่อถือในกระบวนการแยกต่างหาก แม้ว่าทั้ง 2 กระบวนการจะยังคงทำงานภายใต้ UID เดียวกัน แต่การแยกนี้ทำให้การโจมตีทำได้ยากขึ้น