Рубрики
Без рубрики

Вкус потоковой передачи медиа с колбой

Путешествие от едва от подачи 8 -минутной песни до мгновенной трансляции 5 -часовых музыкальных автоматов в Anyaudio. Tagged с помощью колбы, Python, потоковой передачи.

Фото Остина Нила на Unsplash (отредактировано)

Anyaudio это хобби -проект, который я начал с своих друзей во втором курсе моего инженера. На данный момент он состоит из API -сервер (который также обслуживает медиа 😅), a Реакция на основе PWA (По крайней мере, это то, что я пытаюсь сделать это) и Android App Анкет Есть Организация GitHub посвящен различным компонентам проекта.

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

Но откуда у вас есть их медиа?

” Где вы можете найти каждую версию каждой песни, которую вы когда -либо слушали? “

Просто, YouTube !

YouTube проводит каждую песню, которая когда -либо существовала, и будет продолжать это делать. Не только это, это также имеет различные форматы и сочетание битов для аудио, которые он размещает. Не говоря уже о точности результатов поиска, которые он представляет.

Имея в виду эти вещи, мы решили использовать YouTube в качестве источника данных для всего – результаты поиска, плейлисты, медиа, автозаполнение, а что нет.

Большая часть детали была соскребается с YouTube, но для того, чтобы получить аудио URL видео, мы использовали YouTube-DL (Инструмент командной строки для загрузки видео с YouTube и много других веб-сайтов ).

Поток запросов на потоковую передачу

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

  1. Получите детали для звука, в котором он хочет играть. На данный момент есть два способа сделать это – использовать поиск и из предопределенных плейлистов.
  2. Как только клиент получит подробную информацию о аудио, он запрашивает Stream_url с сервера (при необходимости).
  3. Затем ER отвечает URL -адресом, где клиент может сделать запрос, чтобы получить фактический звук. Это URL, который он может внедрить в тег или Exoplayer Источник, чтобы напрямую играть в песню. Это снова обслуживается сервером AnyAudio.
  4. Сервер выступает в качестве посредника и перенаправляет данные с YouTube на клиент.

Первое, что может прийти к вам, это то, что «Почему клиенту нужно сделать дополнительный запрос, чтобы получить фактический URL -адрес потока?»

Это потому, что выяснение фактического медиа-URL-адреса является трудоемким процессом. YouTube-DL Потребуется достаточное количество времени, чтобы получить местоположение ресурса видео на YouTube. Привлечение его заранее для каждого средства массовой информации в результатах поиска или плейлистов будет болью в UX. Итак, мы решили генерировать его по требованию от клиента.

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

Брух, ты вообще ищешь?

API предоставил окончательный URL в потоковом API. Но при использовании этого URL -адреса в качестве источника, полученного игрока не подлежал поиску. Такое поведение не было приемлемым для веб -сайта для воспроизведения в СМИ.

Посмотрев на решение в Интернете (например, вопрос Stackoverflow ), я обнаружил, что Частичный контент Поддержка была необходима для создания элементов СМИ, которые можно найти.

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

"Accept-Ranges": "bytes"

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

Чтобы добавить этот заголовок к ответам, можно использовать следующее украшение –

@app.after_request
def after_request(response):
    response.headers.add('Accept-Ranges', 'bytes')
    return response

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

Range: bytes=0-1023

Итак, теперь нам пришлось обслуживать только эту часть медиа -файла, когда его спросили. В колбе это можно легко сделать следующим образом –

r = requests.get(url)
range_header = request.headers.get('Range', None)
if range_header:  # Client has requested for partial content
  size = int(r.headers.get('content-length'))  # Actual size of song

  # Look up for ranges
  m = re.search('(\d+)-(\d*)', range_header)
  g = m.groups()
  byte1, byte2 = 0, None
  if g[0]:
    byte1 = int(g[0])
  if g[1]:
    byte2 = int(g[1])
  length = size - byte1
  if byte2:
    length = byte2 + 1 - byte1
  data = r.content[byte1: byte2]  # Trim the data from server

  # Prepare response
  rv = Response(data, 206, mimetype=mime, direct_passthrough=True)
  rv.headers.add('Content-Range', 'bytes {0}-{1}/{2}'.format(byte1, byte1 + length - 1, size))
  return rv

# No partial content, handle normally

После этого SEECKBAR был функциональным, и пользователь мог перейти на любую позицию на воспроизводимом аудио.

Проблема с длинными песнями

Потоковое аудио на Anyaudio стала лучшим опытом, чем раньше. Но здесь есть проблема. Для каждого запроса на медиа, сделанном на сервере, первое, что сделано, – это извлечение носителя с серверов YouTube (см. Строку 1 в коде выше). А после этого фиксированная часть СМИ отправляется в ответ на клиента.

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

Решение об этом довольно просто – спросите просто о том, что вам нужно.

Спроси только то, что тебе нужно

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

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

range_header = request.headers.get('Range', None)
    if range_header:
        from_bytes, until_bytes = range_header.replace('bytes=', '').split('-')
        if not until_bytes:  # No until bytes is set, set it to start + 3MB
            until_bytes = int(from_bytes) + int(1024 * 1024 * 3)  # 1MB * 3 = 3MB

        # Get only what required from YouTube
        headers = {'Range': 'bytes=%s-%s' % (from_bytes, until_bytes)}
        r = requests.get(url, headers=headers)
        data = r.content

        # Generate response
        rv = Response(data, 206, mimetype=mime, direct_passthrough=True)
        rv.headers.add('Content-Range', r.headers.get('Content-Range'))
        return rv

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

Еще один шаг вперед – подключение потоков

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

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

Чтобы включить потоковую передачу в Запросы , мы можем просто добавить поток = Верно параметр при выполнении запроса. Объединяя это с Потоковые способности Flask , мы можем мгновенно начать обслуживание данных клиенту, не дожидаясь выполнения некоторых запросов. Вот грубая выборка того, как это будет работать –

def generate_data_from_response(resp, chunk=2048):
    for data_chunk in resp.iter_content(chunk_size=chunk):
        yield data_chunk


def serve_partial(url, range_header, mime, size=3145728):
    from_bytes, until_bytes = range_header.replace('bytes=', '').split('-')
    if not until_bytes:
        until_bytes = int(from_bytes) + size  # Default size is 3MB

    # Make request to YouTube
    headers = {'Range': 'bytes=%s-%s' % (from_bytes, until_bytes)}
    r = requests.get(url, headers=headers, stream=True)

    # Build response
    rv = Response(generate_data_from_response(r), 206, mimetype=mime,
                  direct_passthrough=True)
    rv.headers.add('Content-Range', r.headers.get('Content-Range'))
    rv.headers.add('Content-Length', r.headers['Content-Length'])
    return rv

Это привело к тому, что время первого байта ( ttfb ) составляет около 400 мс на двух основном сервере со средней скоростью клиента. Хотя это нигде нигде по сравнению с запросами, сделанными на YouTube от того же клиента (около 15 мс), это достижение для хобби -проекта с дешевым сервером.

Что мы планируем дальше?

На данный момент очень трудно инвестировать время в Анюао. Но мы действительно с нетерпением ждем возможности представить API V3 на сервере, что позволяет более простым запросам и поддержке плейлиста. Это будет означать, что пользователи смогут прослушать завершенные плейлисты на YouTube в веб -приложении.

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

Интеграция этого потребовала бы, чтобы пользователи создали учетную запись на платформе, которую мы не хотим включать, поскольку мы просто хотим, чтобы платформа была максимально беспроблемной. Итак, для этого мы планируем просто использовать локальное хранилище браузера. Дополнительные функции будут включать сохранение песен и прослушивание их всякий раз, когда захочет пользователь. Оффлайн прослушивание – это то, что мы стремимся интегрировать, когда оно окажется хорошим PWA.

Прощальные слова

Я многому научился с этим проектом и надеюсь узнать гораздо больше. Мы будем рады, если вы зайти и сниматься в нашем Репозитории GitHub . И если вы разработчик и любите исправить немного грязного кода и представить потрясающие тестовые примеры, не стесняйтесь связаться с нами на нашем Канал друтчика Анкет

Итак, посетите Anyaudio сейчас, чтобы послушать ваши любимые песни. Пожалуйста, дайте нам знать ваши взгляды и следите за обновлениями.

Оригинал: “https://dev.to/singhpratyush/the-taste-of-media-streaming-with-flask-58a4”