Lớp TextureView là một đối tượng thành phần hiển thị kết hợp một thành phần hiển thị với SurfaceTexture.
Kết xuất bằng OpenGL ES
Đối tượng TextureView bao bọc một SurfaceTexture, phản hồi các lệnh gọi lại và thu nạp vùng đệm mới. Khi TextureView thu nạp vùng đệm mới, TextureView sẽ đưa ra yêu cầu vô hiệu hoá khung hiển thị và vẽ bằng cách sử dụng nội dung của vùng đệm mới nhất làm nguồn dữ liệu, hiển thị ở bất cứ đâu và bất kể trạng thái khung hiển thị cho biết điều gì.
OpenGL ES (GLES) có thể kết xuất trên TextureView bằng cách truyền SurfaceTexture đến lệnh gọi tạo EGL, nhưng điều này sẽ gây ra sự cố. Khi GLES kết xuất trên TextureView, trình tạo và trình tiêu thụ BufferQueue nằm trong cùng một luồng, điều này có thể khiến lệnh gọi hoán đổi vùng đệm bị đình trệ hoặc không thành công. Ví dụ: nếu một thực thể tạo gửi một số bộ đệm liên tiếp nhanh chóng từ luồng giao diện người dùng, thì lệnh gọi hoán đổi bộ đệm EGL cần phải xoá một bộ đệm khỏi BufferQueue. Tuy nhiên, vì trình tiêu thụ và trình tạo nằm trên cùng một luồng, nên sẽ không có vùng đệm nào và lệnh gọi hoán đổi sẽ bị treo hoặc không thành công.
Để đảm bảo việc hoán đổi vùng đệm không bị đình trệ, BufferQueue luôn cần có vùng đệm để được loại bỏ khỏi hàng đợi. Để triển khai việc này, BufferQueue sẽ loại bỏ nội dung của bộ đệm đã thu được trước đó khi bộ đệm mới được đưa vào hàng đợi và đặt các quy định hạn chế về số lượng bộ đệm tối thiểu và tối đa để ngăn người dùng sử dụng tất cả bộ đệm cùng một lúc.
Chọn SurfaceView hoặc TextureView
SurfaceView và TextureView có vai trò tương tự nhau và đều là thành phần của hệ phân cấp khung hiển thị. Tuy nhiên, SurfaceView và TextureView có cách triển khai khác nhau. SurfaceView có các tham số giống như các thành phần hiển thị khác, nhưng nội dung SurfaceView sẽ trong suốt khi kết xuất.
TextureView có khả năng xử lý độ trong suốt và độ xoay tốt hơn so với SurfaceView, nhưng SurfaceView có lợi thế về hiệu suất khi kết hợp các thành phần trên giao diện người dùng được xếp chồng lên video. Khi một ứng dụng kết xuất bằng SurfaceView, SurfaceView sẽ cung cấp cho ứng dụng một lớp kết hợp riêng biệt. SurfaceFlinger sẽ kết hợp lớp riêng biệt dưới dạng lớp phủ phần cứng nếu thiết bị hỗ trợ. Khi một ứng dụng hiển thị bằng TextureView, bộ công cụ giao diện người dùng sẽ kết hợp nội dung của TextureView vào hệ phân cấp chế độ xem bằng GPU. Nội dung cập nhật có thể khiến các phần tử khung hiển thị khác vẽ lại, ví dụ: nếu các khung hiển thị khác được đặt ở đầu TextureView. Sau khi kết xuất thành phần hiển thị xong, SurfaceFlinger sẽ kết hợp lớp giao diện người dùng của ứng dụng và tất cả các lớp khác để mỗi pixel hiển thị được kết hợp hai lần.
Nghiên cứu điển hình: Video Let's Play của Grafika
Play Video của Grafika bao gồm một cặp trình phát video, một trình phát được triển khai bằng TextureView và một trình phát được triển khai bằng SurfaceView. Phần giải mã video của hoạt động sẽ gửi các khung từ MediaCodec đến một nền tảng cho cả TextureView và SurfaceView. Điểm khác biệt lớn nhất giữa các cách triển khai là các bước cần thiết để trình bày tỷ lệ khung hình chính xác.
Để điều chỉnh theo tỷ lệ SurfaceView, bạn cần triển khai tuỳ chỉnh FrameLayout.
WindowManager cần gửi vị trí cửa sổ mới và các giá trị kích thước mới đến SurfaceFlinger. Để điều chỉnh tỷ lệ SurfaceTexture của TextureView, bạn cần định cấu hình ma trận biến đổi bằng TextureView#setTransform()
.
Sau khi trình bày tỷ lệ khung hình chính xác, cả hai phương thức triển khai đều tuân theo cùng một mẫu. Khi SurfaceView/TextureView tạo bề mặt, mã ứng dụng sẽ bật tính năng phát. Khi người dùng nhấn vào phím phát, thao tác này sẽ bắt đầu một luồng giải mã video, trong đó nền tảng là mục tiêu đầu ra. Sau đó, mã ứng dụng không làm gì cả – thành phần hiển thị và hiển thị do SurfaceFlinger (đối với SurfaceView) hoặc TextureView xử lý.
Nghiên cứu điển hình: Giải mã kép của Grafika
Quy trình giải mã kép của Grafika minh hoạ cách thao tác với SurfaceTexture bên trong TextureView.
Tính năng Giải mã kép của Grafika sử dụng một cặp đối tượng TextureView để hiển thị hai video phát cạnh nhau, mô phỏng một ứng dụng hội nghị truyền hình. Khi hướng của màn hình thay đổi và hoạt động khởi động lại, bộ giải mã MediaCodec sẽ không dừng, mô phỏng việc phát luồng video theo thời gian thực. Để cải thiện hiệu quả, ứng dụng phải duy trì giao diện. Bề mặt là một tay cầm cho giao diện nhà sản xuất trong BufferQueue của SurfaceTexture. Vì TextureView quản lý SurfaceTexture, nên ứng dụng cần duy trì SurfaceTexture để duy trì bề mặt.
Để duy trì SurfaceTexture, tính năng Giải mã kép của Grafika sẽ lấy các tệp tham chiếu đến SurfaceTexture từ các đối tượng TextureView và lưu các tệp đó trong một trường tĩnh.
Sau đó, tính năng Giải mã kép của Grafika sẽ trả về false
từ TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed()
để ngăn chặn việc huỷ bỏ SurfaceTexture. Sau đó, TextureView sẽ truyền một SurfaceTexture đến onSurfaceTextureDestroyed()
có thể được duy trì trong quá trình thay đổi cấu hình hoạt động mà ứng dụng sẽ truyền đến TextureView mới thông qua setSurfaceTexture()
.
Các luồng riêng biệt điều khiển từng bộ giải mã video. Mediaserver gửi vùng đệm có đầu ra đã giải mã đến SurfaceTextures, người dùng BufferQueue. Các đối tượng TextureView thực hiện kết xuất và thực thi trên luồng giao diện người dùng.
Việc triển khai tính năng Giải mã kép của Grafika bằng SurfaceView khó hơn so với triển khai bằng TextureView vì các đối tượng SurfaceView sẽ huỷ bỏ các nền tảng trong quá trình thay đổi hướng. Ngoài ra, việc sử dụng các đối tượng SurfaceView sẽ thêm hai lớp. Đây không phải là lựa chọn lý tưởng do các giới hạn về số lượng lớp phủ có sẵn trên phần cứng.