Picture-in-Picture

Updated

Keep video visible in a floating overlay when your app moves to the background or the user switches to another app.

This feature is supported only on React Native.

When your application moves to the background or the user switches to another app, the Picture-in-Picture (PiP) feature in Video SDK keeps the current video visible as a floating overlay window on screen. This lets users multitask without interrupting an ongoing call or viewing session, improving the multitasking experience and user retention.

PiP is useful in the following scenarios:

Use caseDescription
Video callingKeeps the call visible when the user switches apps or returns to the home screen. Useful for social calls, remote meetings, and online customer service.
Online educationLets students watch a teacher's video explanation while reviewing study materials, taking notes, or using other apps.
Interactive live streamingLets viewers browse products, chat, or use other apps while the stream continues in the background. Useful for e-commerce and content live streaming.
Screen sharingKeeps shared content visible when the user switches apps during remote collaboration or a presentation. Useful for remote work, technical support, and product demos.

Prerequisites

Before implementing Picture-in-Picture, make sure you have the following:

  • Video SDK for React Native v4.6.2 or later integrated into your project. See SDK quickstart.

  • To use PiP, the target device must run one of the following:

    • Android 8.0 or later
    • iOS 15.0 or later

Set up your project

Before using PiP, complete the required configuration for each target platform.

Declare that the current Activity supports PiP mode in your Android project:

<activity
 android:name=".MainActivity"
 android:supportsPictureInPicture="true"
 android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation" />

Then, make MainActivity extend AgoraPIPActivity:

import io.agora.rtc.ng.react.AgoraPIPActivity

class MainActivity : AgoraPIPActivity() {
}

If you need audio or video capture and playback to continue after the app moves to the background or enters PiP, also complete the foreground service configuration. For the required permissions, manifest declarations, and integration steps, see Android background restrictions.

The way PiP is triggered differs by Android version:

  • Android 12 and later: The system can automatically enter PiP when the user leaves the current screen in appropriate scenarios. You can combine this with autoEnterEnabled.
  • Earlier than Android 12: The system does not support automatic PiP entry via autoEnterEnabled. You typically need to trigger PiP entry explicitly. When MainActivity extends AgoraPIPActivity, the SDK handles the relevant system callback adaptation.

Implement Picture-in-Picture

Starting from Video SDK v4.6.2, Agora provides the AgoraPip controller and related configuration options for enabling and managing PiP on Android and iOS. This section describes how to implement Picture-in-Picture in your React Native project.

Get the PiP controller and register a listener

After initializing the RTC engine, call getAgoraPip to get the PiP controller and register a state change listener:

this.engine = createAgoraRtcEngine();
this.engine.initialize({
 appId,
 channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
});
 
const pip = this.engine.getAgoraPip();
pip.registerPipStateChangedObserver(this);

Check device support

Check whether the current device and system support the required capabilities:

const isPipSupported = pip.pipIsSupported();
const isPipAutoEnterSupported = pip.pipIsAutoEnterSupported();
  • pipIsSupported checks whether the current device supports PiP.
  • pipIsAutoEnterSupported checks whether the current device and system support automatic PiP entry.

Configure PiP options

Call pipSetup to configure PiP options. Use AgoraPipOptions to set platform-specific options for Android and iOS.

Pass the measured bounds of the video view to sourceRectHint to improve the transition animation when entering PiP.

On Android, you typically need to configure the aspect ratio, the initial source region, and seamless resize:

const videoViewBounds = {
 left: measuredLeft,
 top: measuredTop,
 right: measuredRight,
 bottom: measuredBottom,
};

const options: AgoraPipOptions = {
 autoEnterEnabled: pip.pipIsAutoEnterSupported(),
 aspectRatioX: 960,
 aspectRatioY: 540,
 sourceRectHintLeft: videoViewBounds.left,
 sourceRectHintTop: videoViewBounds.top,
 sourceRectHintRight: videoViewBounds.right,
 sourceRectHintBottom: videoViewBounds.bottom,
 seamlessResizeEnabled: true,
 useExternalStateMonitor: true,
 externalStateMonitorInterval: 100,
};

pip.pipSetup(options);

useExternalStateMonitor defaults to true. The example above keeps this value explicit for clarity. Only set it to false if you have already extended AgoraPIPActivity and explicitly do not want polling-based state synchronization.

Listen for PiP state changes

Implement AgoraPipStateChangedObserver to listen for PiP state changes. Use the AgoraPipState value to update the UI or handle errors:

onPipStateChanged(state: AgoraPipState, error: string | null): void {
 if (state === AgoraPipState.pipStateFailed) {
  this.engine?.getAgoraPip().pipDispose();
 }
 
 this.setState({ pipState: state });
}

AgoraPipState defines the following states:

  • pipStateStarted: PiP has started successfully.
  • pipStateStopped: PiP has stopped.
  • pipStateFailed: PiP failed to start or encountered an error while running.

Start, stop, and destroy PiP

PiP-related calls operate at two lifecycle levels:

  • Session level: pipStart, pipStop, and pipDispose control the start, stop, and resource release of a single PiP session.
  • Controller or page level: release performs the final cleanup when the page or controller is no longer needed.

After completing configuration, call pipStart to start PiP. Call release only when destroying the page or when the controller is no longer needed, to avoid redundant session-level calls during teardown.

// Session level: start PiP
pip.pipStart();

// Session level: end the current PiP session
pip.pipDispose();

// Controller/page destroy level: final release
pip.release();

On Android, pipStop is not the standard path for exiting PiP and restoring the foreground page. If you no longer need the current PiP session, call pipDispose to release resources. Restoring full-screen view is typically handled by the user through the system PiP window controls.

Development considerations

This section covers platform-specific behavior and best practices to keep in mind when integrating PiP.

  • After entering PiP on Android, you typically need to update the app UI to match the PiP state.
  • Android 12 and later support automatic PiP entry when the app moves to the background. On earlier versions, you must trigger PiP entry explicitly, for example by calling pipStart in response to a user action. Extending AgoraPIPActivity simplifies this adaptation.
  • PiP does not bypass Android background restrictions. If you need to continue capturing or playing audio and video in the background, combine PiP with a foreground service. For details, see Android background restrictions.
  • On Android, pipStop moves the task to the background rather than exiting PiP and restoring the foreground page. Full-screen restoration is typically handled by the user through the system PiP window controls.
  • Distinguish between session-level and controller-level release: call pipDispose to end the current PiP session, and call release only when the page is destroyed or the controller is no longer needed.

Reference

This section includes links to related API reference and additional resources.

API reference

Sample project

Agora provides a sample project demonstrating Picture-in-Picture. Download or view the source code.