mirror of
https://github.com/oxen-io/session-android.git
synced 2025-10-26 00:58:51 +00:00
Use exoplayer for playing video on API 16+ devices
// FREEBIE
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user