FFmpeg support for WebMs. Fixes threads with large amounts of WebM's causing crashes for some users.
This commit is contained in:
parent
c1ecef3772
commit
0a9de3deb5
@ -20,6 +20,16 @@ Add these lines to inc/instance-config.php:
|
||||
$config['additional_javascript'][] = 'js/webm-settings.js';
|
||||
$config['additional_javascript'][] = 'js/expand-video.js';
|
||||
|
||||
If you have an [FFmpeg](https://www.ffmpeg.org/) binary on your server and you wish to generate real thumbnails (the webm thumbnails created with the original implementation reportedly cause users' browsers to crash), add the following to inc/instance-config.php as well:
|
||||
|
||||
$config['webm']['use_ffmpeg'] = true;
|
||||
|
||||
// If your ffmpeg binary isn't in your path you need to set these options
|
||||
// as well.
|
||||
|
||||
$config['webm']['ffmpeg_path'] = '/path/to/ffmeg';
|
||||
$config['webm']['ffprobe_path'] = '/path/to/ffprobe';
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
|
58
inc/lib/webm/ffmpeg.php
Normal file
58
inc/lib/webm/ffmpeg.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/*
|
||||
* ffmpeg.php
|
||||
* A barebones ffmpeg based webm implementation for vichan.
|
||||
*/
|
||||
|
||||
function get_webm_info($filename) {
|
||||
global $board, $config;
|
||||
|
||||
$filename = escapeshellarg($filename);
|
||||
$ffprobe = $config['webm']['ffprobe_path'];
|
||||
$ffprobe_out = array();
|
||||
$webminfo = array();
|
||||
|
||||
exec("$ffprobe -v quiet -print_format json -show_format -show_streams $filename", $ffprobe_out);
|
||||
$ffprobe_out = json_decode(implode("\n", $ffprobe_out), 1);
|
||||
$webminfo['error'] = is_valid_webm($ffprobe_out);
|
||||
|
||||
if(empty($webminfo['error'])) {
|
||||
$webminfo['width'] = $ffprobe_out['streams'][0]['width'];
|
||||
$webminfo['height'] = $ffprobe_out['streams'][0]['height'];
|
||||
}
|
||||
|
||||
return $webminfo;
|
||||
}
|
||||
|
||||
function is_valid_webm($ffprobe_out) {
|
||||
global $board, $config;
|
||||
|
||||
if (empty($ffprobe_out))
|
||||
return array('code' => 1, 'msg' => $config['error']['genwebmerror']);
|
||||
|
||||
if ($ffprobe_out['format']['format_name'] != 'matroska,webm')
|
||||
return array('code' => 2, 'msg' => $config['error']['invalidwebm']);
|
||||
|
||||
if ((count($ffprobe_out['streams']) > 1) && (!$config['webm']['allow_audio']))
|
||||
return array('code' => 3, 'msg' => $config['error']['webmhasaudio']);
|
||||
|
||||
if ($ffprobe_out['streams'][0]['codec_name'] != 'vp8')
|
||||
return array('code' => 2, 'msg' => $config['error']['invalidwebm']);
|
||||
|
||||
if (empty($ffprobe_out['streams'][0]['width']) || (empty($ffprobe_out['streams'][0]['height'])))
|
||||
return array('code' => 2, 'msg' => $config['error']['invalidwebm']);
|
||||
|
||||
if ($ffprobe_out['format']['duration'] > $config['webm']['max_length'])
|
||||
return array('code' => 4, 'msg' => $config['error']['webmtoolong']);
|
||||
}
|
||||
|
||||
function make_webm_thumbnail($filename, $thumbnail, $width, $height) {
|
||||
global $board, $config;
|
||||
|
||||
$ffmpeg = $config['webm']['ffmpeg_path'];
|
||||
$ffmpeg_out = array();
|
||||
|
||||
exec("$ffmpeg -i $filename -v quiet -ss 00:00:00 -an -vframes 1 -f mjpeg -vf scale=$width:$height $thumbnail 2>&1");
|
||||
|
||||
return count($ffmpeg_out);
|
||||
}
|
@ -6,6 +6,34 @@ function postHandler($post) {
|
||||
global $board, $config;
|
||||
|
||||
if ($post->has_file) foreach ($post->files as &$file) if ($file->extension == 'webm') {
|
||||
if ($config['webm']['use_ffmpeg']) {
|
||||
require_once dirname(__FILE__) . '/ffmpeg.php';
|
||||
$webminfo = get_webm_info($file->file_path);
|
||||
|
||||
if (empty($webminfo['error'])) {
|
||||
$file->width = $webminfo['width'];
|
||||
$file->height = $webminfo['height'];
|
||||
|
||||
if ($config['spoiler_images'] && isset($_POST['spoiler'])) {
|
||||
$file = webm_set_spoiler($file);
|
||||
}
|
||||
else {
|
||||
$file = set_thumbnail_dimensions($post, $file);
|
||||
$tn_path = $board['dir'] . $config['dir']['thumb'] . $file->file_id . '.jpg';
|
||||
|
||||
if(empty(make_webm_thumbnail($file->file_path, $tn_path, $file->thumbwidth, $file->thumbheight))) {
|
||||
$file->thumb = $file->file_id . '.jpg';
|
||||
}
|
||||
else {
|
||||
$file->thumb = 'file';
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $webminfo['error']['msg'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
require_once dirname(__FILE__) . '/videodata.php';
|
||||
$videoDetails = videoData($file->file_path);
|
||||
if (!isset($videoDetails['container']) || $videoDetails['container'] != 'webm') return "not a WebM file";
|
||||
@ -14,10 +42,7 @@ function postHandler($post) {
|
||||
$thumbName = $board['dir'] . $config['dir']['thumb'] . $file->file_id . '.webm';
|
||||
if ($config['spoiler_images'] && isset($_POST['spoiler'])) {
|
||||
// Use spoiler thumbnail
|
||||
$file->thumb = 'spoiler';
|
||||
$size = @getimagesize($config['spoiler_image']);
|
||||
$file->thumbwidth = $size[0];
|
||||
$file->thumbheight = $size[1];
|
||||
$file = webm_set_spoiler($file);
|
||||
} elseif (isset($videoDetails['frame']) && $thumbFile = fopen($thumbName, 'wb')) {
|
||||
// Use single frame from video as pseudo-thumbnail
|
||||
fwrite($thumbFile, $videoDetails['frame']);
|
||||
@ -33,17 +58,40 @@ function postHandler($post) {
|
||||
if (isset($videoDetails['width']) && isset($videoDetails['height'])) {
|
||||
$file->width = $videoDetails['width'];
|
||||
$file->height = $videoDetails['height'];
|
||||
|
||||
if ($file->thumb != 'file' && $file->thumb != 'spoiler') {
|
||||
$thumbMaxWidth = $post->op ? $config['thumb_op_width'] : $config['thumb_width'];
|
||||
$thumbMaxHeight = $post->op ? $config['thumb_op_height'] : $config['thumb_height'];
|
||||
if ($videoDetails['width'] > $thumbMaxWidth || $videoDetails['height'] > $thumbMaxHeight) {
|
||||
$file->thumbwidth = min($thumbMaxWidth, intval(round($videoDetails['width'] * $thumbMaxHeight / $videoDetails['height'])));
|
||||
$file->thumbheight = min($thumbMaxHeight, intval(round($videoDetails['height'] * $thumbMaxWidth / $videoDetails['width'])));
|
||||
} else {
|
||||
$file->thumbwidth = $videoDetails['width'];
|
||||
$file->thumbheight = $videoDetails['height'];
|
||||
$file = set_thumbnail_dimensions($post, $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function set_thumbnail_dimensions($post,$file) {
|
||||
global $board, $config;
|
||||
|
||||
$tn_dimensions = array();
|
||||
$tn_maxw = $post->op ? $config['thumb_op_width'] : $config['thumb_width'];
|
||||
$tn_maxh = $post->op ? $config['thumb_op_height'] : $config['thumb_height'];
|
||||
|
||||
if ($file->width > $tn_maxw || $file->height > $tn_maxh) {
|
||||
$file->thumbwidth = min($tn_maxw, intval(round($file->width * $tn_maxh / $file->height)));
|
||||
$file->thumbheight = min($tn_maxh, intval(round($file->height * $tn_maxw / $file->width)));
|
||||
} else {
|
||||
$file->thumbwidth = $file->width;
|
||||
$file->thumbheight = $file->height;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
function webm_set_spoiler($file) {
|
||||
global $board, $config;
|
||||
|
||||
$file->thumb = 'spoiler';
|
||||
$size = @getimagesize($config['spoiler_image']);
|
||||
$file->thumbwidth = $size[0];
|
||||
$file->thumbheight = $size[1];
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user