164 lines
5.8 KiB
Java
164 lines
5.8 KiB
Java
package org.schabi.newpipe.player.mediasource;
|
|
|
|
import android.util.Log;
|
|
|
|
import com.google.android.exoplayer2.MediaItem;
|
|
import com.google.android.exoplayer2.Timeline;
|
|
import com.google.android.exoplayer2.source.CompositeMediaSource;
|
|
import com.google.android.exoplayer2.source.MediaPeriod;
|
|
import com.google.android.exoplayer2.source.MediaSource;
|
|
import com.google.android.exoplayer2.source.SilenceMediaSource;
|
|
import com.google.android.exoplayer2.upstream.Allocator;
|
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
|
|
|
import org.schabi.newpipe.player.mediaitem.ExceptionTag;
|
|
import org.schabi.newpipe.player.mediaitem.MediaItemTag;
|
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
|
|
|
import java.io.IOException;
|
|
import java.util.Collections;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
|
|
public class FailedMediaSource extends CompositeMediaSource<Void> implements ManagedMediaSource {
|
|
private static final long SILENCE_DURATION_US = TimeUnit.SECONDS.toMicros(2);
|
|
|
|
private final String TAG = "FailedMediaSource@" + Integer.toHexString(hashCode());
|
|
private final PlayQueueItem playQueueItem;
|
|
private final Throwable error;
|
|
private final long retryTimestamp;
|
|
private final MediaSource source;
|
|
private final MediaItem mediaItem;
|
|
/**
|
|
* Permanently fail the play queue item associated with this source, with no hope of retrying.
|
|
*
|
|
* 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).
|
|
* Otherwise, a silenced stream will play instead.
|
|
*
|
|
* @param playQueueItem play queue item
|
|
* @param error exception that was the reason to fail
|
|
* @param retryTimestamp epoch timestamp when this MediaSource can be refreshed
|
|
*/
|
|
public FailedMediaSource(@NonNull final PlayQueueItem playQueueItem,
|
|
@NonNull final Throwable error,
|
|
final long retryTimestamp) {
|
|
this.playQueueItem = playQueueItem;
|
|
this.error = error;
|
|
this.retryTimestamp = retryTimestamp;
|
|
|
|
final MediaItemTag tag = ExceptionTag
|
|
.of(playQueueItem, Collections.singletonList(error))
|
|
.withExtras(this);
|
|
this.mediaItem = tag.asMediaItem();
|
|
this.source = new SilenceMediaSource.Factory()
|
|
.setDurationUs(SILENCE_DURATION_US)
|
|
.setTag(tag)
|
|
.createMediaSource();
|
|
}
|
|
|
|
public static FailedMediaSource of(@NonNull final PlayQueueItem playQueueItem,
|
|
@NonNull final FailedMediaSourceException error) {
|
|
return new FailedMediaSource(playQueueItem, error, Long.MAX_VALUE);
|
|
}
|
|
|
|
public static FailedMediaSource of(@NonNull final PlayQueueItem playQueueItem,
|
|
@NonNull final Throwable error,
|
|
final long retryWaitMillis) {
|
|
return new FailedMediaSource(playQueueItem, error,
|
|
System.currentTimeMillis() + retryWaitMillis);
|
|
}
|
|
|
|
public PlayQueueItem getStream() {
|
|
return playQueueItem;
|
|
}
|
|
|
|
public Throwable getError() {
|
|
return error;
|
|
}
|
|
|
|
private boolean canRetry() {
|
|
return System.currentTimeMillis() >= retryTimestamp;
|
|
}
|
|
|
|
/**
|
|
* Returns the {@link MediaItem} whose media is provided by the source.
|
|
*/
|
|
@Override
|
|
public MediaItem getMediaItem() {
|
|
return mediaItem;
|
|
}
|
|
|
|
@Override
|
|
protected void prepareSourceInternal(@Nullable final TransferListener mediaTransferListener) {
|
|
super.prepareSourceInternal(mediaTransferListener);
|
|
Log.e(TAG, "Loading failed source: ", error);
|
|
if (error instanceof FailedMediaSourceException) {
|
|
prepareChildSource(null, source);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
|
if (!(error instanceof FailedMediaSourceException)) {
|
|
throw new IOException(error);
|
|
}
|
|
super.maybeThrowSourceInfoRefreshError();
|
|
}
|
|
|
|
@Override
|
|
protected void onChildSourceInfoRefreshed(final Void id,
|
|
final MediaSource mediaSource,
|
|
final Timeline timeline) {
|
|
refreshSourceInfo(timeline);
|
|
}
|
|
|
|
|
|
@Override
|
|
public MediaPeriod createPeriod(final MediaPeriodId id, final Allocator allocator,
|
|
final long startPositionUs) {
|
|
return source.createPeriod(id, allocator, startPositionUs);
|
|
}
|
|
|
|
@Override
|
|
public void releasePeriod(final MediaPeriod mediaPeriod) {
|
|
source.releasePeriod(mediaPeriod);
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldBeReplacedWith(@NonNull final PlayQueueItem newIdentity,
|
|
final boolean isInterruptable) {
|
|
return newIdentity != playQueueItem || canRetry();
|
|
}
|
|
|
|
@Override
|
|
public boolean isStreamEqual(@NonNull final PlayQueueItem stream) {
|
|
return playQueueItem == stream;
|
|
}
|
|
|
|
public static class FailedMediaSourceException extends Exception {
|
|
FailedMediaSourceException(final String message) {
|
|
super(message);
|
|
}
|
|
|
|
FailedMediaSourceException(final Throwable cause) {
|
|
super(cause);
|
|
}
|
|
}
|
|
|
|
public static final class MediaSourceResolutionException extends FailedMediaSourceException {
|
|
public MediaSourceResolutionException(final String message) {
|
|
super(message);
|
|
}
|
|
}
|
|
|
|
public static final class StreamInfoLoadException extends FailedMediaSourceException {
|
|
public StreamInfoLoadException(final Throwable cause) {
|
|
super(cause);
|
|
}
|
|
}
|
|
}
|