问题
I'm working on an app that controls the Chromecast and whatever's playing on it.
I don't want a sender app, I don't want to register anything to get an api key, none of that stuff.
I'm using so far MediaRouter to control the volume and to see if it's connected to anything.
But I want something like the Google Cast app:
Which knows what's playing and (or at least) the playback state.
Ideally I wouldn't want to use google play services, but if it's the only way, c'est la vie.
回答1:
I finally figured it out. I had to use the google play services and the google cast sdk v2 but without registering the application.
Libraries included in the project:
compile 'com.android.support:mediarouter-v7:24.0.0'
compile 'com.google.android.gms:play-services-cast-framework:9.2.0'
Beware that in the code below onCreate() and onDestroy() aren't methods in an Activity, Fragment or Service, so don't copy/paste the code and expect it works. The code in those methods must pe copy/pasted in your own methods.
Here are the steps of what's happening:
- You select a route either via the cast button either by calling getActiveMediaRoute() which checks for which Chromecast is active (it won't work if nobody is connected to the Chromecast). Override the method or getActiveChromecastRoute() to select based on your preferences
- When onRouteSelected() is called a new Cast GoogleApiClient is instanced with options for the selected chromecast
- When onApplicationMetadataChanged() is called the code will connect to the current application running on the Chromecast
- After the application is successfully connected a new RemoteMediaPlayer is instanced and the MediaStatus is requested
- You should get a callback in onStatusUpdated() and after that you can call mRemoteMediaPlayer.getMediaStatus() and it will contain the data about what's being played on the Chromecast.
public static final String CHROMECAST_SIGNATURE = "cast.media.CastMediaRouteProviderService";
private final MediaRouteSelector mSelector;
private final MediaRouter mMediaRouter;
private CastDevice mSelectedDevice;
private Cast.Listener mCastClientListener;
private RemoteMediaPlayer mRemoteMediaPlayer;
@Override
public void onCreate() {
mMediaRouter = MediaRouter.getInstance(context);
mSelector = new MediaRouteSelector.Builder()
// These are the framework-supported intents
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build();
mMediaRouter.addCallback(mSelector, mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY | MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
}
@Override
public void onDestroy() {
mMediaRouter.removeCallback(mMediaRouterCallback);
}
@UiThread
private boolean isChromecastActive() {
return getActiveChromecastRoute() != null;
}
@UiThread
private Boolean isChromecastPlaying() {
if (mRemoteMediaPlayer == null || mRemoteMediaPlayer.getMediaStatus() == null) {
return null;
}
// Here you can get the playback status and the metadata for what's playing
// But only after the onStatusUpdated() method is called in the mRemoteMediaPlayer callback
int state = mRemoteMediaPlayer.getMediaStatus().getPlayerState();
return (state == MediaStatus.PLAYER_STATE_BUFFERING || state == MediaStatus.PLAYER_STATE_PLAYING);
}
@UiThread
private MediaRouter.RouteInfo getActiveChromecastRoute() {
for (MediaRouter.RouteInfo route : mMediaRouter.getRoutes()) {
if (isCastDevice(route)) {
if (route.getConnectionState() == MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED) {
return route;
}
}
}
return null;
}
private int getMediaRouteVolume(@NonNull MediaRouter.RouteInfo route) {
return route.getVolume();
}
private void setMediaRouteVolume(@NonNull MediaRouter.RouteInfo route, int volume) {
route.requestSetVolume(volume);
}
private int getMediaRouteMaxVolume(@NonNull MediaRouter.RouteInfo route) {
return route.getVolumeMax();
}
@UiThread
private MediaRouter.RouteInfo getActiveMediaRoute() {
if (isChromecastActive()) {
MediaRouter.RouteInfo route = getActiveChromecastRoute();
if (route != null) {
if (!route.isSelected()) {
mMediaRouter.selectRoute(route);
}
}
else if (mSelectedDevice != null) {
mSelectedDevice = null;
}
return route;
}
return null;
}
private boolean isCastDevice(MediaRouter.RouteInfo routeInfo) {
return routeInfo.getId().contains(CHROMECAST_SIGNATURE);
}
private MediaRouter.Callback mMediaRouterCallback = new MediaRouter.Callback() {
@Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
if (isCastDevice(route)) {
Log.i("MediaRouter", "Chromecast found: " + route);
}
}
@Override
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
if (isCastDevice(route)) {
Log.i("MediaRouter", "Chromecast changed: " + route);
}
}
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
if (mSelectedDevice == null && isCastDevice(route)) {
Log.i("MediaRouter", "Chromecast selected: " + route);
mSelectedDevice = CastDevice.getFromBundle(route.getExtras());
mCastClientListener = new Cast.Listener() {
@Override
public void onApplicationStatusChanged() {
Log.i("MediaRouter", "Cast.Listener.onApplicationStatusChanged()");
}
@Override
public void onApplicationMetadataChanged(ApplicationMetadata applicationMetadata) {
Log.i("MediaRouter", "Cast.Listener.onApplicationMetadataChanged(" + applicationMetadata + ")");
if (applicationMetadata != null) {
LaunchOptions launchOptions = new LaunchOptions.Builder().setRelaunchIfRunning(false).build();
Cast.CastApi.launchApplication(mApiClient, applicationMetadata.getApplicationId(), launchOptions).setResultCallback(new ResultCallback<Cast.ApplicationConnectionResult>() {
@Override
public void onResult(@NonNull Cast.ApplicationConnectionResult applicationConnectionResult) {
Log.i("MediaRouter", "Cast.CastApi.joinApplication.onResult() " + applicationConnectionResult.getSessionId());
mRemoteMediaPlayer = new RemoteMediaPlayer();
mRemoteMediaPlayer.setOnStatusUpdatedListener( new RemoteMediaPlayer.OnStatusUpdatedListener() {
@Override
public void onStatusUpdated() {
MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus();
Log.i("MediaRouter", "Remote media player status " + mediaStatus.getPlayerState());
// TODO: you can call isChromecastPlaying() now
}
});
try {
Cast.CastApi.setMessageReceivedCallbacks(mApiClient, mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
} catch(IOException e) {
Log.e("MediaRouter", "Exception while creating media channel ", e );
} catch(NullPointerException e) {
Log.e("MediaRouter", "Something wasn't reinitialized for reconnectChannels", e);
}
mRemoteMediaPlayer.requestStatus(mApiClient).setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
@Override
public void onResult(@NonNull RemoteMediaPlayer.MediaChannelResult mediaChannelResult) {
Log.i("MediaRouter", "requestStatus() " + mediaChannelResult);
}
});
try {
Cast.CastApi.requestStatus(mApiClient);
} catch (IOException e) {
Log.e("MediaRouter", "Couldn't request status", e);
}
}
});
}
}
@Override
public void onApplicationDisconnected(int i) {
Log.i("MediaRouter", "Cast.Listener.onApplicationDisconnected(" + i + ")");
}
@Override
public void onActiveInputStateChanged(int i) {
Log.i("MediaRouter", "Cast.Listener.onActiveInputStateChanged(" + i + ")");
}
@Override
public void onStandbyStateChanged(int i) {
Log.i("MediaRouter", "Cast.Listener.onStandbyStateChanged(" + i + ")");
}
@Override
public void onVolumeChanged() {
Log.i("MediaRouter", "Cast.Listener.onVolumeChanged()");
}
};
Cast.CastOptions.Builder apiOptionsBuilder = new Cast.CastOptions.Builder(mSelectedDevice, mCastClientListener);
mApiClient = new GoogleApiClient.Builder(getContext())
.addApi( Cast.API, apiOptionsBuilder.build() )
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.i("MediaRouter", "GoogleApiClient.onConnected()");
Log.i("MediaRouter", "Bundle " + bundle);
}
@Override
public void onConnectionSuspended(int i) {
Log.i("MediaRouter", "GoogleApiClient.onConnectionSuspended(" + i + ")");
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.i("MediaRouter", "GoogleApiClient.onConnectionFailed()");
}
})
.build();
mApiClient.connect();
}
else {
mSelectedDevice = null;
mRemoteMediaPlayer = null;
}
}
@Override
public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo route) {
if (isCastDevice(route)) {
if (mSelectedDevice != null && mSelectedDevice.isSameDevice(CastDevice.getFromBundle(route.getExtras()))) {
mSelectedDevice = null;
}
Log.i("MediaRouter", "Chromecast lost: " + route);
}
}
};
来源:https://stackoverflow.com/questions/38269861/android-get-current-playing-media-on-chromecast