Commit 04d76fa8 by aquilescanta Committed by Oliver Woodman

Allow easier ExoPlayer/Cast integration

This CL adds the fundamental pieces for ExoPlayer/Cast integration and includes a
demo app to showcase this functionality. However, media queues should be supported
in the first release of this extension.

Issue:#2283

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=165576892
parent 1cfd246c
...@@ -22,6 +22,7 @@ project.ext { ...@@ -22,6 +22,7 @@ project.ext {
buildToolsVersion = '26' buildToolsVersion = '26'
testSupportLibraryVersion = '0.5' testSupportLibraryVersion = '0.5'
supportLibraryVersion = '26.0.1' supportLibraryVersion = '26.0.1'
playServicesLibraryVersion = '11.0.2'
dexmakerVersion = '1.2' dexmakerVersion = '1.2'
mockitoVersion = '1.9.5' mockitoVersion = '1.9.5'
releaseVersion = 'r2.5.1' releaseVersion = 'r2.5.1'
......
...@@ -28,6 +28,7 @@ include modulePrefix + 'extension-ffmpeg' ...@@ -28,6 +28,7 @@ include modulePrefix + 'extension-ffmpeg'
include modulePrefix + 'extension-flac' include modulePrefix + 'extension-flac'
include modulePrefix + 'extension-gvr' include modulePrefix + 'extension-gvr'
include modulePrefix + 'extension-ima' include modulePrefix + 'extension-ima'
include modulePrefix + 'extension-cast'
include modulePrefix + 'extension-mediasession' include modulePrefix + 'extension-mediasession'
include modulePrefix + 'extension-okhttp' include modulePrefix + 'extension-okhttp'
include modulePrefix + 'extension-opus' include modulePrefix + 'extension-opus'
...@@ -46,6 +47,7 @@ project(modulePrefix + 'extension-ffmpeg').projectDir = new File(rootDir, 'exten ...@@ -46,6 +47,7 @@ project(modulePrefix + 'extension-ffmpeg').projectDir = new File(rootDir, 'exten
project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac') project(modulePrefix + 'extension-flac').projectDir = new File(rootDir, 'extensions/flac')
project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr') project(modulePrefix + 'extension-gvr').projectDir = new File(rootDir, 'extensions/gvr')
project(modulePrefix + 'extension-ima').projectDir = new File(rootDir, 'extensions/ima') 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-mediasession').projectDir = new File(rootDir, 'extensions/mediasession')
project(modulePrefix + 'extension-okhttp').projectDir = new File(rootDir, 'extensions/okhttp') project(modulePrefix + 'extension-okhttp').projectDir = new File(rootDir, 'extensions/okhttp')
project(modulePrefix + 'extension-opus').projectDir = new File(rootDir, 'extensions/opus') project(modulePrefix + 'extension-opus').projectDir = new File(rootDir, 'extensions/opus')
......
# Cast demo application #
This folder contains a demo application that showcases ExoPlayer integration
with Google Cast.
// 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')
}
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.exoplayer2.castdemo"
android:versionCode="0001"
android:versionName="0.0.1">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="26"/>
<application android:label="@string/application_name" android:icon="@mipmap/ic_launcher"
android:largeHeap="true" android:allowBackup="false">
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider" />
<activity android:name="com.google.android.exoplayer2.castdemo.MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
android:launchMode="singleTop" android:label="@string/application_name"
android:theme="@style/Theme.AppCompat">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>
</application>
</manifest>
/*
* 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 CastDemoUtil {
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<Sample> 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 type;
/**
* @param uri See {@link #uri}.
* @param name See {@link #name}.
* @param type See {@link #type}.
*/
public Sample(String uri, String name, String type) {
this.uri = uri;
this.name = name;
this.type = type;
}
@Override
public String toString() {
return name;
}
}
static {
// App samples.
ArrayList<Sample> 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 CastDemoUtil() {}
}
/*
* 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.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.exoplayer2.util.Util;
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 = (SimpleExoPlayerView) findViewById(R.id.player_view);
simpleExoPlayerView.requestFocus();
castControlView = (PlaybackControlView) findViewById(R.id.cast_control_view);
ListView sampleList = (ListView) 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 onStart() {
super.onStart();
if (Util.SDK_INT > 23) {
setupPlayerManager();
}
}
@Override
public void onResume() {
super.onResume();
if ((Util.SDK_INT <= 23)) {
setupPlayerManager();
}
}
@Override
public void onPause() {
super.onPause();
if (Util.SDK_INT <= 23) {
releasePlayerManager();
}
}
@Override
public void onStop() {
super.onStop();
if (Util.SDK_INT > 23) {
releasePlayerManager();
}
}
// 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);
}
// Internal methods.
private void setupPlayerManager() {
playerManager = new PlayerManager(simpleExoPlayerView, castControlView,
getApplicationContext());
}
private void releasePlayerManager() {
playerManager.release();
playerManager = null;
}
// User controls.
private final class SampleListAdapter extends ArrayAdapter<CastDemoUtil.Sample> {
public SampleListAdapter() {
super(getApplicationContext(), android.R.layout.simple_list_item_1, CastDemoUtil.SAMPLES);
}
@Override
public View getView(int position, View convertView, 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) {
CastDemoUtil.Sample currentSample = CastDemoUtil.SAMPLES.get(position);
playerManager.setCurrentSample(currentSample, 0, true);
}
}
}
}
/*
* 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.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 CastContext castContext;
private final SimpleExoPlayer exoPlayer;
private final CastPlayer castPlayer;
private int playbackLocation;
private CastDemoUtil.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;
castContext = CastContext.getSharedInstance(context);
DefaultTrackSelector trackSelector = new DefaultTrackSelector(BANDWIDTH_METER);
RenderersFactory renderersFactory = new DefaultRenderersFactory(context, null);
exoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
exoPlayerView.setPlayer(exoPlayer);
castPlayer = new CastPlayer(castContext);
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 CastDemoUtil} 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(CastDemoUtil.Sample currentSample, long positionMs,
boolean playWhenReady) {
this.currentSample = currentSample;
if (playbackLocation == PLAYBACK_REMOTE) {
castPlayer.load(currentSample.name, currentSample.uri, currentSample.type, positionMs,
playWhenReady);
} else /* playbackLocation == PLAYBACK_LOCAL */ {
exoPlayer.setPlayWhenReady(playWhenReady);
exoPlayer.seekTo(positionMs);
exoPlayer.prepare(buildMediaSource(currentSample), true, true);
}
}
/**
* 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 MediaSource buildMediaSource(CastDemoUtil.Sample sample) {
Uri uri = Uri.parse(sample.uri);
switch (sample.type) {
case CastDemoUtil.MIME_TYPE_SS:
return new SsMediaSource(uri, DATA_SOURCE_FACTORY,
new DefaultSsChunkSource.Factory(DATA_SOURCE_FACTORY), null, null);
case CastDemoUtil.MIME_TYPE_DASH:
return new DashMediaSource(uri, DATA_SOURCE_FACTORY,
new DefaultDashChunkSource.Factory(DATA_SOURCE_FACTORY), null, null);
case CastDemoUtil.MIME_TYPE_HLS:
return new HlsMediaSource(uri, DATA_SOURCE_FACTORY, null, null);
case CastDemoUtil.MIME_TYPE_VIDEO_MP4:
return new ExtractorMediaSource(uri, DATA_SOURCE_FACTORY, new DefaultExtractorsFactory(),
null, null);
default: {
throw new IllegalStateException("Unsupported type: " + sample.type);
}
}
}
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 = 0;
boolean playWhenReady = true;
if (exoPlayer != null) {
playbackPositionMs = exoPlayer.getCurrentPosition();
playWhenReady = exoPlayer.getPlayWhenReady();
} else if (this.playbackLocation == PLAYBACK_REMOTE) {
playbackPositionMs = castPlayer.getCurrentPosition();
playWhenReady = castPlayer.getPlayWhenReady();
}
this.playbackLocation = playbackLocation;
if (currentSample != null) {
setCurrentSample(currentSample, playbackPositionMs, playWhenReady);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
<com.google.android.exoplayer2.ui.SimpleExoPlayerView android:id="@+id/player_view"
android:layout_width="match_parent"
app:repeat_toggle_modes="all|one"
android:layout_height="0dp"
android:layout_weight="12" />
<ListView
android:id="@+id/sample_list"
android:choiceMode="singleChoice"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="12" />
<com.google.android.exoplayer2.ui.PlaybackControlView
android:id="@+id/cast_control_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:show_timeout="-1"
android:layout_weight="2"
android:visibility="gone"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/media_route_menu_item"
android:title="@string/media_route_menu_title"
app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
app:showAsAction="always" />
</menu>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources>
<string name="application_name">ExoCast Demo</string>
<string name="media_route_menu_title">ExoCast</string>
<string name="error_unsupported_drm">DRM scheme not supported by this device.</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="PlayerTheme" parent="android:Theme.Holo">
<item name="android:windowBackground">@android:color/black</item>
</style>
</resources>
# 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
// 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'
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest package="com.google.android.exoplayer2.ext.cast"/>
/*
* 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<Integer, String> 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<Integer, String> 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() {}
}
/*
* 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<SessionProvider> getAdditionalSessionProviders(Context context) {
return null;
}
}
...@@ -22,7 +22,7 @@ dependencies { ...@@ -22,7 +22,7 @@ dependencies {
// |-- com.android.support:support-v4:25.2.0 // |-- com.android.support:support-v4:25.2.0
compile 'com.android.support:support-v4:' + supportLibraryVersion compile 'com.android.support:support-v4:' + supportLibraryVersion
compile 'com.google.ads.interactivemedia.v3:interactivemedia:3.7.4' compile 'com.google.ads.interactivemedia.v3:interactivemedia:3.7.4'
compile 'com.google.android.gms:play-services-ads:11.0.2' compile 'com.google.android.gms:play-services-ads:' + playServicesLibraryVersion
androidTestCompile project(modulePrefix + 'library') androidTestCompile project(modulePrefix + 'library')
androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion androidTestCompile 'com.google.dexmaker:dexmaker:' + dexmakerVersion
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion androidTestCompile 'com.google.dexmaker:dexmaker-mockito:' + dexmakerVersion
......
...@@ -13,5 +13,4 @@ ...@@ -13,5 +13,4 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest package="com.google.android.exoplayer2"/> <manifest package="com.google.android.exoplayer2"/>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment