Add Smart View SDK to cast-enabled app

This document assumes you already have a cast-enabled mobile app and a working Tizen TV app. It walks through and compares the steps needed to incorporate Smart View SDK support into a standard Chromecast integration that uses a custom receiver app. This example is based on Android but the same principles can be easily be extended to cover the iOS or Javascript APIs.


Related Info


Getting Started

Components to be converted

  • Sender Application - Discover, Launch, Communication
  • Receiver Application - Commnication
  • Media Player Library - Handle rich media source in receiver app

Sender Application

The sample code written below for Android.

Discover

  • The general mobile workflow is:
Step Google Cast Smart View SDK
1 Sender app starts MediaRouter device discovery (MediaRouter.addCallback)

Start the Discovery Process
2 MediaRouter informs sender app of the route the user selected MediaRouter.Callback.onRouteSelected

Listen for Events indicating services Added/Removed. Present the list of discovered TVs to the user

3 - Stop the Discovery Process. Once the user has selected a TV from the list

Example GoogleCast Android API Usage

mMediaRouter = MediaRouter.getInstance(getApplicationContext());

private class MyMediaRouterCallback extends MediaRouter.Callback {

  @Override
  public void onRouteSelected(MediaRouter router, RouteInfo info) {
    mSelectedDevice = CastDevice.getFromBundle(info.getExtras());
    String routeId = info.getId();
    ...
  }

  @Override
  public void onRouteUnselected(MediaRouter router, RouteInfo info) {
    teardown();
    mSelectedDevice = null;
  }
}

@Override
protected void onStart() {
  super.onStart();
  mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
    MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}

@Override
protected void onStop() {
  mMediaRouter.removeCallback(mMediaRouterCallback);
  super.onStop();
}

Example SmartView SDK Android API Usage

// Get an instance of Search
Search search = Service.search(getContext());

// Add a listener for the service found event
search.setOnServiceFoundListener(
    new OnServiceFoundListener() {
        @Override
        public void onFound(Service service) {
            // Add service to a displayed list 
            //where your user can select one.
        }
    }
);

// Add a listener for the service lost event
search.setOnServiceLostListener(
    new OnServiceLostListener() {
        @Override
        public void onLost(Service service) {
            // Remove this service 
            //from the displayed list.
        }
    }
);
// Start the discovery process
search.start();

// Do something while we wait for services to be discovered.
...

// Stop the discovery process after some amount of time,
search.stop();

Launch

  • The general mobile workflow is:
    Step Google Cast Smart View SDK
    1 Sender app creates a GoogleApiClient GoogleApiClient.Builder

    service instance is returned OnFound()
    2 Sender app connects the GoogleApiClient GoogleApiClient.connect service.createApplication(appId, channelId) Create an application reference to your tv app by app id, and channel id

    3 GoogleApiClient.ConnectionCallbacks.onConnected application.connect() / application.disconnect() Connect to the tv application Disconnect from the tv application

    4 Sender app launches the receiver app: Cast.CastApi.launchApplication Listen for the connect / disconnect event onConnect, onDisConnect

Example GoogleCast Android API Usage

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

mApiClient.connect();

private class ConnectionCallbacks implements
      GoogleApiClient.ConnectionCallbacks {
  @Override
  public void onConnected(Bundle connectionHint) {
    if (mWaitingForReconnect) {
        Cast.CastApi.launchApplication(mApiClient, "YOUR_APPLICATION_ID", false)
          .setResultCallback(
             new ResultCallback() {
            @Override
            public void onResult(Cast.ApplicationConnectionResult result) {
                Status status = result.getStatus();
                if (status.isSuccess()) {
                  ApplicationMetadata applicationMetadata = 
                                                  result.getApplicationMetadata();
                  String sessionId = result.getSessionId();
                  String applicationStatus = result.getApplicationStatus();
                  boolean wasLaunched = result.getWasLaunched();
                  ...
                } else {
                  teardown();
                }
            }
        });
  }

Example SmartView SDK Android API Usage

// Tizen WebApp
String url = "YOUR_APPLICATION_ID";

// Example channel id
String channelId = "com.samsung.multiscreen.helloworld";

// Get an instance of Application.
Application application = service.createApplication(url, channelId);

// Listen for the connect event
application.setOnConnectListener(new OnConnectListener() {

    @Override
    public void onConnect(Client client) {
        Log.d(LOGTAG, "Application.onConnect():" + client.toString());                
    }
});

// Connect and launch the application.
// When you connect to a service, the specified application will 
// be launched automatically.
application.connect(new Result() {
    @Override
    public void onSuccess(Client client) {
        // The application is launched, and is ready to accept messages.
    }

    @Override
    public void onError(Error error) {
    }
);

Communicate

  • The general mobile workflow is:
    Step Google Cast Smart View SDK
    1 Sender app creates a communication channel Cast.CastApi.setMessageReceivedCallbacks

    Sender sends a message to the receiver application.publish

    2 Sender sends a message to the receiver over the communication channel Cast.CastApi.sendMessage*

    Sender receive a message onMessage

    3 Sender receive a message onMessageReceived

Example GoogleCast Android API Usage

/*************
1. Send
**************/
private void sendMessage(String message) {
if (mApiClient != null && mHelloWorldChannel != null) {
try {
  Cast.CastApi.sendMessage(mApiClient
                          ,mHelloWorldChannel.getNamespace()
                          ,message)
    .setResultCallback(
      new ResultCallback() {
        @Override
        public void onResult(Status result) {
          if (!result.isSuccess()) {
            Log.e(TAG, "Sending message failed");
          }
        }
      });
  } catch (Exception e) {
    Log.e(TAG, "Exception while sending message", e);
  }
 }
}


/*************
2. Receive
**************/
 public String getNamespace() {
    return "urn:x-cast:com.example.custom";
  }

@Override
  public void onMessageReceived(CastDevice castDevice, String namespace, String message) {
    Log.d(TAG, "onMessageReceived: " + message);
  }
}

Cast.CastApi.launchApplication(mApiClient,"YOUR_APPLICATION_ID" , false).setResultCallback(
   new ResultCallback() {
  @Override
  public void onResult(Cast.ApplicationConnectionResult result){
      Status status = result.getStatus();
      if (status.isSuccess()) {

        mHelloWorldChannel = new HelloWorldChannel();
        try {
          Cast.CastApi.setMessageReceivedCallbacks(mApiClient,
                mHelloWorldChannel.getNamespace(),
                mHelloWorldChannel);
        } catch (IOException e) {
          Log.e(TAG, "Exception while creating channel", e);
        }
      }
  }
});

Example SmartView SDK Android API Usage

/************* 
 1. Send 
 **************/ 
// Note: The TV application is designated as the "HOST" 
String event = "say";
String messageData = "Hello World!";

// Send a message to the TV application only, by default.
application.publish(event, messageData);

// Send a message to the TV application, explicitly.
application.publish(event, messageData, Message.TARGET_HOST);

// Send a "broadcast" message to all clients EXCEPT yourself. 
application.publish(event, messageData, Message.TARGET_BROADCAST);

// Send a message to all clients INCLUDING yourself 
application.publish(event, messageData, Message.TARGET_ALL);

// Send a message to a specific client
String clientId = "123467"; // Assuming that this is a valid id
Client client = channel.getClients().get(clientId);
application.publish(event, messageData, client);

// Send a message to a list of clients
List clients = ... 
application.publish(event, messageData, clients);

// Send a binary message to the TV application only, by default.
byte[] payload = {0x00, 0x01, 0x02, 0x03};
application.publish(event, messageData, payload);


/************* 
 2. Receive
 **************/ 
 // The event that this client is interested in
// receiving published messages.
String event = "say";

// Listen for a message by event
application.addOnMessageListener(event, new OnMessageListener() {
    @Override
    public void onMessage(Message message) {
        Log.d(LOGTAG, "message: " + message.toString());

        // Got a message with some data or a binary payload.
    }
});

Receiver Application

  • The general mobile workflow is:
    Step Google Cast Smart View SDK
    1 3 types of receiver app: Default, Styled, Custom

    Custom type Receiver App
    2 Get castReceiverManager instance Get a reference to the "local" service window.msf.local {}

    3 Override/provide any event listeners on the CastReceiverManager. Connect to a communication "channel" service.channel('CHANNEL_NAME');
    4 Custom protocol extensions (namespaces), call getCastMessageBus for the namespaces to be used. Add Event Listeners
    5 Call start on the CastReceiverManager to indicate the receiver application is ready to receive messages

Example GoogleCast Android API Usage

window.onload = function() {
  window.castReceiverManager 
        = cast.receiver.CastReceiverManager.getInstance();
  console.log('Starting Receiver Manager');

  // handler for the 'ready' event
  castReceiverManager.onReady = function(event) {
  };

  // handler for 'senderconnected' event
  castReceiverManager.onSenderConnected = function(event) {
  };

  // handler for 'senderdisconnected' event
  castReceiverManager.onSenderDisconnected = function(event) {
    }
  };
  // create a CastMessageBus to handle messages
  // for a custom namespace
  window.messageBus =
    window.castReceiverManager.getCastMessageBus(
        'urn:x-cast:com.google.cast.sample.helloworld');

  // handler for the CastMessageBus message event
  window.messageBus.onMessage = function(event) {
    // display the message from the sender
    displayText(event.data);
    // inform all senders on the CastMessageBus
    // of the incoming message event
    // sender message listener will be invoked
    window.messageBus.send(event.senderId, event.data);
  }
  // initialize the CastReceiverManager
  // with an application status message
  window.castReceiverManager.start
       ({statusText: "Application is starting"});
};

Example SmartView SDK Android API Usage

window.msf.local(function(err, service){
    var channel = service.channel
                 ('com.samsung.multiscreen.helloworld');

    channel.connect({name: 'TV'}, function (err) {
        if(err) return console.error(err);
    });

    channel.on('connect', function(client){
    });

    channel.on('disconnect', function(client){
    });
    channel.on('clientConnect', function(client){
    });

    channel.on('clientDisconnect', function(client){
    });

    channel.on('say', function(msg, from){
    });
});

Media Player Library vs Tizen AVPlay Feature

  • The basic workflow is:
    Step Google Cast AVPlay
    1 Creating a Host object Call webapis.avplay.open()
    2 Creating a Player object Set listener event by webapis.avplay.setListener()
    3 Creating a Protocol object Call webapis.avplay.prepare()
    4 load the media and start at a specific position (typically 0) window.player.load(protocol, initStart);

    Set media size by webapis.avplay.setDisplayRect()
    5 Call webapis.avplay.play()

Example GoogleCast Android API Usage

var mediaElement = document.getElementById('vid');
// Create the media manager. This will handle all media messages by default.
window.mediaManager = new cast.receiver.MediaManager(mediaElement);
if (event.data['media'] && event.data['media']['contentId']) {
    console.log('Starting media application');
    var url = event.data['media']['contentId'];
// Create the Host - much of your interaction with the library uses the Host and
// methods you provide to it.
    window.host = new cast.player.api.Host(
      {'mediaElement':mediaElement, 'url':url});
    var ext = url.substring(url.lastIndexOf('.'), url.length);
    var initStart = event.data['media']['currentTime'] || 0;
    var autoplay = event.data['autoplay'] || true;
    var protocol = null;
    mediaElement.autoplay = autoplay;  // Make sure autoplay get's set
    if (url.lastIndexOf('.m3u8') >= 0) {
// HTTP Live Streaming
      protocol = cast.player.api.CreateHlsStreamingProtocol(host);
    } else if (url.lastIndexOf('.mpd') >= 0) {
// MPEG-DASH
      protocol = cast.player.api.CreateDashStreamingProtocol(host);
    } else if (url.indexOf('.ism/') >= 0) {
// Smooth Streaming
      protocol = cast.player.api.CreateSmoothStreamingProtocol(host);
    }
// How to override a method in Host. I know that it's safe to just provide this
// method.
    host.onError = function(errorCode) {
      console.log("Fatal Error - " + errorCode);
      if (window.player) {
        window.player.unload();
        window.player = null;
      }
    };
    if (protocol !== null) {
      console.log("Starting Media Player Library");
      window.player = new cast.player.api.Player(host);
      window.player.load(protocol, initStart);
    }
    else {
      window.defaultOnLoad(event);    // do the default process
    }
window.player = null;
console.log('Application is ready, starting system');
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
castReceiverManager.start();

Example SmartView SDK Android API Usage

var config = { 
    url: 'http://media.w3.org/2010/05/bunny/trailer.mp4', 
    player: document.getElementById('av-player'),
    /*Smooth Streaming examples*/
    // 'http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/Manifest',
    // url: 'http://playready.directtaps.net/smoothstreaming/TTLSS720VC1/To_The_Limit_720.ism/Manifest'
};
if (!url) {
    url = config.url;
}
log('videoPlayer open: ' + url);
try {
    webapis.avplay.open(url);
    webapis.avplay.setDisplayRect(
        x,
        y,
        width,
        height
    );
    webapis.avplay.setListener(listener);
} catch (e) {
    log(e);
}
//set bitrates according to the values in your stream manifest
//            this.setBitrate(477000, 2056000, 2056000, 688000);
webapis.avplay.prepare();
webapis.avplay.play();
// pause
webapis.avplay.pause();
// stop
webapis.avplay.stop();
// close :To destroy the avplay object
webapis.avplay.close();
// Backward
webapis.avplay.jumpBackward(time);
// Forward
webapis.avplay.jumpForward(time);
// Time
var duration = webapis.avplay.getDuration();
var currentTime = webapis.avplay.getCurrentTime();

Integrating Smart View SDK and Google Cast

Example Android API Usage

/****************************************
 1. Discover
****************************************/
//Smart View SDK Search
mSearch = Service.search(mContext); 
mSearch.start();

// Start media router discovery
mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
        MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);


/****************************************
 2. add Listener to discover device
*****************************************/
//Smart View SDK
mSearch.setOnServiceFoundListener(
                new Search.OnServiceFoundListener() {
                    @Override
                    public void onFound(Service service) {
                        Log.d(TAG, "Search.onFound() service : " + service.toString());
                        if(!mDeviceList.contains(service)) {
                            mDeviceList.add(service);
                            mTVListAdapter.add(service);
                            mTVListAdapter.notifyDataSetChanged();
                        }
                    }
                }
        );
        mSearch.setOnServiceLostListener(
                new Search.OnServiceLostListener() {
                    @Override
                    public void onLost(Service service) {
                        Log.d(TAG, "Search.onLost() service : " + service.toString());
                        mDeviceList.remove(service);
                        mTVListAdapter.remove(service);
                        mTVListAdapter.notifyDataSetChanged();
                    }
                }
        );  
 
 
    /**
     * For intergrate Google Cast Device discover
     * Callback for MediaRouter events
     */
    private class MyMediaRouterCallback extends MediaRouter.Callback {
        @Override
        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
            CastDevice castDevice = CastDevice.getFromBundle(route.getExtras());
            Log.d(TAG, "onRouteAdded CastDevice = " + route.toString());
            if(!mDeviceList.contains(castDevice)) {
                mDeviceList.add(castDevice);
                mTVListAdapter.add(castDevice);
                mTVListAdapter.notifyDataSetChanged();
            }
        }
        @Override
        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
            CastDevice castDevice = CastDevice.getFromBundle(route.getExtras());
            Log.d(TAG, "onRouteChanged CastDevice = " + route.toString());
            if(!mDeviceList.contains(castDevice)) {
                mDeviceList.add(castDevice);
                mTVListAdapter.add(castDevice);
                mTVListAdapter.notifyDataSetChanged();
            }
        }
        @Override
        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo route) {
            Log.d(TAG, "onRouteRemoved");
            CastDevice castDevice = CastDevice.getFromBundle(route.getExtras());
            mDeviceList.add(castDevice);
            mTVListAdapter.remove(castDevice);
            mTVListAdapter.notifyDataSetChanged();
        }
        @Override
        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo info) {
            Log.d(TAG, "onRouteSelected -- not use");
            Log.d(TAG, "onRouteSelected");
        }
        @Override
        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo info) {
            Log.d(TAG, "onRouteUnselected ---not use");
        }
    } 

/****************************************
 3. Discover stop
****************************************/
// Smart View SDK
 mSearch.stop();

// Google cast. End media router discovery
 mMediaRouter.removeCallback(mMediaRouterCallback);
 
 
 /****************************************
 4. Launch : 
 How to get MSF "Service"  and google cast "CastDevice"
****************************************/
String cName = mTVListAdapter.getItem(which).getClass().getName();
 
if (Service.class.getName().equals(cName)) {
    // Launch Smart View SDK app
}else if (CastDevice.class.getName().equals(cName)) {
    // Launch Google Cast app
}

Sample Application

Here are two basic applications.

It's very similar and easy to understand how works with each SDK

Google Cast

  • HelloText-Android

    Android sender application to send and receive text messages.

    Include android sender app and receiver app.

Smart View SDK

  • Hello World

    This is a very simple application that demonstrates the basics of working with the SDK.

    It will allow you to understand and visualize how the core features work.

    This includes the "discovery" process, launching a tv app, and sending/receiving simple messages.

    This application is the best one to start with if you are new to the SDK.

    There are Android mobile versions available and the source for the TV app is also included.