Saturday, February 11, 2012

Building FFmpeg with libmp3lame for Android

The general idea is to create a static lame build (libmp3lame.a) using NDK, and then use it in the configuration and make scripts of ffmpeg, this way ffmpeg will recognize it a valid coder.
Its a little hackish, but its the only way that I managed to do it, I welcome anyone who can improve the process.
I used Ubuntu (had problems with cygwin) and NDK r5c, so I recommend this environment.

1. Follow the instructions to build ffmpeg using this post from halfninja (Thanks Ninja for the great guide!):
https://github.com/halfninja/android-ffmpeg-x264

2.
2.1 Download Lame version 3.98.4 from here (latest source didn't work for me):
http://sourceforge.net/projects/lame/files/lame/3.98.4/

2.2 Create libmp3lame directory in the jni folder and 

2.3 Copy all sources from directory /3.98.4/libmp3lame to jni/libmp3lame directory. Also copy lame.h which is located in include directory of lame sources.

2.4 Edit file jni/utils.h, and replace definition extern ieee754_float32_t fast_log2(ieee754_float32_t x); with this extern float fast_log2(float x);

2.5 Create folder jni/libmp3lame/lame

2.6 Copy jni/libmp3lame/*.h to jni/libmp3lame/lame

4. Create Android.mk_lame as follows:

 4.1

 LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE  := mp3lame

LOCAL_SRC_FILES := libmp3lame/bitstream.c libmp3lame/fft.c libmp3lame/id3tag.c libmp3lame/mpglib_interface.c libmp3lame/presets.c libmp3lame/quantize.c libmp3lame/reservoir.c libmp3lame/tables.c libmp3lame/util.c libmp3lame/VbrTag.c libmp3lame/encoder.c libmp3lame/gain_analysis.c libmp3lame/lame.c libmp3lame/newmdct.c libmp3lame/psymodel.c libmp3lame/quantize_pvt.c libmp3lame/set_get.c libmp3lame/takehiro.c libmp3lame/vbrquantize.c libmp3lame/version.c

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE  := mp3lame_a
LOCAL_STATIC_LIBRARIES := mp3lame

include $(BUILD_EXECUTABLE)


4.2
cp Android.mk_lame Android.mk (rename the original to Android.mk.ffmpeg-x264)


5. call ndk-build to build only lame, this will produce an error, but will also produce libmp3lame.a in the objects directory.

6. copy libmp3lame.a to libmp3lame.

7.
7.1 Edit configure_ffmpeg.sh as follows:

#!/bin/bash
pushd `dirname $0`
. settings.sh

#arm v7n
CPU=armv7-a
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm -march=$CPU -mtune=cortex-a8"
PREFIX=./android/$CPU 
ADDITIONAL_CONFIGURE_FLAG=--enable-neon

if [[ $minimal_featureset == 1 ]]; then
  echo "Using minimal featureset"
  featureflags="--disable-everything \
--enable-decoder=mjpeg --enable-demuxer=mjpeg --enable-parser=mjpeg \
--enable-demuxer=image2 --enable-muxer=mp4 --enable-encoder=libx264 --enable-libx264 \
--enable-decoder=rawvideo \
--enable-protocol=file \
--enable-hwaccels"
fi

if [[ $DEBUG == 1 ]]; then
  echo "DEBUG = 1"
  DEBUG_FLAG="--disable-stripping"
fi

pushd ffmpeg

./configure $DEBUG_FLAG --enable-cross-compile \
--arch=arm5te \
--enable-armv5te \
--target-os=linux \
--disable-stripping \
--prefix=../output \
--disable-neon \
--enable-version3 \
--disable-shared \
--enable-static \
--enable-gpl \
--enable-memalign-hack \
--cc=arm-linux-androideabi-gcc \
--ld=arm-linux-androideabi-ld \
--extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated" \
$featureflags \
--enable-libmp3lame \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-network \
--enable-filter=buffer \
--enable-filter=buffersink \
--disable-demuxer=v4l \
--disable-demuxer=v4l2 \
--disable-indev=v4l \
--disable-indev=v4l2 \
--extra-cflags="-I../x264 -I/home/ehasson/Downloads/android-ffmpeg-x264/Project/jni/libmp3lame -Ivideokit" \
--extra-ldflags="-L../x264 -L/home/ehasson/Downloads/android-ffmpeg-x264/Project/jni/libmp3lame" 

popd; popd



7.2 correct --extra-cflags and --extra-ldflags to your correct full path.


8. call config_make_everything, this should take about 10mins


9. Edit Android.mk.ffmpeg-x264 as follows:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE  := videokit
# These need to be in the right order
FFMPEG_LIBS := $(addprefix ffmpeg/, \
 libavdevice/libavdevice.a \
 libavformat/libavformat.a \
 libavcodec/libavcodec.a \
 libavfilter/libavfilter.a \
 libswscale/libswscale.a \
 libavutil/libavutil.a \
 libpostproc/libpostproc.a )
# ffmpeg uses its own deprecated functions liberally, so turn off that annoying noise
LOCAL_CFLAGS += -g -Iffmpeg -Ivideokit -Wno-deprecated-declarations 
LOCAL_LDLIBS += -llog -lz $(FFMPEG_LIBS) x264/libx264.a libmp3lame/libmp3lame.a
LOCAL_SRC_FILES := videokit/uk_co_halfninja_videokit_Videokit.c videokit/ffmpeg.c videokit/cmdutils.c
include $(BUILD_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_MODULE  := ffmpeg
FFMPEG_LIBS := $(addprefix ffmpeg/, \
 libavdevice/libavdevice.a \
 libavformat/libavformat.a \
 libavcodec/libavcodec.a \
 libavfilter/libavfilter.a \
 libswscale/libswscale.a \
 libavutil/libavutil.a \
 libpostproc/libpostproc.a )
LOCAL_CFLAGS += -g -Iffmpeg -Ivideokit -Wno-deprecated-declarations 
LOCAL_LDLIBS += -llog -lz $(FFMPEG_LIBS) x264/libx264.a libmp3lame/libmp3lame.a
LOCAL_SRC_FILES := ffmpeg/ffmpeg.c ffmpeg/cmdutils.c
include $(BUILD_EXECUTABLE)



10. cp Android.mk.ffmpeg-x264 Android.mk



11 call ndk-build

12. the libvideokit.so that contains ffmpeg with libmp3lame should be in the lib directory.

10 comments:

  1. hello, don't forget --enable-encoder=libmp3lame in ffmpeg's configure

    ReplyDelete
  2. Its enabled, see just after the featureflags in section 7.1

    ReplyDelete
  3. Was using 11.04 but I guess you can use any.

    ReplyDelete
  4. When I build ffmpeg with x264, I faced an error : libx264.a not exists.
    however, I solved it with additional configuration option

    --extra-libs="-lgcc"

    further, I add libswresample/libswresample.a to second Android.mk ,
    so the FFMPEG_LIBS looks like

    FFMPEG_LIBS := $(addprefix ffmpeg/, libavdevice/libavdevice.a \
    libavformat/libavformat.a libavcodec/libavcodec.a \
    libavfilter/libavfilter.a libswscale/libswscale.a \
    libavutil/libavutil.a libpostproc/libpostproc.a \
    libswresample/libswresample.a)

    this could solve the undefined symbol with sws_*

    after that, when I build shared object , I found that there is ffmpeg.c and cmdutil in videokit directory. and the version is not suitable for last ffmpeg.
    so I copied the ffmpeg/ffmpeg.c ffmpeg/cmdutils.c to the videokit directory.

    everything goes fine. thank you :)

    ReplyDelete
  5. You may want to check this project for FFmpeg port for Android:
    http://sourceforge.net/projects/ffmpeg4android/

    ReplyDelete
  6. Thank you so much! Tried so much for whole day and nothing worked :(
    ... except your tutorial.
    Realy thank you!

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi all
    2.4 Edit file jni/utils.h, and replace definition extern ieee754_float32_t fast_log2(ieee754_float32_t x); with this extern float fast_log2(float x);

    I don't see utils.h file in jni folder. Do you have any suggestion? Many Thanks

    ReplyDelete