Chromecast - SIGN_IN_REQUIRED

Posted

技术标签:

【中文标题】Chromecast - SIGN_IN_REQUIRED【英文标题】: 【发布时间】:2014-03-28 04:44:20 【问题描述】:

我的用户中只有一小部分遇到此错误,我终其一生都无法弄清楚。我使用GooglePlayServicesUtil.isGooglePlayServicesAvailable(downloadService) 来测试Play Services 是否可用,它总是返回SUCCESS。我设置了连接到 Chromecast 的通道,一切正常,直到我尝试使用 RemoteMediaPlayer.load。对于某些用户,结果总是SIGN_IN_REQUIREDresolution: nullstatus.toString()Failed to load: StatusstatusCode=SIGN_IN_REQUIRED, resolution=null。我真的不确定我应该怎么做,或者如何为我的少数用户解决这个错误。

我不知道什么部分是相关的,所以我只是发布我的整个控制器类:

public class ChromeCastController extends RemoteController 
private static final String TAG = ChromeCastController.class.getSimpleName();

private CastDevice castDevice;
private GoogleApiClient apiClient;
private ConnectionCallbacks connectionCallbacks;
private ConnectionFailedListener connectionFailedListener;
private Cast.Listener castClientListener;

private boolean applicationStarted = false;
private boolean waitingForReconnect = false;
private boolean error = false;
private boolean ignoreNextPaused = false;
private String sessionId;

private FileProxy proxy;
private String rootLocation;
private RemoteMediaPlayer mediaPlayer;
private double gain = 0.5;

public ChromeCastController(DownloadService downloadService, CastDevice castDevice) 
    this.downloadService = downloadService;
    this.castDevice = castDevice;

    SharedPreferences prefs = Util.getPreferences(downloadService);
    rootLocation = prefs.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, null);


@Override
public void create(boolean playing, int seconds) 
    downloadService.setPlayerState(PlayerState.PREPARING);

    connectionCallbacks = new ConnectionCallbacks(playing, seconds);
    connectionFailedListener = new ConnectionFailedListener();
    castClientListener = new Cast.Listener() 
        @Override
        public void onApplicationStatusChanged() 
            if (apiClient != null && apiClient.isConnected()) 
                Log.i(TAG, "onApplicationStatusChanged: " + Cast.CastApi.getApplicationStatus(apiClient));
            
        

        @Override
        public void onVolumeChanged() 
            if (apiClient != null && applicationStarted) 
                try 
                    gain = Cast.CastApi.getVolume(apiClient);
                 catch(Exception e) 
                    Log.w(TAG, "Failed to get volume");
                
            
        

        @Override
        public void onApplicationDisconnected(int errorCode) 
            shutdownInternal();
        

    ;

    Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions.builder(castDevice, castClientListener);
    apiClient = new GoogleApiClient.Builder(downloadService)
            .addApi(Cast.API, apiOptionsBuilder.build())
            .addConnectionCallbacks(connectionCallbacks)
            .addOnConnectionFailedListener(connectionFailedListener)
            .build();

    apiClient.connect();


@Override
public void start() 
    if(error) 
        error = false;
        Log.w(TAG, "Attempting to restart song");
        startSong(downloadService.getCurrentPlaying(), true, 0);
        return;
    

    try 
        mediaPlayer.play(apiClient);
     catch(Exception e) 
        Log.e(TAG, "Failed to start");
    


@Override
public void stop() 
    try 
        mediaPlayer.pause(apiClient);
     catch(Exception e) 
        Log.e(TAG, "Failed to pause");
    


@Override
public void shutdown() 
    try 
        if(mediaPlayer != null && !error) 
            mediaPlayer.stop(apiClient);
        
     catch(Exception e) 
        Log.e(TAG, "Failed to stop mediaPlayer", e);
    

    try 
        if(apiClient != null) 
            Cast.CastApi.stopApplication(apiClient);
            Cast.CastApi.removeMessageReceivedCallbacks(apiClient, mediaPlayer.getNamespace());
            mediaPlayer = null;
            applicationStarted = false;
        
     catch(Exception e) 
        Log.e(TAG, "Failed to shutdown application", e);
    

    if(apiClient != null && apiClient.isConnected()) 
        apiClient.disconnect();
    
    apiClient = null;

    if(proxy != null) 
        proxy.stop();
        proxy = null;
    


private void shutdownInternal() 
    // This will call this.shutdown() indirectly
    downloadService.setRemoteEnabled(RemoteControlState.LOCAL, null);


@Override
public void updatePlaylist() 
    if(downloadService.getCurrentPlaying() == null) 
        startSong(null, false, 0);
    


@Override
public void changePosition(int seconds) 
    try 
        mediaPlayer.seek(apiClient, seconds * 1000L);
     catch(Exception e) 
        Log.e(TAG, "FAiled to seek to " + seconds);
    


@Override
public void changeTrack(int index, DownloadFile song) 
    startSong(song, true, 0);


@Override
public void setVolume(boolean up) 
    double delta = up ? 0.1 : -0.1;
    gain += delta;
    gain = Math.max(gain, 0.0);
    gain = Math.min(gain, 1.0);

    getVolumeToast().setVolume((float) gain);
    try 
        Cast.CastApi.setVolume(apiClient, gain);
     catch(Exception e) 
        Log.e(TAG, "Failed to the volume");
    


@Override
public int getRemotePosition() 
    if(mediaPlayer != null) 
        return (int) (mediaPlayer.getApproximateStreamPosition() / 1000L);
     else 
        return 0;
    


@Override
public int getRemoteDuration() 
    if(mediaPlayer != null) 
        return (int) (mediaPlayer.getStreamDuration() / 1000L);
     else 
        return 0;
    


void startSong(DownloadFile currentPlaying, boolean autoStart, int position) 
    if(currentPlaying == null) 
        try 
            if (mediaPlayer != null && !error) 
                mediaPlayer.stop(apiClient);
            
         catch(Exception e) 
            // Just means it didn't need to be stopped
        
        downloadService.setPlayerState(PlayerState.IDLE);
        return;
    
    downloadService.setPlayerState(PlayerState.PREPARING);
    MusicDirectory.Entry song = currentPlaying.getSong();

    try 
        MusicService musicService = MusicServiceFactory.getMusicService(downloadService);
        String url;
        // Offline, use file proxy
        if(Util.isOffline(downloadService) || song.getId().indexOf(rootLocation) != -1) 
            if(proxy == null) 
                proxy = new FileProxy(downloadService);
                proxy.start();
            

            url = proxy.getPublicAddress(song.getId());
         else 
            if(proxy != null) 
                proxy.stop();
                proxy = null;
            

            if(song.isVideo()) 
                url = musicService.getHlsUrl(song.getId(), currentPlaying.getBitRate(), downloadService);
             else 
                url = musicService.getMusicUrl(downloadService, song, currentPlaying.getBitRate());
            

            url = fixURLs(url);
        

        // Setup song/video information
        MediaMetadata meta = new MediaMetadata(song.isVideo() ? MediaMetadata.MEDIA_TYPE_MOVIE : MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
        meta.putString(MediaMetadata.KEY_TITLE, song.getTitle());
        if(song.getTrack() != null) 
            meta.putInt(MediaMetadata.KEY_TRACK_NUMBER, song.getTrack());
        
        if(!song.isVideo()) 
            meta.putString(MediaMetadata.KEY_ARTIST, song.getArtist());
            meta.putString(MediaMetadata.KEY_ALBUM_ARTIST, song.getArtist());
            meta.putString(MediaMetadata.KEY_ALBUM_TITLE, song.getAlbum());

            String coverArt = "";
            if(proxy == null) 
                coverArt = musicService.getCoverArtUrl(downloadService, song);
                coverArt = fixURLs(coverArt);
                meta.addImage(new WebImage(Uri.parse(coverArt)));
             else 
                File coverArtFile = FileUtil.getAlbumArtFile(downloadService, song);
                if(coverArtFile != null && coverArtFile.exists()) 
                    coverArt = proxy.getPublicAddress(coverArtFile.getPath());
                    meta.addImage(new WebImage(Uri.parse(coverArt)));
                
            
        

        String contentType;
        if(song.isVideo()) 
            contentType = "application/x-mpegURL";
        
        else if(song.getTranscodedContentType() != null) 
            contentType = song.getTranscodedContentType();
         else if(song.getContentType() != null) 
            contentType = song.getContentType();
         else 
            contentType = "audio/mpeg";
        

        // Load it into a MediaInfo wrapper
        MediaInfo mediaInfo = new MediaInfo.Builder(url)
            .setContentType(contentType)
            .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
            .setMetadata(meta)
            .build();

        if(autoStart) 
            ignoreNextPaused = true;
        

        mediaPlayer.load(apiClient, mediaInfo, autoStart, position * 1000L).setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() 
            @Override
            public void onResult(RemoteMediaPlayer.MediaChannelResult result) 
                if (result.getStatus().isSuccess()) 
                    // Handled in other handler
                 else if(result.getStatus().getStatusCode() != ConnectionResult.SIGN_IN_REQUIRED) 
                    Log.e(TAG, "Failed to load: " + result.getStatus().toString());
                    failedLoad();
                
            
        );
     catch (IllegalStateException e) 
        Log.e(TAG, "Problem occurred with media during loading", e);
        failedLoad();
     catch (Exception e) 
        Log.e(TAG, "Problem opening media during loading", e);
        failedLoad();
    


private String fixURLs(String url) 
    // Only change to internal when using https
    if(url.indexOf("https") != -1) 
        SharedPreferences prefs = Util.getPreferences(downloadService);
        int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
        String externalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
        String internalUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_INTERNAL_URL + instance, null);
        url = url.replace(internalUrl, externalUrl);
    

    //  Use separate profile for Chromecast so users can do ogg on phone, mp3 for CC
    return url.replace(Constants.REST_CLIENT_ID, Constants.CHROMECAST_CLIENT_ID);


private void failedLoad() 
    Util.toast(downloadService, downloadService.getResources().getString(R.string.download_failed_to_load));
    downloadService.setPlayerState(PlayerState.STOPPED);
    error = true;



private class ConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks 
    private boolean isPlaying;
    private int position;
    private ResultCallback<Cast.ApplicationConnectionResult> resultCallback;

    ConnectionCallbacks(boolean isPlaying, int position) 
        this.isPlaying = isPlaying;
        this.position = position;

        resultCallback = new ResultCallback<Cast.ApplicationConnectionResult>() 
            @Override
            public void onResult(Cast.ApplicationConnectionResult result) 
                Status status = result.getStatus();
                if (status.isSuccess()) 
                    ApplicationMetadata applicationMetadata = result.getApplicationMetadata();
                    sessionId = result.getSessionId();
                    String applicationStatus = result.getApplicationStatus();
                    boolean wasLaunched = result.getWasLaunched();

                    applicationStarted = true;
                    setupChannel();
                 else 
                    shutdownInternal();
                
            
        ;
    

    @Override
    public void onConnected(Bundle connectionHint) 
        if (waitingForReconnect) 
            Log.i(TAG, "Reconnecting");
            reconnectApplication();
         else 
            launchApplication();
        
    

    @Override
    public void onConnectionSuspended(int cause) 
        Log.w(TAG, "Connection suspended");
        isPlaying = downloadService.getPlayerState() == PlayerState.STARTED;
        position = getRemotePosition();
        waitingForReconnect = true;
    

    void launchApplication() 
        try 
            Cast.CastApi.launchApplication(apiClient, CastCompat.APPLICATION_ID, false).setResultCallback(resultCallback);
         catch (Exception e) 
            Log.e(TAG, "Failed to launch application", e);
        
    
    void reconnectApplication() 
        try 
            Cast.CastApi.joinApplication(apiClient, CastCompat.APPLICATION_ID, sessionId).setResultCallback(resultCallback);
         catch (Exception e) 
            Log.e(TAG, "Failed to reconnect application", e);
        
    
    void setupChannel() 
        if(!waitingForReconnect) 
            mediaPlayer = new RemoteMediaPlayer();
            mediaPlayer.setOnStatusUpdatedListener(new RemoteMediaPlayer.OnStatusUpdatedListener() 
                @Override
                public void onStatusUpdated() 
                    MediaStatus mediaStatus = mediaPlayer.getMediaStatus();
                    if (mediaStatus == null) 
                        return;
                    

                    switch (mediaStatus.getPlayerState()) 
                        case MediaStatus.PLAYER_STATE_PLAYING:
                            if (ignoreNextPaused) 
                                ignoreNextPaused = false;
                            
                            downloadService.setPlayerState(PlayerState.STARTED);
                            break;
                        case MediaStatus.PLAYER_STATE_PAUSED:
                            if (!ignoreNextPaused) 
                                downloadService.setPlayerState(PlayerState.PAUSED);
                            
                            break;
                        case MediaStatus.PLAYER_STATE_BUFFERING:
                            downloadService.setPlayerState(PlayerState.PREPARING);
                            break;
                        case MediaStatus.PLAYER_STATE_IDLE:
                            if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) 
                                downloadService.setPlayerState(PlayerState.COMPLETED);
                                downloadService.onSongCompleted();
                             else if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_INTERRUPTED) 
                                if (downloadService.getPlayerState() != PlayerState.PREPARING) 
                                    downloadService.setPlayerState(PlayerState.PREPARING);
                                
                             else if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_ERROR) 
                                Log.e(TAG, "Idle due to unknown error");
                                downloadService.setPlayerState(PlayerState.COMPLETED);
                                downloadService.next();
                             else 
                                Log.w(TAG, "Idle reason: " + mediaStatus.getIdleReason());
                                downloadService.setPlayerState(PlayerState.IDLE);
                            
                            break;
                    
                
            );
        

        try 
            Cast.CastApi.setMessageReceivedCallbacks(apiClient, mediaPlayer.getNamespace(), mediaPlayer);
         catch (IOException e) 
            Log.e(TAG, "Exception while creating channel", e);
        

        if(!waitingForReconnect) 
            DownloadFile currentPlaying = downloadService.getCurrentPlaying();
            startSong(currentPlaying, isPlaying, position);
        
        if(waitingForReconnect) 
            waitingForReconnect = false;
        
    


private class ConnectionFailedListener implements GoogleApiClient.OnConnectionFailedListener 
    @Override
    public void onConnectionFailed(ConnectionResult result) 
        shutdownInternal();
    

编辑日志:

03-28 19:04:49.757    6305-6305/github.daneren2005.dsub I/ChromeCastController﹕ onApplicationStatusChanged: Chromecast Home Screen
03-28 19:04:52.280    6305-6305/github.daneren2005.dsub I/ChromeCastController﹕ onApplicationStatusChanged: null
03-28 19:04:54.162    6305-6305/github.daneren2005.dsub I/ChromeCastController﹕ onApplicationStatusChanged: Ready To Cast
03-28 19:05:05.194    6305-6305/github.daneren2005.dsub E/ChromeCastController﹕ Failed to load: StatusstatusCode=SIGN_IN_REQUIRED, resolution=null

【问题讨论】:

【参考方案1】:

奇怪的是你当时得到这样的状态码。想到的是用户可能没有登录他/她的 gmail 帐户或类似的东西。您是否有日志文件供我们查看,看看我们是否可以从上下文中获得更多信息?此外,可以肯定的是,此类用户会看到在电视上启动的应用程序,并且只有在加载媒体时才会引发错误?

【讨论】:

添加了日志。是的,它可以很好地显示启动画面,它只是拒绝实际加载任何内容。我有一台可以重现该问题的设备,它肯定已登录到 Gmail,除了没有 SIM 卡外,它似乎一切正常。 我们需要更多的日志,可能需要整个会话来查看正在发生的事情,并为 Play 服务打开调试日志。由于日志可能很大,请随时打开一个问题并将其附加到那里。 我在gist.github.com/daneren2005/c1cd23a8107f3730290c 附加了完整的日志。我看不出那里有什么特别的事情。我尝试使用 .setVerboseLoggingEnabled(true) 打开调试,但我不知道它实际上改变了什么。我有什么特别需要做的吗?如果您希望在 Chromecast 问题中继续讨论,我可以。 所以你可以自己重现这个?如果是这样,您能否尝试运行我们的一个 android 示例,看看它是否适合您。 我无法构建示例,因为无论我做什么,Gradle 都会出错。【参考方案2】:

问题是由于使用了自签名证书。我没有意识到我的旧手机上的问题,因为我换了主机并在换手机后购买了普通证书。如果 SDK 能通过一个有用的错误,那就太好了。抛出的问题让您认为这是连接到 Play Services SDK 的问题,而不是实际使用的 URL 的问题。

【讨论】:

你指的是什么证书? 我的应用程序允许人们使用自己的 Subsonic 服务器,可以是 http 或 https。使用 http 服务器或带有有效证书的 https 的人可以正常使用 Chromecast。每个使用自签名证书设置服务器的人都会遇到该错误。

以上是关于Chromecast - SIGN_IN_REQUIRED的主要内容,如果未能解决你的问题,请参考以下文章

检测我是不是是唯一连接到 Chromecast 设备的 Chromecast 发件人应用

Chromecast - 网址格式

Chromecast 仅限于 720p 图像?

尝试连接时 Chromecast 给出错误代码 7

升级后的 Chromecast - 设备不再可检测

通过 Chromecast 通过 url 投射网页