Android - 通过自定义应用将屏幕投射到 chromecast
Android - cast screen to chromecast via custom app
我需要对 Chromecast 应用程序执行相同的操作 (com.google.android.apps.chromecast.app) "Mirror screen" 功能:
按应用要求 select chromecast 设备进行流式传输的自定义按钮,然后启动屏幕镜像到 selected chromecast 设备。
实际上,此功能并未在任何地方记录。我必须使用 Presentation class 吗?
阅读 Chromecast 应用程序的日志我发现了以下接收器应用程序 ID:
app id E8C28D3C
app name Backdrop
app id 674A0243
app name Screen Mirroring
我该怎么做?
这是我的代码:
public class MainActivity extends ActionBarActivity
{
private MediaRouter mMediaRouter;
private MediaRouteSelector mMediaRouteSelector;
private android.support.v7.media.MediaRouter.Callback mMediaRouterCallback;
private CastDevice mSelectedDevice;
private GoogleApiClient mApiClient;
private Cast.Listener mCastClientListener;
private ConnectionCallbacks mConnectionCallbacks;
private ConnectionFailedListener mConnectionFailedListener;
private boolean mWaitingForReconnect;
private boolean mApplicationStarted;
private String mSessionId;
private DemoPresentation mPresentation;
private final static String TAG = "CAST-APP";
private String mAppID = "AE85BA70";
private String mirroringAppID="674A0243";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMediaRouter = MediaRouter.getInstance(getApplicationContext());
mMediaRouteSelector = new MediaRouteSelector.Builder()
//.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
//.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
//.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
//.addControlCategory(CastMediaControlIntent.categoryForCast(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID))
.addControlCategory(CastMediaControlIntent.categoryForCast(mirroringAppID))
.build();
mMediaRouterCallback = new MyMediaRouterCallback();
/*MediaRouteButton btn = (MediaRouteButton) findViewById(R.id.mediabutton);
btn.setRouteSelector(mMediaRouteSelector);*/
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
MediaRouteActionProvider mediaRouteActionProvider =
(MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}
@Override
protected void onPause() {
if (isFinishing()) {
mMediaRouter.removeCallback(mMediaRouterCallback);
}
super.onPause();
}
/*
@Override
protected void onStart() {
super.onStart();
mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}
@Override
protected void onStop() {
mMediaRouter.removeCallback(mMediaRouterCallback);
super.onStop();
}
*/
private class MyMediaRouterCallback extends MediaRouter.Callback {
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
try
{
mSelectedDevice = CastDevice.getFromBundle(info.getExtras());
String routeId = info.getId();
mCastClientListener = new Cast.Listener()
{
@Override
public void onApplicationStatusChanged()
{
if (mApiClient != null)
{
Log.d(TAG, "onApplicationStatusChanged: "
+ Cast.CastApi.getApplicationStatus(mApiClient));
//updatePresentation();
}
}
@Override
public void onVolumeChanged()
{
if (mApiClient != null)
{
Log.d(TAG, "onVolumeChanged: " + Cast.CastApi.getVolume(mApiClient));
}
}
@Override
public void onApplicationDisconnected(int errorCode)
{
teardown();
}
};
mConnectionCallbacks = new ConnectionCallbacks();
mConnectionFailedListener = new ConnectionFailedListener();
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
.builder(mSelectedDevice, mCastClientListener);
mApiClient = new GoogleApiClient.Builder(getApplication())
.addApi(Cast.API, apiOptionsBuilder.build())
.addConnectionCallbacks(mConnectionCallbacks)
.addOnConnectionFailedListener(mConnectionFailedListener)
.build();
mApiClient.connect();
} catch (Exception e) {
Log.d(TAG, "onRouteSelected " + e.getMessage());
}
}
@Override
public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) {
Log.d(TAG, "onRouteUnselected: info=" + info);
teardown();
//updatePresentation();
mSelectedDevice = null;
}
@Override
public void onRoutePresentationDisplayChanged(MediaRouter router, MediaRouter.RouteInfo info) {
//updatePresentation();
}
}
private void showPresentation()
{
DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
Display[] presentationDisplays = displayManager.getDisplays();
Log.d("showPresentation", "Displays: " + String.valueOf(presentationDisplays.length));
if (presentationDisplays.length > 0) {
Log.d("showPresentation", "Display : " + presentationDisplays[0].getName());
Display display = presentationDisplays[0];
mPresentation = new DemoPresentation(this, display);
mPresentation.show();
}
//updatePresentation();
}
private class ConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks {
@Override
public void onConnected(Bundle connectionHint) {
if (mWaitingForReconnect) {
mWaitingForReconnect = false;
//reconnectChannels();
if ((connectionHint != null)
&& connectionHint
.getBoolean(Cast.EXTRA_APP_NO_LONGER_RUNNING)) {
Log.d(TAG, "App is no longer running");
teardown();
}
} else {
try {
Cast.CastApi.launchApplication(mApiClient, mirroringAppID, false)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
@Override
public void onResult(Cast.ApplicationConnectionResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata =
result.getApplicationMetadata();
mSessionId = result.getSessionId();
String applicationStatus = result.getApplicationStatus();
boolean wasLaunched = result.getWasLaunched();
mApplicationStarted = true;
} else {
teardown();
}
}
});
} catch (Exception e) {
Log.e(TAG, "Failed to launch application", e);
}
}
}
@Override
public void onConnectionSuspended(int cause) {
mWaitingForReconnect = true;
}
}
private class ConnectionFailedListener implements
GoogleApiClient.OnConnectionFailedListener {
@Override
public void onConnectionFailed(ConnectionResult result) {
teardown();
}
}
private void teardown() {
Log.d(TAG, "teardown");
if (mApiClient != null) {
if (mApplicationStarted) {
if (mApiClient.isConnected() || mApiClient.isConnecting()) {
try {
Cast.CastApi.stopApplication(mApiClient, mSessionId);
} catch (Exception e) {
Log.e(TAG, "Exception while removing channel", e);
}
mApiClient.disconnect();
}
mApplicationStarted = false;
}
mApiClient = null;
}
mSelectedDevice = null;
mWaitingForReconnect = false;
mSessionId = null;
}
}
你不能从你自己的应用程序中做到这一点,目前没有可供开发人员从他们的应用程序中启动投射镜像的 API,它应该由用户手动完成;这在未来可能会改变,但这是当前状态。
看来你只要不设置任何自定义Presentation screen就默认进行镜像:
https://developer.android.com/reference/android/media/MediaRouter.html#ROUTE_TYPE_LIVE_VIDEO
我需要对 Chromecast 应用程序执行相同的操作 (com.google.android.apps.chromecast.app) "Mirror screen" 功能:
按应用要求 select chromecast 设备进行流式传输的自定义按钮,然后启动屏幕镜像到 selected chromecast 设备。
实际上,此功能并未在任何地方记录。我必须使用 Presentation class 吗?
阅读 Chromecast 应用程序的日志我发现了以下接收器应用程序 ID:
app id E8C28D3C
app name Backdrop
app id 674A0243
app name Screen Mirroring
我该怎么做?
这是我的代码:
public class MainActivity extends ActionBarActivity
{
private MediaRouter mMediaRouter;
private MediaRouteSelector mMediaRouteSelector;
private android.support.v7.media.MediaRouter.Callback mMediaRouterCallback;
private CastDevice mSelectedDevice;
private GoogleApiClient mApiClient;
private Cast.Listener mCastClientListener;
private ConnectionCallbacks mConnectionCallbacks;
private ConnectionFailedListener mConnectionFailedListener;
private boolean mWaitingForReconnect;
private boolean mApplicationStarted;
private String mSessionId;
private DemoPresentation mPresentation;
private final static String TAG = "CAST-APP";
private String mAppID = "AE85BA70";
private String mirroringAppID="674A0243";
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMediaRouter = MediaRouter.getInstance(getApplicationContext());
mMediaRouteSelector = new MediaRouteSelector.Builder()
//.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO)
//.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
//.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
//.addControlCategory(CastMediaControlIntent.categoryForCast(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID))
.addControlCategory(CastMediaControlIntent.categoryForCast(mirroringAppID))
.build();
mMediaRouterCallback = new MyMediaRouterCallback();
/*MediaRouteButton btn = (MediaRouteButton) findViewById(R.id.mediabutton);
btn.setRouteSelector(mMediaRouteSelector);*/
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
MediaRouteActionProvider mediaRouteActionProvider =
(MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
@Override
protected void onResume() {
super.onResume();
mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}
@Override
protected void onPause() {
if (isFinishing()) {
mMediaRouter.removeCallback(mMediaRouterCallback);
}
super.onPause();
}
/*
@Override
protected void onStart() {
super.onStart();
mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}
@Override
protected void onStop() {
mMediaRouter.removeCallback(mMediaRouterCallback);
super.onStop();
}
*/
private class MyMediaRouterCallback extends MediaRouter.Callback {
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
try
{
mSelectedDevice = CastDevice.getFromBundle(info.getExtras());
String routeId = info.getId();
mCastClientListener = new Cast.Listener()
{
@Override
public void onApplicationStatusChanged()
{
if (mApiClient != null)
{
Log.d(TAG, "onApplicationStatusChanged: "
+ Cast.CastApi.getApplicationStatus(mApiClient));
//updatePresentation();
}
}
@Override
public void onVolumeChanged()
{
if (mApiClient != null)
{
Log.d(TAG, "onVolumeChanged: " + Cast.CastApi.getVolume(mApiClient));
}
}
@Override
public void onApplicationDisconnected(int errorCode)
{
teardown();
}
};
mConnectionCallbacks = new ConnectionCallbacks();
mConnectionFailedListener = new ConnectionFailedListener();
Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
.builder(mSelectedDevice, mCastClientListener);
mApiClient = new GoogleApiClient.Builder(getApplication())
.addApi(Cast.API, apiOptionsBuilder.build())
.addConnectionCallbacks(mConnectionCallbacks)
.addOnConnectionFailedListener(mConnectionFailedListener)
.build();
mApiClient.connect();
} catch (Exception e) {
Log.d(TAG, "onRouteSelected " + e.getMessage());
}
}
@Override
public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) {
Log.d(TAG, "onRouteUnselected: info=" + info);
teardown();
//updatePresentation();
mSelectedDevice = null;
}
@Override
public void onRoutePresentationDisplayChanged(MediaRouter router, MediaRouter.RouteInfo info) {
//updatePresentation();
}
}
private void showPresentation()
{
DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
Display[] presentationDisplays = displayManager.getDisplays();
Log.d("showPresentation", "Displays: " + String.valueOf(presentationDisplays.length));
if (presentationDisplays.length > 0) {
Log.d("showPresentation", "Display : " + presentationDisplays[0].getName());
Display display = presentationDisplays[0];
mPresentation = new DemoPresentation(this, display);
mPresentation.show();
}
//updatePresentation();
}
private class ConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks {
@Override
public void onConnected(Bundle connectionHint) {
if (mWaitingForReconnect) {
mWaitingForReconnect = false;
//reconnectChannels();
if ((connectionHint != null)
&& connectionHint
.getBoolean(Cast.EXTRA_APP_NO_LONGER_RUNNING)) {
Log.d(TAG, "App is no longer running");
teardown();
}
} else {
try {
Cast.CastApi.launchApplication(mApiClient, mirroringAppID, false)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
@Override
public void onResult(Cast.ApplicationConnectionResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata =
result.getApplicationMetadata();
mSessionId = result.getSessionId();
String applicationStatus = result.getApplicationStatus();
boolean wasLaunched = result.getWasLaunched();
mApplicationStarted = true;
} else {
teardown();
}
}
});
} catch (Exception e) {
Log.e(TAG, "Failed to launch application", e);
}
}
}
@Override
public void onConnectionSuspended(int cause) {
mWaitingForReconnect = true;
}
}
private class ConnectionFailedListener implements
GoogleApiClient.OnConnectionFailedListener {
@Override
public void onConnectionFailed(ConnectionResult result) {
teardown();
}
}
private void teardown() {
Log.d(TAG, "teardown");
if (mApiClient != null) {
if (mApplicationStarted) {
if (mApiClient.isConnected() || mApiClient.isConnecting()) {
try {
Cast.CastApi.stopApplication(mApiClient, mSessionId);
} catch (Exception e) {
Log.e(TAG, "Exception while removing channel", e);
}
mApiClient.disconnect();
}
mApplicationStarted = false;
}
mApiClient = null;
}
mSelectedDevice = null;
mWaitingForReconnect = false;
mSessionId = null;
}
}
你不能从你自己的应用程序中做到这一点,目前没有可供开发人员从他们的应用程序中启动投射镜像的 API,它应该由用户手动完成;这在未来可能会改变,但这是当前状态。
看来你只要不设置任何自定义Presentation screen就默认进行镜像:
https://developer.android.com/reference/android/media/MediaRouter.html#ROUTE_TYPE_LIVE_VIDEO