diff --git a/core_settings.gradle b/core_settings.gradle index 20a7c87bde..7a8320b1a1 100644 --- a/core_settings.gradle +++ b/core_settings.gradle @@ -28,7 +28,6 @@ include modulePrefix + 'extension-ffmpeg' include modulePrefix + 'extension-flac' include modulePrefix + 'extension-gvr' include modulePrefix + 'extension-ima' -include modulePrefix + 'extension-cast' include modulePrefix + 'extension-mediasession' include modulePrefix + 'extension-okhttp' include modulePrefix + 'extension-opus' @@ -47,7 +46,6 @@ project(modulePrefix + 'extension-ffmpeg').projectDir = new File(rootDir, 'exten project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac') project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr') project(modulePrefix + 'extension-ima').projectDir = new File(rootDir, 'extensions/ima') -project(modulePrefix + 'extension-cast').projectDir = new File(rootDir, 'extensions/cast') project(modulePrefix + 'extension-mediasession').projectDir = new File(rootDir, 'extensions/mediasession') project(modulePrefix + 'extension-okhttp').projectDir = new File(rootDir, 'extensions/okhttp') project(modulePrefix + 'extension-opus').projectDir = new File(rootDir, 'extensions/opus') diff --git a/demos/cast/README.md b/demos/cast/README.md deleted file mode 100644 index 2c68a5277a..0000000000 --- a/demos/cast/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Cast demo application # - -This folder contains a demo application that showcases ExoPlayer integration -with Google Cast. diff --git a/demos/cast/build.gradle b/demos/cast/build.gradle deleted file mode 100644 index a9fa27ad58..0000000000 --- a/demos/cast/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2017 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 - buildToolsVersion project.ext.buildToolsVersion - - defaultConfig { - minSdkVersion 16 - targetSdkVersion project.ext.targetSdkVersion - } - - buildTypes { - release { - shrinkResources true - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt') - } - debug { - jniDebuggable = true - } - } - - lintOptions { - // The demo app does not have translations. - disable 'MissingTranslation' - } - -} - -dependencies { - compile project(modulePrefix + 'library-core') - compile project(modulePrefix + 'library-dash') - compile project(modulePrefix + 'library-hls') - compile project(modulePrefix + 'library-smoothstreaming') - compile project(modulePrefix + 'library-ui') - compile project(modulePrefix + 'extension-cast') -} diff --git a/demos/cast/src/main/AndroidManifest.xml b/demos/cast/src/main/AndroidManifest.xml deleted file mode 100644 index 11f8e39b53..0000000000 --- a/demos/cast/src/main/AndroidManifest.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DemoUtil.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DemoUtil.java deleted file mode 100644 index d36f8c319e..0000000000 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DemoUtil.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2017 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.castdemo; - -import com.google.android.exoplayer2.util.MimeTypes; -import com.google.android.gms.cast.MediaInfo; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Utility methods and constants for the Cast demo application. - */ -/* package */ final class DemoUtil { - - public static final String MIME_TYPE_DASH = "application/dash+xml"; - public static final String MIME_TYPE_HLS = "application/vnd.apple.mpegurl"; - public static final String MIME_TYPE_SS = "application/vnd.ms-sstr+xml"; - public static final String MIME_TYPE_VIDEO_MP4 = MimeTypes.VIDEO_MP4; - - /** - * The list of samples available in the cast demo app. - */ - public static final List SAMPLES; - - /** - * Represents a media sample. - */ - public static final class Sample { - - /** - * The uri from which the media sample is obtained. - */ - public final String uri; - /** - * A descriptive name for the sample. - */ - public final String name; - /** - * The mime type of the media sample, as required by {@link MediaInfo#setContentType}. - */ - public final String mimeType; - - /** - * @param uri See {@link #uri}. - * @param name See {@link #name}. - * @param mimeType See {@link #mimeType}. - */ - public Sample(String uri, String name, String mimeType) { - this.uri = uri; - this.name = name; - this.mimeType = mimeType; - } - - @Override - public String toString() { - return name; - } - - } - - static { - // App samples. - ArrayList samples = new ArrayList<>(); - samples.add(new Sample("https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd", - "DASH (clear,MP4,H264)", MIME_TYPE_DASH)); - samples.add(new Sample("https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/" - + "hls/TearsOfSteel.m3u8", "Tears of Steel (HLS)", MIME_TYPE_HLS)); - samples.add(new Sample("https://html5demos.com/assets/dizzy.mp4", "Dizzy (MP4)", - MIME_TYPE_VIDEO_MP4)); - - - SAMPLES = Collections.unmodifiableList(samples); - - } - - private DemoUtil() {} - -} diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java deleted file mode 100644 index 094e9f9e6e..0000000000 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/MainActivity.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2017 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.castdemo; - -import android.graphics.Color; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.ext.cast.CastPlayer; -import com.google.android.exoplayer2.ui.PlaybackControlView; -import com.google.android.exoplayer2.ui.SimpleExoPlayerView; -import com.google.android.gms.cast.framework.CastButtonFactory; - -/** - * An activity that plays video using {@link SimpleExoPlayer} and {@link CastPlayer}. - */ -public class MainActivity extends AppCompatActivity { - - private SimpleExoPlayerView simpleExoPlayerView; - private PlaybackControlView castControlView; - private PlayerManager playerManager; - - // Activity lifecycle methods. - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.main_activity); - - simpleExoPlayerView = findViewById(R.id.player_view); - simpleExoPlayerView.requestFocus(); - - castControlView = findViewById(R.id.cast_control_view); - - ListView sampleList = findViewById(R.id.sample_list); - sampleList.setAdapter(new SampleListAdapter()); - sampleList.setOnItemClickListener(new SampleClickListener()); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.menu, menu); - CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), menu, - R.id.media_route_menu_item); - return true; - } - - @Override - public void onResume() { - super.onResume(); - playerManager = new PlayerManager(simpleExoPlayerView, castControlView, this); - } - - @Override - public void onPause() { - super.onPause(); - playerManager.release(); - playerManager = null; - } - - // Activity input. - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - // If the event was not handled then see if the player view can handle it. - return super.dispatchKeyEvent(event) || playerManager.dispatchKeyEvent(event); - } - - // User controls. - - private final class SampleListAdapter extends ArrayAdapter { - - public SampleListAdapter() { - super(getApplicationContext(), android.R.layout.simple_list_item_1, DemoUtil.SAMPLES); - } - - @Override - @NonNull - public View getView(int position, View convertView, @NonNull ViewGroup parent) { - View view = super.getView(position, convertView, parent); - view.setBackgroundColor(Color.WHITE); - return view; - } - - } - - private class SampleClickListener implements AdapterView.OnItemClickListener { - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (parent.getSelectedItemPosition() != position) { - DemoUtil.Sample currentSample = DemoUtil.SAMPLES.get(position); - playerManager.setCurrentSample(currentSample, 0, true); - } - } - - } - -} diff --git a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java b/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java deleted file mode 100644 index 77dc018a73..0000000000 --- a/demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2017 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.castdemo; - -import android.content.Context; -import android.net.Uri; -import android.view.KeyEvent; -import android.view.View; -import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.ExoPlayerFactory; -import com.google.android.exoplayer2.RenderersFactory; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.ext.cast.CastPlayer; -import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; -import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.dash.DashMediaSource; -import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource; -import com.google.android.exoplayer2.source.hls.HlsMediaSource; -import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource; -import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; -import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; -import com.google.android.exoplayer2.ui.PlaybackControlView; -import com.google.android.exoplayer2.ui.SimpleExoPlayerView; -import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; -import com.google.android.gms.cast.MediaInfo; -import com.google.android.gms.cast.MediaMetadata; -import com.google.android.gms.cast.MediaQueueItem; -import com.google.android.gms.cast.framework.CastContext; - -/** - * Manages players for the ExoPlayer/Cast integration app. - */ -/* package */ final class PlayerManager implements CastPlayer.SessionAvailabilityListener { - - private static final int PLAYBACK_REMOTE = 1; - private static final int PLAYBACK_LOCAL = 2; - - private static final String USER_AGENT = "ExoCastDemoPlayer"; - private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); - private static final DefaultHttpDataSourceFactory DATA_SOURCE_FACTORY = - new DefaultHttpDataSourceFactory(USER_AGENT, BANDWIDTH_METER); - - private final SimpleExoPlayerView exoPlayerView; - private final PlaybackControlView castControlView; - private final SimpleExoPlayer exoPlayer; - private final CastPlayer castPlayer; - - private int playbackLocation; - private DemoUtil.Sample currentSample; - - /** - * @param exoPlayerView The {@link SimpleExoPlayerView} for local playback. - * @param castControlView The {@link PlaybackControlView} to control remote playback. - * @param context A {@link Context}. - */ - public PlayerManager(SimpleExoPlayerView exoPlayerView, PlaybackControlView castControlView, - Context context) { - this.exoPlayerView = exoPlayerView; - this.castControlView = castControlView; - - DefaultTrackSelector trackSelector = new DefaultTrackSelector(BANDWIDTH_METER); - RenderersFactory renderersFactory = new DefaultRenderersFactory(context, null); - exoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector); - exoPlayerView.setPlayer(exoPlayer); - - castPlayer = new CastPlayer(CastContext.getSharedInstance(context)); - castPlayer.setSessionAvailabilityListener(this); - castControlView.setPlayer(castPlayer); - - setPlaybackLocation(castPlayer.isCastSessionAvailable() ? PLAYBACK_REMOTE : PLAYBACK_LOCAL); - } - - /** - * Starts playback of the given sample at the given position. - * - * @param currentSample The {@link DemoUtil} to play. - * @param positionMs The position at which playback should start. - * @param playWhenReady Whether the player should proceed when ready to do so. - */ - public void setCurrentSample(DemoUtil.Sample currentSample, long positionMs, - boolean playWhenReady) { - this.currentSample = currentSample; - if (playbackLocation == PLAYBACK_REMOTE) { - castPlayer.loadItem(buildMediaQueueItem(currentSample), positionMs); - castPlayer.setPlayWhenReady(playWhenReady); - } else /* playbackLocation == PLAYBACK_LOCAL */ { - exoPlayer.prepare(buildMediaSource(currentSample), true, true); - exoPlayer.setPlayWhenReady(playWhenReady); - exoPlayer.seekTo(positionMs); - } - } - - /** - * Dispatches a given {@link KeyEvent} to whichever view corresponds according to the current - * playback location. - * - * @param event The {@link KeyEvent}. - * @return Whether the event was handled by the target view. - */ - public boolean dispatchKeyEvent(KeyEvent event) { - if (playbackLocation == PLAYBACK_REMOTE) { - return castControlView.dispatchKeyEvent(event); - } else /* playbackLocation == PLAYBACK_REMOTE */ { - return exoPlayerView.dispatchKeyEvent(event); - } - } - - /** - * Releases the manager and the players that it holds. - */ - public void release() { - castPlayer.setSessionAvailabilityListener(null); - castPlayer.release(); - exoPlayerView.setPlayer(null); - exoPlayer.release(); - } - - // CastPlayer.SessionAvailabilityListener implementation. - - @Override - public void onCastSessionAvailable() { - setPlaybackLocation(PLAYBACK_REMOTE); - } - - @Override - public void onCastSessionUnavailable() { - setPlaybackLocation(PLAYBACK_LOCAL); - } - - // Internal methods. - - private static MediaQueueItem buildMediaQueueItem(DemoUtil.Sample sample) { - MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); - movieMetadata.putString(MediaMetadata.KEY_TITLE, sample.name); - MediaInfo mediaInfo = new MediaInfo.Builder(sample.uri) - .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED).setContentType(sample.mimeType) - .setMetadata(movieMetadata).build(); - return new MediaQueueItem.Builder(mediaInfo).build(); - } - - private static MediaSource buildMediaSource(DemoUtil.Sample sample) { - Uri uri = Uri.parse(sample.uri); - switch (sample.mimeType) { - case DemoUtil.MIME_TYPE_SS: - return new SsMediaSource(uri, DATA_SOURCE_FACTORY, - new DefaultSsChunkSource.Factory(DATA_SOURCE_FACTORY), null, null); - case DemoUtil.MIME_TYPE_DASH: - return new DashMediaSource(uri, DATA_SOURCE_FACTORY, - new DefaultDashChunkSource.Factory(DATA_SOURCE_FACTORY), null, null); - case DemoUtil.MIME_TYPE_HLS: - return new HlsMediaSource(uri, DATA_SOURCE_FACTORY, null, null); - case DemoUtil.MIME_TYPE_VIDEO_MP4: - return new ExtractorMediaSource(uri, DATA_SOURCE_FACTORY, new DefaultExtractorsFactory(), - null, null); - default: { - throw new IllegalStateException("Unsupported type: " + sample.mimeType); - } - } - } - - private void setPlaybackLocation(int playbackLocation) { - if (this.playbackLocation == playbackLocation) { - return; - } - - // View management. - if (playbackLocation == PLAYBACK_LOCAL) { - exoPlayerView.setVisibility(View.VISIBLE); - castControlView.hide(); - } else { - exoPlayerView.setVisibility(View.GONE); - castControlView.show(); - } - - long playbackPositionMs; - boolean playWhenReady; - if (this.playbackLocation == PLAYBACK_LOCAL) { - playbackPositionMs = exoPlayer.getCurrentPosition(); - playWhenReady = exoPlayer.getPlayWhenReady(); - exoPlayer.stop(); - } else /* this.playbackLocation == PLAYBACK_REMOTE */ { - playbackPositionMs = castPlayer.getCurrentPosition(); - playWhenReady = castPlayer.getPlayWhenReady(); - castPlayer.stop(); - } - - this.playbackLocation = playbackLocation; - if (currentSample != null) { - setCurrentSample(currentSample, playbackPositionMs, playWhenReady); - } - } - -} diff --git a/demos/cast/src/main/res/layout/main_activity.xml b/demos/cast/src/main/res/layout/main_activity.xml deleted file mode 100644 index 5d94931b64..0000000000 --- a/demos/cast/src/main/res/layout/main_activity.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - diff --git a/demos/cast/src/main/res/menu/menu.xml b/demos/cast/src/main/res/menu/menu.xml deleted file mode 100644 index 075ad34ec4..0000000000 --- a/demos/cast/src/main/res/menu/menu.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/demos/cast/src/main/res/mipmap-hdpi/ic_launcher.png b/demos/cast/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 52e8dc93d9..0000000000 Binary files a/demos/cast/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/demos/cast/src/main/res/mipmap-mdpi/ic_launcher.png b/demos/cast/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index b55576eff3..0000000000 Binary files a/demos/cast/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/demos/cast/src/main/res/mipmap-xhdpi/ic_launcher.png b/demos/cast/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index ca84d6a60e..0000000000 Binary files a/demos/cast/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/demos/cast/src/main/res/mipmap-xxhdpi/ic_launcher.png b/demos/cast/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 27ab9b1054..0000000000 Binary files a/demos/cast/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/demos/cast/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/demos/cast/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index d1eb9b78cf..0000000000 Binary files a/demos/cast/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/demos/cast/src/main/res/values/strings.xml b/demos/cast/src/main/res/values/strings.xml deleted file mode 100644 index 766e8972d9..0000000000 --- a/demos/cast/src/main/res/values/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Exo Cast Demo - - Cast - - DRM scheme not supported by this device. - - diff --git a/extensions/cast/README.md b/extensions/cast/README.md deleted file mode 100644 index 73f7041729..0000000000 --- a/extensions/cast/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# ExoPlayer Cast extension # - -## Description ## - -The cast extension is a [Player][] implementation that controls playback on a -Cast receiver app. - -[Player]: https://google.github.io/ExoPlayer/doc/reference/index.html?com/google/android/exoplayer2/Player.html - -## Getting the extension ## - -The easiest way to use the extension is to add it as a gradle dependency: - -```gradle -compile 'com.google.android.exoplayer:extension-cast:rX.X.X' -``` - -where `rX.X.X` is the version, which must match the version of the ExoPlayer -library being used. - -Alternatively, you can clone the ExoPlayer repository and depend on the module -locally. Instructions for doing this can be found in ExoPlayer's -[top level README][]. - -[top level README]: https://github.com/google/ExoPlayer/blob/release-v2/README.md - -## Using the extension ## - -Create a `CastPlayer` and use it to integrate Cast into your app using -ExoPlayer's common Player interface. You can try the Cast Extension to see how a -[PlaybackControlView][] can be used to control playback in a remote receiver app. - -[PlaybackControlView]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer2/ui/PlaybackControlView.html diff --git a/extensions/cast/build.gradle b/extensions/cast/build.gradle deleted file mode 100644 index 7d252332c9..0000000000 --- a/extensions/cast/build.gradle +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2017 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.library' - -android { - compileSdkVersion project.ext.compileSdkVersion - buildToolsVersion project.ext.buildToolsVersion - - defaultConfig { - minSdkVersion 14 - targetSdkVersion project.ext.targetSdkVersion - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } -} - -dependencies { - compile 'com.android.support:appcompat-v7:' + supportLibraryVersion - compile 'com.android.support:mediarouter-v7:' + supportLibraryVersion - compile 'com.google.android.gms:play-services-cast-framework:' + playServicesLibraryVersion - compile project(modulePrefix + 'library-core') - compile project(modulePrefix + 'library-ui') -} - -ext { - javadocTitle = 'Cast extension' -} -apply from: '../../javadoc_library.gradle' - -ext { - releaseArtifact = 'extension-cast' - releaseDescription = 'Cast extension for ExoPlayer.' -} -apply from: '../../publish.gradle' diff --git a/extensions/cast/src/main/AndroidManifest.xml b/extensions/cast/src/main/AndroidManifest.xml deleted file mode 100644 index c12fc1289f..0000000000 --- a/extensions/cast/src/main/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java deleted file mode 100644 index ffb06ed232..0000000000 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Copyright (C) 2017 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.cast; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.Log; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.source.TrackGroup; -import com.google.android.exoplayer2.source.TrackGroupArray; -import com.google.android.exoplayer2.trackselection.FixedTrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelection; -import com.google.android.exoplayer2.trackselection.TrackSelectionArray; -import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.MimeTypes; -import com.google.android.exoplayer2.util.Util; -import com.google.android.gms.cast.CastStatusCodes; -import com.google.android.gms.cast.MediaInfo; -import com.google.android.gms.cast.MediaQueueItem; -import com.google.android.gms.cast.MediaStatus; -import com.google.android.gms.cast.MediaTrack; -import com.google.android.gms.cast.framework.CastContext; -import com.google.android.gms.cast.framework.CastSession; -import com.google.android.gms.cast.framework.SessionManager; -import com.google.android.gms.cast.framework.SessionManagerListener; -import com.google.android.gms.cast.framework.media.RemoteMediaClient; -import com.google.android.gms.cast.framework.media.RemoteMediaClient.MediaChannelResult; -import com.google.android.gms.common.api.PendingResult; -import com.google.android.gms.common.api.ResultCallback; -import java.util.List; -import java.util.concurrent.CopyOnWriteArraySet; - -/** - * {@link Player} implementation that communicates with a Cast receiver app. - * - *

The behavior of this class depends on the underlying Cast session, which is obtained from the - * Cast context passed to {@link #CastPlayer}. To keep track of the session, - * {@link #isCastSessionAvailable()} can be queried and {@link SessionAvailabilityListener} can be - * implemented and attached to the player.

- * - *

If no session is available, the player state will remain unchanged and calls to methods that - * alter it will be ignored. Querying the player state is possible even when no session is - * available, in which case, the last observed receiver app state is reported.

- * - *

Methods should be called on the application's main thread.

- */ -public final class CastPlayer implements Player { - - /** - * Listener of changes in the cast session availability. - */ - public interface SessionAvailabilityListener { - - /** - * Called when a cast session becomes available to the player. - */ - void onCastSessionAvailable(); - - /** - * Called when the cast session becomes unavailable. - */ - void onCastSessionUnavailable(); - - } - - private static final String TAG = "CastPlayer"; - - private static final int RENDERER_COUNT = 3; - private static final int RENDERER_INDEX_VIDEO = 0; - private static final int RENDERER_INDEX_AUDIO = 1; - private static final int RENDERER_INDEX_TEXT = 2; - private static final long PROGRESS_REPORT_PERIOD_MS = 1000; - private static final TrackGroupArray EMPTY_TRACK_GROUP_ARRAY = new TrackGroupArray(); - private static final TrackSelectionArray EMPTY_TRACK_SELECTION_ARRAY = - new TrackSelectionArray(null, null, null); - private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0]; - - private final CastContext castContext; - private final Timeline.Window window; - private final Timeline.Period period; - - private RemoteMediaClient remoteMediaClient; - - // Result callbacks. - private final StatusListener statusListener; - private final SeekResultCallback seekResultCallback; - - // Listeners. - private final CopyOnWriteArraySet listeners; - private SessionAvailabilityListener sessionAvailabilityListener; - - // Internal state. - private CastTimeline currentTimeline; - private TrackGroupArray currentTrackGroups; - private TrackSelectionArray currentTrackSelection; - private int playbackState; - private int repeatMode; - private int currentWindowIndex; - private boolean playWhenReady; - private long lastReportedPositionMs; - private int pendingSeekCount; - private int pendingSeekWindowIndex; - private long pendingSeekPositionMs; - - /** - * @param castContext The context from which the cast session is obtained. - */ - public CastPlayer(CastContext castContext) { - this.castContext = castContext; - window = new Timeline.Window(); - period = new Timeline.Period(); - statusListener = new StatusListener(); - seekResultCallback = new SeekResultCallback(); - listeners = new CopyOnWriteArraySet<>(); - - SessionManager sessionManager = castContext.getSessionManager(); - sessionManager.addSessionManagerListener(statusListener, CastSession.class); - CastSession session = sessionManager.getCurrentCastSession(); - remoteMediaClient = session != null ? session.getRemoteMediaClient() : null; - - playbackState = STATE_IDLE; - repeatMode = REPEAT_MODE_OFF; - currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE; - currentTrackGroups = EMPTY_TRACK_GROUP_ARRAY; - currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY; - pendingSeekWindowIndex = C.INDEX_UNSET; - pendingSeekPositionMs = C.TIME_UNSET; - updateInternalState(); - } - - // Media Queue manipulation methods. - - /** - * Loads a single item media queue. If no session is available, does nothing. - * - * @param item The item to load. - * @param positionMs The position at which the playback should start in milliseconds relative to - * the start of the item at {@code startIndex}. - * @return The Cast {@code PendingResult}, or null if no session is available. - */ - public PendingResult loadItem(MediaQueueItem item, long positionMs) { - return loadItems(new MediaQueueItem[] {item}, 0, positionMs, REPEAT_MODE_OFF); - } - - /** - * Loads a media queue. If no session is available, does nothing. - * - * @param items The items to load. - * @param startIndex The index of the item at which playback should start. - * @param positionMs The position at which the playback should start in milliseconds relative to - * the start of the item at {@code startIndex}. - * @param repeatMode The repeat mode for the created media queue. - * @return The Cast {@code PendingResult}, or null if no session is available. - */ - public PendingResult loadItems(MediaQueueItem[] items, int startIndex, - long positionMs, @RepeatMode int repeatMode) { - if (remoteMediaClient != null) { - return remoteMediaClient.queueLoad(items, startIndex, getCastRepeatMode(repeatMode), - positionMs, null); - } - return null; - } - - /** - * Appends a sequence of items to the media queue. If no media queue exists, does nothing. - * - * @param items The items to append. - * @return The Cast {@code PendingResult}, or null if no media queue exists. - */ - public PendingResult addItems(MediaQueueItem... items) { - return addItems(MediaQueueItem.INVALID_ITEM_ID, items); - } - - /** - * Inserts a sequence of items into the media queue. If no media queue or period with id - * {@code periodId} exist, does nothing. - * - * @param periodId The id of the period ({@see #getCurrentTimeline}) that corresponds to the item - * that will follow immediately after the inserted items. - * @param items The items to insert. - * @return The Cast {@code PendingResult}, or null if no media queue or no period with id - * {@code periodId} exist. - */ - public PendingResult addItems(int periodId, MediaQueueItem... items) { - if (getMediaStatus() != null && (periodId == MediaQueueItem.INVALID_ITEM_ID - || currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET)) { - return remoteMediaClient.queueInsertItems(items, periodId, null); - } - return null; - } - - /** - * Removes an item from the media queue. If no media queue or period with id {@code periodId} - * exist, does nothing. - * - * @param periodId The id of the period ({@see #getCurrentTimeline}) that corresponds to the item - * to remove. - * @return The Cast {@code PendingResult}, or null if no media queue or no period with id - * {@code periodId} exist. - */ - public PendingResult removeItem(int periodId) { - if (getMediaStatus() != null && currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET) { - return remoteMediaClient.queueRemoveItem(periodId, null); - } - return null; - } - - /** - * Moves an existing item within the media queue. If no media queue or period with id - * {@code periodId} exist, does nothing. - * - * @param periodId The id of the period ({@see #getCurrentTimeline}) that corresponds to the item - * to move. - * @param newIndex The target index of the item in the media queue. Must be in the range - * 0 <= index < {@link Timeline#getPeriodCount()}, as provided by - * {@link #getCurrentTimeline()}. - * @return The Cast {@code PendingResult}, or null if no media queue or no period with id - * {@code periodId} exist. - */ - public PendingResult moveItem(int periodId, int newIndex) { - Assertions.checkArgument(newIndex >= 0 && newIndex < currentTimeline.getPeriodCount()); - if (getMediaStatus() != null && currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET) { - return remoteMediaClient.queueMoveItemToNewIndex(periodId, newIndex, null); - } - return null; - } - - /** - * Returns the item that corresponds to the period with the given id, or null if no media queue or - * period with id {@code periodId} exist. - * - * @param periodId The id of the period ({@see #getCurrentTimeline}) that corresponds to the item - * to get. - * @return The item that corresponds to the period with the given id, or null if no media queue or - * period with id {@code periodId} exist. - */ - public MediaQueueItem getItem(int periodId) { - MediaStatus mediaStatus = getMediaStatus(); - return mediaStatus != null && currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET - ? mediaStatus.getItemById(periodId) : null; - } - - // CastSession methods. - - /** - * Returns whether a cast session is available. - */ - public boolean isCastSessionAvailable() { - return remoteMediaClient != null; - } - - /** - * Sets a listener for updates on the cast session availability. - * - * @param listener The {@link SessionAvailabilityListener}. - */ - public void setSessionAvailabilityListener(SessionAvailabilityListener listener) { - sessionAvailabilityListener = listener; - } - - // Player implementation. - - @Override - public void addListener(EventListener listener) { - listeners.add(listener); - } - - @Override - public void removeListener(EventListener listener) { - listeners.remove(listener); - } - - @Override - public int getPlaybackState() { - return playbackState; - } - - @Override - public void setPlayWhenReady(boolean playWhenReady) { - if (remoteMediaClient == null) { - return; - } - if (playWhenReady) { - remoteMediaClient.play(); - } else { - remoteMediaClient.pause(); - } - } - - @Override - public boolean getPlayWhenReady() { - return playWhenReady; - } - - @Override - public void seekToDefaultPosition() { - seekTo(0); - } - - @Override - public void seekToDefaultPosition(int windowIndex) { - seekTo(windowIndex, 0); - } - - @Override - public void seekTo(long positionMs) { - seekTo(getCurrentWindowIndex(), positionMs); - } - - @Override - public void seekTo(int windowIndex, long positionMs) { - MediaStatus mediaStatus = getMediaStatus(); - if (mediaStatus != null) { - if (getCurrentWindowIndex() != windowIndex) { - remoteMediaClient.queueJumpToItem((int) currentTimeline.getPeriod(windowIndex, period).uid, - positionMs, null).setResultCallback(seekResultCallback); - } else { - remoteMediaClient.seek(positionMs).setResultCallback(seekResultCallback); - } - pendingSeekCount++; - pendingSeekWindowIndex = windowIndex; - pendingSeekPositionMs = positionMs; - for (EventListener listener : listeners) { - listener.onPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK); - } - } else if (pendingSeekCount == 0) { - for (EventListener listener : listeners) { - listener.onSeekProcessed(); - } - } - } - - @Override - public void setPlaybackParameters(@Nullable PlaybackParameters playbackParameters) { - // Unsupported by the RemoteMediaClient API. Do nothing. - } - - @Override - public PlaybackParameters getPlaybackParameters() { - return PlaybackParameters.DEFAULT; - } - - @Override - public void stop() { - if (remoteMediaClient != null) { - remoteMediaClient.stop(); - } - } - - @Override - public void release() { - castContext.getSessionManager().removeSessionManagerListener(statusListener, CastSession.class); - } - - @Override - public int getRendererCount() { - // We assume there are three renderers: video, audio, and text. - return RENDERER_COUNT; - } - - @Override - public int getRendererType(int index) { - switch (index) { - case RENDERER_INDEX_VIDEO: - return C.TRACK_TYPE_VIDEO; - case RENDERER_INDEX_AUDIO: - return C.TRACK_TYPE_AUDIO; - case RENDERER_INDEX_TEXT: - return C.TRACK_TYPE_TEXT; - default: - throw new IndexOutOfBoundsException(); - } - } - - @Override - public void setRepeatMode(@RepeatMode int repeatMode) { - if (remoteMediaClient != null) { - remoteMediaClient.queueSetRepeatMode(getCastRepeatMode(repeatMode), null); - } - } - - @Override - @RepeatMode public int getRepeatMode() { - return repeatMode; - } - - @Override - public void setShuffleModeEnabled(boolean shuffleModeEnabled) { - // TODO: Support shuffle mode. - } - - @Override - public boolean getShuffleModeEnabled() { - // TODO: Support shuffle mode. - return false; - } - - @Override - public TrackSelectionArray getCurrentTrackSelections() { - return currentTrackSelection; - } - - @Override - public TrackGroupArray getCurrentTrackGroups() { - return currentTrackGroups; - } - - @Override - public Timeline getCurrentTimeline() { - return currentTimeline; - } - - @Override - @Nullable public Object getCurrentManifest() { - return null; - } - - @Override - public int getCurrentPeriodIndex() { - return getCurrentWindowIndex(); - } - - @Override - public int getCurrentWindowIndex() { - return pendingSeekWindowIndex != C.INDEX_UNSET ? pendingSeekWindowIndex : currentWindowIndex; - } - - @Override - public int getNextWindowIndex() { - return C.INDEX_UNSET; - } - - @Override - public int getPreviousWindowIndex() { - return C.INDEX_UNSET; - } - - @Override - public long getDuration() { - return currentTimeline.isEmpty() ? C.TIME_UNSET - : currentTimeline.getWindow(getCurrentWindowIndex(), window).getDurationMs(); - } - - @Override - public long getCurrentPosition() { - return pendingSeekPositionMs != C.TIME_UNSET ? pendingSeekPositionMs - : remoteMediaClient != null ? remoteMediaClient.getApproximateStreamPosition() - : lastReportedPositionMs; - } - - @Override - public long getBufferedPosition() { - return getCurrentPosition(); - } - - @Override - public int getBufferedPercentage() { - long position = getBufferedPosition(); - long duration = getDuration(); - return position == C.TIME_UNSET || duration == C.TIME_UNSET ? 0 - : duration == 0 ? 100 - : Util.constrainValue((int) ((position * 100) / duration), 0, 100); - } - - @Override - public boolean isCurrentWindowDynamic() { - return !currentTimeline.isEmpty() - && currentTimeline.getWindow(getCurrentWindowIndex(), window).isDynamic; - } - - @Override - public boolean isCurrentWindowSeekable() { - return !currentTimeline.isEmpty() - && currentTimeline.getWindow(getCurrentWindowIndex(), window).isSeekable; - } - - @Override - public boolean isPlayingAd() { - return false; - } - - @Override - public int getCurrentAdGroupIndex() { - return C.INDEX_UNSET; - } - - @Override - public int getCurrentAdIndexInAdGroup() { - return C.INDEX_UNSET; - } - - @Override - public boolean isLoading() { - return false; - } - - @Override - public long getContentPosition() { - return getCurrentPosition(); - } - - // Internal methods. - - public void updateInternalState() { - if (remoteMediaClient == null) { - // There is no session. We leave the state of the player as it is now. - return; - } - - int playbackState = fetchPlaybackState(remoteMediaClient); - boolean playWhenReady = !remoteMediaClient.isPaused(); - if (this.playbackState != playbackState - || this.playWhenReady != playWhenReady) { - this.playbackState = playbackState; - this.playWhenReady = playWhenReady; - for (EventListener listener : listeners) { - listener.onPlayerStateChanged(this.playWhenReady, this.playbackState); - } - } - @RepeatMode int repeatMode = fetchRepeatMode(remoteMediaClient); - if (this.repeatMode != repeatMode) { - this.repeatMode = repeatMode; - for (EventListener listener : listeners) { - listener.onRepeatModeChanged(repeatMode); - } - } - int currentWindowIndex = fetchCurrentWindowIndex(getMediaStatus()); - if (this.currentWindowIndex != currentWindowIndex && pendingSeekCount == 0) { - this.currentWindowIndex = currentWindowIndex; - for (EventListener listener : listeners) { - listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION); - } - } - if (updateTracksAndSelections()) { - for (EventListener listener : listeners) { - listener.onTracksChanged(currentTrackGroups, currentTrackSelection); - } - } - maybeUpdateTimelineAndNotify(); - } - - private void maybeUpdateTimelineAndNotify() { - if (updateTimeline()) { - for (EventListener listener : listeners) { - listener.onTimelineChanged(currentTimeline, null); - } - } - } - - /** - * Updates the current timeline and returns whether it has changed. - */ - private boolean updateTimeline() { - MediaStatus mediaStatus = getMediaStatus(); - if (mediaStatus == null) { - boolean hasChanged = currentTimeline != CastTimeline.EMPTY_CAST_TIMELINE; - currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE; - return hasChanged; - } - - List items = mediaStatus.getQueueItems(); - if (!currentTimeline.represents(items)) { - currentTimeline = !items.isEmpty() ? new CastTimeline(mediaStatus.getQueueItems()) - : CastTimeline.EMPTY_CAST_TIMELINE; - return true; - } - return false; - } - - /** - * Updates the internal tracks and selection and returns whether they have changed. - */ - private boolean updateTracksAndSelections() { - if (remoteMediaClient == null) { - // There is no session. We leave the state of the player as it is now. - return false; - } - - MediaStatus mediaStatus = getMediaStatus(); - MediaInfo mediaInfo = mediaStatus != null ? mediaStatus.getMediaInfo() : null; - List castMediaTracks = mediaInfo != null ? mediaInfo.getMediaTracks() : null; - if (castMediaTracks == null || castMediaTracks.isEmpty()) { - boolean hasChanged = currentTrackGroups != EMPTY_TRACK_GROUP_ARRAY; - currentTrackGroups = EMPTY_TRACK_GROUP_ARRAY; - currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY; - return hasChanged; - } - long[] activeTrackIds = mediaStatus.getActiveTrackIds(); - if (activeTrackIds == null) { - activeTrackIds = EMPTY_TRACK_ID_ARRAY; - } - - TrackGroup[] trackGroups = new TrackGroup[castMediaTracks.size()]; - TrackSelection[] trackSelections = new TrackSelection[RENDERER_COUNT]; - for (int i = 0; i < castMediaTracks.size(); i++) { - MediaTrack mediaTrack = castMediaTracks.get(i); - trackGroups[i] = new TrackGroup(CastUtils.mediaTrackToFormat(mediaTrack)); - - long id = mediaTrack.getId(); - int trackType = MimeTypes.getTrackType(mediaTrack.getContentType()); - int rendererIndex = getRendererIndexForTrackType(trackType); - if (isTrackActive(id, activeTrackIds) && rendererIndex != C.INDEX_UNSET - && trackSelections[rendererIndex] == null) { - trackSelections[rendererIndex] = new FixedTrackSelection(trackGroups[i], 0); - } - } - TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups); - TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections); - - if (!newTrackGroups.equals(currentTrackGroups) - || !newTrackSelections.equals(currentTrackSelection)) { - currentTrackSelection = new TrackSelectionArray(trackSelections); - currentTrackGroups = new TrackGroupArray(trackGroups); - return true; - } - return false; - } - - private void setRemoteMediaClient(@Nullable RemoteMediaClient remoteMediaClient) { - if (this.remoteMediaClient == remoteMediaClient) { - // Do nothing. - return; - } - if (this.remoteMediaClient != null) { - this.remoteMediaClient.removeListener(statusListener); - this.remoteMediaClient.removeProgressListener(statusListener); - } - this.remoteMediaClient = remoteMediaClient; - if (remoteMediaClient != null) { - if (sessionAvailabilityListener != null) { - sessionAvailabilityListener.onCastSessionAvailable(); - } - remoteMediaClient.addListener(statusListener); - remoteMediaClient.addProgressListener(statusListener, PROGRESS_REPORT_PERIOD_MS); - updateInternalState(); - } else { - if (sessionAvailabilityListener != null) { - sessionAvailabilityListener.onCastSessionUnavailable(); - } - } - } - - private @Nullable MediaStatus getMediaStatus() { - return remoteMediaClient != null ? remoteMediaClient.getMediaStatus() : null; - } - - /** - * Retrieves the playback state from {@code remoteMediaClient} and maps it into a {@link Player} - * state - */ - private static int fetchPlaybackState(RemoteMediaClient remoteMediaClient) { - int receiverAppStatus = remoteMediaClient.getPlayerState(); - switch (receiverAppStatus) { - case MediaStatus.PLAYER_STATE_BUFFERING: - return STATE_BUFFERING; - case MediaStatus.PLAYER_STATE_PLAYING: - case MediaStatus.PLAYER_STATE_PAUSED: - return STATE_READY; - case MediaStatus.PLAYER_STATE_IDLE: - case MediaStatus.PLAYER_STATE_UNKNOWN: - default: - return STATE_IDLE; - } - } - - /** - * Retrieves the repeat mode from {@code remoteMediaClient} and maps it into a - * {@link Player.RepeatMode}. - */ - @RepeatMode - private static int fetchRepeatMode(RemoteMediaClient remoteMediaClient) { - MediaStatus mediaStatus = remoteMediaClient.getMediaStatus(); - if (mediaStatus == null) { - // No media session active, yet. - return REPEAT_MODE_OFF; - } - int castRepeatMode = mediaStatus.getQueueRepeatMode(); - switch (castRepeatMode) { - case MediaStatus.REPEAT_MODE_REPEAT_SINGLE: - return REPEAT_MODE_ONE; - case MediaStatus.REPEAT_MODE_REPEAT_ALL: - case MediaStatus.REPEAT_MODE_REPEAT_ALL_AND_SHUFFLE: - return REPEAT_MODE_ALL; - case MediaStatus.REPEAT_MODE_REPEAT_OFF: - return REPEAT_MODE_OFF; - default: - throw new IllegalStateException(); - } - } - - /** - * Retrieves the current item index from {@code mediaStatus} and maps it into a window index. If - * there is no media session, returns 0. - */ - private static int fetchCurrentWindowIndex(@Nullable MediaStatus mediaStatus) { - Integer currentItemId = mediaStatus != null - ? mediaStatus.getIndexById(mediaStatus.getCurrentItemId()) : null; - return currentItemId != null ? currentItemId : 0; - } - - private static boolean isTrackActive(long id, long[] activeTrackIds) { - for (long activeTrackId : activeTrackIds) { - if (activeTrackId == id) { - return true; - } - } - return false; - } - - private static int getRendererIndexForTrackType(int trackType) { - return trackType == C.TRACK_TYPE_VIDEO ? RENDERER_INDEX_VIDEO - : trackType == C.TRACK_TYPE_AUDIO ? RENDERER_INDEX_AUDIO - : trackType == C.TRACK_TYPE_TEXT ? RENDERER_INDEX_TEXT - : C.INDEX_UNSET; - } - - private static int getCastRepeatMode(@RepeatMode int repeatMode) { - switch (repeatMode) { - case REPEAT_MODE_ONE: - return MediaStatus.REPEAT_MODE_REPEAT_SINGLE; - case REPEAT_MODE_ALL: - return MediaStatus.REPEAT_MODE_REPEAT_ALL; - case REPEAT_MODE_OFF: - return MediaStatus.REPEAT_MODE_REPEAT_OFF; - default: - throw new IllegalArgumentException(); - } - } - - private final class StatusListener implements RemoteMediaClient.Listener, - SessionManagerListener, RemoteMediaClient.ProgressListener { - - // RemoteMediaClient.ProgressListener implementation. - - @Override - public void onProgressUpdated(long progressMs, long unusedDurationMs) { - lastReportedPositionMs = progressMs; - } - - // RemoteMediaClient.Listener implementation. - - @Override - public void onStatusUpdated() { - updateInternalState(); - } - - @Override - public void onMetadataUpdated() {} - - @Override - public void onQueueStatusUpdated() { - maybeUpdateTimelineAndNotify(); - } - - @Override - public void onPreloadStatusUpdated() {} - - @Override - public void onSendingRemoteMediaRequest() {} - - @Override - public void onAdBreakStatusUpdated() {} - - - // SessionManagerListener implementation. - - @Override - public void onSessionStarted(CastSession castSession, String s) { - setRemoteMediaClient(castSession.getRemoteMediaClient()); - } - - @Override - public void onSessionResumed(CastSession castSession, boolean b) { - setRemoteMediaClient(castSession.getRemoteMediaClient()); - } - - @Override - public void onSessionEnded(CastSession castSession, int i) { - setRemoteMediaClient(null); - } - - @Override - public void onSessionSuspended(CastSession castSession, int i) { - setRemoteMediaClient(null); - } - - @Override - public void onSessionResumeFailed(CastSession castSession, int statusCode) { - Log.e(TAG, "Session resume failed. Error code " + statusCode + ": " - + CastUtils.getLogString(statusCode)); - } - - @Override - public void onSessionStarting(CastSession castSession) { - // Do nothing. - } - - @Override - public void onSessionStartFailed(CastSession castSession, int statusCode) { - Log.e(TAG, "Session start failed. Error code " + statusCode + ": " - + CastUtils.getLogString(statusCode)); - } - - @Override - public void onSessionEnding(CastSession castSession) { - // Do nothing. - } - - @Override - public void onSessionResuming(CastSession castSession, String s) { - // Do nothing. - } - - } - - // Result callbacks hooks. - - private final class SeekResultCallback implements ResultCallback { - - @Override - public void onResult(@NonNull MediaChannelResult result) { - int statusCode = result.getStatus().getStatusCode(); - if (statusCode != CastStatusCodes.SUCCESS && statusCode != CastStatusCodes.REPLACED) { - Log.e(TAG, "Seek failed. Error code " + statusCode + ": " - + CastUtils.getLogString(statusCode)); - } - if (--pendingSeekCount == 0) { - pendingSeekWindowIndex = C.INDEX_UNSET; - pendingSeekPositionMs = C.TIME_UNSET; - for (EventListener listener : listeners) { - listener.onSeekProcessed(); - } - } - } - } - -} diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java deleted file mode 100644 index 39b57148b2..0000000000 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastTimeline.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2017 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.cast; - -import android.util.SparseIntArray; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Timeline; -import com.google.android.gms.cast.MediaInfo; -import com.google.android.gms.cast.MediaQueueItem; -import java.util.Collections; -import java.util.List; - -/** - * A {@link Timeline} for Cast media queues. - */ -/* package */ final class CastTimeline extends Timeline { - - public static final CastTimeline EMPTY_CAST_TIMELINE = - new CastTimeline(Collections.emptyList()); - - private final SparseIntArray idsToIndex; - private final int[] ids; - private final long[] durationsUs; - private final long[] defaultPositionsUs; - - public CastTimeline(List items) { - int itemCount = items.size(); - int index = 0; - idsToIndex = new SparseIntArray(itemCount); - ids = new int[itemCount]; - durationsUs = new long[itemCount]; - defaultPositionsUs = new long[itemCount]; - for (MediaQueueItem item : items) { - int itemId = item.getItemId(); - ids[index] = itemId; - idsToIndex.put(itemId, index); - durationsUs[index] = getStreamDurationUs(item.getMedia()); - defaultPositionsUs[index] = (long) (item.getStartTime() * C.MICROS_PER_SECOND); - index++; - } - } - - @Override - public int getWindowCount() { - return ids.length; - } - - @Override - public Window getWindow(int windowIndex, Window window, boolean setIds, - long defaultPositionProjectionUs) { - long durationUs = durationsUs[windowIndex]; - boolean isDynamic = durationUs == C.TIME_UNSET; - return window.set(ids[windowIndex], C.TIME_UNSET, C.TIME_UNSET, !isDynamic, isDynamic, - defaultPositionsUs[windowIndex], durationUs, windowIndex, windowIndex, 0); - } - - @Override - public int getPeriodCount() { - return ids.length; - } - - @Override - public Period getPeriod(int periodIndex, Period period, boolean setIds) { - int id = ids[periodIndex]; - return period.set(id, id, periodIndex, durationsUs[periodIndex], 0); - } - - @Override - public int getIndexOfPeriod(Object uid) { - return uid instanceof Integer ? idsToIndex.get((int) uid, C.INDEX_UNSET) : C.INDEX_UNSET; - } - - /** - * Returns whether the timeline represents a given {@code MediaQueueItem} list. - * - * @param items The {@code MediaQueueItem} list. - * @return Whether the timeline represents {@code items}. - */ - /* package */ boolean represents(List items) { - if (ids.length != items.size()) { - return false; - } - int index = 0; - for (MediaQueueItem item : items) { - if (ids[index] != item.getItemId() - || durationsUs[index] != getStreamDurationUs(item.getMedia()) - || defaultPositionsUs[index] != (long) (item.getStartTime() * C.MICROS_PER_SECOND)) { - return false; - } - index++; - } - return true; - } - - private static long getStreamDurationUs(MediaInfo mediaInfo) { - long durationMs = mediaInfo != null ? mediaInfo.getStreamDuration() - : MediaInfo.UNKNOWN_DURATION; - return durationMs != MediaInfo.UNKNOWN_DURATION ? C.msToUs(durationMs) : C.TIME_UNSET; - } - -} diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastUtils.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastUtils.java deleted file mode 100644 index de60437444..0000000000 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastUtils.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2017 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.cast; - -import com.google.android.exoplayer2.Format; -import com.google.android.gms.cast.CastStatusCodes; -import com.google.android.gms.cast.MediaTrack; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -/** - * Utility methods for ExoPlayer/Cast integration. - */ -/* package */ final class CastUtils { - - private static final Map CAST_STATUS_CODE_TO_STRING; - - /** - * Returns a descriptive log string for the given {@code statusCode}, or "Unknown." if not one of - * {@link CastStatusCodes}. - * - * @param statusCode A Cast API status code. - * @return A descriptive log string for the given {@code statusCode}, or "Unknown." if not one of - * {@link CastStatusCodes}. - */ - public static String getLogString(int statusCode) { - String description = CAST_STATUS_CODE_TO_STRING.get(statusCode); - return description != null ? description : "Unknown."; - } - - /** - * Creates a {@link Format} instance containing all information contained in the given - * {@link MediaTrack} object. - * - * @param mediaTrack The {@link MediaTrack}. - * @return The equivalent {@link Format}. - */ - public static Format mediaTrackToFormat(MediaTrack mediaTrack) { - return Format.createContainerFormat(mediaTrack.getContentId(), mediaTrack.getContentType(), - null, null, Format.NO_VALUE, 0, mediaTrack.getLanguage()); - } - - static { - HashMap statusCodeToString = new HashMap<>(); - statusCodeToString.put(CastStatusCodes.APPLICATION_NOT_FOUND, - "A requested application could not be found."); - statusCodeToString.put(CastStatusCodes.APPLICATION_NOT_RUNNING, - "A requested application is not currently running."); - statusCodeToString.put(CastStatusCodes.AUTHENTICATION_FAILED, "Authentication failure."); - statusCodeToString.put(CastStatusCodes.CANCELED, "An in-progress request has been " - + "canceled, most likely because another action has preempted it."); - statusCodeToString.put(CastStatusCodes.ERROR_SERVICE_CREATION_FAILED, - "The Cast Remote Display service could not be created."); - statusCodeToString.put(CastStatusCodes.ERROR_SERVICE_DISCONNECTED, - "The Cast Remote Display service was disconnected."); - statusCodeToString.put(CastStatusCodes.FAILED, "The in-progress request failed."); - statusCodeToString.put(CastStatusCodes.INTERNAL_ERROR, "An internal error has occurred."); - statusCodeToString.put(CastStatusCodes.INTERRUPTED, - "A blocking call was interrupted while waiting and did not run to completion."); - statusCodeToString.put(CastStatusCodes.INVALID_REQUEST, "An invalid request was made."); - statusCodeToString.put(CastStatusCodes.MESSAGE_SEND_BUFFER_TOO_FULL, "A message could " - + "not be sent because there is not enough room in the send buffer at this time."); - statusCodeToString.put(CastStatusCodes.MESSAGE_TOO_LARGE, - "A message could not be sent because it is too large."); - statusCodeToString.put(CastStatusCodes.NETWORK_ERROR, "Network I/O error."); - statusCodeToString.put(CastStatusCodes.NOT_ALLOWED, - "The request was disallowed and could not be completed."); - statusCodeToString.put(CastStatusCodes.REPLACED, - "The request's progress is no longer being tracked because another request of the same type" - + " has been made before the first request completed."); - statusCodeToString.put(CastStatusCodes.SUCCESS, "Success."); - statusCodeToString.put(CastStatusCodes.TIMEOUT, "An operation has timed out."); - statusCodeToString.put(CastStatusCodes.UNKNOWN_ERROR, - "An unknown, unexpected error has occurred."); - CAST_STATUS_CODE_TO_STRING = Collections.unmodifiableMap(statusCodeToString); - } - - private CastUtils() {} - -} diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/DefaultCastOptionsProvider.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/DefaultCastOptionsProvider.java deleted file mode 100644 index 06f0bec971..0000000000 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/DefaultCastOptionsProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2017 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.cast; - -import android.content.Context; -import com.google.android.gms.cast.CastMediaControlIntent; -import com.google.android.gms.cast.framework.CastOptions; -import com.google.android.gms.cast.framework.OptionsProvider; -import com.google.android.gms.cast.framework.SessionProvider; -import java.util.List; - -/** - * A convenience {@link OptionsProvider} to target the default cast receiver app. - */ -public final class DefaultCastOptionsProvider implements OptionsProvider { - - @Override - public CastOptions getCastOptions(Context context) { - return new CastOptions.Builder() - .setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID) - .setStopReceiverApplicationWhenEndingSession(true).build(); - } - - @Override - public List getAdditionalSessionProviders(Context context) { - return null; - } - -} diff --git a/settings.gradle b/settings.gradle index d4530d67b7..0a404aad73 100644 --- a/settings.gradle +++ b/settings.gradle @@ -19,11 +19,9 @@ if (gradle.ext.has('exoplayerModulePrefix')) { } include modulePrefix + 'demo' -include modulePrefix + 'demo-cast' include modulePrefix + 'demo-ima' 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-ima').projectDir = new File(rootDir, 'demos/ima') project(modulePrefix + 'playbacktests').projectDir = new File(rootDir, 'playbacktests')