From b06238ba5d8df01beecd2b97c1c03c6057c4634a Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 22 Feb 2016 20:28:37 +0100 Subject: [PATCH] initial changes --- app/src/main/AndroidManifest.xml | 6 +- .../newpipe/VideoItemDetailFragment.java | 19 +- .../newpipe/exoplayer/ExoPlayerActivity.java | 729 ----------------- .../{ => player}/BackgroundPlayer.java | 8 +- .../newpipe/player/ExoPlayerActivity.java | 755 ++++++++++++++++++ .../{ => player}/PlayVideoActivity.java | 6 +- .../exoplayer/DashRendererBuilder.java | 4 +- .../{ => player}/exoplayer/EventLogger.java | 2 +- .../exoplayer/ExtractorRendererBuilder.java | 4 +- .../exoplayer/HlsRendererBuilder.java | 4 +- .../{ => player}/exoplayer/NPExoPlayer.java | 2 +- .../SmoothStreamingRendererBuilder.java | 4 +- .../SmoothStreamingTestMediaDrmCallback.java | 2 +- .../WidevineTestMediaDrmCallback.java | 2 +- .../main/res/layout/activity_play_video.xml | 2 +- 15 files changed, 796 insertions(+), 753 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/exoplayer/ExoPlayerActivity.java rename app/src/main/java/org/schabi/newpipe/{ => player}/BackgroundPlayer.java (98%) create mode 100644 app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java rename app/src/main/java/org/schabi/newpipe/{ => player}/PlayVideoActivity.java (99%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/DashRendererBuilder.java (98%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/EventLogger.java (99%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/ExtractorRendererBuilder.java (96%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/HlsRendererBuilder.java (98%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/NPExoPlayer.java (99%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/SmoothStreamingRendererBuilder.java (98%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/SmoothStreamingTestMediaDrmCallback.java (98%) rename app/src/main/java/org/schabi/newpipe/{ => player}/exoplayer/WidevineTestMediaDrmCallback.java (97%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bf10d6e05..e4570133d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -76,14 +76,14 @@ - 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - preparePlayer(true); - } else { - Toast.makeText(getApplicationContext(), R.string.storage_permission_denied, - Toast.LENGTH_LONG).show(); - finish(); - } - } - - // Permission management methods - - /** - * Checks whether it is necessary to ask for permission to read storage. If necessary, it also - * requests permission. - * - * @return true if a permission request is made. False if it is not necessary. - */ - @TargetApi(23) - private boolean maybeRequestPermission() { - if (requiresPermission(contentUri)) { - requestPermissions(new String[] {permission.READ_EXTERNAL_STORAGE}, 0); - return true; - } else { - return false; - } - } - - @TargetApi(23) - private boolean requiresPermission(Uri uri) { - return Util.SDK_INT >= 23 - && Util.isLocalFileUri(uri) - && checkSelfPermission(permission.READ_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED; - } - - // Internal methods - - private RendererBuilder getRendererBuilder() { - String userAgent = Util.getUserAgent(this, "NewPipeExoPlayer"); - switch (contentType) { - case Util.TYPE_SS: - return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(), - new SmoothStreamingTestMediaDrmCallback()); - case Util.TYPE_DASH: - return new DashRendererBuilder(this, userAgent, contentUri.toString(), - new WidevineTestMediaDrmCallback(contentId, provider)); - case Util.TYPE_HLS: - return new HlsRendererBuilder(this, userAgent, contentUri.toString()); - case Util.TYPE_OTHER: - return new ExtractorRendererBuilder(this, userAgent, contentUri); - default: - throw new IllegalStateException("Unsupported type: " + contentType); - } - } - - private void preparePlayer(boolean playWhenReady) { - if (player == null) { - player = new NPExoPlayer(getRendererBuilder()); - player.addListener(this); - player.setCaptionListener(this); - player.setMetadataListener(this); - player.seekTo(playerPosition); - playerNeedsPrepare = true; - mediaController.setMediaPlayer(player.getPlayerControl()); - mediaController.setEnabled(true); - eventLogger = new EventLogger(); - eventLogger.startSession(); - player.addListener(eventLogger); - player.setInfoListener(eventLogger); - player.setInternalErrorListener(eventLogger); - debugViewHelper = new DebugTextViewHelper(player, debugTextView); - playerStateTextView.setVisibility(View.GONE); - debugTextView.setVisibility(View.GONE); - debugViewHelper.start(); - } - if (playerNeedsPrepare) { - player.prepare(); - playerNeedsPrepare = false; - updateButtonVisibilities(); - } - player.setSurface(surfaceView.getHolder().getSurface()); - player.setPlayWhenReady(playWhenReady); - } - - private void releasePlayer() { - if (player != null) { - debugViewHelper.stop(); - debugViewHelper = null; - playerPosition = player.getCurrentPosition(); - player.release(); - player = null; - eventLogger.endSession(); - eventLogger = null; - } - } - - // NPExoPlayer.Listener implementation - - @Override - public void onStateChanged(boolean playWhenReady, int playbackState) { - if (playbackState == ExoPlayer.STATE_ENDED) { - showControls(); - } - String text = "playWhenReady=" + playWhenReady + ", playbackState="; - switch(playbackState) { - case ExoPlayer.STATE_BUFFERING: - text += "buffering"; - break; - case ExoPlayer.STATE_ENDED: - text += "ended"; - break; - case ExoPlayer.STATE_IDLE: - text += "idle"; - break; - case ExoPlayer.STATE_PREPARING: - text += "preparing"; - break; - case ExoPlayer.STATE_READY: - text += "ready"; - break; - default: - text += "unknown"; - break; - } - playerStateTextView.setText(text); - updateButtonVisibilities(); - } - - @Override - public void onError(Exception e) { - String errorString = null; - if (e instanceof UnsupportedDrmException) { - // Special case DRM failures. - UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e; - errorString = getString(Util.SDK_INT < 18 ? R.string.error_drm_not_supported - : unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); - } else if (e instanceof ExoPlaybackException - && e.getCause() instanceof DecoderInitializationException) { - // Special case for decoder initialization failures. - DecoderInitializationException decoderInitializationException = - (DecoderInitializationException) e.getCause(); - if (decoderInitializationException.decoderName == null) { - if (decoderInitializationException.getCause() instanceof DecoderQueryException) { - errorString = getString(R.string.error_querying_decoders); - } else if (decoderInitializationException.secureDecoderRequired) { - errorString = getString(R.string.error_no_secure_decoder, - decoderInitializationException.mimeType); - } else { - errorString = getString(R.string.error_no_decoder, - decoderInitializationException.mimeType); - } - } else { - errorString = getString(R.string.error_instantiating_decoder, - decoderInitializationException.decoderName); - } - } - if (errorString != null) { - Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_LONG).show(); - } - playerNeedsPrepare = true; - updateButtonVisibilities(); - showControls(); - } - - @Override - public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, - float pixelWidthAspectRatio) { - shutterView.setVisibility(View.GONE); - videoFrame.setAspectRatio( - height == 0 ? 1 : (width * pixelWidthAspectRatio) / height); - } - - // User controls - - private void updateButtonVisibilities() { - retryButton.setVisibility(playerNeedsPrepare ? View.VISIBLE : View.GONE); - videoButton.setVisibility(haveTracks(NPExoPlayer.TYPE_VIDEO) ? View.VISIBLE : View.GONE); - audioButton.setVisibility(haveTracks(NPExoPlayer.TYPE_AUDIO) ? View.VISIBLE : View.GONE); - textButton.setVisibility(haveTracks(NPExoPlayer.TYPE_TEXT) ? View.VISIBLE : View.GONE); - } - - private boolean haveTracks(int type) { - return player != null && player.getTrackCount(type) > 0; - } - - public void showVideoPopup(View v) { - PopupMenu popup = new PopupMenu(this, v); - configurePopupWithTracks(popup, null, NPExoPlayer.TYPE_VIDEO); - popup.show(); - } - - public void showAudioPopup(View v) { - PopupMenu popup = new PopupMenu(this, v); - Menu menu = popup.getMenu(); - menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.enable_background_audio); - final MenuItem backgroundAudioItem = menu.findItem(0); - backgroundAudioItem.setCheckable(true); - backgroundAudioItem.setChecked(enableBackgroundAudio); - OnMenuItemClickListener clickListener = new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - if (item == backgroundAudioItem) { - enableBackgroundAudio = !item.isChecked(); - return true; - } - return false; - } - }; - configurePopupWithTracks(popup, clickListener, NPExoPlayer.TYPE_AUDIO); - popup.show(); - } - - public void showTextPopup(View v) { - PopupMenu popup = new PopupMenu(this, v); - configurePopupWithTracks(popup, null, NPExoPlayer.TYPE_TEXT); - popup.show(); - } - - public void showVerboseLogPopup(View v) { - PopupMenu popup = new PopupMenu(this, v); - Menu menu = popup.getMenu(); - menu.add(Menu.NONE, 0, Menu.NONE, R.string.logging_normal); - menu.add(Menu.NONE, 1, Menu.NONE, R.string.logging_verbose); - menu.setGroupCheckable(Menu.NONE, true, true); - menu.findItem((VerboseLogUtil.areAllTagsEnabled()) ? 1 : 0).setChecked(true); - popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - if (item.getItemId() == 0) { - VerboseLogUtil.setEnableAllTags(false); - } else { - VerboseLogUtil.setEnableAllTags(true); - } - return true; - } - }); - popup.show(); - } - - private void configurePopupWithTracks(PopupMenu popup, - final OnMenuItemClickListener customActionClickListener, - final int trackType) { - if (player == null) { - return; - } - int trackCount = player.getTrackCount(trackType); - if (trackCount == 0) { - return; - } - popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - return (customActionClickListener != null - && customActionClickListener.onMenuItemClick(item)) - || onTrackItemClick(item, trackType); - } - }); - Menu menu = popup.getMenu(); - // ID_OFFSET ensures we avoid clashing with Menu.NONE (which equals 0). - menu.add(MENU_GROUP_TRACKS, NPExoPlayer.TRACK_DISABLED + ID_OFFSET, Menu.NONE, R.string.off); - for (int i = 0; i < trackCount; i++) { - menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE, - buildTrackName(player.getTrackFormat(trackType, i))); - } - menu.setGroupCheckable(MENU_GROUP_TRACKS, true, true); - menu.findItem(player.getSelectedTrack(trackType) + ID_OFFSET).setChecked(true); - } - - private static String buildTrackName(MediaFormat format) { - if (format.adaptive) { - return "auto"; - } - String trackName; - if (MimeTypes.isVideo(format.mimeType)) { - trackName = joinWithSeparator(joinWithSeparator(buildResolutionString(format), - buildBitrateString(format)), buildTrackIdString(format)); - } else if (MimeTypes.isAudio(format.mimeType)) { - trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(buildLanguageString(format), - buildAudioPropertyString(format)), buildBitrateString(format)), - buildTrackIdString(format)); - } else { - trackName = joinWithSeparator(joinWithSeparator(buildLanguageString(format), - buildBitrateString(format)), buildTrackIdString(format)); - } - return trackName.length() == 0 ? "unknown" : trackName; - } - - private static String buildResolutionString(MediaFormat format) { - return format.width == MediaFormat.NO_VALUE || format.height == MediaFormat.NO_VALUE - ? "" : format.width + "x" + format.height; - } - - private static String buildAudioPropertyString(MediaFormat format) { - return format.channelCount == MediaFormat.NO_VALUE || format.sampleRate == MediaFormat.NO_VALUE - ? "" : format.channelCount + "ch, " + format.sampleRate + "Hz"; - } - - private static String buildLanguageString(MediaFormat format) { - return TextUtils.isEmpty(format.language) || "und".equals(format.language) ? "" - : format.language; - } - - private static String buildBitrateString(MediaFormat format) { - return format.bitrate == MediaFormat.NO_VALUE ? "" - : String.format(Locale.US, "%.2fMbit", format.bitrate / 1000000f); - } - - private static String joinWithSeparator(String first, String second) { - return first.length() == 0 ? second : (second.length() == 0 ? first : first + ", " + second); - } - - private static String buildTrackIdString(MediaFormat format) { - return format.trackId == null ? "" : " (" + format.trackId + ")"; - } - - private boolean onTrackItemClick(MenuItem item, int type) { - if (player == null || item.getGroupId() != MENU_GROUP_TRACKS) { - return false; - } - player.setSelectedTrack(type, item.getItemId() - ID_OFFSET); - return true; - } - - private void toggleControlsVisibility() { - if (mediaController.isShowing()) { - mediaController.hide(); - debugRootView.setVisibility(View.GONE); - playerStateTextView.setVisibility(View.GONE); - debugTextView.setVisibility(View.GONE); - } else { - showControls(); - } - } - - private void showControls() { - mediaController.show(0); - debugRootView.setVisibility(View.VISIBLE); - playerStateTextView.setVisibility(View.VISIBLE); - debugTextView.setVisibility(View.VISIBLE); - } - - // NPExoPlayer.CaptionListener implementation - - @Override - public void onCues(List cues) { - subtitleLayout.setCues(cues); - } - - // NPExoPlayer.MetadataListener implementation - - @Override - public void onId3Metadata(Map metadata) { - for (Map.Entry entry : metadata.entrySet()) { - if (TxxxMetadata.TYPE.equals(entry.getKey())) { - TxxxMetadata txxxMetadata = (TxxxMetadata) entry.getValue(); - Log.i(TAG, String.format("ID3 TimedMetadata %s: description=%s, value=%s", - TxxxMetadata.TYPE, txxxMetadata.description, txxxMetadata.value)); - } else if (PrivMetadata.TYPE.equals(entry.getKey())) { - PrivMetadata privMetadata = (PrivMetadata) entry.getValue(); - Log.i(TAG, String.format("ID3 TimedMetadata %s: owner=%s", - PrivMetadata.TYPE, privMetadata.owner)); - } else if (GeobMetadata.TYPE.equals(entry.getKey())) { - GeobMetadata geobMetadata = (GeobMetadata) entry.getValue(); - Log.i(TAG, String.format("ID3 TimedMetadata %s: mimeType=%s, filename=%s, description=%s", - GeobMetadata.TYPE, geobMetadata.mimeType, geobMetadata.filename, - geobMetadata.description)); - } else { - Log.i(TAG, String.format("ID3 TimedMetadata %s", entry.getKey())); - } - } - } - - // SurfaceHolder.Callback implementation - - @Override - public void surfaceCreated(SurfaceHolder holder) { - if (player != null) { - player.setSurface(holder.getSurface()); - } - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - // Do nothing. - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - if (player != null) { - player.blockingClearSurface(); - } - } - - private void configureSubtitleView() { - CaptionStyleCompat style; - float fontScale; - if (Util.SDK_INT >= 19) { - style = getUserCaptionStyleV19(); - fontScale = getUserCaptionFontScaleV19(); - } else { - style = CaptionStyleCompat.DEFAULT; - fontScale = 1.0f; - } - subtitleLayout.setStyle(style); - subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale); - } - - @TargetApi(19) - private float getUserCaptionFontScaleV19() { - CaptioningManager captioningManager = - (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); - return captioningManager.getFontScale(); - } - - @TargetApi(19) - private CaptionStyleCompat getUserCaptionStyleV19() { - CaptioningManager captioningManager = - (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); - return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); - } - - /** - * Makes a best guess to infer the type from a media {@link Uri} and an optional overriding file - * extension. - * - * @param uri The {@link Uri} of the media. - * @param fileExtension An overriding file extension. - * @return The inferred type. - */ - private static int inferContentType(Uri uri, String fileExtension) { - String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension - : uri.getLastPathSegment(); - return Util.inferContentType(lastPathSegment); - } - - private static final class KeyCompatibleMediaController extends MediaController { - - private MediaController.MediaPlayerControl playerControl; - - public KeyCompatibleMediaController(Context context) { - super(context); - } - - @Override - public void setMediaPlayer(MediaController.MediaPlayerControl playerControl) { - super.setMediaPlayer(playerControl); - this.playerControl = playerControl; - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - int keyCode = event.getKeyCode(); - if (playerControl.canSeekForward() && keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - playerControl.seekTo(playerControl.getCurrentPosition() + 15000); // milliseconds - show(); - } - return true; - } else if (playerControl.canSeekBackward() && keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - playerControl.seekTo(playerControl.getCurrentPosition() - 5000); // milliseconds - show(); - } - return true; - } - return super.dispatchKeyEvent(event); - } - } - -} diff --git a/app/src/main/java/org/schabi/newpipe/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java similarity index 98% rename from app/src/main/java/org/schabi/newpipe/BackgroundPlayer.java rename to app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 47afac5b1..21d3d6e0d 100644 --- a/app/src/main/java/org/schabi/newpipe/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe; +package org.schabi.newpipe.player; import android.app.Notification; import android.app.NotificationManager; @@ -20,6 +20,12 @@ import android.util.Log; import android.widget.RemoteViews; import android.widget.Toast; +import org.schabi.newpipe.ActivityCommunicator; +import org.schabi.newpipe.BuildConfig; +import org.schabi.newpipe.R; +import org.schabi.newpipe.VideoItemDetailActivity; +import org.schabi.newpipe.VideoItemDetailFragment; + import java.io.IOException; /** diff --git a/app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java new file mode 100644 index 000000000..11e650cd6 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Created by Christian Schabesberger on 24.12.15. + * + * Copyright (C) Christian Schabesberger 2015 + * ExoPlayerActivity.java is part of NewPipe. all changes are under GPL3 + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +package org.schabi.newpipe.player; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.player.exoplayer.DashRendererBuilder; +import org.schabi.newpipe.player.exoplayer.EventLogger; +import org.schabi.newpipe.player.exoplayer.ExtractorRendererBuilder; +import org.schabi.newpipe.player.exoplayer.HlsRendererBuilder; +import org.schabi.newpipe.player.exoplayer.NPExoPlayer; +import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; +import org.schabi.newpipe.player.exoplayer.SmoothStreamingRendererBuilder; +import org.schabi.newpipe.player.exoplayer.SmoothStreamingTestMediaDrmCallback; +import org.schabi.newpipe.player.exoplayer.WidevineTestMediaDrmCallback; + +import com.google.android.exoplayer.AspectRatioFrameLayout; +import com.google.android.exoplayer.ExoPlaybackException; +import com.google.android.exoplayer.ExoPlayer; +import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; +import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; +import com.google.android.exoplayer.MediaFormat; +import com.google.android.exoplayer.audio.AudioCapabilities; +import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver; +import com.google.android.exoplayer.drm.UnsupportedDrmException; +import com.google.android.exoplayer.metadata.GeobMetadata; +import com.google.android.exoplayer.metadata.PrivMetadata; +import com.google.android.exoplayer.metadata.TxxxMetadata; +import com.google.android.exoplayer.text.CaptionStyleCompat; +import com.google.android.exoplayer.text.Cue; +import com.google.android.exoplayer.text.SubtitleLayout; +import com.google.android.exoplayer.util.DebugTextViewHelper; +import com.google.android.exoplayer.util.MimeTypes; +import com.google.android.exoplayer.util.Util; +import com.google.android.exoplayer.util.VerboseLogUtil; + +import android.Manifest.permission; +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnKeyListener; +import android.view.View.OnTouchListener; +import android.view.accessibility.CaptioningManager; +import android.widget.Button; +import android.widget.MediaController; +import android.widget.PopupMenu; +import android.widget.PopupMenu.OnMenuItemClickListener; +import android.widget.TextView; +import android.widget.Toast; + +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * An activity that plays media using {@link NPExoPlayer}. + */ +public class ExoPlayerActivity extends Activity { + + // For use within demo app code. + public static final String CONTENT_ID_EXTRA = "content_id"; + public static final String CONTENT_TYPE_EXTRA = "content_type"; + public static final String PROVIDER_EXTRA = "provider"; + + // For use when launching the demo app using adb. + private static final String CONTENT_EXT_EXTRA = "type"; + + private static final String TAG = "PlayerActivity"; + private static final int MENU_GROUP_TRACKS = 1; + private static final int ID_OFFSET = 2; + + private static final CookieManager defaultCookieManager; + static { + defaultCookieManager = new CookieManager(); + defaultCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); + } + + private EventLogger eventLogger; + private MediaController mediaController; + private View debugRootView; + private View shutterView; + private AspectRatioFrameLayout videoFrame; + private SurfaceView surfaceView; + private TextView debugTextView; + private TextView playerStateTextView; + private SubtitleLayout subtitleLayout; + private Button videoButton; + private Button audioButton; + private Button textButton; + private Button retryButton; + + private NPExoPlayer player; + private DebugTextViewHelper debugViewHelper; + private boolean playerNeedsPrepare; + + private long playerPosition; + private boolean enableBackgroundAudio; + + private Uri contentUri; + private int contentType; + private String contentId; + private String provider; + + private AudioCapabilitiesReceiver audioCapabilitiesReceiver; + + + NPExoPlayer.Listener exoPlayerListener = new NPExoPlayer.Listener() { + @Override + public void onStateChanged(boolean playWhenReady, int playbackState) { + if (playbackState == ExoPlayer.STATE_ENDED) { + showControls(); + } + String text = "playWhenReady=" + playWhenReady + ", playbackState="; + switch(playbackState) { + case ExoPlayer.STATE_BUFFERING: + text += "buffering"; + break; + case ExoPlayer.STATE_ENDED: + text += "ended"; + break; + case ExoPlayer.STATE_IDLE: + text += "idle"; + break; + case ExoPlayer.STATE_PREPARING: + text += "preparing"; + break; + case ExoPlayer.STATE_READY: + text += "ready"; + break; + default: + text += "unknown"; + break; + } + playerStateTextView.setText(text); + updateButtonVisibilities(); + } + + @Override + public void onError(Exception e) { + String errorString = null; + if (e instanceof UnsupportedDrmException) { + // Special case DRM failures. + UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e; + errorString = getString(Util.SDK_INT < 18 ? R.string.error_drm_not_supported + : unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME + ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); + } else if (e instanceof ExoPlaybackException + && e.getCause() instanceof DecoderInitializationException) { + // Special case for decoder initialization failures. + DecoderInitializationException decoderInitializationException = + (DecoderInitializationException) e.getCause(); + if (decoderInitializationException.decoderName == null) { + if (decoderInitializationException.getCause() instanceof DecoderQueryException) { + errorString = getString(R.string.error_querying_decoders); + } else if (decoderInitializationException.secureDecoderRequired) { + errorString = getString(R.string.error_no_secure_decoder, + decoderInitializationException.mimeType); + } else { + errorString = getString(R.string.error_no_decoder, + decoderInitializationException.mimeType); + } + } else { + errorString = getString(R.string.error_instantiating_decoder, + decoderInitializationException.decoderName); + } + } + if (errorString != null) { + Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_LONG).show(); + } + playerNeedsPrepare = true; + updateButtonVisibilities(); + showControls(); + } + + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthAspectRatio) { + shutterView.setVisibility(View.GONE); + videoFrame.setAspectRatio( + height == 0 ? 1 : (width * pixelWidthAspectRatio) / height); + } + }; + + SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceCreated(SurfaceHolder holder) { + if (player != null) { + player.setSurface(holder.getSurface()); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // Do nothing. + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + if (player != null) { + player.blockingClearSurface(); + } + } + }; + + NPExoPlayer.CaptionListener captionListener = new NPExoPlayer.CaptionListener() { + @Override + public void onCues(List cues) { + subtitleLayout.setCues(cues); + } + }; + + NPExoPlayer.Id3MetadataListener id3MetadataListener = new NPExoPlayer.Id3MetadataListener() { + @Override + public void onId3Metadata(Map metadata) { + for (Map.Entry entry : metadata.entrySet()) { + if (TxxxMetadata.TYPE.equals(entry.getKey())) { + TxxxMetadata txxxMetadata = (TxxxMetadata) entry.getValue(); + Log.i(TAG, String.format("ID3 TimedMetadata %s: description=%s, value=%s", + TxxxMetadata.TYPE, txxxMetadata.description, txxxMetadata.value)); + } else if (PrivMetadata.TYPE.equals(entry.getKey())) { + PrivMetadata privMetadata = (PrivMetadata) entry.getValue(); + Log.i(TAG, String.format("ID3 TimedMetadata %s: owner=%s", + PrivMetadata.TYPE, privMetadata.owner)); + } else if (GeobMetadata.TYPE.equals(entry.getKey())) { + GeobMetadata geobMetadata = (GeobMetadata) entry.getValue(); + Log.i(TAG, String.format("ID3 TimedMetadata %s: mimeType=%s, filename=%s, description=%s", + GeobMetadata.TYPE, geobMetadata.mimeType, geobMetadata.filename, + geobMetadata.description)); + } else { + Log.i(TAG, String.format("ID3 TimedMetadata %s", entry.getKey())); + } + } + } + }; + + AudioCapabilitiesReceiver.Listener audioCapabilitiesListener = new AudioCapabilitiesReceiver.Listener() { + @Override + public void onAudioCapabilitiesChanged(AudioCapabilities audioCapabilities) { + if (player == null) { + return; + } + boolean backgrounded = player.getBackgrounded(); + boolean playWhenReady = player.getPlayWhenReady(); + releasePlayer(); + preparePlayer(playWhenReady); + player.setBackgrounded(backgrounded); + } + }; + + // Activity lifecycle + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.exo_player_activity); + View root = findViewById(R.id.root); + root.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + toggleControlsVisibility(); + } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + view.performClick(); + } + return true; + } + }); + root.setOnKeyListener(new OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE + || keyCode == KeyEvent.KEYCODE_MENU) { + return false; + } + return mediaController.dispatchKeyEvent(event); + } + }); + + shutterView = findViewById(R.id.shutter); + debugRootView = findViewById(R.id.controls_root); + + videoFrame = (AspectRatioFrameLayout) findViewById(R.id.video_frame); + surfaceView = (SurfaceView) findViewById(R.id.surface_view); + surfaceView.getHolder().addCallback(surfaceHolderCallback); + debugTextView = (TextView) findViewById(R.id.debug_text_view); + + playerStateTextView = (TextView) findViewById(R.id.player_state_view); + subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles); + + mediaController = new KeyCompatibleMediaController(this); + mediaController.setAnchorView(root); + retryButton = (Button) findViewById(R.id.retry_button); + videoButton = (Button) findViewById(R.id.video_controls); + audioButton = (Button) findViewById(R.id.audio_controls); + textButton = (Button) findViewById(R.id.text_controls); + + CookieHandler currentHandler = CookieHandler.getDefault(); + if (currentHandler != defaultCookieManager) { + CookieHandler.setDefault(defaultCookieManager); + } + + audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(this, audioCapabilitiesListener); + audioCapabilitiesReceiver.register(); + + + retryButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + preparePlayer(true); + } + }); + } + + @Override + public void onNewIntent(Intent intent) { + releasePlayer(); + playerPosition = 0; + setIntent(intent); + } + + @Override + public void onResume() { + super.onResume(); + Intent intent = getIntent(); + contentUri = intent.getData(); + contentType = intent.getIntExtra(CONTENT_TYPE_EXTRA, + inferContentType(contentUri, intent.getStringExtra(CONTENT_EXT_EXTRA))); + contentId = intent.getStringExtra(CONTENT_ID_EXTRA); + provider = intent.getStringExtra(PROVIDER_EXTRA); + configureSubtitleView(); + if (player == null) { + if (!maybeRequestPermission()) { + preparePlayer(true); + } + } else { + player.setBackgrounded(false); + } + } + + @Override + public void onPause() { + super.onPause(); + if (!enableBackgroundAudio) { + releasePlayer(); + } else { + player.setBackgrounded(true); + } + shutterView.setVisibility(View.VISIBLE); + } + + @Override + public void onDestroy() { + super.onDestroy(); + audioCapabilitiesReceiver.unregister(); + releasePlayer(); + } + + + // Permission request listener method + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, + int[] grantResults) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + preparePlayer(true); + } else { + Toast.makeText(getApplicationContext(), R.string.storage_permission_denied, + Toast.LENGTH_LONG).show(); + finish(); + } + } + + // Permission management methods + + /** + * Checks whether it is necessary to ask for permission to read storage. If necessary, it also + * requests permission. + * + * @return true if a permission request is made. False if it is not necessary. + */ + @TargetApi(23) + private boolean maybeRequestPermission() { + if (requiresPermission(contentUri)) { + requestPermissions(new String[] {permission.READ_EXTERNAL_STORAGE}, 0); + return true; + } else { + return false; + } + } + + @TargetApi(23) + private boolean requiresPermission(Uri uri) { + return Util.SDK_INT >= 23 + && Util.isLocalFileUri(uri) + && checkSelfPermission(permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED; + } + + // Internal methods + + private RendererBuilder getRendererBuilder() { + String userAgent = Util.getUserAgent(this, "NewPipeExoPlayer"); + switch (contentType) { + case Util.TYPE_SS: + return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(), + new SmoothStreamingTestMediaDrmCallback()); + case Util.TYPE_DASH: + return new DashRendererBuilder(this, userAgent, contentUri.toString(), + new WidevineTestMediaDrmCallback(contentId, provider)); + case Util.TYPE_HLS: + return new HlsRendererBuilder(this, userAgent, contentUri.toString()); + case Util.TYPE_OTHER: + return new ExtractorRendererBuilder(this, userAgent, contentUri); + default: + throw new IllegalStateException("Unsupported type: " + contentType); + } + } + + private void preparePlayer(boolean playWhenReady) { + if (player == null) { + player = new NPExoPlayer(getRendererBuilder()); + player.addListener(exoPlayerListener); + player.setCaptionListener(captionListener); + player.setMetadataListener(id3MetadataListener); + player.seekTo(playerPosition); + playerNeedsPrepare = true; + mediaController.setMediaPlayer(player.getPlayerControl()); + mediaController.setEnabled(true); + eventLogger = new EventLogger(); + eventLogger.startSession(); + player.addListener(eventLogger); + player.setInfoListener(eventLogger); + player.setInternalErrorListener(eventLogger); + debugViewHelper = new DebugTextViewHelper(player, debugTextView); + playerStateTextView.setVisibility(View.GONE); + debugTextView.setVisibility(View.GONE); + debugViewHelper.start(); + } + if (playerNeedsPrepare) { + player.prepare(); + playerNeedsPrepare = false; + updateButtonVisibilities(); + } + player.setSurface(surfaceView.getHolder().getSurface()); + player.setPlayWhenReady(playWhenReady); + } + + private void releasePlayer() { + if (player != null) { + debugViewHelper.stop(); + debugViewHelper = null; + playerPosition = player.getCurrentPosition(); + player.release(); + player = null; + eventLogger.endSession(); + eventLogger = null; + } + } + + // User controls + + private void updateButtonVisibilities() { + retryButton.setVisibility(playerNeedsPrepare ? View.VISIBLE : View.GONE); + videoButton.setVisibility(haveTracks(NPExoPlayer.TYPE_VIDEO) ? View.VISIBLE : View.GONE); + audioButton.setVisibility(haveTracks(NPExoPlayer.TYPE_AUDIO) ? View.VISIBLE : View.GONE); + textButton.setVisibility(haveTracks(NPExoPlayer.TYPE_TEXT) ? View.VISIBLE : View.GONE); + } + + private boolean haveTracks(int type) { + return player != null && player.getTrackCount(type) > 0; + } + + public void showVideoPopup(View v) { + PopupMenu popup = new PopupMenu(this, v); + configurePopupWithTracks(popup, null, NPExoPlayer.TYPE_VIDEO); + popup.show(); + } + + public void showAudioPopup(View v) { + PopupMenu popup = new PopupMenu(this, v); + Menu menu = popup.getMenu(); + menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.enable_background_audio); + final MenuItem backgroundAudioItem = menu.findItem(0); + backgroundAudioItem.setCheckable(true); + backgroundAudioItem.setChecked(enableBackgroundAudio); + OnMenuItemClickListener clickListener = new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if (item == backgroundAudioItem) { + enableBackgroundAudio = !item.isChecked(); + return true; + } + return false; + } + }; + configurePopupWithTracks(popup, clickListener, NPExoPlayer.TYPE_AUDIO); + popup.show(); + } + + public void showTextPopup(View v) { + PopupMenu popup = new PopupMenu(this, v); + configurePopupWithTracks(popup, null, NPExoPlayer.TYPE_TEXT); + popup.show(); + } + + public void showVerboseLogPopup(View v) { + PopupMenu popup = new PopupMenu(this, v); + Menu menu = popup.getMenu(); + menu.add(Menu.NONE, 0, Menu.NONE, R.string.logging_normal); + menu.add(Menu.NONE, 1, Menu.NONE, R.string.logging_verbose); + menu.setGroupCheckable(Menu.NONE, true, true); + menu.findItem((VerboseLogUtil.areAllTagsEnabled()) ? 1 : 0).setChecked(true); + popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if (item.getItemId() == 0) { + VerboseLogUtil.setEnableAllTags(false); + } else { + VerboseLogUtil.setEnableAllTags(true); + } + return true; + } + }); + popup.show(); + } + + private void configurePopupWithTracks(PopupMenu popup, + final OnMenuItemClickListener customActionClickListener, + final int trackType) { + if (player == null) { + return; + } + int trackCount = player.getTrackCount(trackType); + if (trackCount == 0) { + return; + } + popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + return (customActionClickListener != null + && customActionClickListener.onMenuItemClick(item)) + || onTrackItemClick(item, trackType); + } + }); + Menu menu = popup.getMenu(); + // ID_OFFSET ensures we avoid clashing with Menu.NONE (which equals 0). + menu.add(MENU_GROUP_TRACKS, NPExoPlayer.TRACK_DISABLED + ID_OFFSET, Menu.NONE, R.string.off); + for (int i = 0; i < trackCount; i++) { + menu.add(MENU_GROUP_TRACKS, i + ID_OFFSET, Menu.NONE, + buildTrackName(player.getTrackFormat(trackType, i))); + } + menu.setGroupCheckable(MENU_GROUP_TRACKS, true, true); + menu.findItem(player.getSelectedTrack(trackType) + ID_OFFSET).setChecked(true); + } + + private static String buildTrackName(MediaFormat format) { + if (format.adaptive) { + return "auto"; + } + String trackName; + if (MimeTypes.isVideo(format.mimeType)) { + trackName = joinWithSeparator(joinWithSeparator(buildResolutionString(format), + buildBitrateString(format)), buildTrackIdString(format)); + } else if (MimeTypes.isAudio(format.mimeType)) { + trackName = joinWithSeparator(joinWithSeparator(joinWithSeparator(buildLanguageString(format), + buildAudioPropertyString(format)), buildBitrateString(format)), + buildTrackIdString(format)); + } else { + trackName = joinWithSeparator(joinWithSeparator(buildLanguageString(format), + buildBitrateString(format)), buildTrackIdString(format)); + } + return trackName.length() == 0 ? "unknown" : trackName; + } + + private static String buildResolutionString(MediaFormat format) { + return format.width == MediaFormat.NO_VALUE || format.height == MediaFormat.NO_VALUE + ? "" : format.width + "x" + format.height; + } + + private static String buildAudioPropertyString(MediaFormat format) { + return format.channelCount == MediaFormat.NO_VALUE || format.sampleRate == MediaFormat.NO_VALUE + ? "" : format.channelCount + "ch, " + format.sampleRate + "Hz"; + } + + private static String buildLanguageString(MediaFormat format) { + return TextUtils.isEmpty(format.language) || "und".equals(format.language) ? "" + : format.language; + } + + private static String buildBitrateString(MediaFormat format) { + return format.bitrate == MediaFormat.NO_VALUE ? "" + : String.format(Locale.US, "%.2fMbit", format.bitrate / 1000000f); + } + + private static String joinWithSeparator(String first, String second) { + return first.length() == 0 ? second : (second.length() == 0 ? first : first + ", " + second); + } + + private static String buildTrackIdString(MediaFormat format) { + return format.trackId == null ? "" : " (" + format.trackId + ")"; + } + + private boolean onTrackItemClick(MenuItem item, int type) { + if (player == null || item.getGroupId() != MENU_GROUP_TRACKS) { + return false; + } + player.setSelectedTrack(type, item.getItemId() - ID_OFFSET); + return true; + } + + private void toggleControlsVisibility() { + if (mediaController.isShowing()) { + mediaController.hide(); + debugRootView.setVisibility(View.GONE); + playerStateTextView.setVisibility(View.GONE); + debugTextView.setVisibility(View.GONE); + } else { + showControls(); + } + } + + private void showControls() { + mediaController.show(0); + debugRootView.setVisibility(View.VISIBLE); + playerStateTextView.setVisibility(View.VISIBLE); + debugTextView.setVisibility(View.VISIBLE); + } + + private void configureSubtitleView() { + CaptionStyleCompat style; + float fontScale; + if (Util.SDK_INT >= 19) { + style = getUserCaptionStyleV19(); + fontScale = getUserCaptionFontScaleV19(); + } else { + style = CaptionStyleCompat.DEFAULT; + fontScale = 1.0f; + } + subtitleLayout.setStyle(style); + subtitleLayout.setFractionalTextSize(SubtitleLayout.DEFAULT_TEXT_SIZE_FRACTION * fontScale); + } + + @TargetApi(19) + private float getUserCaptionFontScaleV19() { + CaptioningManager captioningManager = + (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); + return captioningManager.getFontScale(); + } + + @TargetApi(19) + private CaptionStyleCompat getUserCaptionStyleV19() { + CaptioningManager captioningManager = + (CaptioningManager) getSystemService(Context.CAPTIONING_SERVICE); + return CaptionStyleCompat.createFromCaptionStyle(captioningManager.getUserStyle()); + } + + /** + * Makes a best guess to infer the type from a media {@link Uri} and an optional overriding file + * extension. + * + * @param uri The {@link Uri} of the media. + * @param fileExtension An overriding file extension. + * @return The inferred type. + */ + private static int inferContentType(Uri uri, String fileExtension) { + String lastPathSegment = !TextUtils.isEmpty(fileExtension) ? "." + fileExtension + : uri.getLastPathSegment(); + return Util.inferContentType(lastPathSegment); + } + + private static final class KeyCompatibleMediaController extends MediaController { + + private MediaController.MediaPlayerControl playerControl; + + public KeyCompatibleMediaController(Context context) { + super(context); + } + + @Override + public void setMediaPlayer(MediaController.MediaPlayerControl playerControl) { + super.setMediaPlayer(playerControl); + this.playerControl = playerControl; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (playerControl.canSeekForward() && keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + playerControl.seekTo(playerControl.getCurrentPosition() + 15000); // milliseconds + show(); + } + return true; + } else if (playerControl.canSeekBackward() && keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + playerControl.seekTo(playerControl.getCurrentPosition() - 5000); // milliseconds + show(); + } + return true; + } + return super.dispatchKeyEvent(event); + } + } + +} diff --git a/app/src/main/java/org/schabi/newpipe/PlayVideoActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayVideoActivity.java similarity index 99% rename from app/src/main/java/org/schabi/newpipe/PlayVideoActivity.java rename to app/src/main/java/org/schabi/newpipe/player/PlayVideoActivity.java index 823fb762c..91ba36f19 100644 --- a/app/src/main/java/org/schabi/newpipe/PlayVideoActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayVideoActivity.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe; +package org.schabi.newpipe.player; import android.content.Context; import android.content.Intent; @@ -27,6 +27,9 @@ import android.widget.MediaController; import android.widget.ProgressBar; import android.widget.VideoView; +import org.schabi.newpipe.App; +import org.schabi.newpipe.R; + /** * Copyright (C) Christian Schabesberger 2015 * PlayVideoActivity.java is part of NewPipe. @@ -191,7 +194,6 @@ public class PlayVideoActivity extends AppCompatActivity { @Override public void onResume() { super.onResume(); - App.checkStartTor(this); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/DashRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/DashRendererBuilder.java similarity index 98% rename from app/src/main/java/org/schabi/newpipe/exoplayer/DashRendererBuilder.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/DashRendererBuilder.java index 857af3a78..f12dc8975 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/DashRendererBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/DashRendererBuilder.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; -import org.schabi.newpipe.exoplayer.NPExoPlayer.RendererBuilder; +import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; import com.google.android.exoplayer.DefaultLoadControl; import com.google.android.exoplayer.LoadControl; diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/EventLogger.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/EventLogger.java similarity index 99% rename from app/src/main/java/org/schabi/newpipe/exoplayer/EventLogger.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/EventLogger.java index f2de9033e..62553ab3b 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/EventLogger.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/EventLogger.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/ExtractorRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/ExtractorRendererBuilder.java similarity index 96% rename from app/src/main/java/org/schabi/newpipe/exoplayer/ExtractorRendererBuilder.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/ExtractorRendererBuilder.java index 0ba716bcb..a74c33bf8 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/ExtractorRendererBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/ExtractorRendererBuilder.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; -import org.schabi.newpipe.exoplayer.NPExoPlayer.RendererBuilder; +import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecSelector; diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/HlsRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/HlsRendererBuilder.java similarity index 98% rename from app/src/main/java/org/schabi/newpipe/exoplayer/HlsRendererBuilder.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/HlsRendererBuilder.java index 8c0fae97d..8e6c2d9f5 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/HlsRendererBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/HlsRendererBuilder.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; -import org.schabi.newpipe.exoplayer.NPExoPlayer.RendererBuilder; +import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; import com.google.android.exoplayer.DefaultLoadControl; import com.google.android.exoplayer.LoadControl; diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/NPExoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/NPExoPlayer.java similarity index 99% rename from app/src/main/java/org/schabi/newpipe/exoplayer/NPExoPlayer.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/NPExoPlayer.java index 377e2e7b8..63a6a9261 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/NPExoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/NPExoPlayer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.DummyTrackRenderer; diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/SmoothStreamingRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingRendererBuilder.java similarity index 98% rename from app/src/main/java/org/schabi/newpipe/exoplayer/SmoothStreamingRendererBuilder.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingRendererBuilder.java index 524e2d5c5..55b59c276 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/SmoothStreamingRendererBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingRendererBuilder.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; -import org.schabi.newpipe.exoplayer.NPExoPlayer.RendererBuilder; +import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; import com.google.android.exoplayer.DefaultLoadControl; import com.google.android.exoplayer.LoadControl; diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/SmoothStreamingTestMediaDrmCallback.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingTestMediaDrmCallback.java similarity index 98% rename from app/src/main/java/org/schabi/newpipe/exoplayer/SmoothStreamingTestMediaDrmCallback.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingTestMediaDrmCallback.java index 3fe1db139..ec9bf41a7 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/SmoothStreamingTestMediaDrmCallback.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingTestMediaDrmCallback.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; import com.google.android.exoplayer.drm.MediaDrmCallback; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; diff --git a/app/src/main/java/org/schabi/newpipe/exoplayer/WidevineTestMediaDrmCallback.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/WidevineTestMediaDrmCallback.java similarity index 97% rename from app/src/main/java/org/schabi/newpipe/exoplayer/WidevineTestMediaDrmCallback.java rename to app/src/main/java/org/schabi/newpipe/player/exoplayer/WidevineTestMediaDrmCallback.java index e23f3fc30..647e6599b 100644 --- a/app/src/main/java/org/schabi/newpipe/exoplayer/WidevineTestMediaDrmCallback.java +++ b/app/src/main/java/org/schabi/newpipe/player/exoplayer/WidevineTestMediaDrmCallback.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.schabi.newpipe.exoplayer; +package org.schabi.newpipe.player.exoplayer; import com.google.android.exoplayer.drm.MediaDrmCallback; import com.google.android.exoplayer.util.Util; diff --git a/app/src/main/res/layout/activity_play_video.xml b/app/src/main/res/layout/activity_play_video.xml index 117006a1a..bdedb2c2c 100644 --- a/app/src/main/res/layout/activity_play_video.xml +++ b/app/src/main/res/layout/activity_play_video.xml @@ -2,7 +2,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="org.schabi.newpipe.PlayVideoActivity" + tools:context=".player.PlayVideoActivity" android:gravity="center">