3 Commits

Author SHA1 Message Date
73c073013b feat: add media session support on android 2024-03-25 02:03:41 +01:00
ec62d5e3c0 chore: add yarn.lock 2024-03-25 00:41:25 +01:00
a6fe6c3d46 chore: add shell.nix 2024-03-25 00:40:20 +01:00
8 changed files with 8778 additions and 4 deletions

5
.gitignore vendored
View File

@@ -39,9 +39,6 @@ local.properties
node_modules/
*.log
# yarn
yarn.lock
# editor workspace settings
.vscode
@@ -59,4 +56,4 @@ android/buildOutput_*
# lib build
lib/
!src/lib
*.tsbuildinfo
*.tsbuildinfo

View File

@@ -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

View File

@@ -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
*

View File

@@ -90,6 +90,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager<ReactExoplayerVi
private static final String PROP_CONTROLS = "controls";
private static final String PROP_SUBTITLE_STYLE = "subtitleStyle";
private static final String PROP_SHUTTER_COLOR = "shutterColor";
private static final String PROP_MEDIA_SESSION = "mediaSession";
private static final String PROP_DEBUG = "debug";
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)
public void setDebug(final ReactExoplayerView videoView,
@Nullable final ReadableMap debugConfig) {

11
shell.nix Normal file
View File

@@ -0,0 +1,11 @@
{pkgs ? import <nixpkgs> {}}:
pkgs.mkShell {
packages = with pkgs; [
nodejs-18_x
nodePackages.yarn
eslint_d
prettierd
jdk11
(jdt-language-server.override { jdk = jdk11; })
];
}

View File

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

View File

@@ -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;
}

8704
yarn.lock Normal file

File diff suppressed because it is too large Load Diff