Mobile Development 17 min read

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.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Analysis of SurfaceView, GLSurfaceView, and TextureView for Picture-in-Picture Video Playback on Android

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.

Mobile DevelopmentAndroidSurfaceViewvideo playbackPicture-in-PictureTextureView
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.