op25/op25/gr-op25_repeater/apps/README-hls

269 lines
10 KiB
Plaintext

OP25 HTTP live streaming December 2020
=====================================================================
These hacks ("OP25-hls hacks") add a new option for audio reception
and playback in OP25; namely, via an HTTP live stream to any remote
client using a standard Web browser*. The web server software used
(nginx) is industrial-strength and immediately scalable to dozens or
hundreds of simultaneous remote users with zero added effort. More
than one upstream source (in parallel) can be served simultaneously.
OP25's liquidsoap script is hacked to pipe output PCM audio data
to ffmpeg, which also reads the www/images/status.png image file
that makes up the video portion of the encoded live stream. The
image png file is kept updated by rx.py.
The selection of ffmpeg codecs ("libx264" for video and "aac" for
audio) allows us directly to send the encoded data stream from
ffmpeg to the web server (nginx) utilizing RTMP. Individual
MPEG-TS segments are stored as files in nginx web server URL-space,
and served to web clients via standard HTTP GET requests. The
hls.js package is used at the client.
The entire effort mostly involved assembling existing off-the-shelf
building blocks. The ffmpeg package was built manually from source
to enable the "libx264" codec, and a modified nginx config was
used.
*the web browser must support the "MediaSource Extensions" API.
All recent broswer versions should qualify.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. nginx installation
The libnginx-mod-rtmp package must be installed (in addition to
nginx itself). You can copy the sample nginx configuration at the
end of this README file to /etc/nginx/nginx.conf, followed by
restarting the web server
sudo systemctl stop nginx
sudo systemctl start nginx
With this configuration the web server should listen on HTTP port
8081 and RTMP port 1935.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2. ffmpeg installation
git clone https://code.videolan.org/videolan/x264.git
git clone https://git.ffmpeg.org/ffmpeg.git
cd x264
./configure
make
sudo make install
cd ../ffmpeg
./configure --enable-shared --enable-libx264 --enable-gpl
make
sudo make install
To confirm the installation run the "ffmpeg" command and
verify the presence of "--enable-shared" and "--enable-libx264"
in the "configuration:" section of the output.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3. liquidsoap installation
Both packages "liquidsoap" and "liquidsoap-plugin-all" were
installed, but not tested whether the plugins are required for
this application.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4. nginx setup
with the custom config installed as per step 1, copy the files
from op25/gr_op25_repeater/www to /var/www/html as follows:
live.html
live.m3u8
hls.js
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5. liquidsoap setup
in the op25/gr_op25_repeater/apps directory, note the ffmpeg.liq
script. Overall the filtering and buffering options should be
similar to those in op25.liq. The default version of ffmpeg.liq
should be OK for most uses.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6. operation
With OP25 rx.py started using the options -V -w (and -2 if using
TDMA) and with ffmpeg.liq started (both from the apps directory),
you should be able to connect to http://hostip:8081/live.html
and click the Play button to begin. NOTE: See note E for more
details about how to specify the value for 'hostip' in the above
link.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7. troubleshooting
A. with the ffmpeg.liq script running ffmpeg should start sending
rtmp data over port 1935 to nginx. You should see files
start being populated in /var/www/html/hls/ .
B. If /var/www/html/hls is empty, check ffmpeg message output
for possible errors, and also check the nginx access and
error logs. Note that the /var/www/html/hls directory should
start receiving files a few seconds after ffmpeg.liq is started
(regardless of whether OP25 is actively receiving a station,
or is not receiving).
C. js debug can be enabled for hls.js by editing that file as
follows; locate the lines of code and change the "debug"
setting to "true"
var hlsDefaultConfig = _objectSpread(_objectSpread({
autoStartLoad: true,
// used by stream-controller
startPosition: -1,
// used by stream-controller
defaultAudioCodec: void 0,
// used by stream-controller
debug: true, ///// <<<=== change this line from
///// "false" to "true"
D. after reloading the page and with the web browser js console
opened (and with all message types enabled), debug messages
should now start appearing in the console. As before another
place to look for messages is in the nginx access and error
logs.
E. if you are doing heavy client-side debugging it may be helpful
to obtain a copy of the hls.js distribution and to populate
the hls.js.map file (with updated hls.js) in /var/www/html.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8. notes
A. due to the propagation delay inherent in the streaming
process, there is a latency of several seconds from when
the transmissions are receieved before they are played in
the remote web browser. OP25 attempts to keep the video
and audio synchronized but the usual user controls (hold,
lockout, etc). are not available (in this release) because
the several-second delay could cause the commands to operate
on stale talkgroup data (without additional work).
B. in keeping with the current OP25 liquidsoap setup, the audio
stream is converted to mono prior to streaming. It might be
possible to retain the stereo data (in cases where the L and
R channels contain separate information), but this has not
been tested. The ffmpeg.liq script would need to be changed to
use "output" instead of "mean(output)" and the ffmpeg script
would need to change "-ac 1" to "-ac 2". In addition the
options stereo=true and channels=2 would need to be set in the
%wav specification parameters.
C. multiple independent streams can be served simultaneously by
invoking a separate ffmpeg.sh script for each stream and by
changing the last component of the rtmp URL to a unique
value; for example:
rtmp://localhost/live/stream2
A unified (parameterized) version of ffmpeg.sh could also be
used.
Also, new versions of live.html and live.m3u8 in /var/www/html
(reflecting the above modification) would need to be added.
D. note that pausing and seeking etc. in the media feed isn't
possible when doing live streaming.
E. when connecting from the remote client to the nginx server as
detailed in step 6 (above) you should specify the value for
'hostip' as follows (omitting the quotes):
'localhost' (default) - use this when the client is on the same
machine as the server
'host.ip.address' - specify the IP address of the server when
the client and server machines are different, and the server
does not have a DNS hostname.
'hostname' - if the server has a DNS hostname, this name should
be used in place of 'hostip'.
Note that in the second and third cases above you must also
change the hostname from 'localhost' to the IP address or DNS
hostname, respectively, in the following files
live.html
live.m3u8
in /var/www/html (total three occurrences).
Similarly, if an IP port other than 8081 is to be used, the same
updates as above must be made, and also the nginx conf file must
be updated to reflect the changed port number.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
########################################################################
####### tested on ubuntu 18.04 #######
####### sample nginx conf file - copy everything below this line #######
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
# RTMP configuration
rtmp {
server {
listen 1935; # Listen on standard RTMP port
chunk_size 4000;
application live {
live on;
# Turn on HLS
hls on;
hls_path /var/www/html/hls/;
hls_fragment 3;
hls_playlist_length 60;
# disable consuming the stream from nginx as rtmp
deny play all;
}
}
}
http {
sendfile off;
tcp_nopush on;
#aio on;
directio 512;
default_type application/octet-stream;
server {
listen 8081;
location / {
# Disable cache
add_header 'Cache-Control' 'no-cache';
# CORS setup
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length';
# allow CORS preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# include /etc/nginx/mime.types;
types {
text/html html;
text/css css;
application/javascript js;
application/dash+xml mpd;
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
root /var/www/html;
}
}
}