@capgo/capacitor-video-player
Play videos in a native fullscreen player in your Capacitor app, with subtitles, picture in picture and Chromecast support.
Capgo: push fixes to your Capacitor users in minutes, build signed iOS and Android apps without a Mac, and roll back in one click.
➡️ Get started for free
14-day unlimited free trial. No credit card required
🤖 Building with an AI coding agent? Install Capgo skills so Claude Code, Codex, Cursor, Copilot, or Gemini CLI know how to install and use
@capgo/capacitor-video-playerin your Capacitor app:npx skills add Cap-go/capgo-skills. Then ask your agent: "Add @capgo/capacitor-video-player to my Capacitor app and wire it up." Agent setup guides · Hosted MCP
Key features
- Players:
initPlayer()creates a player for a URL, withplay(),pause()andstopAllPlayers(). - Playback control:
setCurrentTime(),setVolume(),setMuted()andsetRate(), with matching getters. - Subtitles: one or more subtitle tracks with language options.
- Native extras: picture in picture, background mode, loop on end and Chromecast casting.
- Controller:
showController()andisControllerIsFullyVisible()for the native controls. - Platforms: iOS, Android and Web. Web also supports an embedded player mode.
Documentation
The most complete doc is available here: https://capgo.app/docs/plugins/video-player/
Compatibility
| Plugin version | Capacitor compatibility | Maintained |
|---|---|---|
| v8.*.* | v8.*.* | ✅ |
| v7.*.* | v7.*.* | On demand |
| v6.*.* | v6.*.* | ❌ |
| v5.*.* | v5.*.* | ❌ |
Note: The major version of this plugin follows the major version of Capacitor. Use the version that matches your Capacitor installation (e.g., plugin v8 for Capacitor 8). Only the latest major version is actively maintained.
Install
You can use our AI-Assisted Setup to install the plugin. Add the Capgo skills to your AI tool using the following command:
npx skills add https://github.com/cap-go/capacitor-skills --skill capacitor-plugins
Then use the following prompt:
Use the `capacitor-plugins` skill from `cap-go/capacitor-skills` to install the `@capgo/capacitor-video-player` plugin in my project.
If you prefer Manual Setup, install the plugin by running the following commands and follow the platform-specific instructions below:
bun add @capgo/capacitor-video-player
bunx cap sync
iOS Chromecast setup
iOS Chromecast uses the Google Cast SDK default media receiver. When chromecast is enabled, the plugin adds a Cast button over the fullscreen iOS player, loads the current media on the selected receiver, and keeps plugin play/pause/seek/volume calls pointed at the active Cast session.
For Cast discovery on iOS, add local network keys to your app Info.plist.
<key>NSBonjourServices</key>
<array>
<string>_googlecast._tcp</string>
<string>_CC1AD845._googlecast._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>$(PRODUCT_NAME) uses the local network to discover Cast-enabled devices on your WiFi network.</string>
await VideoPlayer.initPlayer({
playerId: 'fullscreen',
mode: 'fullscreen',
url: 'https://example.com/video.mp4',
title: 'Video title',
smallTitle: 'Video subtitle',
artwork: 'https://example.com/poster.jpg',
chromecast: true,
});
Supported Features
| Feature | iOS | Android | Web |
|---|---|---|---|
| Fullscreen native video player | ✅ | ✅ | ❌ |
| Embedded web video player | ❌ | ❌ | ✅ |
| Picture in Picture | ✅ | ✅ | ❌ |
| Subtitles | ✅ | ✅ | ✅ |
| DRM-protected playback | ✅ | ✅ | ❌ |
| Chromecast sender playback | ❌ | ✅ | ❌ |
Chromecast (Android)
Chromecast is supported on Android in fullscreen mode. Enable it with the chromecast option. When a Cast session starts, the plugin loads the same media URL on the receiver, keeps the current playback position, and routes play, pause, seek, and timeline controls through the Cast session.
await VideoPlayer.initPlayer({
mode: 'fullscreen',
playerId: 'main-player',
url: 'https://example.com/video.m3u8',
title: 'Video title',
smallTitle: 'Optional subtitle',
artwork: 'https://example.com/artwork.jpg',
chromecast: true,
showControls: true,
});
Chromecast Options
| Option | Type | Default | Description |
|---|---|---|---|
chromecast | boolean | true | Shows the Android Cast button and enables sender playback. |
title | string | "" | Title shown in the sender controls and receiver metadata. |
smallTitle | string | "" | Subtitle shown in the sender controls and metadata. |
artwork | string | "" | Image URL used for Cast metadata and sender Cast artwork. |
Requirements and Notes
- The media URL must be reachable by the Chromecast device, not only by the Android app.
- Cast support depends on the receiver app and device media support. MP4, HLS, DASH, and SmoothStreaming streams are mapped to Cast-compatible MIME types by the plugin.
- Widevine DRM metadata is forwarded to the Cast media item. DRM-protected streams may still require a receiver that supports your license server and DRM flow.
- Request headers used by the Android local player are not automatically available to the Chromecast receiver. Use public URLs, signed URLs, cookies supported by your receiver, or a custom receiver for secured media.
Android Picture in Picture
Picture in Picture on Android requires your app activity to declare PiP support in android/app/src/main/AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:supportsPictureInPicture="true"
android:launchMode="singleTask"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
...>
</activity>
launchMode="singleTask" and the configChanges flags prevent the player activity from being recreated when entering or leaving PiP.
iOS Picture in Picture and background audio
On iOS the native player configures AVAudioSession for PiP and background playback when pipEnabled or bkmodeEnabled is true (both default to true). Your app still needs the right plist and capability entries:
- In Xcode, add the Background Modes capability and enable Audio, AirPlay, and Picture in Picture.
- In
ios/App/App/Info.plist, include the audio background mode:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
Test PiP on a physical iPhone (the Simulator does not support Picture in Picture). Setting audioCategory: 'ambient' respects the silent switch but silences audio on screen lock; leave it unset or use playback for audible background playback.
API
initPlayer(...)isPlaying(...)play(...)pause(...)getDuration(...)getCurrentTime(...)setCurrentTime(...)getVolume(...)setVolume(...)getMuted(...)setMuted(...)setRate(...)getRate(...)stopAllPlayers()showController()isControllerIsFullyVisible()exitPlayer()hidePlayer()showPlayer()getPluginVersion()- Interfaces
initPlayer(...)
initPlayer(options: capVideoPlayerOptions) => Promise<capVideoPlayerResult>
Initialize a video player
| Param | Type |
|---|---|
options | capVideoPlayerOptions |
Returns: Promise<capVideoPlayerResult>
isPlaying(...)
isPlaying(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Return if a given playerId is playing
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
play(...)
play(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Play the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
pause(...)
pause(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Pause the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
getDuration(...)
getDuration(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Get the duration of the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
getCurrentTime(...)
getCurrentTime(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Get the current time of the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
setCurrentTime(...)
setCurrentTime(options: capVideoTimeOptions) => Promise<capVideoPlayerResult>
Set the current time to seek the current video to from a given playerId
| Param | Type |
|---|---|
options | capVideoTimeOptions |
Returns: Promise<capVideoPlayerResult>
getVolume(...)
getVolume(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Get the volume of the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
setVolume(...)
setVolume(options: capVideoVolumeOptions) => Promise<capVideoPlayerResult>
Set the volume of the current video to from a given playerId
| Param | Type |
|---|---|
options | capVideoVolumeOptions |
Returns: Promise<capVideoPlayerResult>
getMuted(...)
getMuted(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Get the muted of the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
setMuted(...)
setMuted(options: capVideoMutedOptions) => Promise<capVideoPlayerResult>
Set the muted of the current video to from a given playerId
| Param | Type |
|---|---|
options | capVideoMutedOptions |
Returns: Promise<capVideoPlayerResult>
setRate(...)
setRate(options: capVideoRateOptions) => Promise<capVideoPlayerResult>
Set the rate of the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoRateOptions |
Returns: Promise<capVideoPlayerResult>
getRate(...)
getRate(options: capVideoPlayerIdOptions) => Promise<capVideoPlayerResult>
Get the rate of the current video from a given playerId
| Param | Type |
|---|---|
options | capVideoPlayerIdOptions |
Returns: Promise<capVideoPlayerResult>
stopAllPlayers()
stopAllPlayers() => Promise<capVideoPlayerResult>
Stop all players playing
Returns: Promise<capVideoPlayerResult>
showController()
showController() => Promise<capVideoPlayerResult>
Show controller
Returns: Promise<capVideoPlayerResult>
isControllerIsFullyVisible()
isControllerIsFullyVisible() => Promise<capVideoPlayerResult>
isControllerIsFullyVisible
Returns: Promise<capVideoPlayerResult>
exitPlayer()
exitPlayer() => Promise<capVideoPlayerResult>
Exit player
Returns: Promise<capVideoPlayerResult>
hidePlayer()
hidePlayer() => Promise<capVideoPlayerResult>
Hide the currently presented player UI without stopping playback (native fullscreen).
Call showPlayer() to show it again.
Returns: Promise<capVideoPlayerResult>
showPlayer()
showPlayer() => Promise<capVideoPlayerResult>
Show again a previously hidden player UI (native fullscreen).
Returns: Promise<capVideoPlayerResult>
getPluginVersion()
getPluginVersion() => Promise<{ version: string; }>
Get the native Capacitor plugin version.
On web, the implementation returns "web".
Returns: Promise<{ version: string; }>
Interfaces
capVideoPlayerResult
| Prop | Type | Description |
|---|---|---|
result | boolean | result set to true when successful else false |
method | string | method name |
value | any | value returned |
message | string | message string |
capVideoPlayerOptions
| Prop | Type | Description |
|---|---|---|
mode | string | Player mode - "fullscreen" - "embedded" (Web only) |
url | string | The url of the video to play |
chromecastUrl | string | Optional override URL used when casting to Chromecast (iOS, Android). This lets you cast an alternative rendition/manifest (e.g. MPEG-DASH + Widevine) while keeping the on-device playback URL unchanged (e.g. HLS + FairPlay on iOS). |
subtitle | string | The url of subtitle associated with the video Prefer subtitles when providing more than one track. Kept for backward compatibility. |
language | string | The language of subtitle see https://github.com/libyal/libfwnt/wiki/Language-Code-identifiers Prefer subtitles when providing more than one track. Kept for backward compatibility. |
subtitles | VideoSubtitle[] | Multiple subtitle tracks (iOS, Android). When provided, takes precedence over subtitle / language. |
chapters | VideoChapter[] | Chapter markers for long-form content (iOS, Android). Shown as a chapter list in the native fullscreen player; selecting a chapter seeks to startTime. |
subtitleOptions | SubTitleOptions | SubTitle Options |
playerId | string | Id of DIV Element parent of the player |
rate | number | Initial playing rate |
exitOnEnd | boolean | Exit on VideoEnd (iOS, Android) default: true |
loopOnEnd | boolean | Loop on VideoEnd when exitOnEnd false (iOS, Android) default: false |
pipEnabled | boolean | Picture in Picture Enable (iOS, Android) default: true |
bkmodeEnabled | boolean | Background Mode Enable (iOS, Android) default: true |
audioCategory | 'ambient' | 'playback' | 'moviePlayback' | iOS audio session category behavior (iOS). - "ambient": respects the iPhone silent switch - "playback": ignores the iPhone silent switch - "moviePlayback": playback optimized for video and long-form AirPlay. When omitted, the plugin resolves the category from other options: "playback" if pipEnabled or bkmodeEnabled is true (both default to true), otherwise "ambient". |
showControls | boolean | Show Controls Enable (iOS, Android) default: true |
displayMode | string | Display Mode ["all", "portrait", "landscape"] (iOS, Android) default: "all" |
componentTag | string | Component Tag or DOM Element Tag (React app) |
width | number | Player Width (mode "embedded" only) |
height | number | Player height (mode "embedded" only) |
headers | { [key: string]: string; } | Headers for the request (iOS, Android) by Manuel García Marín (https://github.com/PhantomPainX) |
title | string | Title shown in the player and Chromecast metadata (iOS, Android) by Manuel García Marín (https://github.com/PhantomPainX) |
smallTitle | string | Subtitle shown below the title in the player and Chromecast metadata (iOS, Android) by Manuel García Marín (https://github.com/PhantomPainX) |
accentColor | string | ExoPlayer Progress Bar and Spinner color (Android) by Manuel García Marín (https://github.com/PhantomPainX) Must be a valid hex color code default: #FFFFFF |
chromecast | boolean | Chromecast enable/disable (iOS, Android) iOS requires Google Cast SDK setup and local network Info.plist keys. by Manuel García Marín (https://github.com/PhantomPainX) default: true |
artwork | string | Artwork url to be shown in Chromecast player (iOS, Android) by Manuel García Marín (https://github.com/PhantomPainX) default: "" |
drm | DrmOptions | DRM configuration for protected content (iOS: FairPlay, Android: Widevine) When casting, Widevine's certificateUrl (license URL) is also forwarded to Chromecast receivers as media.customData.laurl. |
VideoSubtitle
| Prop | Type | Description |
|---|---|---|
subtitle | string | The url of the subtitle file (vtt, srt, ...) |
language | string | The language of the subtitle see https://github.com/libyal/libfwnt/wiki/Language-Code-identifiers |
VideoChapter
| Prop | Type | Description |
|---|---|---|
title | string | Chapter title shown in the chapter list |
startTime | number | Start time in seconds |
endTime | number | End time in seconds (optional). Stored for future timeline markers; chapter jumps use startTime only in this release. |
SubTitleOptions
Optional styling for on-screen subtitles.
On iOS, colors and font size are applied through AVPlayerItem
textStyleRules, which Apple documents for WebVTT only. IMSC1 and other
in-manifest timed text formats are not styled through this API.
Inline WebVTT cue styles can take precedence over equivalent
subtitleOptions values; the plugin sets text style rules as a platform
fallback, not a guaranteed override of authored cue styling.
| Prop | Type | Description |
|---|---|---|
foregroundColor | string | Foreground color in RGBA. On Android, when the plugin applies subtitle styling, omitted values fall back to rgba(255,255,255,1). When subtitleOptions is absent, embedded cue colors from the media can override that fallback. On iOS, omitted colors are not set by the plugin; the platform uses the viewer's caption appearance settings instead of fixed plugin defaults. |
backgroundColor | string | Background color in RGBA. On Android, when the plugin applies subtitle styling in fullscreen, omitted values fall back to rgba(0,0,0,1). In picture-in-picture, omitted values fall back to transparent when the plugin applies its caption style (for example sidecar subtitles or non-empty subtitleOptions that include a color override). An empty subtitleOptions object alone does not select that fallback. With font-size-only options, embedded cue styles stay enabled, so authored cue backgrounds can remain visible in PiP. When subtitleOptions is absent, embedded cue colors from the media can override the fallback. On iOS, omitted colors are not set by the plugin; the platform uses the viewer's caption appearance settings instead of fixed plugin defaults. |
fontSize | number | Font size in CSS pixels. On Android, supplied values are rounded to the nearest integer before applying. When omitted on embedded tracks without a sidecar subtitle URI, Android uses SubtitleView.setUserDefaultTextSize() (the user's system caption size), not a fixed 16 px size. Sidecar subtitles still use a fixed default size when this property is omitted. On iOS, values map to AVFoundation relative size where 16 equals the system default (100%). |
DrmOptions
| Prop | Type | Description |
|---|---|---|
fairplay | FairPlayDrmOptions | FairPlay DRM configuration (iOS) |
playready | PlayreadyDrmOptions | PlayReady DRM configuration |
widevine | WidevineDrmOptions | Widevine DRM configuration (Android) |
FairPlayDrmOptions
| Prop | Type | Description |
|---|---|---|
certificateUrl | string | The URL to fetch the FairPlay certificate |
contentKeySpcUrl | string | The URL to send the SPC and receive the CKC license (FairPlay license server URL) |
assetId | string | Optional FairPlay asset identifier used when generating the SPC. Defaults to the skd:// content identifier from the key request. |
PlayreadyDrmOptions
| Prop | Type | Description |
|---|---|---|
certificateUrl | string | The URL to fetch the PlayReady license |
WidevineDrmOptions
| Prop | Type | Description |
|---|---|---|
certificateUrl | string | The URL to fetch the Widevine license |
capVideoPlayerIdOptions
| Prop | Type | Description |
|---|---|---|
playerId | string | Id of DIV Element parent of the player |
capVideoTimeOptions
| Prop | Type | Description |
|---|---|---|
playerId | string | Id of DIV Element parent of the player |
seektime | number | Video time value you want to seek to |
capVideoVolumeOptions
| Prop | Type | Description |
|---|---|---|
playerId | string | Id of DIV Element parent of the player |
volume | number | Volume value between [0 - 1] |
capVideoMutedOptions
| Prop | Type | Description |
|---|---|---|
playerId | string | Id of DIV Element parent of the player |
muted | boolean | Muted value true or false |
capVideoRateOptions
| Prop | Type | Description |
|---|---|---|
playerId | string | Id of DIV Element parent of the player |
rate | number | Rate value |