web-dev-qa-db-fra.com

Utiliser php pour sortir une vidéo mp4

Ok, fondamentalement, j'ai un projet qui nécessite que les vidéos soient cachées aux utilisateurs tout en étant en mesure de les voir (en utilisant php). voici ce que j'ai obtenu jusqu'à présent:

Le fichier video.php a ceci:

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'path/to/movie.mp4');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
curl_setopt($ch, CURLOPT_HEADER, 0);
$out = curl_exec($ch);
curl_close($ch);


header('Content-type: video/mp4');
header('Content-type: video/mpeg');
header('Content-disposition: inline');
header("Content-Transfer-Encoding:­ binary");
header("Content-Length: ".filesize($out));
echo $out;
exit();
?>

et le fichier html qui est censé afficher cela utilise html5 comme il s'y attend. maintenant voici le truc .. quand j'incorpore directement ça (pas) ça marche. mais cela ne fonctionne pas sur mon iPhone et ne fonctionne pas dans la balise ... si j'utilise le fichier direct au lieu du wrapper php, tout fonctionne bien, sur mon iPhone aussi ...

donc je suppose que ma question pour celui-ci est la suivante: quelles sont les informations d'en-tête appropriées pour reproduire parfaitement un mp4 qui peut être diffusé via iPhone et HMTL5?

Solution dérivée de: http://mobiforge.com/developing/story/content-delivery-mobile-devices

fichier video.php:

<?php
$file = 'path/to/videofile.mp4';
$fp = @fopen($file, 'rb');

$size   = filesize($file); // File size
$length = $size;           // Content length
$start  = 0;               // Start byte
$end    = $size - 1;       // End byte

header('Content-type: video/mp4');
header("Accept-Ranges: 0-$length");
if (isset($_SERVER['HTTP_RANGE'])) {

    $c_start = $start;
    $c_end   = $end;

    list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
    if (strpos($range, ',') !== false) {
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        header("Content-Range: bytes $start-$end/$size");
        exit;
    }
    if ($range == '-') {
        $c_start = $size - substr($range, 1);
    }else{
        $range  = explode('-', $range);
        $c_start = $range[0];
        $c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
    }
    $c_end = ($c_end > $end) ? $end : $c_end;
    if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
        header('HTTP/1.1 416 Requested Range Not Satisfiable');
        header("Content-Range: bytes $start-$end/$size");
        exit;
    }
    $start  = $c_start;
    $end    = $c_end;
    $length = $end - $start + 1;
    fseek($fp, $start);
    header('HTTP/1.1 206 Partial Content');
}
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: ".$length);


$buffer = 1024 * 8;
while(!feof($fp) && ($p = ftell($fp)) <= $end) {

    if ($p + $buffer > $end) {
        $buffer = $end - $p + 1;
    }
    set_time_limit(0);
    echo fread($fp, $buffer);
    flush();
}

fclose($fp);
exit();
?>
27
Jubair

Les Iphones utilisent ce que l'on appelle des plages d'octets pour les requêtes audio et vidéo. Voir ce lien pour une solution. C'est à l'annexe A.

http://mobiforge.com/developing/story/content-delivery-mobile-devices

14
sreimer

Voici un code coupé qui fera ce que vous voulez ( à partir de cette question ). La solution PHP semble plus élégante, et elle ajoute une solution plus efficace qui pourrait fonctionner en utilisant le serveur Web pour servir le contenu.

<?php

$path = 'file.mp4';

$size=filesize($path);

$fm=@fopen($path,'rb');
if(!$fm) {
  // You can also redirect here
  header ("HTTP/1.0 404 Not Found");
  die();
}

$begin=0;
$end=$size;

if(isset($_SERVER['HTTP_RANGE'])) {
  if(preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) {
    $begin=intval($matches[0]);
    if(!empty($matches[1])) {
      $end=intval($matches[1]);
    }
  }
}

if($begin>0||$end<$size)
  header('HTTP/1.0 206 Partial Content');
else
  header('HTTP/1.0 200 OK');

header("Content-Type: video/mp4");
header('Accept-Ranges: bytes');
header('Content-Length:'.($end-$begin));
header("Content-Disposition: inline;");
header("Content-Range: bytes $begin-$end/$size");
header("Content-Transfer-Encoding: binary\n");
header('Connection: close');

$cur=$begin;
fseek($fm,$begin,0);

while(!feof($fm)&&$cur<$end&&(connection_status()==0))
{ print fread($fm,min(1024*16,$end-$cur));
  $cur+=1024*16;
  usleep(1000);
}
die();

Plus de performances

Notez que ce n'est pas le moyen le plus efficace de le faire, car tout le fichier doit passer par PHP, vous n'aurez donc qu'à essayer comment cela se passe pour vous.

En supposant que la raison pour laquelle vous souhaitez le faire est de restreindre l'accès et que vous ayez besoin de plus d'efficacité plus tard, vous pouvez utiliser un indicateur pour le serveur Web.

Apache avec le module X-Sendfile ou lightty ( info nginx ici )

$path = 'file.mp4';
header("X-Sendfile: $path");
die();

C'est un peu plus avancé et vous ne devriez l'utiliser que si vous en avez besoin, mais il est relaxant de savoir que vous avez une option de mise à niveau lorsque vous commencez avec quelque chose qui est plutôt facile mais qui a des performances médiocres.

6
cmc

Ce code était très pratique pour moi, mais j'ai rencontré des problèmes parce que j'utilise des variables de session et PHP met en file d'attente l'accès aux sessions. Si une vidéo était en cours de chargement, toutes AJAX les requêtes étaient impossibles, etc. Assurez-vous donc d'appeler session_write_close() avant de démarrer la sortie.

2
jefftimesten

Oui, c'est facile à faire. Pas besoin de définir ces en-têtes manuellement. Laissez le serveur le faire automatiquement.

Voici un script de travail -

ob_start();

if( isset($_SERVER['HTTP_RANGE']) )

$opts['http']['header']="Range: ".$_SERVER['HTTP_RANGE'];

$opts['http']['method']= "HEAD";

$conh=stream_context_create($opts);

$opts['http']['method']= "GET";

$cong= stream_context_create($opts);

$out[]= file_get_contents($real_file_location_path_or_url,false,$conh);

$out[]= $http_response_header;

ob_end_clean();

array_map("header",$http_response_header);

readfile($real_file_location_path_or_url,false,$cong);
1