php - sound - web audio recorder




L'audio HTML5 dit «diffusion en direct» sur iOS lorsqu'il s'agit d'un fichier statique. (2)

Pour Windows Chrome (et probablement de nombreux autres navigateurs), ce code fonctionne pour servir un mp3 dans un élément 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;
}

Mais lorsque j'utilise un iPhone pour accéder à la même page, le fichier mp3 ne montre pas la durée totale, et lorsque je le lis, il indique "Diffusion en direct".

J'ai essayé de suivre les suggestions de différentes réponses à cette question ( HTML5 <audio> Safari retransmission en direct ou non ) et d'autres articles que j'ai lus, mais aucun ne semble avoir d'effet.

Peu importe la façon dont je change les en-têtes, le mp3 semble fonctionner comme souhaité sous Windows et ne fonctionne pas sous iOS.

Comment puis-je déboguer ce que je fais mal?

Voici 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>

Les fichiers MP3 n'ont pas d'horodatage, et donc aucune longueur inhérente pouvant être connue à l'avance. Chrome est juste en train de deviner, basé sur le débit au début du fichier et la taille en octets du fichier. Ça ne sait pas vraiment.

Certains joueurs ne se donnent pas la peine de deviner.

De plus, tous les navigateurs sur iOS sont Safari sous le capot, grâce aux politiques incroyablement restrictives d’Apple. Par conséquent, Chrome sur iOS n’est en réalité qu’un wrapper pour une vue Web Safari.


Whoa, c'était un problème très difficile à résoudre. (Cela m'a pris des jours.)

Et j’ai appris que ce n’était pas seulement iOS qui avait des problèmes: Safari sur Mac ne fonctionnait pas non plus.

Maintenant, je pense que tout fonctionne sur tous les navigateurs que j'ai testés.

Je suis vraiment content d'avoir trouvé cet exemple à suivre.

Voici ma réponse:

/**
 * 
 * @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://.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://.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;
}




audio