mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2025-10-03 09:49:21 +02:00
Merge 268ae39e2b
into 965eea2124
This commit is contained in:
commit
4e9aeb4ced
7 changed files with 159 additions and 19 deletions
|
@ -114,6 +114,7 @@ import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
|||
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
|
||||
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver;
|
||||
import org.schabi.newpipe.player.resolver.VideoPlaybackResolver.SourceType;
|
||||
import org.schabi.newpipe.player.ui.BackgroundPlayerUi;
|
||||
import org.schabi.newpipe.player.ui.MainPlayerUi;
|
||||
import org.schabi.newpipe.player.ui.PlayerUi;
|
||||
import org.schabi.newpipe.player.ui.PlayerUiList;
|
||||
|
@ -271,6 +272,7 @@ public final class Player implements PlaybackListener, Listener {
|
|||
@NonNull
|
||||
private final HistoryRecordManager recordManager;
|
||||
|
||||
private boolean screenOn = true;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
|
@ -592,14 +594,17 @@ public final class Player implements PlaybackListener, Listener {
|
|||
switch (playerType) {
|
||||
case MAIN:
|
||||
UIs.destroyAll(PopupPlayerUi.class);
|
||||
UIs.destroyAll(BackgroundPlayerUi.class);
|
||||
UIs.addAndPrepare(new MainPlayerUi(this, binding));
|
||||
break;
|
||||
case POPUP:
|
||||
UIs.destroyAll(MainPlayerUi.class);
|
||||
UIs.destroyAll(BackgroundPlayerUi.class);
|
||||
UIs.addAndPrepare(new PopupPlayerUi(this, binding));
|
||||
break;
|
||||
case AUDIO:
|
||||
UIs.destroyAll(VideoPlayerUi.class);
|
||||
UIs.addAndPrepare(new BackgroundPlayerUi(this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -842,6 +847,12 @@ public final class Player implements PlaybackListener, Listener {
|
|||
case ACTION_SHUFFLE:
|
||||
toggleShuffleModeEnabled();
|
||||
break;
|
||||
case Intent.ACTION_SCREEN_OFF:
|
||||
screenOn = false;
|
||||
break;
|
||||
case Intent.ACTION_SCREEN_ON:
|
||||
screenOn = true;
|
||||
break;
|
||||
case Intent.ACTION_CONFIGURATION_CHANGED:
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "ACTION_CONFIGURATION_CHANGED received");
|
||||
|
@ -2195,12 +2206,12 @@ public final class Player implements PlaybackListener, Listener {
|
|||
}
|
||||
}
|
||||
|
||||
public void useVideoSource(final boolean videoEnabled) {
|
||||
if (playQueue == null || audioPlayerSelected()) {
|
||||
public void useVideoAndSubtitles(final boolean videoAndSubtitlesEnabled) {
|
||||
if (playQueue == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
isAudioOnly = !videoEnabled;
|
||||
isAudioOnly = !videoAndSubtitlesEnabled;
|
||||
|
||||
getCurrentStreamInfo().ifPresentOrElse(info -> {
|
||||
// In case we don't know the source type, fall back to either video-with-audio, or
|
||||
|
@ -2208,16 +2219,11 @@ public final class Player implements PlaybackListener, Listener {
|
|||
final SourceType sourceType = videoResolver.getStreamSourceType()
|
||||
.orElse(SourceType.VIDEO_WITH_AUDIO_OR_AUDIO_ONLY);
|
||||
|
||||
setRecovery();
|
||||
|
||||
if (playQueueManagerReloadingNeeded(sourceType, info, getVideoRendererIndex())) {
|
||||
reloadPlayQueueManager();
|
||||
}
|
||||
|
||||
setRecovery();
|
||||
|
||||
// Disable or enable video and subtitles renderers depending of the videoEnabled value
|
||||
trackSelector.setParameters(trackSelector.buildUponParameters()
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoEnabled)
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoEnabled));
|
||||
}, () -> {
|
||||
/*
|
||||
The current metadata may be null sometimes (for e.g. when using an unstable connection
|
||||
|
@ -2226,9 +2232,15 @@ public final class Player implements PlaybackListener, Listener {
|
|||
Reload the play queue manager in this case, which is the behavior when we don't know the
|
||||
index of the video renderer or playQueueManagerReloadingNeeded returns true
|
||||
*/
|
||||
reloadPlayQueueManager();
|
||||
setRecovery();
|
||||
reloadPlayQueueManager();
|
||||
});
|
||||
|
||||
// Disable or enable video and subtitles renderers depending of the
|
||||
// videoAndSubtitlesEnabled value
|
||||
trackSelector.setParameters(trackSelector.buildUponParameters()
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, !videoAndSubtitlesEnabled)
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, !videoAndSubtitlesEnabled));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2461,4 +2473,11 @@ public final class Player implements PlaybackListener, Listener {
|
|||
.orElse(RENDERER_UNAVAILABLE);
|
||||
}
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* @return whether the device screen is turned on.
|
||||
*/
|
||||
public boolean isScreenOn() {
|
||||
return screenOn;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,13 @@ public class PlayerDataSource {
|
|||
getDefaultDashChunkSourceFactory(cachelessDataSourceFactory),
|
||||
cachelessDataSourceFactory);
|
||||
}
|
||||
|
||||
public DashMediaSource.Factory getLiveYoutubeDashMediaSourceFactory() {
|
||||
return new DashMediaSource.Factory(
|
||||
getDefaultDashChunkSourceFactory(cachelessDataSourceFactory),
|
||||
cachelessDataSourceFactory)
|
||||
.setManifestParser(new YoutubeDashLiveManifestParser());
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package org.schabi.newpipe.player.helper;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.ProgramInformation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.ServiceDescriptionElement;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link DashManifestParser} fixing YouTube DASH manifests to allow starting playback from the
|
||||
* newest period available instead of the earliest one in some cases.
|
||||
*
|
||||
* <p>
|
||||
* It changes the {@code availabilityStartTime} passed to a custom value doing the workaround.
|
||||
* A better approach to fix the issue should be investigated and used in the future.
|
||||
* </p>
|
||||
*/
|
||||
public class YoutubeDashLiveManifestParser extends DashManifestParser {
|
||||
|
||||
// Result of Util.parseXsDateTime("1970-01-01T00:00:00Z")
|
||||
private static final long AVAILABILITY_START_TIME_TO_USE = 0;
|
||||
|
||||
// There is no computation made with the availabilityStartTime value in the
|
||||
// parseMediaPresentationDescription method itself, so we can just override methods called in
|
||||
// this method using the workaround value
|
||||
// Overriding parsePeriod does not seem to be needed
|
||||
|
||||
@SuppressWarnings("checkstyle:ParameterNumber")
|
||||
@NonNull
|
||||
@Override
|
||||
protected DashManifest buildMediaPresentationDescription(
|
||||
final long availabilityStartTime,
|
||||
final long durationMs,
|
||||
final long minBufferTimeMs,
|
||||
final boolean dynamic,
|
||||
final long minUpdateTimeMs,
|
||||
final long timeShiftBufferDepthMs,
|
||||
final long suggestedPresentationDelayMs,
|
||||
final long publishTimeMs,
|
||||
@Nullable final ProgramInformation programInformation,
|
||||
@Nullable final UtcTimingElement utcTiming,
|
||||
@Nullable final ServiceDescriptionElement serviceDescription,
|
||||
@Nullable final Uri location,
|
||||
@NonNull final List<Period> periods) {
|
||||
return super.buildMediaPresentationDescription(
|
||||
AVAILABILITY_START_TIME_TO_USE,
|
||||
durationMs,
|
||||
minBufferTimeMs,
|
||||
dynamic,
|
||||
minUpdateTimeMs,
|
||||
timeShiftBufferDepthMs,
|
||||
suggestedPresentationDelayMs,
|
||||
publishTimeMs,
|
||||
programInformation,
|
||||
utcTiming,
|
||||
serviceDescription,
|
||||
location,
|
||||
periods);
|
||||
}
|
||||
}
|
|
@ -201,12 +201,13 @@ public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
|
|||
|
||||
try {
|
||||
final StreamInfoTag tag = StreamInfoTag.of(info);
|
||||
if (!info.getHlsUrl().isEmpty()) {
|
||||
return buildLiveMediaSource(dataSource, info.getHlsUrl(), C.CONTENT_TYPE_HLS, tag);
|
||||
} else if (!info.getDashMpdUrl().isEmpty()) {
|
||||
if (!info.getDashMpdUrl().isEmpty()) {
|
||||
return buildLiveMediaSource(
|
||||
dataSource, info.getDashMpdUrl(), C.CONTENT_TYPE_DASH, tag);
|
||||
}
|
||||
if (!info.getHlsUrl().isEmpty()) {
|
||||
return buildLiveMediaSource(dataSource, info.getHlsUrl(), C.CONTENT_TYPE_HLS, tag);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, "Error when generating live media source, falling back to standard sources",
|
||||
e);
|
||||
|
@ -225,7 +226,11 @@ public interface PlaybackResolver extends Resolver<StreamInfo, MediaSource> {
|
|||
factory = dataSource.getLiveSsMediaSourceFactory();
|
||||
break;
|
||||
case C.CONTENT_TYPE_DASH:
|
||||
if (metadata.getServiceId() == ServiceList.YouTube.getServiceId()) {
|
||||
factory = dataSource.getLiveYoutubeDashMediaSourceFactory();
|
||||
} else {
|
||||
factory = dataSource.getLiveDashMediaSourceFactory();
|
||||
}
|
||||
break;
|
||||
case C.CONTENT_TYPE_HLS:
|
||||
factory = dataSource.getLiveHlsMediaSourceFactory();
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package org.schabi.newpipe.player.ui;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.schabi.newpipe.player.Player;
|
||||
|
||||
/**
|
||||
* This is not a real UI for the background player, it used to disable fetching video and text
|
||||
* tracks with it.
|
||||
*
|
||||
* <p>
|
||||
* This allows reducing data usage for manifest sources with demuxed audio and video,
|
||||
* such as livestreams.
|
||||
* </p>
|
||||
*/
|
||||
public class BackgroundPlayerUi extends PlayerUi {
|
||||
|
||||
public BackgroundPlayerUi(@NonNull final Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initPlayback() {
|
||||
super.initPlayback();
|
||||
|
||||
// Make sure to disable video and subtitles track types
|
||||
player.useVideoAndSubtitles(false);
|
||||
}
|
||||
}
|
|
@ -216,6 +216,10 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
|
|||
playQueueAdapter = new PlayQueueAdapter(context,
|
||||
Objects.requireNonNull(player.getPlayQueue()));
|
||||
segmentAdapter = new StreamSegmentAdapter(getStreamSegmentListener());
|
||||
|
||||
// Make sure video and text tracks are enabled if the user is in the app, in the case user
|
||||
// switched from background player to main player
|
||||
player.useVideoAndSubtitles(fragmentIsVisible);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -329,7 +333,7 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
|
|||
} else if (VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED.equals(intent.getAction())) {
|
||||
// Restore video source when user returns to the fragment
|
||||
fragmentIsVisible = true;
|
||||
player.useVideoSource(true);
|
||||
player.useVideoAndSubtitles(true);
|
||||
|
||||
// When a user returns from background, the system UI will always be shown even if
|
||||
// controls are invisible: hide it in that case
|
||||
|
@ -368,7 +372,7 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh
|
|||
if (player.isPlaying() || player.isLoading()) {
|
||||
switch (getMinimizeOnExitAction(context)) {
|
||||
case MINIMIZE_ON_EXIT_MODE_BACKGROUND:
|
||||
player.useVideoSource(false);
|
||||
player.useVideoAndSubtitles(false);
|
||||
break;
|
||||
case MINIMIZE_ON_EXIT_MODE_POPUP:
|
||||
getParentActivity().ifPresent(activity -> {
|
||||
|
|
|
@ -151,6 +151,14 @@ public final class PopupPlayerUi extends VideoPlayerUi {
|
|||
windowManager.addView(closeOverlayBinding.getRoot(), closeOverlayLayoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initPlayback() {
|
||||
super.initPlayback();
|
||||
// Make sure video and text tracks are enabled if the screen is turned on (which should
|
||||
// always be the case), in the case user switched from background player to popup player
|
||||
player.useVideoAndSubtitles(player.isScreenOn());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupElementsVisibility() {
|
||||
binding.fullScreenButton.setVisibility(View.VISIBLE);
|
||||
|
@ -216,10 +224,10 @@ public final class PopupPlayerUi extends VideoPlayerUi {
|
|||
} else if (player.isPlaying() || player.isLoading()) {
|
||||
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
|
||||
// Use only audio source when screen turns off while popup player is playing
|
||||
player.useVideoSource(false);
|
||||
player.useVideoAndSubtitles(false);
|
||||
} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
|
||||
// Restore video source when screen turns on and user was watching video in popup
|
||||
player.useVideoSource(true);
|
||||
player.useVideoAndSubtitles(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue