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 
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> 
</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 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); 
} 
...
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 
ROUTE_TYPE_LIVE_AUDIO 
= 
(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!!
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

Chromecast na Qcon RJ

  • 1.
    Seu App naTV: Desenvolvimento para ChromeCast Ivan de Aguirre ! ivan.aguirre@gmail.com ! Twitter: IvAguirre ! G+: plus.google.com/+IvanAguirreBr
  • 3.
  • 4.
  • 5.
  • 6.
    Sender App: Android iOS Chrome App
  • 7.
    Sender App: Android iOS Chrome App Receiver App:
  • 8.
    Sender App: Android iOS Chrome App Receiver App: HTML 5
  • 9.
    Sender App: Android iOS Chrome App Receiver App: HTML 5 <video>
  • 10.
    Sender App: Android iOS Chrome App Receiver App: HTML 5 <video> Registro
  • 11.
    Sender App: Android iOS Chrome App Receiver App: HTML 5 <video> Registro Application ID = URL
  • 16.
  • 17.
    Workflow em detalhes • Descoberta do Chromecast.
  • 18.
    Workflow em detalhes • Descoberta do Chromecast. • (Re)Conexão com o Chromecast: sessionID.
  • 19.
    Workflow em detalhes • Descoberta do Chromecast. • (Re)Conexão com o Chromecast: sessionID. • Envio do Application ID ao Chromecast.
  • 20.
    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!!
  • 21.
    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…
  • 22.
    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).
  • 23.
    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…
  • 24.
    Por dentro doChromecast
  • 25.
    Por dentro doChromecast • Chrome Browser.
  • 26.
    Por dentro doChromecast • Chrome Browser. • HTML5, CSS 3, JavaScript.
  • 27.
    Por dentro doChromecast • Chrome Browser. • HTML5, CSS 3, JavaScript. • Limitações de memória e CPU.
  • 28.
    Por dentro doChromecast • Chrome Browser. • HTML5, CSS 3, JavaScript. • Limitações de memória e CPU. • Sem WebGL ou Chrome Extensions.
  • 29.
    Por dentro doChromecast • 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.
  • 30.
    Por dentro doChromecast • 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.
  • 31.
    Por dentro doChromecast • 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.
  • 32.
    developers.google.com/cast ! developers.google.com/cast/ docs/ux_guidelines ! developers.google.com/cast/ docs/design_checklist
  • 33.
    Sender com.android.support:appcompat-v7 ! com.android.support:mediarouter-v7 ! com.google.android.gms:play-services
  • 34.
    Sender GoogleApiClient.ConnectionCallbacks GoogleApiClient.OnConnectionFailedListener MediaRouter.Callback Cast.Listener ResultCallback<Cast.ApplicationConnectionResult> RemoteMediaPlayer.OnStatusUpdatedListener RemoteMediaPlayer.OnMetadataUpdatedListener ResultCallback<RemoteMediaPlayer.MediaChannelResult>
  • 35.
    Sender GoogleApiClient.ConnectionCallbacks GoogleApiClient.OnConnectionFailedListener MediaRouter.Callback Cast.Listener ResultCallback<Cast.ApplicationConnectionResult> RemoteMediaPlayer.OnStatusUpdatedListener RemoteMediaPlayer.OnMetadataUpdatedListener ResultCallback<RemoteMediaPlayer.MediaChannelResult>
  • 36.
  • 37.
    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; } }
  • 38.
    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*/); } }
  • 39.
    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; }
  • 40.
    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);
  • 41.
  • 42.
  • 43.
    Receiver • DefaultReceiver. • Styled Receiver.
  • 44.
    Receiver • DefaultReceiver. • Styled Receiver. • Custom Receiver.
  • 45.
    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>
  • 46.
    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>
  • 47.
  • 48.
    Exemplo 1 •Custom Receiver para exibir propaganda e notificações no telefone.
  • 49.
    Exemplo 1 •Custom Receiver para exibir propaganda e notificações no telefone. • Envia URL do vídeo pelo Media Channel.
  • 50.
    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.
  • 51.
    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.
  • 52.
    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; } }
  • 53.
    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); } } }
  • 54.
    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"/>
  • 55.
    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(); }
  • 56.
    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();
  • 57.
  • 58.
    Exemplo 2 •Custom Receiver para exibir um gráfico.
  • 59.
    Exemplo 2 •Custom Receiver para exibir um gráfico. • www.flotcharts.org
  • 60.
    Exemplo 2 •Custom Receiver para exibir um gráfico. • www.flotcharts.org • A página do Custom Receiver quando acessada pelo Chromecast é um Receiver.
  • 61.
    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.
  • 62.
  • 63.
    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; } }
  • 64.
    Exemplo 2 -Sender String json = getData(); ! mDataCastManager.sendDataMessage( json, MyApplication.NAME_SPACE);
  • 65.
    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>
  • 66.
    Exemplo 2 -Receiver if (navigator.userAgent.indexOf('CrKey') >= 0) { ! $('#plot_inputs').hide(); // form inputs $('.chart-­‐container').addClass('chart-­‐container-­‐for-­‐tv'); ! startChromeCastMode(); ! } else { ! startBrowserMode(); ! }
  • 67.
    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(); } }
  • 68.
  • 69.
    Mirror e Presentation • Transmissão de Tela (Mirroring).
  • 70.
    Mirror e Presentation • Transmissão de Tela (Mirroring). • Presentation API: API Level 17, Android 4.2+:
  • 71.
    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).
  • 72.
    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.
  • 73.
    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.
  • 74.
    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 :)
  • 75.
    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.
  • 76.
  • 77.
  • 78.
    Exemplo 3 -Mirror e Presentation
  • 79.
    Exemplo 3 -Mirror e Presentation •Aplicação insere elementos em uma lista.
  • 80.
    Exemplo 3 -Mirror e Presentation •Aplicação insere elementos em uma lista. • A lista é renderizada e manipulada na TV.
  • 81.
    Exemplo 3 -Mirror e Presentation •Aplicação insere elementos em uma lista. • A lista é renderizada e manipulada na TV. •Não é casting!!
  • 82.
    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); } ...
  • 83.
    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
  • 84.
    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!!
  • 85.
    MediaRouter mMediaRouter ROUTE_TYPE_LIVE_AUDIO = (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!!
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
    Developer Tools: <chromecastip>:9222 • window.location.reload(true);
  • 92.
    Developer Tools: <chromecastip>:9222 • window.location.reload(true); • window.location.replace('http://myhost.com/receiver.html');
  • 93.
  • 94.
  • 95.
    FAQ • Sender/Receiver:HTTPS. • URL do Receiver: HTTP em desenvolvimento, HTTPS em produção.
  • 96.
    FAQ • Sender/Receiver:HTTPS. • URL do Receiver: HTTP em desenvolvimento, HTTPS em produção. • Múltiplas conexões ao receiver.
  • 97.
    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.
  • 98.
    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..
  • 99.
    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.
  • 100.
    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 :)
  • 101.
  • 102.
  • 103.
    Futuro • GoogleTV? • Chrome OS: integração no Google Drive na build de desenvolvimento.
  • 104.
    Futuro • GoogleTV? • Chrome OS: integração no Google Drive na build de desenvolvimento. • Conexão fora da mesma rede Wifi.
  • 105.
    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
  • 106.
    Seu App naTV: Desenvolvimento para ChromeCast Obrigado!! Cast your questions :) Ivan de Aguirre ! ivan.aguirre@gmail.com ! Twitter: IvAguirre ! G+: plus.google.com/+IvanAguirreBr