mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-29 22:32:59 +00:00 
			
		
		
		
	-Fixed incorrect stream from being played after consecutive player errors.
-Fixed MediaSource reuse due to MediaSourceManager not resetting source on block.
This commit is contained in:
		 John Zhen M
					John Zhen M
				
			
				
					committed by
					
						 John Zhen Mo
						John Zhen Mo
					
				
			
			
				
	
			
			
			 John Zhen Mo
						John Zhen Mo
					
				
			
						parent
						
							2e414cfd63
						
					
				
				
					commit
					f1e52b8b92
				
			| @@ -32,6 +32,7 @@ import android.os.Build; | ||||
| import android.os.IBinder; | ||||
| import android.os.PowerManager; | ||||
| import android.support.annotation.IntRange; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v4.app.NotificationCompat; | ||||
| import android.util.Log; | ||||
| @@ -49,6 +50,7 @@ import org.schabi.newpipe.extractor.MediaFormat; | ||||
| import org.schabi.newpipe.extractor.StreamingService; | ||||
| import org.schabi.newpipe.extractor.stream.AudioStream; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.ListHelper; | ||||
| import org.schabi.newpipe.util.ThemeHelper; | ||||
| @@ -346,7 +348,7 @@ public final class BackgroundPlayer extends Service { | ||||
|         public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) { | ||||
|             resetNotification(); | ||||
|             if (bigNotRemoteView != null) { | ||||
|                 if (currentInfo != null) { | ||||
|                 if (currentItem != null) { | ||||
|                     bigNotRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle()); | ||||
|                     bigNotRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName()); | ||||
|                 } | ||||
| @@ -354,7 +356,7 @@ public final class BackgroundPlayer extends Service { | ||||
|                 bigNotRemoteView.setTextViewText(R.id.notificationTime, getTimeString(currentProgress) + " / " + getTimeString(duration)); | ||||
|             } | ||||
|             if (notRemoteView != null) { | ||||
|                 if (currentInfo != null) { | ||||
|                 if (currentItem != null) { | ||||
|                     notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle()); | ||||
|                     notRemoteView.setTextViewText(R.id.notificationArtist, getUploaderName()); | ||||
|                 } | ||||
| @@ -442,8 +444,8 @@ public final class BackgroundPlayer extends Service { | ||||
|         //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|         @Override | ||||
|         public void sync(@Nullable final StreamInfo info) { | ||||
|             super.sync(info); | ||||
|         public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) { | ||||
|             super.sync(item, info); | ||||
|  | ||||
|             resetNotification(); | ||||
|             notRemoteView.setTextViewText(R.id.notificationSongName, getVideoTitle()); | ||||
|   | ||||
| @@ -79,6 +79,7 @@ import org.schabi.newpipe.player.playback.MediaSourceManager; | ||||
| import org.schabi.newpipe.player.playback.PlaybackListener; | ||||
| import org.schabi.newpipe.playlist.PlayQueue; | ||||
| import org.schabi.newpipe.playlist.PlayQueueAdapter; | ||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.Serializable; | ||||
| @@ -149,6 +150,7 @@ public abstract class BasePlayer implements Player.EventListener, | ||||
|     private long videoPos = -1; | ||||
|  | ||||
|     protected StreamInfo currentInfo; | ||||
|     protected PlayQueueItem currentItem; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Player | ||||
| @@ -729,23 +731,27 @@ public abstract class BasePlayer implements Player.EventListener, | ||||
|         if (getCurrentState() == STATE_BLOCKED) changeState(STATE_BUFFERING); | ||||
|  | ||||
|         simpleExoPlayer.prepare(mediaSource); | ||||
|         simpleExoPlayer.seekToDefaultPosition(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void sync(@Nullable final StreamInfo info) { | ||||
|         if (info == null || simpleExoPlayer == null) return; | ||||
|     public void sync(@android.support.annotation.NonNull final PlayQueueItem item, | ||||
|                      @Nullable final StreamInfo info) { | ||||
|         if (simpleExoPlayer == null) return; | ||||
|         if (DEBUG) Log.d(TAG, "Syncing..."); | ||||
|  | ||||
|         currentItem = item; | ||||
|         currentInfo = info; | ||||
|  | ||||
|         // Check if on wrong window | ||||
|         final int currentSourceIndex = playQueue.getIndex(); | ||||
|         if (!(simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex)) { | ||||
|             final long startPos = currentInfo != null ? currentInfo.start_position : 0; | ||||
|         if (simpleExoPlayer.getCurrentWindowIndex() != currentSourceIndex) { | ||||
|             final long startPos = info != null ? info.start_position : 0; | ||||
|             if (DEBUG) Log.d(TAG, "Rewinding to correct window: " + currentSourceIndex + " at: " + getTimeString((int)startPos)); | ||||
|             simpleExoPlayer.seekTo(currentSourceIndex, startPos); | ||||
|         } | ||||
|  | ||||
|         currentInfo = info; | ||||
|         initThumbnail(info.thumbnail_url); | ||||
|         initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -797,13 +803,14 @@ public abstract class BasePlayer implements Player.EventListener, | ||||
|     } | ||||
|  | ||||
|     public void onPlayPrevious() { | ||||
|         if (simpleExoPlayer == null || playQueue == null || currentInfo == null) return; | ||||
|         if (simpleExoPlayer == null || playQueue == null) return; | ||||
|         if (DEBUG) Log.d(TAG, "onPlayPrevious() called"); | ||||
|  | ||||
|         /* If current playback has run for PLAY_PREV_ACTIVATION_LIMIT milliseconds, restart current track. | ||||
|         * Also restart the track if the current track is the first in a queue.*/ | ||||
|         if (simpleExoPlayer.getCurrentPosition() > PLAY_PREV_ACTIVATION_LIMIT || playQueue.getIndex() == 0) { | ||||
|             simpleExoPlayer.seekTo(currentInfo.start_position); | ||||
|             final long startPos = currentInfo == null ? 0 : currentInfo.start_position; | ||||
|             simpleExoPlayer.seekTo(startPos); | ||||
|         } else { | ||||
|             playQueue.offsetIndex(-1); | ||||
|         } | ||||
| @@ -947,15 +954,15 @@ public abstract class BasePlayer implements Player.EventListener, | ||||
|     } | ||||
|  | ||||
|     public String getVideoUrl() { | ||||
|         return currentInfo == null ? null : currentInfo.url; | ||||
|         return currentItem == null ? null : currentItem.getUrl(); | ||||
|     } | ||||
|  | ||||
|     public String getVideoTitle() { | ||||
|         return currentInfo == null ? null : currentInfo.name; | ||||
|         return currentItem == null ? null : currentItem.getTitle(); | ||||
|     } | ||||
|  | ||||
|     public String getUploaderName() { | ||||
|         return currentInfo == null ? null : currentInfo.uploader_name; | ||||
|         return currentItem == null ? null : currentItem.getUploader(); | ||||
|     } | ||||
|  | ||||
|     public boolean isCompleted() { | ||||
|   | ||||
| @@ -26,6 +26,7 @@ import android.graphics.Color; | ||||
| import android.media.AudioManager; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.util.Log; | ||||
| import android.view.GestureDetector; | ||||
| @@ -43,6 +44,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; | ||||
|  | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | ||||
| import org.schabi.newpipe.util.AnimationUtils; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.PermissionHelper; | ||||
| @@ -250,8 +252,8 @@ public final class MainVideoPlayer extends Activity { | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void sync(@Nullable final StreamInfo info) { | ||||
|             super.sync(info); | ||||
|         public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) { | ||||
|             super.sync(item, info); | ||||
|             titleTextView.setText(getVideoTitle()); | ||||
|             channelTextView.setText(getUploaderName()); | ||||
|  | ||||
|   | ||||
| @@ -31,6 +31,7 @@ import android.graphics.Color; | ||||
| import android.graphics.PorterDuff; | ||||
| import android.os.Build; | ||||
| import android.os.Handler; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v4.content.ContextCompat; | ||||
| import android.util.Log; | ||||
| @@ -64,6 +65,7 @@ import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.MediaFormat; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | ||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | ||||
| import org.schabi.newpipe.util.AnimationUtils; | ||||
| import org.schabi.newpipe.util.ListHelper; | ||||
|  | ||||
| @@ -304,8 +306,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void sync(@Nullable final StreamInfo info) { | ||||
|         super.sync(info); | ||||
|     public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) { | ||||
|         super.sync(item, info); | ||||
|  | ||||
|         if (info != null) { | ||||
|             final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false); | ||||
|   | ||||
| @@ -29,7 +29,7 @@ import io.reactivex.functions.Consumer; | ||||
| public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|     private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode()); | ||||
|     // One-side rolling window size for default loading | ||||
|     // Effectively loads WINDOW_SIZE * 2 + 1 streams, should be at least 1 to ensure gapless playback | ||||
|     // Effectively loads WINDOW_SIZE * 2 + 1 streams, must be greater than 0 | ||||
|     // todo: inject this parameter, allow user settings perhaps | ||||
|     private static final int WINDOW_SIZE = 1; | ||||
|  | ||||
| @@ -116,7 +116,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|      * */ | ||||
|     public void reset() { | ||||
|         tryBlock(); | ||||
|         resetSources(); | ||||
|         populateSources(); | ||||
|     } | ||||
|  | ||||
| @@ -149,6 +148,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|     private void onPlayQueueChanged(final PlayQueueMessage event) { | ||||
|         // why no pattern matching in Java =( | ||||
|         switch (event.type()) { | ||||
|             case INIT: | ||||
|             case REORDER: | ||||
|                 reset(); | ||||
|                 break; | ||||
|             case APPEND: | ||||
|                 populateSources(); | ||||
|                 break; | ||||
| @@ -159,10 +162,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|                 final RemoveEvent removeEvent = (RemoveEvent) event; | ||||
|                 remove(removeEvent.index()); | ||||
|                 break; | ||||
|             case INIT: | ||||
|             case REORDER: | ||||
|                 reset(); | ||||
|                 break; | ||||
|             case MOVE: | ||||
|                 final MoveEvent moveEvent = (MoveEvent) event; | ||||
|                 move(moveEvent.getFromIndex(), moveEvent.getToIndex()); | ||||
| @@ -195,6 +194,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|     private boolean tryBlock() { | ||||
|         if (!isBlocked) { | ||||
|             playbackListener.block(); | ||||
|             resetSources(); | ||||
|             isBlocked = true; | ||||
|             return true; | ||||
|         } | ||||
| @@ -202,7 +202,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|     } | ||||
|  | ||||
|     private boolean tryUnblock() { | ||||
|         if (isPlayQueueReady() && isBlocked) { | ||||
|         if (isPlayQueueReady() && isBlocked && sources != null) { | ||||
|             isBlocked = false; | ||||
|             playbackListener.unblock(sources); | ||||
|             return true; | ||||
| @@ -216,7 +216,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|         final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() { | ||||
|             @Override | ||||
|             public void accept(StreamInfo streamInfo) throws Exception { | ||||
|                 playbackListener.sync(streamInfo); | ||||
|                 playbackListener.sync(currentItem, streamInfo); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @@ -224,7 +224,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|             @Override | ||||
|             public void accept(Throwable throwable) throws Exception { | ||||
|                 Log.e(TAG, "Sync error:", throwable); | ||||
|                 playbackListener.sync(null); | ||||
|                 playbackListener.sync(currentItem,null); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
| @@ -244,11 +244,12 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { | ||||
|  | ||||
|     private void resetSources() { | ||||
|         if (this.sources != null) this.sources.releaseSource(); | ||||
|  | ||||
|         this.sources = new DynamicConcatenatingMediaSource(); | ||||
|     } | ||||
|  | ||||
|     private void populateSources() { | ||||
|         if (sources == null) return; | ||||
|  | ||||
|         for (final PlayQueueItem item : playQueue.getStreams()) { | ||||
|             insert(playQueue.indexOf(item), new DeferredMediaSource(item, this)); | ||||
|         } | ||||
|   | ||||
| @@ -1,41 +1,43 @@ | ||||
| package org.schabi.newpipe.player.playback; | ||||
|  | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
|  | ||||
| import com.google.android.exoplayer2.source.MediaSource; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.playlist.PlayQueueItem; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| public interface PlaybackListener { | ||||
|     /* | ||||
|     /** | ||||
|      * Called when the stream at the current queue index is not ready yet. | ||||
|     * Signals to the listener to block the player from playing anything. | ||||
|      * Signals to the listener to block the player from playing anything and notify the source | ||||
|      * is now invalid. | ||||
|      * | ||||
|      * May be called at any time. | ||||
|      * */ | ||||
|     void block(); | ||||
|  | ||||
|     /* | ||||
|     /** | ||||
|      * Called when the stream at the current queue index is ready. | ||||
|     * Signals to the listener to resume the player. | ||||
|      * Signals to the listener to resume the player by preparing a new source. | ||||
|      * | ||||
|      * May be called only when the player is blocked. | ||||
|      * */ | ||||
|     void unblock(final MediaSource mediaSource); | ||||
|  | ||||
|     /* | ||||
|     /** | ||||
|      * Called when the queue index is refreshed. | ||||
|      * Signals to the listener to synchronize the player's window to the manager's | ||||
|      * window. | ||||
|      * | ||||
|     * May be null. | ||||
|     * May be called only after playback is unblocked. | ||||
|      * May be called only after unblock is called. | ||||
|      * */ | ||||
|     void sync(@Nullable final StreamInfo info); | ||||
|     void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info); | ||||
|  | ||||
|     /* | ||||
|     /** | ||||
|      * Requests the listener to resolve a stream info into a media source | ||||
|      * according to the listener's implementation (background, popup or main video player). | ||||
|      * | ||||
| @@ -43,7 +45,7 @@ public interface PlaybackListener { | ||||
|      * */ | ||||
|     MediaSource sourceOf(final StreamInfo info); | ||||
|  | ||||
|     /* | ||||
|     /** | ||||
|      * Called when the play queue can no longer to played or used. | ||||
|      * Currently, this means the play queue is empty and complete. | ||||
|      * Signals to the listener that it should shutdown. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user