SurfaceTexture
คือการผสมผสานระหว่างพื้นผิวและพื้นผิว OpenGL ES (GLES) อินสแตนซ์ SurfaceTexture
ใช้เพื่อจัดเตรียมพื้นผิวที่ส่งออกไปยังพื้นผิว GLES
SurfaceTexture
มีอินสแตนซ์ของ BufferQueue
ที่แอปเป็นผู้บริโภค โทรกลับ onFrameAvailable()
จะแจ้งเตือนแอปเมื่อผู้ผลิตเข้าคิวบัฟเฟอร์ใหม่ จากนั้น แอปจะเรียก updateTexImage()
ซึ่งจะปล่อยบัฟเฟอร์ที่เก็บไว้ก่อนหน้านี้ รับบัฟเฟอร์ใหม่จากคิว และทำการเรียก EGL เพื่อให้บัฟเฟอร์พร้อมใช้งานสำหรับ GLES ในรูปแบบพื้นผิวภายนอก
พื้นผิว GLES ภายนอก
พื้นผิว GLES ภายนอก ( GL_TEXTURE_EXTERNAL_OES
) แตกต่างจากพื้นผิว GLES แบบดั้งเดิม ( GL_TEXTURE_2D
) ในลักษณะต่อไปนี้:
- พื้นผิวภายนอกจะแสดงรูปหลายเหลี่ยมที่มีพื้นผิวโดยตรงจากข้อมูลที่ได้รับจาก
BufferQueue
- ตัวแสดงพื้นผิวภายนอกได้รับการกำหนดค่าแตกต่างจากตัวแสดงพื้นผิว GLES แบบดั้งเดิม
- พื้นผิวภายนอกไม่สามารถทำกิจกรรมพื้นผิว GLES แบบดั้งเดิมได้ทั้งหมด
ประโยชน์หลักของพื้นผิวภายนอกคือความสามารถในการเรนเดอร์โดยตรงจากข้อมูล BufferQueue
อินสแตนซ์ SurfaceTexture
ตั้งค่าแฟล็กการใช้งานของผู้บริโภคเป็น GRALLOC_USAGE_HW_TEXTURE
เมื่อสร้างอินสแตนซ์ BufferQueue
สำหรับพื้นผิวภายนอก เพื่อให้แน่ใจว่า GLES จะจดจำข้อมูลในบัฟเฟอร์ได้
เนื่องจากอินสแตนซ์ SurfaceTexture
โต้ตอบกับบริบท EGL แอปจึงสามารถเรียกใช้เมธอดของมันได้เท่านั้น ในขณะที่บริบท EGL ที่เป็นเจ้าของพื้นผิวนั้นเป็นปัจจุบันบนเธรดการเรียก สำหรับข้อมูลเพิ่มเติม โปรดดูเอกสารประกอบคลาส SurfaceTexture
การประทับเวลาและการเปลี่ยนแปลง
อินสแตนซ์ SurfaceTexture
ประกอบด้วยเมธอด getTimeStamp()
ซึ่งดึงข้อมูลการประทับเวลา และเมธอด getTransformMatrix()
ซึ่งดึงข้อมูลเมทริกซ์การแปลง การเรียก updateTexImage()
จะตั้งค่าทั้งการประทับเวลาและเมทริกซ์การแปลง แต่ละบัฟเฟอร์ที่ BufferQueue
ส่งผ่านจะมีพารามิเตอร์การแปลงและการประทับเวลา
พารามิเตอร์การแปลงมีประโยชน์สำหรับประสิทธิภาพ ในบางกรณี ข้อมูลต้นฉบับอาจอยู่ในการวางแนวที่ไม่ถูกต้องสำหรับผู้ใช้ทั่วไป แทนที่จะหมุนเวียนข้อมูลก่อนที่จะส่งไปยังผู้บริโภค ให้ส่งข้อมูลในทิศทางที่มีการแปลงที่แก้ไข เมทริกซ์การแปลงสามารถรวมเข้ากับการแปลงอื่น ๆ ได้เมื่อใช้ข้อมูล เพื่อลดค่าใช้จ่ายให้เหลือน้อยที่สุด
การประทับเวลามีประโยชน์สำหรับแหล่งบัฟเฟอร์ที่ขึ้นอยู่กับเวลา ตัวอย่างเช่น เมื่อ setPreviewTexture()
เชื่อมต่ออินเทอร์เฟซของผู้ผลิตเข้ากับเอาต์พุตของกล้อง คุณจะสามารถใช้เฟรมจากกล้องเพื่อสร้างวิดีโอได้ แต่ละเฟรมจะต้องมีการประทับเวลาการนำเสนอจากเวลาที่เฟรมถูกจับ ไม่ใช่เวลาที่แอปได้รับเฟรม รหัสกล้องจะตั้งค่าการประทับเวลาที่มาพร้อมกับบัฟเฟอร์ ส่งผลให้ชุดการประทับเวลาสอดคล้องกันมากขึ้น
กรณีศึกษา: การจับภาพอย่างต่อเนื่องของ Grafika
การจับภาพอย่างต่อเนื่องของ Grafika เกี่ยวข้องกับการบันทึกเฟรมจากกล้องของอุปกรณ์และการแสดงเฟรมเหล่านั้นบนหน้าจอ หากต้องการบันทึกเฟรม ให้สร้างพื้นผิวด้วยเมธอด createInputSurface()
ของคลาส MediaCodec แล้วส่งพื้นผิวไปยังกล้อง หากต้องการแสดงเฟรม ให้สร้างอินสแตนซ์ของ SurfaceView
และส่งพื้นผิวไปที่ setPreviewDisplay()
โปรดทราบว่าการบันทึกเฟรมและการแสดงพร้อมกันนั้นเป็นกระบวนการที่เกี่ยวข้องมากกว่า
กิจกรรม การถ่ายภาพต่อเนื่อง จะแสดงวิดีโอจากกล้องในขณะที่กำลังบันทึกวิดีโอ ในกรณีนี้ วิดีโอที่เข้ารหัสจะถูกเขียนลงในบัฟเฟอร์แบบวงกลมในหน่วยความจำซึ่งสามารถบันทึกลงในดิสก์ได้ตลอดเวลา
โฟลว์นี้เกี่ยวข้องกับคิวบัฟเฟอร์สามคิว:
-
App
— แอปใช้อินสแตนซ์SurfaceTexture
เพื่อรับเฟรมจากกล้อง โดยแปลงเป็นพื้นผิว GLES ภายนอก -
SurfaceFlinger
— แอปประกาศอินสแตนซ์SurfaceView
เพื่อแสดงเฟรม -
MediaServer
— กำหนดค่าตัวเข้ารหัสMediaCodec
ด้วยพื้นผิวอินพุตเพื่อสร้างวิดีโอ
ในภาพด้านล่าง ลูกศรแสดงถึงการแพร่กระจายข้อมูลจากกล้อง อินสแตนซ์ BufferQueue
เป็นแบบสี (ตัวสร้างเป็นสีน้าน ส่วนผู้บริโภคเป็นสีเขียว)
วิดีโอ H.264 ที่เข้ารหัสจะไปที่บัฟเฟอร์แบบวงกลมใน RAM ในกระบวนการของแอป เมื่อผู้ใช้กดปุ่มจับภาพ คลาส MediaMuxer
จะเขียนวิดีโอที่เข้ารหัสลงในไฟล์ MP4 บนดิสก์
อินสแตนซ์ BufferQueue
ทั้งหมดได้รับการจัดการด้วยบริบท EGL เดียวในแอป ในขณะที่ดำเนินการ GLES บนเธรด UI การจัดการข้อมูลที่เข้ารหัส (การจัดการบัฟเฟอร์แบบวงกลมและการเขียนลงดิสก์) จะดำเนินการบนเธรดที่แยกจากกัน
SurfaceView
การโทรกลับของ surfaceCreated()
จะสร้างอินสแตนซ์ EGLContext
และ EGLSurface
สำหรับจอแสดงผลและตัวเข้ารหัสวิดีโอ เมื่อมีเฟรมใหม่มาถึง SurfaceTexture
จะดำเนินการสี่กิจกรรม:- รับเฟรม.
- ทำให้กรอบพร้อมใช้งานเป็นพื้นผิว GLES
- เรนเดอร์เฟรมด้วยคำสั่ง GLES
- ส่งต่อการแปลงและการประทับเวลาสำหรับแต่ละอินสแตนซ์ของ
EGLSurface
เธรดตัวเข้ารหัสจะดึงเอาต์พุตที่เข้ารหัสจาก MediaCodec
และเก็บไว้ในหน่วยความจำ
การเล่นวิดีโอพื้นผิวที่ปลอดภัย
Android รองรับการประมวลผลหลัง GPU ของเนื้อหาวิดีโอที่มีการป้องกัน ซึ่งช่วยให้แอปใช้ GPU สำหรับเอฟเฟกต์วิดีโอที่ซับซ้อนและไม่เชิงเส้น (เช่น การบิดงอ) การแมปเนื้อหาวิดีโอที่ได้รับการป้องกันบนพื้นผิวเพื่อใช้ในฉากกราฟิกทั่วไป (เช่น การใช้ GLES) และความเป็นจริงเสมือน (VR)
การสนับสนุนเปิดใช้งานโดยใช้ส่วนขยายสองรายการต่อไปนี้:
- ส่วนขยาย EGL — (
EGL_EXT_protected_content
) ช่วยให้สามารถสร้างบริบทและพื้นผิว GL ที่ได้รับการป้องกัน ซึ่งสามารถดำเนินการกับเนื้อหาที่มีการป้องกันได้ - ส่วนขยาย GLES — (
GL_EXT_protected_textures
) เปิดใช้งานการแท็กพื้นผิวที่มีการป้องกัน เพื่อให้สามารถใช้เป็นไฟล์แนบพื้นผิวเฟรมบัฟเฟอร์ได้
Android เปิดใช้งาน SurfaceTexture
และ ACodec ( libstagefright.so
) เพื่อส่งเนื้อหาที่มีการป้องกัน แม้ว่าพื้นผิวของหน้าต่างจะไม่เข้าคิวกับ SurfaceFlinger
และจัดเตรียมพื้นผิววิดีโอที่ได้รับการป้องกันเพื่อใช้ภายในบริบทที่ได้รับการป้องกัน สิ่งนี้ทำได้โดยการตั้งค่าบิตผู้บริโภคที่ได้รับการป้องกัน ( GRALLOC_USAGE_PROTECTED
) บนพื้นผิวที่สร้างขึ้นในบริบทที่ได้รับการป้องกัน (ตรวจสอบโดย ACodec)
การเล่นวิดีโอพื้นผิวที่ปลอดภัยจะวางรากฐานสำหรับการนำ DRM ไปใช้ในสภาพแวดล้อม OpenGL ES หากไม่มีการนำ DRM มาใช้อย่างเข้มงวด เช่น Widevine ระดับ 1 ผู้ให้บริการเนื้อหาหลายรายจะไม่อนุญาตให้เรนเดอร์เนื้อหาที่มีมูลค่าสูงของตนในสภาพแวดล้อม OpenGL ES ซึ่งจะป้องกันกรณีการใช้งาน VR ที่สำคัญ เช่น การรับชมเนื้อหาที่มีการป้องกัน DRM ใน VR
AOSP มีโค้ดเฟรมเวิร์กสำหรับการเล่นวิดีโอพื้นผิวที่ปลอดภัย การสนับสนุนไดรเวอร์ขึ้นอยู่กับ OEM ผู้ใช้อุปกรณ์ต้องใช้ส่วนขยาย EGL_EXT_protected_content
และ GL_EXT_protected_textures extensions
เมื่อใช้ไลบรารีตัวแปลงสัญญาณของคุณเอง (เพื่อแทนที่ libstagefright
) ให้สังเกตการเปลี่ยนแปลงใน /frameworks/av/media/libstagefright/SurfaceUtils.cpp
ที่อนุญาตให้บัฟเฟอร์ที่ทำเครื่องหมายด้วย GRALLOC_USAGE_PROTECTED
ถูกส่งไปยัง ANativeWindow
(แม้ว่า ANativeWindow
จะไม่เข้าคิวโดยตรงไปยัง window composer) ตราบใดที่บิตการใช้งานของผู้บริโภคมี GRALLOC_USAGE_PROTECTED
สำหรับเอกสารโดยละเอียดเกี่ยวกับการใช้ส่วนขยาย โปรดดูที่รีจิสทรี Khronos ( EGL_EXT_protected_content
และ GL_EXT_protected_textures
)