#!/usr/bin/php = $_set->loglevel) echo "[".date('Y-m-d H:i:s')."]".str_repeat("\t", $tabs)."$txt\n"; if ($exit) exit; return true; } function is_dir_empty($dir) { if (!is_readable($dir)) return NULL; return (count(scandir($dir)) == 2); } function notify($sub, $msg, $loglevel = 1) { global $_set; if (is_array($_set->mail) && !empty($_set->mail)) foreach ($_set->mail AS $mail) if (!@mail($mail, "=?UTF-8?B?".base64_encode('[hls-recorder] '.$sub)."?=", $msg, "Content-type: text/plain; charset=utf-8")) logger($loglevel, 'Unable to send notification E-mail'); } // Check requirements if (!function_exists('curl_init')) logger(1, 'cURL is required but not available', 1, true); if (!function_exists('json_decode')) logger(1, 'json_decode is required but not available', 1, true); // Settings $config = isset($opts['c']) ? $opts['c'] : __DIR__.'/config.json'; if (!file_exists($config)) logger(1, 'Config file not found: '.$config, 1, true); if (!$_set = @json_decode(@file_get_contents($config))) logger(1, 'Unable to load or parse configuration file', 1, true); // Check settings $_set_def = [ 'rtmp' => '', 'recordhls' => false ]; foreach ($_set_def AS $i => $v) if (!isset($_set->{$i})) $_set->{$i} = $v; // Loop indefinitely while (true) : if ($api = @file_get_contents($_set->api.'/states')) if ($api = @json_decode($api)) if (isset($api->repeat_to_local_nginx) && $api->repeat_to_local_nginx->type == 'connected') { logger(1, 'Repeater seems to be connected'); $handle = curl_init($_set->hls); curl_setopt($handle, CURLOPT_RETURNTRANSFER, TRUE); $response = curl_exec($handle); $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); curl_close($handle); if ($httpCode == 200) { $output_directory = $_set->vod.'/vod-'.date('Ymd-His'); $exitcode = 0; if (@mkdir($output_directory)) { logger(2, 'All checks passed, starting ffmpeg'); notify('Recording', 'Restreamer API reported that repeating to local nginx is connected and the HLS endpoint is found. The HLS output is now being recorded. HLS URL is the following: '.$_set->hls.', Output directory is: '.$output_directory, 2); if ($_set->recordhls || !$_set->rtmp) exec('flock -n '.$_set->pid.' -c "ffmpeg -re -i '.$_set->hls.' -c copy -hls_list_size 0 '.$output_directory.'/index.m3u8 2> '.$output_directory.'/ffmpeg.log"', $output, $exitcode); else exec('flock -n '.$_set->pid.' -c "ffmpeg -re -i '.$_set->rtmp.' -c copy -start_number 0 -hls_time 10 -hls_list_size 0 -f hls '.$output_directory.'/index.m3u8 2> '.$output_directory.'/ffmpeg.log"', $output, $exitcode); logger(2, 'ffmpeg exited with code '.$exitcode); notify('Recording stopped', 'The FFMPEG process shut down with exit code: '.$exitcode, 2); if ($exitcode) { logger(3, 'Non-clean exit (expected), checking if output directory is empty'); if (is_dir_empty($output_directory)) { if (@rmdir($output_directory)) logger(4, 'Output directory removed'); else { logger(4, 'Unable to remove output directory'); notify('Error', 'The FFMPEG process shut down with exit code '.$exitcode.' and the output directory is empty (nothing recorded). Output directory should have been removed automatically, but the process failed. Remove the output directory manually: '.$output_directory, 4); } } } if (file_exists($output_directory.'/index.m3u8')) { exec('ffmpeg -i '.$output_directory.'/index.m3u8 -acodec copy -vcodec copy '.$_set->vod.'/'.basename($output_directory).'.mp4'); if (file_exists($_set->vod.'/'.basename($output_directory).'.mp4')) { logger(4, 'Conversion to single file successful'); notify('Conversion successful', 'The HLS stream is now available as an MP4 file here: '.$_set->vod.'/'.basename($output_directory).'.mp4', 4); } else { logger(4, 'Conversion to single file failed'); notify('Conversion unsuccessful', 'The output file of FFMPEG does not exists. This is an indiction that the conversion failed.', 4); } } } else logger(3, 'Unable to create output directory'); } else logger(2, 'HLS inaccessible, HTTP Code '.$httpCode); } else logger(1, 'Repeater not in connected state', 0); else logger(1, 'API response unreadable'); else logger(1, 'API can not be accessed'); sleep($_set->sleep); endwhile;