Как реализовать поддержку Video Seek со встроенным HTTP-сервером на iOS?

У меня есть клиентское приложение iOS, которое использует GCDWebServer для показа изображений и видео, хранящихся в папке NSSearchPathDirectory.DocumentDirectory приложения на мое устройство.

При запуске приложения я запускаю экземпляр GCDWebServer и добавляю обработчик файловых ответов для своих запросов:

self.addDefaultHandlerForMethod("GET", requestClass: GCDWebServerFileRequest.self) { request in

    return GCDWebServerFileResponse(file: self.filePathForResponse(request.URL))
}

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

curl -O http://192.168.0.15:8080/files/IMG_1213-1280x720.MOV

Мое приложение взаимодействует с устройством Chromecast, отправляя URL-адреса, подобные приведенному выше, на медиа-канал, а приложение-приемник Chromecast загружает и воспроизводит видео по указанным URL-адресам - пока все хорошо.

Моя проблема в том, что я хочу реализовать поддержку поиска для воспроизводимого в данный момент видео, и как только я отправляю запрос на поиск на медиаканал, я получаю ошибку "Broken pipe" от GCDWebServer и воспроизведение видео прерывается. Лог с сервера такой:

....
[DEBUG] Connection sent 32768 bytes on socket 24
[DEBUG] Connection sent 32768 bytes on socket 24
[ERROR] Error while writing to socket 24: Broken pipe (32)
[DEBUG] Did close connection on socket 24

Мое лучшее понимание проблемы заключается в том, что нормальное воспроизведение работает, потому что это то же самое, что и загрузка файла от начала до конца, и это может быть выполнено с помощью обычного GCDWebServerFileResponse, однако поиск эквивалентен «переходу» к другой части файла и Я не уверен, что чтение такого файла будет работать с моей конфигурацией.

  • Есть ли способ настроить GCDWebServer, чтобы это работало? Я знаю, что проблема может быть решена, потому что есть несколько живых приложений, которые делают это.
  • Нужно ли использовать сервер, поддерживающий другие протоколы, такие как HLS или RTSP?
  • Нужно ли кодировать видеофайлы особым образом?

Для справки, я также попробовал другой HTTP-сервер под названием Swifter, но столкнулся с той же проблемой.


person bizz84    schedule 15.04.2016    source источник


Ответы (1)


iOS' AVPlayer сначала запрашивает размер файла, а затем запрашивает у сервера фрагменты данных, включая желаемый диапазон в запросе. В этом ответе показана сетевая активность AVPlayer, а код состояния ответа — 206, т. е. Partial Content.

Итак, нам нужно ответить только желаемым фрагментом данных:

webServer?.addDefaultHandlerForMethod("GET", requestClass: GCDWebServerRequest.self, asyncProcessBlock: { (request, completionBlock) in
  let response = GCDWebServerFileResponse(file: path, byteRange: request.byteRange)
  completionBlock(response)
})

Обратите внимание, что важно проверить, определяется ли request.byteRange вызовом request.hasByteRange().

Используя код, который я предоставил, я смог воспроизвести видео с помощью AVPlayer или веб-браузера, и очистка / поиск по времени работали отлично.

person Richard Topchii    schedule 14.06.2016
comment
Я изменил свой код, чтобы использовать byteRange, как было предложено, однако в моем случае потребителем является не AVPlayer, а Chromecast, отправляющий запросы на видеоданные. Похоже, что byteRange.length == -1, contentType = nil и contentLength = -1. Даже при использовании этого с hasByteRange() видео очень прерывистое, и я получаю много: [ОШИБКА] Ошибка при записи в сокет 18: Broken pipe (32) - person bizz84; 19.08.2016
comment
Я наблюдал, как клиенты (AVPlayer или Chrome) потребляют контент: после первоначальных запросов на получение размера видео они запрашивают все видео с самого начала. После того, как клиент буферизировал достаточно данных, он перестает получать и закрывает соединение, отсюда и ошибка неработающего канала. Когда клиенту нужно больше данных (т.е. проигрыватель воспроизвел почти все буферизованное видео или пользователь начал пролистывать в другое место), клиент запускает новый запрос с диапазоном байтов, который начинается не с нуля, а заканчивается в конце файла. - person Richard Topchii; 21.08.2016
comment
Например, если размер файла 500, а запросов два: 1. 0-499 - после того, как плеер накопит достаточно данных, он закроет соединение (на самом деле получил только 100 байт) 2. 101-499 - когда буфер станет почти пустым , плеер снова запускает предварительную загрузку, но не запрашивает уже буферизованные данные. Это объясняет ошибки сломанной трубы - person Richard Topchii; 21.08.2016
comment
Не уверен, как Chrome выполняет буферизацию, но, судя по прерывистости видео, кажется, что запросы делаются часто и для небольших буферов, но данные не принимаются достаточно быстро, чтобы видео могло воспроизводиться непрерывно. Если я опускаю byteRange, то воспроизведение идет нормально, но я не могу найти, что было моей первоначальной проблемой. - person bizz84; 25.08.2016
comment
Интересно, используют ли другие приложения в App Store GCDWebServer или другие решения. Я также пробовал Swifter https://github.com/httpswift/swifter, но проблема с сломанным каналом кажется еще хуже. - person bizz84; 25.08.2016
comment
наконец, это решение сработало. спасибо за публикацию этого. однако это не работает, если ваш URL-адрес не имеет расширения. Этот URL-адрес воспроизводит видео в веб-браузере, но в этом случае оно не воспроизводится в AVPlayer с использованием GCDWebServer. - person Mahesh Agrawal; 13.12.2019
comment
я хочу запросить частичные данные на сервер и отправить ответ с частичными данными. в этом случае я не хочу сохранять данные в файл. для этого сценария, как я могу отправить частичный ответ клиенту? - person Mahesh Agrawal; 10.01.2020