Почему могут быть ошибки при потоковой выгрузке видео на Safari?

Всем привет, вывожу видео потоково используя PHP и React. Проблем с выводом видео в браузере Google Chrome не имею. Единственное, что я понял, что Chrome по другому запрашивает Range (0=) видео через стандартный проигрыватель. А в Safari по другому, 0=1.

Из за этого возникает проблема при просмотре большого видео, хоть они и воспроизводятся:

введите сюда описание изображения

Вот мой код

public function currentFileUnloading(string $range, object $storage_disk): ?array
    {
        try {
            $chunk_size = InfoEnum::CHUNK_SIZE->value;
            $total_size = $storage_disk->size($this->url);
            $mime_type = $storage_disk->mimeType($this->url);

            // Проверяем, что файл существует в S3 перед продолжением
            abort_unless($storage_disk->exists($this->url), Response::HTTP_NOT_FOUND);

            $range = ltrim(strstr($range, '='), '=');
            [$start, $end] = array_map('intval', explode('-', $range));

            // Проверяем диапазон и корректируем end для Safari и других браузеров
            if ($end - $start > $chunk_size || $end === 0) {
                $end = (($start + $chunk_size) > $total_size) ? $total_size - 1 : $start + $chunk_size;
            }

            if (app()->hasDebugModeEnabled()) {
                // Локальная среда - используем локальный stream
                $stream = $storage_disk->readStream($this->url);
            } else {
                // Запрашиваем объект с нужным диапазоном
                $stream = $this->getAwsObjectByRange($start, $end);
            }

            // Отправляем заголовки и запускаем callback для потоковой передачи
            return [
                'headers' => $this->getHeaders($mime_type, $start ?? 0, $end ?? ($total_size - 1), $total_size),
                'callback' => $this->getCallBack($stream, $start ?? 0, $end ?? ($total_size - 1))
            ];

        } catch (Throwable $e) {
            report($e);
        }

        return null;
    }
    protected function getHeaders(mixed $mime_type, int $start, int $end, mixed $total_size): array
    {
        return [
            'Content-Type' => $mime_type,
            'Accept-Ranges' => 'bytes',
            'Content-Length' => $this->getLength($start, $end),
            'Content-Range' => sprintf("bytes %s-%s/%s", $start, $end, $total_size),
            'Cache-Control' => 'max-age=31536000'
        ];
    }
    private function getAwsObjectByRange(int $start, int $end): mixed
    {
        // Используем AWS S3 SDK для получения объекта с диапазоном байтов
        $s3_client = AWS::getInstance()->getClient();

        $result = $s3_client->getObject(
            [
                'Bucket' => config('filesystems.disks.s3.bucket'),
                'Key' => $this->url,
                'Range' => sprintf("bytes=%s-%s", $start, $end),
            ]
        );

        return data_get($result, 'Body');
    }
    protected function getCallBack(mixed $stream, int $start, int $end): Closure
    {
        $length = $this->getLength($start, $end);

        if (app()->hasDebugModeEnabled()) {
            // Перемещаем указатель на начало нужной части файла
            fseek($stream, $start);

            return static function () use ($stream, $length) {
                echo fread($stream, $length);
                if (is_resource($stream)) {
                    fclose($stream);
                }
            };
        }

        return static function () use ($stream, $length) {
            $read_bytes = 0;

            // Закрываем поток, если это ресурс
            if (is_resource($stream)) {
                fclose($stream);
            }

            // Читаем и выводим блоки данных, пока не прочитаем все необходимые байты
            while ($read_bytes < $length && !$stream->eof()) {
                // Читаем блоки данных (например, по InfoEnum::CHUNK_SIZE байт)
                $chunk = $stream->read(InfoEnum::CHUNK_SIZE->value);

                // Если нет данных для чтения, прерываем
                if ($chunk === false || $chunk === '') {
                    break;
                }

                // Отправляем данные пользователю
                echo $chunk;
                flush();

                // Увеличиваем счётчик прочитанных байт
                $read_bytes += strlen($chunk);
            }
        };
    }
    protected function getLength(int $start, int $end): int
    {
        return $end - $start + 1;
    }

return (
<SliderImage key={el.id} style={{translate: `${-100 * currentImage}%`}}>
    <video
       preload="auto"
       ref={currentImage === index ? ref : null}
       style={{maxHeight: visualViewport ? visualViewport?.height * 8 / 12 : "600px"}}
       className={"w-full h-full"}
       aria-hidden={currentImage !== index}
       controls
       key={index}
     >
     <source src={link(el.file_id)}
     type={el.extension === "webm" ? "video/webm" : "video/mp4"}/>
     </video>
     </SliderImage>
)

Ответы (0 шт):