TextureView

המחלקות TextureView הן אובייקטים של תצוגה שמשלבים תצוגה עם SurfaceTexture.

עיבוד באמצעות OpenGL ES

אובייקט TextureView עוטף SurfaceTexture, מגיב לקריאות חוזרות ומקבל מאגרי נתונים חדשים. כשבאובייקט TextureView יש מאגרי נתונים זמניים חדשים, הוא שולח בקשה לביטול התוקף של התצוגה ויוצר ציור באמצעות התוכן של מאגר הנתונים הזמני החדש ביותר כמקור הנתונים שלו, ויוצר רינדור בכל מקום ובכל אופן שבו מצב התצוגה מציין שצריך.TextureView

OpenGL ES (GLES) יכול לבצע רינדור באובייקט TextureView על ידי העברת SurfaceTexture לקריאת היצירה של EGL, אבל זה יוצר בעיה. כש-GLES מבצע רינדור ב-TextureView, היצרנים והצרכנים של BufferQueue נמצאים באותו השרשור, מה שעלול לגרום לעיכוב או לכשל בקריאה להחלפת מאגר הנתונים הזמני. לדוגמה, אם רכיב Producer שולח כמה מאגרי נתונים (buffers) ברצף מהיר משרשור UI, הפונקציה EGL buffer swap צריכה להוציא מאגר נתונים מהתור BufferQueue. עם זאת, מכיוון שהצרכן והיצרן נמצאים באותו השרשור, לא יהיו מאגרי נתונים זמניים זמינים, והשיחה להחלפה תיעצר או תיכשל.

כדי למנוע מצבים של עצירה בהחלפת מאגרים, תמיד צריך להיות מאגר זמין ב-BufferQueue להוצאה מהתור. לשם כך, BufferQueue משליך את התוכן של המאגר שנרכש קודם כשמאגר חדש מתווסף לתור. בנוסף, המערכת מטילה מגבלות על מספר המאגרים המינימלי והמקסימלי כדי למנוע ממנוע צריכה לצרוך את כל המאגרים בבת אחת.

בחירה בין SurfaceView לבין TextureView

SurfaceView ו-TextureView ממלאים תפקידים דומים ושניהם אזרחים בהיררכיית התצוגה. עם זאת, יש הבדלים בין ההטמעות של SurfaceView ו-TextureView. אובייקט SurfaceView מקבל את אותם פרמטרים כמו תצוגות אחרות, אבל התוכן של SurfaceView שקוף כשהוא מוצג.

ל-TextureView יש טיפול טוב יותר באלפא ובסיבובים מאשר ל-SurfaceView, אבל ל-SurfaceView יש יתרונות בביצועים כשמבצעים קומפוזיציה של רכיבי ממשק משתמש בשכבות על סרטונים. כשלקוח מבצע רינדור עם אובייקט SurfaceView, האובייקט SurfaceView מספק ללקוח שכבת קומפוזיציה נפרדת. ‫SurfaceFlinger מרכיב את השכבה הנפרדת ככיסוי חומרה אם המכשיר תומך בכך. כשלקוח מבצע רינדור באמצעות TextureView, ערכת הכלים של ממשק המשתמש מרכיבה את התוכן של אובייקט TextureView בהיררכיית התצוגה באמצעות ה-GPU. עדכונים בתוכן עשויים לגרום לציור מחדש של רכיבי תצוגה אחרים, לדוגמה, אם התצוגות האחרות ממוקמות מעל TextureView. אחרי שהעיבוד של התצוגה מסתיים, SurfaceFlinger יוצר קומפוזיציה של שכבת ממשק המשתמש של האפליקציה ושל כל השכבות האחרות, כך שכל פיקסל גלוי עובר קומפוזיציה פעמיים.

מקרה לדוגמה: הפעלת סרטון של Grafika

Grafika's Play Video כולל שני נגני וידאו, אחד מיושם באמצעות TextureView ואחד מיושם באמצעות SurfaceView. החלק של פענוח הווידאו בפעילות שולח פריימים מ-MediaCodec אל Surface גם עבור TextureView וגם עבור SurfaceView. ההבדל הגדול ביותר בין ההטמעות הוא השלבים שנדרשים כדי להציג את יחס הגובה-רוחב הנכון.

כדי לשנות את הגודל של SurfaceView, צריך להטמיע FrameLayout בהתאמה אישית. ‫WindowManager צריך לשלוח ל-SurfaceFlinger מיקום חדש של החלון וערכים חדשים של הגודל. כדי לשנות את הגודל של אובייקט TextureView מסוג SurfaceTexture, צריך להגדיר מטריצת טרנספורמציה עם TextureView#setTransform().

אחרי שמציגים את יחס הגובה-רוחב הנכון, שתי ההטמעות פועלות לפי אותו דפוס. כש-SurfaceView או TextureView יוצרים את הממשק, קוד האפליקציה מאפשר הפעלה. כשמשתמש מקיש על הפעלה, מתחיל שרשור של פענוח סרטון, והמשטח מוגדר כיעד הפלט. אחרי זה, קוד האפליקציה לא עושה כלום – ההרכבה וההצגה מטופלות על ידי SurfaceFlinger (ב-SurfaceView) או על ידי TextureView.

מקרה לדוגמה: פענוח כפול של Grafika

Grafika's Double Decode מדגים מניפולציה של SurfaceTexture בתוך TextureView.

התכונה Double Decode באפליקציית Grafika משתמשת בזוג אובייקטים מסוג TextureView כדי להציג שני סרטונים שמופעלים זה לצד זה, וכך מדמה אפליקציה לשיחות ועידה בווידאו. כשההתמצאות של המסך משתנה והפעילות מופעלת מחדש, המפענחים של MediaCodec לא מפסיקים, וכך מדמים הפעלה של סטרימינג וידאו בזמן אמת. כדי לשפר את היעילות, הלקוח צריך להשאיר את המשטח פעיל. ה-Surface הוא נקודת אחיזה לממשק של היצרן ב-BufferQueue של SurfaceTexture. מכיוון שאובייקט TextureView מנהל את SurfaceTexture, הלקוח צריך לשמור על SurfaceTexture פעיל כדי לשמור על פני השטח פעילים.

כדי לשמור את SurfaceTexture פעיל, הפונקציה Double Decode של Grafika מקבלת הפניות לאובייקטים של SurfaceTexture מ-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 מוסיף שתי שכבות, וזה לא אידיאלי בגלל המגבלות על מספר שכבות העל שזמינות בחומרה.