Skip to content

[video_player] Move Android buffer updates to Dart #9771

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

Merged
Merged
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
4 changes: 4 additions & 0 deletions packages/video_player/video_player_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.8.12

* Moves buffer position update event generation to Dart.

## 2.8.11

* Updates kotlin version to 2.2.0 to enable gradle 8.11 support.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,100 @@ ArrayList<Object> toList() {
}
}

/** Generated class from Pigeon that represents data sent in messages. */
public static final class PlaybackState {
/** The current playback position, in milliseconds. */
private @NonNull Long playPosition;

public @NonNull Long getPlayPosition() {
return playPosition;
}

public void setPlayPosition(@NonNull Long setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"playPosition\" is null.");
}
this.playPosition = setterArg;
}

/** The current buffer position, in milliseconds. */
private @NonNull Long bufferPosition;

public @NonNull Long getBufferPosition() {
return bufferPosition;
}

public void setBufferPosition(@NonNull Long setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"bufferPosition\" is null.");
}
this.bufferPosition = setterArg;
}

/** Constructor is non-public to enforce null safety; use Builder. */
PlaybackState() {}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PlaybackState that = (PlaybackState) o;
return playPosition.equals(that.playPosition) && bufferPosition.equals(that.bufferPosition);
}

@Override
public int hashCode() {
return Objects.hash(playPosition, bufferPosition);
}

public static final class Builder {

private @Nullable Long playPosition;

@CanIgnoreReturnValue
public @NonNull Builder setPlayPosition(@NonNull Long setterArg) {
this.playPosition = setterArg;
return this;
}

private @Nullable Long bufferPosition;

@CanIgnoreReturnValue
public @NonNull Builder setBufferPosition(@NonNull Long setterArg) {
this.bufferPosition = setterArg;
return this;
}

public @NonNull PlaybackState build() {
PlaybackState pigeonReturn = new PlaybackState();
pigeonReturn.setPlayPosition(playPosition);
pigeonReturn.setBufferPosition(bufferPosition);
return pigeonReturn;
}
}

@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<>(2);
toListResult.add(playPosition);
toListResult.add(bufferPosition);
return toListResult;
}

static @NonNull PlaybackState fromList(@NonNull ArrayList<Object> pigeonVar_list) {
PlaybackState pigeonResult = new PlaybackState();
Object playPosition = pigeonVar_list.get(0);
pigeonResult.setPlayPosition((Long) playPosition);
Object bufferPosition = pigeonVar_list.get(1);
pigeonResult.setBufferPosition((Long) bufferPosition);
return pigeonResult;
}
}

private static class PigeonCodec extends StandardMessageCodec {
public static final PigeonCodec INSTANCE = new PigeonCodec();

Expand All @@ -346,6 +440,8 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
return PlatformVideoViewCreationParams.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 132:
return CreateMessage.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 133:
return PlaybackState.fromList((ArrayList<Object>) readValue(buffer));
default:
return super.readValueOfType(type, buffer);
}
Expand All @@ -365,6 +461,9 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
} else if (value instanceof CreateMessage) {
stream.write(132);
writeValue(stream, ((CreateMessage) value).toList());
} else if (value instanceof PlaybackState) {
stream.write(133);
writeValue(stream, ((PlaybackState) value).toList());
} else {
super.writeValue(stream, value);
}
Expand Down Expand Up @@ -532,21 +631,26 @@ static void setUp(
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface VideoPlayerInstanceApi {

/** Sets whether to automatically loop playback of the video. */
void setLooping(@NonNull Boolean looping);

/** Sets the volume, with 0.0 being muted and 1.0 being full volume. */
void setVolume(@NonNull Double volume);

/** Sets the playback speed as a multiple of normal speed. */
void setPlaybackSpeed(@NonNull Double speed);

/** Begins playback if the video is not currently playing. */
void play();

@NonNull
Long getPosition();

void seekTo(@NonNull Long position);

/** Pauses playback if the video is currently playing. */
void pause();
/** Seeks to the given playback position, in milliseconds. */
void seekTo(@NonNull Long position);
/**
* Returns the current playback state.
*
* <p>This is combined into a single call to minimize platform channel calls for state that
* needs to be polled frequently.
*/
@NonNull
PlaybackState getPlaybackState();

/** The codec used by VideoPlayerInstanceApi. */
static @NonNull MessageCodec<Object> getCodec() {
Expand Down Expand Up @@ -668,16 +772,16 @@ static void setUp(
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.video_player_android.VideoPlayerInstanceApi.getPosition"
"dev.flutter.pigeon.video_player_android.VideoPlayerInstanceApi.pause"
+ messageChannelSuffix,
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<>();
try {
Long output = api.getPosition();
wrapped.add(0, output);
api.pause();
wrapped.add(0, null);
} catch (Throwable exception) {
wrapped = wrapError(exception);
}
Expand Down Expand Up @@ -716,16 +820,16 @@ static void setUp(
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.video_player_android.VideoPlayerInstanceApi.pause"
"dev.flutter.pigeon.video_player_android.VideoPlayerInstanceApi.getPlaybackState"
+ messageChannelSuffix,
getCodec());
if (api != null) {
channel.setMessageHandler(
(message, reply) -> {
ArrayList<Object> wrapped = new ArrayList<>();
try {
api.pause();
wrapped.add(0, null);
PlaybackState output = api.getPlaybackState();
wrapped.add(0, output);
} catch (Throwable exception) {
wrapped = wrapError(exception);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ public void setDisposeHandler(@Nullable DisposeHandler handler) {
protected abstract ExoPlayerEventListener createExoPlayerEventListener(
@NonNull ExoPlayer exoPlayer, @Nullable SurfaceProducer surfaceProducer);

void sendBufferingUpdate() {
videoPlayerEvents.onBufferingUpdate(exoPlayer.getBufferedPosition());
}

private static void setAudioAttributes(ExoPlayer exoPlayer, boolean isMixMode) {
exoPlayer.setAudioAttributes(
new AudioAttributes.Builder().setContentType(C.AUDIO_CONTENT_TYPE_MOVIE).build(),
Expand Down Expand Up @@ -107,12 +103,11 @@ public void setPlaybackSpeed(@NonNull Double speed) {
}

@Override
public @NonNull Long getPosition() {
long position = exoPlayer.getCurrentPosition();
// TODO(stuartmorgan): Move this; this is relying on the fact that getPosition is called
// frequently to drive buffering updates, which is a fragile hack.
sendBufferingUpdate();
return position;
public @NonNull Messages.PlaybackState getPlaybackState() {
return new Messages.PlaybackState.Builder()
.setPlayPosition(exoPlayer.getCurrentPosition())
.setBufferPosition(exoPlayer.getBufferedPosition())
.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,6 @@ public void playsAndPausesProvidedMedia() {
videoPlayer.dispose();
}

@Test
public void sendsBufferingUpdatesOnDemand() {
VideoPlayer videoPlayer = createVideoPlayer();

when(mockExoPlayer.getBufferedPosition()).thenReturn(10L);
videoPlayer.sendBufferingUpdate();
verify(mockEvents).onBufferingUpdate(10L);

videoPlayer.dispose();
}

@Test
public void togglesLoopingEnablesAndDisablesRepeatMode() {
VideoPlayer videoPlayer = createVideoPlayer();
Expand Down Expand Up @@ -177,14 +166,29 @@ public void setPlaybackSpeedSetsPlaybackParametersWithValue() {
}

@Test
public void seekAndGetPosition() {
public void seekTo() {
VideoPlayer videoPlayer = createVideoPlayer();

videoPlayer.seekTo(10L);
verify(mockExoPlayer).seekTo(10);

when(mockExoPlayer.getCurrentPosition()).thenReturn(20L);
assertEquals(20L, videoPlayer.getPosition().longValue());
videoPlayer.dispose();
}

@Test
public void getPlaybackState() {
VideoPlayer videoPlayer = createVideoPlayer();

final long playbackPosition = 20L;
final long bufferedPosition = 10L;
when(mockExoPlayer.getCurrentPosition()).thenReturn(playbackPosition);
when(mockExoPlayer.getBufferedPosition()).thenReturn(bufferedPosition);

final Messages.PlaybackState state = videoPlayer.getPlaybackState();
assertEquals(playbackPosition, state.getPlayPosition().longValue());
assertEquals(bufferedPosition, state.getBufferPosition().longValue());

videoPlayer.dispose();
}

@Test
Expand Down
Loading