Android GStreamer audio video playback optimization

From RidgeRun Developer Wiki


Overview

Audio/video playback on Android devices can often be optimized by taking advantage of the device's hardware accelerators. GStreamer supports plug-ins that make it easy to use those hardware accelerators. An iMX6 based hardware platform is used to demonstrate how to create an APK that supports hardware accelerated audio/video playback.

Note: These steps were run in ubuntu 14.04

Porting GStreamer 1.6.0 for Android

Cross-compiling GStreamer 1.6.0 core using Cerbero

Cerbero is a multi-platform build system for Open Source projects that builds and creates native packages for different platforms, architectures and distributions. Projects are defined using recipes files (.recipe), which provides a description of the project being built such as name, version, licenses, source packages and instruction to build the project. Recipes also provide listing of files, which are later used for the packaging. Moreover, the built projects can be combined into packages for distribution. These packages are, depending on the target platform, Windows or OS X installers or Linux packages.

The following steps cross-compile the GStreamer 1.6.0 static libraries using cerbero.

Clone Cerbero repository

cd $HOME
git clone git://anongit.freedesktop.org/gstreamer/cerbero cerbero
cd cerbero
git checkout 1.6.0

Set distribution version

Edit the ~/cerbero/config/cross-android-armv7.cbc file and set the target_distro_version like this.

target_distro_version = DistroVersion.ANDROID_JELLY_BEAN

Fetch all necessary dependent packages

./cerbero-uninstalled -c config/cross-android-armv7.cbc bootstrap

Build the GStreamer 1.6.0 package

./cerbero-uninstalled -c config/cross-android-armv7.cbc package gstreamer-1.0

list available packages

./cerbero-uninstalled -c config/cross-android-armv7.cbc list-packages

Set up and build Android Jelly Bean for Nitrogen6x

A Boundary Devices Nitrogen6x board is used for the target hardware.

Setting up your host machine to build the AOSP correctly

To get a successful compilation of the Android Open Source Project, make sure you have installed the following packages in your host computer.

sudo apt-get install bison g++-multilib git gperf libxml2-utils make python-networkx zlib1g-dev:i386 zip
sudo apt-get install libswitch-perl

Register and install SSH key

To get Android source code for the Boundary Devices boards, you need to register at the Boundary Devices website and then upload your public SSH key so you can get access to the repository. See this links for instructions:

Download repo tool

mkdir -p ~/bin
curl http://commondatastorage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

Create directory to hold Boundary Devices repository

mkdir myandroid
cd myandroid

Fetch Android SDK repository

~/bin/repo init -u git://github.com/boundarydevices/imx-android-r13.4-ga.git -b boundary-imx_jb4.3_1.0.0-ga
~/bin/repo sync

Configure environment

source build/envsetup.sh
lunch

For example, for Nitrogen you can use: 15. nitrogen6x-eng

Build the AOSP

make -j2

Cross-compiling GStreamer-i.MX using Cerbero

In order to build 'gstreamer-imx' and the 'imxeglvivsink' element, 3 dependencies are needed:

  • libimxvpuapi: an alternative to Freescale's VPU wrapper. Both the wrapper and this library are layered on top of imx-vpu , the low-level iMX6 VPU interface.
  • libvpu: which is included with the Android distribution for the iMX6 under external/linux-lib/vpu.
  • libfslvpuwrapper: v 1.0.45 or higher, provided by Freescale.

For this steps is assumed that the GStreamer core is already built as described above.

Extract kernel headers from your Android build

export KERNEL_HEADERS=/tmp/kernel_headers/usr/
cd $DEVDIR/myandroid/jb-4.3/kernel_imx
make headers_install ARCH=arm INSTALL_HDR_PATH=$KERNEL_HEADERS

Copy headers and library to the cerbero

cp myandroid/jb-4.3/external/linux-lib/vpu/vpu_lib.h ~/cerbero/dist/android_armv7/include
cp myandroid/jb-4.3/external/linux-lib/vpu/vpu_io.h ~/cerbero/dist/android_armv7/include
cp myandroid/jb-4.3/out/target/product/nitrogen6x/obj/lib/libvpu.so ~/cerbero/dist/android_armv7/lib

Create and update libimxvpuapi recipe

./cerbero-uninstalled add-recipe libimxvpuapi 0.10.0

Update ~/cerbero/recipes/libimxvpuapi.recipe by replacing the contents with the following recipe.

# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python
from cerbero.build.build import MakefilesBase

# Static
class Recipe(recipe.Recipe):

    name = 'libimxvpuapi'
    version = '0.10.0'
    licenses = [License.LGPL]
    commit = '0.10.0'
    remotes = {'origin': 'https://github.com/Freescale/libimxvpuapi.git'}
    config_sh = './waf configure'
    make = './waf build'
    make_install = './waf install'
 
    def configure(self):
        MakefilesBase.configure(self)

Build libimxvpuapi

./cerbero-uninstalled -c config/cross-android-armv7.cbc build libimxvpuapi

Create libimxvpuapi and libgstimxveglvivsink pkg-config files

Store the following information in the ~/cerbero/dist/android_armv7/lib/pkgconfig/libimxvpuapi.pc file:

prefix=/home/fquiros/cerbero/dist/android_armv7
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: libimxvpuapi
Description: interface library for i.MX6 VPU devices
Version: 0.10.0
Libs: -L${libdir} -limxvpuapi
Cflags: -I${includedir}

Store the following information in the ~/cerbero/dist/android_armv7/lib/pkgconfig/libgstimxveglvivsink.pc file:

prefix=/home/fquiros/cerbero/dist/android_armv7
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: imxeglvivsink
Description: custom OpenGL ES 2.x based video sink by using the Vivante direct textures, which allow for smooth playback.
Version: 0.12.0
Libs: -L${libdir} -landroid
Cflags: -I${includedir}

Create libfslvpuwrap recipe

./cerbero-uninstalled add-recipe libfslvpuwrap 1.0.46

Update ~/cerbero/recipes/libfslvpuwrap.recipe by replacing the contents with the following recipe.

# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python

class Recipe(recipe.Recipe):
    name = 'libfslvpuwrap'
    version = '1.0.46'
    licenses = [License.LGPL]
    stype = SourceType.TARBALL
    url = 'http://downloads.ridgerun.com/packages/libfslvpuwrap-1.0.46.tar.gz'

Build libfslvpuwrap

PLATFORM=IMX6Q DEST_DIR=~/cerbero/dist/android_armv7/ ./cerbero-uninstalled -c config/cross-android-armv7.cbc build libfslvpuwrap

Create and update gstreamer-imx recipe

./cerbero-uninstalled add-recipe gstreamer-imx 0.10.1

Update ~/cerbero/recipes/gstreamer-imx.recipe by replacing the contents with the following recipe.

# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python
from cerbero.build.build import MakefilesBase

# Static
class Recipe(recipe.Recipe):

    name = 'gstreamer-imx'
    version = '0.12.0'
    licenses = [License.LGPL]
    commit = '0.12.0'
    remotes = {'origin': 'https://github.com/Freescale/gstreamer-imx.git'}
    config_sh = './waf configure'
    configure_options = '--build-for-android --kernel-headers=$KERNEL_HEADERS --egl-platform=android'
    make = './waf build'
    make_install = './waf install'
    
    def configure(self):
        MakefilesBase.configure(self)

Fetch and build gstreamer-imx package

mkdir recipes/gstreamer-imx
wget -O recipes/gstreamer-imx/gstreamer-imx-0.10.1-patchset.tar.gz http://downloads.ridgerun.com/packages/gstreamer-imx-0.10.1-patchset.tar.gz
./cerbero-uninstalled -c config/cross-android-armv7.cbc build gstreamer-imx

Fetch helper files

Ah, so here is the magic.

wget http://downloads.ridgerun.com/packages/gstreamer-imx_android_required_files.tar.gz
tar -C ~/cerbero/dist/android_armv7/lib/pkgconfig -xzf gstreamer-imx_android_required_files.tar.gz

If you changed where files are getting installed, then adjust the prefix value in the *.pc files.

Building GStreamer plugins using Cerbero

Create and update gst1.0-fsl-plugins recipe

./cerbero-uninstalled add-recipe gst1.0-fsl-plugins 4.0.4

Update ~/cerbero/recipes/gst1.0-fsl-plugins.recipe by replacing the contents with the following recipe.

# -*- Mode: Python -*- vi:si:et:sw=4:sts=4:ts=4:syntax=python

# For Static (4.0.4)
class Recipe(recipe.Recipe):
    name = 'gst1.0-fsl-plugins'
    version = '4.0.4'
    licenses = [License.LGPL]
    stype = SourceType.TARBALL
    url = 'http://www.freescale.com/lgfiles/NMG/MAD/YOCTO/gst1.0-fsl-plugins-4.0.4.tar.gz'
    configure_options = '--enable-static-plugins --enable-static --disable-shared  PLATFORM=MX6 CFLAGS="-O0 -ggdb3 -I/tmp/kernel_headers/usr/include -I/home/<user>/cerbero/sources/local/gstreamer-imx/src/common/ -DGST_PLUGIN_BUILD_STATIC -funwind-tables -ffunction-sections -funwind-tables -fstack-protector -no-canonical-prefixes -fPIC -mthumb -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -DANDROID -DPIC -D__ARM_ARCH_7A__ -Wa,--noexecstack"'
    patches = ['gst1.0-fsl-plugins/0001-fixing-plugins-name.patch',
			   'gst1.0-fsl-plugins/0002-allow-set-bitrate-on-the-fly.patch',
			   'gst1.0-fsl-plugins/0006-avoid-tools-build-4.0.4.patch',
			   'gst1.0-fsl-plugins/0007-vpuenc-gst-imx-compatibility.patch',
			   'gst1.0-fsl-plugins/0008-v4l2-add-capture-type.patch']

Patch build and install gst1.0-fsl-plugins

mkdir recipes/gst1.0-fsl-plugins
wget http://downloads.ridgerun.com/packages/gst1.0-fsl-plugins-4.0.3-patchset.tar.gz
tar -C recipes/gst1.0-fsl-plugins -xzf gst1.0-fsl-plugins-4.0.3-patchset.tar.gz
./cerbero-uninstalled -c config/cross-android-armv7.cbc build gst1.0-fsl-plugins
cp ~/cerbero/dist/android_armv7/lib/gstreamer-1.0/libgstvpu.a ~/cerbero/dist/android_armv7/lib/gstreamer-1.0/static
cp ~/cerbero/dist/android_armv7/lib/gstreamer-1.0/libgstvpu.la ~/cerbero/dist/android_armv7/lib/gstreamer-1.0/static

Improving the GStreamer 1.6.0 audio/video pipeline

Assuming that the previous steps were completed successfully, follow these additional instructions in order to get a better CPU performance and smooth playback when the respective GStreamer 1.6.0 Android application. In this section, the approach used consists in a pipeline that uses the 'imxeglvivsink' element, where you need to add the following linking libraries, GStreamer plugins and dependences in the Android.mk file of your application.

LOCAL_LDLIBS              := -llog -landroid -lGLESv2 -lEGL
GSTREAMER_PLUGINS         := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_PLUGINS_EFFECTS) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_CODECS_RESTRICTED) vpu imxvpu avi isomp4 imxeglvivsink
GSTREAMER_EXTRA_DEPS      := gstreamer-video-1.0 libgstvpu libgstimxvpu libfslvpuwrap libgstimxcommon libimxvpuapi libgstimxeglvivsink 

GStreamer 1.6.0 audio/video playback pipeline

This section shows both the pipeline used in the source code of the application, where the 'imxeglvivsink' elements was used in order to display the audio and video playback in an Android window.

  • 'imxeglvivsink' pipeline
'C' SOURCE CODE PIPELINE

MOVIE=/tmp/bigbuckbunny.mp4
gst_parse_launch("playbin uri=file://$MOVIE video-sink='imxeglvivsink'", &error);

CPU Performance

Run the playback by hitting 'Play' on the Android interface, where the performance measurements recorded were the following and they demonstrate that the performance improves and the video is playing smoother.

  • 'imxeglvivsink' pipeline, using 'vpudec'
HIGHEST CPU LOAD

User 16%, System 10%, IOW 0%, IRQ 0%
User 69 + Nice 0 + Sys 44 + Idle 307 + IOW 0 + IRQ 0 + SIRQ 0 = 420

PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
3383  2  19% S    25 952436K  45372K  fg u0_a78   com.gst_sdk_tutorials.tutorial_3
LOWEST CPU LOAD

User 4%, System 10%, IOW 0%, IRQ 0%
User 16 + Nice 0 + Sys 39 + Idle 329 + IOW 0 + IRQ 0 + SIRQ 0 = 384

PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
3383  2   8% S    25 952604K  45576K  fg u0_a78   com.gst_sdk_tutorials.tutorial_3
  • 'imxeglvivsink' pipeline, using 'imxvpudec'
HIGHEST CPU LOAD

User 19%, System 6%, IOW 0%, IRQ 0%
User 83 + Nice 0 + Sys 29 + Idle 310 + IOW 0 + IRQ 0 + SIRQ 1 = 423

PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
3312  0  19% S    25 956436K  45668K  fg u0_a48   com.gst_sdk_tutorials.tutorial_3
LOWEST CPU LOAD

User 3%, System 15%, IOW 0%, IRQ 0%
User 15 + Nice 0 + Sys 62 + Idle 312 + IOW 0 + IRQ 0 + SIRQ 0 = 389

PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
3312  0   9% S    25 964352K  45744K  fg u0_a48   com.gst_sdk_tutorials.tutorial_3