Android: 在 Chromecast 上获取当前播放的媒体?
Android: Get current playing media on Chromecast?
我正在开发一款可以控制 Chromecast 及其上播放的任何内容的应用。
我不想要发件人应用程序,我不想注册任何东西来获得 api 密钥,none 之类的东西。
目前我正在使用 MediaRouter 来控制音量并查看它是否连接到任何东西。
但我想要类似 Google Cast 应用程序的东西:
知道正在播放什么和(或至少)播放状态。
理想情况下,我不想使用 google 播放服务,但如果这是唯一的方式,那就生活吧。
我终于明白了。我不得不使用 google 播放服务和 google cast sdk v2 但没有注册应用程序。
项目中包含的库:
compile 'com.android.support:mediarouter-v7:24.0.0'
compile 'com.google.android.gms:play-services-cast-framework:9.2.0'
请注意,在下面的代码中,onCreate() 和 onDestroy() 不是 Activity、片段或服务中的方法,所以不要 copy/paste 代码并期望它起作用。这些方法中的代码必须 copy/pasted 在您自己的方法中。
以下是发生的步骤:
- 您 select 通过投射按钮或通过调用 getActiveMediaRoute() 来检查哪个 Chromecast active 的路线(如果没有人,它将无法工作连接到 Chromecast)。根据您的喜好将方法或 getActiveChromecastRoute() 改写为 select
- 当 onRouteSelected() 被调用时,新的 Cast GoogleApiClient 实例化为 selected chromecast
的选项
- 调用 onApplicationMetadataChanged() 时,代码将连接到 Chromecast运行 上的当前应用程序
- 应用程序成功连接后,将实例化一个新的 RemoteMediaPlayer 并请求 MediaStatus
- 您应该在 onStatusUpdated() 中获得回调,之后您可以调用 mRemoteMediaPlayer.getMediaStatus(),它将包含有关 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);
}
}
};
我正在开发一款可以控制 Chromecast 及其上播放的任何内容的应用。
我不想要发件人应用程序,我不想注册任何东西来获得 api 密钥,none 之类的东西。
目前我正在使用 MediaRouter 来控制音量并查看它是否连接到任何东西。
但我想要类似 Google Cast 应用程序的东西:
知道正在播放什么和(或至少)播放状态。
理想情况下,我不想使用 google 播放服务,但如果这是唯一的方式,那就生活吧。
我终于明白了。我不得不使用 google 播放服务和 google cast sdk v2 但没有注册应用程序。
项目中包含的库:
compile 'com.android.support:mediarouter-v7:24.0.0'
compile 'com.google.android.gms:play-services-cast-framework:9.2.0'
请注意,在下面的代码中,onCreate() 和 onDestroy() 不是 Activity、片段或服务中的方法,所以不要 copy/paste 代码并期望它起作用。这些方法中的代码必须 copy/pasted 在您自己的方法中。
以下是发生的步骤:
- 您 select 通过投射按钮或通过调用 getActiveMediaRoute() 来检查哪个 Chromecast active 的路线(如果没有人,它将无法工作连接到 Chromecast)。根据您的喜好将方法或 getActiveChromecastRoute() 改写为 select
- 当 onRouteSelected() 被调用时,新的 Cast GoogleApiClient 实例化为 selected chromecast 的选项
- 调用 onApplicationMetadataChanged() 时,代码将连接到 Chromecast运行 上的当前应用程序
- 应用程序成功连接后,将实例化一个新的 RemoteMediaPlayer 并请求 MediaStatus
- 您应该在 onStatusUpdated() 中获得回调,之后您可以调用 mRemoteMediaPlayer.getMediaStatus(),它将包含有关 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);
}
}
};