diff --git a/app/build.gradle b/app/build.gradle index f2f467326..a1da6a7f0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,10 +42,10 @@ dependencies { compile 'de.hdodenhof:circleimageview:2.0.0' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5' compile 'com.github.nirhart:parallaxscroll:1.0' - compile 'com.google.android.exoplayer:exoplayer:r1.5.5' compile 'com.google.code.gson:gson:2.4' compile 'com.nononsenseapps:filepicker:3.0.0' compile 'ch.acra:acra:4.9.0' + compile 'com.devbrackets.android:exomedia:3.1.1' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.json:json:20160810' diff --git a/app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java index c38e63545..7fe97d0dc 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ExoPlayerActivity.java @@ -16,549 +16,240 @@ /** * Extended 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 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.OnKeyListener; -import android.view.View.OnTouchListener; -import android.view.accessibility.CaptioningManager; -import android.widget.MediaController; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; -import android.widget.Toast; +import android.view.WindowManager; +import android.widget.ImageButton; +import android.widget.SeekBar; -import java.net.CookieHandler; -import java.net.CookieManager; -import java.net.CookiePolicy; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import com.devbrackets.android.exomedia.listener.OnCompletionListener; +import com.devbrackets.android.exomedia.listener.OnPreparedListener; +import com.devbrackets.android.exomedia.listener.VideoControlsVisibilityListener; +import com.devbrackets.android.exomedia.ui.widget.EMVideoView; +import com.devbrackets.android.exomedia.ui.widget.VideoControlsMobile; -/** - * An activity that plays media using {@link NPExoPlayer}. - */ -public class ExoPlayerActivity extends Activity { +import org.schabi.newpipe.R; - // 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"; +public class ExoPlayerActivity extends Activity implements OnPreparedListener, OnCompletionListener { + private static final String TAG = "ExoPlayerActivity"; + private EMVideoView videoView; + private CustomVideoControls videoControls; - // 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 shutterView; - private AspectRatioFrameLayout videoFrame; - private SurfaceView surfaceView; - private SubtitleLayout subtitleLayout; - - private NPExoPlayer player; - private boolean playerNeedsPrepare; - - private long playerPosition; - private boolean enableBackgroundAudio = true; - - 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; - } - //todo: put text in some log - } - - @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; - 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 + public static final String VIDEO_TITLE = "video_title"; + public static final String CHANNEL_NAME = "channel_name"; + private String videoTitle = ""; + private volatile String channelName = ""; + private int lastPosition; + private boolean isFinished; @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); - - videoFrame = (AspectRatioFrameLayout) findViewById(R.id.video_frame); - surfaceView = (SurfaceView) findViewById(R.id.surface_view); - surfaceView.getHolder().addCallback(surfaceHolderCallback); - subtitleLayout = (SubtitleLayout) findViewById(R.id.subtitles); - - //todo: replace that creapy mediaController - mediaController = new KeyCompatibleMediaController(this); - mediaController.setAnchorView(root); - - //todo: check what cookie handler does, and if we even need it - CookieHandler currentHandler = CookieHandler.getDefault(); - if (currentHandler != defaultCookieManager) { - CookieHandler.setDefault(defaultCookieManager); - } - - audioCapabilitiesReceiver = new AudioCapabilitiesReceiver(this, audioCapabilitiesListener); - audioCapabilitiesReceiver.register(); + setContentView(R.layout.activity_exo_player); + videoView = (EMVideoView) findViewById(R.id.emVideoView); } @Override - public void onNewIntent(Intent intent) { - releasePlayer(); - playerPosition = 0; - setIntent(intent); - } - - @Override - public void onResume() { - super.onResume(); + protected void onStart() { + super.onStart(); 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); + videoTitle = intent.getStringExtra(VIDEO_TITLE); + channelName = intent.getStringExtra(CHANNEL_NAME); + videoView.setOnPreparedListener(this); + videoView.setOnCompletionListener(this); + videoView.setVideoURI(intent.getData()); + + videoControls = new CustomVideoControls(this); + videoControls.setTitle(videoTitle); + videoControls.setSubTitle(channelName); + + //We don't need these button until the playlist or queue is implemented + videoControls.setNextButtonRemoved(true); + videoControls.setPreviousButtonRemoved(true); + + videoControls.setVisibilityListener(new VideoControlsVisibilityListener() { + @Override + public void onControlsShown() { + Log.d(TAG, "------------ onControlsShown() called"); + showSystemUi(); } - } else { - player.setBackgrounded(false); - } + + @Override + public void onControlsHidden() { + Log.d(TAG, "------------ onControlsHidden() called"); + hideSystemUi(); + } + }); + videoView.setControls(videoControls); } @Override - public void onPause() { + public void onPrepared() { + Log.d(TAG, "onPrepared() called"); + videoView.start(); + } + + @Override + public void onCompletion() { + Log.d(TAG, "onCompletion() called"); +// videoView.getVideoControls().setButtonListener(); + //videoView.restart(); + videoControls.setRewindButtonRemoved(true); + videoControls.setFastForwardButtonRemoved(true); + isFinished = true; + videoControls.getSeekBar().setEnabled(false); + } + + @Override + protected void onPause() { super.onPause(); - if (!enableBackgroundAudio) { - releasePlayer(); - } else { - player.setBackgrounded(true); - } - shutterView.setVisibility(View.VISIBLE); + videoView.stopPlayback(); + lastPosition = videoView.getCurrentPosition(); } @Override - public void onDestroy() { + protected void onResume() { + super.onResume(); + if (lastPosition > 0) videoView.seekTo(lastPosition); + } + + @Override + protected void onDestroy() { super.onDestroy(); - audioCapabilitiesReceiver.unregister(); - releasePlayer(); + videoView.stopPlayback(); } + private void showSystemUi() { + Log.d(TAG, "showSystemUi() called"); + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + getWindow().getDecorView().setSystemUiVisibility(0); + } - // 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(); + private void hideSystemUi() { + Log.d(TAG, "hideSystemUi() called"); + if (android.os.Build.VERSION.SDK_INT >= 17) { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION); } + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } - // Permission management methods + private class CustomVideoControls extends VideoControlsMobile { + protected static final int FAST_FORWARD_REWIND_AMOUNT = 8000; - /** - * 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; - } - } + protected ImageButton fastForwardButton; + protected ImageButton rewindButton; - @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: - // default - //return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString()); - case Util.TYPE_DASH: - // if a dash manifest is available - //return new DashRendererBuilder(this, userAgent, contentUri.toString()); - case Util.TYPE_HLS: - // for livestreams - return new HlsRendererBuilder(this, userAgent, contentUri.toString()); - case Util.TYPE_OTHER: - // video only streaming - 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); - } - if (playerNeedsPrepare) { - player.prepare(); - playerNeedsPrepare = false; - } - player.setSurface(surfaceView.getHolder().getSurface()); - player.setPlayWhenReady(playWhenReady); - } - - private void releasePlayer() { - if (player != null) { - playerPosition = player.getCurrentPosition(); - player.release(); - player = null; - eventLogger.endSession(); - eventLogger = null; - } - } - - private void toggleControlsVisibility() { - if (mediaController.isShowing()) { - mediaController.hide(); - } else { - showControls(); - } - } - - private void showControls() { - mediaController.show(0); - } - - 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) { + public CustomVideoControls(Context context) { super(context); } @Override - public void setMediaPlayer(MediaController.MediaPlayerControl playerControl) { - super.setMediaPlayer(playerControl); - this.playerControl = playerControl; + protected int getLayoutResource() { + return R.layout.exomedia_custom_controls; } @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(); + protected void retrieveViews() { + super.retrieveViews(); + rewindButton = (ImageButton) findViewById(R.id.exomedia_controls_frewind_btn); + fastForwardButton = (ImageButton) findViewById(R.id.exomedia_controls_fforward_btn); + } + + @Override + protected void registerListeners() { + super.registerListeners(); + rewindButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + onRewindClicked(); } - return true; - } else if (playerControl.canSeekBackward() && keyCode == KeyEvent.KEYCODE_MEDIA_REWIND) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - playerControl.seekTo(playerControl.getCurrentPosition() - 5000); // milliseconds - show(); + }); + fastForwardButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + onFastForwardClicked(); } - return true; + }); + } + + public boolean onFastForwardClicked() { + if (videoView == null) return false; + + int newPosition = videoView.getCurrentPosition() + FAST_FORWARD_REWIND_AMOUNT; + if (newPosition > seekBar.getMax()) newPosition = seekBar.getMax(); + + performSeek(newPosition); + return true; + } + + public boolean onRewindClicked() { + if (videoView == null) return false; + + int newPosition = videoView.getCurrentPosition() - FAST_FORWARD_REWIND_AMOUNT; + if (newPosition < 0) newPosition = 0; + + performSeek(newPosition); + return true; + } + + @Override + public void setFastForwardButtonRemoved(boolean removed) { + fastForwardButton.setVisibility(removed ? View.GONE : View.VISIBLE); + } + + @Override + public void setRewindButtonRemoved(boolean removed) { + rewindButton.setVisibility(removed ? View.GONE : View.VISIBLE); + } + + @Override + protected void onPlayPauseClick() { + super.onPlayPauseClick(); + if (videoView == null) return; + Log.d(TAG, "onPlayPauseClick() called" + videoView.getDuration()+" position= "+ videoView.getCurrentPosition()); + if (isFinished) { + videoView.restart(); + setRewindButtonRemoved(false); + setFastForwardButtonRemoved(false); + isFinished = false; + seekBar.setEnabled(true); } - return super.dispatchKeyEvent(event); + } + + private void performSeek(int newPosition) { + internalListener.onSeekEnded(newPosition); + } + + public SeekBar getSeekBar() { + return seekBar; } } - } diff --git a/app/src/main/java/org/schabi/newpipe/player/exoplayer/DashRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/DashRendererBuilder.java deleted file mode 100644 index f12dc8975..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/exoplayer/DashRendererBuilder.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * 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. - */ -package org.schabi.newpipe.player.exoplayer; - -import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; - -import com.google.android.exoplayer.DefaultLoadControl; -import com.google.android.exoplayer.LoadControl; -import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; -import com.google.android.exoplayer.MediaCodecSelector; -import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; -import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer.audio.AudioCapabilities; -import com.google.android.exoplayer.chunk.ChunkSampleSource; -import com.google.android.exoplayer.chunk.ChunkSource; -import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; -import com.google.android.exoplayer.dash.DashChunkSource; -import com.google.android.exoplayer.dash.DefaultDashTrackSelector; -import com.google.android.exoplayer.dash.mpd.AdaptationSet; -import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription; -import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser; -import com.google.android.exoplayer.dash.mpd.Period; -import com.google.android.exoplayer.dash.mpd.UtcTimingElement; -import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver; -import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver.UtcTimingCallback; -import com.google.android.exoplayer.drm.MediaDrmCallback; -import com.google.android.exoplayer.drm.StreamingDrmSessionManager; -import com.google.android.exoplayer.drm.UnsupportedDrmException; -import com.google.android.exoplayer.text.TextTrackRenderer; -import com.google.android.exoplayer.upstream.DataSource; -import com.google.android.exoplayer.upstream.DefaultAllocator; -import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer.upstream.DefaultUriDataSource; -import com.google.android.exoplayer.upstream.UriDataSource; -import com.google.android.exoplayer.util.ManifestFetcher; -import com.google.android.exoplayer.util.Util; - -import android.content.Context; -import android.media.AudioManager; -import android.media.MediaCodec; -import android.os.Handler; -import android.util.Log; - -import java.io.IOException; - -/** - * A {@link RendererBuilder} for DASH. - */ -public class DashRendererBuilder implements RendererBuilder { - - private static final String TAG = "DashRendererBuilder"; - - private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; - private static final int VIDEO_BUFFER_SEGMENTS = 200; - private static final int AUDIO_BUFFER_SEGMENTS = 54; - private static final int TEXT_BUFFER_SEGMENTS = 2; - private static final int LIVE_EDGE_LATENCY_MS = 30000; - - private static final int SECURITY_LEVEL_UNKNOWN = -1; - private static final int SECURITY_LEVEL_1 = 1; - private static final int SECURITY_LEVEL_3 = 3; - - private final Context context; - private final String userAgent; - private final String url; - private final MediaDrmCallback drmCallback; - - private AsyncRendererBuilder currentAsyncBuilder; - - public DashRendererBuilder(Context context, String userAgent, String url, - MediaDrmCallback drmCallback) { - this.context = context; - this.userAgent = userAgent; - this.url = url; - this.drmCallback = drmCallback; - } - - @Override - public void buildRenderers(NPExoPlayer player) { - currentAsyncBuilder = new AsyncRendererBuilder(context, userAgent, url, drmCallback, player); - currentAsyncBuilder.init(); - } - - @Override - public void cancel() { - if (currentAsyncBuilder != null) { - currentAsyncBuilder.cancel(); - currentAsyncBuilder = null; - } - } - - private static final class AsyncRendererBuilder - implements ManifestFetcher.ManifestCallback, UtcTimingCallback { - - private final Context context; - private final String userAgent; - private final MediaDrmCallback drmCallback; - private final NPExoPlayer player; - private final ManifestFetcher manifestFetcher; - private final UriDataSource manifestDataSource; - - private boolean canceled; - private MediaPresentationDescription manifest; - private long elapsedRealtimeOffset; - - public AsyncRendererBuilder(Context context, String userAgent, String url, - MediaDrmCallback drmCallback, NPExoPlayer player) { - this.context = context; - this.userAgent = userAgent; - this.drmCallback = drmCallback; - this.player = player; - MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser(); - manifestDataSource = new DefaultUriDataSource(context, userAgent); - manifestFetcher = new ManifestFetcher<>(url, manifestDataSource, parser); - } - - public void init() { - manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this); - } - - public void cancel() { - canceled = true; - } - - @Override - public void onSingleManifest(MediaPresentationDescription manifest) { - if (canceled) { - return; - } - - this.manifest = manifest; - if (manifest.dynamic && manifest.utcTiming != null) { - UtcTimingElementResolver.resolveTimingElement(manifestDataSource, manifest.utcTiming, - manifestFetcher.getManifestLoadCompleteTimestamp(), this); - } else { - buildRenderers(); - } - } - - @Override - public void onSingleManifestError(IOException e) { - if (canceled) { - return; - } - - player.onRenderersError(e); - } - - @Override - public void onTimestampResolved(UtcTimingElement utcTiming, long elapsedRealtimeOffset) { - if (canceled) { - return; - } - - this.elapsedRealtimeOffset = elapsedRealtimeOffset; - buildRenderers(); - } - - @Override - public void onTimestampError(UtcTimingElement utcTiming, IOException e) { - if (canceled) { - return; - } - - Log.e(TAG, "Failed to resolve UtcTiming element [" + utcTiming + "]", e); - // Be optimistic and continue in the hope that the device clock is correct. - buildRenderers(); - } - - private void buildRenderers() { - Period period = manifest.getPeriod(0); - Handler mainHandler = player.getMainHandler(); - LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); - DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player); - - boolean hasContentProtection = false; - for (int i = 0; i < period.adaptationSets.size(); i++) { - AdaptationSet adaptationSet = period.adaptationSets.get(i); - if (adaptationSet.type != AdaptationSet.TYPE_UNKNOWN) { - hasContentProtection |= adaptationSet.hasContentProtection(); - } - } - - // Check drm support if necessary. - boolean filterHdContent = false; - StreamingDrmSessionManager drmSessionManager = null; - if (hasContentProtection) { - if (Util.SDK_INT < 18) { - player.onRenderersError( - new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); - return; - } - try { - drmSessionManager = StreamingDrmSessionManager.newWidevineInstance( - player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); - filterHdContent = getWidevineSecurityLevel(drmSessionManager) != SECURITY_LEVEL_1; - } catch (UnsupportedDrmException e) { - player.onRenderersError(e); - return; - } - } - - // Build the video renderer. - DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, - DefaultDashTrackSelector.newVideoInstance(context, true, filterHdContent), - videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS, - elapsedRealtimeOffset, mainHandler, player, NPExoPlayer.TYPE_VIDEO); - ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, - VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, - NPExoPlayer.TYPE_VIDEO); - TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource, - MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, - drmSessionManager, true, mainHandler, player, 50); - - // Build the audio renderer. - DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - ChunkSource audioChunkSource = new DashChunkSource(manifestFetcher, - DefaultDashTrackSelector.newAudioInstance(), audioDataSource, null, LIVE_EDGE_LATENCY_MS, - elapsedRealtimeOffset, mainHandler, player, NPExoPlayer.TYPE_AUDIO); - ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, - AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, - NPExoPlayer.TYPE_AUDIO); - TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, - MediaCodecSelector.DEFAULT, drmSessionManager, true, mainHandler, player, - AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); - - // Build the text renderer. - DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - ChunkSource textChunkSource = new DashChunkSource(manifestFetcher, - DefaultDashTrackSelector.newTextInstance(), textDataSource, null, LIVE_EDGE_LATENCY_MS, - elapsedRealtimeOffset, mainHandler, player, NPExoPlayer.TYPE_TEXT); - ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, - TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, - NPExoPlayer.TYPE_TEXT); - TrackRenderer textRenderer = new TextTrackRenderer(textSampleSource, player, - mainHandler.getLooper()); - - // Invoke the callback. - TrackRenderer[] renderers = new TrackRenderer[NPExoPlayer.RENDERER_COUNT]; - renderers[NPExoPlayer.TYPE_VIDEO] = videoRenderer; - renderers[NPExoPlayer.TYPE_AUDIO] = audioRenderer; - renderers[NPExoPlayer.TYPE_TEXT] = textRenderer; - player.onRenderers(renderers, bandwidthMeter); - } - - private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) { - String securityLevelProperty = sessionManager.getPropertyString("securityLevel"); - return securityLevelProperty.equals("L1") ? SECURITY_LEVEL_1 : securityLevelProperty - .equals("L3") ? SECURITY_LEVEL_3 : SECURITY_LEVEL_UNKNOWN; - } - - } - -} diff --git a/app/src/main/java/org/schabi/newpipe/player/exoplayer/EventLogger.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/EventLogger.java deleted file mode 100644 index 62553ab3b..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/exoplayer/EventLogger.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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. - */ -package org.schabi.newpipe.player.exoplayer; - -import com.google.android.exoplayer.ExoPlayer; -import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; -import com.google.android.exoplayer.TimeRange; -import com.google.android.exoplayer.audio.AudioTrack; -import com.google.android.exoplayer.chunk.Format; -import com.google.android.exoplayer.util.VerboseLogUtil; - -import android.media.MediaCodec.CryptoException; -import android.os.SystemClock; -import android.util.Log; - -import java.io.IOException; -import java.text.NumberFormat; -import java.util.Locale; - -/** - * Logs player events using {@link Log}. - */ -public class EventLogger implements NPExoPlayer.Listener, NPExoPlayer.InfoListener, - NPExoPlayer.InternalErrorListener { - - private static final String TAG = "EventLogger"; - private static final NumberFormat TIME_FORMAT; - static { - TIME_FORMAT = NumberFormat.getInstance(Locale.US); - TIME_FORMAT.setMinimumFractionDigits(2); - TIME_FORMAT.setMaximumFractionDigits(2); - } - - private long sessionStartTimeMs; - private long[] loadStartTimeMs; - private long[] availableRangeValuesUs; - - public EventLogger() { - loadStartTimeMs = new long[NPExoPlayer.RENDERER_COUNT]; - } - - public void startSession() { - sessionStartTimeMs = SystemClock.elapsedRealtime(); - Log.d(TAG, "start [0]"); - } - - public void endSession() { - Log.d(TAG, "end [" + getSessionTimeString() + "]"); - } - - // NPExoPlayer.Listener - - @Override - public void onStateChanged(boolean playWhenReady, int state) { - Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", " - + getStateString(state) + "]"); - } - - @Override - public void onError(Exception e) { - Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e); - } - - @Override - public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, - float pixelWidthHeightRatio) { - Log.d(TAG, "videoSizeChanged [" + width + ", " + height + ", " + unappliedRotationDegrees - + ", " + pixelWidthHeightRatio + "]"); - } - - // NPExoPlayer.InfoListener - - @Override - public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) { - Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + ", " - + getTimeString(elapsedMs) + ", " + bitrateEstimate + "]"); - } - - @Override - public void onDroppedFrames(int count, long elapsed) { - Log.d(TAG, "droppedFrames [" + getSessionTimeString() + ", " + count + "]"); - } - - @Override - public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, - long mediaStartTimeMs, long mediaEndTimeMs) { - loadStartTimeMs[sourceId] = SystemClock.elapsedRealtime(); - if (VerboseLogUtil.isTagEnabled(TAG)) { - Log.v(TAG, "loadStart [" + getSessionTimeString() + ", " + sourceId + ", " + type - + ", " + mediaStartTimeMs + ", " + mediaEndTimeMs + "]"); - } - } - - @Override - public void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, - long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs) { - if (VerboseLogUtil.isTagEnabled(TAG)) { - long downloadTime = SystemClock.elapsedRealtime() - loadStartTimeMs[sourceId]; - Log.v(TAG, "loadEnd [" + getSessionTimeString() + ", " + sourceId + ", " + downloadTime - + "]"); - } - } - - @Override - public void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs) { - Log.d(TAG, "videoFormat [" + getSessionTimeString() + ", " + format.id + ", " - + Integer.toString(trigger) + "]"); - } - - @Override - public void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs) { - Log.d(TAG, "audioFormat [" + getSessionTimeString() + ", " + format.id + ", " - + Integer.toString(trigger) + "]"); - } - - // NPExoPlayer.InternalErrorListener - - @Override - public void onLoadError(int sourceId, IOException e) { - printInternalError("loadError", e); - } - - @Override - public void onRendererInitializationError(Exception e) { - printInternalError("rendererInitError", e); - } - - @Override - public void onDrmSessionManagerError(Exception e) { - printInternalError("drmSessionManagerError", e); - } - - @Override - public void onDecoderInitializationError(DecoderInitializationException e) { - printInternalError("decoderInitializationError", e); - } - - @Override - public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { - printInternalError("audioTrackInitializationError", e); - } - - @Override - public void onAudioTrackWriteError(AudioTrack.WriteException e) { - printInternalError("audioTrackWriteError", e); - } - - @Override - public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", " - + elapsedSinceLastFeedMs + "]", null); - } - - @Override - public void onCryptoError(CryptoException e) { - printInternalError("cryptoError", e); - } - - @Override - public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, - long initializationDurationMs) { - Log.d(TAG, "decoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]"); - } - - @Override - public void onAvailableRangeChanged(int sourceId, TimeRange availableRange) { - availableRangeValuesUs = availableRange.getCurrentBoundsUs(availableRangeValuesUs); - Log.d(TAG, "availableRange [" + availableRange.isStatic() + ", " + availableRangeValuesUs[0] - + ", " + availableRangeValuesUs[1] + "]"); - } - - private void printInternalError(String type, Exception e) { - Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e); - } - - private String getStateString(int state) { - switch (state) { - case ExoPlayer.STATE_BUFFERING: - return "B"; - case ExoPlayer.STATE_ENDED: - return "E"; - case ExoPlayer.STATE_IDLE: - return "I"; - case ExoPlayer.STATE_PREPARING: - return "P"; - case ExoPlayer.STATE_READY: - return "R"; - default: - return "?"; - } - } - - private String getSessionTimeString() { - return getTimeString(SystemClock.elapsedRealtime() - sessionStartTimeMs); - } - - private String getTimeString(long timeMs) { - return TIME_FORMAT.format((timeMs) / 1000f); - } - -} diff --git a/app/src/main/java/org/schabi/newpipe/player/exoplayer/ExtractorRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/ExtractorRendererBuilder.java deleted file mode 100644 index a74c33bf8..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/exoplayer/ExtractorRendererBuilder.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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. - */ -package org.schabi.newpipe.player.exoplayer; - -import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; - -import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; -import com.google.android.exoplayer.MediaCodecSelector; -import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; -import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer.audio.AudioCapabilities; -import com.google.android.exoplayer.extractor.Extractor; -import com.google.android.exoplayer.extractor.ExtractorSampleSource; -import com.google.android.exoplayer.text.TextTrackRenderer; -import com.google.android.exoplayer.upstream.Allocator; -import com.google.android.exoplayer.upstream.DataSource; -import com.google.android.exoplayer.upstream.DefaultAllocator; -import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer.upstream.DefaultUriDataSource; - -import android.content.Context; -import android.media.AudioManager; -import android.media.MediaCodec; -import android.net.Uri; - -/** - * A {@link RendererBuilder} for streams that can be read using an {@link Extractor}. - */ -public class ExtractorRendererBuilder implements RendererBuilder { - - private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; - private static final int BUFFER_SEGMENT_COUNT = 256; - - private final Context context; - private final String userAgent; - private final Uri uri; - - public ExtractorRendererBuilder(Context context, String userAgent, Uri uri) { - this.context = context; - this.userAgent = userAgent; - this.uri = uri; - } - - @Override - public void buildRenderers(NPExoPlayer player) { - Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE); - - // Build the video and audio renderers. - DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(player.getMainHandler(), - null); - DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator, - BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE); - MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, - sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, - player.getMainHandler(), player, 50); - MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, - MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player, - AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); - TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player, - player.getMainHandler().getLooper()); - - // Invoke the callback. - TrackRenderer[] renderers = new TrackRenderer[NPExoPlayer.RENDERER_COUNT]; - renderers[NPExoPlayer.TYPE_VIDEO] = videoRenderer; - renderers[NPExoPlayer.TYPE_AUDIO] = audioRenderer; - renderers[NPExoPlayer.TYPE_TEXT] = textRenderer; - player.onRenderers(renderers, bandwidthMeter); - } - - @Override - public void cancel() { - // Do nothing. - } - -} diff --git a/app/src/main/java/org/schabi/newpipe/player/exoplayer/HlsRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/HlsRendererBuilder.java deleted file mode 100644 index 8e6c2d9f5..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/exoplayer/HlsRendererBuilder.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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. - */ -package org.schabi.newpipe.player.exoplayer; - -import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; - -import com.google.android.exoplayer.DefaultLoadControl; -import com.google.android.exoplayer.LoadControl; -import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; -import com.google.android.exoplayer.MediaCodecSelector; -import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; -import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer.audio.AudioCapabilities; -import com.google.android.exoplayer.hls.DefaultHlsTrackSelector; -import com.google.android.exoplayer.hls.HlsChunkSource; -import com.google.android.exoplayer.hls.HlsMasterPlaylist; -import com.google.android.exoplayer.hls.HlsPlaylist; -import com.google.android.exoplayer.hls.HlsPlaylistParser; -import com.google.android.exoplayer.hls.HlsSampleSource; -import com.google.android.exoplayer.hls.PtsTimestampAdjusterProvider; -import com.google.android.exoplayer.metadata.Id3Parser; -import com.google.android.exoplayer.metadata.MetadataTrackRenderer; -import com.google.android.exoplayer.text.TextTrackRenderer; -import com.google.android.exoplayer.text.eia608.Eia608TrackRenderer; -import com.google.android.exoplayer.upstream.DataSource; -import com.google.android.exoplayer.upstream.DefaultAllocator; -import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer.upstream.DefaultUriDataSource; -import com.google.android.exoplayer.util.ManifestFetcher; -import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback; - -import android.content.Context; -import android.media.AudioManager; -import android.media.MediaCodec; -import android.os.Handler; - -import java.io.IOException; -import java.util.Map; - -/** - * A {@link RendererBuilder} for HLS. - */ -public class HlsRendererBuilder implements RendererBuilder { - - private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; - private static final int MAIN_BUFFER_SEGMENTS = 256; - private static final int TEXT_BUFFER_SEGMENTS = 2; - - private final Context context; - private final String userAgent; - private final String url; - - private AsyncRendererBuilder currentAsyncBuilder; - - public HlsRendererBuilder(Context context, String userAgent, String url) { - this.context = context; - this.userAgent = userAgent; - this.url = url; - } - - @Override - public void buildRenderers(NPExoPlayer player) { - currentAsyncBuilder = new AsyncRendererBuilder(context, userAgent, url, player); - currentAsyncBuilder.init(); - } - - @Override - public void cancel() { - if (currentAsyncBuilder != null) { - currentAsyncBuilder.cancel(); - currentAsyncBuilder = null; - } - } - - private static final class AsyncRendererBuilder implements ManifestCallback { - - private final Context context; - private final String userAgent; - private final String url; - private final NPExoPlayer player; - private final ManifestFetcher playlistFetcher; - - private boolean canceled; - - public AsyncRendererBuilder(Context context, String userAgent, String url, NPExoPlayer player) { - this.context = context; - this.userAgent = userAgent; - this.url = url; - this.player = player; - HlsPlaylistParser parser = new HlsPlaylistParser(); - playlistFetcher = new ManifestFetcher<>(url, new DefaultUriDataSource(context, userAgent), - parser); - } - - public void init() { - playlistFetcher.singleLoad(player.getMainHandler().getLooper(), this); - } - - public void cancel() { - canceled = true; - } - - @Override - public void onSingleManifestError(IOException e) { - if (canceled) { - return; - } - - player.onRenderersError(e); - } - - @Override - public void onSingleManifest(HlsPlaylist manifest) { - if (canceled) { - return; - } - - Handler mainHandler = player.getMainHandler(); - LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); - DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); - PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider(); - - // Build the video/audio/metadata renderers. - DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - HlsChunkSource chunkSource = new HlsChunkSource(true /* isMaster */, dataSource, url, - manifest, DefaultHlsTrackSelector.newDefaultInstance(context), bandwidthMeter, - timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE); - HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl, - MAIN_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, NPExoPlayer.TYPE_VIDEO); - MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, - sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, - 5000, mainHandler, player, 50); - MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, - MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player, - AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); - MetadataTrackRenderer> id3Renderer = new MetadataTrackRenderer<>( - sampleSource, new Id3Parser(), player, mainHandler.getLooper()); - - // Build the text renderer, preferring Webvtt where available. - boolean preferWebvtt = false; - if (manifest instanceof HlsMasterPlaylist) { - preferWebvtt = !((HlsMasterPlaylist) manifest).subtitles.isEmpty(); - } - TrackRenderer textRenderer; - if (preferWebvtt) { - DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - HlsChunkSource textChunkSource = new HlsChunkSource(false /* isMaster */, textDataSource, - url, manifest, DefaultHlsTrackSelector.newVttInstance(), bandwidthMeter, - timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE); - HlsSampleSource textSampleSource = new HlsSampleSource(textChunkSource, loadControl, - TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, NPExoPlayer.TYPE_TEXT); - textRenderer = new TextTrackRenderer(textSampleSource, player, mainHandler.getLooper()); - } else { - textRenderer = new Eia608TrackRenderer(sampleSource, player, mainHandler.getLooper()); - } - - TrackRenderer[] renderers = new TrackRenderer[NPExoPlayer.RENDERER_COUNT]; - renderers[NPExoPlayer.TYPE_VIDEO] = videoRenderer; - renderers[NPExoPlayer.TYPE_AUDIO] = audioRenderer; - renderers[NPExoPlayer.TYPE_METADATA] = id3Renderer; - renderers[NPExoPlayer.TYPE_TEXT] = textRenderer; - player.onRenderers(renderers, bandwidthMeter); - } - - } - -} diff --git a/app/src/main/java/org/schabi/newpipe/player/exoplayer/NPExoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/NPExoPlayer.java deleted file mode 100644 index 63a6a9261..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/exoplayer/NPExoPlayer.java +++ /dev/null @@ -1,599 +0,0 @@ -/* - * 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. - */ -package org.schabi.newpipe.player.exoplayer; - -import com.google.android.exoplayer.CodecCounters; -import com.google.android.exoplayer.DummyTrackRenderer; -import com.google.android.exoplayer.ExoPlaybackException; -import com.google.android.exoplayer.ExoPlayer; -import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; -import com.google.android.exoplayer.MediaCodecTrackRenderer; -import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException; -import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; -import com.google.android.exoplayer.MediaFormat; -import com.google.android.exoplayer.TimeRange; -import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer.audio.AudioTrack; -import com.google.android.exoplayer.chunk.ChunkSampleSource; -import com.google.android.exoplayer.chunk.Format; -import com.google.android.exoplayer.dash.DashChunkSource; -import com.google.android.exoplayer.drm.StreamingDrmSessionManager; -import com.google.android.exoplayer.hls.HlsSampleSource; -import com.google.android.exoplayer.metadata.MetadataTrackRenderer.MetadataRenderer; -import com.google.android.exoplayer.text.Cue; -import com.google.android.exoplayer.text.TextRenderer; -import com.google.android.exoplayer.upstream.BandwidthMeter; -import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer.util.DebugTextViewHelper; -import com.google.android.exoplayer.util.PlayerControl; - -import android.media.MediaCodec.CryptoException; -import android.os.Handler; -import android.os.Looper; -import android.view.Surface; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * A wrapper around {@link ExoPlayer} that provides a higher level interface. It can be prepared - * with one of a number of {@link RendererBuilder} classes to suit different use cases (e.g. DASH, - * SmoothStreaming and so on). - */ -public class NPExoPlayer implements ExoPlayer.Listener, ChunkSampleSource.EventListener, - HlsSampleSource.EventListener, DefaultBandwidthMeter.EventListener, - MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener, - StreamingDrmSessionManager.EventListener, DashChunkSource.EventListener, TextRenderer, - MetadataRenderer>, DebugTextViewHelper.Provider { - - /** - * Builds renderers for the player. - */ - public interface RendererBuilder { - /** - * Builds renderers for playback. - * - * @param player The player for which renderers are being built. {@link NPExoPlayer#onRenderers} - * should be invoked once the renderers have been built. If building fails, - * {@link NPExoPlayer#onRenderersError} should be invoked. - */ - void buildRenderers(NPExoPlayer player); - /** - * Cancels the current build operation, if there is one. Else does nothing. - *

- * A canceled build operation must not invoke {@link NPExoPlayer#onRenderers} or - * {@link NPExoPlayer#onRenderersError} on the player, which may have been released. - */ - void cancel(); - } - - /** - * A listener for core events. - */ - public interface Listener { - void onStateChanged(boolean playWhenReady, int playbackState); - void onError(Exception e); - void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, - float pixelWidthHeightRatio); - } - - /** - * A listener for internal errors. - *

- * These errors are not visible to the user, and hence this listener is provided for - * informational purposes only. Note however that an internal error may cause a fatal - * error if the player fails to recover. If this happens, {@link Listener#onError(Exception)} - * will be invoked. - */ - public interface InternalErrorListener { - void onRendererInitializationError(Exception e); - void onAudioTrackInitializationError(AudioTrack.InitializationException e); - void onAudioTrackWriteError(AudioTrack.WriteException e); - void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); - void onDecoderInitializationError(DecoderInitializationException e); - void onCryptoError(CryptoException e); - void onLoadError(int sourceId, IOException e); - void onDrmSessionManagerError(Exception e); - } - - /** - * A listener for debugging information. - */ - public interface InfoListener { - void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs); - void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs); - void onDroppedFrames(int count, long elapsed); - void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate); - void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, - long mediaStartTimeMs, long mediaEndTimeMs); - void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, - long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs); - void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, - long initializationDurationMs); - void onAvailableRangeChanged(int sourceId, TimeRange availableRange); - } - - /** - * A listener for receiving notifications of timed text. - */ - public interface CaptionListener { - void onCues(List cues); - } - - /** - * A listener for receiving ID3 metadata parsed from the media stream. - */ - public interface Id3MetadataListener { - void onId3Metadata(Map metadata); - } - - // Constants pulled into this class for convenience. - public static final int STATE_IDLE = ExoPlayer.STATE_IDLE; - public static final int STATE_PREPARING = ExoPlayer.STATE_PREPARING; - public static final int STATE_BUFFERING = ExoPlayer.STATE_BUFFERING; - public static final int STATE_READY = ExoPlayer.STATE_READY; - public static final int STATE_ENDED = ExoPlayer.STATE_ENDED; - public static final int TRACK_DISABLED = ExoPlayer.TRACK_DISABLED; - public static final int TRACK_DEFAULT = ExoPlayer.TRACK_DEFAULT; - - public static final int RENDERER_COUNT = 4; - public static final int TYPE_VIDEO = 0; - public static final int TYPE_AUDIO = 1; - public static final int TYPE_TEXT = 2; - public static final int TYPE_METADATA = 3; - - private static final int RENDERER_BUILDING_STATE_IDLE = 1; - private static final int RENDERER_BUILDING_STATE_BUILDING = 2; - private static final int RENDERER_BUILDING_STATE_BUILT = 3; - - private final RendererBuilder rendererBuilder; - private final ExoPlayer player; - private final PlayerControl playerControl; - private final Handler mainHandler; - private final CopyOnWriteArrayList listeners; - - private int rendererBuildingState; - private int lastReportedPlaybackState; - private boolean lastReportedPlayWhenReady; - - private Surface surface; - private TrackRenderer videoRenderer; - private CodecCounters codecCounters; - private Format videoFormat; - private int videoTrackToRestore; - - private BandwidthMeter bandwidthMeter; - private boolean backgrounded; - - private CaptionListener captionListener; - private Id3MetadataListener id3MetadataListener; - private InternalErrorListener internalErrorListener; - private InfoListener infoListener; - - public NPExoPlayer(RendererBuilder rendererBuilder) { - this.rendererBuilder = rendererBuilder; - player = ExoPlayer.Factory.newInstance(RENDERER_COUNT, 1000, 5000); - player.addListener(this); - playerControl = new PlayerControl(player); - mainHandler = new Handler(); - listeners = new CopyOnWriteArrayList<>(); - lastReportedPlaybackState = STATE_IDLE; - rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; - // Disable text initially. - player.setSelectedTrack(TYPE_TEXT, TRACK_DISABLED); - } - - public PlayerControl getPlayerControl() { - return playerControl; - } - - public void addListener(Listener listener) { - listeners.add(listener); - } - - public void removeListener(Listener listener) { - listeners.remove(listener); - } - - public void setInternalErrorListener(InternalErrorListener listener) { - internalErrorListener = listener; - } - - public void setInfoListener(InfoListener listener) { - infoListener = listener; - } - - public void setCaptionListener(CaptionListener listener) { - captionListener = listener; - } - - public void setMetadataListener(Id3MetadataListener listener) { - id3MetadataListener = listener; - } - - public void setSurface(Surface surface) { - this.surface = surface; - pushSurface(false); - } - - public Surface getSurface() { - return surface; - } - - public void blockingClearSurface() { - surface = null; - pushSurface(true); - } - - public int getTrackCount(int type) { - return player.getTrackCount(type); - } - - public MediaFormat getTrackFormat(int type, int index) { - return player.getTrackFormat(type, index); - } - - public int getSelectedTrack(int type) { - return player.getSelectedTrack(type); - } - - public void setSelectedTrack(int type, int index) { - player.setSelectedTrack(type, index); - if (type == TYPE_TEXT && index < 0 && captionListener != null) { - captionListener.onCues(Collections.emptyList()); - } - } - - public boolean getBackgrounded() { - return backgrounded; - } - - public void setBackgrounded(boolean backgrounded) { - if (this.backgrounded == backgrounded) { - return; - } - this.backgrounded = backgrounded; - if (backgrounded) { - videoTrackToRestore = getSelectedTrack(TYPE_VIDEO); - setSelectedTrack(TYPE_VIDEO, TRACK_DISABLED); - blockingClearSurface(); - } else { - setSelectedTrack(TYPE_VIDEO, videoTrackToRestore); - } - } - - public void prepare() { - if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILT) { - player.stop(); - } - rendererBuilder.cancel(); - videoFormat = null; - videoRenderer = null; - rendererBuildingState = RENDERER_BUILDING_STATE_BUILDING; - maybeReportPlayerState(); - rendererBuilder.buildRenderers(this); - } - - /** - * Invoked with the results from a {@link RendererBuilder}. - * - * @param renderers Renderers indexed by {@link NPExoPlayer} TYPE_* constants. An individual - * element may be null if there do not exist tracks of the corresponding type. - * @param bandwidthMeter Provides an estimate of the currently available bandwidth. May be null. - */ - /* package */ void onRenderers(TrackRenderer[] renderers, BandwidthMeter bandwidthMeter) { - for (int i = 0; i < RENDERER_COUNT; i++) { - if (renderers[i] == null) { - // Convert a null renderer to a dummy renderer. - renderers[i] = new DummyTrackRenderer(); - } - } - // Complete preparation. - this.videoRenderer = renderers[TYPE_VIDEO]; - this.codecCounters = videoRenderer instanceof MediaCodecTrackRenderer - ? ((MediaCodecTrackRenderer) videoRenderer).codecCounters - : renderers[TYPE_AUDIO] instanceof MediaCodecTrackRenderer - ? ((MediaCodecTrackRenderer) renderers[TYPE_AUDIO]).codecCounters : null; - this.bandwidthMeter = bandwidthMeter; - pushSurface(false); - player.prepare(renderers); - rendererBuildingState = RENDERER_BUILDING_STATE_BUILT; - } - - /** - * Invoked if a {@link RendererBuilder} encounters an error. - * - * @param e Describes the error. - */ - /* package */ void onRenderersError(Exception e) { - if (internalErrorListener != null) { - internalErrorListener.onRendererInitializationError(e); - } - for (Listener listener : listeners) { - listener.onError(e); - } - rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; - maybeReportPlayerState(); - } - - public void setPlayWhenReady(boolean playWhenReady) { - player.setPlayWhenReady(playWhenReady); - } - - public void seekTo(long positionMs) { - player.seekTo(positionMs); - } - - public void release() { - rendererBuilder.cancel(); - rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; - surface = null; - player.release(); - } - - public int getPlaybackState() { - if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILDING) { - return STATE_PREPARING; - } - int playerState = player.getPlaybackState(); - if (rendererBuildingState == RENDERER_BUILDING_STATE_BUILT && playerState == STATE_IDLE) { - // This is an edge case where the renderers are built, but are still being passed to the - // player's playback thread. - return STATE_PREPARING; - } - return playerState; - } - - @Override - public Format getFormat() { - return videoFormat; - } - - @Override - public BandwidthMeter getBandwidthMeter() { - return bandwidthMeter; - } - - @Override - public CodecCounters getCodecCounters() { - return codecCounters; - } - - @Override - public long getCurrentPosition() { - return player.getCurrentPosition(); - } - - public long getDuration() { - return player.getDuration(); - } - - public int getBufferedPercentage() { - return player.getBufferedPercentage(); - } - - public boolean getPlayWhenReady() { - return player.getPlayWhenReady(); - } - - /* package */ Looper getPlaybackLooper() { - return player.getPlaybackLooper(); - } - - /* package */ Handler getMainHandler() { - return mainHandler; - } - - @Override - public void onPlayerStateChanged(boolean playWhenReady, int state) { - maybeReportPlayerState(); - } - - @Override - public void onPlayerError(ExoPlaybackException exception) { - rendererBuildingState = RENDERER_BUILDING_STATE_IDLE; - for (Listener listener : listeners) { - listener.onError(exception); - } - } - - @Override - public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, - float pixelWidthHeightRatio) { - for (Listener listener : listeners) { - listener.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio); - } - } - - @Override - public void onDroppedFrames(int count, long elapsed) { - if (infoListener != null) { - infoListener.onDroppedFrames(count, elapsed); - } - } - - @Override - public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) { - if (infoListener != null) { - infoListener.onBandwidthSample(elapsedMs, bytes, bitrateEstimate); - } - } - - @Override - public void onDownstreamFormatChanged(int sourceId, Format format, int trigger, - long mediaTimeMs) { - if (infoListener == null) { - return; - } - if (sourceId == TYPE_VIDEO) { - videoFormat = format; - infoListener.onVideoFormatEnabled(format, trigger, mediaTimeMs); - } else if (sourceId == TYPE_AUDIO) { - infoListener.onAudioFormatEnabled(format, trigger, mediaTimeMs); - } - } - - @Override - public void onDrmKeysLoaded() { - // Do nothing. - } - - @Override - public void onDrmSessionManagerError(Exception e) { - if (internalErrorListener != null) { - internalErrorListener.onDrmSessionManagerError(e); - } - } - - @Override - public void onDecoderInitializationError(DecoderInitializationException e) { - if (internalErrorListener != null) { - internalErrorListener.onDecoderInitializationError(e); - } - } - - @Override - public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { - if (internalErrorListener != null) { - internalErrorListener.onAudioTrackInitializationError(e); - } - } - - @Override - public void onAudioTrackWriteError(AudioTrack.WriteException e) { - if (internalErrorListener != null) { - internalErrorListener.onAudioTrackWriteError(e); - } - } - - @Override - public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { - if (internalErrorListener != null) { - internalErrorListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); - } - } - - @Override - public void onCryptoError(CryptoException e) { - if (internalErrorListener != null) { - internalErrorListener.onCryptoError(e); - } - } - - @Override - public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, - long initializationDurationMs) { - if (infoListener != null) { - infoListener.onDecoderInitialized(decoderName, elapsedRealtimeMs, initializationDurationMs); - } - } - - @Override - public void onLoadError(int sourceId, IOException e) { - if (internalErrorListener != null) { - internalErrorListener.onLoadError(sourceId, e); - } - } - - @Override - public void onCues(List cues) { - if (captionListener != null && getSelectedTrack(TYPE_TEXT) != TRACK_DISABLED) { - captionListener.onCues(cues); - } - } - - @Override - public void onMetadata(Map metadata) { - if (id3MetadataListener != null && getSelectedTrack(TYPE_METADATA) != TRACK_DISABLED) { - id3MetadataListener.onId3Metadata(metadata); - } - } - - @Override - public void onAvailableRangeChanged(int sourceId, TimeRange availableRange) { - if (infoListener != null) { - infoListener.onAvailableRangeChanged(sourceId, availableRange); - } - } - - @Override - public void onPlayWhenReadyCommitted() { - // Do nothing. - } - - @Override - public void onDrawnToSurface(Surface surface) { - // Do nothing. - } - - @Override - public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, - long mediaStartTimeMs, long mediaEndTimeMs) { - if (infoListener != null) { - infoListener.onLoadStarted(sourceId, length, type, trigger, format, mediaStartTimeMs, - mediaEndTimeMs); - } - } - - @Override - public void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, - long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs) { - if (infoListener != null) { - infoListener.onLoadCompleted(sourceId, bytesLoaded, type, trigger, format, mediaStartTimeMs, - mediaEndTimeMs, elapsedRealtimeMs, loadDurationMs); - } - } - - @Override - public void onLoadCanceled(int sourceId, long bytesLoaded) { - // Do nothing. - } - - @Override - public void onUpstreamDiscarded(int sourceId, long mediaStartTimeMs, long mediaEndTimeMs) { - // Do nothing. - } - - private void maybeReportPlayerState() { - boolean playWhenReady = player.getPlayWhenReady(); - int playbackState = getPlaybackState(); - if (lastReportedPlayWhenReady != playWhenReady || lastReportedPlaybackState != playbackState) { - for (Listener listener : listeners) { - listener.onStateChanged(playWhenReady, playbackState); - } - lastReportedPlayWhenReady = playWhenReady; - lastReportedPlaybackState = playbackState; - } - } - - private void pushSurface(boolean blockForSurfacePush) { - if (videoRenderer == null) { - return; - } - - if (blockForSurfacePush) { - player.blockingSendMessage( - videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); - } else { - player.sendMessage( - videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface); - } - } - -} diff --git a/app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingRendererBuilder.java b/app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingRendererBuilder.java deleted file mode 100644 index 55b59c276..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/exoplayer/SmoothStreamingRendererBuilder.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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. - */ -package org.schabi.newpipe.player.exoplayer; - - -import org.schabi.newpipe.player.exoplayer.NPExoPlayer.RendererBuilder; - -import com.google.android.exoplayer.DefaultLoadControl; -import com.google.android.exoplayer.LoadControl; -import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; -import com.google.android.exoplayer.MediaCodecSelector; -import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; -import com.google.android.exoplayer.TrackRenderer; -import com.google.android.exoplayer.audio.AudioCapabilities; -import com.google.android.exoplayer.chunk.ChunkSampleSource; -import com.google.android.exoplayer.chunk.ChunkSource; -import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; -import com.google.android.exoplayer.drm.DrmSessionManager; -import com.google.android.exoplayer.drm.MediaDrmCallback; -import com.google.android.exoplayer.drm.StreamingDrmSessionManager; -import com.google.android.exoplayer.drm.UnsupportedDrmException; -import com.google.android.exoplayer.smoothstreaming.DefaultSmoothStreamingTrackSelector; -import com.google.android.exoplayer.smoothstreaming.SmoothStreamingChunkSource; -import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest; -import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser; -import com.google.android.exoplayer.text.TextTrackRenderer; -import com.google.android.exoplayer.upstream.DataSource; -import com.google.android.exoplayer.upstream.DefaultAllocator; -import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer.upstream.DefaultUriDataSource; -import com.google.android.exoplayer.util.ManifestFetcher; -import com.google.android.exoplayer.util.Util; - -import android.content.Context; -import android.media.AudioManager; -import android.media.MediaCodec; -import android.os.Handler; - -import java.io.IOException; - -/** - * A {@link RendererBuilder} for SmoothStreaming. - */ -public class SmoothStreamingRendererBuilder implements RendererBuilder { - - private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; - private static final int VIDEO_BUFFER_SEGMENTS = 200; - private static final int AUDIO_BUFFER_SEGMENTS = 54; - private static final int TEXT_BUFFER_SEGMENTS = 2; - private static final int LIVE_EDGE_LATENCY_MS = 30000; - - private final Context context; - private final String userAgent; - private final String url; - private final MediaDrmCallback drmCallback; - - private AsyncRendererBuilder currentAsyncBuilder; - - public SmoothStreamingRendererBuilder(Context context, String userAgent, String url, - MediaDrmCallback drmCallback) { - this.context = context; - this.userAgent = userAgent; - this.url = Util.toLowerInvariant(url).endsWith("/manifest") ? url : url + "/Manifest"; - this.drmCallback = drmCallback; - } - - @Override - public void buildRenderers(NPExoPlayer player) { - currentAsyncBuilder = new AsyncRendererBuilder(context, userAgent, url, drmCallback, player); - currentAsyncBuilder.init(); - } - - @Override - public void cancel() { - if (currentAsyncBuilder != null) { - currentAsyncBuilder.cancel(); - currentAsyncBuilder = null; - } - } - - private static final class AsyncRendererBuilder - implements ManifestFetcher.ManifestCallback { - - private final Context context; - private final String userAgent; - private final MediaDrmCallback drmCallback; - private final NPExoPlayer player; - private final ManifestFetcher manifestFetcher; - - private boolean canceled; - - public AsyncRendererBuilder(Context context, String userAgent, String url, - MediaDrmCallback drmCallback, NPExoPlayer player) { - this.context = context; - this.userAgent = userAgent; - this.drmCallback = drmCallback; - this.player = player; - SmoothStreamingManifestParser parser = new SmoothStreamingManifestParser(); - manifestFetcher = new ManifestFetcher<>(url, new DefaultHttpDataSource(userAgent, null), - parser); - } - - public void init() { - manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this); - } - - public void cancel() { - canceled = true; - } - - @Override - public void onSingleManifestError(IOException exception) { - if (canceled) { - return; - } - - player.onRenderersError(exception); - } - - @Override - public void onSingleManifest(SmoothStreamingManifest manifest) { - if (canceled) { - return; - } - - Handler mainHandler = player.getMainHandler(); - LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); - DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player); - - // Check drm support if necessary. - DrmSessionManager drmSessionManager = null; - if (manifest.protectionElement != null) { - if (Util.SDK_INT < 18) { - player.onRenderersError( - new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); - return; - } - try { - drmSessionManager = new StreamingDrmSessionManager(manifest.protectionElement.uuid, - player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); - } catch (UnsupportedDrmException e) { - player.onRenderersError(e); - return; - } - } - - // Build the video renderer. - DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - ChunkSource videoChunkSource = new SmoothStreamingChunkSource(manifestFetcher, - DefaultSmoothStreamingTrackSelector.newVideoInstance(context, true, false), - videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS); - ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, - VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, - NPExoPlayer.TYPE_VIDEO); - TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource, - MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, - drmSessionManager, true, mainHandler, player, 50); - - // Build the audio renderer. - DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - ChunkSource audioChunkSource = new SmoothStreamingChunkSource(manifestFetcher, - DefaultSmoothStreamingTrackSelector.newAudioInstance(), - audioDataSource, null, LIVE_EDGE_LATENCY_MS); - ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, - AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, - NPExoPlayer.TYPE_AUDIO); - TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource, - MediaCodecSelector.DEFAULT, drmSessionManager, true, mainHandler, player, - AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC); - - // Build the text renderer. - DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); - ChunkSource textChunkSource = new SmoothStreamingChunkSource(manifestFetcher, - DefaultSmoothStreamingTrackSelector.newTextInstance(), - textDataSource, null, LIVE_EDGE_LATENCY_MS); - ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, - TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, - NPExoPlayer.TYPE_TEXT); - TrackRenderer textRenderer = new TextTrackRenderer(textSampleSource, player, - mainHandler.getLooper()); - - // Invoke the callback. - TrackRenderer[] renderers = new TrackRenderer[NPExoPlayer.RENDERER_COUNT]; - renderers[NPExoPlayer.TYPE_VIDEO] = videoRenderer; - renderers[NPExoPlayer.TYPE_AUDIO] = audioRenderer; - renderers[NPExoPlayer.TYPE_TEXT] = textRenderer; - player.onRenderers(renderers, bandwidthMeter); - } - - } - -} diff --git a/app/src/main/res/layout/activity_exo_player.xml b/app/src/main/res/layout/activity_exo_player.xml new file mode 100644 index 000000000..5653532ec --- /dev/null +++ b/app/src/main/res/layout/activity_exo_player.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/layout/exo_player_activity.xml b/app/src/main/res/layout/exo_player_activity.xml deleted file mode 100644 index c7d3a7c95..000000000 --- a/app/src/main/res/layout/exo_player_activity.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/exomedia_custom_controls.xml b/app/src/main/res/layout/exomedia_custom_controls.xml new file mode 100644 index 000000000..dedaf7908 --- /dev/null +++ b/app/src/main/res/layout/exomedia_custom_controls.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file