feat: add media session support on android

This commit is contained in:
2024-03-25 02:03:41 +01:00
parent ec62d5e3c0
commit 73c073013b
6 changed files with 64 additions and 0 deletions

View File

@@ -158,6 +158,9 @@ dependencies {
// For media playback using ExoPlayer // For media playback using ExoPlayer
implementation "androidx.media3:media3-exoplayer:$media3_version" 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 // For Smooth Streaming playback support with ExoPlayer
implementation "androidx.media3:media3-exoplayer-smoothstreaming:$media3_version" implementation "androidx.media3:media3-exoplayer-smoothstreaming:$media3_version"
// For DASH playback support with ExoPlayer // For DASH playback support with ExoPlayer

View File

@@ -14,6 +14,8 @@ import android.media.AudioManager;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.os.Message; import android.os.Message;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; 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.AdEvent;
import com.google.ads.interactivemedia.v3.api.AdErrorEvent; import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
import com.google.common.collect.ImmutableList; 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.CookieHandler;
import java.net.CookieManager; import java.net.CookieManager;
@@ -163,6 +167,10 @@ public class ReactExoplayerView extends FrameLayout implements
private FullScreenPlayerView fullScreenPlayerView; private FullScreenPlayerView fullScreenPlayerView;
private ImaAdsLoader adsLoader; private ImaAdsLoader adsLoader;
private MediaSessionCompat mediaSession;
private MediaSessionConnector mediaSessionConnector;
private MediaDescriptionCompat.Builder mediaSessionMetadata = new MediaDescriptionCompat.Builder();
private DataSource.Factory mediaDataSourceFactory; private DataSource.Factory mediaDataSourceFactory;
private ExoPlayer player; private ExoPlayer player;
private DefaultTrackSelector trackSelector; private DefaultTrackSelector trackSelector;
@@ -663,6 +671,16 @@ public class ReactExoplayerView extends FrameLayout implements
PlaybackParameters params = new PlaybackParameters(rate, 1f); PlaybackParameters params = new PlaybackParameters(rate, 1f);
player.setPlaybackParameters(params); player.setPlaybackParameters(params);
changeAudioOutput(this.audioOutput); 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) { private DrmSessionManager initializePlayerDrm(ReactExoplayerView self) {
@@ -733,6 +751,10 @@ public class ReactExoplayerView extends FrameLayout implements
player.prepare(); player.prepare();
playerNeedsSource = false; playerNeedsSource = false;
if (mediaSession != null) {
mediaSession.setActive(true);
}
reLayoutControls(); reLayoutControls();
eventEmitter.loadStart(); eventEmitter.loadStart();
@@ -909,6 +931,10 @@ public class ReactExoplayerView extends FrameLayout implements
adsLoader.release(); adsLoader.release();
} }
adsLoader = null; adsLoader = null;
if (mediaSession != null) {
mediaSession.release();
mediaSession = null;
}
progressHandler.removeMessages(SHOW_PROGRESS); progressHandler.removeMessages(SHOW_PROGRESS);
audioBecomingNoisyReceiver.removeListener(); audioBecomingNoisyReceiver.removeListener();
bandwidthMeter.removeEventListener(this); bandwidthMeter.removeEventListener(this);
@@ -1038,6 +1064,9 @@ public class ReactExoplayerView extends FrameLayout implements
if (isFullscreen) { if (isFullscreen) {
setFullscreen(false); setFullscreen(false);
} }
if (mediaSession != null) {
mediaSession.setActive(false);
}
audioManager.abandonAudioFocus(audioFocusChangeListener); audioManager.abandonAudioFocus(audioFocusChangeListener);
} }
@@ -2098,6 +2127,11 @@ public class ReactExoplayerView extends FrameLayout implements
DebugLog.d("DRM Info", "onDrmKeysRemoved"); 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 * Handling controls prop
* *

View File

@@ -90,6 +90,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_CONTROLS = "controls"; private static final String PROP_CONTROLS = "controls";
private static final String PROP_SUBTITLE_STYLE = "subtitleStyle"; private static final String PROP_SUBTITLE_STYLE = "subtitleStyle";
private static final String PROP_SHUTTER_COLOR = "shutterColor"; private static final String PROP_SHUTTER_COLOR = "shutterColor";
private static final String PROP_MEDIA_SESSION = "mediaSession";
private static final String PROP_DEBUG = "debug"; private static final String PROP_DEBUG = "debug";
private ReactExoplayerConfig config; private ReactExoplayerConfig config;
@@ -433,6 +434,21 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
} }
} }
@ReactProp(name = PROP_MEDIA_SESSION)
public void setMediaSession(final ReactExoplayerView videoView, @Nullable ReadableMap mediaSessionMetadata) {
if (mediaSessionMetadata == null) return;
String title = mediaSessionMetadata.getString("title");
String subtitle = mediaSessionMetadata.getString("subtitle");
String description = mediaSessionMetadata.getString("description");
String imageUri = mediaSessionMetadata.getString("imageUri");
if (title != null) { videoView.setMediaSessionTitle(title); }
if (subtitle != null) { videoView.setMediaSessionSubtitle(subtitle); }
if (description != null) { videoView.setMediaSessionDescription(description); }
if (imageUri != null) { videoView.setMediaSessionImage(imageUri); }
}
@ReactProp(name = PROP_DEBUG, defaultBoolean = false) @ReactProp(name = PROP_DEBUG, defaultBoolean = false)
public void setDebug(final ReactExoplayerView videoView, public void setDebug(final ReactExoplayerView videoView,
@Nullable final ReadableMap debugConfig) { @Nullable final ReadableMap debugConfig) {

View File

@@ -5,5 +5,7 @@
nodePackages.yarn nodePackages.yarn
eslint_d eslint_d
prettierd prettierd
jdk11
(jdt-language-server.override { jdk = jdk11; })
]; ];
} }

View File

@@ -71,6 +71,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
selectedVideoTrack, selectedVideoTrack,
selectedAudioTrack, selectedAudioTrack,
selectedTextTrack, selectedTextTrack,
mediaSession,
onLoadStart, onLoadStart,
onLoad, onLoad,
onError, onError,

View File

@@ -180,6 +180,13 @@ export enum PosterResizeModeType {
export type AudioOutput = 'speaker' | 'earpiece'; export type AudioOutput = 'speaker' | 'earpiece';
export type MediaSession = {
title: string;
subtitle: string;
description: string;
imageUri: string;
};
export interface ReactVideoProps extends ReactVideoEvents, ViewProps { export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
source?: ReactVideoSource; source?: ReactVideoSource;
drm?: Drm; drm?: Drm;
@@ -235,4 +242,5 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
localSourceEncryptionKeyScheme?: string; localSourceEncryptionKeyScheme?: string;
debug?: DebugConfig; debug?: DebugConfig;
allowsExternalPlayback?: boolean; // iOS allowsExternalPlayback?: boolean; // iOS
mediaSession?: MediaSession;
} }