name: inverse layout: true class: center, top, inverse --- # Hacking together a FreeBSD # presentation streaming box # For as little as possible .footnoteleft[Tom Jones] .footnoteright[tj@enoti.me] --- layout:false # `whoami` - Internet Engineer - Foolish volunteer - "I said yes because I don't know how to say no" - Founder, sometime organiser at local hackerspace - I take the hackerspace camping every summer (campgnd.com) - Organiser of Techmeetup Aberdeen - Monthly event with beer, pizza and talks --- # When will the talks be up? "What is needed is adding logos to the front of the video and uploading them to YouTube. That takes time and effort." "Video is not high priority. It is a volunteer effort done only because folks want it. There is so much work and effort put into the the conference in the months before the conference, that once it is over, we want to attend to so many other tasks which have been pushed to the side. We need to get caught up on our personal lives. This is part of why it takes so long to get post-conference tasks completed." - Dan Langille --- # Why this talk? - Recording talks is easy - uploading them is nigh on impossible - Streaming - expensive - hard - require multiple people to operate - physically exhausting and emotionally devastating - If we can can make the equipment cheap the *event* can own it - If the *event* owns the equipment they can train a team --- # What this talk is - Architecture, tools and equipment to stream events - Technical explanation of what these pieces do - Instructions on how to hack these pieces together - Tools to debug --- # Typical Box
--- # Iron Age Smelting ![Smelt](images/sirastreamssmelt2.jpg "Smelt") --- # ERG Summer School ![ERG Summer School](images/ergsummerschool2.jpg "ERG Summer School") --- # ERG Summer School - Projector - Capture Devices - Audio £ 200 + (microphones) - Video £1600 - Mixing £1699 - Combine multiple video sources - Combine multiple audio sources - Combine audio and video - Uplink laptop £1000 Total £4499 --- # ERG Summer School .center[![Coffeepot Cam](images/coffeepotcam.jpg "Coffeepot Cam")] --- # Summer School Architecture .center[
t
n
t
p
M
d
r
i
t
C
r
r
s
t
A
e
n
o
t
S
#
_
C
l
c
e
M
i
|
u
e
t
e
p
d
e
a
l
l
e
a
t
O
n
V
i
|
o
i
x
r
T
t
a
e
c
#
i
i
e
e
s
a
i
S
S
d
e
i
D
k
l
I
n
m
m
m
r
C
i
n
a
L
#
e
o
o
C
t
C
e
s
s
e
] --- # What are we doing right now? - My laptop is connected to this projector, you can see my slides - I am standing up here talking - **You** are listening to me (I hope) and watching stuff from my laptop .center[
L
P
o
j
p
t
r
o
a
o
p
e
c
t
r
] - Ideally we would share this experience far beyond this room --- # Requirements - Capture the output from the laptop - Capture the audio from speaker - Send our capture to the internet - Distribute it as far as possible ## The Solution - Self contained - Plug and play - Cheap enough for the event to own it - Less that £100 would be ideal - Components **MUST** be generally for sale - **Run FreeBSD** --- # What we don't get - Video of the speaker - Audience participation (repeat the question!) --- # Components - **Capturing Slides** - Capturing Audio - Mixing - Streaming Out - Recording Video --- # Capturing Slides - Screen Capture software - HDMI Capture Card - Hacked up HDMI thing --- # Capturing Slides - ~~Screen Capture software~~ - HDMI Capture Card - Hacked up HDMI thing --- # Capturing Slides - ~~Screen Capture software~~ - HDMI Capture Card - Hacked up HDMI thing ![Magic Smoke](images/magicsmoke.png "Magic Smoke") --- # Capturing Slides - ~~Screen Capture software~~ - ~~HDMI Capture Card~~ - **Hacked up HDMI thing** --- # HDMI Ethernet Extender - **NOT** 60m HDMI CAT6 Ethernet extenders - LKV373 based HDMI Ethernet extender ![HDMI Range Extender Orders](images/hdmiextender-order.png "HDMI Range Extender Orders") --- # HDMI Ethernet Extender - **NOT** 60m HDMI CAT6 Ethernet extenders - LKV373 based HDMI Ethernet extender ![CAT6 Extender Glitch](images/cat6glitch.jpg "CAT6 Extender Glitch") --- # HDMI Ethernet Extender - LKV373 HDMI Extender - MPEG2 Multicast - Fully reverse engineered [1] ![HDMI Range Extender](images/hdmiethernetextender.jpg "HDMI Range Extender") .footnote[1: https://blog.danman.eu/new-version-of-lenkeng-hdmi-over-ip-extender-lkv373a/] --- # HDMI Ethernet Extender # tcpdump -i ue0 17:00:28.285648 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.285902 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.286156 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.286410 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.286664 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.286918 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.287172 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.287426 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 1316 17:00:28.288095 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 0 17:00:28.289093 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 0 17:00:28.290093 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 0 17:00:28.291093 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 0 17:00:28.292094 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 0 17:00:28.293093 IP 192.168.1.238.5004 > 239.255.42.42.5004: UDP, length 0 --- # HDMI Ethernet Extender ![HDMI Range Extender Wireshark](images/hdmiextender-wireshark.png "HDMI Range Extender Wireshark") --- # HDMI Ethernet Extender # ifconfig ue0 192.168.1.2/24 $ cvlc udp://@239.255.42.42:5004 ![HDMI Check signal](images/hdmitx-checksignal.png "HDMI Check Signal") --- # Bridging multicast to unicast bridge.sh #!/bin/sh socat UDP4-RECV:5004,ip-add-membership=239.255.42.42:192.168.1.2 STDOUT | nc -u 192.168.1.2 5000 $ cvlc udp://@:5000 $ bridge.sh --- # Components - Capturing Slides - **Capturing Audio** - Mixing - Streaming Out - Recording Video --- # Capturing Audio ## Interfaces - USB Desk ($$) - USB Interface ($) - Built-in Audio Interface (0) ## Microphones - Lavalier - Cardioid - Boundary/Area --- # Capturing Audio .center[![USB Audio](images/usbaudio.jpg "USB Audio")] --- # Capturing Audio ![Microphone](images/wirelessmic.jpg "Microphone") --- # Capturing Audio - sox is netcat for audio - check your levels: $ rec -n Input File : 'default' (coreaudio) Channels : 2 Sample Rate : 44100 Precision : 32-bit Sample Encoding: 32-bit Signed Integer PCM In:0.00% 00:00:11.96 [00:00:00.00] Out:526k [ -====|====- ] Clip:6 ## Record and play back $ rec -c 1 test.wav $ play test.wav --- # Check that time is progressing at the correct rate $ man 4 snd_uaudio ... BUGS ... Some USB audio devices might refuse to work properly unless the sample rate is configured the same for both recording and playback, even if only simplex is used. See the dev.pcm.%d.[play|rec].vchanrate sysctls. The PCM framework in FreeBSD currently doesn't support the full set of USB audio mixer controls. Some mixer controls are only available as dev.pcm.%d.mixer sysctls. If your test is fast or slow you may need to manually sync the rate $ sudo sysctl dev.pcm.4.rec.vchanrate=44100 dev.pcm.4.rec.vchanrate: 48000 -> 44100 --- # Aside: Software Defined Radio - A wireless lavalier mic made the most sense - It says 229.7M on it - could we just grab it from the air? ![Wireless mic frequency](images/microphonefrequency.jpg "Wireless mic frequency") --- # Aside: Software Defined Radio .center[![RTLSDR](images/rtlsdr.jpg "RTLSDR")] --- # Aside: Software Defined Radio
--- # Aside: Software Defined Radio
--- # Aside: Software Defined Radio - AM Voice - 229.7MHz center freq - ~15KHz wide --- # Playing audio with rtl_sdr ## Test SDR with FM radio $ rtl_fm -M wbfm -f 97.5M | \ play -r 32k -t raw -e s -b 16 -c 1 -V1 - ## Capturing the wireless mic with rtl_sdr $ rtl_fm -M am -f 229.7M -s 15k -E swagc | \ play -r 15k -t raw -e s -b 16 -c 1 -V1 - --- # Components - Capturing Slides - Capturing Audio - **Mixing** - Streaming Out - Recording Video --- # Mixing: gstreamer Everything assemblerizer for video and audio .center[
S
K
R
p
O
E
p
D
S
p
O
p
N
C
N
D
2
p
p
1
N
E
I
] --- # Mixing: gstreamer audio $ gst-launch-1.0 audiotestsrc ! autoaudiosink BEEEEEEEEEEEP! --- # Mixing: gstreamer video $ gst-launch-1.0 videotestsrc ! autovideosink --- # Mixing: gstreamer video ![Video Test SRC](images/videotestsrc.png "Video Test SRC") --- # Mixing: gstreamer muxing .center[
A
N
E
M
I
p
A
O
I
V
O
O
R
p
D
N
p
E
U
K
V
D
E
O
S
I
p
p
N
D
C
p
X
R
C
D
p
U
p
p
S
S
] --- # Mixing: gstreamer demuxing .center[
E
R
O
N
O
D
P
E
M
E
V
N
U
D
p
U
S
D
O
p
A
D
O
p
S
K
p
D
p
D
p
I
O
I
A
p
D
E
N
V
E
C
V
p
p
O
I
X
R
I
] --- # Playing video from the feed recvfeed.sh gst-launch-1.0 \ udpsrc port=5000 \ ! tsdemux \ ! queue \ ! h264parse \ ! avdec_h264 \ ! autovideosink --- # Playing Audio gst-launch-1.0 \ autoaudiosrc \ ! autoaudiosink --- # Components - Capturing Slides - Capturing Audio - Mixing - **Streaming Out** - Recording Video --- # Streaming Out - Streaming Service handles: - Ingestion (accepting our feed) - Distribution - Recording - Fan out - Scale Engine - **FreeBSD** Streaming CDN - Free streaming for BSD events (ask Allan) - Other streaming services are available --- # Streaming Out - Service accepts a Real-Time Messaging Protocol (RTMP) stream - RTMP - TCP protocol on port 1935 - H264 Video - AAC Audio --- # Streaming box .center[![x5 box](images/x5-case.jpg "x5 box")] --- # Streaming out Mux it together flvmux name=muxer streamable=true \ ! rtmpsink location='rtmp://example-origin.secdn.net/example-origin/live/419fw' \ --- # Streaming out Audio autoaudiosrc \ ! audioconvert \ ! wavenc \ ! wavparse \ ! audioconvert ! audioresample \ ! audio/x-raw, rate=48000 \ ! avenc_aac \ ! muxer. \ --- # Streaming out Video udpsrc port=5000 \ ! tsdemux \ ! queue \ ! h264parse \ ! muxer. --- # Streaming out In full we get `stream.sh` gst-launch-1.0 -e \ flvmux name=muxer streamable=true \ ! rtmpsink location='rtmp://example-origin.secdn.net/example-origin/live/419fw' \ autoaudiosrc \ ! audioconvert \ ! wavenc \ ! wavparse \ ! audioconvert ! audioresample \ ! audio/x-raw, rate=48000 \ ! avenc_aac \ ! muxer. \ udpsrc port=5000 \ ! tsdemux \ ! queue \ ! h264parse \ ! muxer. --- # Components - Capturing Slides - Capturing Audio - Mixing - Streaming Out - **Recording Video** --- # Recording - Scale Engine defaults: - Start a recording at rtmp publish - Stop recording at rtmp unpublish - Recordings recoverable from Scale Engine file store --- # Pulling it all together ![Lantap](images/lantap.jpg "Lantap") --- # Pulling it all together ![All together](images/streamcomputer.jpg "All together") --- # Pulling it all together ![Tidy setup](images/tidysetup.jpg "Tidy setup") --- # Architecture .center[
r
l
n
e
i
P
c
C
i
t
i
e
P
t
t
a
m
T
i
p
i
u
o
N
j
t
i
d
t
e
e
x
M
a
C
l
e
t
L
e
s
B
x
T
P
r
n
a
s
e
p
h
t
l
e
a
S
t
n
n
d
o
h
e
S
f
m
m
m
A
C
i
x
o
t
t
c
E
e
'
L
n
l
a
r
r
A
e
n
g
S
d
i
i
r
o
c
r
o
] --- # Using it - Find scaleengine dashboard demo feed - Launch stream.sh - Launch bridge.sh --- # Final System - LKV373 HDMI Extender Pair £ 61.99 - Lavalier mic £ 10.98 - Lantap £ 11.52 - USB Ethernet Nic £ 12.99 - USB Audio £ 6.49 ======= Total £103.97 - x5-z8350 PC £ 72.99 - Whole bunch of cables £ ??.?? --- ## Thank you for listening # Questions?
--- # Colophon This presentation is open source software and was made with open source software. Open tools were used to build the system presented and the final system is open source. - FreeBSD - wireshark - gstreamer - socat - sox - vim - remark.js - goat - firefox --- # Saving the video feed to a file recordfeed.sh gst-launch-1.0 \ udpsrc port=5000 \ ! tsdemux \ ! queue \ ! h264parse \ ! mp4mux \ ! filesink location=test.mp4 --- # Capturing AAC Audio gstreamer audio pipeline gst-launch-1.0 \ autoaudiosrc \ ! audioconvert \ ! wavenc \ ! wavparse \ ! audioconvert ! audioresample \ ! audio/x-raw, rate=48000 \ ! avenc_aac \ ! filesink location=output.mp4 --- # Feeding the rtlsdr into gstreamer $ rtl_fm -M am -f 229.7M -s 15k -l 150 -E swagc | \ gst-launch-1.0 fdsrc \ ! audio/x-raw, format=S16E, channels=1, layout=interleaved, rate=15000 \ ! autoaudiosink