added: documentations to MediaItemTags and Player.
fixed: checkStyle failures.
This commit is contained in:
parent
4e459b3383
commit
69646e5b5d
|
@ -133,7 +133,6 @@ import com.google.android.exoplayer2.PlaybackException;
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
import com.google.android.exoplayer2.Player.PositionInfo;
|
import com.google.android.exoplayer2.Player.PositionInfo;
|
||||||
import com.google.android.exoplayer2.RenderersFactory;
|
import com.google.android.exoplayer2.RenderersFactory;
|
||||||
import com.google.android.exoplayer2.SeekParameters;
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.TracksInfo;
|
import com.google.android.exoplayer2.TracksInfo;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
@ -2487,6 +2486,7 @@ public final class Player implements
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
//region ExoPlayer listeners (that didn't fit in other categories)
|
//region ExoPlayer listeners (that didn't fit in other categories)
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onEvents(@NonNull final com.google.android.exoplayer2.Player player,
|
public void onEvents(@NonNull final com.google.android.exoplayer2.Player player,
|
||||||
@NonNull final com.google.android.exoplayer2.Player.Events events) {
|
@NonNull final com.google.android.exoplayer2.Player.Events events) {
|
||||||
Listener.super.onEvents(player, events);
|
Listener.super.onEvents(player, events);
|
||||||
|
@ -2546,14 +2546,6 @@ public final class Player implements
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newPosition.contentPositionMs == 0 &&
|
|
||||||
simpleExoPlayer.getTotalBufferedDuration() < 500L) {
|
|
||||||
Log.d(TAG, "Playback - skipping to initial keyframe.");
|
|
||||||
simpleExoPlayer.setSeekParameters(SeekParameters.CLOSEST_SYNC);
|
|
||||||
simpleExoPlayer.seekTo(1L);
|
|
||||||
simpleExoPlayer.setSeekParameters(PlayerHelper.getSeekParameters(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh the playback if there is a transition to the next video
|
// Refresh the playback if there is a transition to the next video
|
||||||
final int newIndex = newPosition.mediaItemIndex;
|
final int newIndex = newPosition.mediaItemIndex;
|
||||||
switch (discontinuityReason) {
|
switch (discontinuityReason) {
|
||||||
|
@ -2605,7 +2597,29 @@ public final class Player implements
|
||||||
//region Errors
|
//region Errors
|
||||||
/**
|
/**
|
||||||
* Process exceptions produced by {@link com.google.android.exoplayer2.ExoPlayer ExoPlayer}.
|
* Process exceptions produced by {@link com.google.android.exoplayer2.ExoPlayer ExoPlayer}.
|
||||||
*
|
* <p>There are multiple types of errors:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link PlaybackException#ERROR_CODE_BEHIND_LIVE_WINDOW BEHIND_LIVE_WINDOW}:
|
||||||
|
* If the playback on livestreams are lagged too far behind the current playable
|
||||||
|
* window. Then we seek to the latest timestamp and restart the playback.
|
||||||
|
* </li>
|
||||||
|
* <li>From {@link PlaybackException#ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE BAD_IO} to
|
||||||
|
* {@link PlaybackException#ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED UNSUPPORTED_FORMATS}:
|
||||||
|
* If the stream source is validated by the extractor but not recognized by the player,
|
||||||
|
* then we can try to recover playback by signal an error on the {@link PlayQueue}.</li>
|
||||||
|
* <li>For {@link PlaybackException#ERROR_CODE_TIMEOUT PLAYER_TIMEOUT},
|
||||||
|
* {@link PlaybackException#ERROR_CODE_IO_UNSPECIFIED MEDIA_SOURCE_RESOLVER_TIMEOUT} and
|
||||||
|
* {@link PlaybackException#ERROR_CODE_IO_NETWORK_CONNECTION_FAILED NO_NETWORK}:
|
||||||
|
* We can keep set the recovery record and keep to player at the current state until
|
||||||
|
* it is ready to play by restarting the {@link MediaSourceManager}.</li>
|
||||||
|
* <li>On any ExoPlayer specific issue internal to its device interaction, such as
|
||||||
|
* {@link PlaybackException#ERROR_CODE_DECODER_INIT_FAILED DECODER_ERROR}:
|
||||||
|
* We terminate the playback.</li>
|
||||||
|
* <li>For any other unspecified issue internal: We set a recovery and try to restart
|
||||||
|
* the playback.</li>
|
||||||
|
* In the case of decoder/renderer or unspecified errors, the player will create a
|
||||||
|
* notification so the users are aware.
|
||||||
|
* </ul>
|
||||||
* @see com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException)
|
* @see com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException)
|
||||||
* */
|
* */
|
||||||
@SuppressLint("SwitchIntDef")
|
@SuppressLint("SwitchIntDef")
|
||||||
|
@ -2648,6 +2662,9 @@ public final class Player implements
|
||||||
case ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT:
|
case ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT:
|
||||||
// Don't create notification on timeout/networking errors:
|
// Don't create notification on timeout/networking errors:
|
||||||
isCatchableException = true;
|
isCatchableException = true;
|
||||||
|
setRecovery();
|
||||||
|
reloadPlayQueueManager();
|
||||||
|
break;
|
||||||
case ERROR_CODE_UNSPECIFIED:
|
case ERROR_CODE_UNSPECIFIED:
|
||||||
// Reload playback on unexpected errors:
|
// Reload playback on unexpected errors:
|
||||||
setRecovery();
|
setRecovery();
|
||||||
|
@ -2749,7 +2766,6 @@ public final class Player implements
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean onPlaybackInitial = currentItem == null;
|
|
||||||
final boolean hasPlayQueueItemChanged = currentItem != item;
|
final boolean hasPlayQueueItemChanged = currentItem != item;
|
||||||
|
|
||||||
final int currentPlayQueueIndex = playQueue.indexOf(item);
|
final int currentPlayQueueIndex = playQueue.indexOf(item);
|
||||||
|
@ -2953,10 +2969,8 @@ public final class Player implements
|
||||||
//region StreamInfo history: views and progress
|
//region StreamInfo history: views and progress
|
||||||
|
|
||||||
private void registerStreamViewed() {
|
private void registerStreamViewed() {
|
||||||
getCurrentStreamInfo().ifPresent(info -> {
|
getCurrentStreamInfo().ifPresent(info -> databaseUpdateDisposable
|
||||||
databaseUpdateDisposable
|
.add(recordManager.onViewed(info).onErrorComplete().subscribe()));
|
||||||
.add(recordManager.onViewed(info).onErrorComplete().subscribe());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveStreamProgressState(final long progressMillis) {
|
private void saveStreamProgressState(final long progressMillis) {
|
||||||
|
@ -3134,7 +3148,7 @@ public final class Player implements
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playQueue.getIndex() == index && simpleExoPlayer.getCurrentWindowIndex() == index) {
|
if (playQueue.getIndex() == index && simpleExoPlayer.getCurrentMediaItemIndex() == index) {
|
||||||
seekToDefault();
|
seekToDefault();
|
||||||
} else {
|
} else {
|
||||||
saveStreamProgressState();
|
saveStreamProgressState();
|
||||||
|
@ -3880,9 +3894,10 @@ public final class Player implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onOpenInBrowserClicked() {
|
private void onOpenInBrowserClicked() {
|
||||||
getCurrentStreamInfo().map(Info::getOriginalUrl).ifPresent(originalUrl -> {
|
getCurrentStreamInfo()
|
||||||
ShareUtils.openUrlInBrowser(Objects.requireNonNull(getParentActivity()), originalUrl);
|
.map(Info::getOriginalUrl)
|
||||||
});
|
.ifPresent(originalUrl -> ShareUtils.openUrlInBrowser(
|
||||||
|
Objects.requireNonNull(getParentActivity()), originalUrl));
|
||||||
}
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,16 @@ import java.util.Optional;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link MediaItemTag} object is designed to contain metadata for a stream
|
||||||
|
* that has failed to load. It supplies metadata from an underlying
|
||||||
|
* {@link PlayQueueItem}, which is used by the internal players to resolve actual
|
||||||
|
* playback info.
|
||||||
|
*
|
||||||
|
* This {@link MediaItemTag} does not contain any {@link StreamInfo} that can be
|
||||||
|
* used to start playback and can be detected by checking {@link ExceptionTag#getErrors()}
|
||||||
|
* when in generic form.
|
||||||
|
**/
|
||||||
public final class ExceptionTag implements MediaItemTag {
|
public final class ExceptionTag implements MediaItemTag {
|
||||||
@NonNull
|
@NonNull
|
||||||
private final PlayQueueItem item;
|
private final PlayQueueItem item;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.net.Uri;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.google.android.exoplayer2.MediaMetadata;
|
import com.google.android.exoplayer2.MediaMetadata;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
@ -16,6 +17,13 @@ import java.util.UUID;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata container and accessor used by player internals.
|
||||||
|
*
|
||||||
|
* This interface ensures consistency of fetching metadata on each stream,
|
||||||
|
* which is encapsulated in a {@link MediaItem} and delivered via ExoPlayer's
|
||||||
|
* {@link Player.Listener} on event triggers to the downstream users.
|
||||||
|
**/
|
||||||
public interface MediaItemTag {
|
public interface MediaItemTag {
|
||||||
|
|
||||||
List<Throwable> getErrors();
|
List<Throwable> getErrors();
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.schabi.newpipe.player.mediaitem;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -10,6 +11,12 @@ import java.util.Optional;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a Placeholding {@link MediaItemTag}, designed as a dummy metadata object for
|
||||||
|
* any stream that has not been resolved.
|
||||||
|
*
|
||||||
|
* This object cannot be instantiated and does not hold real metadata of any form.
|
||||||
|
* */
|
||||||
public final class PlaceholderTag implements MediaItemTag {
|
public final class PlaceholderTag implements MediaItemTag {
|
||||||
public static final PlaceholderTag EMPTY = new PlaceholderTag(null);
|
public static final PlaceholderTag EMPTY = new PlaceholderTag(null);
|
||||||
private static final String UNKNOWN_VALUE_INTERNAL = "Placeholder";
|
private static final String UNKNOWN_VALUE_INTERNAL = "Placeholder";
|
||||||
|
@ -29,7 +36,7 @@ public final class PlaceholderTag implements MediaItemTag {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getServiceId() {
|
public int getServiceId() {
|
||||||
return -1;
|
return Constants.NO_SERVICE_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -44,7 +51,7 @@ public final class PlaceholderTag implements MediaItemTag {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getDurationSeconds() {
|
public long getDurationSeconds() {
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.schabi.newpipe.player.mediaitem;
|
package org.schabi.newpipe.player.mediaitem;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.extractor.stream.VideoStream;
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
||||||
|
@ -11,6 +13,12 @@ import java.util.Optional;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link MediaItemTag} object contains metadata for a resolved stream
|
||||||
|
* that is ready for playback. This object guarantees the {@link StreamInfo}
|
||||||
|
* is available and may provide the {@link Quality} of video stream used in
|
||||||
|
* the {@link MediaItem}.
|
||||||
|
**/
|
||||||
public final class StreamInfoTag implements MediaItemTag {
|
public final class StreamInfoTag implements MediaItemTag {
|
||||||
@NonNull
|
@NonNull
|
||||||
private final StreamInfo streamInfo;
|
private final StreamInfo streamInfo;
|
||||||
|
|
|
@ -23,7 +23,15 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
public class FailedMediaSource extends CompositeMediaSource<Void> implements ManagedMediaSource {
|
public class FailedMediaSource extends CompositeMediaSource<Void> implements ManagedMediaSource {
|
||||||
private static final long SILENCE_DURATION_US = TimeUnit.SECONDS.toMicros(2);
|
/**
|
||||||
|
* Play 2 seconds of silenced audio when a stream fails to resolve due to a known issue,
|
||||||
|
* such as {@link org.schabi.newpipe.extractor.exceptions.ExtractionException}.
|
||||||
|
*
|
||||||
|
* This silence duration allows user to react and have time to jump to a previous stream,
|
||||||
|
* while still provide a smooth playback experience. A duration lower than 1 second is
|
||||||
|
* not recommended, it may cause ExoPlayer to buffer for a while.
|
||||||
|
* */
|
||||||
|
public static final long SILENCE_DURATION_US = TimeUnit.SECONDS.toMicros(2);
|
||||||
|
|
||||||
private final String TAG = "FailedMediaSource@" + Integer.toHexString(hashCode());
|
private final String TAG = "FailedMediaSource@" + Integer.toHexString(hashCode());
|
||||||
private final PlayQueueItem playQueueItem;
|
private final PlayQueueItem playQueueItem;
|
||||||
|
@ -32,7 +40,7 @@ public class FailedMediaSource extends CompositeMediaSource<Void> implements Man
|
||||||
private final MediaSource source;
|
private final MediaSource source;
|
||||||
private final MediaItem mediaItem;
|
private final MediaItem mediaItem;
|
||||||
/**
|
/**
|
||||||
* Permanently fail the play queue item associated with this source, with no hope of retrying.
|
* Fail the play queue item associated with this source, with potential future retries.
|
||||||
*
|
*
|
||||||
* The error will be propagated if the cause for load exception is unspecified.
|
* The error will be propagated if the cause for load exception is unspecified.
|
||||||
* This means the error might be caused by reasons outside of extraction (e.g. no network).
|
* This means the error might be caused by reasons outside of extraction (e.g. no network).
|
||||||
|
|
Loading…
Reference in New Issue