TextureView

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

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

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

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

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

בחירת SurfaceView או TextureView

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

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

מקרה לדוגמה: סרטון ה-Play של Grafika

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

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

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

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

ב-Double Decode של Grafika מוצגת מניפולציה של SurfaceTexture בתוך TextureView.

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

כדי לשמור על SurfaceTexture פעיל, ה-Double Decode של Grafika מקבל הפניות ל-SurfaceTextures מאובייקטי TextureView ושומר אותן בשדה סטטי. לאחר מכן, הפונקציה Double Decode של Grafika מחזירה את הערך false מ-TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() כדי למנוע את השמדת SurfaceTexture. לאחר מכן, ‏TextureView מעבירה ל-onSurfaceTextureDestroyed() SurfaceTexture שאפשר לשמור במהלך שינוי ההגדרות של הפעילות, והלקוח מעביר את ה-SurfaceTexture ל-TextureView החדש דרך setSurfaceTexture().

כל מקודד וידאו מופעל על ידי שרשור נפרד. Mediaserver שולח מאגרים עם פלט מפוענח ל-SurfaceTextures, לצרכני BufferQueue. אובייקטים של TextureView מבצעים עיבוד וירוצו בשרשור של ממשק המשתמש.

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