Устранение неполадок


Как устранить ошибку "Незашифрованный HTTP-трафик запрещен"

Эта ошибка возникает, если приложение запрашивает незашифрованный HTTP-трафик (то есть http:// вместо https://), когда это запрещено в конфигурации сетевой безопасности. Если ваше приложение предназначено для Android 9 (уровень API 28) или более поздней версии, то по умолчанию HTTP-трафик в виде открытого текста отключен.

Если вашему приложению необходимо работать с незашифрованным HTTP-трафиком, вам нужно использовать конфигурацию сетевой безопасности, которая это разрешает. Подробную информацию можно найти в документации по сетевой безопасности Android. Чтобы разрешить весь незашифрованный HTTP-трафик, добавьте android:usesCleartextTraffic="true" в элемент application файла AndroidManifest.xml вашего приложения.

Демонстрационное приложение ExoPlayer использует конфигурацию сетевой безопасности по умолчанию, поэтому оно не разрешает незашифрованный HTTP-трафик. Вы можете включить его, следуя инструкциям выше.

Как исправить ошибки SSLHandshakeException, CertPathValidatorException и ERR_CERT_AUTHORITY_INVALID

SSLHandshakeException, CertPathValidatorException и ERR_CERT_AUTHORITY_INVALID указывают на проблему с SSL-сертификатом сервера. Эти ошибки не связаны с ExoPlayer. Подробную информацию можно найти в документации по SSL для Android.

Почему некоторые медиафайлы не поддерживают перемотку?

По умолчанию ExoPlayer не поддерживает перемотку в медиафайлах, для которых единственным способом точной перемотки является сканирование и индексирование всего файла. ExoPlayer считает такие файлы недоступными для поиска. Большинство современных форматов медиаконтейнеров включают метаданные для перемотки (например, индекс выборки), имеют четко определенный алгоритм перемотки (например, интерполированный поиск делением пополам для Ogg) или указывают, что их контент имеет постоянный битрейт. В таких случаях ExoPlayer поддерживает эффективные операции перемотки.

Если вам нужна перемотка, но у вас есть медиафайлы, которые ее не поддерживают, рекомендуем преобразовать контент в более подходящий формат контейнера. Для файлов MP3, ADTS и AMR можно также включить перемотку, если у них постоянный битрейт, как описано здесь.

Почему в некоторых MP3-файлах перемотка работает неточно?

MP3-файлы с переменным битрейтом (VBR) не подходят для случаев, когда требуется точный поиск. Это связано с двумя причинами:

  1. Для точной перемотки контейнер должен обеспечивать точное сопоставление времени и байтов в заголовке. Это позволяет проигрывателю сопоставить запрошенное время перемотки с соответствующим смещением байтов и начать запрашивать, анализировать и воспроизводить медиаконтент с этого смещения. Заголовки, доступные для указания этого сопоставления в MP3 (например, заголовки XING), к сожалению, часто неточны.
  2. Для форматов контейнеров, которые не обеспечивают точное сопоставление времени с байтами (или вообще не обеспечивают сопоставление времени с байтами), по-прежнему можно выполнить точный поиск, если контейнер включает абсолютные временные метки выборки в потоке. В этом случае проигрыватель может сопоставить время поиска с наиболее вероятным смещением байтов, начать запрашивать медиаконтент с этого смещения, проанализировать первую абсолютную временную метку выборки и выполнить двоичный поиск по медиаконтенту, пока не найдет нужную выборку. К сожалению, в MP3-файлах нет абсолютных временных меток, поэтому такой подход невозможен.

Поэтому единственный способ выполнить точный поиск в MP3-файле с переменным битрейтом – это просканировать весь файл и вручную сопоставить время и байты в проигрывателе. Эту стратегию можно включить с помощью FLAG_ENABLE_INDEX_SEEKING, которую можно задать для DefaultExtractorsFactory с помощью setMp3ExtractorFlags. Обратите внимание, что этот формат не подходит для больших MP3-файлов, особенно если пользователь пытается перемотать поток ближе к концу вскоре после начала воспроизведения. В этом случае проигрывателю придется подождать, пока он скачает и проиндексирует весь поток, прежде чем выполнить перемотку. В ExoPlayer мы решили отдать приоритет скорости, а не точности, поэтому FLAG_ENABLE_INDEX_SEEKING по умолчанию отключен.

Если вы можете управлять воспроизводимым медиаконтентом, настоятельно рекомендуем использовать более подходящий формат контейнера, например MP4. Нам неизвестны случаи, когда MP3 был бы лучшим выбором.

Почему перемотка видео работает медленно?

Когда проигрыватель переходит к новой позиции воспроизведения в видео, ему необходимо сделать две вещи:

  1. Загрузите в буфер данные, соответствующие новой позиции воспроизведения (это может не потребоваться, если эти данные уже буферизованы).
  2. Очистить видеодекодер и начать декодирование с I-кадра (ключевого кадра) перед новой позицией воспроизведения. Это необходимо из-за внутрикадрового кодирования, которое используется в большинстве форматов сжатия видео. Чтобы обеспечить точность перемотки (то есть чтобы воспроизведение начиналось точно с выбранной позиции), все кадры между предыдущим I-кадром и выбранной позицией должны быть декодированы и немедленно отброшены (без показа на экране).

Задержку, вызванную первой причиной, можно уменьшить, увеличив объем данных, буферизуемых в памяти проигрывателя, или предварительно кешировав данные на диск.

Задержку, вызванную вторым фактором, можно уменьшить, снизив точность поиска с помощью ExoPlayer.setSeekParameters или перекодировав видео, чтобы увеличить частоту опорных кадров (это приведет к увеличению размера выходного файла).

Почему некоторые файлы MPEG-TS не воспроизводятся?

Некоторые файлы MPEG-TS не содержат разделителей блоков доступа (AUD). По умолчанию ExoPlayer использует AUD для обнаружения границ кадров. Аналогично, некоторые файлы MPEG-TS не содержат ключевых кадров IDR. По умолчанию ExoPlayer учитывает только такие ключевые кадры.

Если ExoPlayer попытается воспроизвести файл MPEG-TS, в котором нет AUD или ключевых кадров IDR, то он зависнет в состоянии буферизации. Если вам нужно воспроизвести такие файлы, вы можете сделать это с помощью FLAG_DETECT_ACCESS_UNITS и FLAG_ALLOW_NON_IDR_KEYFRAMES соответственно. Эти флаги можно установить для DefaultExtractorsFactory с помощью setTsExtractorFlags или для DefaultHlsExtractorFactory с помощью конструктора. Использование FLAG_DETECT_ACCESS_UNITS не имеет побочных эффектов, кроме того, что оно требует больших вычислительных ресурсов по сравнению с обнаружением границ кадров на основе AUD. Использование FLAG_ALLOW_NON_IDR_KEYFRAMES может привести к временному искажению изображения в начале воспроизведения и сразу после перемотки некоторых файлов MPEG-TS.

Почему в некоторых файлах MPEG-TS не удается найти субтитры?

Некоторые файлы MPEG-TS содержат дорожки CEA-608, но не объявляют их в метаданных контейнера, поэтому ExoPlayer не может их обнаружить. Вы можете вручную указать дорожки субтитров, предоставив DefaultExtractorsFactory список ожидаемых форматов субтитров, включая каналы специальных возможностей, которые можно использовать для их идентификации в потоке MPEG-TS:

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory()
    .setTsSubtitleFormats(
      listOf(
        Format.Builder()
          .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
          .setAccessibilityChannel(accessibilityChannel)
          // Set other subtitle format info, such as language.
          .build()
      )
    )
val player: Player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, extractorsFactory)).build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory()
        .setTsSubtitleFormats(
            ImmutableList.of(
                new Format.Builder()
                    .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
                    .setAccessibilityChannel(accessibilityChannel)
                    // Set other subtitle format info, such as language.
                    .build()));
Player player =
    new ExoPlayer.Builder(context, new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

Почему некоторые файлы MP4/FMP4 воспроизводятся неправильно?

Некоторые файлы MP4/FMP4 содержат списки редактирования, которые перезаписывают временную шкалу медиафайла, пропуская, перемещая или повторяя списки образцов. ExoPlayer частично поддерживает применение списков редактирования. Например, он может задерживать или повторять группы фрагментов, начиная с фрагмента синхронизации, но не усекает аудиофрагменты и не выполняет предварительную прокрутку медиаконтента для правок, которые не начинаются с фрагмента синхронизации.

Если вы видите, что часть медиаконтента неожиданно отсутствует или повторяется, попробуйте установить Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS или FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS. В этом случае экстрактор будет полностью игнорировать списки редактирования. Их можно задать в DefaultExtractorsFactory с помощью setMp4ExtractorFlags или setFragmentedMp4ExtractorFlags.

Почему при попытке воспроизвести некоторые трансляции возвращается код ответа HTTP 301 или 302?

Коды HTTP-ответа 301 и 302 указывают на переадресацию. Краткие описания можно найти в Википедии. Когда ExoPlayer отправляет запрос и получает ответ с кодом статуса 301 или 302, он обычно выполняет переадресацию и начинает воспроизведение. Единственный случай, когда это не происходит по умолчанию, – переадресация между протоколами. Межпротокольная переадресация – это переадресация с HTTPS на HTTP или наоборот (реже – между другой парой протоколов). Чтобы проверить, не приводит ли URL к переадресации между протоколами, используйте инструмент командной строки wget:

wget "https://yourserver.example.com/test.mp3" 2>&1  | grep Location

Вывод должен выглядеть примерно так:

Location: https://secondserver.example.net/test.mp3 [following]
Location: http://thirdserver.example.org/test.mp3 [following]

В этом примере есть две переадресации. Первая переадресация выполняется с URL https://yourserver.example.com/test.mp3 на URL https://secondserver.example.net/test.mp3. Оба URL используют протокол HTTPS, поэтому перенаправление не является межпротокольным. Вторая переадресация – с https://secondserver.example.net/test.mp3 на http://thirdserver.example.org/test.mp3. Это переадресация с HTTPS на HTTP, то есть межпротокольная. ExoPlayer не будет следовать этому перенаправлению в своей конфигурации по умолчанию, то есть воспроизведение не удастся.

При необходимости вы можете настроить ExoPlayer так, чтобы он следовал межпротокольным переадресациям при создании экземпляров DefaultHttpDataSource.Factory, используемых в вашем приложении. Подробнее о выборе и настройке сетевого стека…

Почему некоторые потоки завершаются с ошибкой UnrecognizedInputFormatException?

Этот вопрос относится к сбоям воспроизведения следующего вида:

UnrecognizedInputFormatException: None of the available extractors
(MatroskaExtractor, FragmentedMp4Extractor, ...) could read the stream.

Возможны две причины. Чаще всего это происходит, когда вы пытаетесь воспроизвести контент DASH (mpd), HLS (m3u8) или SmoothStreaming (ism, isml), но проигрыватель пытается воспроизвести его как прогрессивный поток. Чтобы воспроизводить такие потоки, необходимо использовать соответствующий модуль ExoPlayer. Если URI потока не заканчивается стандартным расширением имени файла, вы также можете передать MimeTypes.APPLICATION_MPD, MimeTypes.APPLICATION_M3U8 или MimeTypes.APPLICATION_SS в setMimeType объекта MediaItem.Builder, чтобы явно указать тип потока.

Вторая, менее распространенная причина заключается в том, что ExoPlayer не поддерживает формат контейнера медиафайла, который вы пытаетесь воспроизвести. В этом случае сбой является ожидаемым поведением. Однако вы можете отправить запрос на добавление функции в нашей системе отслеживания ошибок, указав сведения о формате контейнера и тестовом потоке. Прежде чем отправлять запрос на добавление функции, воспользуйтесь поиском, чтобы узнать, не предлагал ли кто-то эту функцию раньше.

Почему метод setPlaybackParameters работает некорректно на некоторых устройствах?

При запуске отладочной сборки приложения на Android M и более ранних версиях вы можете столкнуться с прерывистым воспроизведением, слышимыми артефактами и высокой загрузкой ЦП при использовании API setPlaybackParameters. Это связано с тем, что оптимизация, важная для этого API, отключена для отладочных сборок, работающих на этих версиях Android.

Важно отметить, что эта проблема затрагивает только отладочные сборки. Это не влияет на конечные сборки, для которых оптимизация всегда включена. Поэтому выпуски, которые вы предоставляете конечным пользователям, не должны быть затронуты этой проблемой.

Что означает ошибка "Доступ к проигрывателю получен в неправильном потоке"?

Ознакомьтесь с разделом Примечание о потоках на странице "Начало работы".

Как устранить ошибку "Unexpected status line: ICY 200 OK"?

Это может произойти, если ответ сервера содержит строку статуса ICY, а не строку, соответствующую HTTP. Строки статуса ICY устарели и не должны использоваться. Если вы управляете сервером, обновите его, чтобы он предоставлял ответ, соответствующий HTTP. Если это невозможно, используйте библиотеку ExoPlayer OkHttp, которая корректно обрабатывает строки состояния ICY.

Как узнать, является ли воспроизводимый поток прямой трансляцией?

Вы можете запросить метод isCurrentWindowLive проигрывателя. Кроме того, вы можете проверить isCurrentWindowDynamic, чтобы узнать, является ли окно динамическим (то есть обновляется ли оно со временем).

Как сделать так, чтобы аудио продолжало воспроизводиться, когда приложение работает в фоновом режиме?

Чтобы аудио продолжало воспроизводиться, когда приложение работает в фоновом режиме, выполните следующие действия:

  1. У вас должна быть запущена активная служба. Это предотвращает завершение процесса системой для освобождения ресурсов.
  2. Вам нужно иметь WifiLock и WakeLock. Они гарантируют, что система не отключит Wi-Fi и процессор. Это легко сделать, если вы используете ExoPlayer, вызвав setWakeMode. В этом случае нужные блокировки будут автоматически устанавливаться и сниматься в нужное время.

Важно снять блокировки (если вы не используете setWakeMode) и остановить сервис, как только воспроизведение аудио прекратится.

Почему ExoPlayer поддерживает мой контент, а библиотека ExoPlayer Cast – нет?

Возможно, контент, который вы пытаетесь воспроизвести, не поддерживает CORS. Для воспроизведения контента в Cast framework требуется поддержка CORS.

Почему контент не воспроизводится, но ошибка не появляется?

Возможно, устройство, на котором вы воспроизводите контент, не поддерживает определенный формат медиафайлов. Чтобы убедиться в этом, добавьте в проигрыватель слушатель EventLogger и найдите в Logcat строку, похожую на следующую:

[ ] Track:x, id=x, mimeType=mime/type, ... , supported=NO_UNSUPPORTED_TYPE

NO_UNSUPPORTED_TYPE означает, что устройство не может декодировать формат медиафайла, указанный в mimeType. Информацию о поддерживаемых форматах можно найти в документации по медиаформатам Android. Как загрузить библиотеку декодирования и использовать ее для воспроизведения?

Как загрузить библиотеку декодирования и использовать ее для воспроизведения?

  • В большинстве библиотек декодеров есть инструкции по проверке и созданию зависимостей. Убедитесь, что вы выполнили все шаги, описанные в файле README для нужной библиотеки. Например, для библиотеки ExoPlayer FFmpeg необходимо следовать инструкциям в файле libraries/decoder_ffmpeg/README.md, в том числе передавать флаги конфигурации, чтобы включить декодеры для всех форматов, которые вы хотите воспроизводить.
  • Если в библиотеках есть нативный код, убедитесь, что вы используете правильную версию Android NDK, указанную в файле README, и следите за ошибками, которые появляются во время настройки и сборки. После выполнения инструкций из файла README в подкаталоге libs пути к библиотеке для каждой поддерживаемой архитектуры должны появиться файлы .so.
  • Чтобы попробовать воспроизведение с помощью библиотеки в демонстрационном приложении, ознакомьтесь с разделом Включение встроенных декодеров. Инструкции по использованию библиотеки в вашем приложении приведены в файле README.
  • Если вы используете DefaultRenderersFactory, то при загрузке декодера в Logcat должна появиться строка журнала уровня info, например "Loaded FfmpegAudioRenderer". Если его нет, убедитесь, что приложение зависит от библиотеки декодирования.
  • Если в Logcat вы видите предупреждения от LibraryLoader, это означает, что не удалось загрузить нативный компонент библиотеки. В этом случае убедитесь, что вы правильно выполнили инструкции из файла README библиотеки и в процессе не возникло ошибок.

Если у вас по-прежнему возникают проблемы с использованием библиотек декодирования, проверьте систему отслеживания ошибок Media3 на наличие актуальных проблем. Если вы хотите сообщить о новой проблеме, связанной с созданием нативной части библиотеки, включите в сообщение полный вывод командной строки, полученный при выполнении инструкций из файла README. Это поможет нам диагностировать проблему.

Можно ли воспроизводить видео YouTube напрямую с помощью ExoPlayer?

Нет, ExoPlayer не может воспроизводить видео с YouTube, например URL в формате https://www.youtube.com/watch?v=.... Вместо этого используйте YouTube IFrame Player API – официальный способ воспроизведения видео YouTube на устройствах Android.

Видео воспроизводится с задержками

Устройство может не успевать декодировать контент, например если битрейт или разрешение контента превышают возможности устройства. Чтобы обеспечить хорошую производительность на таких устройствах, вам может потребоваться использовать контент более низкого качества.

Если видео воспроизводится с перебоями на устройстве с Android 6.0 (уровень API 23) – Android 11 (уровень API 30), особенно при воспроизведении контента с технологиями DRM или высокой частотой кадров, попробуйте включить асинхронную очередь буфера.

Ошибки линтинга нестабильного API

Media3 гарантирует двоичную совместимость для подмножества API. Части, которые не гарантируют двоичную совместимость, отмечены значком @UnstableApi. Чтобы сделать это различие очевидным, использование нестабильных символов API генерирует ошибку lint, если они не аннотированы с помощью @OptIn.

Аннотация @UnstableApi не говорит о качестве или эффективности API, а лишь указывает на то, что он не является стабильным.

У вас есть два варианта действий:

  • Перейдите на стабильный API, который позволяет достичь того же результата.
  • Продолжить использовать нестабильный API и добавить аннотацию @OptIn, как показано ниже.
Добавить аннотацию "@OptIn"

Android Studio может помочь вам добавить аннотацию:

Скриншот: как добавить аннотацию Optin
Рисунок 2. Добавление аннотации @androidx.annotations.OptIn в Android Studio.

Вы также можете вручную добавить аннотации для определенных сайтов:

Kotlin

import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi

@OptIn(UnstableApi::class)
fun functionUsingUnstableApi() { ... }

Java

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

@OptIn(markerClass = UnstableApi.class)
private void methodUsingUnstableApis() { ... }

Чтобы включить целые пакеты, добавьте файл package-info:

Kotlin

// In your package-info.kt
@OptIn(UnstableApi::class)
package name.of.your.package

import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi

Java

// In your package-info.java
@OptIn(markerClass = UnstableApi.class)
package name.of.your.package;

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

Чтобы включить проверку для всего проекта, подавите определенную ошибку в файле lint.xml:

 <?xml version="1.0" encoding="utf-8"?>
 <lint>
   <issue id="UnsafeOptInUsageError">
     <option name="opt-in" value="androidx.media3.common.util.UnstableApi" />
   </issue>
 </lint>

Также есть аннотация kotlin.OptIn, которую не следует использовать. Важно использовать аннотацию androidx.annotation.OptIn.

Как протестировать или изолировать проблемы, вызванные новыми оптимизациями или функциями?

В Media3 есть инфраструктура флагов функций, в которой новые функции и оптимизации по умолчанию включены в режиме Canary.

Если вы подозреваете, что проблема или регрессия в приложении связана с новой функцией или оптимизацией, вы можете временно отключить режим Canary:

Flags.setCanaryModeEnabled(false);

или отключите отдельные флаги функций с помощью Flags.disableFlag(flagId). Подробную информацию о настройке режима Canary, переопределении флагов и настройке удаленных аварийных выключателей можно найти в руководстве по флагам функций и режиму Canary.