HTML5 audio says “live broadcast” in iOS when it's a static file

前端 未结 3 560
自闭症患者
自闭症患者 2020-12-02 03:09

For Windows Chrome (and probably many other browsers), this code works for serving an mp3 in an audio element:

/**
 * 
 * @param string $filenam         


        
相关标签:
3条回答
  • 2020-12-02 03:23

    Whoa, that was a very difficult problem to solve. (It took me days.)

    And I learned that it wasn't just iOS that was having problems: Safari on Mac hadn't been working either.

    Now I think everything works on every browser I've tested.

    I'm really glad I found this example to follow.

    Here is my answer:

    /**
     * 
     * @param string $disk
     * @param string $filename
     * @return \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\StreamedResponse
     */
    public static function getMediaFile($disk, $filename) {
        $rangeHeader = request()->header('Range');
        $fileContents = Storage::disk($disk)->get($filename);
        $fullFilePath = Storage::disk($disk)->path($filename); //https://stackoverflow.com/a/49532280/470749
        $headers = ['Content-Type' => Storage::disk($disk)->mimeType($fullFilePath)];
        if ($rangeHeader) {
            return self::getResponseStream($disk, $fullFilePath, $fileContents, $rangeHeader, $headers);
        } else {
            $httpStatusCode = 200;
            return response($fileContents, $httpStatusCode, $headers);
        }
    }
    
    /**
     * 
     * @param string $disk
     * @param string $fullFilePath
     * @param string $fileContents
     * @param string $rangeRequestHeader
     * @param array  $responseHeaders
     * @return \Symfony\Component\HttpFoundation\StreamedResponse
     */
    public static function getResponseStream($disk, $fullFilePath, $fileContents, $rangeRequestHeader, $responseHeaders) {
        $stream = Storage::disk($disk)->readStream($fullFilePath);
        $fileSize = strlen($fileContents);
        $fileSizeMinusOneByte = $fileSize - 1; //because it is 0-indexed. https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
        list($param, $rangeHeader) = explode('=', $rangeRequestHeader);
        if (strtolower(trim($param)) !== 'bytes') {
            abort(400, "Invalid byte range request"); //Note, this is not how https://stackoverflow.com/a/29997555/470749 did it
        }
        list($from, $to) = explode('-', $rangeHeader);
        if ($from === '') {
            $end = $fileSizeMinusOneByte;
            $start = $end - intval($from);
        } elseif ($to === '') {
            $start = intval($from);
            $end = $fileSizeMinusOneByte;
        } else {
            $start = intval($from);
            $end = intval($to);
        }
        $length = $end - $start + 1;
        $httpStatusCode = 206;
        $responseHeaders['Content-Range'] = sprintf('bytes %d-%d/%d', $start, $end, $fileSize);
        $responseStream = response()->stream(function() use ($stream, $start, $length) {
            fseek($stream, $start, SEEK_SET);
            echo fread($stream, $length);
            fclose($stream);
        }, $httpStatusCode, $responseHeaders);
        return $responseStream;
    }
    
    0 讨论(0)
  • 2020-12-02 03:25

    MP3 files don't have timestamps, and therefore no inherent length that can be known ahead of time. Chrome is just guessing, based on the bitrate at the beginning of the file and the byte size of the file. It doesn't really know.

    Some players don't bother guessing.

    Also, all browsers on iOS are Safari under the hood, thanks to some incredibly restrictive policies by Apple. Therefore, Chrome on iOS is really just a wrapper for a Safari web view.

    0 讨论(0)
  • 2020-12-02 03:36

    I can't comment since I just made my account, so... complementing RYAN's

    Just found out that you can save some loading time removing the

    $fileContents = Storage::disk($disk)->get($filename);
    

    And replacing it with

    $fileSize = Storage::disk($disk)->size($filename);
    

    Passing the size directly to the getResponseStream function, instead of downloading the whole content into a variable and then measuring the length.

    Thank you Ryan, saved me a lot of precious time with the stinky safari.

    0 讨论(0)
提交回复
热议问题