TextureView

คลาส TextureView เป็นวัตถุมุมมองที่รวมมุมมองเข้ากับ SurfaceTexture

การเรนเดอร์ด้วย OpenGL ES

วัตถุ TextureView ล้อมรอบ SurfaceTexture ตอบสนองต่อการโทรกลับและรับบัฟเฟอร์ใหม่ เมื่อ TextureView ได้รับบัฟเฟอร์ใหม่ TextureView จะออกคำขอดูที่ไม่ถูกต้องและดึงโดยใช้เนื้อหาของบัฟเฟอร์ใหม่ล่าสุดเป็นแหล่งข้อมูล โดยแสดงผลทุกที่และทุกสถานะมุมมองบ่งชี้ว่าควร

OpenGL ES (GLES) สามารถ แสดงผลบน TextureView ได้โดยส่ง SurfaceTexture ไปยังการเรียกสร้าง EGL แต่สิ่งนี้จะสร้างปัญหา เมื่อ GLES แสดงผลบน TextureView ผู้ผลิต BufferQueue และ Consumer จะอยู่ในเธรดเดียวกัน ซึ่งอาจทำให้การเรียกสลับบัฟเฟอร์หยุดทำงานหรือล้มเหลว ตัวอย่างเช่น หากผู้ผลิตส่งบัฟเฟอร์หลายตัวติดต่อกันอย่างรวดเร็วจากเธรด UI การเรียกสลับบัฟเฟอร์ EGL จะต้องแยกบัฟเฟอร์ออกจาก BufferQueue อย่างไรก็ตาม เนื่องจากผู้บริโภคและผู้ผลิตอยู่ในเธรดเดียวกัน จะไม่มีบัฟเฟอร์ใดๆ และการเรียกสลับจะหยุดทำงานหรือล้มเหลว

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

การเลือก SurfaceView หรือ TextureView

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

TextureView มีการจัดการอัลฟ่าและการหมุนที่ดีกว่า SurfaceView แต่ SurfaceView มีข้อดีด้านประสิทธิภาพเมื่อจัดวางองค์ประกอบ UI ที่ซ้อนกันเป็นชั้นๆ บนวิดีโอ เมื่อไคลเอนต์เรนเดอร์ด้วย SurfaceView SurfaceView จะให้เลเยอร์องค์ประกอบแยกต่างหากแก่ลูกค้า SurfaceFlinger ประกอบเลเยอร์ที่แยกจากกันเป็นฮาร์ดแวร์ซ้อนทับหากอุปกรณ์รองรับ เมื่อไคลเอนต์เรนเดอร์ด้วย TextureView ชุดเครื่องมือ UI จะรวมเนื้อหาของ TextureView ให้เป็นลำดับชั้นการดูด้วย GPU การอัปเดตเนื้อหาอาจทำให้องค์ประกอบมุมมองอื่น ๆ ถูกวาดใหม่ ตัวอย่างเช่น หากมุมมองอื่นวางอยู่บน TextureView หลังจากการเรนเดอร์มุมมองเสร็จสมบูรณ์ SurfaceFlinger จะรวมเลเยอร์ UI ของแอปและเลเยอร์อื่นๆ ทั้งหมด เพื่อให้ทุกพิกเซลที่มองเห็นถูกรวมเข้าด้วยกันสองครั้ง

กรณีศึกษา: การเล่นวิดีโอของ Grafika

Play Video ของ Grafika ประกอบด้วยเครื่องเล่นวิดีโอคู่หนึ่ง ตัวหนึ่งใช้งานกับ TextureView และอีกตัวใช้งานกับ SurfaceView ส่วนการถอดรหัสวิดีโอของกิจกรรมจะส่งเฟรมจาก MediaCodec ไปยังพื้นผิวสำหรับทั้ง TextureView และ SurfaceView ความแตกต่างที่ใหญ่ที่สุดระหว่างการใช้งานคือขั้นตอนที่จำเป็นในการนำเสนออัตราส่วนภาพที่ถูกต้อง

การปรับขนาด SurfaceView จำเป็นต้องมีการใช้งาน FrameLayout แบบกำหนดเอง WindowManager จำเป็นต้องส่งตำแหน่งหน้าต่างใหม่และค่าขนาดใหม่ไปยัง SurfaceFlinger การปรับขนาด SurfaceTexture ของ TextureView จำเป็นต้องกำหนดค่าเมทริกซ์การแปลงด้วย TextureView#setTransform()

หลังจากนำเสนออัตราส่วนภาพที่ถูกต้องแล้ว การใช้งานทั้งสองจะมีรูปแบบเดียวกัน เมื่อ SurfaceView/TextureView สร้างพื้นผิว โค้ดของแอปจะเปิดใช้งานการเล่น เมื่อผู้ใช้แตะ เล่น ระบบจะเริ่มเธรดการถอดรหัสวิดีโอ โดยมีพื้นผิวเป็นเป้าหมายเอาต์พุต หลังจากนั้น โค้ดของแอปจะไม่ทำอะไรเลย องค์ประกอบและการแสดงผลได้รับการจัดการโดย SurfaceFlinger (สำหรับ SurfaceView) หรือโดย TextureView

กรณีศึกษา: การถอดรหัสสองครั้งของ Grafika

Double Decode ของ Grafika สาธิตการจัดการ SurfaceTexture ภายใน TextureView

Double Decode ของ Grafika ใช้วัตถุ TextureView คู่หนึ่งเพื่อแสดงวิดีโอสองรายการที่เล่นเคียงข้างกัน ซึ่งเป็นการจำลองแอปการประชุมทางวิดีโอ เมื่อการวางแนวของหน้าจอเปลี่ยนไปและกิจกรรมเริ่มต้นใหม่ ตัวถอดรหัส MediaCodec จะไม่หยุด ซึ่งเป็นการจำลองการเล่นสตรีมวิดีโอแบบเรียลไทม์ เพื่อปรับปรุงประสิทธิภาพ ลูกค้าควรรักษาพื้นผิวให้คงอยู่ พื้นผิวเป็นตัวจัดการอินเทอร์เฟซของผู้ผลิตใน BufferQueue ของ SurfaceTexture เนื่องจาก TextureView จัดการ SurfaceTexture ลูกค้าจึงต้องรักษา SurfaceTexture ให้คงอยู่เพื่อรักษาพื้นผิวให้คงอยู่

เพื่อให้ SurfaceTexture คงอยู่ Double Decode ของ Grafika จะได้รับการอ้างอิงถึง SurfaceTextures จากออบเจ็กต์ TextureView และบันทึกไว้ในฟิลด์คงที่ จากนั้น Double Decode ของ Grafika จะคืนค่า false จาก TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() เพื่อป้องกันการทำลาย SurfaceTexture จากนั้น TextureView จะส่ง SurfaceTexture ไปที่ onSurfaceTextureDestroyed() ซึ่งสามารถคงไว้ตลอดการเปลี่ยนแปลงการกำหนดค่ากิจกรรม ซึ่งไคลเอนต์ส่งผ่านไปยัง TextureView ใหม่ผ่าน setSurfaceTexture()

เธรดที่แยกจากกันขับเคลื่อนตัวถอดรหัสวิดีโอแต่ละตัว Mediaserver ส่งบัฟเฟอร์พร้อมเอาต์พุตที่ถอดรหัสไปยัง SurfaceTextures ซึ่งเป็นผู้บริโภค BufferQueue วัตถุ TextureView ทำการเรนเดอร์และดำเนินการบนเธรด UI

การใช้ Double Decode ของ Grafika กับ SurfaceView นั้นยากกว่าการใช้งานกับ TextureView เนื่องจากวัตถุ SurfaceView ทำลายพื้นผิวระหว่างการเปลี่ยนการวางแนว นอกจากนี้ การใช้วัตถุ SurfaceView จะเพิ่มสองชั้น ซึ่งไม่เหมาะเนื่องจากข้อจำกัดเกี่ยวกับจำนวนการซ้อนทับที่มีอยู่บนฮาร์ดแวร์