-Modified quality change to use internal track selection.

-Enabled audio processing integration with system EQ.
-Re-endabled HDR through tunneling on videos only.
This commit is contained in:
John Zhen M 2017-09-23 21:50:32 -07:00 committed by John Zhen Mo
parent 8e3be3826f
commit e742091a37
4 changed files with 122 additions and 81 deletions

View File

@ -395,13 +395,8 @@ public final class BackgroundPlayer extends Service {
@Override @Override
public MediaSource sourceOf(final StreamInfo info) { public MediaSource sourceOf(final StreamInfo info) {
List<MediaSource> sources = new ArrayList<>(); final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
for (final AudioStream audio : info.audio_streams) { return buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
final MediaSource audioSource = buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
sources.add(audioSource);
}
return new MergingMediaSource(sources.toArray(new MediaSource[sources.size()]));
} }
@Override @Override

View File

@ -29,6 +29,7 @@ import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.audiofx.AudioEffect;
import android.net.Uri; import android.net.Uri;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.text.TextUtils; import android.text.TextUtils;
@ -40,11 +41,14 @@ import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
@ -103,7 +107,7 @@ import io.reactivex.functions.Predicate;
*/ */
@SuppressWarnings({"WeakerAccess", "unused"}) @SuppressWarnings({"WeakerAccess", "unused"})
public abstract class BasePlayer implements Player.EventListener, public abstract class BasePlayer implements Player.EventListener,
AudioManager.OnAudioFocusChangeListener, PlaybackListener { AudioManager.OnAudioFocusChangeListener, PlaybackListener, AudioRendererEventListener {
// TODO: Check api version for deprecated audio manager methods // TODO: Check api version for deprecated audio manager methods
public static final boolean DEBUG = true; public static final boolean DEBUG = true;
@ -159,6 +163,7 @@ public abstract class BasePlayer implements Player.EventListener,
protected SimpleExoPlayer simpleExoPlayer; protected SimpleExoPlayer simpleExoPlayer;
protected boolean isPrepared = false; protected boolean isPrepared = false;
protected DefaultTrackSelector trackSelector;
protected CacheDataSourceFactory cacheDataSourceFactory; protected CacheDataSourceFactory cacheDataSourceFactory;
protected final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory(); protected final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
protected final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); protected final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
@ -212,11 +217,13 @@ public abstract class BasePlayer implements Player.EventListener,
} }
AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(trackSelectionFactory); trackSelector = new DefaultTrackSelector(trackSelectionFactory);
DefaultLoadControl loadControl = new DefaultLoadControl(); DefaultLoadControl loadControl = new DefaultLoadControl();
final RenderersFactory renderFactory = new DefaultRenderersFactory(context); final RenderersFactory renderFactory = new DefaultRenderersFactory(context);
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderFactory, defaultTrackSelector, loadControl); simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderFactory, trackSelector, loadControl);
simpleExoPlayer.setAudioDebugListener(this);
simpleExoPlayer.addListener(this); simpleExoPlayer.addListener(this);
simpleExoPlayer.setPlayWhenReady(true); simpleExoPlayer.setPlayWhenReady(true);
} }
@ -346,6 +353,7 @@ public abstract class BasePlayer implements Player.EventListener,
unregisterBroadcastReceiver(); unregisterBroadcastReceiver();
trackSelector = null;
simpleExoPlayer = null; simpleExoPlayer = null;
} }
@ -455,6 +463,33 @@ public abstract class BasePlayer implements Player.EventListener,
animateAudio(simpleExoPlayer.getVolume(), DUCK_AUDIO_TO, DUCK_DURATION); animateAudio(simpleExoPlayer.getVolume(), DUCK_AUDIO_TO, DUCK_DURATION);
} }
/*//////////////////////////////////////////////////////////////////////////
// Audio Processing
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onAudioEnabled(DecoderCounters decoderCounters) {}
@Override
public void onAudioSessionId(int i) {
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, i);
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
context.sendBroadcast(intent);
}
@Override
public void onAudioDecoderInitialized(String s, long l, long l1) {}
@Override
public void onAudioInputFormatChanged(Format format) {}
@Override
public void onAudioTrackUnderrun(int i, long l, long l1) {}
@Override
public void onAudioDisabled(DecoderCounters decoderCounters) {}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// States Implementation // States Implementation
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -594,6 +629,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
if (DEBUG) Log.d(TAG, "onTracksChanged(), track group size = " + trackGroups.length);
} }
@Override @Override

View File

@ -418,7 +418,7 @@ public final class PopupVideoPlayer extends Service {
} else { } else {
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class) intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
.putExtra(PlayVideoActivity.VIDEO_TITLE, getVideoTitle()) .putExtra(PlayVideoActivity.VIDEO_TITLE, getVideoTitle())
.putExtra(PlayVideoActivity.STREAM_URL, getSelectedStreamUri().toString()) .putExtra(PlayVideoActivity.STREAM_URL, getSelectedVideoStream().url)
.putExtra(PlayVideoActivity.VIDEO_URL, getVideoUrl()) .putExtra(PlayVideoActivity.VIDEO_URL, getVideoUrl())
.putExtra(PlayVideoActivity.START_POSITION, Math.round(getPlayer().getCurrentPosition() / 1000f)); .putExtra(PlayVideoActivity.START_POSITION, Math.round(getPlayer().getCurrentPosition() / 1000f));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

View File

@ -45,20 +45,25 @@ import android.widget.ProgressBar;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.TextView; import android.widget.TextView;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.playlist.PlayQueue; import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.ListHelper;
@ -91,9 +96,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public static final String PLAY_QUEUE = "play_queue"; public static final String PLAY_QUEUE = "play_queue";
public static final String PLAYER_INTENT = "player_intent"; public static final String PLAYER_INTENT = "player_intent";
private int selectedIndexStream = -1; private VideoStream selectedIndexStream;
private ArrayList<VideoStream> videoStreamsList = new ArrayList<>();
private AudioStream videoOnlyAudioStream;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Player // Player
@ -102,6 +105,12 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
private static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f}; private static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f};
private static final TrackSelection.Factory FIXED_FACTORY = new FixedTrackSelection.Factory();
private int videoRendererIndex;
private TrackGroupArray videoTrackGroups;
private List<TrackGroupInfo> trackGroupInfos;
private MappingTrackSelector.SelectionOverride override;
private boolean startedFromNewPipe = true; private boolean startedFromNewPipe = true;
protected boolean wasPlaying = false; protected boolean wasPlaying = false;
@ -202,6 +211,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
super.initPlayer(); super.initPlayer();
simpleExoPlayer.setVideoSurfaceView(surfaceView); simpleExoPlayer.setVideoSurfaceView(surfaceView);
simpleExoPlayer.setVideoListener(this); simpleExoPlayer.setVideoListener(this);
trackSelector.setTunnelingAudioSessionId(C.generateAudioSessionIdV21(context));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -240,16 +251,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
super.sync(info, sortedStreamsIndex); super.sync(info, sortedStreamsIndex);
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false); final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
videoStreamsList = new ArrayList<>(videos); selectedIndexStream = videos.get(ListHelper.getDefaultResolutionIndex(context, videos));
if (sortedStreamsIndex == PlayQueueItem.DEFAULT_QUALITY) {
selectedIndexStream = ListHelper.getDefaultResolutionIndex(context, videos);
} else {
selectedIndexStream = sortedStreamsIndex;
}
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
buildQualityMenu(qualityPopupMenu);
playbackSpeedPopupMenu.getMenu().removeGroup(playbackSpeedPopupMenuGroupId); playbackSpeedPopupMenu.getMenu().removeGroup(playbackSpeedPopupMenuGroupId);
buildPlaybackSpeedMenu(playbackSpeedPopupMenu); buildPlaybackSpeedMenu(playbackSpeedPopupMenu);
@ -264,26 +266,9 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
sources.add(mediaSource); sources.add(mediaSource);
} }
final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
if (audio != null) {
final Uri audioUri = Uri.parse(audio.url);
final MediaSource audioSource = new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null);
sources.add(audioSource);
}
return new MergingMediaSource(sources.toArray(new MediaSource[sources.size()])); return new MergingMediaSource(sources.toArray(new MediaSource[sources.size()]));
} }
public void buildQualityMenu(PopupMenu popupMenu) {
for (int i = 0; i < videoStreamsList.size(); i++) {
VideoStream videoStream = videoStreamsList.get(i);
popupMenu.getMenu().add(qualityPopupMenuGroupId, i, Menu.NONE, MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
}
qualityTextView.setText(getSelectedVideoStream().resolution);
popupMenu.setOnMenuItemClickListener(this);
popupMenu.setOnDismissListener(this);
}
private void buildPlaybackSpeedMenu(PopupMenu popupMenu) { private void buildPlaybackSpeedMenu(PopupMenu popupMenu) {
for (int i = 0; i < PLAYBACK_SPEEDS.length; i++) { for (int i = 0; i < PLAYBACK_SPEEDS.length; i++) {
popupMenu.getMenu().add(playbackSpeedPopupMenuGroupId, i, Menu.NONE, formatSpeed(PLAYBACK_SPEEDS[i])); popupMenu.getMenu().add(playbackSpeedPopupMenuGroupId, i, Menu.NONE, formatSpeed(PLAYBACK_SPEEDS[i]));
@ -368,6 +353,61 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
// ExoPlayer Video Listener // ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private class TrackGroupInfo {
final int track;
final int group;
final Format format;
TrackGroupInfo(final int track, final int group, final Format format) {
this.track = track;
this.group = group;
this.format = format;
}
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
super.onTracksChanged(trackGroups, trackSelections);
for (int t = 0; t < simpleExoPlayer.getRendererCount(); t++) {
if (simpleExoPlayer.getRendererType(t) == C.TRACK_TYPE_VIDEO) {
videoRendererIndex = t;
}
}
videoTrackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(videoRendererIndex);
override = trackSelector.getSelectionOverride(videoRendererIndex, videoTrackGroups);
final Format format = trackSelections.get(videoRendererIndex).getSelectedFormat();
final String resolution = Math.min(format.width, format.height) + "p";
qualityTextView.setText(resolution);
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
buildQualityMenu(qualityPopupMenu);
}
private void buildQualityMenu(PopupMenu popupMenu) {
trackGroupInfos = new ArrayList<>();
int acc = 0;
for (int groupIndex = 0; groupIndex < videoTrackGroups.length; groupIndex++) {
final TrackGroup group = videoTrackGroups.get(groupIndex);
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
final Format format = group.getFormat(trackIndex);
final MediaFormat mediaFormat = MediaFormat.getFromMimeType(format.sampleMimeType);
final String mediaName = mediaFormat == null ? format.sampleMimeType : mediaFormat.name;
final String resolution = Math.min(format.width, format.height) + "p";
popupMenu.getMenu().add(qualityPopupMenuGroupId, acc, Menu.NONE, mediaName + " " + resolution);
trackGroupInfos.add(new TrackGroupInfo(trackIndex, groupIndex, format));
acc++;
}
}
popupMenu.setOnMenuItemClickListener(this);
popupMenu.setOnDismissListener(this);
}
@Override @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
if (DEBUG) { if (DEBUG) {
@ -471,13 +511,13 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]"); Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
if (qualityPopupMenuGroupId == menuItem.getGroupId()) { if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
if (selectedIndexStream == menuItem.getItemId() || getRecovery()) return true; final int itemId = menuItem.getItemId();
final TrackGroupInfo info = trackGroupInfos.get(itemId);
final int index = playQueue.getIndex();
setRecovery(index, simpleExoPlayer.getCurrentPosition());
playQueue.updateIndex(index, menuItem.getItemId());
qualityTextView.setText(menuItem.getTitle()); qualityTextView.setText(menuItem.getTitle());
override = new MappingTrackSelector.SelectionOverride(FIXED_FACTORY, info.group, info.track);
trackSelector.setSelectionOverride(videoRendererIndex, videoTrackGroups, override);
return true; return true;
} else if (playbackSpeedPopupMenuGroupId == menuItem.getGroupId()) { } else if (playbackSpeedPopupMenuGroupId == menuItem.getGroupId()) {
int speedIndex = menuItem.getItemId(); int speedIndex = menuItem.getItemId();
@ -506,8 +546,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
isSomePopupMenuVisible = true; isSomePopupMenuVisible = true;
showControls(300); showControls(300);
VideoStream videoStream = getSelectedVideoStream();
qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
wasPlaying = simpleExoPlayer.getPlayWhenReady(); wasPlaying = simpleExoPlayer.getPlayWhenReady();
} }
@ -665,42 +703,14 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
return wasPlaying; return wasPlaying;
} }
public VideoStream getSelectedVideoStream() {
return videoStreamsList.get(selectedIndexStream);
}
public Uri getSelectedStreamUri() {
return Uri.parse(getSelectedVideoStream().url);
}
public int getQualityPopupMenuGroupId() { public int getQualityPopupMenuGroupId() {
return qualityPopupMenuGroupId; return qualityPopupMenuGroupId;
} }
public int getSelectedStreamIndex() { public VideoStream getSelectedVideoStream() {
return selectedIndexStream; return selectedIndexStream;
} }
public void setSelectedIndexStream(int selectedIndexStream) {
this.selectedIndexStream = selectedIndexStream;
}
public void setAudioStream(AudioStream audioStream) {
this.videoOnlyAudioStream = audioStream;
}
public AudioStream getAudioStream() {
return videoOnlyAudioStream;
}
public ArrayList<VideoStream> getVideoStreamsList() {
return videoStreamsList;
}
public void setVideoStreamsList(ArrayList<VideoStream> videoStreamsList) {
this.videoStreamsList = videoStreamsList;
}
public boolean isStartedFromNewPipe() { public boolean isStartedFromNewPipe() {
return startedFromNewPipe; return startedFromNewPipe;
} }