ExoPlayer часто используется для потоковой передачи медиаконтента через интернет. Для выполнения сетевых запросов он поддерживает несколько сетевых стеков. Выбор сетевого стека может значительно повлиять на производительность потоковой передачи.
На этой странице рассказывается, как настроить ExoPlayer для использования выбранного сетевого стека, перечислены доступные варианты, приведены рекомендации по выбору сетевого стека для приложения и объясняется, как включить кеширование для потокового мультимедиа.
Как настроить ExoPlayer для использования определенного сетевого стека
ExoPlayer загружает данные через компоненты DataSource, которые он получает из экземпляров DataSource.Factory, внедренных из кода приложения.
Если вашему приложению нужно только воспроизводить контент по протоколу HTTP(S), то для выбора сетевого стека достаточно обновить все экземпляры DataSource.Factory, которые внедряет приложение, чтобы они стали экземплярами HttpDataSource.Factory, соответствующими нужному сетевому стеку. Если приложению также нужно воспроизводить контент, не относящийся к протоколу HTTP(S), например локальные файлы, используйте DefaultDataSource.Factory:
Kotlin
DefaultDataSource.Factory( context, /* baseDataSourceFactory= */ PreferredHttpDataSource.Factory(context), )
Java
new DefaultDataSource.Factory( context, /* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(context));
В этом примере PreferredHttpDataSource.Factory – это фабрика, соответствующая предпочитаемому сетевому стеку. Уровень DefaultDataSource.Factory добавляет поддержку источников, не использующих протокол HTTP(S), например локальных файлов.
В примере ниже показано, как создать ExoPlayer, который будет использовать сетевой стек Cronet и поддерживать воспроизведение контента, передаваемого не по протоколу HTTP(S).
Kotlin
// Given a CronetEngine and Executor, build a CronetDataSource.Factory. val cronetDataSourceFactory = CronetDataSource.Factory(cronetEngine, executor) // Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds // in support for requesting data from other sources (such as files, resources, // etc). val dataSourceFactory = DefaultDataSource.Factory(context, /* baseDataSourceFactory= */ cronetDataSourceFactory) // Inject the DefaultDataSource.Factory when creating the player. val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
Java
// Given a CronetEngine and Executor, build a CronetDataSource.Factory. CronetDataSource.Factory cronetDataSourceFactory = new CronetDataSource.Factory(cronetEngine, executor); // Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds // in support for requesting data from other sources (such as files, resources, // etc). DefaultDataSource.Factory dataSourceFactory = new DefaultDataSource.Factory( context, /* baseDataSourceFactory= */ cronetDataSourceFactory); // Inject the DefaultDataSource.Factory when creating the player. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
Поддерживаемые сетевые стеки
ExoPlayer напрямую поддерживает HttpEngine, Cronet, OkHttp и встроенный сетевой стек Android по умолчанию. ExoPlayer также можно расширить для поддержки любого другого сетевого стека, работающего на Android.
HttpEngine
HttpEngine – это рекомендуемый сетевой стек по умолчанию для Android, начиная с API 34 (или S extensions 7). В большинстве случаев он использует сетевой стек Cronet, поддерживающий протоколы HTTP, HTTP/2 и HTTP/3 через QUIC.
ExoPlayer поддерживает HttpEngine с помощью HttpEngineDataSource.Factory. Вы можете внедрить эту фабрику источников данных, как описано в разделе Настройка ExoPlayer для использования определенного сетевого стека.
Cronet
Cronet – это сетевой стек Chromium, доступный приложениям Android в виде библиотеки. В Cronet используется несколько технологий, которые снижают задержку и увеличивают пропускную способность сетевых запросов, необходимых для работы приложения, в том числе отправляемых ExoPlayer. Он поддерживает протоколы HTTP, HTTP/2 и HTTP/3 через QUIC. Cronet используется в некоторых крупнейших потоковых сервисах, включая YouTube.
ExoPlayer поддерживает Cronet через библиотеку Cronet.
Подробные инструкции по использованию библиотеки приведены в README.md. Обратите внимание, что библиотека Cronet может использовать три базовые реализации Cronet:
- Сервисы Google Play. Мы рекомендуем использовать эту реализацию в большинстве случаев, а если сервисы Google Play недоступны, переходить на встроенный сетевой стек Android (
DefaultHttpDataSource). - Встроенная библиотека Cronet. Подходит, если значительная часть ваших пользователей находится в регионах, где сервисы Google Play недоступны, или если вы хотите контролировать, какая именно версия Cronet используется. Основной недостаток встроенной библиотеки Cronet заключается в том, что она увеличивает размер приложения примерно на 8 МБ.
- Резервная реализация Cronet. Реализует API Cronet как оболочку для встроенного сетевого стека Android. Не используйте его с ExoPlayer, поскольку в этом случае эффективнее напрямую использовать встроенный сетевой стек Android (с помощью
DefaultHttpDataSource).
OkHttp
OkHttp – ещё один современный сетевой стек, который широко используется во многих популярных приложениях для Android. Поддерживаются протоколы HTTP и HTTP/2, но не HTTP/3 через QUIC.
ExoPlayer поддерживает OkHttp через библиотеку OkHttp.
Подробные инструкции по использованию библиотеки приведены в README.md. При использовании библиотеки OkHttp сетевой стек встраивается в приложение. Это похоже на Cronet Embedded, однако OkHttp значительно меньше и добавляет к приложению менее 1 МБ.
Встроенный сетевой стек Android
ExoPlayer поддерживает использование встроенного сетевого стека Android с DefaultHttpDataSource и DefaultHttpDataSource.Factory, которые являются частью основной библиотеки ExoPlayer.
Точная реализация сетевого стека зависит от программного обеспечения, работающего на базовом устройстве. На большинстве устройств поддерживается только HTTP (то есть HTTP/2 и HTTP/3 через QUIC не поддерживаются).
Другие сетевые стеки
В приложениях также можно интегрировать другие сетевые стеки с ExoPlayer.
Для этого реализуйте HttpDataSource, который упаковывает сетевой стек, вместе с соответствующим HttpDataSource.Factory. Хорошие примеры того, как это сделать, – библиотеки Cronet и OkHttp в ExoPlayer.
При интеграции с сетевым стеком на чистой Java рекомендуется реализовать DataSourceContractTest, чтобы проверить, правильно ли работает ваша реализация HttpDataSource. Хороший пример реализации – OkHttpDataSourceContractTest в библиотеке OkHttp.
Как выбрать сетевой стек
В таблице ниже приведены плюсы и минусы сетевых стеков, поддерживаемых ExoPlayer.
| Сетевой стек | Протоколы | Влияние на размер APK-файла | Примечания |
|---|---|---|---|
| HttpEngine | HTTP HTTP/2 HTTP/3 через QUIC |
Нет | Доступно только для API 34 или S Extensions 7. |
| Cronet (сервисы Google Play) | HTTP HTTP/2 HTTP/3 через QUIC |
Маленькие (<100 КБ) |
Требуются сервисы Google Play. Версия Cronet обновляется автоматически |
| Cronet (встроенная) | HTTP HTTP/2 HTTP/3 через QUIC |
Большой (~8 МБ) |
Версия Cronet, контролируемая разработчиком приложения |
| Cronet (резервный) | HTTP (зависит от устройства) |
Маленькие (<100 КБ) |
Не рекомендуется для ExoPlayer |
| OkHttp | HTTP HTTP/2 |
Маленький (< 1 МБ) |
|
| Встроенный сетевой стек | HTTP (зависит от устройства) |
Нет | Реализация зависит от устройства. |
Протоколы HTTP/2 и HTTP/3 поверх QUIC могут значительно повысить производительность потоковой передачи медиаконтента. В частности, при потоковой передаче адаптивного медиаконтента, распространяемого через сеть доставки контента (CDN), использование этих протоколов может значительно повысить эффективность работы CDN. Поэтому поддержка HTTP/2 и HTTP/3 через QUIC в HttpEngine и Cronet (а также поддержка HTTP/2 в OkHttp) – это значительное преимущество по сравнению со встроенным сетевым стеком Android, если серверы, на которых размещен контент, также поддерживают эти протоколы.
Если вы рассматриваете только потоковую передачу медиаконтента, рекомендуем использовать HttpEngine или Cronet, предоставляемые сервисами Google Play, с возвратом к DefaultHttpDataSource, если сервисы Google Play недоступны. Это позволит использовать HTTP/2 и HTTP/3 через QUIC на большинстве устройств и избежать значительного увеличения размера APK. Из этой рекомендации есть исключения. Если сервисы Google Play недоступны на значительном количестве устройств, на которых будет работать ваше приложение, лучше использовать Cronet Embedded или OkHttp. Использование встроенного сетевого стека может быть приемлемым, если размер APK-файла имеет критическое значение или если потоковая передача медиаконтента является лишь незначительной частью функциональности вашего приложения.
Обычно рекомендуется выбрать один сетевой стек для всех сетевых операций, выполняемых приложением. Это позволяет эффективно объединять ресурсы (например, сокеты) и совместно использовать их в ExoPlayer и других компонентах приложения.
Поскольку вашему приложению, скорее всего, потребуется выполнять сетевые операции, не связанные с воспроизведением медиаконтента, при выборе сетевого стека следует учитывать наши рекомендации по потоковой передаче медиаконтента, требования других компонентов, выполняющих сетевые операции, и их относительную важность для вашего приложения.
Кеширование медиаконтента
ExoPlayer поддерживает кеширование загруженных байтов на диск, чтобы предотвратить повторную загрузку одних и тех же байтов из сети. Это полезно, когда нужно перемотать назад текущий медиафайл или повторить его воспроизведение.
Для кеширования требуется экземпляр SimpleCache, указывающий на выделенный каталог кеша, и CacheDataSource.Factory:
Kotlin
// Note: This should be a singleton in your app. val databaseProvider = StandaloneDatabaseProvider(context) // An on-the-fly cache should evict media when reaching a maximum disk space limit. val cache = SimpleCache(downloadDirectory, LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider) // Configure the DataSource.Factory with the cache and factory for the desired HTTP stack. val cacheDataSourceFactory = CacheDataSource.Factory().setCache(cache).setUpstreamDataSourceFactory(httpDataSourceFactory) // Inject the DefaultDataSource.Factory when creating the player. val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory) ) .build()
Java
// Note: This should be a singleton in your app. DatabaseProvider databaseProvider = new StandaloneDatabaseProvider(context); // An on-the-fly cache should evict media when reaching a maximum disk space limit. Cache cache = new SimpleCache( downloadDirectory, new LeastRecentlyUsedCacheEvictor(maxBytes), databaseProvider); // Configure the DataSource.Factory with the cache and factory for the desired HTTP stack. DataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory() .setCache(cache) .setUpstreamDataSourceFactory(httpDataSourceFactory); // Inject the DefaultDataSource.Factory when creating the player. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory)) .build();