-
Notifications
You must be signed in to change notification settings - Fork 3.6k
feat(android): add audio track selection for ExoPlayer #10312
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
b544b45
1fdb5d5
a0a9a0d
061388d
8c776aa
40b1b3f
f9d72fe
f0a3f38
25fa206
308a341
2bcef5f
fc51b80
15db47e
ffdd3ac
c8a591f
8538ab1
9a9b64d
a6221b7
cfbdb9b
a8ed46c
c80b01c
8ab60f5
5c4b8eb
30f2b5f
920b2e5
9b2f9a6
0a80faf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,14 +7,23 @@ | |
| import static androidx.media3.common.Player.REPEAT_MODE_ALL; | ||
| import static androidx.media3.common.Player.REPEAT_MODE_OFF; | ||
|
|
||
| import android.util.Log; | ||
| import androidx.annotation.NonNull; | ||
| import androidx.annotation.Nullable; | ||
| import androidx.media3.common.AudioAttributes; | ||
| import androidx.media3.common.C; | ||
| import androidx.media3.common.Format; | ||
| import androidx.media3.common.MediaItem; | ||
| import androidx.media3.common.PlaybackParameters; | ||
| import androidx.media3.common.TrackGroup; | ||
| import androidx.media3.common.TrackSelectionOverride; | ||
| import androidx.media3.common.Tracks; | ||
| import androidx.media3.common.util.UnstableApi; | ||
| import androidx.media3.exoplayer.ExoPlayer; | ||
| import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; | ||
| import io.flutter.view.TextureRegistry.SurfaceProducer; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * A class responsible for managing video playback using {@link ExoPlayer}. | ||
|
|
@@ -26,6 +35,7 @@ public abstract class VideoPlayer implements VideoPlayerInstanceApi { | |
| @Nullable protected final SurfaceProducer surfaceProducer; | ||
| @Nullable private DisposeHandler disposeHandler; | ||
| @NonNull protected ExoPlayer exoPlayer; | ||
| @UnstableApi @Nullable protected DefaultTrackSelector trackSelector; | ||
|
|
||
| /** A closure-compatible signature since {@link java.util.function.Supplier} is API level 24. */ | ||
| public interface ExoPlayerProvider { | ||
|
|
@@ -43,6 +53,7 @@ public interface DisposeHandler { | |
| void onDispose(); | ||
| } | ||
|
|
||
| @UnstableApi | ||
| // Error thrown for this-escape warning on JDK 21+ due to https://bugs.openjdk.org/browse/JDK-8015831. | ||
| // Keeping behavior as-is and addressing the warning could cause a regression: https://github.com/flutter/packages/pull/10193 | ||
| @SuppressWarnings("this-escape") | ||
|
|
@@ -55,6 +66,12 @@ public VideoPlayer( | |
| this.videoPlayerEvents = events; | ||
| this.surfaceProducer = surfaceProducer; | ||
| exoPlayer = exoPlayerProvider.get(); | ||
|
|
||
| // Try to get the track selector from the ExoPlayer if it was built with one | ||
| if (exoPlayer.getTrackSelector() instanceof DefaultTrackSelector) { | ||
| trackSelector = (DefaultTrackSelector) exoPlayer.getTrackSelector(); | ||
| } | ||
|
|
||
| exoPlayer.setMediaItem(mediaItem); | ||
| exoPlayer.prepare(); | ||
| exoPlayer.addListener(createExoPlayerEventListener(exoPlayer, surfaceProducer)); | ||
|
|
@@ -125,6 +142,101 @@ public ExoPlayer getExoPlayer() { | |
| return exoPlayer; | ||
| } | ||
|
|
||
| @UnstableApi | ||
nateshmbhat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| @Override | ||
| public @NonNull NativeAudioTrackData getAudioTracks() { | ||
| List<ExoPlayerAudioTrackData> audioTracks = new ArrayList<>(); | ||
|
|
||
| // Get the current tracks from ExoPlayer | ||
| Tracks tracks = exoPlayer.getCurrentTracks(); | ||
|
|
||
| // Iterate through all track groups | ||
| for (int groupIndex = 0; groupIndex < tracks.getGroups().size(); groupIndex++) { | ||
| Tracks.Group group = tracks.getGroups().get(groupIndex); | ||
|
|
||
| // Only process audio tracks | ||
| if (group.getType() == C.TRACK_TYPE_AUDIO) { | ||
| for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { | ||
| Format format = group.getTrackFormat(trackIndex); | ||
| boolean isSelected = group.isTrackSelected(trackIndex); | ||
|
|
||
| // Create audio track data with metadata | ||
| ExoPlayerAudioTrackData audioTrack = | ||
| new ExoPlayerAudioTrackData( | ||
| (long) groupIndex, | ||
| (long) trackIndex, | ||
| format.label, | ||
| format.language, | ||
| isSelected, | ||
| format.bitrate != Format.NO_VALUE ? (long) format.bitrate : null, | ||
| format.sampleRate != Format.NO_VALUE ? (long) format.sampleRate : null, | ||
| format.channelCount != Format.NO_VALUE ? (long) format.channelCount : null, | ||
| format.codecs != null ? format.codecs : null); | ||
|
|
||
| audioTracks.add(audioTrack); | ||
| } | ||
| } | ||
| } | ||
| return new NativeAudioTrackData(audioTracks); | ||
| } | ||
|
|
||
| @UnstableApi | ||
| @Override | ||
| public void selectAudioTrack(long groupIndex, long trackIndex) { | ||
| if (trackSelector == null) { | ||
| Log.w("VideoPlayer", "Cannot select audio track: track selector is null"); | ||
| return; | ||
| } | ||
|
|
||
| // Get current tracks | ||
| Tracks tracks = exoPlayer.getCurrentTracks(); | ||
|
|
||
| if (groupIndex < 0 || groupIndex >= tracks.getGroups().size()) { | ||
| Log.w( | ||
nateshmbhat marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "VideoPlayer", | ||
| "Cannot select audio track: groupIndex " | ||
| + groupIndex | ||
| + " is out of bounds (available groups: " | ||
| + tracks.getGroups().size() | ||
| + ")"); | ||
| return; | ||
| } | ||
|
|
||
| Tracks.Group group = tracks.getGroups().get((int) groupIndex); | ||
|
|
||
| // Verify it's an audio track | ||
| if (group.getType() != C.TRACK_TYPE_AUDIO) { | ||
| Log.w( | ||
| "VideoPlayer", | ||
| "Cannot select audio track: group at index " | ||
| + groupIndex | ||
| + " is not an audio track (type: " | ||
| + group.getType() | ||
| + ")"); | ||
| return; | ||
| } | ||
|
|
||
| // Verify the track index is valid | ||
| if (trackIndex < 0 || (int) trackIndex >= group.length) { | ||
| Log.w( | ||
| "VideoPlayer", | ||
| "Cannot select audio track: trackIndex " | ||
| + trackIndex | ||
| + " is out of bounds (available tracks in group: " | ||
| + group.length | ||
| + ")"); | ||
| return; | ||
| } | ||
|
|
||
| // Get the track group and create a selection override | ||
| TrackGroup trackGroup = group.getMediaTrackGroup(); | ||
| TrackSelectionOverride override = new TrackSelectionOverride(trackGroup, (int) trackIndex); | ||
|
|
||
| // Apply the track selection override | ||
| trackSelector.setParameters( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am looking at the usages of track selector in this class and it looks like the instance variable Maybe you are relying on the value being null someplace. |
||
| trackSelector.buildUponParameters().setOverrideForType(override).build()); | ||
| } | ||
|
|
||
| public void dispose() { | ||
| if (disposeHandler != null) { | ||
| disposeHandler.onDispose(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.