diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1c4971fe6f..223824b79a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -40,7 +40,6 @@ even if they are listed lower in the `MediaCodecList`. * Add a workaround for broken raw audio decoding on Oppo R9 ([#5782](https://github.com/google/ExoPlayer/issues/5782)). -* Add VR player demo. * Wrap decoder exceptions in a new `DecoderException` class and report as renderer error. * Do not pass the manifest to callbacks of `Player.EventListener` and @@ -99,6 +98,7 @@ [#6315](https://github.com/google/ExoPlayer/issues/6315) and [#5658](https://github.com/google/ExoPlayer/issues/5658)). * Pass the codec output `MediaFormat` to `VideoFrameMetadataListener`. +* Deprecate the GVR extension. ### 2.10.6 (2019-10-17) ### diff --git a/demos/gvr/README.md b/demos/gvr/README.md deleted file mode 100644 index 8cc52c5f10..0000000000 --- a/demos/gvr/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# ExoPlayer VR player demo # - -This folder contains a demo application that showcases 360 video playback using -ExoPlayer GVR extension. diff --git a/demos/gvr/build.gradle b/demos/gvr/build.gradle deleted file mode 100644 index 96c699b2e6..0000000000 --- a/demos/gvr/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2019 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -apply from: '../../constants.gradle' -apply plugin: 'com.android.application' - -android { - compileSdkVersion project.ext.compileSdkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - defaultConfig { - versionName project.ext.releaseVersion - versionCode project.ext.releaseVersionCode - minSdkVersion 19 - targetSdkVersion project.ext.targetSdkVersion - } - - buildTypes { - release { - shrinkResources true - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt') - } - debug { - jniDebuggable = true - } - } - - lintOptions { - // The demo app isn't indexed and doesn't have translations. - disable 'GoogleAppIndexingWarning','MissingTranslation' - } -} - -dependencies { - implementation project(modulePrefix + 'library-core') - implementation project(modulePrefix + 'library-ui') - implementation project(modulePrefix + 'library-dash') - implementation project(modulePrefix + 'library-hls') - implementation project(modulePrefix + 'library-smoothstreaming') - implementation project(modulePrefix + 'extension-gvr') - implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion -} - -apply plugin: 'com.google.android.gms.strict-version-matcher-plugin' diff --git a/demos/gvr/src/main/AndroidManifest.xml b/demos/gvr/src/main/AndroidManifest.xml deleted file mode 100644 index ccdcae9cae..0000000000 --- a/demos/gvr/src/main/AndroidManifest.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/PlayerActivity.java b/demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/PlayerActivity.java deleted file mode 100644 index e236c85a26..0000000000 --- a/demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/PlayerActivity.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.gvrdemo; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.widget.Toast; -import androidx.annotation.Nullable; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.C.ContentType; -import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.ext.gvr.GvrPlayerActivity; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.ProgressiveMediaSource; -import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.source.dash.DashMediaSource; -import com.google.android.exoplayer2.source.hls.HlsMediaSource; -import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; -import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; -import com.google.android.exoplayer2.util.EventLogger; -import com.google.android.exoplayer2.util.Util; - -/** An activity that plays media using {@link SimpleExoPlayer}. */ -public class PlayerActivity extends GvrPlayerActivity { - - public static final String EXTENSION_EXTRA = "extension"; - - public static final String SPHERICAL_STEREO_MODE_EXTRA = "spherical_stereo_mode"; - public static final String SPHERICAL_STEREO_MODE_MONO = "mono"; - public static final String SPHERICAL_STEREO_MODE_TOP_BOTTOM = "top_bottom"; - public static final String SPHERICAL_STEREO_MODE_LEFT_RIGHT = "left_right"; - - private SimpleExoPlayer player; - private DefaultTrackSelector trackSelector; - private TrackGroupArray lastSeenTrackGroupArray; - private boolean startAutoPlay; - private int startWindow; - private long startPosition; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - String sphericalStereoMode = getIntent().getStringExtra(SPHERICAL_STEREO_MODE_EXTRA); - if (sphericalStereoMode != null) { - int stereoMode; - if (SPHERICAL_STEREO_MODE_MONO.equals(sphericalStereoMode)) { - stereoMode = C.STEREO_MODE_MONO; - } else if (SPHERICAL_STEREO_MODE_TOP_BOTTOM.equals(sphericalStereoMode)) { - stereoMode = C.STEREO_MODE_TOP_BOTTOM; - } else if (SPHERICAL_STEREO_MODE_LEFT_RIGHT.equals(sphericalStereoMode)) { - stereoMode = C.STEREO_MODE_LEFT_RIGHT; - } else { - showToast(R.string.error_unrecognized_stereo_mode); - finish(); - return; - } - setDefaultStereoMode(stereoMode); - } - - clearStartPosition(); - } - - @Override - protected Player createPlayer() { - Intent intent = getIntent(); - Uri uri = intent.getData(); - DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this); - - trackSelector = new DefaultTrackSelector(/* context= */ this); - lastSeenTrackGroupArray = null; - - player = - new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory) - .setTrackSelector(trackSelector) - .build(); - player.addListener(new PlayerEventListener()); - player.setPlayWhenReady(startAutoPlay); - player.addAnalyticsListener(new EventLogger(trackSelector)); - MediaSource mediaSource = buildMediaSource(uri, intent.getStringExtra(EXTENSION_EXTRA)); - boolean haveStartPosition = startWindow != C.INDEX_UNSET; - if (haveStartPosition) { - player.seekTo(startWindow, startPosition); - } - player.prepare(mediaSource, !haveStartPosition, false); - return player; - } - - private MediaSource buildMediaSource(Uri uri, @Nullable String overrideExtension) { - String userAgent = Util.getUserAgent(this, "ExoPlayerVrDemo"); - DefaultDataSourceFactory dataSourceFactory = - new DefaultDataSourceFactory(this, new DefaultHttpDataSourceFactory(userAgent)); - @ContentType int type = Util.inferContentType(uri, overrideExtension); - switch (type) { - case C.TYPE_DASH: - return new DashMediaSource.Factory(dataSourceFactory).createMediaSource(uri); - case C.TYPE_SS: - return new SsMediaSource.Factory(dataSourceFactory).createMediaSource(uri); - case C.TYPE_HLS: - return new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(uri); - case C.TYPE_OTHER: - return new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri); - default: - throw new IllegalStateException("Unsupported type: " + type); - } - } - - private void updateStartPosition() { - if (player != null) { - startAutoPlay = player.getPlayWhenReady(); - startWindow = player.getCurrentWindowIndex(); - startPosition = Math.max(0, player.getContentPosition()); - } - } - - private void clearStartPosition() { - startAutoPlay = true; - startWindow = C.INDEX_UNSET; - startPosition = C.TIME_UNSET; - } - - private void showToast(int messageId) { - showToast(getString(messageId)); - } - - private void showToast(String message) { - Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); - } - - private class PlayerEventListener implements Player.EventListener { - - @Override - public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {} - - @Override - public void onPositionDiscontinuity(@Player.DiscontinuityReason int reason) { - if (player.getPlaybackError() != null) { - // The user has performed a seek whilst in the error state. Update the resume position so - // that if the user then retries, playback resumes from the position to which they seeked. - updateStartPosition(); - } - } - - @Override - public void onPlayerError(ExoPlaybackException e) { - updateStartPosition(); - } - - @Override - @SuppressWarnings("ReferenceEquality") - public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { - if (trackGroups != lastSeenTrackGroupArray) { - MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo(); - if (mappedTrackInfo != null) { - if (mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_VIDEO) - == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) { - showToast(R.string.error_unsupported_video); - } - if (mappedTrackInfo.getTypeSupport(C.TRACK_TYPE_AUDIO) - == MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS) { - showToast(R.string.error_unsupported_audio); - } - } - lastSeenTrackGroupArray = trackGroups; - } - } - } -} diff --git a/demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/SampleChooserActivity.java b/demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/SampleChooserActivity.java deleted file mode 100644 index 0694f072ee..0000000000 --- a/demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/SampleChooserActivity.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.gvrdemo; - -import static com.google.android.exoplayer2.gvrdemo.PlayerActivity.SPHERICAL_STEREO_MODE_LEFT_RIGHT; -import static com.google.android.exoplayer2.gvrdemo.PlayerActivity.SPHERICAL_STEREO_MODE_MONO; -import static com.google.android.exoplayer2.gvrdemo.PlayerActivity.SPHERICAL_STEREO_MODE_TOP_BOTTOM; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.widget.ArrayAdapter; -import android.widget.ListView; - -/** An activity for selecting from a list of media samples. */ -public class SampleChooserActivity extends Activity { - - private final Sample[] samples = - new Sample[] { - new Sample( - "Congo (360 top-bottom stereo)", - "https://storage.googleapis.com/exoplayer-test-media-1/360/congo.mp4", - SPHERICAL_STEREO_MODE_TOP_BOTTOM), - new Sample( - "Sphericalv2 (180 top-bottom stereo)", - "https://storage.googleapis.com/exoplayer-test-media-1/360/sphericalv2.mp4", - SPHERICAL_STEREO_MODE_TOP_BOTTOM), - new Sample( - "Iceland (360 top-bottom stereo ts)", - "https://storage.googleapis.com/exoplayer-test-media-1/360/iceland0.ts", - SPHERICAL_STEREO_MODE_TOP_BOTTOM), - }; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.sample_chooser_activity); - ListView sampleListView = findViewById(R.id.sample_list); - sampleListView.setAdapter( - new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, samples)); - sampleListView.setOnItemClickListener( - (parent, view, position, id) -> - startActivity( - samples[position].buildIntent(/* context= */ SampleChooserActivity.this))); - } - - private static final class Sample { - public final String name; - public final String uri; - public final String extension; - public final String sphericalStereoMode; - - public Sample(String name, String uri, String sphericalStereoMode) { - this(name, uri, sphericalStereoMode, null); - } - - public Sample(String name, String uri, String sphericalStereoMode, String extension) { - this.name = name; - this.uri = uri; - this.extension = extension; - this.sphericalStereoMode = sphericalStereoMode; - } - - public Intent buildIntent(Context context) { - Intent intent = new Intent(context, PlayerActivity.class); - return intent - .setData(Uri.parse(uri)) - .putExtra(PlayerActivity.EXTENSION_EXTRA, extension) - .putExtra(PlayerActivity.SPHERICAL_STEREO_MODE_EXTRA, sphericalStereoMode); - } - - @Override - public String toString() { - return name; - } - } -} diff --git a/demos/gvr/src/main/res/layout/sample_chooser_activity.xml b/demos/gvr/src/main/res/layout/sample_chooser_activity.xml deleted file mode 100644 index ce520e70e4..0000000000 --- a/demos/gvr/src/main/res/layout/sample_chooser_activity.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/demos/gvr/src/main/res/mipmap-hdpi/ic_launcher.png b/demos/gvr/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index adaa93220e..0000000000 Binary files a/demos/gvr/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/demos/gvr/src/main/res/mipmap-mdpi/ic_launcher.png b/demos/gvr/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 9b6f7d5e80..0000000000 Binary files a/demos/gvr/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/demos/gvr/src/main/res/mipmap-xhdpi/ic_launcher.png b/demos/gvr/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 2101026c9f..0000000000 Binary files a/demos/gvr/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/demos/gvr/src/main/res/mipmap-xxhdpi/ic_launcher.png b/demos/gvr/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 223ec8bd11..0000000000 Binary files a/demos/gvr/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/demos/gvr/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/demos/gvr/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 698ed68c42..0000000000 Binary files a/demos/gvr/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/demos/gvr/src/main/res/values/strings.xml b/demos/gvr/src/main/res/values/strings.xml deleted file mode 100644 index 08feccb398..0000000000 --- a/demos/gvr/src/main/res/values/strings.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - ExoPlayer VR Demo - - Cleartext traffic not permitted - - Unrecognized stereo mode - - Media includes video tracks, but none are playable by this device - - Media includes audio tracks, but none are playable by this device - - diff --git a/extensions/gvr/README.md b/extensions/gvr/README.md index 1874ff77d7..43a9e2cb62 100644 --- a/extensions/gvr/README.md +++ b/extensions/gvr/README.md @@ -1,11 +1,15 @@ # ExoPlayer GVR extension # +**DEPRECATED - If you still need this extension, please contact us by filing an +issue on our [issue tracker][].** + The GVR extension wraps the [Google VR SDK for Android][]. It provides a GvrAudioProcessor, which uses [GvrAudioSurround][] to provide binaural rendering of surround sound and ambisonic soundfields. [Google VR SDK for Android]: https://developers.google.com/vr/android/ [GvrAudioSurround]: https://developers.google.com/vr/android/reference/com/google/vr/sdk/audio/GvrAudioSurround +[issue tracker]: https://github.com/google/ExoPlayer/issues ## Getting the extension ## diff --git a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java index 02e4328ec7..412eb58ad3 100644 --- a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java +++ b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java @@ -28,7 +28,11 @@ import java.nio.ByteOrder; /** * An {@link AudioProcessor} that uses {@code GvrAudioSurround} to provide binaural rendering of * surround sound and ambisonic soundfields. + * + * @deprecated If you still need this component, please contact us by filing an issue on our issue tracker. */ +@Deprecated public final class GvrAudioProcessor implements AudioProcessor { static { diff --git a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrPlayerActivity.java b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrPlayerActivity.java deleted file mode 100644 index 5491707c30..0000000000 --- a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrPlayerActivity.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.ext.gvr; - -import android.content.Context; -import android.content.Intent; -import android.graphics.SurfaceTexture; -import android.opengl.Matrix; -import android.os.Bundle; -import android.view.ContextThemeWrapper; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.Surface; -import android.view.View; -import androidx.annotation.BinderThread; -import androidx.annotation.CallSuper; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.ui.PlayerControlView; -import com.google.android.exoplayer2.ui.spherical.PointerRenderer; -import com.google.android.exoplayer2.ui.spherical.SceneRenderer; -import com.google.android.exoplayer2.ui.spherical.ViewRenderer; -import com.google.android.exoplayer2.util.Assertions; -import com.google.vr.ndk.base.DaydreamApi; -import com.google.vr.sdk.base.AndroidCompat; -import com.google.vr.sdk.base.Eye; -import com.google.vr.sdk.base.GvrActivity; -import com.google.vr.sdk.base.GvrView; -import com.google.vr.sdk.base.HeadTransform; -import com.google.vr.sdk.base.Viewport; -import com.google.vr.sdk.controller.Controller; -import com.google.vr.sdk.controller.ControllerManager; -import com.google.vr.sdk.controller.Orientation; -import javax.microedition.khronos.egl.EGLConfig; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -/** Base activity for VR 360 video playback. */ -public abstract class GvrPlayerActivity extends GvrActivity { - - private static final int EXIT_FROM_VR_REQUEST_CODE = 42; - - @Nullable private Player player; - private @MonotonicNonNull ControllerManager controllerManager; - private @MonotonicNonNull SurfaceTexture surfaceTexture; - private @MonotonicNonNull Surface surface; - private @MonotonicNonNull SceneRenderer sceneRenderer; - private @MonotonicNonNull PlayerControlView playerControlView; - - @CallSuper - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setScreenAlwaysOn(true); - - GvrView gvrView = new GvrView(/* context= */ this); - gvrView.setRenderTargetScale(getRenderTargetScale()); - - // If a custom theme isn't specified, the Context's theme is used. For VR Activities, this is - // the old Android default theme rather than a modern theme. Override this with a custom theme. - Context theme = new ContextThemeWrapper(this, R.style.ExoVrTheme); - View viewGroup = LayoutInflater.from(theme).inflate(R.layout.exo_vr_ui, /* root= */ null); - - ViewRenderer viewRenderer = new ViewRenderer(/* context= */ this, gvrView, viewGroup); - - playerControlView = Assertions.checkNotNull(viewGroup.findViewById(R.id.controller)); - playerControlView.setShowVrButton(true); - playerControlView.setVrButtonListener(v -> exit()); - - sceneRenderer = new SceneRenderer(); - PointerRenderer pointerRenderer = new PointerRenderer(); - Renderer renderer = new Renderer(sceneRenderer, pointerRenderer, viewRenderer); - - // Standard GvrView configuration - gvrView.setEGLConfigChooser( - 8, 8, 8, 8, // RGBA bits. - 16, // Depth bits. - 0); // Stencil bits. - gvrView.setRenderer(renderer); - setContentView(gvrView); - - if (gvrView.setAsyncReprojectionEnabled(true)) { - AndroidCompat.setSustainedPerformanceMode(/* activity= */ this, true); - } - - // Handle the user clicking on the 'X' in the top left corner. Since this is done when the user - // has taken the headset out of VR, it should launch the app's exit flow directly rather than - // using Daydream's exit transition. - gvrView.setOnCloseButtonListener(this::finish); - - controllerManager = - new ControllerManager(/* context= */ this, new ControllerManagerEventListener()); - Controller controller = controllerManager.getController(); - ControllerEventListener controllerEventListener = - new ControllerEventListener(controller, pointerRenderer, viewRenderer); - controller.setEventListener(controllerEventListener); - } - - @CallSuper - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent unused) { - if (requestCode == EXIT_FROM_VR_REQUEST_CODE && resultCode == RESULT_OK) { - finish(); - } - } - - @CallSuper - @Override - protected void onResume() { - super.onResume(); - player = createPlayer(); - Player.VideoComponent videoComponent = player.getVideoComponent(); - if (videoComponent != null) { - videoComponent.setVideoFrameMetadataListener(Assertions.checkNotNull(sceneRenderer)); - videoComponent.setCameraMotionListener(sceneRenderer); - videoComponent.setVideoSurface(surface); - } - Assertions.checkNotNull(playerControlView).setPlayer(player); - Assertions.checkNotNull(controllerManager).start(); - } - - @CallSuper - @Override - protected void onPause() { - Assertions.checkNotNull(controllerManager).stop(); - Assertions.checkNotNull(playerControlView).setPlayer(null); - Assertions.checkNotNull(player).release(); - player = null; - super.onPause(); - } - - @CallSuper - @Override - protected void onDestroy() { - releaseSurface(surfaceTexture, surface); - super.onDestroy(); - } - - /** - * Called by {@link #onCreate(Bundle)} to get the render target scale value that will be passed to - * {@link GvrView#setRenderTargetScale(float)}. Since videos typically have fewer pixels per - * degree than the phone displays, the target can normally be lower than 1 to reduce the amount of - * work required to render the scene. The default value is 0.5. - * - * @return The render target scale value that will be passed to {@link - * GvrView#setRenderTargetScale(float)}. - */ - protected float getRenderTargetScale() { - return 0.5f; - } - - /** Called by {@link #onResume()} to create a player instance for this activity to use. */ - protected abstract Player createPlayer(); - - /** - * Sets the stereo mode that will be used for video content that does not specify its own mode. - * - * @param stereoMode The default {@link C.StereoMode}. - */ - protected void setDefaultStereoMode(@C.StereoMode int stereoMode) { - Assertions.checkNotNull(sceneRenderer).setDefaultStereoMode(stereoMode); - } - - /** Tries to exit gracefully from VR using a VR transition dialog. */ - @SuppressWarnings("nullness:argument.type.incompatible") - protected void exit() { - DaydreamApi daydreamApi = DaydreamApi.create(this); - if (daydreamApi != null) { - // Use Daydream's exit transition to avoid disorienting the user. This will cause - // onActivityResult to be called. - daydreamApi.exitFromVr(/* activity= */ this, EXIT_FROM_VR_REQUEST_CODE, /* data= */ null); - daydreamApi.close(); - } else { - finish(); - } - } - - /** Toggles PlayerControl visibility. */ - @UiThread - protected void togglePlayerControlVisibility() { - if (Assertions.checkNotNull(playerControlView).isVisible()) { - playerControlView.hide(); - } else { - playerControlView.show(); - } - } - - private void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture) { - // Called on the GL thread. Post to the main thread. - runOnUiThread( - () -> { - SurfaceTexture oldSurfaceTexture = this.surfaceTexture; - Surface oldSurface = this.surface; - this.surfaceTexture = surfaceTexture; - this.surface = new Surface(surfaceTexture); - if (player != null) { - Player.VideoComponent videoComponent = player.getVideoComponent(); - if (videoComponent != null) { - videoComponent.setVideoSurface(surface); - } - } - releaseSurface(oldSurfaceTexture, oldSurface); - }); - } - - private static void releaseSurface( - @Nullable SurfaceTexture oldSurfaceTexture, @Nullable Surface oldSurface) { - if (oldSurfaceTexture != null) { - oldSurfaceTexture.release(); - } - if (oldSurface != null) { - oldSurface.release(); - } - } - - private class Renderer implements GvrView.StereoRenderer { - private static final float Z_NEAR = 0.1f; - private static final float Z_FAR = 100; - - private final SceneRenderer sceneRenderer; - private final PointerRenderer pointerRenderer; - private final ViewRenderer viewRenderer; - private final float[] viewProjectionMatrix; - - public Renderer( - SceneRenderer sceneRenderer, PointerRenderer pointerRenderer, ViewRenderer viewRenderer) { - this.sceneRenderer = sceneRenderer; - this.pointerRenderer = pointerRenderer; - this.viewRenderer = viewRenderer; - viewProjectionMatrix = new float[16]; - } - - @Override - public void onNewFrame(HeadTransform headTransform) {} - - @Override - public void onDrawEye(Eye eye) { - Matrix.multiplyMM( - viewProjectionMatrix, 0, eye.getPerspective(Z_NEAR, Z_FAR), 0, eye.getEyeView(), 0); - sceneRenderer.drawFrame(viewProjectionMatrix, eye.getType() == Eye.Type.RIGHT); - if (viewRenderer.isVisible()) { - viewRenderer.draw(viewProjectionMatrix); - pointerRenderer.draw(viewProjectionMatrix); - } - } - - @Override - public void onFinishFrame(Viewport viewport) {} - - @Override - public void onSurfaceCreated(EGLConfig config) { - onSurfaceTextureAvailable(sceneRenderer.init()); - viewRenderer.init(); - pointerRenderer.init(); - } - - @Override - public void onSurfaceChanged(int width, int height) {} - - @Override - public void onRendererShutdown() { - viewRenderer.shutdown(); - pointerRenderer.shutdown(); - sceneRenderer.shutdown(); - } - } - - private class ControllerEventListener extends Controller.EventListener { - - private final Controller controller; - private final PointerRenderer pointerRenderer; - private final ViewRenderer viewRenderer; - private final float[] controllerOrientationMatrix; - private boolean clickButtonDown; - private boolean appButtonDown; - - public ControllerEventListener( - Controller controller, PointerRenderer pointerRenderer, ViewRenderer viewRenderer) { - this.controller = controller; - this.pointerRenderer = pointerRenderer; - this.viewRenderer = viewRenderer; - controllerOrientationMatrix = new float[16]; - } - - @Override - @BinderThread - public void onUpdate() { - controller.update(); - Orientation orientation = controller.orientation; - orientation.toRotationMatrix(controllerOrientationMatrix); - pointerRenderer.setControllerOrientation(controllerOrientationMatrix); - - if (clickButtonDown || controller.clickButtonState) { - int action; - if (clickButtonDown != controller.clickButtonState) { - clickButtonDown = controller.clickButtonState; - action = clickButtonDown ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP; - } else { - action = MotionEvent.ACTION_MOVE; - } - float[] yawPitchRoll = orientation.toYawPitchRollRadians(new float[3]); - runOnUiThread(() -> dispatchClick(action, yawPitchRoll[0], yawPitchRoll[1])); - } else if (!appButtonDown && controller.appButtonState) { - runOnUiThread(GvrPlayerActivity.this::togglePlayerControlVisibility); - } - appButtonDown = controller.appButtonState; - } - - private void dispatchClick(int action, float yaw, float pitch) { - boolean clickedOnView = viewRenderer.simulateClick(action, yaw, pitch); - if (action == MotionEvent.ACTION_DOWN && !clickedOnView) { - togglePlayerControlVisibility(); - } - } - } - - private final class ControllerManagerEventListener implements ControllerManager.EventListener { - - @Override - public void onApiStatusChanged(int status) { - // Do nothing. - } - - @Override - public void onRecentered() { - // TODO: If in cardboard mode call gvrView.recenterHeadTracker(). - runOnUiThread(() -> Assertions.checkNotNull(playerControlView).show()); - } - } -} diff --git a/extensions/jobdispatcher/README.md b/extensions/jobdispatcher/README.md index a6f0c3966a..613277bad2 100644 --- a/extensions/jobdispatcher/README.md +++ b/extensions/jobdispatcher/README.md @@ -1,6 +1,7 @@ # ExoPlayer Firebase JobDispatcher extension # -**DEPRECATED - Please use [WorkManager extension][] or [PlatformScheduler][] instead.** +**DEPRECATED - Please use [WorkManager extension][] or [PlatformScheduler][] +instead.** This extension provides a Scheduler implementation which uses [Firebase JobDispatcher][]. diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/CanvasRenderer.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/CanvasRenderer.java deleted file mode 100644 index 82a9f48d04..0000000000 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/CanvasRenderer.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.ui.spherical; - -import static com.google.android.exoplayer2.util.GlUtil.checkGlError; - -import android.graphics.Canvas; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.SurfaceTexture; -import android.opengl.GLES11Ext; -import android.opengl.GLES20; -import android.view.Surface; -import androidx.annotation.Nullable; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.util.GlUtil; -import java.nio.FloatBuffer; -import java.util.concurrent.atomic.AtomicBoolean; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; - -/** - * Renders a canvas on a quad. - * - *

A CanvasRenderer can be created on any thread, but {@link #init()} needs to be called on the - * GL thread before it can be rendered. - */ -public final class CanvasRenderer { - - private static final float WIDTH_UNIT = 0.8f; - private static final float DISTANCE_UNIT = 1f; - private static final float X_UNIT = -WIDTH_UNIT / 2; - private static final float Y_UNIT = -0.3f; - - // Standard vertex shader that passes through the texture data. - private static final String[] VERTEX_SHADER_CODE = { - "uniform mat4 uMvpMatrix;", - // 3D position data. - "attribute vec3 aPosition;", - // 2D UV vertices. - "attribute vec2 aTexCoords;", - "varying vec2 vTexCoords;", - - // Standard transformation. - "void main() {", - " gl_Position = uMvpMatrix * vec4(aPosition, 1);", - " vTexCoords = aTexCoords;", - "}" - }; - - private static final String[] FRAGMENT_SHADER_CODE = { - // This is required since the texture data is GL_TEXTURE_EXTERNAL_OES. - "#extension GL_OES_EGL_image_external : require", - "precision mediump float;", - "uniform samplerExternalOES uTexture;", - "varying vec2 vTexCoords;", - "void main() {", - " gl_FragColor = texture2D(uTexture, vTexCoords);", - "}" - }; - - // The quad has 2 triangles built from 4 total vertices. Each vertex has 3 position and 2 texture - // coordinates. - private static final int POSITION_COORDS_PER_VERTEX = 3; - private static final int TEXTURE_COORDS_PER_VERTEX = 2; - private static final int COORDS_PER_VERTEX = - POSITION_COORDS_PER_VERTEX + TEXTURE_COORDS_PER_VERTEX; - private static final int VERTEX_STRIDE_BYTES = COORDS_PER_VERTEX * C.BYTES_PER_FLOAT; - private static final int VERTEX_COUNT = 4; - private static final float HALF_PI = (float) (Math.PI / 2); - - private final FloatBuffer vertexBuffer; - private final AtomicBoolean surfaceDirty; - - private int width; - private int height; - private float heightUnit; - - // Program-related GL items. These are only valid if program != 0. - private int program = 0; - private int mvpMatrixHandle; - private int positionHandle; - private int textureCoordsHandle; - private int textureHandle; - private int textureId; - - // Components used to manage the Canvas that the View is rendered to. These are only valid after - // GL initialization. The client of this class acquires a Canvas from the Surface, writes to it - // and posts it. This marks the Surface as dirty. The GL code then updates the SurfaceTexture - // when rendering only if it is dirty. - private @MonotonicNonNull SurfaceTexture displaySurfaceTexture; - private @MonotonicNonNull Surface displaySurface; - - public CanvasRenderer() { - vertexBuffer = GlUtil.createBuffer(COORDS_PER_VERTEX * VERTEX_COUNT); - surfaceDirty = new AtomicBoolean(); - } - - public void setSize(int width, int height) { - this.width = width; - this.height = height; - heightUnit = WIDTH_UNIT * height / width; - - float[] vertexData = new float[COORDS_PER_VERTEX * VERTEX_COUNT]; - int vertexDataIndex = 0; - for (int y = 0; y < 2; y++) { - for (int x = 0; x < 2; x++) { - vertexData[vertexDataIndex++] = X_UNIT + (WIDTH_UNIT * x); - vertexData[vertexDataIndex++] = Y_UNIT + (heightUnit * y); - vertexData[vertexDataIndex++] = -DISTANCE_UNIT; - vertexData[vertexDataIndex++] = x; - vertexData[vertexDataIndex++] = 1 - y; - } - } - vertexBuffer.position(0); - vertexBuffer.put(vertexData); - } - - /** - * Calls {@link Surface#lockCanvas(Rect)}. - * - * @return {@link Canvas} for the View to render to or {@code null} if {@link #init()} has not yet - * been called. - */ - @Nullable - public Canvas lockCanvas() { - return displaySurface == null ? null : displaySurface.lockCanvas(/* inOutDirty= */ null); - } - - /** - * Calls {@link Surface#unlockCanvasAndPost(Canvas)} and marks the SurfaceTexture as dirty. - * - * @param canvas the canvas returned from {@link #lockCanvas()} - */ - public void unlockCanvasAndPost(@Nullable Canvas canvas) { - if (canvas == null || displaySurface == null) { - // glInit() hasn't run yet. - return; - } - displaySurface.unlockCanvasAndPost(canvas); - } - - /** Finishes constructing this object on the GL Thread. */ - public void init() { - if (program != 0) { - return; - } - - // Create the program. - program = GlUtil.compileProgram(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE); - mvpMatrixHandle = GLES20.glGetUniformLocation(program, "uMvpMatrix"); - positionHandle = GLES20.glGetAttribLocation(program, "aPosition"); - textureCoordsHandle = GLES20.glGetAttribLocation(program, "aTexCoords"); - textureHandle = GLES20.glGetUniformLocation(program, "uTexture"); - textureId = GlUtil.createExternalTexture(); - checkGlError(); - - // Create the underlying SurfaceTexture with the appropriate size. - displaySurfaceTexture = new SurfaceTexture(textureId); - displaySurfaceTexture.setOnFrameAvailableListener(surfaceTexture -> surfaceDirty.set(true)); - displaySurfaceTexture.setDefaultBufferSize(width, height); - displaySurface = new Surface(displaySurfaceTexture); - } - - /** - * Renders the quad. - * - * @param viewProjectionMatrix Array of floats containing the quad's 4x4 perspective matrix in the - * {@link android.opengl.Matrix} format. - */ - public void draw(float[] viewProjectionMatrix) { - if (displaySurfaceTexture == null) { - return; - } - - GLES20.glUseProgram(program); - checkGlError(); - - GLES20.glEnableVertexAttribArray(positionHandle); - GLES20.glEnableVertexAttribArray(textureCoordsHandle); - checkGlError(); - - GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, viewProjectionMatrix, 0); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); - GLES20.glUniform1i(textureHandle, 0); - checkGlError(); - - // Load position data. - vertexBuffer.position(0); - GLES20.glVertexAttribPointer( - positionHandle, - POSITION_COORDS_PER_VERTEX, - GLES20.GL_FLOAT, - false, - VERTEX_STRIDE_BYTES, - vertexBuffer); - checkGlError(); - - // Load texture data. - vertexBuffer.position(POSITION_COORDS_PER_VERTEX); - GLES20.glVertexAttribPointer( - textureCoordsHandle, - TEXTURE_COORDS_PER_VERTEX, - GLES20.GL_FLOAT, - false, - VERTEX_STRIDE_BYTES, - vertexBuffer); - checkGlError(); - - if (surfaceDirty.compareAndSet(true, false)) { - // If the Surface has been written to, get the new data onto the SurfaceTexture. - displaySurfaceTexture.updateTexImage(); - } - - // Render. - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX_COUNT); - checkGlError(); - - GLES20.glDisableVertexAttribArray(positionHandle); - GLES20.glDisableVertexAttribArray(textureCoordsHandle); - } - - /** Frees GL resources. */ - public void shutdown() { - if (program != 0) { - GLES20.glDeleteProgram(program); - GLES20.glDeleteTextures(1, new int[] {textureId}, 0); - } - - if (displaySurfaceTexture != null) { - displaySurfaceTexture.release(); - } - if (displaySurface != null) { - displaySurface.release(); - } - } - - /** - * Translates an orientation into pixel coordinates on the canvas. - * - *

This is a minimal hit detection system that works for this quad because it has no model - * matrix. All the math is based on the fact that its size and distance are hard-coded into this - * class. For a more complex 3D mesh, a general bounding box and ray collision system would be - * required. - * - * @param yaw Yaw of the orientation in radians. - * @param pitch Pitch of the orientation in radians. - * @return A {@link PointF} which contains the translated coordinate, or null if the point is - * outside of the quad's bounds. - */ - @Nullable - public PointF translateClick(float yaw, float pitch) { - return internalTranslateClick( - yaw, pitch, X_UNIT, Y_UNIT, WIDTH_UNIT, heightUnit, width, height); - } - - @Nullable - /* package */ static PointF internalTranslateClick( - float yaw, - float pitch, - float xUnit, - float yUnit, - float widthUnit, - float heightUnit, - int widthPixel, - int heightPixel) { - if (yaw >= HALF_PI || yaw <= -HALF_PI || pitch >= HALF_PI || pitch <= -HALF_PI) { - return null; - } - double clickXUnit = Math.tan(yaw) * DISTANCE_UNIT - xUnit; - double clickYUnit = Math.tan(pitch) * DISTANCE_UNIT - yUnit; - if (clickXUnit < 0 || clickXUnit > widthUnit || clickYUnit < 0 || clickYUnit > heightUnit) { - return null; - } - // Convert from the polar coordinates of the controller to the rectangular coordinates of the - // View. Note the negative yaw and pitch used to generate Android-compliant x and y coordinates. - float clickXPixel = (float) (widthPixel - clickXUnit * widthPixel / widthUnit); - float clickYPixel = (float) (heightPixel - clickYUnit * heightPixel / heightUnit); - return new PointF(clickXPixel, clickYPixel); - } -} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/PointerRenderer.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/PointerRenderer.java deleted file mode 100644 index c8773a2838..0000000000 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/PointerRenderer.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.ui.spherical; - -import static com.google.android.exoplayer2.util.GlUtil.checkGlError; - -import android.opengl.GLES20; -import android.opengl.Matrix; -import com.google.android.exoplayer2.util.GlUtil; -import java.nio.FloatBuffer; - -/** Renders a pointer. */ -public final class PointerRenderer { - // The pointer quad is 2 * SIZE units. - private static final float SIZE = 0.01f; - private static final float DISTANCE = 1; - - // Standard vertex shader. - private static final String[] VERTEX_SHADER_CODE = - new String[] { - "uniform mat4 uMvpMatrix;", - "attribute vec3 aPosition;", - "varying vec2 vCoords;", - - // Pass through normalized vertex coordinates. - "void main() {", - " gl_Position = uMvpMatrix * vec4(aPosition, 1);", - " vCoords = aPosition.xy / vec2(" + SIZE + ", " + SIZE + ");", - "}" - }; - - // Procedurally render a ring on the quad between the specified radii. - private static final String[] FRAGMENT_SHADER_CODE = - new String[] { - "precision mediump float;", - "varying vec2 vCoords;", - - // Simple ring shader that is white between the radii and transparent elsewhere. - "void main() {", - " float r = length(vCoords);", - // Blend the edges of the ring at .55 +/- .05 and .85 +/- .05. - " float alpha = smoothstep(0.5, 0.6, r) * (1.0 - smoothstep(0.8, 0.9, r));", - " if (alpha == 0.0) {", - " discard;", - " } else {", - " gl_FragColor = vec4(alpha);", - " }", - "}" - }; - - // Simple quad mesh. - private static final int COORDS_PER_VERTEX = 3; - private static final float[] VERTEX_DATA = { - -SIZE, -SIZE, -DISTANCE, SIZE, -SIZE, -DISTANCE, -SIZE, SIZE, -DISTANCE, SIZE, SIZE, -DISTANCE, - }; - private final FloatBuffer vertexBuffer; - - // The pointer doesn't have a real modelMatrix. Its distance is baked into the mesh and it - // uses a rotation matrix when rendered. - private final float[] modelViewProjectionMatrix; - // This is accessed on the binder & GL Threads. - private final float[] controllerOrientationMatrix; - - // Program-related GL items. These are only valid if program != 0. - private int program = 0; - private int mvpMatrixHandle; - private int positionHandle; - - public PointerRenderer() { - vertexBuffer = GlUtil.createBuffer(VERTEX_DATA); - modelViewProjectionMatrix = new float[16]; - controllerOrientationMatrix = new float[16]; - Matrix.setIdentityM(controllerOrientationMatrix, 0); - } - - /** Finishes initialization of this object on the GL thread. */ - public void init() { - if (program != 0) { - return; - } - - program = GlUtil.compileProgram(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE); - mvpMatrixHandle = GLES20.glGetUniformLocation(program, "uMvpMatrix"); - positionHandle = GLES20.glGetAttribLocation(program, "aPosition"); - checkGlError(); - } - - /** - * Renders the pointer. - * - * @param viewProjectionMatrix Scene's view projection matrix. - */ - public void draw(float[] viewProjectionMatrix) { - // Configure shader. - GLES20.glUseProgram(program); - checkGlError(); - - synchronized (controllerOrientationMatrix) { - Matrix.multiplyMM( - modelViewProjectionMatrix, 0, viewProjectionMatrix, 0, controllerOrientationMatrix, 0); - } - GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, modelViewProjectionMatrix, 0); - checkGlError(); - - // Render quad. - GLES20.glEnableVertexAttribArray(positionHandle); - checkGlError(); - - GLES20.glVertexAttribPointer( - positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, /* stride= */ 0, vertexBuffer); - checkGlError(); - - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX_DATA.length / COORDS_PER_VERTEX); - checkGlError(); - - GLES20.glDisableVertexAttribArray(positionHandle); - } - - /** Frees GL resources. */ - public void shutdown() { - if (program != 0) { - GLES20.glDeleteProgram(program); - } - } - - /** Updates the pointer's position with the latest Controller pose. */ - public void setControllerOrientation(float[] rotationMatrix) { - synchronized (controllerOrientationMatrix) { - System.arraycopy(rotationMatrix, 0, controllerOrientationMatrix, 0, rotationMatrix.length); - } - } -} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SceneRenderer.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SceneRenderer.java index 4343800500..5080e86345 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SceneRenderer.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SceneRenderer.java @@ -37,7 +37,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Renders a GL Scene. */ -public final class SceneRenderer implements VideoFrameMetadataListener, CameraMotionListener { +/* package */ final class SceneRenderer + implements VideoFrameMetadataListener, CameraMotionListener { private final AtomicBoolean frameAvailable; private final AtomicBoolean resetRotationAtNextFrame; diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/ViewRenderer.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/ViewRenderer.java deleted file mode 100644 index a1a3eb28bb..0000000000 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/ViewRenderer.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.ui.spherical; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.os.SystemClock; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import com.google.android.exoplayer2.util.Assertions; - -/** Renders a {@link View} on a quad and supports simulated clicks on the view. */ -public final class ViewRenderer { - - private final CanvasRenderer canvasRenderer; - private final View view; - private final InternalFrameLayout frameLayout; - - /** - * @param context A context. - * @param parentView The parent view. - * @param view The view to render. - */ - public ViewRenderer(Context context, ViewGroup parentView, View view) { - this.canvasRenderer = new CanvasRenderer(); - this.view = view; - // Wrap the view in an internal view that redirects rendering. - frameLayout = new InternalFrameLayout(context, view, canvasRenderer); - canvasRenderer.setSize(frameLayout.getMeasuredWidth(), frameLayout.getMeasuredHeight()); - // The internal view must be added to the parent to ensure proper delivery of UI events. - parentView.addView(frameLayout); - } - - /** Finishes constructing this object on the GL Thread. */ - public void init() { - canvasRenderer.init(); - } - - /** - * Renders the view as a quad. - * - * @param viewProjectionMatrix Array of floats containing the quad's 4x4 perspective matrix in the - * {@link android.opengl.Matrix} format. - */ - public void draw(float[] viewProjectionMatrix) { - canvasRenderer.draw(viewProjectionMatrix); - } - - /** Frees GL resources. */ - public void shutdown() { - canvasRenderer.shutdown(); - } - - /** Returns whether the view is currently visible. */ - @UiThread - public boolean isVisible() { - return view.getVisibility() == View.VISIBLE; - } - - /** - * Simulates a click on the view. - * - * @param action Click action. - * @param yaw Yaw of the click's orientation in radians. - * @param pitch Pitch of the click's orientation in radians. - * @return Whether the click was simulated. If false then the view is not visible or the click was - * outside of its bounds. - */ - @UiThread - public boolean simulateClick(int action, float yaw, float pitch) { - if (!isVisible()) { - return false; - } - @Nullable PointF point = canvasRenderer.translateClick(yaw, pitch); - if (point == null) { - return false; - } - long now = SystemClock.uptimeMillis(); - MotionEvent event = MotionEvent.obtain(now, now, action, point.x, point.y, /* metaState= */ 1); - frameLayout.dispatchTouchEvent(event); - return true; - } - - private static final class InternalFrameLayout extends FrameLayout { - - private final CanvasRenderer canvasRenderer; - - public InternalFrameLayout(Context context, View wrappedView, CanvasRenderer canvasRenderer) { - super(context); - this.canvasRenderer = canvasRenderer; - addView(wrappedView); - measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); - int width = getMeasuredWidth(); - int height = getMeasuredHeight(); - Assertions.checkState(width > 0 && height > 0); - setLayoutParams(new FrameLayout.LayoutParams(width, height)); - } - - @Override - public void dispatchDraw(Canvas notUsed) { - @Nullable Canvas glCanvas = canvasRenderer.lockCanvas(); - if (glCanvas == null) { - // This happens if Android tries to draw this View before GL initialization completes. We - // need to retry until the draw call happens after GL invalidation. - postInvalidate(); - return; - } - - // Clear the canvas first. - glCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - // Have Android render the child views. - super.dispatchDraw(glCanvas); - // Commit the changes. - canvasRenderer.unlockCanvasAndPost(glCanvas); - } - } -} diff --git a/library/ui/src/test/java/com/google/android/exoplayer2/ui/spherical/CanvasRendererTest.java b/library/ui/src/test/java/com/google/android/exoplayer2/ui/spherical/CanvasRendererTest.java deleted file mode 100644 index 098f4157ef..0000000000 --- a/library/ui/src/test/java/com/google/android/exoplayer2/ui/spherical/CanvasRendererTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.android.exoplayer2.ui.spherical; - -import static com.google.common.truth.Truth.assertThat; - -import android.graphics.PointF; -import androidx.annotation.Nullable; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Tests for {@link CanvasRenderer}. */ -@RunWith(AndroidJUnit4.class) -public class CanvasRendererTest { - - private static final float JUST_BELOW_45_DEGREES = (float) (Math.PI / 4 - 1.0E-08); - private static final float JUST_ABOVE_45_DEGREES = (float) (Math.PI / 4 + 1.0E-08); - private static final float TOLERANCE = .00001f; - - @Test - public void testClicksOnCanvas() { - assertClick(translateClick(JUST_BELOW_45_DEGREES, JUST_BELOW_45_DEGREES), 0, 0); - assertClick(translateClick(JUST_BELOW_45_DEGREES, -JUST_BELOW_45_DEGREES), 0, 100); - assertClick(translateClick(0, 0), 50, 50); - assertClick(translateClick(-JUST_BELOW_45_DEGREES, JUST_BELOW_45_DEGREES), 100, 0); - assertClick(translateClick(-JUST_BELOW_45_DEGREES, -JUST_BELOW_45_DEGREES), 100, 100); - } - - @Test - public void testClicksNotOnCanvas() { - assertThat(translateClick(JUST_ABOVE_45_DEGREES, JUST_ABOVE_45_DEGREES)).isNull(); - assertThat(translateClick(JUST_ABOVE_45_DEGREES, -JUST_ABOVE_45_DEGREES)).isNull(); - assertThat(translateClick(-JUST_ABOVE_45_DEGREES, JUST_ABOVE_45_DEGREES)).isNull(); - assertThat(translateClick(-JUST_ABOVE_45_DEGREES, -JUST_ABOVE_45_DEGREES)).isNull(); - assertThat(translateClick((float) (Math.PI / 2), 0)).isNull(); - assertThat(translateClick(0, (float) Math.PI)).isNull(); - } - - private static PointF translateClick(float yaw, float pitch) { - return CanvasRenderer.internalTranslateClick( - yaw, - pitch, - /* xUnit= */ -1, - /* yUnit= */ -1, - /* widthUnit= */ 2, - /* heightUnit= */ 2, - /* widthPixel= */ 100, - /* heightPixel= */ 100); - } - - private static void assertClick(@Nullable PointF actual, float expectedX, float expectedY) { - assertThat(actual).isNotNull(); - assertThat(actual.x).isWithin(TOLERANCE).of(expectedX); - assertThat(actual.y).isWithin(TOLERANCE).of(expectedY); - } -} diff --git a/settings.gradle b/settings.gradle index 3e544854ad..39e4791bb5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,12 +20,10 @@ if (gradle.ext.has('exoplayerModulePrefix')) { include modulePrefix + 'demo' include modulePrefix + 'demo-cast' -include modulePrefix + 'demo-gvr' include modulePrefix + 'demo-surface' include modulePrefix + 'playbacktests' project(modulePrefix + 'demo').projectDir = new File(rootDir, 'demos/main') project(modulePrefix + 'demo-cast').projectDir = new File(rootDir, 'demos/cast') -project(modulePrefix + 'demo-gvr').projectDir = new File(rootDir, 'demos/gvr') project(modulePrefix + 'demo-surface').projectDir = new File(rootDir, 'demos/surface') project(modulePrefix + 'playbacktests').projectDir = new File(rootDir, 'playbacktests')