TextureView

کلاس TextureView یک شیء view است که یک view را با یک SurfaceTexture ترکیب می‌کند.

رندرینگ با OpenGL ES

یک شیء TextureView یک SurfaceTexture را در بر می‌گیرد، به فراخوانی‌های برگشتی پاسخ می‌دهد و بافرهای جدید را به دست می‌آورد. هنگامی که یک TextureView بافرهای جدید را به دست می‌آورد، یک TextureView یک درخواست نامعتبرسازی view صادر می‌کند و با استفاده از محتویات جدیدترین بافر به عنوان منبع داده خود، ترسیم را انجام می‌دهد و هر جا و به هر شکلی که view state نشان می‌دهد، رندر می‌کند.

OpenGL ES (GLES) می‌تواند با ارسال SurfaceTexture به فراخوانی ایجاد EGL، روی یک TextureView رندر کند، اما این کار مشکلی ایجاد می‌کند. وقتی GLES روی یک TextureView رندر می‌شود، تولیدکنندگان و مصرف‌کنندگان BufferQueue در یک نخ قرار دارند که می‌تواند باعث شود فراخوانی مبادله بافر متوقف شود یا با شکست مواجه شود. به عنوان مثال، اگر یک تولیدکننده چندین بافر را پشت سر هم از نخ UI ارسال کند، فراخوانی مبادله بافر EGL باید یک بافر را از BufferQueue خارج کند. با این حال، از آنجا که مصرف‌کننده و تولیدکننده در یک نخ هستند، هیچ بافری در دسترس نخواهد بود و فراخوانی مبادله متوقف می‌شود یا با شکست مواجه می‌شود.

برای جلوگیری از توقف تعویض بافر، BufferQueue همیشه به یک بافر برای خارج کردن از صف نیاز دارد. برای انجام این کار، BufferQueue محتویات بافر قبلی را هنگام صف‌بندی یک بافر جدید دور می‌ریزد. همچنین محدودیت‌هایی را برای حداقل و حداکثر تعداد بافر اعمال می‌کند تا از مصرف همزمان همه بافرها توسط یک مصرف‌کننده جلوگیری شود.

انتخاب SurfaceView یا TextureView

SurfaceView و TextureView نقش‌های مشابهی را ایفا می‌کنند و هر دو عضو سلسله مراتب نماها هستند. با این حال، SurfaceView و TextureView پیاده‌سازی‌های متفاوتی دارند. یک SurfaceView پارامترهای مشابهی مانند سایر نماها می‌گیرد، اما محتوای SurfaceView هنگام رندر شدن شفاف است.

یک TextureView در مقایسه با SurfaceView، مدیریت آلفا و چرخش بهتری دارد، اما SurfaceView در ترکیب عناصر رابط کاربری که روی ویدیوها لایه‌بندی شده‌اند، مزایای عملکردی دارد. وقتی یک کلاینت با SurfaceView رندر می‌کند، SurfaceView یک لایه ترکیب جداگانه به کلاینت ارائه می‌دهد. SurfaceFlinger در صورت پشتیبانی دستگاه، لایه جداگانه را به عنوان یک پوشش سخت‌افزاری ترکیب می‌کند. وقتی یک کلاینت با TextureView رندر می‌کند، جعبه ابزار UI محتوای TextureView را با GPU در سلسله مراتب نما ترکیب می‌کند. به‌روزرسانی‌های محتوا ممکن است باعث شود عناصر نمای دیگر دوباره ترسیم شوند، به عنوان مثال، اگر نماهای دیگر روی یک TextureView قرار گرفته باشند. پس از اتمام رندر نما، SurfaceFlinger لایه رابط کاربری برنامه و تمام لایه‌های دیگر را ترکیب می‌کند، به طوری که هر پیکسل قابل مشاهده دو بار ترکیب می‌شود.

مطالعه موردی: ویدیوی پخش گرافیکا

ابزار پخش ویدئوی گرافیکا شامل دو پخش‌کننده‌ی ویدئو است که یکی با TextureView و دیگری با SurfaceView پیاده‌سازی شده است. بخش رمزگشایی ویدئو در این فعالیت، فریم‌ها را از MediaCodec به سطحی برای TextureView و SurfaceView ارسال می‌کند. بزرگترین تفاوت بین پیاده‌سازی‌ها، مراحل مورد نیاز برای ارائه نسبت ابعاد صحیح است.

مقیاس‌بندی SurfaceView نیاز به پیاده‌سازی سفارشی FrameLayout دارد. WindowManager باید موقعیت پنجره جدید و مقادیر اندازه جدید را به SurfaceFlinger ارسال کند. مقیاس‌بندی SurfaceTextureView نیاز به پیکربندی ماتریس تبدیل با TextureView#setTransform() دارد.

پس از ارائه نسبت ابعاد صحیح، هر دو پیاده‌سازی از الگوی یکسانی پیروی می‌کنند. وقتی SurfaceView/TextureView سطح را ایجاد می‌کند، کد برنامه پخش را فعال می‌کند. وقتی کاربر روی پخش ضربه می‌زند، یک رشته رمزگشایی ویدیو را شروع می‌کند که سطح را به عنوان هدف خروجی در نظر می‌گیرد. پس از آن، کد برنامه هیچ کاری انجام نمی‌دهد - ترکیب و نمایش توسط SurfaceFlinger (برای SurfaceView) یا توسط TextureView مدیریت می‌شود.

مطالعه موردی: رمزگشایی دوگانه گرافیکا

رمزگشایی دوگانه‌ی گرافیکا، دستکاری SurfaceTexture را درون یک TextureView نشان می‌دهد.

رمزگشایی دوگانه‌ی گرافیکا از یک جفت شیء TextureView برای نمایش دو ویدیو که در کنار هم پخش می‌شوند، استفاده می‌کند و یک برنامه‌ی کنفرانس ویدیویی را شبیه‌سازی می‌کند. وقتی جهت صفحه نمایش تغییر می‌کند و فعالیت دوباره شروع می‌شود، رمزگشاهای MediaCodec متوقف نمی‌شوند و پخش یک جریان ویدیویی بلادرنگ را شبیه‌سازی می‌کنند. برای بهبود کارایی، کلاینت باید surface را فعال نگه دارد. surface یک دستگیره برای رابط تولیدکننده در BufferQueue مربوط به SurfaceTexture است. از آنجا که TextureView SurfaceTexture را مدیریت می‌کند، کلاینت باید SurfaceTexture را فعال نگه دارد تا surface زنده بماند.

برای زنده نگه داشتن SurfaceTexture، تابع Double Decode در Grafika ارجاعات به SurfaceTextures را از اشیاء TextureView دریافت کرده و آنها را در یک فیلد استاتیک ذخیره می‌کند. سپس، تابع Double Decode در Grafika مقدار false را از TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() برمی‌گرداند تا از تخریب SurfaceTexture جلوگیری کند. سپس TextureView یک SurfaceTexture به onSurfaceTextureDestroyed() ارسال می‌کند که می‌تواند در طول تغییر پیکربندی activity حفظ شود، که کلاینت آن را از طریق setSurfaceTexture() به TextureView جدید ارسال می‌کند.

هر دیکدر ویدیو توسط رشته‌های جداگانه‌ای هدایت می‌شود. مدیاسرور بافرهایی با خروجی رمزگشایی شده را به SurfaceTextures، مصرف‌کنندگان BufferQueue، ارسال می‌کند. اشیاء TextureView رندر را انجام می‌دهند و روی رشته UI اجرا می‌شوند.

پیاده‌سازی Double Decode در گرافیکا با SurfaceView سخت‌تر از پیاده‌سازی آن با TextureView است، زیرا اشیاء SurfaceView در طول تغییر جهت، سطوح را از بین می‌برند. علاوه بر این، استفاده از اشیاء SurfaceView دو لایه اضافه می‌کند که به دلیل محدودیت‌های تعداد لایه‌های موجود در سخت‌افزار، ایده‌آل نیست.