1. Что такое Chromecast и как он работает?
Chromecast это гаджет, который позволяет пользователям передавать медиа контент с мобильных устройств на телевизор.
Устройство стоимостью 35$ было анонсировано 24 Июля 2013 года и названо гаджетом года по версии Time's 2013. В первую очередь это технология передачи медиа контента с телефонов, планшетов или ноутбуков на устройство с большим экраном — например на телевизор.
Сразу после анонса Chromacast SDK был в стадии бета тестирования, что ограничивало его практическое применение. Но 3 февраля 2014 года Google выпустила финальную версию SDK.
Теперь количество приложений, поддерживающих Chromecast стремительно растет, так что мы решили описать наш опыт разработки Chromecast ready приложений для Android. Эта статья раскрывает тему разработки Chromecast ready приложения: ниже вы найдете некоторые заметки, комментарии и рекомендации для разработчиков также как и примеры и исходные коды нашего приложения.
Как это работает?
Chromecast устройство исполняет специальную версию броузера Chrome, который может отображать специальные Web приложения (называемые “Receiver” приложениями). Непосредственная цель этих приложений — это показ медиа контента конечным пользователям. Но Chromecast устройство не имеет никаких прямых рычагов управления, так-что очевидно должны существовать некоторые внешние элементы управления, и как было сказано ранее, это может быть или мобильное приложение, или Web приложение (которые называют “Sender” приложением).
Таким образом с помощью Sender приложений мы можем управлять Chromecast устройством. Но как именно это происходит?
Это можно сделать через общую WiFi сеть — т.е. и Sender и Receiver приложения должны быть подключены к единой WiFi сети. Этот канал двунаправленный, так что “Sender” приложение например может выдавать команды на проигрывание медиа контента, в то время как “Receiver” приложение будет оповещать “Sender” приложение о статусе проигрывания и позиции.
2. Разработка приложения
В общем, разработка приложений для Chromecast включает разработку Sender и Receiver приложений. Как было сказано ранее, Sender приложением может быть мобильное или Web приложение, хотя это и не ограничение. Receiver приложение — это специальное Web приложение, которое может быть запущено на Chromecast устройстве.
В этой статье мы опишем разработку простого Chromecast приложения, которое позволит обмен сообщениями между sender и receiver частями. Приложение, которое мы разработали может быть использовано как шаблон для дальнейших разработок.
Но прежде чем мы начнем, нам нужно настроить Chromecast устройство и зарегистрировать наше приложение.
2.1. Настройка Chromecast устройства
Процедура описана здесь.
2.2. Регистрация приложения
Вам нужно зарегистрировать ваше приложение для того чтоб получить ID приложения (который будет использоваться и в “sender” и в “receiver” приложениях).
Процедура регистрации описана здесь.
2.3. Разработка “Sender” приложения
Сейчас мы обсудим процесс разработки “Sender” приложения. Хотя для того чтоб быть функциональным, “Sender” приложение требует работающего “Receiver” приложения. Так что мы сможем проверить результаты только после разработки “Receiver” приложения.
Также разработка “Sender” приложения для Android описана здесь.
В нашем случае, “Sender” приложение — это Android приложение которое использует набор технологий и библиотек для связи с “Receiver” приложением.
2.3.1. Пререквизиты:
- IDE для разработки Android приложений (например Eclipse)
- Android SDK
- Google Play Services SDK версии 15 или выше
- appcompat и media router libraries из пакета Android Support Library
2.3.2. Создание скелетона
Создайте пустое Android приложение и добавьте следующие библиотеки и зависимости:
- android-sdk\extras\android\support\v7\appcompat
- android-sdk\extras\android\support\v7\mediarouter
- android-sdk\extras\google\google_play_services\libproject\google-play-services_lib
Укажите минимальную версию Android, разрешения и другие необходимые данные:
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
</application>
Теперь создайте активити расширяющее ActionBarActivity.
2.3.3. Реализация
Есть несколько ключевых состояний в работе приложения:
- Обнаружение устройства
- Работа с сессией
- Обмен сообщениями между Sender и Receiver приложениями
Давайте опишем каждое состояние более детально:
1) Обнаружение устройства:
Обнаружение может быть запущено нажатием на кнопку “Cast”. Есть несколько способов показать эту кнопку, но мы будем использовать MediaRouter ActionBar провайдер.
Добавьте следующее в ваше меню:
<item
android:id="@+id/media_route_menu_item"
android:title="Route"
app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
app:showAsAction="always"/>
Следующие поля вовлечены в процесс:
private final MediaRouter.Callback mediaRouterCallback = new MediaRouter.Callback()
{
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route)
{
CastDevice device = CastDevice.getFromBundle(route.getExtras());
//setSelectedDevice(device);
}
@Override
public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route)
{
//setSelectedDevice(null);
}
};
private MediaRouter mediaRouter;
private MediaRouteSelector mediaRouteSelector;
Инициализируйте mediaRouter и mediaRouteSelector в методе onCreate:
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mediaRouter = MediaRouter.getInstance(getApplicationContext());
mediaRouteSelector = new MediaRouteSelector.Builder().addControlCategory(CastMediaControlIntent.categoryForCast(APP_ID)).build();
}
Сконфигурируйте action провайдер:
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.main, menu);
MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
MediaRouteActionProvider mediaRouteActionProvider = (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
mediaRouteActionProvider.setRouteSelector(mediaRouteSelector);
return true;
}
и зарегистрируйте callback в методах onStart и onStop:
@Override
protected void onStart()
{
super.onStart();
mediaRouter.addCallback(mediaRouteSelector, mediaRouterCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
}
@Override
protected void onStop()
{
//setSelectedDevice(null);
mediaRouter.removeCallback(mediaRouterCallback);
super.onStop();
}
APP_ID это строка с идентификатором приложения полученным при регистрации приложения в части 2.2
Теперь подключите Android устройства к общей с Chromecast устройством WiFi сети и запустите приложение (обратите внимание что receiver приложение должно уже быть доступным. Оно очень простое в нашем случае так что мы можем взять его из приведенное исходных кодов. Мы вернемся к этому позже).
Если все условия соблюдены, то иконка трансляции появится в верхнем правом угле. Нажатие на нее откроет со списком доступных устройств, позволяя тем самым подключится к одному из них. После подключения иконка станет голубой, оповещая что подключение установлено. Нажатие на нее еще раз откроет другой диалог, который позволит изменить громкость на Chromecast устройстве или отключится от него:
2) Управление сессией:
Для управления сессией мы добавим несколько полей и методов:
@Override
protected void onStart()
{
super.onStart();
mediaRouter.addCallback(mediaRouteSelector, mediaRouterCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
}
@Override
protected void onStop()
{
//setSelectedDevice(null);
mediaRouter.removeCallback(mediaRouterCallback);
super.onStop();
}
и несколько listeners:
private CastDevice selectedDevice;
private GoogleApiClient apiClient;
private boolean applicationStarted;
private void setSelectedDevice(CastDevice device)
{
Log.d(TAG, "setSelectedDevice: " + device);
selectedDevice = device;
if (selectedDevice != null)
{
try
{
stopApplication();
disconnectApiClient();
connectApiClient();
}
catch (IllegalStateException e)
{
Log.w(TAG, "Exception while connecting API client", e);
disconnectApiClient();
}
}
else
{
if (apiClient != null)
{
disconnectApiClient();
}
mediaRouter.selectRoute(mediaRouter.getDefaultRoute());
}
}
private void connectApiClient()
{
Cast.CastOptions apiOptions = Cast.CastOptions.builder(selectedDevice, castClientListener).build();
apiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptions)
.addConnectionCallbacks(connectionCallback)
.addOnConnectionFailedListener(connectionFailedListener)
.build();
apiClient.connect();
}
private void disconnectApiClient()
{
if (apiClient != null)
{
apiClient.disconnect();
apiClient = null;
}
}
private void stopApplication()
{
if (apiClient == null) return;
if (applicationStarted)
{
Cast.CastApi.stopApplication(apiClient);
applicationStarted = false;
}
}
Теперь если вы подключитесь к Chromecast, “Receiver” приложение будет активированно и вы увидите некоторую отладочную информацию. Давайте раскроем тему обмена сообщениями.
3) Обмен сообщениями между Sender и Receiver приложениями.
Исходящие сообщения посылаются в помощью соедующего метода:
private void sendMessage(String message)
{
if (apiClient != null)
{
try
{
Cast.CastApi.sendMessage(apiClient, NAMESPACE, message)
.setResultCallback(new ResultCallback<Status>()
{
@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);
}
}
}
В нашем случае NAMESPACE это строка «urn:x-cast:com.ls.cast.sample». Она используется для того чтоб отделить наш канал от других. Такая же конатснта должна быть использована в Receiver приложении.
Для обработки входящих сообщений нам нужно зарегистрировать Cast.MessageReceivedCallback как получатель сообщений. Мы сделает это в connectionResultCallback:
@Override
public void onResult(Cast.ApplicationConnectionResult result)
{
Status status = result.getStatus();
if (status.isSuccess())
{
applicationStarted = true;
try
{
Cast.CastApi.setMessageReceivedCallbacks(apiClient, NAMESPACE, incomingMsgHandler);
}
catch (IOException e)
{
Log.e(TAG, "Exception while creating channel", e);
}
}
}
public final Cast.MessageReceivedCallback incomingMsgHandler = new Cast.MessageReceivedCallback()
{
@Override
public void onMessageReceived(CastDevice castDevice, String namespace, String message)
{
}
};
И последнее что можно сделать — это обменятся сообщениями. В нашем демонстрационном приложении мы создали простой лейаут который позволяет показывать входящие сообщения. Вы можете ознакомится в полными исходными кодами в прикрепленном файле.
2.4. Разработка “Receiver” приложения
“Receiver” приложение — это специальное Web приложение, способное работать в Chromecast устройстве.
Конечная цель приложения — показывать пользователю полезную информацию и позволить ему взаимодействовать с этой информацией через “Sender” приложение.
Первая задача отображения информации есть общая для Web приложений и может быть очень разной: это может быть медиа приложение типа YouTube или что-то наподобие медиа-плеера, или же это может быть некоторое тяжеловесное UI приложение наподобии Google Maps с дополнительными слоями, либо это может быть простая HTML страница, отображающая некоторую статистику.
Насчет второй задачи взаимодействия между Receiver и Sender приложением — Chromecast SDK предоставляет для этого JavaScript библиотеку. Это позволит обмениваться строковыми сообщениями в обе стороны. Давайте раскроем эту тему.
Прежде всего имейте в виду что приложение должно быть зарегистрировано как описано в пункте 2.2. При регистрации нужно предоставить URL. Этот URL будет использован для хостинга вашего Receiver приложения. Разработка Receiver приложения состоит из таких этапов:
1) разработка приложения локально;
2) размещение его на указанном URL;
3) активация и отладка приложения через специальный URL. Для активации приложения просто запустите Sender приложение и подключитесь к устройству. Для отладки “Receiver” приложения откройте следующий линк на вашем PC: http://CHROMECAST_IP:9222, где CHROMECAST_IP это IP адрес Chromecast устройства. Для того чтоб получить IP адрес устройства, запустите утилиту настройки Chromecast для PC или для мобильных устройств. Обратите внимание что устройства используемое для конфигурации должно иметь прямое WiFi соединение с Chromecast устройством (таким образом ваш PC должен иметь WiFi адаптер если вы выберете его для конфигурации):
Также обратите внимание, что опция “Send this Chromecast’s serial number when checking for updates” должна быть включенной чтоб позволить отладку. После открытия броузером найденного адреса вы увидите следующее:
Подсвеченный линк относится к исполняющемуся приложению.Если вы нажмете на нем, будет открыта новая страница с Chrome Developer tools. Не плутайте их с локальными инструментами разработчика — на самом деле они удаленные и относятся к вашему Chromecast устройству:
Теперь мы наконец готовы к разработке приложения.
Прежде всего включите скрипт поддержки Chromecast:
<script src="https://www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js">
После этого нам нужен cast.receiver.CastReceiverManager инстанс, подключить onSenderConnected, onSenderDisconnected и onMessage листенеры и запустить обмен сообщениями. Мы можем сделать это в onLoad листенере:
window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
window.castReceiverManager.onSenderConnected = onChannelOpened;
window.castReceiverManager.onSenderDisconnected = onChannelClosed;
window.customMessageBus = window.castReceiverManager.getCastMessageBus(namespace);
window.customMessageBus.onMessage = onMessage;
window.castReceiverManager.start();
Для обработки входящих сообщений может быть использован следующий метод:
function onMessage(event)
{
var message = event.data;
var senderId = event.senderId;
log("message from: " + senderId + " message: " + message);
}
Для отсылки исходящий сообщений будет использован следующий метод:
function broadcast(message)
{
window.customMessageBus.broadcast(message);
}
Обратите внимание что несколько Sender приложений могут подключится к одному Receiver приложению одновременно. Поэтому мы используем метод broadcast для отсылки сообщения всем подключенным sender приложениям.
В нашем демонстрационном приложении мы отсылаем сообщения по таймеру для целей демонстрации.
И еще несколько замечаний. Цвет текста по умолчанию черный, так что его нужно переопределить через CSS для того чтоб что-нибудь увидеть. Также обратите внимание что разрешение экрана в Chromecast устройстве 1280x720 пикселей.
Фуух. Мы создали кастомное Sender и Receiver приложения способные обмениватся сообщениями в обе стороны. Этого должно быть достаточно для понимания и использования всей мощи Chromecast устройства.
Полный исходный код можно скачать здесь.
P.S. На самом деле я также являюсь и автором статьи, так что могу ответить на технические вопросы.