Analysis of SurfaceView, GLSurfaceView, and TextureView for Picture-in-Picture Video Playback on Android
The article compares Android’s SurfaceView, GLSurfaceView, and TextureView for picture‑in‑picture video playback, explaining each view’s rendering model, the hole‑punch limitations of SurfaceView, Android N’s synchronous updates, and TextureView’s full transformation support at higher power cost, to guide developers in choosing the optimal solution.
This article provides an in‑depth study of picture‑in‑picture (PiP) video playback on Android, comparing several implementation schemes and offering the optimal solution. It analyzes the system source code to explore how to achieve smooth moving, scaling, and animation effects for PiP windows.
The discussion focuses on three core view types used for video rendering: SurfaceView , GLSurfaceView , and TextureView . It explains their characteristics, how they interact with the Android window manager, and the trade‑offs of each approach.
1. Introduction
Many video apps (e.g., YouTube, Facebook) support PiP playback, but they use different techniques. YouTube embeds the video view inside the app UI, while Facebook adds the video view via WindowManager , allowing both in‑app and out‑of‑app playback.
During PiP transitions, apps must handle user interactions (move, scale, animate) while keeping video playback smooth and free of stutter.
2. SurfaceView and GLSurfaceView
When Android uses MediaPlayer for video, developers typically choose one of the following rendering surfaces:
SurfaceView
GLSurfaceView
TextureView
YouTube and Facebook both rely on these surfaces, especially SurfaceView and GLSurfaceView , to render video.
2.1 What is a Surface?
Handle onto a raw buffer that is being managed by the screen compositor.[1]
A Surface represents a raw buffer managed by the screen compositor (SurfaceFlinger). Each window has two Surface objects: one created by the app process and one by the WindowManagerService.
public class SurfaceView extends View { final Surface mSurface = new Surface(); }2.2 Creation Process of SurfaceView’s Surface
Each window corresponds to a Layer in SurfaceFlinger. When a window needs to refresh, ViewRootImpl.performTraversals() triggers SurfaceView to request a new Surface via WindowManagerService.updateWindow() .
public class SurfaceView extends View { final Surface mSurface = new Surface(); MyWindow mWindow; int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; private void updateWindow(boolean force, boolean redrawNeeded) { if (mWindow == null) { mSession.addWithoutInputChannel(mWindow, mLayout, …); } mSurfaceLock.lock(); try { final int relayoutResult = mSession.relayout(mWindow, mLayout, …, mSurface); } finally { mSurfaceLock.unlock(); } } }2.3 “Hole‑Punching” (Transparent Region) Mechanism
When SurfaceView is attached to its host window, it calls requestTransparentRegion() to create a transparent hole in the host’s surface. The host’s ViewGroup.gatherTransparentRegion() aggregates these holes and informs WindowManagerService via setTransparentRegion() .
public final class ViewRootImpl { public void requestTransparentRegion(View child) { if (mView == child) { mView.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS; mWindowAttributesChanged = true; requestLayout(); } } }The aggregated transparent region is later used during layout traversal to ensure the SurfaceView appears through the hole.
2.4 Rendering of SurfaceView
Although SurfaceView has its own surface, it still participates in the host window’s drawing pass. Its draw() and dispatchDraw() methods clear the underlying area and then invoke updateWindow() to keep the surface in sync.
public class SurfaceView extends View { @Override public void draw(Canvas canvas) { if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { if ((mPrivateFlags & SKIP_DRAW) == 0) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } } super.draw(canvas); } @Override protected void dispatchDraw(Canvas canvas) { if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { canvas.drawColor(0, PorterDuff.Mode.CLEAR); } } mHaveFrame = true; updateWindow(false, false); super.dispatchDraw(canvas); } }2.5 Summary of SurfaceView Analysis
SurfaceView lives beneath the host window via the hole‑punch mechanism; rotation does not rotate the video content.
Transparency and alpha animations cause visual glitches.
During move/scale, black borders may appear if the surface update lags.
Independent surface allows rendering on a separate thread, enabling complex UI.
3. New Features of SurfaceView on Android N+
Starting with Android N, the window position of SurfaceView is updated synchronously with other view rendering. This eliminates artifacts when translating or scaling the view, although rotation and transparency limitations remain.
4. TextureView
TextureView extends View and participates fully in the view hierarchy. Its draw() method updates a HardwareLayer with frames from a SurfaceTexture . This enables normal view transformations (move, rotate, scale, animation) without the “hole‑punch” drawbacks of SurfaceView , but it requires hardware acceleration and consumes more power.
public class TextureView extends View { private HardwareLayer mLayer; private SurfaceTexture mSurface; private SurfaceTextureListener mListener; public final void draw(Canvas canvas) { mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; applyUpdate(); applyTransformMatrix(); } private void applyUpdate() { if (mLayer == null) return; synchronized (mLock) { if (mUpdateLayer) { mUpdateLayer = false; } else { return; } } mLayer.prepare(getWidth(), getHeight(), mOpaque); mLayer.updateSurfaceTexture(); if (mListener != null) { mListener.onSurfaceTextureUpdated(mSurface); } } }5. Conclusion
SurfaceView provides an independent surface with a hole‑punch rendering model, which leads to issues with rotation, transparency, and occasional black borders. Android N’s synchronous update mitigates some artifacts, making SurfaceView the preferred choice when no rotation or alpha animation is required. TextureView, being a regular view, supports full transformations but incurs higher performance and power costs. Developers should select the view type based on the specific PiP requirements.
Tencent Music Tech Team
Public account of Tencent Music's development team, focusing on technology sharing and communication.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.