• Stars
    star
    775
  • Rank 58,632 (Top 2 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

VR re:stream

https://gitlab.com/voicerepublic/voicerepublic_logorrhoe/badges/master/build.svg

General

This is literate documentation for the VR native streaming desktop application. The stack is based on

Audio Data Flow

+----------------------+        +--------------------+          +-------------------+
|    Audio Input       |        |    MP3 Encoding    |          |  Web Audio Stream |
|   Java `sampled`     +------->|     shell lame     +--------->|      HTTP PUT     |
+----------------------+        +--------------------+          +-------------------+

To be able to stream Audio date from sampled over to the Shell (lame) and from there again to http put, many options were evaluated. The Clojure implementation of clojure.java.shell does not support creating or sustaining a stream of output, only discrete steps. See ClojureDoc for more information.

Alternative options

Using clojure.java.shell2

This is the version that is currently implemented!

According to this discussion on the Clojure dev mailing list, Stuart Halloway (who is very active on this mailing list) sees clojure.java.shell as under-powered and doesn’t want to add any more effort in sustaining it’s development.

However, Marc Limotte has created clojure.java.shell2 which is an extension of the core library and supports various non-breaking additions.

Encoding in discrete steps

This has several obvious downsides. For example that it would create multiple MP3 blobs, each with it’s own header. Also synchronization between sending those blobs would be hard.

Monkey Patching clojure.java.shell

I tried, but got stuck with deadlocks. I didn’t continue down that path, because other people have spent more time on doing this right (see below).

Using the Conch Library for shelling out asynchronously

This one was a good contender, however it was dropped in favor of clojure.java.shell2. The reasons being that shell2 feels more idiomatic and that Conch isn’t being actively developed anymore. shell2 hasn’t seen a commit even longer, but is based on the Clojure core lib clojure.java.shell which gives it some additional credibility.

Using Java ProcessBuilder

Java has a Class for creating and managing Sub-Processes called ProcessBuilder. I did some initial tests, but shell2 prevailed.

Icecast

Stats

Protocol

Reverse engineered streaming of Ogg Vorbis files:

(client/put "http://52.58.65.224/4609"
            {
             :basic-auth ["source" "thisisagoodpassword"]
             :body (clojure.java.io/file "/home/munen/src/voicerepublic_icecast_tests/manual_put/test.ogg")
             :headers {
                       :user-agent "vr_shout/0.2.0"
                       :ice-bitrate "128"
                       :content-type "application/ogg"
                       :ice-name "VR Server Name"
                       :ice-genre "Rock"
                       :ice-title "VR Title"
                       :ice-url "https://voicerepublic.com"
                       :ice-private "0"
                       :ice-public "1"
                       :ice-description "VR Server Description"
                       :ice-audio-info "ice-samplerate=44100;ice-bitrate=128;ice-channels=2"
                       }
             })

To-dos

Implementation Notes

Finding available audio input devices

Note: It would be possible to check for LINE_IN and MICROPHONE directly using (AudioSystem/isLineSupported Port$Info/LINE_IN) and (AudioSystem/getLine Port$Info/LINE_IN) However, this does not always return a line with said capability (seen in Debian 8). Therefore, we’re using the method to request a line via the Mixer.

Why streaming MP3 and not Vorbis?

vorbis-java is the official Java lib from Xiph, the creators of Ogg Vorbis. It has example source and even a port of the libshout lib.

Unfortunately, though, it hasn’t been updated since 2007, is still in Beta and not available from Maven. For a manual install, package the Class files with jar cf something.jar [files] and then create a local Maven Repo for Leiningen: http://www.elangocheran.com/blog/2013/03/installing-jar-files-locally-for-leiningen-2/

libshout-java

Install

Note that putting a Jar into /lib or using :resource-paths in project.clj doesn’t seem to be the way to go since Leiningen 2.

OS X
Checkout libshout-java
git clone [email protected]:poochiethecat/libshout-java.git
cd libshout-java
brew install ./libshout.rb
mvn install
cp ./target/libshout-java.so ~/src/voicerepublic_logorrhoe/target/libshout-java.so
Debian
Checkout libshout-java
apt-get install git libshout3-dev maven
git clone https://github.com/OlegKunitsyn/libshout-java.git
cd libshout-java
Fix a test (otherwise it will not install)
  • Fix the test testVersion() in src/test/java/com/gmail/kunicins/olegs/libshout/LibshoutTest.java to check for your installed version of libshout
  • It’s tested and working with version 2.3.1
  • Find your version via
apt-cache show libshout3-dev | ag version | egrep -o "2.[0-9].[0-9]"
  • Then install the library to your local maven repository so that `lein deps` can find it
mvn install
Move this compiled Library to a HTTP repo so that not every user has to install it to a local repo

Development

Package for OS X

To start of packaging the Java Swing GUI for OS X, let’s pack the whole app inside of a stand alone jar.

To create a standalone Java Application through Leiningen, the main class of the program has to be put into project.clj as the value of the :main key. For this class to be available during the build process, (:gen-class) has to be called within the (ns) declaration of the responsible Clojure file. Documentation on how to achieve this can be found here.

# lein compile
lein uberjar

When curious, try starting the jar manually

java -jar target/clojure_desktop_app_demo-0.1.0-SNAPSHOT-standalone.jar

The resulting JAR file is ready to be packaged into a Mac OS X Installer.

By hand

This is more for testing purposes since you will still need the JRE installed.

rm -rf /tmp/vr-restream /tmp/vr-restream.dmg
mkdir /tmp/vr-restream
cp target/*standalone.jar /tmp/vr-restream/vr-restream.jar
ln -s /Applications /tmp/vr-restream/Applications
hdiutil create -srcfolder /tmp/vr-restream /tmp/vr-restream.dmg

Using the javapackager tool

The following script is based on this tutorial.

cd target
rm -rf *iconset
rm -rf package deploy
rm vr-restream.dmg
mkdir vr-restream.iconset
sips -z 128 128 ../resources/img/logo.png --out vr-restream.iconset/icon_128x128.png
iconutil --convert icns vr-restream.iconset
mkdir -p package/macosx
cp vr-restream.icns package/macosx
jdk=$(/usr/libexec/java_home)
$jdk/bin/javapackager -version
$jdk/bin/javapackager -deploy -native dmg \
   -srcfiles vr_logorrhoe-0.1.0-SNAPSHOT-standalone.jar -appclass vr_logorrhoe.core -name vr-restream \
   -outdir deploy -outfile vr-restream -v

Note: If something needs to be packed into the .dmg file, this would be a way. In this example a config value is changed inside a file: For that, let’s mount the .dmg file in RW mode, use sed to change the config file value and then create a new .dmg file that’s again RO. This process has been inspired by: http://www.macenterprise.org/articles/creativewaysofusingshadowfiles

# Step 1: Eject the volume that has been mounted by javapackager
hdiutil detach /Volumes/vr-restream
# Step 2: Attach the read-only image with a shadow option
hdiutil attach -owners on deploy/bundles/vr-restream-1.0.dmg -shadow
# Step 3: Mutate inside the image what needs mutating
sed -i '' -e "s/app.classpath=/app.classpath=vr_logorrhoe-0.1.0-SNAPSHOT-standalone.jar/g" /Volumes/vr-restream/vr-restream.app/Contents/Java/vr-restream.cfg
# Step 4: Detach the currently attached image
hdiutil detach /Volumes/vr-restream
# Step 5: Convert the image back to read-only, in the process creating a new image
hdiutil convert -format UDZO -o vr-restream.dmg deploy/bundles/vr-restream-1.0.dmg -shadow

After a good while (there’s code signing going on), you will find a dmg file sitting in the deploy/bundles directory.

License

Copyright © 2016 Voice Republic Media AG

Bundled software

This project bundles the LAME Encoder. LAME is under the LGPL. Since it has been bundled in binary form and has not been modified, it is allowed to distribute it within this project without releasing the source of the project itself.