2017-07-31 20:40:46 -05:00
|
|
|
/**
|
|
|
|
* Copyright (C) 2017 Whisper Systems
|
|
|
|
*
|
|
|
|
* This program 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.
|
|
|
|
*
|
|
|
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2016-11-26 12:10:14 -08:00
|
|
|
package org.thoughtcrime.securesms.video;
|
|
|
|
|
|
|
|
import android.content.Context;
|
2017-04-13 14:15:06 -07:00
|
|
|
import android.os.Build;
|
2016-11-26 12:10:14 -08:00
|
|
|
import android.support.annotation.NonNull;
|
|
|
|
import android.support.annotation.Nullable;
|
|
|
|
import android.util.AttributeSet;
|
2016-12-11 13:37:27 -08:00
|
|
|
import android.util.Log;
|
2017-07-31 20:40:46 -05:00
|
|
|
import android.view.Window;
|
|
|
|
import android.view.WindowManager;
|
2016-11-26 12:10:14 -08:00
|
|
|
import android.widget.FrameLayout;
|
|
|
|
import android.widget.MediaController;
|
2016-12-11 13:37:27 -08:00
|
|
|
import android.widget.Toast;
|
2016-11-26 12:10:14 -08:00
|
|
|
import android.widget.VideoView;
|
|
|
|
|
2017-04-13 14:15:06 -07:00
|
|
|
import com.google.android.exoplayer2.DefaultLoadControl;
|
2017-07-31 20:40:46 -05:00
|
|
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
|
|
|
import com.google.android.exoplayer2.ExoPlayer;
|
2017-04-13 14:15:06 -07:00
|
|
|
import com.google.android.exoplayer2.ExoPlayerFactory;
|
|
|
|
import com.google.android.exoplayer2.LoadControl;
|
|
|
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
2017-07-31 20:40:46 -05:00
|
|
|
import com.google.android.exoplayer2.Timeline;
|
2017-04-13 14:15:06 -07:00
|
|
|
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
|
|
|
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
|
|
|
|
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
|
|
|
import com.google.android.exoplayer2.source.MediaSource;
|
2017-07-31 20:40:46 -05:00
|
|
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
2017-04-13 14:15:06 -07:00
|
|
|
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
|
|
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
|
|
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
2017-07-31 20:40:46 -05:00
|
|
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
2017-04-13 14:15:06 -07:00
|
|
|
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
|
|
|
import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
|
|
|
|
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
|
|
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
|
|
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
|
|
|
|
2016-11-26 12:10:14 -08:00
|
|
|
import org.thoughtcrime.securesms.R;
|
|
|
|
import org.thoughtcrime.securesms.attachments.AttachmentServer;
|
|
|
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
2016-12-11 13:37:27 -08:00
|
|
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
2016-11-26 12:10:14 -08:00
|
|
|
import org.thoughtcrime.securesms.mms.VideoSlide;
|
|
|
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
2017-04-13 14:15:06 -07:00
|
|
|
import org.thoughtcrime.securesms.video.exo.AttachmentDataSourceFactory;
|
2016-11-26 12:10:14 -08:00
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
|
|
|
public class VideoPlayer extends FrameLayout {
|
|
|
|
|
2016-12-11 13:37:27 -08:00
|
|
|
private static final String TAG = VideoPlayer.class.getName();
|
|
|
|
|
2017-04-13 14:15:06 -07:00
|
|
|
@Nullable private final VideoView videoView;
|
|
|
|
@Nullable private final SimpleExoPlayerView exoView;
|
|
|
|
|
|
|
|
@Nullable private SimpleExoPlayer exoPlayer;
|
|
|
|
@Nullable private AttachmentServer attachmentServer;
|
2017-07-31 20:40:46 -05:00
|
|
|
@Nullable private Window window;
|
2016-11-26 12:10:14 -08:00
|
|
|
|
|
|
|
public VideoPlayer(Context context) {
|
|
|
|
this(context, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public VideoPlayer(Context context, AttributeSet attrs) {
|
|
|
|
this(context, attrs, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
public VideoPlayer(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
|
|
super(context, attrs, defStyleAttr);
|
|
|
|
|
|
|
|
inflate(context, R.layout.video_player, this);
|
|
|
|
|
2017-04-13 14:15:06 -07:00
|
|
|
if (Build.VERSION.SDK_INT >= 16) {
|
|
|
|
this.exoView = ViewUtil.findById(this, R.id.video_view);
|
|
|
|
this.videoView = null;
|
|
|
|
} else {
|
|
|
|
this.videoView = ViewUtil.findById(this, R.id.video_view);
|
|
|
|
this.exoView = null;
|
|
|
|
initializeVideoViewControls(videoView);
|
|
|
|
}
|
|
|
|
}
|
2016-11-26 12:10:14 -08:00
|
|
|
|
2017-04-13 14:15:06 -07:00
|
|
|
public void setVideoSource(@NonNull MasterSecret masterSecret, @NonNull VideoSlide videoSource)
|
|
|
|
throws IOException
|
|
|
|
{
|
2017-04-16 11:11:08 -07:00
|
|
|
if (Build.VERSION.SDK_INT >= 16) setExoViewSource(masterSecret, videoSource);
|
2017-04-13 14:15:06 -07:00
|
|
|
else setVideoViewSource(masterSecret, videoSource);
|
2016-11-26 12:10:14 -08:00
|
|
|
}
|
|
|
|
|
2017-04-13 14:15:06 -07:00
|
|
|
public void cleanup() {
|
|
|
|
if (this.attachmentServer != null) {
|
|
|
|
this.attachmentServer.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.exoPlayer != null) {
|
|
|
|
this.exoPlayer.release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-31 20:40:46 -05:00
|
|
|
public void setWindow(Window window) {
|
|
|
|
this.window = window;
|
|
|
|
}
|
|
|
|
|
2017-04-13 14:15:06 -07:00
|
|
|
private void setExoViewSource(@NonNull MasterSecret masterSecret, @NonNull VideoSlide videoSource)
|
|
|
|
throws IOException
|
|
|
|
{
|
|
|
|
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
|
|
|
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
|
|
|
|
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
|
|
|
LoadControl loadControl = new DefaultLoadControl();
|
|
|
|
|
|
|
|
exoPlayer = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, loadControl);
|
2017-07-31 20:40:46 -05:00
|
|
|
exoPlayer.addListener(new ExoPlayerListener(window));
|
2017-04-13 14:15:06 -07:00
|
|
|
exoView.setPlayer(exoPlayer);
|
|
|
|
|
|
|
|
DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(getContext(), "GenericUserAgent", null);
|
|
|
|
AttachmentDataSourceFactory attachmentDataSourceFactory = new AttachmentDataSourceFactory(getContext(), masterSecret, defaultDataSourceFactory, null);
|
|
|
|
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
|
|
|
|
|
|
|
|
MediaSource mediaSource = new ExtractorMediaSource(videoSource.getUri(), attachmentDataSourceFactory, extractorsFactory, null, null);
|
|
|
|
|
|
|
|
exoPlayer.prepare(mediaSource);
|
|
|
|
exoPlayer.setPlayWhenReady(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setVideoViewSource(@NonNull MasterSecret masterSecret, @NonNull VideoSlide videoSource)
|
|
|
|
throws IOException
|
|
|
|
{
|
2016-11-26 12:10:14 -08:00
|
|
|
if (this.attachmentServer != null) {
|
|
|
|
this.attachmentServer.stop();
|
|
|
|
}
|
|
|
|
|
2016-12-11 13:37:27 -08:00
|
|
|
if (videoSource.getUri() != null && PartAuthority.isLocalUri(videoSource.getUri())) {
|
|
|
|
Log.w(TAG, "Starting video attachment server for part provider Uri...");
|
|
|
|
this.attachmentServer = new AttachmentServer(getContext(), masterSecret, videoSource.asAttachment());
|
|
|
|
this.attachmentServer.start();
|
|
|
|
|
|
|
|
this.videoView.setVideoURI(this.attachmentServer.getUri());
|
|
|
|
} else if (videoSource.getUri() != null) {
|
|
|
|
Log.w(TAG, "Playing video directly from non-local Uri...");
|
|
|
|
this.videoView.setVideoURI(videoSource.getUri());
|
|
|
|
} else {
|
2017-02-01 03:50:48 +01:00
|
|
|
Toast.makeText(getContext(), getContext().getString(R.string.VideoPlayer_error_playing_video), Toast.LENGTH_LONG).show();
|
2016-12-11 13:37:27 -08:00
|
|
|
return;
|
|
|
|
}
|
2016-11-26 12:10:14 -08:00
|
|
|
|
|
|
|
this.videoView.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void initializeVideoViewControls(@NonNull VideoView videoView) {
|
|
|
|
MediaController mediaController = new MediaController(getContext());
|
|
|
|
mediaController.setAnchorView(videoView);
|
|
|
|
mediaController.setMediaPlayer(videoView);
|
|
|
|
|
|
|
|
videoView.setMediaController(mediaController);
|
|
|
|
}
|
2017-07-31 20:40:46 -05:00
|
|
|
|
|
|
|
private class ExoPlayerListener implements ExoPlayer.EventListener {
|
|
|
|
private final Window window;
|
|
|
|
|
|
|
|
ExoPlayerListener(Window window) {
|
|
|
|
this.window = window;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
|
|
|
switch(playbackState) {
|
|
|
|
case ExoPlayer.STATE_IDLE:
|
|
|
|
case ExoPlayer.STATE_BUFFERING:
|
|
|
|
case ExoPlayer.STATE_ENDED:
|
|
|
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
|
|
break;
|
|
|
|
case ExoPlayer.STATE_READY:
|
|
|
|
if (playWhenReady) {
|
|
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
|
|
} else {
|
|
|
|
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTimelineChanged(Timeline timeline, Object manifest) { }
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { }
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onLoadingChanged(boolean isLoading) { }
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPlayerError(ExoPlaybackException error) { }
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onPositionDiscontinuity() { }
|
|
|
|
}
|
2016-11-26 12:10:14 -08:00
|
|
|
}
|