2021-05-21 03:35:51 +02:00
#!/usr/bin/php
< ? php
/* -------------------------------------------------------------- */
2021-05-28 09:12:15 +02:00
// Options
2021-05-28 09:21:36 +02:00
$opts = getopt ( 'c:' );
2021-05-28 09:12:15 +02:00
2021-05-21 03:35:51 +02:00
// 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 )
exit ;
return true ;
}
2021-05-21 15:50:37 +02:00
function is_dir_empty ( $dir )
{
2021-05-21 03:35:51 +02:00
if ( ! is_readable ( $dir )) return NULL ;
return ( count ( scandir ( $dir )) == 2 );
}
2021-05-21 15:50:37 +02:00
2021-05-21 03:35:51 +02:00
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' );
}
2021-05-21 15:50:37 +02:00
// 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
2021-05-28 09:19:15 +02:00
$config = isset ( $opts [ 'c' ]) ? $opts [ 'c' ] : __DIR__ . '/config.json' ;
if ( ! file_exists ( $config ))
2021-05-28 09:18:26 +02:00
logger ( 1 , 'Config file not found: ' . $config , 1 , true );
2021-05-28 09:27:44 +02:00
if ( ! $_set = @ json_decode ( @ file_get_contents ( $config )))
2021-05-21 15:50:37 +02:00
logger ( 1 , 'Unable to load or parse configuration file' , 1 , true );
2021-05-28 10:25:40 +02:00
// Check settings
$_set_def = [
'rtmp' => '' ,
'recordhls' => false
];
foreach ( $_set_def AS $i => $v )
if ( ! isset ( $_set -> { $i }))
$_set -> { $i } = $v ;
2021-05-21 03:35:51 +02:00
// 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' );
2021-05-28 10:25:40 +02:00
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 )
2021-05-29 16:32:17 +02:00
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 );
2021-05-28 10:25:40 +02:00
else
2021-05-29 16:32:17 +02:00
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 );
2021-05-21 03:35:51 +02:00
logger ( 2 , 'ffmpeg exited with code ' . $exitcode );
2021-05-28 10:25:40 +02:00
notify ( 'Recording stopped' , 'The FFMPEG process shut down with exit code: ' . $exitcode , 2 );
2021-05-21 03:35:51 +02:00
if ( $exitcode )
{
logger ( 3 , 'Non-clean exit (expected), checking if output directory is empty' );
if ( is_dir_empty ( $output_directory ))
2022-06-22 09:09:14 +02:00
{
2021-05-21 03:35:51 +02:00
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 );
}
2022-06-22 09:09:14 +02:00
}
}
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 );
}
2021-05-21 03:35:51 +02:00
}
} 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' );
2021-05-21 15:55:31 +02:00
sleep ( $_set -> sleep );
2021-05-21 03:35:51 +02:00
endwhile ;