1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-12-23 00:20:32 +00:00

added: documentations to MediaItemTags and Player.

fixed: checkStyle failures.
This commit is contained in:
karyogamy 2022-03-16 20:47:29 -04:00
parent 4e459b3383
commit 69646e5b5d
6 changed files with 79 additions and 23 deletions

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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).