Add readme
This commit is contained in:
parent
e57458dcab
commit
f571c2bca7
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
|||||||
encoding.log
|
process.log
|
||||||
uploadpage/streams/*
|
uploadpage/streams/*
|
||||||
|
|||||||
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# streamupload
|
||||||
|
**Upload videos to be re-encoded and scheduled for streaming**
|
||||||
|
|
||||||
|
## Install
|
||||||
|
* Prepare a server, set its timezone to the users' timezone
|
||||||
|
(on deb-based systems: `dpkg-reconfigure tzdata`)
|
||||||
|
* On the server, `cd` to the place where you want the files
|
||||||
|
* Clone repo: `git clone https://gitlab.com/pepa65/streamupload`
|
||||||
|
* `cd streamupload`
|
||||||
|
* Change the value of the `repopath` variable in `stream` and `encode`
|
||||||
|
to the output of `echo $PWD`
|
||||||
|
* Run a php/webserver on $PWD/uploadpage
|
||||||
|
- Get it to restart on reboot
|
||||||
|
- Setting up basicauth on the page is a good idea if others can get access!
|
||||||
|
- Change the relevant `php.ini` to allow large file uploads:
|
||||||
|
* `post_max_size` - Upper limit of uploaded video sizes, say `10G`
|
||||||
|
* `upload_max_filesize` - same value as `post_max_size`
|
||||||
|
* Make a crontab-entry: "* * * * * $PWD/encode" (replace $PWD with its value!)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
* Get a streamkey for the target by scheduling a stream
|
||||||
|
(supported are: Restream.io, YouTube.com, Facebook.com)
|
||||||
|
* Go to the server's IP address in the browser: `http://$ipaddress`
|
||||||
|
* Fill in the form, click "Schedule Stream"
|
||||||
@ -1,16 +1,17 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# prep - Encode for streaming and schedule cron job
|
# encode - Encode for streaming and schedule cron job
|
||||||
# Usage: prep
|
# Usage: encode
|
||||||
# Called by cron every minute; process oldest .upload file in /var/www/uploadpage/streams)
|
# Called by cron every minute; process oldest .upload file in uploadpage/streams)
|
||||||
|
|
||||||
log=/var/www/encoding.log
|
repopath=/var/www
|
||||||
dir=/var/www/uploadpage/streams
|
log=$repopath/process.log
|
||||||
|
dir=$repopath/uploadpage/streams
|
||||||
|
|
||||||
# Check for oldest uploaded file
|
# Check for oldest uploaded file
|
||||||
upload=$(/usr/bin/ls -1tr "$dir"/*.upload 2>/dev/null |head -1)
|
upload=$(/usr/bin/ls -1tr "$dir"/*.upload 2>/dev/null |head -1)
|
||||||
|
|
||||||
# Finished if no /var/www/uploadpage/streams/*.upload files found
|
# Finished if no uploadpage/streams/*.upload files found
|
||||||
[[ $upload ]] || exit 0
|
[[ $upload ]] || exit 0
|
||||||
|
|
||||||
Log(){ # 1:message 2:returncode
|
Log(){ # 1:message 2:returncode
|
||||||
@ -18,7 +19,7 @@ Log(){ # 1:message 2:returncode
|
|||||||
# Remove source (if returncode not zero)
|
# Remove source (if returncode not zero)
|
||||||
/usr/bin/sync
|
/usr/bin/sync
|
||||||
/usr/bin/sleep 1
|
/usr/bin/sleep 1
|
||||||
(($2)) && /usr/bin/rm "$file"
|
#(($2)) && /usr/bin/rm "$file"
|
||||||
exit $2
|
exit $2
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,16 +34,16 @@ type=$(file -bL --mime-type "$file")
|
|||||||
## Single pass
|
## Single pass
|
||||||
#/usr/bin/ffmpeg -y -i "$file" -c:v libx264 -x264opts no-scenecut -b:v 6M -force_key_frames 'expr:gte(t,n_forced*2)' -c:a copy -tune zerolatency -f mp4 "$video" ||
|
#/usr/bin/ffmpeg -y -i "$file" -c:v libx264 -x264opts no-scenecut -b:v 6M -force_key_frames 'expr:gte(t,n_forced*2)' -c:a copy -tune zerolatency -f mp4 "$video" ||
|
||||||
# Double pass
|
# Double pass
|
||||||
/usr/bin/ffmpeg -y -i "$file" -c:v libx264 -x264opts no-scenecut -b:v 6M -force_key_frames 'expr:gte(t,n_forced*2)' -movflags faststart -c:a copy -tune zerolatency -pass 1 -f mp4 "$video" &&
|
/usr/bin/ffmpeg -y -i "$file" -c:v libx264 -x264opts no-scenecut -b:v 6M -maxrate 6M -bufsize 1M -force_key_frames 'expr:gte(t,n_forced*2)' -movflags faststart -c:a copy -tune zerolatency -pass 1 -f mp4 "$video" &&
|
||||||
/usr/bin/ffmpeg -y -i "$file" -c:v libx264 -x264opts no-scenecut -b:v 6M -force_key_frames 'expr:gte(t,n_forced*2)' -movflags faststart -c:a copy -tune zerolatency -pass 2 -f mp4 "$video" ||
|
/usr/bin/ffmpeg -y -i "$file" -c:v libx264 -x264opts no-scenecut -b:v 6M -maxrate 6M -bufsize 1M -force_key_frames 'expr:gte(t,n_forced*2)' -movflags faststart -c:a copy -tune zerolatency -pass 2 -f mp4 "$video" ||
|
||||||
Log "Error encoding $name" 2
|
Log "Error encoding $name" 2
|
||||||
|
|
||||||
# Schedule cron job
|
# Schedule cron job
|
||||||
m=${date:14:2} m=${m#0} h=${date:11:2} h=${h#0}
|
m=${date:13:2} m=${m#0} h=${date:11:2} h=${h#0}
|
||||||
D=${date:8:2} D=${D#0} M=${date:5:2} M=${M#0}
|
D=${date:8:2} D=${D#0} M=${date:5:2} M=${M#0}
|
||||||
/usr/bin/crontab -l ||
|
/usr/bin/crontab -l ||
|
||||||
echo -e "# m h dom mon dow command\n" |/usr/bin/crontab -
|
echo -e "# m h dom mon dow command\n" |/usr/bin/crontab -
|
||||||
line="$m $h $D $M "'*'" /var/www/stream '${video##*/}'"
|
line="$m $h $D $M "'*'" $repopath/stream '${video##*/}'"
|
||||||
echo -e "$(/usr/bin/crontab -l)\n$line" |/usr/bin/crontab -
|
echo -e "$(/usr/bin/crontab -l)\n$line" |/usr/bin/crontab -
|
||||||
|
|
||||||
Log "crontab: '$line'" 0
|
Log "crontab: '$line'" 0
|
||||||
20
stream
20
stream
@ -2,12 +2,18 @@
|
|||||||
|
|
||||||
# stream - Stream video when called from crontab
|
# stream - Stream video when called from crontab
|
||||||
# Usage: stream <video>
|
# Usage: stream <video>
|
||||||
# The video should have been re-encoded by `prep` first,
|
# The video should have been re-encoded by `encode` first,
|
||||||
# and have the streamkey at the start of the filename.
|
# and have the streamkey at the start of the filename.
|
||||||
|
|
||||||
in=/var/www/uploadpage/streams/$1 key=${1%%.*}
|
repopath=/var/www
|
||||||
/usr/bin/ffmpeg -re -y -i $in -c:v copy -c:a copy \
|
log=$repopath/process.log
|
||||||
-f flv "rtmp://singapore.restream.io/live/$key"
|
in=$repopath/uploadpage/streams/$1 key=${1%%.*} target=${1##*@}
|
||||||
# -f flv "rtmp://live.restream.io/live/$key"
|
case $target in
|
||||||
# -f flv "rtmp://a.rtmp.youtube.com/live2/$key"
|
Facebook) rtmp=rtmps://live-api-s.facebook.com:443/rtmp/$key ;;
|
||||||
# -f flv "rtmps://live-api-s.facebook.com:443/rtmp/$key"
|
YouTube) rtmp=rtmp://a.rtmp.youtube.com/live2/$key ;;
|
||||||
|
RestreamSG) rtmp=rtmp://singapore.restream.io/live/$key ;;
|
||||||
|
*) rtmp=rtmp://live.restream.io/live/$key
|
||||||
|
esac
|
||||||
|
|
||||||
|
/usr/bin/ffmpeg -re -y -i $in -c:v copy -c:a copy -f flv "$rtmp" ||
|
||||||
|
echo "Error while streaming $1" >>"$log"
|
||||||
|
|||||||
@ -11,9 +11,18 @@ function respond(){
|
|||||||
<form action="upload.php" method="post" enctype="multipart/form-data" onsubmit="respond()">
|
<form action="upload.php" method="post" enctype="multipart/form-data" onsubmit="respond()">
|
||||||
<table>
|
<table>
|
||||||
<tr><td colspan=2 align=center><h1>Video Upload Page</h1></td></tr>
|
<tr><td colspan=2 align=center><h1>Video Upload Page</h1></td></tr>
|
||||||
|
<tr>
|
||||||
|
<td>Target:</td>
|
||||||
|
<td>
|
||||||
|
<select name="target" id="target" required>
|
||||||
|
<option value="Restream">Restream</option>
|
||||||
|
<option value="Facebook">Facebook</option>
|
||||||
|
<option value="YouTube">YouTube</option>
|
||||||
|
</select>
|
||||||
|
</td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Streamkey:</td>
|
<td>Streamkey:</td>
|
||||||
<td><input type="text" name="streamkey" id="streamkey" required title="string of 0-9, a-z or underscore characters" pattern="[a-z0-9_]+"></td></tr>
|
<td><input type="text" name="streamkey" id="streamkey" required title="string of 0-9, a-z, A-Z or underscore characters" pattern="[a-zA-Z0-9_]+"></td></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Date:</td>
|
<td>Date:</td>
|
||||||
<td><input type="date" name="date" id="date" required></td></tr>
|
<td><input type="date" name="date" id="date" required></td></tr>
|
||||||
@ -23,9 +32,9 @@ function respond(){
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Video File:</td>
|
<td>Video File:</td>
|
||||||
<td><input type="file" name="fileToUpload" id="fileToUpload" required accept=".mp4"></td></tr>
|
<td><input type="file" name="fileToUpload" id="fileToUpload" required accept=".mp4"></td></tr>
|
||||||
<tr><td><p></td></tr>
|
<tr><td><br></td></tr>
|
||||||
<tr><td align=center colspan="2"><input type="submit" value="Schedule Stream" name="submit"></td></tr>
|
<tr><td></td><td><input type="submit" value="Schedule Stream" name="submit"></td></tr>
|
||||||
<tr><td><p></td></tr>
|
<tr><td><br></td></tr>
|
||||||
<tr><td align=center colspan="2" id="response"></td></tr>
|
<tr><td align=center colspan="2" id="response"></td></tr>
|
||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -1,18 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
error_reporting(E_ALL);
|
error_reporting(E_ALL);
|
||||||
set_time_limit(0);
|
//set_time_limit(0);
|
||||||
//ob_implicit_flush(true);
|
|
||||||
//ob_end_flush();
|
|
||||||
if($_SERVER['REQUEST_METHOD']!=='POST'){
|
if($_SERVER['REQUEST_METHOD']!=='POST'){
|
||||||
header('Location: /');
|
header('Location: /');
|
||||||
}
|
}
|
||||||
|
|
||||||
function back($msg){
|
function Back($msg){
|
||||||
print($msg);
|
print('<br> <br>'.$msg.'<br> <br>
|
||||||
print('<br><br>
|
|
||||||
<form action="/" method="post">
|
<form action="/" method="post">
|
||||||
<input type="submit" value="Upload another file" name="submit">
|
<input type="submit" value="Upload another file" name="submit">
|
||||||
</form>');
|
</form></div>');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,32 +17,34 @@ header('Content-type: text/html; charset=utf-8');
|
|||||||
$upload=htmlspecialchars(basename($_FILES['fileToUpload']['name']));
|
$upload=htmlspecialchars(basename($_FILES['fileToUpload']['name']));
|
||||||
$key=$_POST['streamkey'];
|
$key=$_POST['streamkey'];
|
||||||
$date=$_POST['date'];
|
$date=$_POST['date'];
|
||||||
$time=$_POST['time'];
|
$hour=substr($_POST['time'], 0, 2);
|
||||||
|
$min=substr($_POST['time'], 3, 2);
|
||||||
|
$time=$hour.$min;
|
||||||
|
$target=$_POST['target'];
|
||||||
$dir='streams/';
|
$dir='streams/';
|
||||||
$name=$key.'.'.$date.'_'.$time;
|
$name=$key.'.'.$date.'_'.$time.'@'.$target;
|
||||||
$file=$dir.$name.'.upload';
|
$file=$dir.$name.'.upload';
|
||||||
print('<!DOCTYPE html>
|
print('<!DOCTYPE html>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Uploading</title>
|
<title>Encoding</title>
|
||||||
<h3>Uploading</h3>
|
<div style="display:flex; flex-direction:column; justify-content:center; text-align:center; align-items:center; height:95vh;">
|
||||||
File: '.$upload.'<br>');
|
<h3>Encoding</h3>
|
||||||
ob_flush();
|
File: '.$upload);
|
||||||
flush();
|
$now=date('Y-m-dHi');
|
||||||
$now=date('Y-m-dH:i');
|
|
||||||
if(strcmp($now, $date.$time)>0){
|
if(strcmp($now, $date.$time)>0){
|
||||||
back('Scheduling '.$now.' in the past: '.$date.' '.$time);
|
Back('Scheduling '.$now.' in the past: '.$date.' '.$time);
|
||||||
}
|
}
|
||||||
$nextyear=date('Y-m-dH:i', strtotime('+1 year'));
|
$nextyear=date('Y-m-dHi', strtotime('+1 year'));
|
||||||
if(strcmp($nextyear, $date.$time)<0){
|
if(strcmp($nextyear, $date.$time)<0){
|
||||||
back('Scheduling too far into the future: '.$date.' '.$time);
|
Back('Scheduling too far into the future: '.$date.' '.$time);
|
||||||
}
|
}
|
||||||
if($_FILES['fileToUpload']['error']!=UPLOAD_ERR_OK){
|
if($_FILES['fileToUpload']['error']!=UPLOAD_ERR_OK){
|
||||||
back('Error uploading the file');
|
Back('Error uploading the file');
|
||||||
}
|
}
|
||||||
if(!move_uploaded_file($_FILES['fileToUpload']['tmp_name'], $file)){
|
if(!move_uploaded_file($_FILES['fileToUpload']['tmp_name'], $file)){
|
||||||
back('Error moving the file');
|
Back('Error moving the file');
|
||||||
}
|
}
|
||||||
|
|
||||||
back('File is now being encoded to "'.$name.'.mp4"<br>
|
Back('File is now being encoded to "'.$name.'.mp4"<br> <br>
|
||||||
Scheduled for '.$date.' at '.$time);
|
Scheduling for '.$date.' at '.$hour.':'.$min.'h on '.$target);
|
||||||
?>
|
?>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user