[출처] : http://whiteduck.tistory.com/130
참고로 cygwin에서 하다보면 경로 문제로 인해서 빌드가 안되는 경우가 있다.
아래를 참고하자고!!! 나는 이렇게 해버렸다. 아예 복사를 해버렸어.. 경로가 문제 되니까.
그리고 /home/erato/android-ndk-r14b을 build.sh에 넣어주었다.
cp /cygdrive/d/android/android-ndk-r14b-windows-x86_64/android-ndk-r14b/ . -r
근데 안된다.. 그냥 우분투에서 해라!
=================================================================================
|| 개발 환경 ||
1. OS : Ubuntu 16.04 ( VMware workstation 12 ) : https://www.ubuntu.com/download/desktop
2. Android Studio 2.2.3 : https://developer.android.com/studio/index.html?hl=ko
3. RAM 3GB ( VMware에서 돌아가기 위해서 충분한 RAM 이 필요 )
4. Memory : 30GB ( VMware에서 돌아가기 위해서 충분한 메모리가 필요 )
// Ubuntu 설치 이후부터 설명하며 한글은 지원하지 않은 채로 진행합니다. //
// USB 인식이 안 될 수 있습니다. VMWare 업데이트를 진행한 후에 팔로우하시기 바랍니다. //
// 저자의 ubuntu id는 user입니다. //
|| Android studio 설치 ||
참고 : Android 설치 환경 구축 : https://www.davidlab.net/ko/tech/how-to-setup-android-dev-env-on-ubuntu-part1/
1. JDK 설치
| $ sudo add-apt-repository ppa:webupd8team/java |
| $ sudo apt-get update |
| $ sudo apt-get install oracle-java8-installer |
2. Android 설치
// 저자는 앞으로 다운로드하는 모든 항목은 Downloads에 모두 저장하였습니다. //
3. gmtp 설치 [ cpu가 VT-x 를 지원하지 않을 경우, 또는 실 장비 테스트가 필요한 경우 ]
// 저자의 경우에는 그냥 안되서 실 장비 테스트를 하였습니다. //
참고 : http://askubuntu.com/questions/146529/how-to-connect-mtp-devices-via-usb
| $ sudo apt-get install gmtp |
4. SDK 설치
4.1. AS(Android Studio 실행)
| $ cd /home/user/Downloads/android-studio/bin/ |
| $ ./studio.sh |
4.2 SDK Manager
Configure > SDKManager
4.3 install SDK
하고 싶은 SDK 선택
4.4 install SDK tool
NDK 설치
|| FFmpeg 빌드 ||
참고 : http://dev2.prompt.co.kr/77
1. FFMpeg 다운로드 : https://ffmpeg.org/download.html#releases
2. FFMpeg 파일 이동
미리 설치했던 NDK 폴더 하위의 sourses 폴더에 집어넣습니다.
3. Configure 파일 수정
configure 파일은 ffmpeg-3.2.2 파일 안에 있습니다. 이 파일을 변경하지 않으면 빌드 후 so 파일이 만들어질때 *.so.57 과 같은 형식으로 만들어서 AS에 적용되지 않습니다.
4. build script 작성
ffmpeg-3.2.2 하위에 build_android.sh 생성
| NDK=$HOME/Android/Sdk/ndk-bundle |
| SYSROOT=$NDK/platforms/android-16/arch-arm/ |
| TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 |
|
|
| function build_one |
|
|
| { |
| ./configure \ |
| --prefix=$PREFIX \ |
| --enable-shared \ |
| --disable-static \ |
| --disable-doc \ |
| --disable-ffmpeg \ |
| --disable-ffplay \ |
| --disable-ffprobe \ |
| --disable-ffserver \ |
| --disable-avdevice \ |
| --disable-doc \ |
| --disable-symver \ |
| --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \ |
| --target-os=linux \ |
| --arch=arm \ |
| --enable-cross-compile \ |
| --sysroot=$SYSROOT \ |
| --extra-cflags="-Os -fpic $ADDI_CFLAGS" \ |
| --extra-ldflags="$ADDI_LDFLAGS" \ |
| $ADDITIONAL_CONFIGURE_FLAG |
| make clean |
| make |
| make install |
| } |
|
|
| CPU=arm |
| PREFIX=$(pwd)/android/$CPU |
| ADDI_CFLAGS="-marm" |
| build_one |
5. build
| $ chmod +x build_android.sh |
| $ ./build_android.sh |
|| JNI 환경 구축 ||
참고 : http://dev2.prompt.co.kr/78
1. 프로젝트 생성
일반적인 프로젝트 생성후 Project로 보기
2. 오른쪽 클릭 tool 생성 [ 편의성을 위해서 사용합니다. ]
File > Setting 클릭 ( 키 중첩으로 Ctrl + Alt + S 가 먹히지 않습니다. )
2.1. 추가
| Name : javah |
| Program : /usr/lib/jvm/java-8-oracle/bin/javah |
| Parameters : -classpath $ClassPath$ -v -jni $FileClass$ |
| Working directory : $ModuleFileDir$/src/main/jni |
|
|
| Name : ndk-build |
| Program : /home/user/Android/Sdk/ndk-bundle/build/ndk-build |
| Working directory : $ProjectFileDir$/app/src/main |
|
|
| Name : ndk-build clean |
| Program : /home/user/Android/Sdk/ndk-bundle/build/ndk-build |
| Parameters : clean |
| Working directory : $ProjectFileDir$/app/src/main |
|| FFMpeg Setting ||
참고 : http://dev2.prompt.co.kr/79 http://dev2.prompt.co.kr/80
1. ffmpeg Android.mk 세팅 [ ffmpeg에서 so 파일을 가져오기 위한 설정 ]
$NDK/sources/ffmpeg-3.2.2/android/arm/Android.mk 를 다음 내용으로 만듭니다.
실제 파일이 정말로 있는지 확인한 후에 진행합니다.
| LOCAL_PATH:= $(call my-dir) |
|
|
| include $(CLEAR_VARS) |
| LOCAL_MODULE:= libavcodec |
| LOCAL_SRC_FILES:= lib/libavcodec-57.so |
| LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include |
| include $(PREBUILT_SHARED_LIBRARY) |
|
|
| include $(CLEAR_VARS) |
| LOCAL_MODULE:= libavformat |
| LOCAL_SRC_FILES:= lib/libavformat-57.so |
| LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include |
| include $(PREBUILT_SHARED_LIBRARY) |
|
|
| include $(CLEAR_VARS) |
| LOCAL_MODULE:= libswscale |
| LOCAL_SRC_FILES:= lib/libswscale-4.so |
| LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include |
| include $(PREBUILT_SHARED_LIBRARY) |
|
|
| include $(CLEAR_VARS) |
| LOCAL_MODULE:= libavutil |
| LOCAL_SRC_FILES:= lib/libavutil-55.so |
| LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include |
| include $(PREBUILT_SHARED_LIBRARY) |
|
|
| include $(CLEAR_VARS) |
| LOCAL_MODULE:= libavfilter |
| LOCAL_SRC_FILES:= lib/libavfilter-6.so |
| LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include |
| include $(PREBUILT_SHARED_LIBRARY) |
|
|
| include $(CLEAR_VARS) |
| LOCAL_MODULE:= libswresample |
| LOCAL_SRC_FILES:= lib/libswresample-2.so |
| LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include |
| include $(PREBUILT_SHARED_LIBRARY) |
2. build.gradle 수정
src 하위의 build.gradle을 수정합니다.
| sourceSets.main { |
| jni.srcDirs=[] |
| jniLibs.srcDirs "src/main/libs" |
| } |
3. jni의 Android.mk, Application.mk 작성
src의 main에 jni을 생성하고 Android.mk와 Application.mk를 작성합니다.
| LOCAL_PATH := $(call my-dir) |
| include $(CLEAR_VARS) |
| LOCAL_MODULE := ffmpeg |
| LOCAL_SRC_FILES := ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c |
| LOCAL_LDLIBS := -llog |
| LOCAL_SHARED_LIBRARIES := libavformat libavcodec libswscale libavutil libswresample libavfilter |
| include $(BUILD_SHARED_LIBRARY) |
| $(call import-module, ffmpeg-3.2.2/android/arm) |
보면 알겠지만 ffmpeg라이브러리 이름을 맞춰줘야 한다.
LOCAL_MODULE은 호출되는 ModuleName이며 나중에 loadLibrary와 쌍을 이룬다.
build.gradle에 다음과 같이 써야 적용된다.
|| JNI 컴파일 및 간단한 예제 ||
예제는 http://dev2.prompt.co.kr/80 와 같이 비디오 파일을 복사하는 것이며
ffmpeg 명령은
ffmpeg -i input.mp4 -filter:v crop=300:400:10:20 output.mp4
이라도 한다.
저자는 Download 폴더 밑 demo.mp4 파일을 output.mp4 파일로 복사하였다.
1. AndroidManifest.xml 변경
외부 입력 장치 접근 권한 허가
| <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
2. MainActivity.java 변경
ndk는 NDKAdapter를 나중에 추가할 것이며, 메소드는 run_ffmepg이다.
WRITE_EXTERNAL_STORAGE가 있어야 최신 AndroidVersion에서 외부 저장장치 접근이 가능하다.
| package com.gluesys.nvr; |
|
|
| import android.Manifest; |
| import android.content.pm.PackageManager; |
| import android.os.Environment; |
| import android.support.v4.app.ActivityCompat; |
| import android.support.v4.content.ContextCompat; |
| import android.support.v7.app.AppCompatActivity; |
| import android.os.Bundle; |
| import android.util.Log; |
|
|
| import com.gluesys.util.NDKAdapter; |
|
|
| import java.io.File; |
|
|
| public class MainActivity extends AppCompatActivity { |
|
|
| final int MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE =0; |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| setContentView(R.layout.activity_main); |
|
|
| if (ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) { |
| // 이 권한을 필요한 이유를 설명 |
| // It can see the file list in external storage. |
| if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)) { |
| // 다이어로그같은것을 띄워서 사용자에게 해당 권한이 필요한 이유에 대해 설명합니다 |
| // 해당 설명이 끝난뒤 requestPermissions()함수를 호출하여 권한허가를 요청해야 합니다 |
| } else { |
| ActivityCompat.requestPermissions(this, |
| new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, |
| MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); |
|
|
| // 필요한 권한과 요청 코드를 넣어서 권한허가요청에 대한 결과를 받아야 합니다 |
| } |
| } |
| else { |
| new NDKAdapter().run_ffmpeg(); |
| } |
| } |
|
|
| @Override |
| public void onRequestPermissionsResult(int requestCode, |
| String permissions[], int[] grantResults) { |
| switch (requestCode) { |
| case MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: |
|
|
| if (grantResults.length > 0 |
| && grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
| // 권한 허가 |
| // 해당 권한을 사용해서 작업을 진행할 수 있습니다 |
| new NDKAdapter().run_ffmpeg(); |
| } else { |
| // 권한 거부 |
| // 사용자가 해당권한을 거부했을때 해주어야 할 동작을 수행합니다 |
| } |
| return; |
| } |
| } |
| } |
3. NDKAdapter.java 생성
간접적으로 ffmpeg 라이브러리에 접근하기 위한 클래스이다.
| package com.gluesys.util; |
|
|
| /** |
| * Created by user on 12/12/16. |
| */ |
|
|
| public class NDKAdapter { |
| static { |
| System.loadLibrary("ffmpeg"); |
| } |
|
|
| public native int run_ffmpeg(); |
| } |
4. ffmpeg 라이브러리 가져오기
jni파일에 ffmpeg에 있던 *.h 밑 .c 파일을 가져온다. ( 파일 통채로 복사해도 됨 )
5. javah NDKAdapter
이것을 통해서 jni에 직접적으로 ffmpeg에 접근할 것을 만들어준다.
참고로 Build > Make Project를 하지 않으면 안 만들어 진다.
6. com_gluesys_util_NDKAdapter.h 확인
해당 폴더에는 (5) 번에서 진행되어 컴파일된 .h파일이 있다. 안에는 사용되는 클래스와 메소드가 나오는데 이에 따라 직접적으로 연결시킬 메소드의 이름을 알수 있다.
|
|
| /* |
| * Class: com_gluesys_util_NDKAdapter |
| * Method: run_ffmpeg |
| * Signature: ()I |
| */ |
| JNIEXPORT jint JNICALL Java_com_gluesys_util_NDKAdapter_run_1ffmpeg (JNIEnv *, jobject); |
7. ffmpeg.c 수정
jni 파일에 보면 ffmpeg..c 파일이 있다. 거기에는 main()이 있는데 이를 run_ffmpeg으로 수정한다.
| //int main(int argc, char **argv) |
| int run_ffmpeg(int argc, char **argv) |
7.1 상단에는 include와 define을 정의하고
| // test - start // |
| #include <jni.h> |
| #include <android/log.h> |
| #define LOG_TAG "veaver" |
| #define LOGI(...) __android_log_print(4, LOG_TAG, __VA_ARGS__); |
| #define LOGE(...) __android_log_print(6, LOG_TAG, __VA_ARGS__); |
| // test -end // |
7.2최 하단에는 (6)에서 만들어진 메소드에 다음과 같은 소스를 기입한다. [ 실제로 테스트의 모바일 저장장치에는 /Download 밑에 demo.mp4가 있어야 한다. ]
| JNIEXPORT jint JNICALL Java_com_gluesys_util_NDKAdapter_run_1ffmpeg(JNIEnv *env, jobject obj) |
| { |
| // /sdcard/input.mp4 가 있다는 가정하에 작성된 코드 입니다. |
|
|
| // 아래의 명령어를 수행하는 코드입니다. |
| // ffmpeg -i /sdcard/input.mp4 -filter:v crop=300:400:10:20 /sdcard/output.mp4 |
| char* a0 = "ffmpeg"; |
| char* a1 = "-i"; |
| char* a2 = "/storage/emulated/0/Download/demo.mp4"; |
| char* a3 = "-filter:v"; |
| char* a4 = "crop=300:400:10:20"; |
| char* a5 = "/storage/emulated/0/Download/output.mp4"; |
|
|
| char* argv[6]; |
| argv[0] = a0; |
| argv[1] = a1; |
| argv[2] = a2; |
| argv[3] = a3; |
| argv[4] = a4; |
| argv[5] = a5; |
|
|
| LOGI("call run_ffmpeg"); |
| run_ffmpeg(6, &argv); |
| } |
8. 다 작성되었으면 ndk-build를 실행한다.
9. 실행 (run) 하면 메인 화면이 크다면 300x400 만큼 짤린 비디오가 생성되어 있을 것이다.
/Dowdloads
└ demo.mp4
└ output.mp4
그 외 참고 자료 :
* Android Studio에서 JNI 연동 #1 [ http://blog.naver.com/PostView.nhn?blogId=just4u78&logNo=220630233740&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView ]
|| 2016.12.21 일자 추가 사항 ||
.ffmpeg 명령을 java에서 동작할 수 있도록 수정
1. MainActivity.java
| String a0 = "ffmpeg"; |
| String a1 = "-i"; |
| String a2 = "/storage/emulated/0/Download/demo.mp4"; |
| String a3 = "-filter:v"; |
| String a4 = "crop=300:400:10:20"; |
| String a5 = "/storage/emulated/0/Download/output.mp4"; |
|
|
|
|
| String[] argv = new String[]{a0,a1,a2,a3,a4,a5}; |
|
|
|
|
| // 동작 |
| new NDKAdapter().run_ffmpeg(argv); |
2. NDKActivity.java
| public native int run_ffmpeg(String[] cmdLine); // It is thread. |
3. ffmpeg.c
| JNIEXPORT jint JNICALL Java_com_gluesys_util_NDKAdapter_run_1ffmpeg(JNIEnv *env, jobject obj, jobjectArray cmdLines) |
| { |
|
|
| jstring jstr; |
| jsize len = (*env)->GetArrayLength(env, cmdLines); |
| char **argv = (char **) malloc(len*sizeof(char *)); |
|
|
| int i=0; |
|
|
| for (i=0 ; i<len; i++) { |
| jstr = (*env)->GetObjectArrayElement(env, cmdLines, i); |
| argv[i] = (char *)(*env)->GetStringUTFChars(env, jstr, 0); |
|
|
| } |
|
|
| LOGI("call run_ffmpeg"); |
| if( len > 0) |
| run_ffmpeg(len, argv); |
| } |
4. 실행 (run) 하면 메인 화면이 크다면 300x400 만큼 짤린 비디오가 생성되어 있을 것이다.
/Dowdloads
└ demo.mp4
└ output.mp4
출처: http://whiteduck.tistory.com/130 [이헌제의 블로그]
ffmpegtmp폴더도 만들었는데 왜 이런 걸까요 ?
맥입니다.
Hongsungjunui-MacBook-Pro:ffmpeg hongsungjun$ ./config.sh
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc is unable to create an executable file.
C compiler test failed.
If you think configure made a mistake, make sure you are using the latest
version from Git. If the latest version fails, report the problem to the
ffmpeg-user@ffmpeg.org mailing list or IRC #ffmpeg on irc.freenode.net.
Include the log file "config.log" produced by configure as this will help
solving the problem.
Hongsungjunui-MacBook-Pro:ffmpeg hongsungjun$
로그 파일은
END /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.goXeRHOP.c
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -E -o /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.qjgCC7XV.o /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.goXeRHOP.c
check_ld cc
check_cc
BEGIN /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.goXeRHOP.c
1 int main(void){ return 0; }
END /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.goXeRHOP.c
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc -marm -march=armv7-a -mfloat-abi=softfp -mfpu=neon -march=armv7-a -c -o /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.qjgCC7XV.o /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.goXeRHOP.c
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc -Wl,--fix-cortex-a8 -o /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.ExgNPyY2 /var/folders/8t/gk5ft7ts7lq4fb28mbqg42fm0000gn/T//ffconf.qjgCC7XV.o
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: error: cannot open crtbegin_dynamic.o: No such file or directory
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: error: cannot open crtend_android.o: No such file or directory
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: error: cannot find -lc
/Users/hongsungjun/Documents/android-ndk-r9c/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: error: cannot find -ldl
collect2: ld returned 1 exit status
C compiler test failed.