Search code examples
phpheaderstreamingstdoutjplayer

Streaming an MP3 on stdout to Jplayer using PHP


I'm initializing jplayer with the following parameters:

$jplayer.jPlayer('setMedia',{
  mp3: data.audioMP3,
  oga: data.audioOGA
});

Assume that data.autdioMP3 (and it's OGA counterpart) are paths to a php script, for example: 'http://myserver.local/playaudio.php?songID=99&format=mp3'

Where I am struggling is with playaudio.php. I would like to read the MP3 file and stream it to jplayer without revealing the path to the audio (this is why I am not initializing jplayer with a path to the audio file).

Something like (taken partially from the example for readfile at php docs):

<?php

$if ($validUser && file_exists($file){
     header('Content-Transfer-Encoding: binary');
     header('Content-Type: audio/mpeg');
     header('Expires: 0');
     header('Cache-Control: must-revalidate');
     header('Content-Length: ' . filesize($file));
     ob_clean();
     flush();
     readfile($file);
     exit;
}
?>

I think this just forces the download of the file though... will this still reveal the path to the file to the user? Is there a better option to stream raw mp3 data to the user without revealing the path to the file that you know of?

Thanks!


Solution

  • Thanks for everyone's answers.

    I actually ended up solving the problem without revealing the path to the audio file to the user.

    Because this is for an online voice messaging application, it was important to protect the path to the messages in order to protect the user's privacy.

    The situation ended up being further complicated by the fact that the messages are stored on a remote server... found this out right after I posted this question.

    So all in all the process was:

    1) Curl the MP3 file from the remote server

    2) Set some headers to force the download of the data, which you then echo to stdout

    Because the data is being put on standard out and the headers are set to force a download, the effect for jPlayer is the same as if it had requested a file at /some/dir/message.mp3, except that the request goes to /some/dir/playmessage.php - thus the path is never revealed to the user.

    EDIT** - Assume that I have access control, validation running before the snippit below, otherwise exposing the path to the script below is no different than just exposing the path to the mp3. (see comments between me and Lloyd)

    Here's the code that ended up getting the job done:

    $ch = curl_init($remoteFile);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HEADER, true);
    $data = curl_exec($ch);
    curl_close($ch);
    if ($data === false) {
      echo 'CURL Failed';
      exit;
    }
    
    //Get file size
    if (preg_match('/Content-Length: (\d+)/', $data, $matches)) {
      $contentLength = (int)$matches[1];
    }
    
    //force user to download file contents
    header('Content-Transfer-Encoding: binary');
    header('Content-Type: audio/mpeg');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Content-Length: ' . $contentLength);
    ob_clean();
    flush();
    echo $data;
    exit;
    ?>
    

    Assume that $remoteFile is a path to the mp3 file that I want to play on the server where we are storing messages.

    I found this resource on reading a remote file using PHP especially helpful. Here's some more information on modifying http headers.

    NOTE I am essentially downloading the file twice, which slows the process down for the user. There is probably a better way to do this. :) If you kept the file locally, what I had initially in my question actually works (using readfile() to output the raw mp3 data).