Pass through clip information to video player.

fork-5.53.8
Alex Hart 2022-10-12 14:49:27 -03:00 zatwierdzone przez Greyson Parrelli
rodzic 8c76cead58
commit 220931d3df
10 zmienionych plików z 61 dodań i 41 usunięć

Wyświetl plik

@ -13,9 +13,9 @@ import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
@ -54,7 +54,7 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener, De
this.policyEnforcer = policyEnforcer; this.policyEnforcer = policyEnforcer;
if (player.getExoPlayer() == null) { if (player.getExoPlayer() == null) {
SimpleExoPlayer fromPool = ApplicationDependencies.getExoPlayerPool().get(TAG); ExoPlayer fromPool = ApplicationDependencies.getExoPlayerPool().get(TAG);
if (fromPool == null) { if (fromPool == null) {
Log.i(TAG, "Could not get exoplayer from pool."); Log.i(TAG, "Could not get exoplayer from pool.");
@ -75,7 +75,7 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener, De
this.mediaItem = null; this.mediaItem = null;
this.policyEnforcer = null; this.policyEnforcer = null;
SimpleExoPlayer exoPlayer = player.getExoPlayer(); ExoPlayer exoPlayer = player.getExoPlayer();
if (exoPlayer != null) { if (exoPlayer != null) {
player.stop(); player.stop();
player.setExoPlayer(null); player.setExoPlayer(null);
@ -142,7 +142,7 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener, De
@Override @Override
public void onResume(@NonNull LifecycleOwner owner) { public void onResume(@NonNull LifecycleOwner owner) {
if (mediaItem != null) { if (mediaItem != null) {
SimpleExoPlayer fromPool = ApplicationDependencies.getExoPlayerPool().get(TAG); ExoPlayer fromPool = ApplicationDependencies.getExoPlayerPool().get(TAG);
if (fromPool != null) { if (fromPool != null) {
ExoPlayerKt.configureForGifPlayback(fromPool); ExoPlayerKt.configureForGifPlayback(fromPool);
fromPool.addListener(this); fromPool.addListener(this);

Wyświetl plik

@ -13,8 +13,8 @@ import androidx.annotation.Nullable;
import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.DefaultLifecycleObserver;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
@ -31,10 +31,10 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = Log.tag(GiphyMp4VideoPlayer.class); private static final String TAG = Log.tag(GiphyMp4VideoPlayer.class);
private final PlayerView exoView; private final PlayerView exoView;
private SimpleExoPlayer exoPlayer; private ExoPlayer exoPlayer;
private CornerMask cornerMask; private CornerMask cornerMask;
private MediaItem mediaItem; private MediaItem mediaItem;
public GiphyMp4VideoPlayer(Context context) { public GiphyMp4VideoPlayer(Context context) {
this(context, null); this(context, null);
@ -61,11 +61,11 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
} }
} }
@Nullable SimpleExoPlayer getExoPlayer() { @Nullable ExoPlayer getExoPlayer() {
return exoPlayer; return exoPlayer;
} }
void setExoPlayer(@Nullable SimpleExoPlayer exoPlayer) { void setExoPlayer(@Nullable ExoPlayer exoPlayer) {
exoView.setPlayer(exoPlayer); exoView.setPlayer(exoPlayer);
this.exoPlayer = exoPlayer; this.exoPlayer = exoPlayer;
} }

Wyświetl plik

@ -42,7 +42,7 @@ object NotificationStateProvider {
val parentRecord = conversationId.groupStoryId?.let { val parentRecord = conversationId.groupStoryId?.let {
try { try {
SignalDatabase.mms.getMessageRecord(it) SignalDatabase.mms.getMessageRecord(it)
} catch (e : NoSuchMessageException) { } catch (e: NoSuchMessageException) {
null null
} }
} }

Wyświetl plik

@ -43,6 +43,7 @@ import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.time.Duration.Companion.microseconds
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ -368,6 +369,7 @@ object Stories {
} }
private fun transformMedia(media: Media, transformProperties: AttachmentDatabase.TransformProperties): Media { private fun transformMedia(media: Media, transformProperties: AttachmentDatabase.TransformProperties): Media {
Log.d(TAG, "Transforming media clip: ${transformProperties.videoTrimStartTimeUs.microseconds.inWholeSeconds}s to ${transformProperties.videoTrimEndTimeUs.microseconds.inWholeSeconds}s")
return Media( return Media(
media.uri, media.uri,
media.mimeType, media.mimeType,

Wyświetl plik

@ -55,7 +55,7 @@ class StoryPostFragment : Fragment(R.layout.stories_post_fragment) {
postViewModel.onPostContentChanged(it) postViewModel.onPostContentChanged(it)
} }
disposables += postViewModel.state.subscribe { state -> disposables += postViewModel.state.distinctUntilChanged().subscribe { state ->
when (state) { when (state) {
is StoryPostState.None -> presentNone() is StoryPostState.None -> presentNone()
is StoryPostState.TextPost -> presentTextPost(state) is StoryPostState.TextPost -> presentTextPost(state)

Wyświetl plik

@ -5,6 +5,7 @@ import android.net.Uri
import org.thoughtcrime.securesms.blurhash.BlurHash import org.thoughtcrime.securesms.blurhash.BlurHash
import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.linkpreview.LinkPreview
import kotlin.time.Duration
sealed class StoryPostState { sealed class StoryPostState {
data class TextPost( data class TextPost(
@ -21,7 +22,9 @@ sealed class StoryPostState {
data class VideoPost( data class VideoPost(
val videoUri: Uri, val videoUri: Uri,
val size: Long val size: Long,
val clipStart: Duration,
val clipEnd: Duration
) : StoryPostState() ) : StoryPostState()
data class None(private val ts: Long = System.currentTimeMillis()) : StoryPostState() data class None(private val ts: Long = System.currentTimeMillis()) : StoryPostState()

Wyświetl plik

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.StoryTextPost
import org.thoughtcrime.securesms.stories.viewer.page.StoryPost import org.thoughtcrime.securesms.stories.viewer.page.StoryPost
import org.thoughtcrime.securesms.util.Base64 import org.thoughtcrime.securesms.util.Base64
import org.thoughtcrime.securesms.util.rx.RxStore import org.thoughtcrime.securesms.util.rx.RxStore
import kotlin.time.Duration.Companion.microseconds
class StoryPostViewModel(private val repository: StoryTextPostRepository) : ViewModel() { class StoryPostViewModel(private val repository: StoryTextPostRepository) : ViewModel() {
@ -38,7 +39,14 @@ class StoryPostViewModel(private val repository: StoryTextPostRepository) : View
if (storyPostContent.uri == null) { if (storyPostContent.uri == null) {
store.update { StoryPostState.None() } store.update { StoryPostState.None() }
} else if (storyPostContent.isVideo()) { } else if (storyPostContent.isVideo()) {
store.update { StoryPostState.VideoPost(videoUri = storyPostContent.uri, storyPostContent.attachment.size) } store.update {
StoryPostState.VideoPost(
videoUri = storyPostContent.uri,
size = storyPostContent.attachment.size,
clipStart = storyPostContent.attachment.transformProperties.videoTrimStartTimeUs.microseconds,
clipEnd = storyPostContent.attachment.transformProperties.videoTrimEndTimeUs.microseconds
)
}
} else { } else {
store.update { StoryPostState.ImagePost(storyPostContent.uri, storyPostContent.attachment.blurHash) } store.update { StoryPostState.ImagePost(storyPostContent.uri, storyPostContent.attachment.blurHash) }
} }

Wyświetl plik

@ -22,7 +22,7 @@ class StoryVideoLoader(
fun load() { fun load() {
fragment.viewLifecycleOwner.lifecycle.addObserver(this) fragment.viewLifecycleOwner.lifecycle.addObserver(this)
videoPlayer.setVideoSource(VideoSlide(fragment.requireContext(), videoPost.videoUri, videoPost.size, false), true, TAG) videoPlayer.setVideoSource(VideoSlide(fragment.requireContext(), videoPost.videoUri, videoPost.size, false), true, TAG, videoPost.clipStart.inWholeMilliseconds, videoPost.clipEnd.inWholeMilliseconds)
} }
fun clear() { fun clear() {

Wyświetl plik

@ -26,10 +26,12 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Tracks;
import com.google.android.exoplayer2.source.ClippingMediaSource; import com.google.android.exoplayer2.source.ClippingMediaSource;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory; import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
@ -56,7 +58,7 @@ public class VideoPlayer extends FrameLayout {
private final PlayerControlView exoControls; private final PlayerControlView exoControls;
private final DefaultMediaSourceFactory mediaSourceFactory; private final DefaultMediaSourceFactory mediaSourceFactory;
private SimpleExoPlayer exoPlayer; private ExoPlayer exoPlayer;
private Window window; private Window window;
private PlayerStateCallback playerStateCallback; private PlayerStateCallback playerStateCallback;
private PlayerPositionDiscontinuityCallback playerPositionDiscontinuityCallback; private PlayerPositionDiscontinuityCallback playerPositionDiscontinuityCallback;
@ -125,6 +127,10 @@ public class VideoPlayer extends FrameLayout {
private MediaItem mediaItem; private MediaItem mediaItem;
public void setVideoSource(@NonNull VideoSlide videoSource, boolean autoplay, String poolTag) { public void setVideoSource(@NonNull VideoSlide videoSource, boolean autoplay, String poolTag) {
setVideoSource(videoSource, autoplay, poolTag, 0, 0);
}
public void setVideoSource(@NonNull VideoSlide videoSource, boolean autoplay, String poolTag, long clipStartMs, long clipEndMs) {
if (exoPlayer == null) { if (exoPlayer == null) {
exoPlayer = ApplicationDependencies.getExoPlayerPool().require(poolTag); exoPlayer = ApplicationDependencies.getExoPlayerPool().require(poolTag);
exoPlayer.addListener(exoPlayerListener); exoPlayer.addListener(exoPlayerListener);
@ -136,7 +142,10 @@ public class VideoPlayer extends FrameLayout {
} }
} }
mediaItem = MediaItem.fromUri(Objects.requireNonNull(videoSource.getUri())); mediaItem = MediaItem.fromUri(Objects.requireNonNull(videoSource.getUri())).buildUpon()
.setClippingConfiguration(getClippingConfiguration(clipStartMs, clipEndMs))
.build();
exoPlayer.setMediaItem(mediaItem); exoPlayer.setMediaItem(mediaItem);
exoPlayer.prepare(); exoPlayer.prepare();
exoPlayer.setPlayWhenReady(autoplay); exoPlayer.setPlayWhenReady(autoplay);
@ -144,31 +153,22 @@ public class VideoPlayer extends FrameLayout {
public void mute() { public void mute() {
this.muted = true; this.muted = true;
if (exoPlayer != null && exoPlayer.getAudioComponent() != null) { if (exoPlayer != null) {
exoPlayer.getAudioComponent().setVolume(0f); exoPlayer.setVolume(0f);
} }
} }
public void unmute() { public void unmute() {
this.muted = false; this.muted = false;
if (exoPlayer != null && exoPlayer.getAudioComponent() != null) { if (exoPlayer != null) {
exoPlayer.getAudioComponent().setVolume(1f); exoPlayer.setVolume(1f);
} }
} }
public boolean hasAudioTrack() { public boolean hasAudioTrack() {
if (exoPlayer != null) { if (exoPlayer != null) {
TrackGroupArray trackGroupArray = exoPlayer.getCurrentTrackGroups(); Tracks tracks = exoPlayer.getCurrentTracks();
if (trackGroupArray != null) { return tracks.containsType(C.TRACK_TYPE_AUDIO);
for (int i = 0; i < trackGroupArray.length; i++) {
for (int j = 0; j < trackGroupArray.get(i).length; j++) {
String sampleMimeType = trackGroupArray.get(i).getFormat(j).sampleMimeType;
if (MediaUtil.isAudioType(sampleMimeType)) {
return true;
}
}
}
}
} }
return false; return false;
@ -317,6 +317,14 @@ public class VideoPlayer extends FrameLayout {
} }
} }
private @NonNull MediaItem.ClippingConfiguration getClippingConfiguration(long startMs, long endMs) {
return startMs != endMs ? new MediaItem.ClippingConfiguration.Builder()
.setStartPositionMs(startMs)
.setEndPositionMs(endMs)
.build()
: MediaItem.ClippingConfiguration.UNSET;
}
private class ExoPlayerListener implements Player.Listener { private class ExoPlayerListener implements Player.Listener {
@Override @Override

Wyświetl plik

@ -3,11 +3,10 @@ package org.thoughtcrime.securesms.video.exo
import android.content.Context import android.content.Context
import androidx.annotation.MainThread import androidx.annotation.MainThread
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil import com.google.android.exoplayer2.mediacodec.MediaCodecUtil
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException
import com.google.android.exoplayer2.source.MediaSourceFactory import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.util.MimeTypes import com.google.android.exoplayer2.util.MimeTypes
import org.signal.core.util.logging.Log import org.signal.core.util.logging.Log
@ -19,11 +18,11 @@ import org.thoughtcrime.securesms.util.DeviceProperties
/** /**
* ExoPlayerPool concrete instance which helps to manage a pool of SimpleExoPlayer objects * ExoPlayerPool concrete instance which helps to manage a pool of SimpleExoPlayer objects
*/ */
class SimpleExoPlayerPool(context: Context) : ExoPlayerPool<SimpleExoPlayer>(MAXIMUM_RESERVED_PLAYERS) { class SimpleExoPlayerPool(context: Context) : ExoPlayerPool<ExoPlayer>(MAXIMUM_RESERVED_PLAYERS) {
private val context: Context = context.applicationContext private val context: Context = context.applicationContext
private val okHttpClient = ApplicationDependencies.getOkHttpClient().newBuilder().proxySelector(ContentProxySelector()).build() private val okHttpClient = ApplicationDependencies.getOkHttpClient().newBuilder().proxySelector(ContentProxySelector()).build()
private val dataSourceFactory: DataSource.Factory = SignalDataSource.Factory(ApplicationDependencies.getApplication(), okHttpClient, null) private val dataSourceFactory: DataSource.Factory = SignalDataSource.Factory(ApplicationDependencies.getApplication(), okHttpClient, null)
private val mediaSourceFactory: MediaSourceFactory = ProgressiveMediaSource.Factory(dataSourceFactory) private val mediaSourceFactory: MediaSource.Factory = DefaultMediaSourceFactory(dataSourceFactory)
init { init {
ApplicationDependencies.getAppForegroundObserver().addListener(this) ApplicationDependencies.getAppForegroundObserver().addListener(this)
@ -57,8 +56,8 @@ class SimpleExoPlayerPool(context: Context) : ExoPlayerPool<SimpleExoPlayer>(MAX
} }
@MainThread @MainThread
override fun createPlayer(): SimpleExoPlayer { override fun createPlayer(): ExoPlayer {
return SimpleExoPlayer.Builder(context) return ExoPlayer.Builder(context)
.setMediaSourceFactory(mediaSourceFactory) .setMediaSourceFactory(mediaSourceFactory)
.build() .build()
} }