mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-23 08:27:30 +00:00
Use exoplayer for playing video on API 16+ devices
// FREEBIE
This commit is contained in:
parent
fad697ba2a
commit
4fd41080ac
@ -57,6 +57,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:r2.3.1'
|
||||
|
||||
compile 'org.whispersystems:jobmanager:1.0.2'
|
||||
compile 'org.whispersystems:libpastelog:1.0.7'
|
||||
compile 'org.whispersystems:signal-service-android:2.5.5'
|
||||
@ -127,6 +129,7 @@ dependencyVerification {
|
||||
'com.google.android.gms:play-services-gcm:312e61253a236f2d9b750b9c04fc92fd190d23b0b2755c99de6ce4a28b259dae',
|
||||
'com.google.android.gms:play-services-maps:45e8021e7ddac4a44a82a0e9698991389ded3023d35c58f38dbd86d54211ec0e',
|
||||
'com.google.android.gms:play-services-places:abf3a4a3b146ec7e6e753be62775e512868cf37d6f88ffe2d81167b33b57132b',
|
||||
'com.google.android.exoplayer:exoplayer:955085aa611a8f7cf6c61b88ae03d1a392f4ad94c9bfbc153f3dedb9ffb14718',
|
||||
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
|
||||
'org.whispersystems:libpastelog:bb331d9a98240fc139101128ba836c1edec3c40e000597cdbb29ebf4cbf34d88',
|
||||
'org.whispersystems:signal-service-android:3d7859b194e518fbaf5a082daf22ca345411705e825791f751eb388f149583c3',
|
||||
@ -154,7 +157,6 @@ dependencyVerification {
|
||||
'com.davemorrissey.labs:subsampling-scale-image-view:550c5baa07e0bb4ff0a18b705e96d34436d22619248bd8c08c08c730b1f55cfe',
|
||||
'cn.carbswang.android:NumberPickerView:18b3c316d62c7c277978a8d4ed57a5b8f4e943762264960f579a8a549c756729',
|
||||
'com.tomergoldst.android:tooltips:4c56697dd1ad64b8066535c61f961a6d901e7ae5d97ae27084ba40ad620349b6',
|
||||
'com.android.support:support-annotations:fb941680f43afbd70ce01ec3cc837a5037f0a774701b12a9fd3090bd4727cf15',
|
||||
'com.android.support:support-v4:ed4cda7c752f51d33f9bbdfff3422b425b323d356cd1bdc9786aa413c912e594',
|
||||
'com.android.support:support-vector-drawable:2697503d3e8e709023ae176ba5db7f98ca0aa0b4e6290aedcb3c371904806bf7',
|
||||
'com.android.support:animated-vector-drawable:6d05cb63d1f68900220f85c56dfe1066a9bb19cb0ec1247cc68fc2ba32f6b4a7',
|
||||
@ -180,6 +182,7 @@ dependencyVerification {
|
||||
'com.fasterxml.jackson.core:jackson-annotations:0ca408c24202a7626ec8b861e99d85eca5e38b73311dd6dd12e3e9deecc3fe94',
|
||||
'com.fasterxml.jackson.core:jackson-core:cbf4604784b4de226262845447a1ad3bb38a6728cebe86562e2c5afada8be2c0',
|
||||
'com.squareup.okio:okio:8c5436cadfab36bbd97db5f5c43b7bfdb5bf2f5f894ec8709b1929f14bdd010c',
|
||||
'com.android.support:support-annotations:47a2a30eab487a490a8a8f16678007c3d2b6dcae1e09b0485a12bbf921200ec3',
|
||||
'com.android.support:support-media-compat:8d6a1a5ba3d9eb1a25cb8f21bb312ac6280202e3d2900cb0b447d065d0d8a125',
|
||||
'com.android.support:support-core-utils:a7649e18c04143dde40c218c5ce9a030e7ae674089cd7b18c6cf8ed2a22cf01a',
|
||||
'com.android.support:support-fragment:1294500b357f52cf3779e2521c79f54ae7844f3b9a5f6727495dbbda7f231377',
|
||||
|
14
res/layout-v16/video_player.xml
Normal file
14
res/layout-v16/video_player.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
|
||||
android:id="@+id/video_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"/>
|
||||
|
||||
</FrameLayout>
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.video;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
@ -11,12 +12,30 @@ import android.widget.MediaController;
|
||||
import android.widget.Toast;
|
||||
import android.widget.VideoView;
|
||||
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.LoadControl;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
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;
|
||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
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;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentServer;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.mms.VideoSlide;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.video.exo.AttachmentDataSourceFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@ -24,8 +43,11 @@ public class VideoPlayer extends FrameLayout {
|
||||
|
||||
private static final String TAG = VideoPlayer.class.getName();
|
||||
|
||||
@NonNull private final VideoView videoView;
|
||||
@Nullable private AttachmentServer attachmentServer;
|
||||
@Nullable private final VideoView videoView;
|
||||
@Nullable private final SimpleExoPlayerView exoView;
|
||||
|
||||
@Nullable private SimpleExoPlayer exoPlayer;
|
||||
@Nullable private AttachmentServer attachmentServer;
|
||||
|
||||
public VideoPlayer(Context context) {
|
||||
this(context, null);
|
||||
@ -40,12 +62,57 @@ public class VideoPlayer extends FrameLayout {
|
||||
|
||||
inflate(context, R.layout.video_player, this);
|
||||
|
||||
this.videoView = ViewUtil.findById(this, R.id.video_view);
|
||||
|
||||
initializeVideoViewControls(videoView);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void setVideoSource(@NonNull MasterSecret masterSecret, @NonNull VideoSlide videoSource) throws IOException {
|
||||
public void setVideoSource(@NonNull MasterSecret masterSecret, @NonNull VideoSlide videoSource)
|
||||
throws IOException
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= 14) setExoViewSource(masterSecret, videoSource);
|
||||
else setVideoViewSource(masterSecret, videoSource);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
if (this.attachmentServer != null) {
|
||||
this.attachmentServer.stop();
|
||||
}
|
||||
|
||||
if (this.exoPlayer != null) {
|
||||
this.exoPlayer.release();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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
|
||||
{
|
||||
if (this.attachmentServer != null) {
|
||||
this.attachmentServer.stop();
|
||||
}
|
||||
@ -67,12 +134,6 @@ public class VideoPlayer extends FrameLayout {
|
||||
this.videoView.start();
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
if (this.attachmentServer != null) {
|
||||
this.attachmentServer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeVideoViewControls(@NonNull VideoView videoView) {
|
||||
MediaController mediaController = new MediaController(getContext());
|
||||
mediaController.setAnchorView(videoView);
|
||||
|
@ -0,0 +1,48 @@
|
||||
package org.thoughtcrime.securesms.video.exo;
|
||||
|
||||
|
||||
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 org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AttachmentDataSource implements DataSource {
|
||||
|
||||
private final DefaultDataSource defaultDataSource;
|
||||
private final PartDataSource partDataSource;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
public AttachmentDataSource(DefaultDataSource defaultDataSource, PartDataSource partDataSource) {
|
||||
this.defaultDataSource = defaultDataSource;
|
||||
this.partDataSource = partDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long open(DataSpec dataSpec) throws IOException {
|
||||
if (PartAuthority.isLocalUri(dataSpec.uri)) dataSource = partDataSource;
|
||||
else dataSource = defaultDataSource;
|
||||
|
||||
return dataSource.open(dataSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int readLength) throws IOException {
|
||||
return dataSource.read(buffer, offset, readLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return dataSource.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package org.thoughtcrime.securesms.video.exo;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
|
||||
public class AttachmentDataSourceFactory implements DataSource.Factory {
|
||||
|
||||
private final Context context;
|
||||
private final MasterSecret masterSecret;
|
||||
|
||||
private final DefaultDataSourceFactory defaultDataSourceFactory;
|
||||
private final TransferListener<? super DataSource> listener;
|
||||
|
||||
public AttachmentDataSourceFactory(@NonNull Context context, @NonNull MasterSecret masterSecret,
|
||||
@NonNull DefaultDataSourceFactory defaultDataSourceFactory,
|
||||
@Nullable TransferListener<? super DataSource> listener)
|
||||
{
|
||||
this.context = context;
|
||||
this.masterSecret = masterSecret;
|
||||
this.defaultDataSourceFactory = defaultDataSourceFactory;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttachmentDataSource createDataSource() {
|
||||
return new AttachmentDataSource(defaultDataSourceFactory.createDataSource(),
|
||||
new PartDataSource(context, masterSecret, listener));
|
||||
}
|
||||
}
|
88
src/org/thoughtcrime/securesms/video/exo/PartDataSource.java
Normal file
88
src/org/thoughtcrime/securesms/video/exo/PartDataSource.java
Normal file
@ -0,0 +1,88 @@
|
||||
package org.thoughtcrime.securesms.video.exo;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.mms.PartUriParser;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class PartDataSource implements DataSource {
|
||||
|
||||
private final @NonNull Context context;
|
||||
private final @NonNull MasterSecret masterSecret;
|
||||
private final @Nullable TransferListener<? super PartDataSource> listener;
|
||||
|
||||
private Uri uri;
|
||||
private InputStream inputSteam;
|
||||
|
||||
public PartDataSource(@NonNull Context context,
|
||||
@NonNull MasterSecret masterSecret,
|
||||
@Nullable TransferListener<? super PartDataSource> listener)
|
||||
{
|
||||
this.context = context.getApplicationContext();
|
||||
this.masterSecret = masterSecret;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long open(DataSpec dataSpec) throws IOException {
|
||||
this.uri = dataSpec.uri;
|
||||
|
||||
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
|
||||
PartUriParser partUri = new PartUriParser(uri);
|
||||
Attachment attachment = attachmentDatabase.getAttachment(masterSecret, partUri.getPartId());
|
||||
|
||||
if (attachment == null) throw new IOException("Attachment not found");
|
||||
|
||||
this.inputSteam = attachmentDatabase.getAttachmentStream(masterSecret, partUri.getPartId());
|
||||
|
||||
if (inputSteam == null) throw new IOException("InputStream not foudn");
|
||||
|
||||
long skipped = this.inputSteam.skip(dataSpec.position);
|
||||
|
||||
if (skipped != dataSpec.position) throw new IOException("Skip failed!");
|
||||
|
||||
if (listener != null) {
|
||||
listener.onTransferStart(this, dataSpec);
|
||||
}
|
||||
|
||||
if (attachment.getSize() - dataSpec.position <= 0) throw new EOFException("No more data");
|
||||
|
||||
return attachment.getSize() - dataSpec.position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int readLength) throws IOException {
|
||||
int read = inputSteam.read(buffer, offset, readLength);
|
||||
|
||||
if (read > 0 && listener != null) {
|
||||
listener.onBytesTransferred(this, read);
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
inputSteam.close();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user