How to add a custom SessionProvider with Google Ca

2019-04-12 15:02发布

问题:

We've recently updated the Google Cast SDK to version 3. With this SDK it should be possible to add support for non-cast devices by implementing getAdditionalSessionProviders() in an OptionsProvider. We have created a custom SessionProvider to return with the getAdditionalSessionProviders() method.

However, this SessionProvider is never used and it will probably only be used when a device has been discovered and selected in the selection list when the MediaRouteButton is pressed as described in the Session documentation. But we can not find a way to add our discovered non-cast device to this list. We have searched the API, the documentation and the examples that are available online, but we couldn't find how to do this. We have only found examples of older sdk versions, but these are completely different and not usable.

We would like to use this functionality to add Samsung tv's to the Google Cast list with Samsung's SmartView SDK just like the YouTube and Netflix app do.

回答1:

Create a custom Media Route Provider: Here is a fake MediaRouteProvider for a custom media route. This class does nothing but publish a fake custom MediaRoute so that it can be seen and selected by the Cast SDK from MediaRouter.

import android.content.Context;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.v7.media.MediaRouteDescriptor;
import android.support.v7.media.MediaRouteDiscoveryRequest;
import android.support.v7.media.MediaRouteProvider;
import android.support.v7.media.MediaRouteProvider.RouteController;
import android.support.v7.media.MediaRouteProviderDescriptor;
import android.support.v7.media.MediaRouter;
import java.util.ArrayList;

public final class CustomMediaRouteProvider extends MediaRouteProvider {
  private static final ArrayList<IntentFilter> CONTROL_FILTERS_BASIC;
  private static MediaRouteDescriptor DEFAULT_MEDIA_ROUTE_DESCRIPTOR;

  static {
    // This filter will be used by Cast SDK to match the session category.
    IntentFilter customControls = new IntentFilter();
    customControls.addCategory(CustomSessionProvider.CUSTOM_CATEGORY);

    CONTROL_FILTERS_BASIC = new ArrayList<IntentFilter>();
    CONTROL_FILTERS_BASIC.add(customControls);

    Bundle extras = new Bundle();
    extras.putCharSequence("ROUTE_URL", "http://abcdef.cyz");
    DEFAULT_MEDIA_ROUTE_DESCRIPTOR =
        new MediaRouteDescriptor.Builder("fake-custom-route-id", "fake custom route")
            .setDescription("Foo description")
            .addControlFilters(CONTROL_FILTERS_BASIC)
            .setPlaybackStream(AudioManager.STREAM_MUSIC)
            .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
            .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
            .setVolumeMax(100)
            .setVolume(10)
            .setExtras(extras)
            .build();
  }

  public CustomMediaRouteProvider(Context context) {
    super(context);
  }

  @Override
  public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
    if (request == null || request.getSelector() == null) {
      return;
    }

    publishRoutes();
  }

  @Override
  public RouteController onCreateRouteController(String routeId) {
    return null;
  }

  private void publishRoutes() {
    MediaRouteProviderDescriptor providerDescriptor =
        new MediaRouteProviderDescriptor.Builder().addRoute(DEFAULT_MEDIA_ROUTE_DESCRIPTOR).build();

    setDescriptor(providerDescriptor);
  }
}

You need a SessionProvider implementation for your custom session:

import android.content.Context;
import com.google.android.gms.cast.framework.Session;
import com.google.android.gms.cast.framework.SessionProvider;

public class CustomSessionProvider extends SessionProvider {
  public static final String CUSTOM_CATEGORY = "CUSTOM";

  public CustomSessionProvider(Context applicationContext) {
    super(applicationContext, CUSTOM_CATEGORY);
  }

  @Override
  public Session createSession(String sessionId) {
    return new CustomSession(getContext(), getCategory(), sessionId);
  }

  @Override
  public boolean isSessionRecoverable() {
    return true;
  }
}

And a Session implementation: Here is a fake Session implementation for custom media route type. It always succeeds on start/resume/end.

import android.content.Context;
import android.os.Bundle;
import com.google.android.gms.cast.framework.Session;

public class CustomSession extends Session {
  private static final String FAKE_SESSION_ID = "custom.session.id.12345";

  CustomSession(Context applicationContext, String category, String sessionId) {
    super(applicationContext, category, sessionId);
  }

  @Override
  protected void start(Bundle routeInfoExtra) {
    notifySessionStarted(FAKE_SESSION_ID);
  }

  @Override
  protected void resume(Bundle routeInfoExtra) {
    notifySessionResumed(false);
  }

  @Override
  protected void end(boolean stopCasting) {
    notifySessionEnded(0);
  }
}

In your Cast V3 sender app, specify the additional session provider:

public class CastOptionsProvider implements OptionsProvider {
@Override
  public List<SessionProvider> getAdditionalSessionProviders(Context appContext) {
    List<SessionProvider> additionalProviders = new ArrayList<>();
    additionalProviders.add(new CustomSessionProvider(appContext));
    return additionalProviders;
  }
}

In your Application add the MediaRouter provider:

public class CastVideosApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    MediaRouter mediaRouter = MediaRouter.getInstance(this);
    mediaRouter.addProvider(new CustomMediaRouteProvider(this));
  }
}


回答2:

So, I had just the same problem as you, and I found some way to make it work, but as you noted, as nothing is really documented on this topic, I am not sure it is the "right way to do it".

Here is basically what I do:

  • Create a Media Route Provider (the dev guide is here)
  • Extend Session and SessionProvider
  • Implement OptionsProvider and return your SessionProvider in the method getAdditionalSessionProviders

The Media Route Provider is in charge of managing your device and all related commands / communication. You can test it with a very simple app using the Media Router API. There is a github from googlesample about Media Router : Android BasicMediaRouter Sample.

In you custom Session class, you can access the MediaRouter (you got context passed in the constructor) and retrieve the current Route :

 MediaRouter mediaRouter = MediaRouter.getInstance(context);
  if (mediaRouter != null && mediaRouter.getSelectedRoute() != null)
    Log.d("Cast", "Selected route name is " +  mediaRouter.getSelectedRoute().getName());

With that, you should be able to get your device if it is the currently selected one (you'll be notified in the start() method of Session), and pass commands from the Session object to your Media Route Provider, as done in the Media Route sample.