From 73c073013b3cb351cfa97c226b56d73e386ec50b Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 25 Mar 2024 02:03:41 +0100 Subject: [PATCH] feat: add media session support on android --- android/build.gradle | 3 ++ .../exoplayer/ReactExoplayerView.java | 34 +++++++++++++++++++ .../exoplayer/ReactExoplayerViewManager.java | 16 +++++++++ shell.nix | 2 ++ src/Video.tsx | 1 + src/types/video.ts | 8 +++++ 6 files changed, 64 insertions(+) diff --git a/android/build.gradle b/android/build.gradle index a17296a1..9be826e2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -158,6 +158,9 @@ dependencies { // For media playback using ExoPlayer implementation "androidx.media3:media3-exoplayer:$media3_version" + // For exposing and controlling media sessions + implementation "androidx.media3:media3-session:$media3_version" + // For Smooth Streaming playback support with ExoPlayer implementation "androidx.media3:media3-exoplayer-smoothstreaming:$media3_version" // For DASH playback support with ExoPlayer diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index c13118aa..a1d172f3 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -14,6 +14,8 @@ import android.media.AudioManager; import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.support.v4.media.MediaDescriptionCompat; +import android.support.v4.media.session.MediaSessionCompat; import android.os.Message; import android.text.TextUtils; import android.view.View; @@ -112,6 +114,8 @@ import com.google.ads.interactivemedia.v3.api.AdError; import com.google.ads.interactivemedia.v3.api.AdEvent; import com.google.ads.interactivemedia.v3.api.AdErrorEvent; import com.google.common.collect.ImmutableList; +import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; +import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator; import java.net.CookieHandler; import java.net.CookieManager; @@ -163,6 +167,10 @@ public class ReactExoplayerView extends FrameLayout implements private FullScreenPlayerView fullScreenPlayerView; private ImaAdsLoader adsLoader; + private MediaSessionCompat mediaSession; + private MediaSessionConnector mediaSessionConnector; + private MediaDescriptionCompat.Builder mediaSessionMetadata = new MediaDescriptionCompat.Builder(); + private DataSource.Factory mediaDataSourceFactory; private ExoPlayer player; private DefaultTrackSelector trackSelector; @@ -663,6 +671,16 @@ public class ReactExoplayerView extends FrameLayout implements PlaybackParameters params = new PlaybackParameters(rate, 1f); player.setPlaybackParameters(params); changeAudioOutput(this.audioOutput); + + mediaSession = new MediaSessionCompat(this, "sample"); + mediaSessionConnector = new MediaSessionConnector(mediaSession); + mediaSessionConnector.setPlayer(player); + mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) { + @Override + public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) { + return mediaSessionMetadata.build(); + } + }); } private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) { @@ -733,6 +751,10 @@ public class ReactExoplayerView extends FrameLayout implements player.prepare(); playerNeedsSource = false; + if (mediaSession != null) { + mediaSession.setActive(true); + } + reLayoutControls(); eventEmitter.loadStart(); @@ -909,6 +931,10 @@ public class ReactExoplayerView extends FrameLayout implements adsLoader.release(); } adsLoader = null; + if (mediaSession != null) { + mediaSession.release(); + mediaSession = null; + } progressHandler.removeMessages(SHOW_PROGRESS); audioBecomingNoisyReceiver.removeListener(); bandwidthMeter.removeEventListener(this); @@ -1038,6 +1064,9 @@ public class ReactExoplayerView extends FrameLayout implements if (isFullscreen) { setFullscreen(false); } + if (mediaSession != null) { + mediaSession.setActive(false); + } audioManager.abandonAudioFocus(audioFocusChangeListener); } @@ -2098,6 +2127,11 @@ public class ReactExoplayerView extends FrameLayout implements DebugLog.d("DRM Info", "onDrmKeysRemoved"); } + public void setMediaSessionTitle(String title) { this.mediaSessionMetadata.setTitle((title)); } + public void setMediaSessionSubtitle(String subtitle) { this.mediaSessionMetadata.setSubtitle((subtitle)); } + public void setMediaSessionDescription(String description) { this.mediaSessionMetadata.setDescription((description)); } + public void setMediaSessionImage(String uri) { this.mediaSessionMetadata.setMediaUri((Uri.parse(uri))); } + /** * Handling controls prop * diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 3b2e8c5f..da6f8567 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -90,6 +90,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager( selectedVideoTrack, selectedAudioTrack, selectedTextTrack, + mediaSession, onLoadStart, onLoad, onError, diff --git a/src/types/video.ts b/src/types/video.ts index 5f505118..36e1efd0 100644 --- a/src/types/video.ts +++ b/src/types/video.ts @@ -180,6 +180,13 @@ export enum PosterResizeModeType { export type AudioOutput = 'speaker' | 'earpiece'; +export type MediaSession = { + title: string; + subtitle: string; + description: string; + imageUri: string; +}; + export interface ReactVideoProps extends ReactVideoEvents, ViewProps { source?: ReactVideoSource; drm?: Drm; @@ -235,4 +242,5 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps { localSourceEncryptionKeyScheme?: string; debug?: DebugConfig; allowsExternalPlayback?: boolean; // iOS + mediaSession?: MediaSession; }