From bae6662e37c96b5d922e818c195adba44f547cce Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 9 Nov 2022 02:26:51 -0800 Subject: [PATCH] Start transcoding server --- lib/Command/VideoSetup.php | 19 ++++++++-- lib/Controller/VideoController.php | 50 ++++++++++++++++++++----- scripts/bundle.sh | 2 +- transcoder.yaml | 59 ++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 transcoder.yaml diff --git a/lib/Command/VideoSetup.php b/lib/Command/VideoSetup.php index a4c5630f..abdb0eb3 100644 --- a/lib/Command/VideoSetup.php +++ b/lib/Command/VideoSetup.php @@ -50,8 +50,14 @@ class VideoSetup extends Command protected function execute(InputInterface $input, OutputInterface $output): int { + // Check nohup binary + $nohup = shell_exec('nohup --version'); + if (!$nohup || strpos($nohup, 'nohup') === false) { + $output->writeln('nohup binary not found. Please install nohup.'); + return $this->suggestDisable($output); + } + // Get ffmpeg version - $output->writeln('Checking for ffmpeg binary'); $ffmpeg = shell_exec('ffmpeg -version'); if (false === strpos($ffmpeg, 'ffmpeg version')) { $ffmpeg = null; @@ -61,7 +67,6 @@ class VideoSetup extends Command } // Get ffprobe version - $output->writeln('Checking for ffprobe binary'); $ffprobe = shell_exec('ffprobe -version'); if (false === strpos($ffprobe, 'ffprobe version')) { $ffprobe = null; @@ -102,8 +107,11 @@ class VideoSetup extends Command $output->writeln('go-transcode is installed!'); $output->writeln(''); $output->writeln('You can use transcoding and HLS streaming'); - $output->writeln('This is recommended for better performance, but not for very slow systems'); - $output->writeln('For more details see: https://github.com/pulsejet/memories/wiki/Configuration'); + $output->writeln('This is recommended for better performance, but has implications if'); + $output->writeln('you are using external storage or run Nextcloud on a slow system.'); + $output->writeln(''); + $output->writeln('Read the following documentation carefully before continuing:'); + $output->writeln('https://github.com/pulsejet/memories/wiki/Configuration'); $output->writeln(''); $output->writeln('Do you want to enable transcoding and HLS? [Y/n]'); @@ -115,7 +123,10 @@ class VideoSetup extends Command return 0; } + $tConfig = realpath(__DIR__."/../../transcoder.yaml"); + $this->config->setSystemValue('memories.transcoder', $goTranscodePath); + $this->config->setSystemValue('memories.transcoder_config', $tConfig); $this->config->setSystemValue('memories.no_transcode', false); $output->writeln('Transcoding and HLS are now enabled!'); diff --git a/lib/Controller/VideoController.php b/lib/Controller/VideoController.php index 72f90fdc..463860a5 100644 --- a/lib/Controller/VideoController.php +++ b/lib/Controller/VideoController.php @@ -49,7 +49,7 @@ class VideoController extends ApiBase } // Make sure not running in read-only mode - if ($this->config->getSystemValue('memories.no_transcode', false)) { + if ($this->config->getSystemValue('memories.no_transcode', 'UNSET') !== false) { return new JSONResponse(['message' => 'Transcoding disabled'], Http::STATUS_FORBIDDEN); } @@ -71,15 +71,35 @@ class VideoController extends ApiBase return new JSONResponse(['message' => 'File not found'], Http::STATUS_NOT_FOUND); } + // Check if file starts with temp dir + $tmpDir = sys_get_temp_dir(); + if (0 === strpos($path, $tmpDir)) { + return new JSONResponse(['message' => 'File is in temp dir!'], Http::STATUS_NOT_FOUND); + } + // Make upstream request - $ch = curl_init("http://localhost:9999/vod/{$path}/{$profile}"); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); - curl_setopt($ch, CURLOPT_HEADER, 0); - $data = curl_exec($ch); - $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); - $returnCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); + [$data, $contentType, $returnCode] = $this->getUpstream($path, $profile); + + // If status code was 0, it's likely the server is down + // Make one attempt to start if we can't find the process + if (0 === $returnCode) { + $transcoder = $this->config->getSystemValue('memories.transcoder', false); + $tConfig = $this->config->getSystemValue('memories.transcoder_config', false); + if (!$transcoder || !$tConfig) { + return new JSONResponse(['message' => 'Transcoder not configured'], Http::STATUS_INTERNAL_SERVER_ERROR); + } + + // Check if already running + exec('ps a | grep go-transcode | grep -v grep', $procs); + if (0 === \count($procs)) { + shell_exec("mkdir -p $tmpDir/transcoder"); // php func has some weird problems + shell_exec("nohup $transcoder serve --config $tConfig > $tmpDir/transcoder/run.log 2>&1 & > /dev/null"); + } + + // wait for 2s and try again + sleep(2); + [$data, $contentType, $returnCode] = $this->getUpstream($path, $profile); + } // Check data was received if ($returnCode >= 400 || false === $data) { @@ -94,4 +114,16 @@ class VideoController extends ApiBase return $response; } + + private function getUpstream($path, $profile) { + $ch = curl_init("http://localhost:47788/vod/{$path}/{$profile}"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($ch, CURLOPT_HEADER, 0); + $data = curl_exec($ch); + $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); + $returnCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + return [$data, $contentType, $returnCode]; + } } diff --git a/scripts/bundle.sh b/scripts/bundle.sh index 39e33859..03c634fb 100755 --- a/scripts/bundle.sh +++ b/scripts/bundle.sh @@ -4,7 +4,7 @@ od=`pwd` rm -rf /tmp/memories mkdir -p /tmp/memories -cp -R appinfo l10n img js lib templates COPYING README.md exiftest* composer* /tmp/memories +cp -R appinfo l10n img js lib templates COPYING README.md transcoder.yaml exiftest* composer* /tmp/memories cd /tmp rm -f memories/appinfo/screencap* memories/js/*.map diff --git a/transcoder.yaml b/transcoder.yaml new file mode 100644 index 00000000..063b40e7 --- /dev/null +++ b/transcoder.yaml @@ -0,0 +1,59 @@ +# allow debug outputs +debug: false + +# mount debug pprof endpoint at /debug/pprof/ +pprof: false + +# bind server to IP:PORT (use :47788 for all connections) +# DO NOT expose this port to the world +bind: localhost:47788 + +# X-Forwarded-For headers will be used to determine the client IP +proxy: true + +# For static files +vod: + # Root directory for media + media-dir: / + + # Temporary transcode output directory, if empty, default tmp folder will be used + transcode-dir: /tmp/transcoder/data + + # Available video profiles + # Do not change these + video-profiles: + 360p: + width: 640 # px + height: 360 # px + bitrate: 800 # kbps + 480p: + width: 640 + height: 480 + bitrate: 1200 + 720p: + width: 1280 + height: 720 + bitrate: 2800 + 1080p: + width: 1920 + height: 1080 + bitrate: 5000 + + # Use video keyframes as existing reference for chunks split + # Using this might cause long probing times in order to get + # all keyframes - therefore they should be cached + video-keyframes: false + + # Single audio profile used + audio-profile: + bitrate: 192 # kbps + + # If cache is enabled + cache: true + # If dir is empty, cache will be stored in the same directory as media source + # If not empty, cache files will be saved to specified directory + cache-dir: /tmp/transcoder/cache + + # OPTIONAL: Use custom ffmpeg & ffprobe binary paths + ffmpeg-binary: ffmpeg + ffprobe-binary: ffprobe