From 7ccbc4c436f4ac078b6cee0e964750f2a380789c Mon Sep 17 00:00:00 2001 From: olly Date: Mon, 21 Oct 2019 13:14:38 +0100 Subject: [PATCH] Remove VR code - Leaving GvrAudioProcessor for now. - Removing GvrPlayerActivity because it was never released. Also removing related UI classes. These were released, but it's unlikely anyone would have been using them directly. PiperOrigin-RevId: 275822516 --- RELEASENOTES.md | 2 +- demos/gvr/README.md | 4 - demos/gvr/build.gradle | 59 --- demos/gvr/src/main/AndroidManifest.xml | 60 --- .../exoplayer2/gvrdemo/PlayerActivity.java | 189 ---------- .../gvrdemo/SampleChooserActivity.java | 92 ----- .../res/layout/sample_chooser_activity.xml | 25 -- .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3394 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2184 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4886 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7492 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 10801 -> 0 bytes demos/gvr/src/main/res/values/strings.xml | 28 -- extensions/gvr/README.md | 4 + .../exoplayer2/ext/gvr/GvrAudioProcessor.java | 4 + .../exoplayer2/ext/gvr/GvrPlayerActivity.java | 344 ------------------ extensions/jobdispatcher/README.md | 3 +- .../ui/spherical/CanvasRenderer.java | 294 --------------- .../ui/spherical/PointerRenderer.java | 145 -------- .../ui/spherical/SceneRenderer.java | 3 +- .../exoplayer2/ui/spherical/ViewRenderer.java | 137 ------- .../ui/spherical/CanvasRendererTest.java | 70 ---- settings.gradle | 2 - 23 files changed, 13 insertions(+), 1452 deletions(-) delete mode 100644 demos/gvr/README.md delete mode 100644 demos/gvr/build.gradle delete mode 100644 demos/gvr/src/main/AndroidManifest.xml delete mode 100644 demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/PlayerActivity.java delete mode 100644 demos/gvr/src/main/java/com/google/android/exoplayer2/gvrdemo/SampleChooserActivity.java delete mode 100644 demos/gvr/src/main/res/layout/sample_chooser_activity.xml delete mode 100644 demos/gvr/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 demos/gvr/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 demos/gvr/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 demos/gvr/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 demos/gvr/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 demos/gvr/src/main/res/values/strings.xml delete mode 100644 extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrPlayerActivity.java delete mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/CanvasRenderer.java delete mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/PointerRenderer.java delete mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/ViewRenderer.java delete mode 100644 library/ui/src/test/java/com/google/android/exoplayer2/ui/spherical/CanvasRendererTest.java 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 adaa93220eb81c58e5c85874d1cf127a3188e419..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3394 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 KMNUMnLSTZLL=Vyc 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 2101026c9fe1b48ab11a23fd440cf2f80af2afae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4886 zcmV+x6Y1=UP)NZw)cXxMpfx5T2C&3}PVZZ;p-o4i2PAJ@j z+=Q8)wZ4!Axsm_BXYYOXxj|RrxHs+%BJu*>GCn21kMjaLPsdZ=m^Y{(+<2>%EO28%%wuqUDMu8OH$8%zKu7G4&}qFQk04YA1g*$9*T z-fAmCrB}H$Kq34SU>Gp7@Ek5m5M2v1I8?C_X8FB7VgIvuz5!mq7wBXR;06n?h|Qh^ zpAc-s4G;!&GQPme(+%+E*a@BkO92bdVTL>$4o@VHrSfOt+~Do02C(pgS3oS_4`l^Z zo>0t&81Rby&*~gy9`Es{e^8wD9OKI!Tp)18WVKp2jmH3M;1&be{m1d9 zjuM7eWu?2zR$)RxLBX-u*w`n5Q!~LS@l#V%#hHJEh}<7?G!99^gxuWRqXRF(=;(_=tajB`c+L4k@u+Sx9J@^Gl=@-gH zbtLBlx_R@aA5c{psY<%p+1bzG#bO+-^QBvxg}5s4BkSp3$(EDi>G?o{CL$EY9zA-r z3SiC3!~idWh;ewGB~Da4FkquP1DLI$BwbxbHcOsIw=OV5C|U`vyjeG=4PYlx_v#6Q zW1pr5FuUI*DgFSSOY+EkKo~hWIdtX9mA;@;qG0A=0pN&_+$2GE8k#f#5uZfT(GWp_6rYpFk-=oLzv*V}fW~yFWgb4PcD=P|8`jfTZkf%}Uawq@;u?Q>N4ay5-B4`v4syZWv*J zub3*RVMCPR$CU=e{Td|1QbqL0AaN|E&kCUZuju*rB^tn(-W*7IySeS{`Y?3x;K6=E zTb$e#fsPS33|Bwi=x0n)p8c*g;P&tk)dsAoZ4lKF&m7eTFy@GGD!6pfrR{2T5(xUv z7TPMSp+AJ5)~#FnB5s|~(txB1fh?Gls5gM~L)O^e$(mC3ZEx1__U+qeDpjiVAmA`m zGz4+=bNTY+`Z&M@v@!t6)1THi3UiwQjIpPIa+WV6DKD?g^FT^Uih0qZMJywMn(qh+O?~XvK&=*KE2U;{)V?3 zF$%RZAbUC68A6DLj#Q|19>{a*nYdzQV`uNEueJd~DO z+BhckhB^k?(?U-NTIp$56Fse|8Ae+h7$`L`&@ls;&|u1%GMOZ!(Ww@-NW^E)o?Qfu zeOYgyE0TaQDxw0~?hXUE`L?r*x>>NPu7NK9BZSfdiwXzf`FCZ4u}^QxUB8Y{(m2!| z#USL@z0mkUzI>&K0U|bqhK4?jHfkge(A)s?`~W%>&_k8@+Zq}u$v=oi(ggEIA5i{@ z6Atx-Pzjyy*s;Dj?}UiDUX}Q3Yw9WfbrxTH zGDnRhDLbpw1`r0WBaQy#lTWJse|NOV*5jHrYii@MNnAd;$^Z^G*MA76P0byP`NunQ za&;e>BS)wzMX|q(CdC=xy<^9YhIp{a)dnPv3#47$oU3?lzZC3N@fpkY>!mgz zIXT%papJ^QLTkGQY}vA<84k#Fl>u32w%qA@R(HOfGBDlMZItdC>mzvs$f5t~P-AVCXiQ+4&2H3^>xZ zhuaOvnlN5f5=NURB_blCd9enBhlf|eI^Y(UPOdb7=`s5}ef{ZTTGZq%b?xXH28wyb zO<^b`h_a_m6^k-eW$3M2x1zDusw1@KYlU>U;f0uYtY#JJ5>I+yG`; z_Xt%pVI3B5QQboSkdza!NJ|L_EHmLv@4Q9%`}aHAAr*3pUXW--JPEvUm4ra26F0&D z{1olzhmk1exXXavlTOgWX78}LszcKDMh4p56f>j#o!@P&YjD~GW0-+*mM$SFH`meq z0B*(+zBjNG8b2bmRyN~wVv|5LiZD%nOl%nafdzu)~bNU%tUaz0q4KNMtPkFm`lFe!@^DW7^xH!{i zpMBOlb<%JH2}wK6vtA(iO1-dz)`fadYCE$ z%%6Tt1*cDIa(yT+bR&F|{zIWH^1%H-$A}w7p7him_mC#m{pg?Cg1PNY# z`DHN+p2har68TB2T2w*51dT4W0V8^i7suFbYlMHfoE9{Hr-%W(xt={^I!R`;OGQ`I z|5g!xcJJQ37MeaMv;{gwz;_s7LPb3I@wIE$_7zXUVaU}6^u;1;kE!Rh z(|lYpRaGPn=|7p4*Xbh+po~F3lI7M-w_Ki%|0qLa$BunpXv*>5UJhV53_V!2YSr3^ zT^G17q{T{j;MhYntK(E&g7mY_QN$O^M1@0h+k7a_cpCreQ2H`rl?XAV z68_C4mudT@bh1j?imo{OZRo4fiWMuy3oV7FK&M39DB&>lXy3kl+hI+R#3iW)1~^67 zyo)sVXd-3im5^YyOTH<7etzAAmLGle(S1OtgzprAuMxt}N~cbpnrgkXGW2KzM(l{D z7^B<&mjowY8%|t89-SvNl(qEIsTsmCv2f_!yLTr{rDUE3o@T(OtFO_im`t||uRQzl zzamm`_WxaI*uQ`O`+?3L;>Iq*vCf@4KZ2iC_V3^SD|X7n3wVqH{TE%P4d+s+z^Y8J z?mzoQ<88^3C6k4AvW8wj{lL@_x!_4uL``s2*JWN8xYGdrTx{ALj3;A`R&iea#tXvs zwd&QYw-VZM?Oz;x$1cJ-Oe5i7<(V^QzMqwqMf}BWcNp-~mRocqHAibv)o#2Ku6+1H zXa)@*<+I}L?{wFJX#v?!HTPN=%$$@O+{6|>-FFE&H+fWBnZPd z#&T>M-D}(44PNMMW6jXnw!O9;INP>u1(|%heRnbt14b z%;Lo@rkE*HrZ6tZ?cTk6A5BrbgurH8$W7n8Nx<)a%hSQ~hu;gn{w};1_&F?ie(0fx zUWr5^1Hfwd@Zr|;X&s6kJa}-Lo{P^of zJw)x{Aa-#5_19krHil8cJ{~AWaV@1ohYsv(snrbMVg?%`n*RSiP4T)0OGht#?k~U} zvEl?QV@Ug|tcrm|EgWDaO&Gy75fbYi%YJ9S+CPZ$$ z@y2sCSo==oM$FWPHe2egZQ};S@251qB5eUAlC^2@Uh-&p%9~ z!k37Z{%eEZQZF00a_r^l)2APaZPUW*LBs^)q~fsU%&dzH{T4b zHc%6waF=?K>B_m@V8x0RYpbfNhGG-8brppTd`B_X4eL-*QBkDlDslAGQ%{|)t&h*> z4o#fk90C6nrmiJAp<5tASC#r_wKO{u8?eP}>R>ivYiKD}oLp8`mXn>GU7^?Y>FMeH zZ@&5FOP_e+iFfEJ;Rq29_`#g9$6`D>pbHAIh^{@H?`T@#a&$&_Y`_+5GTYh&xsAS^ z%FD}(tu{o43>h+R@#4id$PsT-%2;cL?7vtbN}^xTks(4RviXdzB|2#p>qf0$O+Z(4 zMt5xR8*==Iq~zS(+$^mS(LmRTqy75z8>>=wk*fKVv=KIvcFy<_qI5#eDR7g3Hn4x6 zmkTlOo`1TP=y2V2*InGFPoMGVhK}fp&gdTQkkf>bv$%t*bB-G4a&tsP!}z`n7cM+e zy9ZWj1JkPj!lH(&8IAzXy0kz1iGDQxl}JO{--GQ+I*w+XB3Z8>6WP+CAG)9ux}hVw zdKuYx0v2&c~z1(GLVH#Wc!ct;U{-SIo<&2pwgMf zfoA&Nga@l36&A8;0Mxk7vAwUcG!^`Y-&!8ICaE@0ri|jx?k-uv5rmFW@bA2pnr1XB_`0cAr~1`(4QCXBG>Tj>W)rr~m)}07*qo IM6N<$f{P}YasU7T 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 223ec8bd1132aab759469bd5ad2232f35f7be2c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7492 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%0Dj^J4_SbOxi?LOAH?pqcJIPrNphD%I2G8C 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 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')