HTML5 VIDEO IN SCALA OR JAVA WITH XUGGLE

Introduction

If you are looking for information to use free libraries to convert and resize video files within a Scala or Java project, you may probably have cross the road of an open source project called Xuggle .
This library is written in Java but is using native code from ffmpeg (another open source project) for the many video and audio codecs.

  • build the Xuggle project on Debian Linux
  • write a sample class in Java and Scala to use Xuggle
  • resize a video and resample its audio
  • convert common video/audio formats to HTML5 video

When we decided to use Xuggle last year, we encountered some real difficulties to use it for HTML5 videos.
The official website of Xuggle has only old versions (5.4) of Xuggle that does not include the AAC audio codec, which is necessary on iPhone and Android devices.
After some googling I found a repository with the version 5.5, but it was impossible to build it successfully on Debian without a lot a tries and errors and manual patches and workarounds.
I had to restart the building process more than hundred times and wait each time 20 minutes before getting to the next error.
Hopefully this guide would save you such pain and make it easy to use.

The 1st part of this guide will explain step by step which commands you will type in the Linux console to build Xuggle successfully.
The 2nd part will show you the Java/Scala code that use the library to convert and resize video for HTML5.

Part I – Building Xuggle-Xuggler 5.5 on Linux Debian 7.6 Wheezy

The described steps were tested on Debian 7.1 to install and build xuggle-xuggler 5.5.
Following links were also helpful to understand the building steps:

Downloading and Installing
Basic Build Instructions
Advanced Building Topics
How to cross-compile Xuggle-Xuggler for Windows 7 64 or 32 bit
Building the latest Xuggler for Windows
Xuggler Users: building 5.5 from git – maven artifact error
Xuggler Users: Debugging ffmpeg libs in Eclipse

Step 1 – Set Environment Variables

export XUGGLE_HOME=/opt/xuggler   
export PATH=$XUGGLE_HOME/bin:$PATH
export LD_LIBRARY_PATH=$XUGGLE_HOME/ffmpeg_build/lib:$LD_LIBRARY_PATH
#export XUGGLE_NATIVE_CONFIGURE="--enable-captive-librtmp"
export clang=gcc
export CXX=g++

Step 2 – Install Necessary Packages

aptitude -P purge openjdk-7-{jre,jdk} openjdk-{6,7}-jre-{lib,headless} default-jre-headless icedtea-{6,7}-jre-{cacao,jamvm} sun-java{6,7}-{bin,jdk,jre,plugin,fonts,runtime} update-sun-jre java{6,7}-{jdk,runtime} #gsfonts-x11 odbcinst odbcinst1debian2 unixodbc
aptitude purge ffmpeg
echo "deb http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list
echo "deb-src http://ppa.launchpad.net/webupd8team/java/ubuntu precise main" | tee -a /etc/apt/sources.list
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EEA14886
#or add-apt-repository ppa:webupd8team/java #http://www.webupd8.org/2012/01/install-oracle-java-jdk-7-in-ubuntu-via.html
echo "deb http://www.deb-multimedia.org wheezy main" | tee -a /etc/apt/sources.list
echo "deb-src http://www.deb-multimedia.org wheezy main" | tee -a /etc/apt/sources.list
#or add-apt-repository http://www.deb-multimedia.org
#apt-get reinstall debian-keyring
#gpg --keyserver pgp.mit.edu --recv-keys 1F41B907
#gpg --armor --export 1F41B907 | apt-key add -
gpg --keyserver pgp.mit.edu --recv-keys 07DC563D1F41B907
gpg --armor --export 07DC563D1F41B907 | apt-key add -
aptitude update
aptitude -P install oracle-java6-installer oracle-java7-installer oracle-java7-set-default ant subversion git #maven scala eclipse
aptitude -P install ant-optional libass-dev libfaac-dev #libaacs-dev #junit mingw-w64
update-alternatives --config java
aptitude -P install make qt4-qmake cmake gcc gcc-doc g++ git-core byacc flex m4 patch python perl perl-base perl-modules perl-doc yasm #erlang
aptitude -P install build-essential ext2fs-dev linux-kernel-headers linux-source binutils-dev

aptitude -P install checkinstall libgpac-dev libdirac-dev libgsm1-dev libschroedinger-dev libspeex-dev libvorbis-dev libopenjpeg-dev libdc1394-22-dev libsdl1.2-dev zlib1g-dev texi2html libfaac-dev libfaad-dev libmp3lame-dev libtheora-dev libxvidcore4-dev libopencore-amrnb-dev libopencore-amrwb-dev libvpx-dev libfreetype6-dev frei0r-plugins-dev librtmp-dev libaacplus-dev libx264-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev libvo-aacenc-dev libxfixes-dev libfdk-aac-dev libopus-dev

aptitude -P install autoconf automake libass-dev libgpac-dev libtheora-dev libtool libvorbis-dev texi2html zlib1g-dev #libsdl1.2-dev libva-dev libvdpau-dev libx11-dev libxext-dev libxfixes-dev

Step 3 – Download The Source Code of Xuggle

mkdir -pv $XUGGLE_HOME
cd $XUGGLE_HOME
git clone git://git.videolan.org/x264.git
#commit f0c1c53d58420d209f0fb7b63e49125ab1c85aa7
#Author: Jason Garrett-Glaser <jason@x264.com>
#Date:   Thu Jun 20 15:51:39 2013 -0700
git clone git://github.com/xuggle/xuggle-xuggler.git
#commit 2501f93b041f2bb44d2ca03373ba6b66464a0df8
#Author: Art Clarke <art.clarke@gmail.com>
#Date:   Tue Aug 27 18:45:31 2013 -0600
#svn checkout http://xuggle.googlecode.com/svn/trunk/repo/share/java/xuggle/xuggle-xuggler/5.4 xuggle-xuggler-5.4

Step 4 – Initialize The Source Folder for a New Build and Change Default Compile Options

cd $XUGGLE_HOME/xuggle-xuggler
(cd $XUGGLE_HOME && rm -rf bin/ certs/ include/ lib/ misc/ openssl.cnf private/ share/)
git stash; git pull; git checkout master; git reset --hard; git clean -fxd
sed -i ./captive/ffmpeg/incarcerate.in -e 's%echo "Configuring FFMPEG with these options: $FFMPEG_OPTIONS"%FFMPEG_OPTIONS="$FFMPEG_OPTIONS --extra-libs=\"-ldl\" --enable-gpl --enable-libass --enable-libfaac --enable-libvo-aacenc --enable-version3 --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-nonfree"\n&%' #solve java.lang.RuntimeException: could not find encoder: libfaac

Step 5 – Apply Patches and Fix

Copy this patch (download Converter.java.patch) to the $XUGGLE_HOME folder and then execute following commands:

sed -i configure.ac -i mk/buildtools/Makefile.global.in mk/buildtools/cxxtest/sample/Makefile.unix -e 's%-Werror%%g'
rm -rf captive/libx264/csrc; cp -ai $XUGGLE_HOME/x264 captive/libx264/csrc #solve [exec] /opt/xuggler/ffmpeg/libavcodec/libx264.c:552: undefined reference to `x264_encoder_open_135'
#rm -rf captive/ffmpeg/csrc; cp -ai $XUGGLE_HOME/ffmpeg captive/ffmpeg/csrc #solve [exec] /opt/xuggler/ffmpeg/libavcodec/libx264.c:552: undefined reference to `x264_encoder_open_135'
#sed -i captive/configure.ac -e '/speex/d' #solve java.lang.UnsatisfiedLinkError: /tmp/sbt_9609b5d2/sbt_2564e44f/libxuggle.so: /tmp/sbt_9609b5d2/sbt_2564e44f/libxuggle.so: undefined symbol: speex_uwb_mode
#grep -rnl AVCODEC_MAX_AUDIO_FRAME_SIZE | xargs -l sed -i -e 's%AVCODEC_MAX_AUDIO_FRAME_SIZE%192000%g' #solve [exec] ../../../../../../../csrc/com/xuggle/xuggler/AudioSamples.cpp:101:25: error: 'AVCODEC_MAX_AUDIO_FRAME_SIZE' was not declared in this scope
#(cd captive/ffmpeg/csrc/; ./configure --extra-libs="-ldl" --enable-gpl --enable-libass --enable-libfaac --enable-libvo-aacenc --enable-version3 --enable-libmp3lame --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-nonfree; make) #--enable-x11grab #=> [exec] Out of tree builds are impossible with config.h in source dir.
patch -p0 <../Converter.java.patch #added by francois scheurer: add vsize parameter

#ant clobber
#mkdir mybuild
#cd mybuild
#../configure --disable-captives
#make
#make check
#sudo make install
#cd ..
#ant stage-java run-tests-java
#sudo ant install-java
#=> ../../../../../csrc/com/xuggle/xuggler/AudioSamples.cpp:101:25: error: 'AVCODEC_MAX_AUDIO_FRAME_SIZE' was not declared in this scope

Step 6 – Build The Project

#time ant run-tests
ant -p
time ant compile
time ant stage
time ant install
#find dist/ -type f -ls | o
#find . -iname "*.jar" -ls
#rm -rfv /opt/loco/lib/*; cp -aiv captive/ffmpeg/csrc/{ff,}presets dist/lib/* /opt/loco/lib/
rm -rfv /opt/loco/lib/*; cp -aiv captive/ffmpeg/csrc/presets dist/lib/{commons-cli.jar,xuggle-xuggler.jar} /opt/loco/lib/

At the end copy the two jar-files into your lib-folder (here loco as an example).

The following steps are optional. They may be useful if you also want to compile ffmpeg from the source.

#===ffmpeg===
#http://www.ffmpeg.org/download.html
#https://groups.google.com/forum/#!msg/xuggler-users/VqIx74bgY1s/zbuUmSa1h7oJ
#http://www.zoharbabin.com/build-and-install-ffmpeg-and-x264-on-debian-squeeze-the-dumb-guide
#https://ffmpeg.org/trac/ffmpeg/wiki/UbuntuCompilationGuide

export XUGGLE_HOME=/opt/xuggler
export PATH=$XUGGLE_HOME/bin:$PATH
export LD_LIBRARY_PATH=$XUGGLE_HOME/ffmpeg_build/lib:$LD_LIBRARY_PATH
#export XUGGLE_NATIVE_CONFIGURE="--enable-captive-librtmp"
export clang=gcc
export CXX=g++

cd $XUGGLE_HOME
git clone git://git.videolan.org/x264.git
#commit f0c1c53d58420d209f0fb7b63e49125ab1c85aa7
#Author: Jason Garrett-Glaser <jason@x264.com>
#Date:   Thu Jun 20 15:51:39 2013 -0700
cd x264
make distclean
#make
#make install
./configure --prefix="$XUGGLE_HOME/ffmpeg_build" --bindir="$XUGGLE_HOME/bin" --enable-static --enable-shared
time make -j4
make install
  #install -d /opt/xuggler/bin
  #install x264 /opt/xuggler/bin
  #install -d /opt/xuggler/ffmpeg_build/include
  #install -d /opt/xuggler/ffmpeg_build/lib
  #install -d /opt/xuggler/ffmpeg_build/lib/pkgconfig
  #install -m 644 ./x264.h /opt/xuggler/ffmpeg_build/include
  #install -m 644 x264_config.h /opt/xuggler/ffmpeg_build/include
  #install -m 644 x264.pc /opt/xuggler/ffmpeg_build/lib/pkgconfig
  #ln -f -s libx264.so.135 /opt/xuggler/ffmpeg_build/lib/libx264.so
  #install -m 755 libx264.so.135 /opt/xuggler/ffmpeg_build/lib
  #install -m 644 libx264.a /opt/xuggler/ffmpeg_build/lib
  #ranlib /opt/xuggler/ffmpeg_build/lib/libx264.a
#mkdir $XUGGLE_HOME/lib/
#cp -siv --remove-destination $XUGGLE_HOME/x264/x264* $XUGGLE_HOME/lib/
make distclean

#cd $XUGGLE_HOME
#svn co https://xavs.svn.sourceforge.net/svnroot/xavs/trunk xavs
#cd xavs
#./configure --enable-shared --disable-asm
#make
#make install

#cd $XUGGLE_HOME
#wget http://sourceforge.net/projects/opencore-amr/files/vo-aacenc/vo-aacenc-0.1.1.tar.gz
#tar zxvf vo-aacenc-0.1.1.tar.gz
#cd vo-aacenc-0.1.1
#./configure
#make
#make install
#checkinstall --pakdir "$HOME" --pkgname vo-aacenc --pkgversion 0.1.1 --backup=no --default && sudo ldconfig
#mv -vi /root/vo-aacenc_0.1.1-1_amd64.deb .
#dpkg -i vo-aacenc_0.1.1-1_amd64.deb

cd $XUGGLE_HOME
git clone --depth 1 git://source.ffmpeg.org/ffmpeg
cd ffmpeg
make distclean
#./configure --enable-shared --enable-libx264 --enable-gpl
#./configure --enable-gpl --enable-libfaac --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-nonfree --enable-version3 --enable-x11grab --enable-librtmp --enable-bzlib --enable-libspeex --enable-pthreads --enable-zlib --enable-libvpx --enable-libopenjpeg --enable-runtime-cpudetect --enable-postproc --enable-swscale --enable-libxvid --enable-libdc1394 --enable-vdpau --enable-libvo-aacenc
#make
#make install
#make tools/qt-faststart
#make install tools/qt-faststart
export PKG_CONFIG_PATH="$XUGGLE_HOME/ffmpeg_build/lib/pkgconfig"
./configure --prefix="$XUGGLE_HOME/ffmpeg_build" \
  --extra-cflags="-I$XUGGLE_HOME/ffmpeg_build/include" --extra-ldflags="-L$XUGGLE_HOME/ffmpeg_build/lib" \
  --bindir="$XUGGLE_HOME/bin" --extra-libs="-ldl" --enable-gpl --enable-libass --enable-libfdk-aac \
  --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libvpx \
  --enable-libx264 --enable-nonfree #--enable-x11grab
time make -j4
make install
make distclean
hash -r

#cd /opt/xuggler/x264
#make distclean
#./configure --enable-static
#make
#make install

ffmpeg -version
ffmpeg 2>&1 | head -n1
#ffmpeg version git-2013-05-18-5918b7a Copyright (c) 2000-2013 the FFmpeg developers

You will find here some useful settings to encode with ffmpeg.

ffmpeg Settings for HTML5 codecs (h264/mp4, theora/ogg, vp8/webm)
ffmpeg settings for converting to mp4 and ogg for HTML5 video
FFmpeg and x264 Encoding Guide
FFmpeg and AAC Encoding Guide
Theora and Vorbis Encoding Guide

#h264/aac: ie, safari, chrome, ios, android
ffmpeg.exe -i -H.264.mp4 -c:v libx264   -crf:v 22 -preset:v medium -c:a libvo_aacenc -b:a 56k -ac:a 2             -s:v 960x540 -r:v 24 a.mp4 #best compression
ffmpeg.exe -i -H.264.mp4 -c:v libx264   -crf:v 22 -preset:v medium -c:a libvo_aacenc -b:a 56k -ac:a 2                                  c.mp4 #best quality
ffmpeg.exe -i -H.264.mp4 -c:v libx264   -crf:v 25 -preset:v medium                            -an                 -s:v 158x88  -r:v 16 m.mp4 #tiny size
    
#theora/vorbis: firefox, chrome, opera
ffmpeg.exe -i -H.264.mp4 -c:v libtheora           -qscale:v 5      -c:a libvorbis             -ac:a 2 -qscale:a 3              -r:v 24 k.ogv #best compression
ffmpeg.exe -i -H.264.mp4 -c:v libtheora           -qscale:v 7      -c:a libvorbis             -ac:a 2 -qscale:a 5                      l.ogv #best quality
ffmpeg.exe -i -H.264.mp4 -c:v libtheora           -qscale:v 7      -c:a libvorbis             -an     -qscale:a 5 -s:v 158x88  -r:v 16 m.ogv #tiny size

#misc:
#ffmpeg.exe -i %1 -b 1500k -vcodec libx264 -vpre slow -vpre baseline                                           -g 30 -s 640x360 %1.mp4  #mp4  (H.264 / ACC)
#ffmpeg.exe -i %1 -b 1500k -vcodec libvpx                              -acodec libvorbis -ab 160000 -f webm    -g 30 -s 640x360 %1.webm #webm (VP8 / Vorbis)
#ffmpeg.exe -i %1 -b 1500k -vcodec libtheora                           -acodec libvorbis -ab 160000            -g 30 -s 640x360 %1.ogv  #ogv  (Theora / Vorbis)
#ffmpeg.exe -i %1 -ss 00:10 -vframes 1 -r 1 -s 640x360 -f image2                                                                %1.jpg  #jpeg (screenshot at 10 seconds)

Part II – Convert and Resize Videos for HTML5 with Scala and Java

A HTML5 video need to be encoded with specific video and audio codecs that must match the targeted browser.
There is no single combination of containers and codecs that works in all HTML5 browsers.
See here for the compatibility matrix: What Works on the Web .
Also here: Encode videos for the Web and HTML5 .
And for information about H.264 and licencing fees: MPEG LA News List .

Following Java file can be compiled and used by a Scala project that need to convert videos using Xuggle.

//written by francois scheurer 2013
package models.util;

import com.xuggle.xuggler.Converter;
//svn checkout http://xuggle.googlecode.com/svn/trunk/ xuggle-read-only
//git clone git://github.com/xuggle/xuggle-xuggler.git
import java.io.File;
//import java.io.IOException;

//video converting:
//http://springjavatutorial.blogspot.ch/2013/06/xuggler-video-conversion-mov-to-mp4.html
//http://stackoverflow.com/questions/3285529/iphone-cannot-play-mp4-h-264-video-file
//http://www.javacodegeeks.com/2011/02/xuggler-tutorial-transcoding-media.html
//http://www.jochus.be/site/2010-10-12/java/converting-resizing-videos-in-java-xuggler
//http://paultela.com/media-transcoding-with-xuggler/
//http://whaticode.com/2010/04/30/use-java-to-convert-any-media-type-to-flv-with-xuggler-part-2/
//http://whaticode.com/2010/04/15/use-java-to-convert-any-media-type-to-flv-with-xuggler/
//http://trac.ffmpeg.org/wiki/Scaling%20(resizing)%20with%20ffmpeg
  
public class VideoConvert { //changed by francois scheurer
  public static void run(String inputFilename, String outputFilename, int size) {
    File inputFile = new File(inputFilename);
    File outputFile = new File(outputFilename);
    //File apresetsFile = new File("lib/presets/libfaac-html5.ffpreset");
    //File vpresetsFile = new File("lib/presets/libx264-html5.ffpreset");
    //File presetsFile = new File("lib/presets/libx264-ultrafast.ffpreset");
    File vpresetsFile = new File("lib/presets/libx264-ipod640.ffpreset");

    Converter converter = new Converter(); // This is the converter object we will use.

    // These are the arguments to pass to the converter object.
    // For H264 transcoding, the -vpreset option is very
    // important. Here, presetsFile is a File object corresponding
    // to a libx264 video presets file. These are in the
    // /usr/local/share/ffmpeg directory.
    String[] arguments = new String[] { 
        inputFile.getAbsolutePath(),
        //"--help",
        //"--containerformat", "container-format",   //output container format to use (e.g. \"mov\")
        //"--icontainerformat", "icontainer-format", //input container format to use (e.g. \"mov\")
        //"--cpreset", "file",                       //container option presets file
        //"--ano",                                   //no audio
        //"--apreset", "file",                       //audio option presets file 
        "--acodec", "libfaac",                       //audio codec to encode with (e.g. \"libmp3lame\") 
        //"--iacodec", "icodec",                     //input audio codec  (e.g. \"libmp3lame\")
        "--asamplerate", "44100",                    //audio sample rate to (up/down) encode with (in hz) (e.g. \"22050\")
        //"--iasamplerate, "isample-rate",           //input audio sample rate to (up/down) encode with (in hz) (e.g. \"22050\")
        //"--achannels", "channels",                 //number of audio channels (1 or 2) to encode with (e.g. \"2\")
        //"--iachannels", "ichannels",               //input number of audio channels (1 or 2)
        "--abitrate", "64000",                       //bit rate to encode audio with (in bps) (e.g. \"60000\")
        //"--astream", "stream",                     //if multiple audio streams of a given type, this is the stream you want to output
	//"--aquality", "quality",                   //quality setting to use for audio.  0 means same as source; higher numbers are (perversely) lower quality.  Defaults to 0. 
        //"--vno",                                   //no video
        "--vpreset", vpresetsFile.getAbsolutePath(), //video option presets file 
        "--vcodec", "libx264",                       //video codec to encode with (e.g. \"mpeg4\")
        "--vsize", String.valueOf(size),             //max resolution to scale down output video (e.g. \"320\")
        //"--vscalefactor", "factor",                //scaling factor to scale output video by (e.g. \"0.75\") 
        "--vbitrate", "600000",                      //bit rate to encode video with (in bps) (e.g. \"60000\")
	//"--vbitratetolerance", "1200000",          //bit rate tolerance the bitstream is allowed to diverge from the reference (in bits) (e.g. \"1200000\")
        //"--vstream", "stream",                     //if multiple video streams of a given type, this is the stream you want to output
        //"--vquality", "quality",                   //quality setting to use for video.  0 means same as source; higher numbers are (perversely) lower quality.  Defaults to 0.  If set, then bitrate flags are ignored.
        //"--realtime",                              //attempt to encode frames at the realtime rate -- i.e. it encodes when the picture should play
        outputFile.getAbsolutePath()
        //inputFile.getParent() + "/" + inputFile.getName() + ".mp4",
        };

    try { // Finally, we run the transcoder with the options we provided.
      System.out.printf("begin conversion: " + java.util.Arrays.toString(arguments) + "\n");
      converter.run(converter.parseOptions(converter.defineOptions(), arguments));
      System.out.printf("wrote converted and resized video: %s\n", outputFilename);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Remember that in Scala, classes cannot have static methods, which need to be placed in their companion object.
So here it is necessary to call the Java static method ‘run’ with its companion object ‘VideoConvert’.

import VideoConvert
VideoConvert.run("test.mov", "test.mp4", 256) //shrink resolution of video and convert format

Et voilà! ^^

Those converted video are compatible with HTML5 for iPhones (Safari) and Android devices with Chrome.

Schreiben Sie einen Kommentar