View
426
Download
1
Category
Preview:
Citation preview
Seu App na TV: Desenvolvimento para ChromeCast
Ivan de Aguirre !ivan.aguirre@gmail.com !Twitter: IvAguirre !G+: plus.google.com/+IvanAguirreBr
Sender App:
Sender App:Android
Sender App:Android
iOS
Sender App:Android
iOSChrome App
Sender App:Android
iOSChrome App
Receiver App:
Sender App:Android
iOSChrome App
Receiver App:HTML 5
Sender App:Android
iOSChrome App
Receiver App:HTML 5<video>
Sender App:Android
iOSChrome App
Receiver App:HTML 5<video>Registro
Sender App:Android
iOSChrome App
Receiver App:HTML 5<video>Registro
Application ID = URL
Workflow em detalhes
Workflow em detalhes
• Descoberta do Chromecast.
Workflow em detalhes
• Descoberta do Chromecast.
• (Re)Conexão com o Chromecast: sessionID.
Workflow em detalhes
• Descoberta do Chromecast.
• (Re)Conexão com o Chromecast: sessionID.
• Envio do Application ID ao Chromecast.
Workflow em detalhes
• Descoberta do Chromecast.
• (Re)Conexão com o Chromecast: sessionID.
• Envio do Application ID ao Chromecast.
• Chromecast acessa a URL do Application ID: Receiver App no ar!!
Workflow em detalhes
• Descoberta do Chromecast.
• (Re)Conexão com o Chromecast: sessionID.
• Envio do Application ID ao Chromecast.
• Chromecast acessa a URL do Application ID: Receiver App no ar!!
• Sender envia a URL para o vídeo (media channel) e/ou…
Workflow em detalhes
• Descoberta do Chromecast.
• (Re)Conexão com o Chromecast: sessionID.
• Envio do Application ID ao Chromecast.
• Chromecast acessa a URL do Application ID: Receiver App no ar!!
• Sender envia a URL para o vídeo (media channel) e/ou…
• Envia texto (custom channel).
Workflow em detalhes
• Descoberta do Chromecast.
• (Re)Conexão com o Chromecast: sessionID.
• Envio do Application ID ao Chromecast.
• Chromecast acessa a URL do Application ID: Receiver App no ar!!
• Sender envia a URL para o vídeo (media channel) e/ou…
• Envia texto (custom channel).
• Callbacks, callbacks, callbacks, callbacks…
Por dentro do Chromecast
Por dentro do Chromecast
• Chrome Browser.
Por dentro do Chromecast
• Chrome Browser.
• HTML5, CSS 3, JavaScript.
Por dentro do Chromecast
• Chrome Browser.
• HTML5, CSS 3, JavaScript.
• Limitações de memória e CPU.
Por dentro do Chromecast
• Chrome Browser.
• HTML5, CSS 3, JavaScript.
• Limitações de memória e CPU.
• Sem WebGL ou Chrome Extensions.
Por dentro do Chromecast
• Chrome Browser.
• HTML5, CSS 3, JavaScript.
• Limitações de memória e CPU.
• Sem WebGL ou Chrome Extensions.
• Nada de Tabs, janelas, popups ou inputs.
Por dentro do Chromecast
• Chrome Browser.
• HTML5, CSS 3, JavaScript.
• Limitações de memória e CPU.
• Sem WebGL ou Chrome Extensions.
• Nada de Tabs, janelas, popups ou inputs.
• Suporte à WebAudio API.
Por dentro do Chromecast
• Chrome Browser.
• HTML5, CSS 3, JavaScript.
• Limitações de memória e CPU.
• Sem WebGL ou Chrome Extensions.
• Nada de Tabs, janelas, popups ou inputs.
• Suporte à WebAudio API.
• Uma tag <video> ativa por vez.
developers.google.com/cast !
developers.google.com/cast/docs/ux_guidelines
!
developers.google.com/cast/docs/design_checklist
Sender
com.android.support:appcompat-v7
!
com.android.support:mediarouter-v7
!
com.google.android.gms:play-services
Sender
GoogleApiClient.ConnectionCallbacks GoogleApiClient.OnConnectionFailedListener MediaRouter.Callback Cast.Listener ResultCallback<Cast.ApplicationConnectionResult> RemoteMediaPlayer.OnStatusUpdatedListener RemoteMediaPlayer.OnMetadataUpdatedListener ResultCallback<RemoteMediaPlayer.MediaChannelResult>
Sender
GoogleApiClient.ConnectionCallbacks GoogleApiClient.OnConnectionFailedListener MediaRouter.Callback Cast.Listener ResultCallback<Cast.ApplicationConnectionResult> RemoteMediaPlayer.OnStatusUpdatedListener RemoteMediaPlayer.OnMetadataUpdatedListener ResultCallback<RemoteMediaPlayer.MediaChannelResult>
Sender
github.com/googlecast/CastCompanionLibrary-android
Sender + CastCompanionLibrary
public class MyApplication extends Application { private static VideoCastManager mCastMgr; public static VideoCastManager getVideoCastManager(Context ctx) { if (null == mCastMgr) { ! mCastMgr = VideoCastManager.initialize(ctx, "XYZ1234", null, /* activity com player */ null /* namespace */); mCastMgr.enableFeatures(… ! } mCastMgr.setContext(ctx); return mCastMgr; } }
Sender + CastCompanionLibrary
public class MainActivity extends ActionBarActivity { private VideoCastManager mVideoCastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BaseCastManager.checkGooglePlayServices(this); mVideoCastManager = MyApplication.getVideoCastManager( this); mVideoCastManager.reconnectSessionIfPossible(this, true, 5 /*sec*/); } }
Sender + CastCompanionLibrary
public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); ! getMenuInflater().inflate(R.menu.main, menu); ! mVideoCastManager.addMediaRouterButton(menu, R.id.media_route_menu_item); ! return true; }
Sender + CastCompanionLibrary
MediaMetadata mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
mediaMetadata.putString(MediaMetadata.KEY_TITLE,
"Title: Chromecast na QCON RJ 2014");
mediaMetadata.putString(MediaMetadata.KEY_SUBTITLE, "");
mediaMetadata.putString(MediaMetadata.KEY_STUDIO,
"Ivan de Aguirre Productions");
MediaInfo mediaInfo = new MediaInfo.Builder(
"https://d2k4ls0ga9ks2.cloudfront.net/VID_20140727_225510282.mp4")
.setContentType("video/mp4")
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setMetadata(mediaMetadata)
.build();
mVideoCastManager.startCastControllerActivity(this, mediaInfo, 0, true);
Receiver
Receiver
• Default Receiver.
Receiver
• Default Receiver.
• Styled Receiver.
Receiver
• Default Receiver.
• Styled Receiver.
• Custom Receiver.
Custom Receiver Mínimo
<html>
<head>
<title>Example minimum receiver</title>
<script src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
</head>
<body>
<video id='media'/>
<script>
...
</script>
</body>
</html>
Custom Receiver Mínimo
<script> window.onload = function() { window.mediaElement=document.getElementById('media'); ! window.mediaManager = new cast.receiver.MediaManager( window.mediaElement); ! window.castReceiverManager = cast.receiver .CastReceiverManager.getInstance(); ! window.castReceiverManager.start(); } </script>
Exemplo 1
Exemplo 1
• Custom Receiver para exibir propaganda e notificações no telefone.
Exemplo 1
• Custom Receiver para exibir propaganda e notificações no telefone.
• Envia URL do vídeo pelo Media Channel.
Exemplo 1
• Custom Receiver para exibir propaganda e notificações no telefone.
• Envia URL do vídeo pelo Media Channel.
• Envia texto pelo Custom Channel com as notificações.
Exemplo 1
• Custom Receiver para exibir propaganda e notificações no telefone.
• Envia URL do vídeo pelo Media Channel.
• Envia texto pelo Custom Channel com as notificações.
• No Receiver exibe propagandas.
Exemplo 1 - Sender
public class MyApplication extends Application { private static VideoCastManager mCastMgr; public static VideoCastManager getVideoCastManager(Context ctx) { if (null == mCastMgr) { mCastMgr = VideoCastManager.initialize(ctx, "XYZ1234", "urn:x-‐cast:org.gcastsamples.castnotifications"); // configurar opções... } ! mCastMgr.setContext(ctx); return mCastMgr; } }
Exemplo 1 - Senderpublic class MyNotificationListenerService extends NotificationListenerService { @Override public void onNotificationPosted(StatusBarNotification statusBarNotification) { String msg = String.valueOf( statusBarNotification.getNotification().tickerText); ! try { ! MyApplication.getVideoCastManager(getApplicationContext()) .sendDataMessage(msg); ! } catch (TransientNetworkDisconnectionException e) { Log.e("NotificationListenerService", "Can't send message", e); } catch (NoConnectionException e) { Log.e("NotificationListenerService", "Can't send message", e); } } }
Exemplo 1 - Receiver
<div id="notification_banner" class="alert alert-‐info" role="alert">
<h4>New Notification from your phone!!</h4>
<p id="notification_text">Test!!!</p>
</div>
!
<div id="ad_banner" class="alert alert-‐warning" role="alert">
<h4 id="ad_text">New Notification from your phone!!</h4>
</div>
!
<video id="media"/>
Exemplo 1 - Receiver
window.mediaElement = document.getElementById('media');
window.mediaElement.addEventListener('playing', function(event) {
advertising.start();
});
!window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
!window.castReceiverManager.onSenderDisconnected = function(event) {
if (window.castReceiverManager.getSenders().length == 0 &&
event.reason == cast.receiver.system.DisconnectReason.REQUESTED_BY_SENDER) {
advertising.stop();
window.close();
}
Exemplo 1 - Receiver
var nms = 'urn:x-‐cast:org.gcastsamples.castnotifications';
var customMessageBus = window.castReceiverManager .getCastMessageBus(nms);
customMessageBus.onMessage = function(event) { showNotification(event.data); }
window.castReceiverManager.start();
Exemplo 2
Exemplo 2
• Custom Receiver para exibir um gráfico.
Exemplo 2
• Custom Receiver para exibir um gráfico.
• www.flotcharts.org
Exemplo 2
• Custom Receiver para exibir um gráfico.
• www.flotcharts.org
• A página do Custom Receiver quando acessada pelo Chromecast é um Receiver.
Exemplo 2
• Custom Receiver para exibir um gráfico.
• www.flotcharts.org
• A página do Custom Receiver quando acessada pelo Chromecast é um Receiver.
• A página do Custom Receiver quando acessada pelo Browser é uma aplicação Web.
Exemplo 2
Exemplo 2 - Sender
public class MyApplication extends Application { ! private static DataCastManager mCastMgr; public static final String NAME_SPACE = "urn:x-‐cast:org.gcastsamples.plotandcast"; public static DataCastManager getDataCastManager(Context ctx) { if (null == mCastMgr) { mCastMgr = DataCastManager.initialize(ctx,"XYZ123", NAME_SPACE); } ! mCastMgr.setContext(ctx); return mCastMgr; } }
Exemplo 2 - Sender
String json = getData();
!
mDataCastManager.sendDataMessage(
json, MyApplication.NAME_SPACE);
Exemplo 2 - Receiver
<body> <form id="plot_inputs">
…
</form>
<div id="content">
<div class="chart-‐container">
<div id="placeholder" class="chart-‐placeholder"></div>
</div>
</div>
</body>
Exemplo 2 - Receiver
if (navigator.userAgent.indexOf('CrKey') >= 0) { ! $('#plot_inputs').hide(); // form inputs $('.chart-‐container').addClass('chart-‐container-‐for-‐tv'); ! startChromeCastMode(); !} else { ! startBrowserMode(); !}
Exemplo 2 - Receiver
function startChromeCastMode() { window.onload = function() { window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance(); var nms='urn:x-‐cast:org.gcastsamples.plotandcast'; var customMessageBus = window.castReceiverManager.getCastMessageBus(nms); customMessageBus.onMessage = function(event) { var json = $.parseJSON(event.data);; plot(json); } window.castReceiverManager.start(); } }
Mirror e Presentation
Mirror e Presentation
• Transmissão de Tela (Mirroring).
Mirror e Presentation
• Transmissão de Tela (Mirroring).
• Presentation API: API Level 17, Android 4.2+:
Mirror e Presentation
• Transmissão de Tela (Mirroring).
• Presentation API: API Level 17, Android 4.2+:
• Em modo Mirror renderizar um Layout na TV (não há receiver).
Mirror e Presentation
• Transmissão de Tela (Mirroring).
• Presentation API: API Level 17, Android 4.2+:
• Em modo Mirror renderizar um Layout na TV (não há receiver).
• Wireless Display.
Mirror e Presentation
• Transmissão de Tela (Mirroring).
• Presentation API: API Level 17, Android 4.2+:
• Em modo Mirror renderizar um Layout na TV (não há receiver).
• Wireless Display.
• Suporta Miracast.
Mirror e Presentation
• Transmissão de Tela (Mirroring).
• Presentation API: API Level 17, Android 4.2+:
• Em modo Mirror renderizar um Layout na TV (não há receiver).
• Wireless Display.
• Suporta Miracast.
• E Chromecast :)
Mirror e Presentation
• Transmissão de Tela (Mirroring).
• Presentation API: API Level 17, Android 4.2+:
• Em modo Mirror renderizar um Layout na TV (não há receiver).
• Wireless Display.
• Suporta Miracast.
• E Chromecast :)
• Plugin do Chromecast para Chrome: espelha aba e tela.
Mirror e Presentation
Mirror e Presentation
Exemplo 3 - Mirror e Presentation
Exemplo 3 - Mirror e Presentation
•Aplicação insere elementos em uma lista.
Exemplo 3 - Mirror e Presentation
•Aplicação insere elementos em uma lista.•A lista é renderizada e manipulada na TV.
Exemplo 3 - Mirror e Presentation
•Aplicação insere elementos em uma lista.•A lista é renderizada e manipulada na TV.•Não é casting!!
Exemplo 3 - Mirror e Presentationpublic class ListPresentation extends Presentation { private RecyclerView mRecyclerView; private RecyclerView.LayoutManager mLayoutManager; private MyAdapter mAdapter; public ListPresentation(Context context, Display display) { super(context, display); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Context ctx = getContext(); Resources r = ctx.getResources(); setContentView(R.layout.presentation); mRecyclerView = (RecyclerView) findViewById(R.id.list); mLayoutManager = new LinearLayoutManager(ctx); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mAdapter = new MyAdapter(); mRecyclerView.setAdapter(mAdapter); } ...
MediaRouter mMediaRouter = (MediaRouter)getSystemService(Context.MEDIA_ROUTER_SERVICE); MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO); Display presentationDisplay = route != null ? route.getPresentationDisplay() : null; if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) { mPresentation.dismiss(); mPresentation = null; } if (mPresentation == null && presentationDisplay != null) { mPresentation = new ListPresentation(this, presentationDisplay); mPresentation.setOnDismissListener(mOnDismissListener); try { mPresentation.show(); } catch (WindowManager.InvalidDisplayException ex) { Log.w(TAG, "Display was removed in the meantime.", ex); mPresentation = null; } }
Exemplo 3 - Mirror e Presentation
MediaRouter mMediaRouter = (MediaRouter)getSystemService(Context.MEDIA_ROUTER_SERVICE); MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO); Display presentationDisplay = route != null ? route.getPresentationDisplay() : null; if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) { mPresentation.dismiss(); mPresentation = null; } if (mPresentation == null && presentationDisplay != null) { mPresentation = new ListPresentation(this, presentationDisplay); mPresentation.setOnDismissListener(mOnDismissListener); try { mPresentation.show(); } catch (WindowManager.InvalidDisplayException ex) { Log.w(TAG, "Display was removed in the meantime.", ex); mPresentation = null; } }
Exemplo 3 - Mirror e Presentation
android.media.MediaRouter não é app compact!!
MediaRouter mMediaRouter = (MediaRouter)getSystemService(Context.MEDIA_ROUTER_SERVICE); MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO); Display presentationDisplay = route != null ? route.getPresentationDisplay() : null; if (mPresentation != null && mPresentation.getDisplay() != presentationDisplay) { mPresentation.dismiss(); mPresentation = null; } if (mPresentation == null && presentationDisplay != null) { mPresentation = new ListPresentation(this, presentationDisplay); mPresentation.setOnDismissListener(mOnDismissListener); try { mPresentation.show(); } catch (WindowManager.InvalidDisplayException ex) { Log.w(TAG, "Display was removed in the meantime.", ex); mPresentation = null; } }
Exemplo 3 - Mirror e Presentation
android.media.MediaRouter não é app compact!!
ROUTE_TYPE_LIVE_AUDIO
Chrome Sender
Chrome Sender
Chrome Sender
Developer Tools: <chromecast ip>:9222
Developer Tools: <chromecast ip>:9222
Developer Tools: <chromecast ip>:9222
• window.location.reload(true);
Developer Tools: <chromecast ip>:9222
• window.location.reload(true);
• window.location.replace('http://myhost.com/receiver.html');
FAQ
FAQ
• Sender/Receiver: HTTPS.
FAQ
• Sender/Receiver: HTTPS.
• URL do Receiver: HTTP em desenvolvimento, HTTPS em produção.
FAQ
• Sender/Receiver: HTTPS.
• URL do Receiver: HTTP em desenvolvimento, HTTPS em produção.
• Múltiplas conexões ao receiver.
FAQ
• Sender/Receiver: HTTPS.
• URL do Receiver: HTTP em desenvolvimento, HTTPS em produção.
• Múltiplas conexões ao receiver.
• Segurança: é preciso implementar os mecanismos.
FAQ
• Sender/Receiver: HTTPS.
• URL do Receiver: HTTP em desenvolvimento, HTTPS em produção.
• Múltiplas conexões ao receiver.
• Segurança: é preciso implementar os mecanismos.
• Media Player Library (Beta): Live Streaming, MPEG-DASH, Smooth Streaming, DRM, etc..
FAQ
• Sender/Receiver: HTTPS.
• URL do Receiver: HTTP em desenvolvimento, HTTPS em produção.
• Múltiplas conexões ao receiver.
• Segurança: é preciso implementar os mecanismos.
• Media Player Library (Beta): Live Streaming, MPEG-DASH, Smooth Streaming, DRM, etc..
• CORS.
FAQ
• Sender/Receiver: HTTPS.
• URL do Receiver: HTTP em desenvolvimento, HTTPS em produção.
• Múltiplas conexões ao receiver.
• Segurança: é preciso implementar os mecanismos.
• Media Player Library (Beta): Live Streaming, MPEG-DASH, Smooth Streaming, DRM, etc..
• CORS.
• Não esqueçam do iOS :)
Futuro
Futuro
• Google TV?
Futuro
• Google TV?
• Chrome OS: integração no Google Drive na build de desenvolvimento.
Futuro
• Google TV?
• Chrome OS: integração no Google Drive na build de desenvolvimento.
• Conexão fora da mesma rede Wifi.
Referências
developers.google.com/cast
cast.google.com/publish
github.com/googlecast
code.google.com/p/google-cast-sdk/issues/list
github.com/ivan-aguirre/chromecast_samples
ivan-aguirre.github.io/ccast-graph/receiver.html
ivan-aguirre.github.io/video-ccast-player/receiver.html
G+: Google Cast Developers
Seu App na TV: Desenvolvimento para ChromeCast
Obrigado!! Cast your questions :)
Ivan de Aguirre !
ivan.aguirre@gmail.com !
Twitter: IvAguirre !
G+: plus.google.com/+IvanAguirreBr
Recommended