diff --git a/build.gradle b/build.gradle index 5d3e3b461d..d9e355b023 100644 --- a/build.gradle +++ b/build.gradle @@ -76,8 +76,8 @@ dependencies { compile 'com.google.android.gms:play-services-maps:9.6.1' compile 'com.google.android.gms:play-services-places:9.6.1' - compile 'com.google.android.exoplayer:exoplayer-core:2.8.4' - compile 'com.google.android.exoplayer:exoplayer-ui:2.8.4' + compile 'com.google.android.exoplayer:exoplayer-core:2.9.1' + compile 'com.google.android.exoplayer:exoplayer-ui:2.9.1' compile 'org.whispersystems:signal-service-android:2.12.2' compile 'org.whispersystems:webrtc-android:M69' @@ -172,8 +172,8 @@ dependencyVerification { 'com.google.android.gms:play-services-gcm:312e61253a236f2d9b750b9c04fc92fd190d23b0b2755c99de6ce4a28b259dae', 'com.google.android.gms:play-services-places:abf3a4a3b146ec7e6e753be62775e512868cf37d6f88ffe2d81167b33b57132b', 'com.google.android.gms:play-services-maps:45e8021e7ddac4a44a82a0e9698991389ded3023d35c58f38dbd86d54211ec0e', - 'com.google.android.exoplayer:exoplayer-ui:027557b2d69b15e1852a2530b36971f0dcc177abae240ee35e05f63502cdb0a7', - 'com.google.android.exoplayer:exoplayer-core:e69b409e11887c955deb373357c30eeabf183395db0092b4817e0f80bb467d5b', + 'com.google.android.exoplayer:exoplayer-ui:7a942afcc402ff01e9bf48e8d3942850986710f06562d50a1408aaf04a683151', + 'com.google.android.exoplayer:exoplayer-core:b6ab34abac36bc2bc6934b7a50008162feca2c0fde91aaf1e8c1c22f2c16e2c0', 'org.whispersystems:signal-service-android:26639df2a9c31b6f31f82034091a4ea3002ca6b1088e7fe6d30428a8290dcf2a', 'org.whispersystems:webrtc-android:5493c92141ce884fc5ce8240d783232f4fe14bd17a8d0d7d1bd4944d0bd1682f', 'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774', diff --git a/res/layout-v16/video_player.xml b/res/layout-v16/video_player.xml index 9d8b075204..ba3075f06f 100644 --- a/res/layout-v16/video_player.xml +++ b/res/layout-v16/video_player.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - listener; - private @Nullable MediaPlayerWrapper mediaPlayer; + private @Nullable SimpleExoPlayer mediaPlayer; private @Nullable AttachmentServer audioAttachmentServer; private long startTime; @@ -85,64 +100,64 @@ public class AudioSlidePlayer implements SensorEventListener { private void play(final double progress, boolean earpiece) throws IOException { if (this.mediaPlayer != null) return; - this.mediaPlayer = new MediaPlayerWrapper(); + LoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE).createDefaultLoadControl(); + this.mediaPlayer = ExoPlayerFactory.newSimpleInstance(context, new DefaultRenderersFactory(context), new DefaultTrackSelector(), loadControl); this.audioAttachmentServer = new AttachmentServer(context, slide.asAttachment()); this.startTime = System.currentTimeMillis(); audioAttachmentServer.start(); - mediaPlayer.setDataSource(context, audioAttachmentServer.getUri()); + mediaPlayer.prepare(createMediaSource(audioAttachmentServer.getUri())); + mediaPlayer.setPlayWhenReady(true); mediaPlayer.setAudioStreamType(earpiece ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC); - mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + mediaPlayer.addListener(new Player.DefaultEventListener() { + @Override - public void onPrepared(MediaPlayer mp) { - Log.i(TAG, "onPrepared"); - synchronized (AudioSlidePlayer.this) { - if (mediaPlayer == null) return; + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + switch (playbackState) { + case Player.STATE_READY: + Log.w(TAG, "onPrepared"); + synchronized (AudioSlidePlayer.this) { + if (mediaPlayer == null) return; - if (progress > 0) { - mediaPlayer.seekTo((int) (mediaPlayer.getDuration() * progress)); - } + if (progress > 0) { + mediaPlayer.seekTo((long) (mediaPlayer.getDuration() * progress)); + } - sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); - mediaPlayer.start(); + sensorManager.registerListener(AudioSlidePlayer.this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); - setPlaying(AudioSlidePlayer.this); + setPlaying(AudioSlidePlayer.this); + } + + notifyOnStart(); + progressEventHandler.sendEmptyMessage(0); + break; + + case Player.STATE_ENDED: + Log.w(TAG, "onComplete"); + synchronized (AudioSlidePlayer.this) { + mediaPlayer = null; + + if (audioAttachmentServer != null) { + audioAttachmentServer.stop(); + audioAttachmentServer = null; + } + + sensorManager.unregisterListener(AudioSlidePlayer.this); + + if (wakeLock != null && wakeLock.isHeld()) { + wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); + } + } + + notifyOnStop(); + progressEventHandler.removeMessages(0); } - - notifyOnStart(); - progressEventHandler.sendEmptyMessage(0); } - }); - mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override - public void onCompletion(MediaPlayer mp) { - Log.i(TAG, "onComplete"); - synchronized (AudioSlidePlayer.this) { - mediaPlayer = null; - - if (audioAttachmentServer != null) { - audioAttachmentServer.stop(); - audioAttachmentServer = null; - } - - sensorManager.unregisterListener(AudioSlidePlayer.this); - - if (wakeLock != null && wakeLock.isHeld()) { - wakeLock.release(PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY); - } - } - - notifyOnStop(); - progressEventHandler.removeMessages(0); - } - }); - - mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public boolean onError(MediaPlayer mp, int what, int extra) { - Log.w(TAG, "MediaPlayer Error: " + what + " , " + extra); + public void onPlayerError(ExoPlaybackException error) { + Log.w(TAG, "MediaPlayer Error: " + error); Toast.makeText(context, R.string.AudioSlidePlayer_error_playing_audio, Toast.LENGTH_SHORT).show(); @@ -163,11 +178,14 @@ public class AudioSlidePlayer implements SensorEventListener { notifyOnStop(); progressEventHandler.removeMessages(0); - return true; } }); + } - mediaPlayer.prepareAsync(); + private MediaSource createMediaSource(@NonNull Uri uri) { + return new ExtractorMediaSource.Factory(new DefaultDataSourceFactory(context, BuildConfig.USER_AGENT)) + .setExtractorsFactory(new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)) + .createMediaSource(uri); } public synchronized void stop() { @@ -199,7 +217,7 @@ public class AudioSlidePlayer implements SensorEventListener { public void setListener(@NonNull Listener listener) { this.listener = new WeakReference<>(listener); - if (this.mediaPlayer != null && this.mediaPlayer.isPlaying()) { + if (this.mediaPlayer != null && this.mediaPlayer.getPlaybackState() == Player.STATE_READY) { notifyOnStart(); } } @@ -214,7 +232,7 @@ public class AudioSlidePlayer implements SensorEventListener { return new Pair<>(0D, 0); } else { return new Pair<>((double) mediaPlayer.getCurrentPosition() / (double) mediaPlayer.getDuration(), - mediaPlayer.getCurrentPosition()); + (int) mediaPlayer.getCurrentPosition()); } } @@ -277,7 +295,7 @@ public class AudioSlidePlayer implements SensorEventListener { @Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() != Sensor.TYPE_PROXIMITY) return; - if (mediaPlayer == null || !mediaPlayer.isPlaying()) return; + if (mediaPlayer == null || mediaPlayer.getPlaybackState() != Player.STATE_READY) return; int streamType; @@ -335,7 +353,7 @@ public class AudioSlidePlayer implements SensorEventListener { public void handleMessage(Message msg) { AudioSlidePlayer player = playerReference.get(); - if (player == null || player.mediaPlayer == null || !player.mediaPlayer.isPlaying()) { + if (player == null || player.mediaPlayer == null || player.mediaPlayer.getPlaybackState() != ExoPlayer.STATE_READY) { return; } @@ -344,19 +362,4 @@ public class AudioSlidePlayer implements SensorEventListener { sendEmptyMessageDelayed(0, 50); } } - - private static class MediaPlayerWrapper extends MediaPlayer { - - private int streamType; - - @Override - public void setAudioStreamType(int streamType) { - this.streamType = streamType; - super.setAudioStreamType(streamType); - } - - public int getAudioStreamType() { - return streamType; - } - } } diff --git a/src/org/thoughtcrime/securesms/video/VideoPlayer.java b/src/org/thoughtcrime/securesms/video/VideoPlayer.java index 0a965aa61c..b309b3e2f2 100644 --- a/src/org/thoughtcrime/securesms/video/VideoPlayer.java +++ b/src/org/thoughtcrime/securesms/video/VideoPlayer.java @@ -46,6 +46,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelector; +import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.SimpleExoPlayerView; import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; @@ -66,7 +67,7 @@ public class VideoPlayer extends FrameLayout { private static final String TAG = VideoPlayer.class.getName(); @Nullable private final VideoView videoView; - @Nullable private final SimpleExoPlayerView exoView; + @Nullable private final PlayerView exoView; @Nullable private SimpleExoPlayer exoPlayer; @Nullable private AttachmentServer attachmentServer; diff --git a/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java b/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java index d3cf6f2b82..2989ff35c2 100644 --- a/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java +++ b/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSource.java @@ -6,10 +6,14 @@ import android.net.Uri; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DefaultDataSource; +import com.google.android.exoplayer2.upstream.TransferListener; import org.thoughtcrime.securesms.mms.PartAuthority; import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; public class AttachmentDataSource implements DataSource { @@ -23,6 +27,10 @@ public class AttachmentDataSource implements DataSource { this.partDataSource = partDataSource; } + @Override + public void addTransferListener(TransferListener transferListener) { + } + @Override public long open(DataSpec dataSpec) throws IOException { if (PartAuthority.isLocalUri(dataSpec.uri)) dataSource = partDataSource; @@ -41,6 +49,11 @@ public class AttachmentDataSource implements DataSource { return dataSource.getUri(); } + @Override + public Map> getResponseHeaders() { + return Collections.emptyMap(); + } + @Override public void close() throws IOException { dataSource.close(); diff --git a/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java b/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java index ec216148cb..0d9410f7d0 100644 --- a/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java +++ b/src/org/thoughtcrime/securesms/video/exo/AttachmentDataSourceFactory.java @@ -13,12 +13,12 @@ public class AttachmentDataSourceFactory implements DataSource.Factory { private final Context context; - private final DefaultDataSourceFactory defaultDataSourceFactory; - private final TransferListener listener; + private final DefaultDataSourceFactory defaultDataSourceFactory; + private final TransferListener listener; public AttachmentDataSourceFactory(@NonNull Context context, @NonNull DefaultDataSourceFactory defaultDataSourceFactory, - @Nullable TransferListener listener) + @Nullable TransferListener listener) { this.context = context; this.defaultDataSourceFactory = defaultDataSourceFactory; diff --git a/src/org/thoughtcrime/securesms/video/exo/PartDataSource.java b/src/org/thoughtcrime/securesms/video/exo/PartDataSource.java index 220c733d83..78116e5992 100644 --- a/src/org/thoughtcrime/securesms/video/exo/PartDataSource.java +++ b/src/org/thoughtcrime/securesms/video/exo/PartDataSource.java @@ -18,20 +18,27 @@ import org.thoughtcrime.securesms.mms.PartUriParser; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.Map; public class PartDataSource implements DataSource { - private final @NonNull Context context; - private final @Nullable TransferListener listener; + private final @NonNull Context context; + private final @Nullable TransferListener listener; private Uri uri; private InputStream inputSteam; - PartDataSource(@NonNull Context context, @Nullable TransferListener listener) { + PartDataSource(@NonNull Context context, @Nullable TransferListener listener) { this.context = context.getApplicationContext(); this.listener = listener; } + @Override + public void addTransferListener(TransferListener transferListener) { + } + @Override public long open(DataSpec dataSpec) throws IOException { this.uri = dataSpec.uri; @@ -45,7 +52,7 @@ public class PartDataSource implements DataSource { this.inputSteam = attachmentDatabase.getAttachmentStream(partUri.getPartId(), dataSpec.position); if (listener != null) { - listener.onTransferStart(this, dataSpec); + listener.onTransferStart(this, dataSpec, false); } if (attachment.getSize() - dataSpec.position <= 0) throw new EOFException("No more data"); @@ -58,7 +65,7 @@ public class PartDataSource implements DataSource { int read = inputSteam.read(buffer, offset, readLength); if (read > 0 && listener != null) { - listener.onBytesTransferred(this, read); + listener.onBytesTransferred(this, null, false, read); } return read; @@ -69,6 +76,11 @@ public class PartDataSource implements DataSource { return uri; } + @Override + public Map> getResponseHeaders() { + return Collections.emptyMap(); + } + @Override public void close() throws IOException { inputSteam.close();