Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add media session support on android #3604

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ local.properties
node_modules/
*.log

# yarn
yarn.lock

# editor workspace settings
.vscode

Expand All @@ -59,4 +56,4 @@ android/buildOutput_*
# lib build
lib/
!src/lib
*.tsbuildinfo
*.tsbuildinfo
3 changes: 3 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
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;
Expand Down Expand Up @@ -112,6 +114,8 @@
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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -663,6 +671,16 @@ private void initializePlayerCore(ReactExoplayerView self) {
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) {
Expand Down Expand Up @@ -733,6 +751,10 @@ private void initializePlayerSource(ReactExoplayerView self, DrmSessionManager d
player.prepare();
playerNeedsSource = false;

if (mediaSession != null) {
mediaSession.setActive(true);
}

reLayoutControls();

eventEmitter.loadStart();
Expand Down Expand Up @@ -909,6 +931,10 @@ private void releasePlayer() {
adsLoader.release();
}
adsLoader = null;
if (mediaSession != null) {
mediaSession.release();
mediaSession = null;
}
progressHandler.removeMessages(SHOW_PROGRESS);
audioBecomingNoisyReceiver.removeListener();
bandwidthMeter.removeEventListener(this);
Expand Down Expand Up @@ -1038,6 +1064,9 @@ private void onStopPlayback() {
if (isFullscreen) {
setFullscreen(false);
}
if (mediaSession != null) {
mediaSession.setActive(false);
}
audioManager.abandonAudioFocus(audioFocusChangeListener);
}

Expand Down Expand Up @@ -2098,6 +2127,11 @@ public void onDrmKeysRemoved(int windowIndex, MediaSource.MediaPeriodId mediaPer
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
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -433,6 +434,21 @@ public void setBufferConfig(final ReactExoplayerView videoView, @Nullable Readab
}
}

@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) {
Expand Down
11 changes: 11 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -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; })
];
}
1 change: 1 addition & 0 deletions src/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
selectedVideoTrack,
selectedAudioTrack,
selectedTextTrack,
mediaSession,

Check failure on line 74 in src/Video.tsx

View workflow job for this annotation

GitHub Actions / Lint JS (eslint, prettier)

'mediaSession' is defined but never used. Allowed unused args must match /^_/u.
onLoadStart,
onLoad,
onError,
Expand Down
8 changes: 8 additions & 0 deletions src/types/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -235,4 +242,5 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
localSourceEncryptionKeyScheme?: string;
debug?: DebugConfig;
allowsExternalPlayback?: boolean; // iOS
mediaSession?: MediaSession;
}
Loading
Loading