代码之家  ›  专栏  ›  技术社区  ›  Ryan

HTML5音频是静态文件时在iOS中说“直播”

  •  1
  • Ryan  · 技术社区  · 6 年前

    对于WindowsChrome(可能还有许多其他浏览器),此代码适用于在 audio

    /**
     * 
     * @param string $filename
     * @return \Illuminate\Http\Response|\Illuminate\Contracts\Routing\ResponseFactory
     */
    public function getMp3($filename) {
        $fileContents = Storage::disk(\App\Helpers\CoachingCallsHelper::DISK)->get($filename);
        $fileSize = Storage::disk(\App\Helpers\CoachingCallsHelper::DISK)->size($filename);
        $shortlen = $fileSize - 1;
        $headers = [
            'Accept-Ranges' => 'bytes',
            'Content-Range' => 'bytes 0-' . $shortlen . '/' . $fileSize,
            'Content-Type' => "audio/mpeg"
        ];
        Log::debug('$headers=' . json_encode($headers));
        $response = response($fileContents, 200, $headers);
        return $response;
    }
    

    但当我用iPhone浏览同一页面时,mp3文件并没有显示总的持续时间,当我播放时,它会显示“直播”。

    HTML5 <audio> Safari live broadcast vs not )以及我读过的其他文章,但似乎都没有效果。

    无论我如何更改标题,mp3在Windows上似乎都能正常工作 在iOS上工作。

    这里是HTML:

    <audio controls preload="auto">
        <source src="{{$coachingCall->getMp3Url()}}" type="audio/mpeg"/>
        <p>Your browser doesnt support embedded HTML5 audio. Here is a <a href="{{$coachingCall->getMp3Url()}}">link to the audio</a> instead.</p>
    </audio>
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Brad    6 年前

    MP3文件没有时间戳,因此没有可以提前知道的固有长度。Chrome只是猜测,基于文件开头的比特率和文件的字节大小。它真的不知道。

    此外,由于苹果的一些限制性政策,iOS上的所有浏览器都是隐藏的Safari。因此,iOS上的Chrome实际上只是一个Safari web视图的包装器。

        2
  •  1
  •   Ryan    6 年前

    哇,那是个很难解决的问题。(我花了好几天时间。)

    现在我想我测试过的每一款浏览器都能正常工作。

    我真的很高兴我发现 this example 跟随。

    我的答案是:

    /**
     * 
     * @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;
    }
    
    推荐文章