Browse Source

Initial commit

Sándor 6 months ago
3 changed files with 110 additions and 0 deletions
  1. +12
  2. +8
  3. +90

+ 12
- 0
README View File

@ -0,0 +1,12 @@
With this utility it is possible to record live streams that is available through a Restreamer instance.
This monitors the Restreamer API to check if the local repeater is connected. When it is, it probes the
HLS endpoint and depending on the HTTP Code in the response it starts an FFmpeg process that copies the
HLS output to a specified location on the filesystem. Then it can be played back.
It is also possible to provide a "rewindable" live stream, but keep in mind that it will introduce big
To set this up, clone this repository and move or copy config.example.json to config.json, then edit it.
Then, run the following command to start monitoring and recording streams: php -q /path/to/record.php
Create a cronjob or systemd service to make it run on boot and handle potential failures/exits.
Example cronjob: * * * * * flock -n /tmp/hls-recorder-flock -c "php -q /path/to/record.php" &

+ 8
- 0
config.example.json View File

@ -0,0 +1,8 @@
"mail":["[email protected]","[email protected]"],

+ 90
- 0
record.php View File

@ -0,0 +1,90 @@
// Settings
if (!file_exists(__DIR__.'/config.json'))
logger(1, 'Config file not found', 1, true);
if (!$_set = @json_decode(@file_get_contents(__DIR__.'/config.json')))
logger(1, 'Unable to load or parse configuration file', 1, true);
/* -------------------------------------------------------------- */
// Basic functions
function logger($tabs = 1, $txt, $lvl = 1, $exit = false)
global $_set;
if ($lvl >= $_set->loglevel)
echo "[".date('Y-m-d H:i:s')."]".str_repeat("\t", $tabs)."$txt\n";
if ($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 ([email protected]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');
// 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);
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);
exec('flock -n '.$_set->pid.' -c "ffmpeg -i '.$_set->hls.' -c copy '.$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');
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);
} 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);
logger(1, 'API response unreadable');
logger(1, 'API can not be accessed');