HTML5音频是静态文件时,在iOS中显示“实时广播”

时间:2019-02-18 00:02:43

标签: php html5 laravel audio

对于Windows Chrome(可能还有许多其他浏览器),此代码适用于在audio元素中提供mp3:

/**
 * 
 * @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>

3 个答案:

答案 0 :(得分:1)

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.

答案 1 :(得分:1)

自从我刚建立帐户以来,我无法发表评论,所以...补充了RYAN的

刚刚发现您可以节省一些加载时间,而无需删除

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

并用

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

直接将大小传递给getResponseStream函数,而不是将全部内容下载到变量中,然后再测量长度。

谢谢瑞安(Ryan),我用臭味十足的野生动物园为我节省了很多宝贵的时间。

答案 2 :(得分:0)

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

我了解到,不仅仅是iOS出现了问题:Mac上的Safari也无法正常工作。

现在,我认为所有功能都可以在我测试过的每种浏览器上正常工作。

我很高兴我找到了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;
}