extended mp4 support and rendered in html like imgur does with "gifv"

This commit is contained in:
Christian Haschek
2015-11-25 10:11:47 +01:00
parent 2a04abdc3a
commit f8c75f418d
4 changed files with 267 additions and 13 deletions

View File

@@ -42,5 +42,7 @@ else if($_REQUEST['base64'])
}
else if($_REQUEST['geturlinfo'])
echo json_encode($pm->getURLInfo($_REQUEST['geturlinfo']));
else if($_REQUEST['a']=='oembed')
echo json_encode($pm->oembed($_REQUEST['url'],$_REQUEST['t']));
else
echo json_encode(array('status'=>'ERR'));

View File

@@ -165,15 +165,27 @@ function renderImage($data)
case 'mp4':
if(!$cached)
{
$path = $pm->resizeMP4($data,$cachepath);
$pm->resizeMP4($data,$cachepath);
$path = $cachepath;
}
header('Content-type: video/mp4');
//header('Content-type: video/mpeg');
header('Content-disposition: inline');
header("Content-Transfer-Encoding:­ binary");
header("Content-Length: ".filesize($path));
readfile($path);
if(filesize($path)==0) //if there was an error and the file is 0 bytes, use the original
$path = ROOT.DS.'upload'.DS.$hash.DS.$hash;
if($data['raw'])
{
serveFile($path, '/raw/'.$hash,'video/mp4');
}
else if($data['preview'])
{
$file = $path.'.jpg';
if(!file_exists($file))
$pm->saveFirstFrameOfMP4($path);
header ("Content-type: image/jpeg");
readfile($file);
}
else
renderMP4($path,$data);
break;
}
@@ -202,3 +214,91 @@ function render($variables=null)
extract($variables);
include (ROOT . DS . 'template.php');
}
function renderMP4($path,$data)
{
$pm = new PictshareModel;
$hash = $data['hash'];
if($data['size'])
$hash = $data['size'].'/'.$hash;
$info = $pm->getSizeOfMP4($path);
$width = $info['width'];
$height = $info['height'];
include (ROOT . DS . 'template_mp4.php');
}
//
// from: https://stackoverflow.com/questions/25975943/php-serve-mp4-chrome-provisional-headers-are-shown-request-is-not-finished-ye
//
function serveFile($filename, $filename_output = false, $mime = 'application/octet-stream')
{
$buffer_size = 8192;
$expiry = 90; //days
if(!file_exists($filename))
{
throw new Exception('File not found: ' . $filename);
}
if(!is_readable($filename))
{
throw new Exception('File not readable: ' . $filename);
}
header_remove('Cache-Control');
header_remove('Pragma');
$byte_offset = 0;
$filesize_bytes = $filesize_original = filesize($filename);
header('Accept-Ranges: bytes', true);
header('Content-Type: ' . $mime, true);
if($filename_output)
{
header('Content-Disposition: attachment; filename="' . $filename_output . '"');
}
// Content-Range header for byte offsets
if (isset($_SERVER['HTTP_RANGE']) && preg_match('%bytes=(\d+)-(\d+)?%i', $_SERVER['HTTP_RANGE'], $match))
{
$byte_offset = (int) $match[1];//Offset signifies where we should begin to read the file
if (isset($match[2]))//Length is for how long we should read the file according to the browser, and can never go beyond the file size
{
$filesize_bytes = min((int) $match[2], $filesize_bytes - $byte_offset);
}
header("HTTP/1.1 206 Partial content");
header(sprintf('Content-Range: bytes %d-%d/%d', $byte_offset, $filesize_bytes - 1, $filesize_original)); ### Decrease by 1 on byte-length since this definition is zero-based index of bytes being sent
}
$byte_range = $filesize_bytes - $byte_offset;
header('Content-Length: ' . $byte_range);
header('Expires: ' . date('D, d M Y H:i:s', time() + 60 * 60 * 24 * $expiry) . ' GMT');
$buffer = '';
$bytes_remaining = $byte_range;
$handle = fopen($filename, 'r');
if(!$handle)
{
throw new Exception("Could not get handle for file: " . $filename);
}
if (fseek($handle, $byte_offset, SEEK_SET) == -1)
{
throw new Exception("Could not seek to byte offset %d", $byte_offset);
}
while ($bytes_remaining > 0)
{
$chunksize_requested = min($buffer_size, $bytes_remaining);
$buffer = fread($handle, $chunksize_requested);
$chunksize_real = strlen($buffer);
if ($chunksize_real == 0)
{
break;
}
$bytes_remaining -= $chunksize_real;
echo $buffer;
flush();
}
}

View File

@@ -21,8 +21,19 @@ class PictshareModel extends Model
$path = ROOT.DS.'upload'.DS.$hash.DS.$file;
if(file_exists($path))
{
$type = $this->getTypeOfHash($hash);
$byte = filesize($path);
return array('status'=>'ok','hash'=>$hash,'cachename'=>$file,'size'=>$byte,'humansize'=>$html->renderSize($byte));
if($type=='mp4')
{
$info = $this->getSizeOfMP4($path);
$width = intval($info['width']);
$height = intval($info['height']);
}
else
{
list($width, $height) = getimagesize($path);
}
return array('status'=>'ok','hash'=>$hash,'cachename'=>$file,'size'=>$byte,'humansize'=>$html->renderSize($byte),'width'=>$width,'height'=>$height,'type'=>$type);
}
else
@@ -44,8 +55,8 @@ class PictshareModel extends Model
if($this->isImage($el))
$data['hash']=$el;
else if($el=='mp4')
$data['mp4'] = 1;
else if($el=='mp4' || $el=='raw' || $el=='preview' || $el=='webm')
$data[$el] = 1;
else if($this->isSize($el))
$data['size'] = $el;
else if($this->isRotation($el))
@@ -179,6 +190,9 @@ class PictshareModel extends Model
function getCacheName($data)
{
ksort($data);
unset($data['raw']);
unset($data['preview']);
unset($data['raw']);
$name = false;
foreach($data as $key=>$val)
{
@@ -190,7 +204,6 @@ class PictshareModel extends Model
foreach($val as $valdata)
$name[] = $valdata;
}
}
if(is_array($name))
@@ -332,7 +345,14 @@ class PictshareModel extends Model
mkdir(ROOT.DS.'upload'.DS.$hash);
$file = ROOT.DS.'upload'.DS.$hash.DS.$hash;
$status = file_put_contents($file, file_get_contents($url));
if($type=='mp4')
{
$this->saveFirstFrameOfMP4($file);
}
file_put_contents($file, file_get_contents($url));
//remove all exif data from jpeg
if($type=='jpg')
@@ -679,4 +699,89 @@ class PictshareModel extends Model
return $mp4file;
}
function saveAsMP4($source,$target)
{
$bin = escapeshellcmd(ROOT.DS.'bin'.DS.'ffmpeg');
$source = escapeshellarg($source);
$target = escapeshellarg($target);
$cmd = "$bin -y -i $source -c:v libx264 $target";
system($cmd);
}
function saveFirstFrameOfMP4($path)
{
//$path = ROOT.DS.'upload'.DS.$hash.DS.$hash;
$jpgpath = $path.'.jpg';
$bin = escapeshellcmd(ROOT.DS.'bin'.DS.'ffmpeg');
$file = escapeshellarg($path);
$cmd = "$bin -y -i $file -vframes 1 -f image2 $jpgpath";
system($cmd);
}
//from https://stackoverflow.com/questions/4847752/how-to-get-video-duration-dimension-and-size-in-php
function getSizeOfMP4($video)
{
$video = escapeshellarg($video);
$bin = escapeshellcmd(ROOT.DS.'bin'.DS.'ffmpeg');
$command = $bin . ' -i ' . $video . ' -vstats 2>&1';
$output = shell_exec($command);
$regex_sizes = "/Video: ([^,]*), ([^,]*), ([0-9]{1,4})x([0-9]{1,4})/";
if (preg_match($regex_sizes, $output, $regs)) {
$codec = $regs [1] ? $regs [1] : null;
$width = $regs [3] ? $regs [3] : null;
$height = $regs [4] ? $regs [4] : null;
}
$regex_duration = "/Duration: ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}).([0-9]{1,2})/";
if (preg_match($regex_duration, $output, $regs)) {
$hours = $regs [1] ? $regs [1] : null;
$mins = $regs [2] ? $regs [2] : null;
$secs = $regs [3] ? $regs [3] : null;
$ms = $regs [4] ? $regs [4] : null;
}
return array ('codec' => $codec,
'width' => $width,
'height' => $height,
'hours' => $hours,
'mins' => $mins,
'secs' => $secs,
'ms' => $ms
);
}
function oembed($url,$type)
{
$data = $this->getURLInfo($url);
$rawurl = $url.'/raw';
switch($type)
{
case 'json':
header('Content-Type: application/json');
return array( "version"=> "1.0",
"type"=> "video",
"thumbnail_url"=>$url.'/preview',
"thumbnail_width"=>$data['width'],
"thumbnail_height"=>$data['height'],
"width"=> $data['width'],
"height"=> $data['height'],
"title"=> "PictShare",
//"url"=> $url.'/raw',
"provider_name"=> "PictShare",
"provider_url"=> DOMAINPATH,
"html"=> '<video id="video" poster="'.$url.'/preview'.'" preload="auto" autoplay="autoplay" muted="muted" loop="loop" webkit-playsinline>
<source src="'.$rawurl.'" type="video/mp4">
</video>');
break;
case 'xml':
break;
}
}
}

47
template_mp4.php Normal file
View File

@@ -0,0 +1,47 @@
<!doctype html>
<html>
<head>
<title>PictShare</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta name="copyright" content="Copyright <?php echo date("Y"); ?> PictShare" />
<meta id="viewport" name="viewport" content="width=<?php echo $width ?>, user-scalable=yes" />
<style type="text/css">
#content, #video {
width: <?php echo $width ?>px;
height: <?php echo $height ?>px;
}
</style>
<link rel="alternate" type="application/json+oembed" href="<?php echo DOMAINPATH; ?>backend.php?a=oembed&t=json&url=<?php echo rawurlencode(DOMAINPATH.$hash); ?>" title="PictShare" />
<link rel="canonical" href="<?php echo DOMAINPATH.$hash; ?>" />
<meta property="og:site_name" content="Imgur" />
<meta property="og:url" content="<?php echo DOMAINPATH.$hash; ?>" />
<meta property="og:title" content="PictShare MP4" />
<meta property="og:type" content="video.other" />
<meta property="og:image" content="<?php echo DOMAINPATH.'preview/'.$hash; ?>" />
<meta property="og:image:width" content="<?php echo $width ?>" />
<meta property="og:image:height" content="<?php echo $height ?>" />
<meta property="og:description" content="PictShare MP4 Video" />
<meta property="og:video" content="<?php echo DOMAINPATH.$hash; ?>" />
<meta property="og:video:secure_url" content="<?php echo DOMAINPATH.$hash; ?>" />
<meta property="og:video:type" content="application/x-shockwave-flash" />
<meta property="og:video:width" content="<?php echo $width ?>" />
<meta property="og:video:height" content="<?php echo $height ?>" />
<meta property="og:video:type" content="video/mp4" />
<meta property="og:video:width" content="<?php echo $width ?>" />
<meta property="og:video:height" content="<?php echo $height ?>" />
</head>
<body id="body">
<div id="content">
<video id="video" poster="<?php echo DOMAINPATH.'preview/'.$hash; ?>" preload="auto" autoplay="autoplay" muted="muted" loop="loop" webkit-playsinline>
<source src="<?php echo DOMAINPATH.'raw/'.$hash; ?>" type="video/mp4">
</video>
</div>
</body>
</html>