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')