leftypol/inc/lib/webm/ffmpeg.php

160 lines
4.4 KiB
PHP
Raw Normal View History

<?php
/*
2020-08-30 22:02:53 +00:00
* ffmpeg.php
* A barebones ffmpeg based webm implementation for vichan.
*/
2020-10-19 23:01:15 -04:00
function get_webm_info($filename) {
2024-09-12 00:23:16 +02:00
global $config;
2020-08-30 22:02:53 +00:00
$filename = escapeshellarg($filename);
$ffprobe = $config['webm']['ffprobe_path'];
2024-09-12 00:23:16 +02:00
$ffprobe_out = [];
$webminfo = [];
2020-10-19 23:01:15 -04:00
2020-08-30 22:02:53 +00:00
exec("$ffprobe -v quiet -print_format json -show_format -show_streams $filename", $ffprobe_out);
$ffprobe_out = json_decode(implode("\n", $ffprobe_out), 1);
2021-01-27 20:09:18 -06:00
$validcheck = is_valid_webm($ffprobe_out);
$webminfo['error'] = $validcheck['error'];
2024-09-12 00:23:16 +02:00
if (empty($webminfo['error'])) {
2021-01-27 20:09:18 -06:00
$trackmap = $validcheck['trackmap'];
$videoidx = $trackmap['videoat'][0];
$webminfo['width'] = $ffprobe_out['streams'][$videoidx]['width'];
$webminfo['height'] = $ffprobe_out['streams'][$videoidx]['height'];
2020-08-30 22:02:53 +00:00
$webminfo['duration'] = $ffprobe_out['format']['duration'];
}
return $webminfo;
}
2021-01-27 20:09:18 -06:00
function locate_webm_tracks($ffprobe_out) {
2024-09-12 00:23:16 +02:00
$streams = $ffprobe_out['streams'];
$streamcount = count($streams);
$video_at = [];
$audio_at = [];
$others = [];
for ($k = 0; $k < $streamcount; $k++) {
$stream = $streams[$k];
$codec_type = $stream['codec_type'];
if ($codec_type === 'video') {
$video_at[] = $k;
} elseif ($codec_type === 'audio') {
$audio_at[] = $k;
} else {
if (isset($others[$codec_type])) {
$others[$codec_type][] = $k;
} else {
$others[$codec_type] = [$k];
}
}
}
return [ 'videoat' => $video_at, 'audioat' => $audio_at, 'others' => $others ];
2021-01-27 20:09:18 -06:00
}
function is_valid_webm($ffprobe_out) {
2024-09-12 00:23:16 +02:00
global $config;
if (empty($ffprobe_out)) {
return [ 'error' => [ 'code' => 1, 'msg' => $config['error']['genwebmerror'] ] ];
}
2021-01-27 20:09:18 -06:00
$trackmap = locate_webm_tracks($ffprobe_out);
// one video track
2024-09-12 00:23:16 +02:00
if (count($trackmap['videoat']) != 1) {
return [
'error' => [
'code' => 2,
'msg' => $config['error']['invalidwebm'] . ' [video track count]'
]
];
}
2021-01-27 20:09:18 -06:00
$videoidx = $trackmap['videoat'][0];
2020-08-30 22:02:53 +00:00
$extension = pathinfo($ffprobe_out['format']['filename'], PATHINFO_EXTENSION);
if ($extension === 'webm' && !stristr($ffprobe_out['format']['format_name'], 'mp4')) {
2024-09-12 00:23:16 +02:00
if ($ffprobe_out['format']['format_name'] != 'matroska,webm') {
return [
'error' => [
'code' => 2,
'msg' => $config['error']['invalidwebm'] . 'error 1'
]
];
}
2020-08-30 22:03:31 +00:00
} elseif ($extension === 'mp4' || stristr($ffprobe_out['format']['format_name'], 'mp4')) {
2024-09-12 00:23:16 +02:00
// If the video is not h264 or (there is audio but it's not aac).
if (($ffprobe_out['streams'][$videoidx]['codec_name'] != 'h264') || ((count($trackmap['audioat']) > 0) && ($ffprobe_out['streams'][$trackmap['audioat'][0]]['codec_name'] != 'aac'))) {
return [
'error' => [
'code' => 2,
'msg' => $config['error']['invalidwebm'] . ' [h264/aac check]'
]
];
}
2020-08-30 22:02:53 +00:00
} else {
2024-09-12 00:23:16 +02:00
return [
'error' => [
'code' => 1,
'msg' => $config['error']['genwebmerror'] . 'error 3'
]
];
}
if ((count($ffprobe_out['streams']) > 1) && (!$config['webm']['allow_audio'])) {
return [
'error' => [
'code' => 3,
'msg' => $config['error']['webmhasaudio'] . 'error 4'
]
];
2020-08-30 22:02:53 +00:00
}
2021-01-27 20:09:18 -06:00
if ((count($trackmap['audioat']) > 0) && !$config['webm']['allow_audio']) {
2024-09-12 00:23:16 +02:00
return [
'error' => [
'code' => 3,
'msg' => $config['error']['webmhasaudio'] . 'error 5'
]
];
}
if ($ffprobe_out['format']['duration'] > $config['webm']['max_length']) {
return [
'error' => [
'code' => 4,
'msg' => sprintf($config['error']['webmtoolong'], $config['webm']['max_length']) . 'error 6'
]
];
}
2024-09-12 00:23:16 +02:00
return [
'error' => [],
'trackmap' => $trackmap
];
}
2024-09-12 00:23:16 +02:00
2015-08-10 22:13:42 -03:00
function make_webm_thumbnail($filename, $thumbnail, $width, $height, $duration) {
2024-09-12 00:23:16 +02:00
global $config;
2020-08-30 22:02:53 +00:00
$filename = escapeshellarg($filename);
2024-09-12 00:23:16 +02:00
// Should be safe by default but you can never be too safe.
$thumbnailfc = escapeshellarg($thumbnail);
// Same as above.
2020-08-30 22:02:53 +00:00
$width = escapeshellarg($width);
2024-09-12 00:23:16 +02:00
$height = escapeshellarg($height);
2020-08-30 22:02:53 +00:00
$ffmpeg = $config['webm']['ffmpeg_path'];
$ret = 0;
2024-09-12 00:23:16 +02:00
$ffmpeg_out = [];
2020-08-30 22:02:53 +00:00
exec("$ffmpeg -strict -2 -ss " . floor($duration / 2) . " -i $filename -v quiet -an -vframes 1 -f mjpeg -vf scale=$width:$height $thumbnailfc 2>&1", $ffmpeg_out, $ret);
2024-09-12 00:23:16 +02:00
// Work around for https://trac.ffmpeg.org/ticket/4362.
2020-08-30 22:02:53 +00:00
if (filesize($thumbnail) === 0) {
2024-09-12 00:23:16 +02:00
// Try again with first frame.
2020-08-30 22:02:53 +00:00
exec("$ffmpeg -y -strict -2 -ss 0 -i $filename -v quiet -an -vframes 1 -f mjpeg -vf scale=$width:$height $thumbnailfc 2>&1", $ffmpeg_out, $ret);
clearstatcache();
2024-09-12 00:23:16 +02:00
// Failed if no thumbnail size even if ret code 0, ffmpeg is buggy.
2020-08-30 22:02:53 +00:00
if (filesize($thumbnail) === 0) {
$ret = 1;
}
}
return $ret;
2021-01-27 20:09:18 -06:00
}