Android Sender App中的CastCompanion

时间:2016-04-24 01:36:19

标签: android chromecast google-cast

我跟随此tutorial。这是我的project

这是我的主要课程:

package com.mac.isaac.castcompanionapp;

import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.MediaRouteActionProvider;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.CastMediaControlIntent;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaStatus;
import com.google.android.gms.cast.RemoteMediaPlayer;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;

import java.io.IOException;

public class CastActivity extends AppCompatActivity {

    private Button mButton;
    private MediaRouter mMediaRouter;
    private MediaRouteSelector mMediaRouteSelector;
    private MediaRouter.Callback mMediaRouterCallback;
    private CastDevice mSelectedDevice;
    private GoogleApiClient mApiClient;
    private RemoteMediaPlayer mRemoteMediaPlayer;
    private Cast.Listener mCastClientListener;
    private boolean mWaitingForReconnect = false;
    private boolean mApplicationStarted = false;
    private boolean mVideoIsLoaded;
    private boolean mIsPlaying;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i("MYTAG", "onCreate()");

        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mVideoIsLoaded)
                    startVideo();
                else
                    controlVideo();
            }
        });

        initMediaRouter();
    }

    private void initMediaRouter() {
        Log.i("MYTAG", "initMediaRouter()");
        // Configure Cast device discovery
        mMediaRouter = MediaRouter.getInstance(getApplicationContext());
        mMediaRouteSelector = new MediaRouteSelector.Builder()
                .addControlCategory(
                        CastMediaControlIntent.categoryForCast(getString(R.string.app_id)))
                .build();
        mMediaRouterCallback = new MediaRouterCallback();
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("MYTAG", "onResume()");
        // Start media router discovery
        mMediaRouter.addCallback( mMediaRouteSelector,
                mMediaRouterCallback,
                MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN );
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu ) {
        Log.i("MYTAG", "onCreateOptionsMenu()");
        super.onCreateOptionsMenu( menu );
        getMenuInflater().inflate( R.menu.menu, menu );
        MenuItem mediaRouteMenuItem = menu.findItem( R.id.media_route_menu_item );
        MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat.getActionProvider( mediaRouteMenuItem );
        mediaRouteActionProvider.setRouteSelector( mMediaRouteSelector );
        return true;
    }

    private void startVideo() {
        Log.i("MYTAG", "startVideo()");
        MediaMetadata mediaMetadata = new MediaMetadata( MediaMetadata.MEDIA_TYPE_MOVIE );
        mediaMetadata.putString( MediaMetadata.KEY_TITLE, getString( R.string.video_title ) );

        MediaInfo mediaInfo = new MediaInfo.Builder( getString( R.string.video_url ) )
                .setContentType( getString( R.string.content_type_mp4 ) )
                .setStreamType( MediaInfo.STREAM_TYPE_BUFFERED )
                .setMetadata( mediaMetadata )
                .build();
        try {
            mRemoteMediaPlayer
                    .load( mApiClient, mediaInfo, true )
                    .setResultCallback( new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
                        @Override
                        public void onResult( RemoteMediaPlayer.MediaChannelResult mediaChannelResult ) {
                            if( mediaChannelResult.getStatus().isSuccess() ) {
                                mVideoIsLoaded = true;
                                mButton.setText( getString( R.string.pause_video ) );
                            }
                        }
                    } );
        } catch( Exception e ) {
            Log.e("MYTAG", "Error starting video "+e.getMessage());
            e.printStackTrace();
        }
    }

    private void controlVideo() {
        Log.i("MYTAG", "controlVideo()");
        if( mRemoteMediaPlayer == null || !mVideoIsLoaded )
            return;
        if( mIsPlaying ) {
            mRemoteMediaPlayer.pause( mApiClient );
            mButton.setText( getString( R.string.resume_video ) );
        } else {
            mRemoteMediaPlayer.play( mApiClient );
            mButton.setText( getString( R.string.pause_video ) );
        }
    }

    private void reconnectChannels( Bundle hint ) {
        Log.i("MYTAG", "reconnectChannels()");
        if( ( hint != null ) && hint.getBoolean( Cast.EXTRA_APP_NO_LONGER_RUNNING ) ) {
            //Log.e( TAG, "App is no longer running" );
            teardown();
        } else {
            try {
                Cast.CastApi.setMessageReceivedCallbacks( mApiClient, mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer );
            } catch( IOException e ) {
                Log.e( "MYTAG", "Exception while creating media channel ", e );
            } catch( NullPointerException e ) {
                Log.e( "MYTAG", "Something wasn't reinitialized for reconnectChannels" );
            }
        }
    }

    private class ConnectionFailedListener implements GoogleApiClient.OnConnectionFailedListener {

        @Override
        public void onConnectionFailed( ConnectionResult connectionResult ) {
            Log.i("MYTAG", "onConnectionFailed()");
            teardown();
        }
    }

    @Override
    protected void onPause() {
        Log.i("MYTAG", "onPause()");
        if ( isFinishing() ) {
            // End media router discovery
            mMediaRouter.removeCallback( mMediaRouterCallback );
        }
        super.onPause();
    }

    private void teardown() {
        Log.i("MYTAG", "teardown()");
        if( mApiClient != null ) {
            if( mApplicationStarted ) {
                try {
                    Cast.CastApi.stopApplication( mApiClient );
                    if( mRemoteMediaPlayer != null ) {
                        Cast.CastApi.removeMessageReceivedCallbacks( mApiClient, mRemoteMediaPlayer.getNamespace() );
                        mRemoteMediaPlayer = null;
                    }
                } catch( IOException e ) {
                    Log.e( "MYTAG", "Exception while removing application " + e );
                }
                mApplicationStarted = false;
            }
            if( mApiClient.isConnected() )
                mApiClient.disconnect();
            mApiClient = null;
        }
        mSelectedDevice = null;
        mVideoIsLoaded = false;
    }

    private void initCastClientListener() {
        mCastClientListener = new Cast.Listener() {
            @Override
            public void onApplicationStatusChanged() {
            }

            @Override
            public void onVolumeChanged() {
            }

            @Override
            public void onApplicationDisconnected( int statusCode ) {
                teardown();
            }
        };
    }

    private void launchReceiver() {
        Log.i("MYTAG", "launchReceiver()");
        Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
                .builder( mSelectedDevice, mCastClientListener );

        ConnectionCallbacks mConnectionCallbacks = new ConnectionCallbacks();
        ConnectionFailedListener mConnectionFailedListener = new ConnectionFailedListener();
        mApiClient = new GoogleApiClient.Builder(CastActivity.this)
                .addApi( Cast.API, apiOptionsBuilder.build() )
                .addConnectionCallbacks( mConnectionCallbacks )
                .addOnConnectionFailedListener( mConnectionFailedListener )
                .build();

        mApiClient.connect();
    }

    private void initRemoteMediaPlayer() {
        Log.i("MYTAG", "initRemoteMediaPlayer()");
        mRemoteMediaPlayer = new RemoteMediaPlayer();
        mRemoteMediaPlayer.setOnStatusUpdatedListener( new RemoteMediaPlayer.OnStatusUpdatedListener() {
            @Override
            public void onStatusUpdated() {
                MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus();
                mIsPlaying = mediaStatus.getPlayerState() == MediaStatus.PLAYER_STATE_PLAYING;
            }
        });

        mRemoteMediaPlayer.setOnMetadataUpdatedListener( new RemoteMediaPlayer.OnMetadataUpdatedListener() {
            @Override
            public void onMetadataUpdated() {
                Log.i("MYTAG", "onMetadataUpdated()");
            }
        });
    }

    private class MediaRouterCallback extends MediaRouter.Callback {

        @Override
        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
            Log.i("MYTAG", "onRouteSelected()");
            initCastClientListener();
            initRemoteMediaPlayer();

            mSelectedDevice = CastDevice.getFromBundle( info.getExtras() );

            launchReceiver();
        }

        @Override
        public void onRouteUnselected( MediaRouter router, MediaRouter.RouteInfo info ) {
            Log.i("MYTAG", "onRouteUnselected()");
            teardown();
            mSelectedDevice = null;
            mButton.setText( getString( R.string.play_video ) );
            mVideoIsLoaded = false;
        }

    }

    private class ConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks {

        @Override
        public void onConnected( Bundle hint ) {
            Log.i("MYTAG", "onConnected()");
            if( mWaitingForReconnect ) {
                mWaitingForReconnect = false;
                reconnectChannels( hint );
            } else {
                try {
                    Cast.CastApi.launchApplication( mApiClient, getString( R.string.app_id ), false )
                            .setResultCallback(
                                    new ResultCallback<Cast.ApplicationConnectionResult>() {
                                        @Override
                                        public void onResult(
                                                Cast.ApplicationConnectionResult applicationConnectionResult) {
                                            Status status = applicationConnectionResult.getStatus();
                                            if( status.isSuccess() ) {
                                                //Values that can be useful for storing/logic
                                                ApplicationMetadata applicationMetadata =
                                                        applicationConnectionResult.getApplicationMetadata();
                                                String sessionId =
                                                        applicationConnectionResult.getSessionId();
                                                String applicationStatus =
                                                        applicationConnectionResult.getApplicationStatus();
                                                boolean wasLaunched =
                                                        applicationConnectionResult.getWasLaunched();

                                                mApplicationStarted = true;
                                                reconnectChannels( null );
                                            }
                                        }
                                    }
                            );
                } catch ( Exception e ) {
                    Log.e("MYTAG", "error launching application");
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onConnectionSuspended(int i) {
            Log.i("MYTAG", "onConnectionSuspended()");
            mWaitingForReconnect = true;
        }
    }

}

我在这里收到错误:

mRemoteMediaPlayer.load

它说:

04-23 21:29:31.730 4524-4524/com.mac.isaac.castcompanionapp E/MYTAG: Error starting video Attempt to invoke virtual method 'com.google.android.gms.common.api.PendingResult com.google.android.gms.cast.RemoteMediaPlayer.load(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.cast.MediaInfo, boolean)' on a null object reference
04-23 21:29:31.730 4524-4524/com.mac.isaac.castcompanionapp W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.android.gms.common.api.PendingResult com.google.android.gms.cast.RemoteMediaPlayer.load(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.cast.MediaInfo, boolean)' on a null object reference
04-23 21:29:31.730 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at com.mac.isaac.castcompanionapp.CastActivity.startVideo(CastActivity.java:110)
04-23 21:29:31.730 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at com.mac.isaac.castcompanionapp.CastActivity.access$100(CastActivity.java:30)
04-23 21:29:31.730 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at com.mac.isaac.castcompanionapp.CastActivity$1.onClick(CastActivity.java:57)
04-23 21:29:31.731 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at android.view.View.performClick(View.java:5204)
04-23 21:29:31.731 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at android.view.View$PerformClick.run(View.java:21153)
04-23 21:29:31.731 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at android.os.Handler.handleCallback(Handler.java:739)
04-23 21:29:31.731 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
04-23 21:29:31.731 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at android.os.Looper.loop(Looper.java:148)
04-23 21:29:31.731 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5417)
04-23 21:29:31.731 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
04-23 21:29:31.732 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
04-23 21:29:31.732 4524-4524/com.mac.isaac.castcompanionapp W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

调试我收到了这个错误:

  

public static final int APPLICATION_NOT_RUNNING状态代码指示   请求的应用程序当前未运行。常数值:   2005

所以我猜问题出在chromecast设备而不是我的应用程序中。但是我的应用程序看起来像是在播放一首歌而不是视频,但仍然没有任何反应。

我可以在工具栏上看到投射按钮,我可以选择一个设备,然后看起来Chromecast在黑屏上持续3秒后重新启动,但是当它没有显示我的视频时。 我认为问题出在我的应用中,因为我无法正确发送信息。 我已经在Cast SDK控制台中设置了所有内容,我尝试了所有3个选项:

  • 自定义接收器
  • 样式媒体接收器
  • 远程显示接收器

我仍然无法找到一个有效的教程(即使是谷歌的GitHub上的教程也不起作用,我认为它没有更新)。 有没有人使用过这个并且有工作样本?

1 个答案:

答案 0 :(得分:0)

解决了,我必须选择StyledMediaReceiver,发布应用并重新启动Chromecast设备。每次在Google Cast SDK控制台中进行更改时,都必须重新启动chromecast设备才能获得它(看起来它只在启动时获取信息)。 我希望这有助于其他有同样问题的人。