воскресенье, 31 января 2016 г.

AcePHProxy: обновление до 0.6.4

Не так давно я выпустил новую версию AceStream прокси-сервера, и она имеет значительные улучшения!
Из самого вкусного:
  • прямой просмотр *.torrent файлов без скачивания, как обычных фильмов
  • при этом видео можно перематывать
А также:
  • вывод содержимого (.torrent-файлов) из спец. папки как плейлиста
  • вывод .torrent-файлов, состоящих из нескольких видео, как плейлиста
  • эффективное использование памяти
  • слегка обновленный интерфейс
Ложка дегтя
  • магнет-ссылки проигрывать не умеет
  • если торрент предоставляет отдельный файл субтитров или звуковой дорожки, они не подцепляются
Еще с того момента, как мне удалось первый раз запустить воспроизведение из .torrent-файла, стало ясно, что без перемотки жить грустно.
XBMC начинал показывать фильм, и даже правильно отображал его длительность. но перематывать отказывался. Я пробовал выдавать тестовый файл из PHP-скрипта последовательным чтением, но перемотка не работала.

Для достижения цели вырисовывался такой план:
  1. понять, чего не хватает XBMC для поддержки перемотки
  2. написать скрипт, выдающий тестовый видеофайл так, чтобы работала перемотка
  3. интегрировать результаты исследований в AcePHProxy

Как же выдать видео по HTTP с поддержкой перемотки

Я вооружился анализатором сетевого трафика Wireshark, положил тестовый видеоролик в папку вебсервера, убедился, что XBMC открывает его по HTTP, и перемотка при этом работает, и стал гонять, открывая файл снова и снова, пока не стал ясен следующий алгоритм:
  1. XBMC делает HEAD запрос, чтобы получить ответные заголовки с информацией о содержимом (код ответа, а то может такого контента и нет, версия протокола HTTP, размер контента, контент-тип, поддержка Ranged-запросов и т.д.). Если HEAD не поддерживается сервером, делает запрос с заголовком Range: bytes=0-0
  2. Дальше начинаются реальные запросы данных. Первый такой запрос идет с заголовком "Range: bytes=0-", т.е. запрашивается файл целиком. Обозначу этот запрос "А". Скоро он будет сброшен (передача отменится). Важно заметить, что ответный заголовок HTTP/1.1 206 Partial Content, а не HTTP/1.1 200 OK!
  3. К этому моменту XBMC уже знает длину содержимого, и делает второй запрос данных ("Б"), запрашивает около 1Мб с конца файла (Range: bytes=1455123456-). "А" еще активен, т.е. запросы идут параллельно, и далее будет описано, почему это является проблемой. 
  4. Коннект "А" отменяется, открывается "В", запрашивается диапазон "Range: bytes=4108-", почти с начала файла. "Б" еще активен
  5. Коннект "Б" отменяется. делается запрос "Г" с диапазоном "Range: bytes=1455123456-", т.е. опять с конца файла, почти с того же места, разница в несколько байт.
  6. Через некоторое время без видимых причин коннект "В" отменяется и далее активен только коннект "Г", запрошенный конец файла докачивается целиком
  7. И наконец следует последний запрос "Д" с диапазоном "Range: bytes=4108-", который и остается активен до конца.
Вся вышеописанная канитель происходит буквально за секунду.
Т.е. для поддержки перемотки XBMC нужно получить хвост файла, для этого он делает частичные запросы, и требуется, чтобы сервер умел работать по HTTP/1.1, поддерживал заголовки "Range: bytes", и правильно на них отвечал.

Используя эти данные я написал скрипт, выдающий видеофайл так же, как вебсервер, в соответствии со всеми этими правилами, и XBMC стал перематывать!
Теперь осталось внедрить это в AcePHProxy, только есть проблема с параллельными запросами.

А именно: когда AceStream открывает поток и выдает ссылку START http://..., ее можно открыть только в один поток. Ну т.е. второй коннект к ней можно сделать, но данные пойдут только в первый. А значит нельзя честным образом обрабатывать все параллельные запросы XBMC к одному файлу (запрос начала файла, "почти начала" и конца). Значит придется хитрить. Тем более, что "Range: bytes=x-y" XBMC всегда запрашивает без аргумента "y", т.е. никогда не известно до какого байта надо выдать. Однако у XBMC есть такая черта поведения: если коннект сбросился, он переспросит с того места, где все оборвалось.

А значит допустим следующий алгоритм: клиент коннектится, выдаем ему все, что он попросил "отсюда и до обеда". Когда коннектится второй клиент (фактически плеер один и тот же, но запрос отдельный, со стороны софта каждый коннект считается отдельным клиентом), сбрасываем первого и обслуживаем второго. Иначе никак, ибо, повторюсь, AceStream не позволяет множественные подключения к одному своему видеопотоку.

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

Платите за 100Мбит-ный интернет, чтобы фильмы и сериалы качались быстрее?
Я комфортно смотрю фильмы онлайн при своем 8Мбит-ном интернете.
С каналом 48Мбит, конечно, повеселее. Для сравнения - при 8Мбит фильм запускается за 35-50сек (пребуферизация, подгрузка нужных частей видеофайла), а при 48Мбит - за 5-10 сек.
Или вот еще выгода: иногда скачанный торрент не совсем удачен (оригинальная озвучка по громкости равна переводу и разобрать родную речь в этой мешанине сложно), в данном случае достаточно скачать другой вариант того же торрента и можно тут же запускать просмотр.

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

Немного о других улучшениях

Разделяемый видеобуфер: это когда на каждую трансляцию есть FIFO-буфер с видеоданными (14Мб на трансляцию, настраиваемо), а у каждого объекта клиента есть указатель на место в буфере. В итоге использование памяти при установившемся режиме составляет 15.4MB, при подключении еще 4 клиентов к той же трансляции значение не выросло (фото делалось не в этот момент).

Папка с торрент-файлами: в коде прописана папка, в которую можно скидывать .torrent-файлы, AcePHProxy по ее содержимому сформирует плейлист, для этого надо обратиться к софту по спец.ссылке http://<server_ip>:8000/playlist

И немного статистики в интерфейсе: версия софта, аптайм, потребление памяти и т.д. На скрине выше в колонке Client - uptime каждого клиента и число скачанных им за время подключения мегабайт.

О дегте

Очень хотелось мне научить софтину открывать содержимое по INFOHASH, оно же магнет-ссылка, потому что в автоматическом режиме каким-нибудь краулером (ботом-поисковиком) найти в сети магнет-ссылку куда проще, чем готовый .torrent-файл для скачивания. И это открыло бы новые возможности для автоматизации домашней мультимедиа. Сам AceServer не умеет открывать INFOHASH до сих пор. хоть и заявлена поддержка, поэтому я искал обходные пути. Их было два. но однотипные.
  1. сформировать простейший .torrent-файл с содержимым
    magnet:?xt=urn:btih:<INFOHASH>
  2. скормить магнет-ссылку rTorrent-у, тот сделает torrent-файл, который и можно будет взять в папке session. Я даже специально обновил rtorrent до 0.9.2 (ох непросто это было)
Наивный землянин.. Первый способ конечно никуда не годен, это было бы слишком просто.
Второй способ подавал надежды. rTorrent нормально съел ссылку в том виде, в каком она описана в первом способе, чем порадовал меня. И даже .torrent файл я нашел в папке session. Однако при попытке скормить его AceStream я получил ошибку "announce, nodes and announce-list missing".

Впрочем, буквально на днях мне удалось через сайт http://magnet2torrent.me/ сделать из магнет-ссылки работающий в Ace торрент, но там кроме инфохэша еще адреса трекеров были, а это уже "и я так могу". Тем не менее, кажется еще не все потеряно ;)

TODO

1. объявление о своем присутствии в сети по DLNA
2. если DLNA будет слишком сложно, можно сделать спецссылку для выдачи RSS-плейлиста
3. хэндлер для приема поисковых запросов, поиск в инете и выдача плейлиста из найденного
4. научить проигрывать magnet-ссылки

19 комментариев:

  1. А не логичней было бы задавать путь/имя_папки для торрентов в виде переменной в файлике .acePHproxy.settings , а не статично в коде как /STORAGE/FILES/ ??

    ОтветитьУдалить
  2. Этот комментарий был удален автором.

    ОтветитьУдалить
  3. Ну и еще "ложечка дегтя" ..... А если торрент-файл содержит несколько "видео" внутри? Как задать которое проигрывать ? Например - http://www.nyaa.se/?page=download&tid=477288 ??? или будут проигрываться все по очереди ?

    В остальном - БОМБА !

    ОтветитьУдалить
    Ответы
    1. благодарю!
      торрент из нескольких видео парсится и выдается как плейлист, где и можно выбрать какой файл проигрывать.
      либо можно сослаться на конкретный файл по индексу:
      http://server:8000/torrent/BigBangTheory.torrent/2

      Удалить
  4. Цитата :"А именно: когда AceStream открывает поток и выдает ссылку START http://..., ее можно открыть только в один поток." и " Иначе никак, ибо, повторюсь, AceStream не позволяет множественные подключения к одному своему видеопотоку." Я почитал API движка на wiki в версии 3.0 добавлен идентификатор плеера именно для предоставления возможности одновременного просмотра ...
    http://wiki.acestream.org/wiki/index.php/Engine_HTTP_API#.D0.98.D0.B4.D0.B5.D0.BD.D1.82.D0.B8.D1.84.D0.B8.D0.BA.D0.B0.D1.82.D0.BE.D1.80_.D0.BF.D0.BB.D0.B5.D0.B5.D1.80.D0.B0
    Может я неверно понял в Вашем описании что к чему ...но мало ли поможет

    ОтветитьУдалить
    Ответы
    1. не, это только, чтобы помочь AceServer-у разобраться, кому в итоге отдавать поток.
      "движок перестает отдавать плееру данные по трансляции, если эту же трансляцию запустили в другом плеере, но делает это только в том случае, если может отличить один плеер от другого."
      иначе трансляция адски глючит в обоих плеерах, проверено

      Удалить
    2. Уже проверил тоже ... тупит )) Безбожно ... или одна трансляция стопорится, вторая начинает идти .... Уже вкурил для чего идентификатор ввели :)

      Удалить
  5. Для дальнейшего развития проекта -> http://api.torrent-tv.ru/v3/api_v3.html , полезная ссылочка, может есть у Вас , но на всякий случай :) Раз в .acePHProxy.settings есть "куда вводить" данные, то логично их в дальнейшем использовать :) Тем более что даже "без оплаты" доступен плейлист "Free" ... но "текущую" возможность ... "подсовывания" плейлиста заданного "формата" .... тоже надо оставить ... полезная функция !

    ОтветитьУдалить
    Ответы
    1. вот это очень интересно! полезная ссылка

      Удалить
    2. Да пожалуйста :) У меня еще есть :) Жму руку ! Спасибо за разработку. Надеюсь будет продолжение :)

      Удалить
    3. Что ещё интересного есть? :)

      Удалить
  6. А вообще тут не самое лучшее место для обсуждения наработок :) и ссылочек :) Нужен боле "интимный" канал для связи :) Есть ряд мыслишек ...

    ОтветитьУдалить
  7. Приветствую!
    Подскажите пож, что нужно сделать, что-бы прокси отправлял Content-type, например, x-mpegurl или video/mp4?
    Пробовал поток воспроизвести на сайте, в хедерах приходит Content-Type: None, хром интерпретирует как октет/стрим и начинает загрузку

    ОтветитьУдалить
    Ответы
    1. привет. в class.lib.ace.php в методе readStreamHeaders в районе строки 376 можно None переписать на свой mime-type. там готовая закомментированная строка

      Удалить
  8. Здравствуйте, Валерий!
    Большое спасибо за разработку отличного прокси!
    Очень помогло!
    Правда, с PHP 7.0, который поставляется с Ubuntu 16.04, php-ncurses установить не получается :(

    ОтветитьУдалить
  9. Что приятно, acePHPproxy работает и под Windows. Надо только скормить ему порт AceStream по пути: C:\Users\USERNAME\AppData\Roaming\ACEStream\engine\acestream.port.
    Валерий, благодаря Вами разработанному буферу и логике его обработки, acePHPproxy стабильно показывает торрент-фильмы, можно смело ставить их на паузу - после возобновления, соединение не сбрасывается. Все хорошо с перемоткой. Спасибо!
    Немного модифицировал acePHPproxy, чтобы он проигрывал торрент-фильмы по типовой ссылке: http://server:port/torrent/http%3A%2F%2Frutor.in%2Fdownload%2Ffile.php%3Fid%3D1288380/0. А для многофайлового торрента выдавал m3u-плейлист.

    ОтветитьУдалить
  10. Здравствуйте. А не подскажите, как вы реализовали асинхронность в php?

    ОтветитьУдалить