Slides
Transcrição
Slides
Seu App na TV: Desenvolvimento para ChromeCast Ivan de Aguirre ! [email protected] ! Twitter: IvAguirre ! G+: plus.google.com/+IvanAguirreBr Sender App: Sender App: Android Sender App: Android iOS Sender App: Android iOS Chrome App Sender App: Android iOS Chrome App Receiver App: Sender App: Android iOS Chrome App Receiver App: HTML 5 Sender App: Android iOS Chrome App Receiver App: HTML 5 <video> Sender App: Android iOS Chrome App Receiver App: HTML 5 <video> Registro Sender App: Android iOS Chrome 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 - Sender public 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> </body> </div> 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 Presentation public 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); } ... 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 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(); android.media.MediaRouter mPresentation = null; } não é app compact!! 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 ROUTE_TYPE_LIVE_AUDIO 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(); android.media.MediaRouter mPresentation = null; } não é app compact!! 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; } } 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 ! [email protected] ! Twitter: IvAguirre ! G+: plus.google.com/+IvanAguirreBr