public final class

PlayerMessage

extends java.lang.Object

 java.lang.Object

↳androidx.media3.exoplayer.PlayerMessage

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-exoplayer', version: '1.0.0-alpha03'

  • groupId: androidx.media3
  • artifactId: media3-exoplayer
  • version: 1.0.0-alpha03

Artifact androidx.media3:media3-exoplayer:1.0.0-alpha03 it located at Google repository (https://maven.google.com/)

Overview

Defines a player message which can be sent with a PlayerMessage.Sender and received by a PlayerMessage.Target.

Summary

Constructors
publicPlayerMessage(PlayerMessage.Sender sender, PlayerMessage.Target target, Timeline timeline, int defaultMediaItemIndex, Clock clock, Looper defaultLooper)

Creates a new message.

Methods
public synchronized booleanblockUntilDelivered()

Blocks until after the message has been delivered or the player is no longer able to deliver the message.

public synchronized booleanblockUntilDelivered(long timeoutMs)

Blocks until after the message has been delivered or the player is no longer able to deliver the message or the specified timeout elapsed.

public synchronized PlayerMessagecancel()

Cancels the message delivery.

public booleangetDeleteAfterDelivery()

Returns whether the message will be deleted after delivery.

public LoopergetLooper()

Returns the the message is delivered on.

public intgetMediaItemIndex()

Returns media item index at which the message will be delivered.

public java.lang.ObjectgetPayload()

Returns the message payload forwarded to PlayerMessage.Target.handleMessage(int, Object).

public longgetPositionMs()

Returns position in the media item at PlayerMessage.getMediaItemIndex() at which the message will be delivered, in milliseconds.

public PlayerMessage.TargetgetTarget()

Returns the target the message is sent to.

public TimelinegetTimeline()

Returns the timeline used for setting the position with PlayerMessage.setPosition(long).

public intgetType()

Returns the message type forwarded to PlayerMessage.Target.handleMessage(int, Object).

public synchronized booleanisCanceled()

Returns whether the message delivery has been canceled.

public synchronized voidmarkAsProcessed(boolean isDelivered)

Marks the message as processed.

public PlayerMessagesend()

Sends the message.

public PlayerMessagesetDeleteAfterDelivery(boolean deleteAfterDelivery)

Sets whether the message will be deleted after delivery.

public PlayerMessagesetHandler(Handler handler)

public PlayerMessagesetLooper(Looper looper)

Sets the the message is delivered on.

public PlayerMessagesetPayload(java.lang.Object payload)

Sets the message payload forwarded to PlayerMessage.Target.handleMessage(int, Object).

public PlayerMessagesetPosition(int mediaItemIndex, long positionMs)

Sets a position in a media item at which the message will be delivered.

public PlayerMessagesetPosition(long positionMs)

Sets a position in the current media item at which the message will be delivered.

public PlayerMessagesetType(int messageType)

Sets the message type forwarded to PlayerMessage.Target.handleMessage(int, Object).

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public PlayerMessage(PlayerMessage.Sender sender, PlayerMessage.Target target, Timeline timeline, int defaultMediaItemIndex, Clock clock, Looper defaultLooper)

Creates a new message.

Parameters:

sender: The PlayerMessage.Sender used to send the message.
target: The PlayerMessage.Target the message is sent to.
timeline: The timeline used when setting the position with PlayerMessage.setPosition(long). If set to Timeline.EMPTY, any position can be specified.
defaultMediaItemIndex: The default media item index in the timeline when no other media item index is specified.
clock: The Clock.
defaultLooper: The default to send the message on when no other looper is specified.

Methods

public Timeline getTimeline()

Returns the timeline used for setting the position with PlayerMessage.setPosition(long).

public PlayerMessage.Target getTarget()

Returns the target the message is sent to.

public PlayerMessage setType(int messageType)

Sets the message type forwarded to PlayerMessage.Target.handleMessage(int, Object).

Parameters:

messageType: The message type.

Returns:

This message.

public int getType()

Returns the message type forwarded to PlayerMessage.Target.handleMessage(int, Object).

public PlayerMessage setPayload(java.lang.Object payload)

Sets the message payload forwarded to PlayerMessage.Target.handleMessage(int, Object).

Parameters:

payload: The message payload.

Returns:

This message.

public java.lang.Object getPayload()

Returns the message payload forwarded to PlayerMessage.Target.handleMessage(int, Object).

public PlayerMessage setHandler(Handler handler)

Deprecated: Use PlayerMessage.setLooper(Looper) instead.

public PlayerMessage setLooper(Looper looper)

Sets the the message is delivered on.

Parameters:

looper: A .

Returns:

This message.

public Looper getLooper()

Returns the the message is delivered on.

public long getPositionMs()

Returns position in the media item at PlayerMessage.getMediaItemIndex() at which the message will be delivered, in milliseconds. If C.TIME_UNSET, the message will be delivered immediately. If C.TIME_END_OF_SOURCE, the message will be delivered at the end of the media item at PlayerMessage.getMediaItemIndex().

public PlayerMessage setPosition(long positionMs)

Sets a position in the current media item at which the message will be delivered.

Parameters:

positionMs: The position in the current media item at which the message will be sent, in milliseconds, or C.TIME_END_OF_SOURCE to deliver the message at the end of the current media item.

Returns:

This message.

public PlayerMessage setPosition(int mediaItemIndex, long positionMs)

Sets a position in a media item at which the message will be delivered.

Parameters:

mediaItemIndex: The index of the media item at which the message will be sent.
positionMs: The position in the media item with index mediaItemIndex at which the message will be sent, in milliseconds, or C.TIME_END_OF_SOURCE to deliver the message at the end of the media item with index mediaItemIndex.

Returns:

This message.

public int getMediaItemIndex()

Returns media item index at which the message will be delivered.

public PlayerMessage setDeleteAfterDelivery(boolean deleteAfterDelivery)

Sets whether the message will be deleted after delivery. If false, the message will be resent if playback reaches the specified position again. Only allowed to be false if a position is set with PlayerMessage.setPosition(long).

Parameters:

deleteAfterDelivery: Whether the message is deleted after delivery.

Returns:

This message.

public boolean getDeleteAfterDelivery()

Returns whether the message will be deleted after delivery.

public PlayerMessage send()

Sends the message. If the target throws an ExoPlaybackException then it is propagated out of the player as an error using .

Returns:

This message.

public synchronized PlayerMessage cancel()

Cancels the message delivery.

Returns:

This message.

public synchronized boolean isCanceled()

Returns whether the message delivery has been canceled.

public synchronized void markAsProcessed(boolean isDelivered)

Marks the message as processed. Should only be called by a PlayerMessage.Sender and may be called multiple times.

Parameters:

isDelivered: Whether the message has been delivered to its target. The message is considered as being delivered when this method has been called with isDelivered set to true at least once.

public synchronized boolean blockUntilDelivered()

Blocks until after the message has been delivered or the player is no longer able to deliver the message.

Note that this method must not be called if the current thread is the same thread used by the message looper as it would cause a deadlock.

Returns:

Whether the message was delivered successfully.

public synchronized boolean blockUntilDelivered(long timeoutMs)

Blocks until after the message has been delivered or the player is no longer able to deliver the message or the specified timeout elapsed.

Note that this method must not be called if the current thread is the same thread used by the message looper as it would cause a deadlock.

Parameters:

timeoutMs: The timeout in milliseconds.

Returns:

Whether the message was delivered successfully.

Source

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.exoplayer;

import android.os.Handler;
import android.os.Looper;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.IllegalSeekPositionException;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player;
import androidx.media3.common.Timeline;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.Renderer.MessageType;
import java.util.concurrent.TimeoutException;

/**
 * Defines a player message which can be sent with a {@link Sender} and received by a {@link
 * Target}.
 */
@UnstableApi
public final class PlayerMessage {

  /** A target for messages. */
  public interface Target {

    /**
     * Handles a message delivered to the target.
     *
     * @param messageType The message type.
     * @param message The message payload.
     * @throws ExoPlaybackException If an error occurred whilst handling the message. Should only be
     *     thrown by targets that handle messages on the playback thread.
     */
    void handleMessage(@MessageType int messageType, @Nullable Object message)
        throws ExoPlaybackException;
  }

  /** A sender for messages. */
  public interface Sender {

    /**
     * Sends a message.
     *
     * @param message The message to be sent.
     */
    void sendMessage(PlayerMessage message);
  }

  private final Target target;
  private final Sender sender;
  private final Clock clock;
  private final Timeline timeline;

  private int type;
  @Nullable private Object payload;
  private Looper looper;
  private int mediaItemIndex;
  private long positionMs;
  private boolean deleteAfterDelivery;
  private boolean isSent;
  private boolean isDelivered;
  private boolean isProcessed;
  private boolean isCanceled;

  /**
   * Creates a new message.
   *
   * @param sender The {@link Sender} used to send the message.
   * @param target The {@link Target} the message is sent to.
   * @param timeline The timeline used when setting the position with {@link #setPosition(long)}. If
   *     set to {@link Timeline#EMPTY}, any position can be specified.
   * @param defaultMediaItemIndex The default media item index in the {@code timeline} when no other
   *     media item index is specified.
   * @param clock The {@link Clock}.
   * @param defaultLooper The default {@link Looper} to send the message on when no other looper is
   *     specified.
   */
  public PlayerMessage(
      Sender sender,
      Target target,
      Timeline timeline,
      int defaultMediaItemIndex,
      Clock clock,
      Looper defaultLooper) {
    this.sender = sender;
    this.target = target;
    this.timeline = timeline;
    this.looper = defaultLooper;
    this.clock = clock;
    this.mediaItemIndex = defaultMediaItemIndex;
    this.positionMs = C.TIME_UNSET;
    this.deleteAfterDelivery = true;
  }

  /** Returns the timeline used for setting the position with {@link #setPosition(long)}. */
  public Timeline getTimeline() {
    return timeline;
  }

  /** Returns the target the message is sent to. */
  public Target getTarget() {
    return target;
  }

  /**
   * Sets the message type forwarded to {@link Target#handleMessage(int, Object)}.
   *
   * @param messageType The message type.
   * @return This message.
   * @throws IllegalStateException If {@link #send()} has already been called.
   */
  public PlayerMessage setType(int messageType) {
    Assertions.checkState(!isSent);
    this.type = messageType;
    return this;
  }

  /** Returns the message type forwarded to {@link Target#handleMessage(int, Object)}. */
  public int getType() {
    return type;
  }

  /**
   * Sets the message payload forwarded to {@link Target#handleMessage(int, Object)}.
   *
   * @param payload The message payload.
   * @return This message.
   * @throws IllegalStateException If {@link #send()} has already been called.
   */
  public PlayerMessage setPayload(@Nullable Object payload) {
    Assertions.checkState(!isSent);
    this.payload = payload;
    return this;
  }

  /** Returns the message payload forwarded to {@link Target#handleMessage(int, Object)}. */
  @Nullable
  public Object getPayload() {
    return payload;
  }

  /** @deprecated Use {@link #setLooper(Looper)} instead. */
  @Deprecated
  public PlayerMessage setHandler(Handler handler) {
    return setLooper(handler.getLooper());
  }

  /**
   * Sets the {@link Looper} the message is delivered on.
   *
   * @param looper A {@link Looper}.
   * @return This message.
   * @throws IllegalStateException If {@link #send()} has already been called.
   */
  public PlayerMessage setLooper(Looper looper) {
    Assertions.checkState(!isSent);
    this.looper = looper;
    return this;
  }

  /** Returns the {@link Looper} the message is delivered on. */
  public Looper getLooper() {
    return looper;
  }

  /**
   * Returns position in the media item at {@link #getMediaItemIndex()} at which the message will be
   * delivered, in milliseconds. If {@link C#TIME_UNSET}, the message will be delivered immediately.
   * If {@link C#TIME_END_OF_SOURCE}, the message will be delivered at the end of the media item at
   * {@link #getMediaItemIndex()}.
   */
  public long getPositionMs() {
    return positionMs;
  }

  /**
   * Sets a position in the current media item at which the message will be delivered.
   *
   * @param positionMs The position in the current media item at which the message will be sent, in
   *     milliseconds, or {@link C#TIME_END_OF_SOURCE} to deliver the message at the end of the
   *     current media item.
   * @return This message.
   * @throws IllegalStateException If {@link #send()} has already been called.
   */
  public PlayerMessage setPosition(long positionMs) {
    Assertions.checkState(!isSent);
    this.positionMs = positionMs;
    return this;
  }

  /**
   * Sets a position in a media item at which the message will be delivered.
   *
   * @param mediaItemIndex The index of the media item at which the message will be sent.
   * @param positionMs The position in the media item with index {@code mediaItemIndex} at which the
   *     message will be sent, in milliseconds, or {@link C#TIME_END_OF_SOURCE} to deliver the
   *     message at the end of the media item with index {@code mediaItemIndex}.
   * @return This message.
   * @throws IllegalSeekPositionException If the timeline returned by {@link #getTimeline()} is not
   *     empty and the provided media item index is not within the bounds of the timeline.
   * @throws IllegalStateException If {@link #send()} has already been called.
   */
  public PlayerMessage setPosition(int mediaItemIndex, long positionMs) {
    Assertions.checkState(!isSent);
    Assertions.checkArgument(positionMs != C.TIME_UNSET);
    if (mediaItemIndex < 0
        || (!timeline.isEmpty() && mediaItemIndex >= timeline.getWindowCount())) {
      throw new IllegalSeekPositionException(timeline, mediaItemIndex, positionMs);
    }
    this.mediaItemIndex = mediaItemIndex;
    this.positionMs = positionMs;
    return this;
  }

  /** Returns media item index at which the message will be delivered. */
  public int getMediaItemIndex() {
    return mediaItemIndex;
  }

  /**
   * Sets whether the message will be deleted after delivery. If false, the message will be resent
   * if playback reaches the specified position again. Only allowed to be false if a position is set
   * with {@link #setPosition(long)}.
   *
   * @param deleteAfterDelivery Whether the message is deleted after delivery.
   * @return This message.
   * @throws IllegalStateException If {@link #send()} has already been called.
   */
  public PlayerMessage setDeleteAfterDelivery(boolean deleteAfterDelivery) {
    Assertions.checkState(!isSent);
    this.deleteAfterDelivery = deleteAfterDelivery;
    return this;
  }

  /** Returns whether the message will be deleted after delivery. */
  public boolean getDeleteAfterDelivery() {
    return deleteAfterDelivery;
  }

  /**
   * Sends the message. If the target throws an {@link ExoPlaybackException} then it is propagated
   * out of the player as an error using {@link Player.Listener#onPlayerError(PlaybackException)}.
   *
   * @return This message.
   * @throws IllegalStateException If this message has already been sent.
   */
  public PlayerMessage send() {
    Assertions.checkState(!isSent);
    if (positionMs == C.TIME_UNSET) {
      Assertions.checkArgument(deleteAfterDelivery);
    }
    isSent = true;
    sender.sendMessage(this);
    return this;
  }

  /**
   * Cancels the message delivery.
   *
   * @return This message.
   * @throws IllegalStateException If this method is called before {@link #send()}.
   */
  public synchronized PlayerMessage cancel() {
    Assertions.checkState(isSent);
    isCanceled = true;
    markAsProcessed(/* isDelivered= */ false);
    return this;
  }

  /** Returns whether the message delivery has been canceled. */
  public synchronized boolean isCanceled() {
    return isCanceled;
  }

  /**
   * Marks the message as processed. Should only be called by a {@link Sender} and may be called
   * multiple times.
   *
   * @param isDelivered Whether the message has been delivered to its target. The message is
   *     considered as being delivered when this method has been called with {@code isDelivered} set
   *     to true at least once.
   */
  public synchronized void markAsProcessed(boolean isDelivered) {
    this.isDelivered |= isDelivered;
    isProcessed = true;
    notifyAll();
  }

  /**
   * Blocks until after the message has been delivered or the player is no longer able to deliver
   * the message.
   *
   * <p>Note that this method must not be called if the current thread is the same thread used by
   * the message {@link #getLooper() looper} as it would cause a deadlock.
   *
   * @return Whether the message was delivered successfully.
   * @throws IllegalStateException If this method is called before {@link #send()}.
   * @throws IllegalStateException If this method is called on the same thread used by the message
   *     {@link #getLooper() looper}.
   * @throws InterruptedException If the current thread is interrupted while waiting for the message
   *     to be delivered.
   */
  public synchronized boolean blockUntilDelivered() throws InterruptedException {
    Assertions.checkState(isSent);
    Assertions.checkState(looper.getThread() != Thread.currentThread());
    while (!isProcessed) {
      wait();
    }
    return isDelivered;
  }

  /**
   * Blocks until after the message has been delivered or the player is no longer able to deliver
   * the message or the specified timeout elapsed.
   *
   * <p>Note that this method must not be called if the current thread is the same thread used by
   * the message {@link #getLooper() looper} as it would cause a deadlock.
   *
   * @param timeoutMs The timeout in milliseconds.
   * @return Whether the message was delivered successfully.
   * @throws IllegalStateException If this method is called before {@link #send()}.
   * @throws IllegalStateException If this method is called on the same thread used by the message
   *     {@link #getLooper() looper}.
   * @throws TimeoutException If the {@code timeoutMs} elapsed and this message has not been
   *     delivered and the player is still able to deliver the message.
   * @throws InterruptedException If the current thread is interrupted while waiting for the message
   *     to be delivered.
   */
  public synchronized boolean blockUntilDelivered(long timeoutMs)
      throws InterruptedException, TimeoutException {
    Assertions.checkState(isSent);
    Assertions.checkState(looper.getThread() != Thread.currentThread());

    long deadlineMs = clock.elapsedRealtime() + timeoutMs;
    long remainingMs = timeoutMs;
    while (!isProcessed && remainingMs > 0) {
      clock.onThreadBlocked();
      wait(remainingMs);
      remainingMs = deadlineMs - clock.elapsedRealtime();
    }
    if (!isProcessed) {
      throw new TimeoutException("Message delivery timed out.");
    }
    return isDelivered;
  }
}