Как уменьшить задержку запуска iOS AVPlayer


115

Обратите внимание на вопрос ниже: все ресурсы локальны на устройстве - потоковая передача по сети не выполняется. Видео содержат звуковые дорожки.

Я работаю над приложением iOS, которое требует воспроизведения видеофайлов с минимальной задержкой для запуска рассматриваемого видеоклипа. К сожалению, мы не знаем, какой именно видеоклип будет следующим, до тех пор, пока нам действительно не понадобится его запустить. В частности: когда воспроизводится один видеоклип, мы будем знать, каков будет следующий набор (примерно) из 10 видеоклипов, но мы не знаем, какой именно, пока не наступит время «немедленно» воспроизвести следующий клип.

Что я сделал, чтобы посмотреть на фактические задержки запуска, так это вызвал addBoundaryTimeObserverForTimesвидеоплеер с периодом времени в одну миллисекунду, чтобы увидеть, когда видео действительно начало воспроизводиться, и я беру разницу этой отметки времени с первым местом в код, который указывает, какой актив начать воспроизведение.

Из того, что я видел до сих пор, я обнаружил, что использование комбинации AVAssetзагрузки, а затем создание AVPlayerItemиз нее, когда она будет готова, а затем ожидание, AVPlayerStatusReadyToPlayпрежде чем я вызову play, обычно занимает от 1 до 3 секунд, чтобы запустить клип.

С тех пор я переключился на то, что, по моему мнению, примерно равноценно: звонить [AVPlayerItem playerItemWithURL:]и ждать начала AVPlayerItemStatusReadyToPlayигры. Примерно такая же производительность.

Одна вещь, которую я наблюдаю, - это то, что загрузка первого элемента AVPlayer происходит медленнее, чем остальных. Кажется, одна из идей - предварительно запустить AVPlayer с коротким / пустым активом, прежде чем пытаться воспроизвести первое видео, может быть хорошей общей практикой. [ Медленный запуск AVAudioPlayer при первом воспроизведении звука

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

Обновление: приведенная ниже идея 7 в том виде, в котором она реализована, дает время переключения около 500 мс. Это улучшение, но было бы неплохо сделать это еще быстрее.

Идея 1: использовать N AVPlayers (не сработает)

Используем ~ 10 AVPPlayerобъектов и запускаем и приостанавливаем все ~ 10 клипов, и как только мы знаем, какой из них нам действительно нужен, переключаемся на правильный AVPlayer, снимаем паузу и начинаем все заново для следующего цикла.

Я не думаю, что это работает, потому что я читал, что AVPlayer'sв iOS есть примерно 4 активных . Кто-то спрашивал об этом здесь в StackOverflow и узнал об ограничении в 4 AVPlayer: быстрое переключение между видео-использованием-avfoundation

Идея 2: использовать AVQueuePlayer (не сработает)

Я не верю, что добавление 10 AVPlayerItemsв одну AVQueuePlayerзаранее загрузит их все для плавного старта. AVQueuePlayerэто очередь, и я думаю, что она действительно только делает следующее видео в очереди готовым для немедленного воспроизведения. Я не знаю, какое из ~ 10 видео мы хотим воспроизвести, пока не пришло время начать это. ИОС-AVPlayer-видео-поджимать

Идея 3: загрузка, воспроизведение и сохранение AVPlayerItemsв фоновом режиме (еще не уверен на 100%, но не очень хорошо)

Я смотрю, есть ли какие-либо преимущества для загрузки и воспроизведения первой секунды каждого видеоклипа в фоновом режиме (подавление вывода видео и звука) и сохранения ссылки на каждый из них AVPlayerItem, и когда мы знаем, какой элемент необходимо воспроизвести для real, поменяйте местами этот и поменяйте местами фоновый AVPlayer с активным. Промыть и повторить.

Теоретически можно было бы предположить, что недавно воспроизведенные AVPlayer/AVPlayerItemфайлы могут содержать некоторые подготовленные ресурсы, которые ускорят последующее воспроизведение. До сих пор я не видел преимуществ от этого, но, возможно, у меня нет AVPlayerLayerправильной настройки для фона. Я сомневаюсь, что это действительно улучшит ситуацию по сравнению с тем, что я видел.

Идея 4. Используйте другой формат файла - может быть, тот, который загружается быстрее?

В настоящее время я использую формат H.264 .m4v (видео-MPEG4). H.264 имеет множество различных вариантов кодеков, поэтому возможно, что некоторые параметры искать быстрее, чем другие. Я обнаружил, что использование более продвинутых настроек, которые уменьшают размер файла, увеличивают время поиска, но я не нашел никаких других вариантов.

Идея 5: Комбинация формата видео без потерь + AVQueuePlayer

Если есть видеоформат, который быстро загружается, но, возможно, размер файла безумный, одна из идей может заключаться в том, чтобы предварительно подготовить первые 10 секунд каждого видеоклипа с версией, которая раздута, но быстрее загружается, но возвращается это с активом, закодированным в H.264. Используйте AVQueuePlayer и добавьте первые 10 секунд в несжатый формат файла, а затем добавьте в H.264, который занимает до 10 секунд времени подготовки / предварительной загрузки. Так что я бы получил «лучшее» из обоих миров: быстрое время запуска, но также преимущества более компактного формата.

Идея 6: использовать нестандартный AVPlayer / написать свой / использовать чужой

Учитывая мои потребности, возможно, я не могу использовать AVPlayer, но должен прибегнуть к AVAssetReader и декодировать первые несколько секунд (возможно, записать необработанный файл на диск), а когда дело доходит до воспроизведения, использовать необработанный формат для его воспроизведения назад быстро. Мне кажется, что это огромный проект, и если я подойду к нему наивно, то будет неясно / вряд ли получится лучше. Размер каждого декодированного и несжатого видеокадра составляет 2,25 МБ. Наивно говоря - если мы выберем для видео ~ 30 кадров в секунду, я получу требование на чтение с диска ~ 60 МБ / с, что, вероятно, невозможно. Очевидно, нам нужно будет выполнить некоторый уровень сжатия изображений (возможно, собственные форматы сжатия openGL / es через PVRTC) ... но это безумие. Может быть, есть библиотека, которую я могу использовать?

Идея 7. Объедините все в один актив фильма и выполните seekToTime.

Одна идея, которая может быть проще, чем некоторые из вышеперечисленных, - это объединить все в один фильм и использовать seekToTime. Дело в том, что мы бы прыгали повсюду. По сути произвольный доступ к фильму. Я думаю, что это действительно может сработать: avplayer-movie-playing-lag-in-ios5

Как вы думаете, какой подход будет лучшим? Пока что я не добился больших успехов в сокращении задержки.


Как бы то ни было, я выбираю Idea 7. Она все еще медленная, но не такая непредсказуемо медленная, как другие варианты. Следующий вопрос: влияют ли параметры кодека, разрешение и частота ключевых кадров на время поиска?
Bernt Habermeier

Поздно к игре, но, возможно, стоит переключить видео как можно быстрее (т. Е. Сразу после начала воспроизведения нового) и профилировать приложение, чтобы увидеть, где оно тратит большую часть своего процессорного времени.
тк.

1
Что вы узнали об этом почти год спустя?
lnafziger

1
Я выбрал вариант 7 и добрался до места между 300 и 500 мс поисков. Я обнаружил, что чем красивее параметры кодека mp4, тем медленнее seekTo. Есть несколько параметров сжатия видео, которые позволяют улучшить сжатие и сохранить качество видео, но сокращают время декодирования.
Bernt Habermeier

2
Это разумно звучащий запрос, но на самом деле, чтобы реализовать вариант 7, в реализации слишком много аспектов для публикации. Подумайте: (a) Создайте цепочку инструментов для объединения видеоресурсов, (b) убедитесь, что вы отслеживаете смещения сегментов видео, (c) смещения поиска, когда приходит запрос на воспроизведение определенного клипа, (d) используйте addPeriodicTimeObserverForInterval для проверки если вы запустили видеоклип и отреагировали соответствующим образом (используйте этот метод вместо одноразового addBoundaryTimeObserverForTimes, потому что я обнаружил, что последний иногда не запускается ... в целом, это не поддается вставке кода.
Бернт Хабермайер,

Ответы:


4

Для iOS 10.x и выше, чтобы уменьшить задержку запуска AVPlayer, я установил: avplayer.automaticallyWaitsToMinimizeStalling = false; и это, похоже, исправило это для меня. Это могло иметь другие последствия, но я еще не столкнулся с ними.

Я получил идею от: https://stackoverflow.com/a/50598525/9620547


Получилось сокращение задержки на 6-7 секунд. Но у меня вопрос, повлияет ли это на производительность приложения?
кальпа

@kalpa Мы использовали этот код в производственном приложении более года без каких-либо заметных отрицательных эффектов. В основном мы проигрываем 20-60-минутные аудиофайлы слушателям в США, где мобильная передача данных обычно является быстрой. Однако ваш вариант использования может быть другим.
grizzb

Спасибо за информацию. @grizzb
калпа

1

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


1

Вы должны сначала попробовать вариант №7, просто чтобы увидеть, сможете ли вы заставить его работать. Я подозреваю, что на самом деле это не сработает для ваших нужд, поскольку время поиска, вероятно, будет недостаточно быстрым, чтобы обеспечить плавное переключение между клипами. Если вы попробуете это, и это не удастся, я бы посоветовал вам выбрать вариант 4/6 и взглянуть на мою библиотеку iOS, разработанную специально для этой цели, просто выполните быстрый поиск в Google на AVAnimator, чтобы узнать больше. Моя библиотека позволяет реализовать бесшовные циклы и переключаться с одного клипа на другой, это очень быстро, потому что видео нужно предварительно декодировать в файл. В вашем случае все 10 видеоклипов будут декодированы в файлы до того, как вы начнете, но тогда переключение между ними будет быстрым.


А как насчет звуковой части видео? Мне нужно синхронизировать видео и аудио.
Bernt Habermeier 06

Да, звук уже обрабатывается с очень плотной синхронизацией между звуковой дорожкой и видеоклипом. См. Примеры проектов xcode. Это уже все реализовано, вам просто нужно скачать и опробовать.
MoDJ 06

Есть ли возможность проигрывать сетевое видео с помощью AVAnimator?
Ричард Топчи

Нет, он работает с локальными файлами, потоковое сетевое видео - совсем другое дело.
MoDJ 07

0

Не делая ничего подобного в прошлом, основываясь на ваших мыслях и опыте, я бы попробовал комбинацию 7 и 1: Предварительно загрузите один AVPlayer с первыми парой секунд из 10 последующих видео. Тогда пропуск будет быстрее и надежнее из-за меньшего количества данных. Пока вы играете выбранный фрагмент, у вас есть достаточно времени, чтобы подготовить AVPlayer для оставшейся части выбранного последующего видео в фоновом режиме. Когда начало закончено, вы переключаетесь на подготовленный AVPlayer. Таким образом, в любой момент у вас может быть загружено не более 2 AV-плееров.

Конечно, я не знаю, можно ли переключить так плавно, чтобы не мешать воспроизведению.

(Я бы добавил это как комментарий, если бы мог.)

Бест, Питер


Я не обнаружил никакой выгоды от последовательной загрузки 10 ресурсов на один AVPlayer. Кроме того, я не понимаю вашего предложения, так как считаю варианты (1) и (7) взаимоисключающими. Вариант 7 объединяет все видеоресурсы в один актив - таким образом, есть только один актив для периода загрузки. Это то, что я делаю сегодня, и чего бы это ни стоило, я получаю задержку примерно в 500 мс по фактическому времени запуска / воспроизведения. Стоит отметить, что SeekTo завершается быстрее, чем воспроизводится фактический первый кадр, поэтому для истинного времени начала я измеряю, когда первый кадр фактически воспроизводится, с помощью обратного вызова по времени.
Bernt Habermeier 08

Чтобы прояснить мою идею: моя идея заключалась в том, чтобы разделить актив на две части: первые несколько секунд и остальные. Теперь вы сделали два актива из одного. Вы бы присоединились к 10 начинаниям и использовали бы skip, а пока играете начало, вы загружаете остальные. Затем это предложение 8, которое добавляется в ваш список.
ilmiacs 08

Но, как я понял из вашего последнего комментария, тем временем вы продвинули исследования дальше, что хорошо, и решение 7 также не работает. Похоже, что AV по сути остается на вашем пути, и единственный выход, который вы можете сделать, - это использовать технологию под ним, то есть Core Media, чтобы получить больший контроль над вашими активами. Питер.
ilmiacs 08

О, теперь я лучше понимаю твою идею. Спасибо. Было бы интересно исследовать, получаете ли вы быстрое время поиска для коротких видеоклипов. Я еще не пробовал, но стоит подумать. Что касается использования основных носителей - я не нашел хороших справочных материалов по этому API. У вас есть хороший ресурс, на который можно мне указать?
Бернт Хабермайер,

Прошу прощения, но нет. Как я уже сказал, я не эксперт ни в AV, ни в Core Media. Просто прочитайте свой вопрос, у меня были идеи, как я лично буду действовать, и я хотел ими поделиться. Peter
ilmiacs

0

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

Если это так, я предлагаю изучить BASS . BASS - это аудио-библиотека, очень похожая на AVPlayer, которая дает (относительно) легкий доступ к низкоуровневым API фреймворка AudioUnits в iOS. Что это значит для тебя? Это означает, что с помощью крошечных манипуляций с буфером (она может вам даже не понадобиться, зависит от того, насколько крошечной вы хотите задержку), вы можете немедленно начать воспроизведение музыки.

Однако ограничения распространяются на видео, как я уже сказал, это аудиотека, поэтому любые манипуляции с видео все равно придется выполнять с помощью AVPlayer. Однако с помощью -seekToTime:toleranfeBefore:toleranceAfter:вы сможете добиться быстрого поиска по видео, если предварительно прокрутите все необходимые параметры.

Если вы синхронизируете несколько устройств (что может предложить ваше приложение), просто оставьте комментарий, и я буду счастлив отредактировать свой ответ.

PS: Поначалу BASS может показаться устрашающим из-за своего C-подобного формата, но его действительно очень легко использовать в том виде, в каком он есть.


-2

Вот несколько свойств и методов, предоставляемых классом AVAsset, которые могут помочь:

- (void)_pu_setCachedDuration:(id)arg1;
- (id)pu_cachedDuration; 
- (struct
 { 
   long long x1; 
   int x2;
   unsigned int x3; 
   long long x4; 
})pu_duration;
- (void)pu_loadDurationWithCompletionHandler:(id /* block */)arg1;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.