Передача-кодирование: фрагментированное и MP3/Lame

У меня есть веб-служба PHP, которая возвращает HTTP-ответ mp3. Это работает, но когда я включаю дросселирование сети Chrome в DevTools, он возвращает только часть ответа:

        $stream_start1 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start2 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start3 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start4 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start5 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start6 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start7 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start8 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start9 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start10 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start11 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start12 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));
        $stream_start13 = Psr7\stream_for(fopen('./sounds/ping.mp3', 'r'));

        return new Psr7\AppendStream([$stream_start1 , $stream_start2 , $stream_start3 , $stream_start4 , $stream_start5 , $stream_start6 , $stream_start7 , $stream_start8 , $stream_start9 , $stream_start10, $stream_start11, $stream_start12, $stream_start13]);

С приведенным выше кодом при включенном дросселировании dev я получаю 7 пингов вместо 13.

На самом деле реальный код получает поток от стороннего сервиса и помещает его между двумя файлами:

 return new Psr7\AppendStream([file1Stream, 3rdPartyStream, file2Stream])

Итак, мне не обязательно знать длину, чтобы установить Content-Length, поэтому я использую Transfer-Encoding: chunked.

Когда я смотрю в инструментах разработки на укороченные ответы на запросы, я постоянно вижу, что они заканчиваются чем-то похожим на какие-то ХРОМЫЕ метаданные или добавленную тишину:

e¨Deš•†š¹‡ÌC`̹3†'2 CBR9S¨Âöe­i´g¡ÿ–W©^¥ÔzWI}I8¸¶vv,¸¼Ù£J*ÙÙû±W•'X&+X±£@È ·bbÂìýbŸâÿâŸëLAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUÿóbÄ”AIÖtUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU

Мне интересно, возможно ли, что браузер путает часть этой информации LAME (или что-то еще) с концом последовательности фрагментов из-за задержек в получении фрагментов. Возможно ли, что в двоичном аудиофайле смешано что-то, напоминающее пустой фрагмент, указывающий на последний фрагмент? Есть ли способ избежать или закодировать данные, чтобы избежать потенциальных последовательностей, подобных этой? Может через компрессию?

Мой другой вариант - прочитать сторонний поток (в середине сэндвича) полностью в память перед его пересылкой. Таким образом, я могу рассчитать длину и установить Content-Length, так что это план Б.

ОБНОВЛЕНИЕ: я попытался установить Content-Length в моем небольшом примере с 13 файлами, но я все еще получаю 7 пингов в качестве ответа. Длина, которую я рассчитал с:

$length = strlen($stream_start0->getContents()) * 13 

Это 122252. Но возвращаемое содержимое файла имеет длину около 65529 байт (шестнадцатеричный редактор говорит 65536...?). В любом случае, это слишком коротко. Кроме того, заголовок Transfer-Encoding исчез, что означает, что я думаю, что он больше не разбивается на части?

ОБНОВЛЕНИЕ 2: Также возможно, что проблема в том, что я объединяю mp3-файлы в необработанном виде. Я думаю, что технически все становится сомнительным, когда вы это делаете, особенно если аудио содержит теги ID3 и тому подобное, хотя в файлах ping, использованных выше, не должно быть никаких тегов ID3. Однако в нашей среде stg, где я наблюдаю эту проблему без инструментов разработчика, отключение не всегда происходит в перерывах между файлами.


person xdhmoore    schedule 07.10.2020    source источник


Ответы (1)


Так что все вышеперечисленное было отвлекающим маневром. Оказалось, что проблема заключалась в том, что я скопировал какой-то пост в блоге с кодом JavaScript, который вызывал response.getReader().read() только один раз и, таким образом, получал только первый фрагмент ответа.

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

Решение заключалось в использовании response.blob(), которое считывает до конца потока и создает нужный мне большой двоичный объект за один раз.

Я думал, что проверил все, что скопировал, но, похоже, это не так. Но я кое-чему научился, вот и все.

Документы на reader.read()
Документы по response.blob()

person xdhmoore    schedule 07.10.2020