Android NDK FFmpeg 컴파일 강좌 (4/4)
안녕하세요 ^^
마지막 강좌로 실제 컴파일을 완료하고 간단한 플레이어를 만들어 동영상을 돌려보겠습니다.
8. 간단한 Player 소스 작성 (1)
일단은 컴파일 테스트만 해보기 위해서 빈 소스를 작성해 봅니다.
g:/Root/FFmpegBasic/jni/ 폴더 밑에 BasicPlayer 폴더를 만듭니다.
다음과 같은 4개의 파일을 작성합니다.
* Android.mk
01.
LOCAL_PATH := $(call my-dir)
02.
03.
include $(CLEAR_VARS)
04.
05.
LOCAL_MODULE := libbasicplayer
06.
LOCAL_SRC_FILES := BasicPlayer.c Interface.c
07.
08.
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../ffmpeg/ \
09.
$(LOCAL_PATH)/../ffmpeg/libavcodec \
10.
$(LOCAL_PATH)/../ffmpeg/libavformat \
11.
$(LOCAL_PATH)/../ffmpeg/libswscale
12.
13.
LOCAL_STATIC_LIBRARIES := libavformat libavcodec libswscale libavutil cpufeatures
14.
15.
LOCAL_LDLIBS := -lz -ljnigraphics
16.
17.
LOCAL_ARM_MODE := arm
18.
19.
include $(BUILD_SHARED_LIBRARY)
20.
21.
$(call import-module,android/cpufeatures)
* Interface.c
1.
#include <jni.h>
2.
#include <android/bitmap.h>
3.
#include "BasicPlayer.h"
4.
5.
void
Java_net_jbong_FFmpegBasic_MoviePlayView_initBasicPlayer(JNIEnv *env, jobject thiz)
6.
{
7.
av_register_all();
8.
}
* BasicPlayer.h
1.
#ifndef BASICPLAYER_H__INCED__110326
2.
#define BASICPLAYER_H__INCED__110326
3.
4.
#endif
* BasicPlayer.c
1.
#include "avcodec.h"
2.
#include "avformat.h"
3.
#include "swscale.h"
4.
#include "BasicPlayer.h"
9. FFmpeg 컴파일
cygwin에서 g:/Root/FFmpegBasic 폴더로 이동한 후 다음 명령으로 컴파일을 수행합니다.
$ ndk-build
시간이 제법 걸립니다.
Warning 메시지가 제법 뜨겠지만 컴파일은 Error없이 잘 수행됩니다.
10. 간단한 Player 소스 작성 (2)
컴파일이 잘 되는 것을 확인 했으니 실제 폰에서 FFmpeg 가 잘 돌아가는지 테스트 해보겠습니다.
간단한 동영상 플레이어를 만듭니다.
이 동영상 플레이어는 가장 기본적인 FFmpeg 테스트 소스입니다.
음성은 나오지도 않고, 영상의 sync도 전혀 맞지 않습니다.
화면이 잘릴 수도 있고, 프로그램이 종료 될 때 자원 해제도 완전하지 않을 수 있습니다.
하지만 어찌되었던 영상이 돌아가긴 돌아갑니다. ^^
소스 파일은 첨부드리니 다운로드 받아서 적용해 보시기 바랍니다.
g:/Root/FFmpegBasic/BasicPlayer 폴더에 다음 4개 소스 파일을 넣습니다.
Android.mk, BasicPlayer.c, BasicPlayer.h, Interface.c
(아까 작성한 빈 소스 파일을 다 덮어쓰시면 됩니다)
g:/Root/FFmpegBasic/src/net/jbong/FFmpegBasic 폴더의 FFmpegBasic.java 파일도 첨부파일로 덮어씁니다.
FFmpegBasic.java 파일을 열어 아래와 같은 부분을 찾습니다.
1.
String fname =
"/mnt/sdcard/HSTest/T4_MVI_1498.AVI"
;
제 폰에 들어있는 동영상 파일의 경로명이 적혀 있습니다.
sdcard에 적당한 동영상 파일을 하나 넣어두고 해당 파일의 경로명으로 수정합니다.
이제 스마트폰을 연결한 후 실행해 봅니다.
동영상이 나오면 성공입니다 ^-^
11. FFmpeg 공부
이제 컴파일을 성공했지만 기본적인 플레이어를 만드는 것도 아직 많은 과정이 남아 있습니다.
다음 강좌가 도움이 될 것입니다.
http://dranger.com/ffmpeg/
총 8편의 강좌를 여러번 읽어서 완전히 이해하신 후 이 내용을 Android에 맞게 수정하시면 잘 돌아갑니다.
위의 강좌가 아주 좋긴 하지만, FFmpeg 최신 버전에 맞지 않는 부분도 많이 있고,
배포할 수 있을 정도의 플레이어를 만들기에는 기능이 부족합니다.
더 필요한 기능은 ffmpeg 폴더 밑에 ffplay.c 소스 파일을 분석해서 구현합니다.
ffplay.c는 ffmpeg와 같이 배포되는 기본 플레이어기 때문에 가장 최신 모범 답안이라고 할 수 있습니다.
소스가 다소 방대하게 느껴지기는 하지만 반복해서 읽어보면 충분히 정복 가능한 분량입니다.
물론 쉽지는 않습니다. 상당한 시행착오와 삽질이 필요하긴 합니다.
소스를 분석하시다가 종종 FFmpeg의 Doxygen 문서를 참고하시는게 좋습니다.
http://ffmpeg.org/doxygen/trunk/annotated.html
http://ffmpeg.org/doxygen/trunk/globals.html
12. 라이센스 관련
FFmpeg는 LGPL을 따릅니다.
자세한 사항은 구글링을 해보시기 바랍니다.
요점을 정리하면 다음과 같습니다.
"FFmpeg와 static 하게 link되는 소스는 모두 공개해야 합니다"
이 말은 저작권을 어기지 않게 최대한 조심한다고 할 때,
제작하신 어플의 C 코드는 일부 (또는 전부) 공개해야 할 수도 있지만
Java 코드는 전혀 공개하지 않아도 된다는 뜻입니다.
즉, "FFmpeg로 어플을 만들면 소스를 공개해야 해서 남들이 내 어플을 허락없이 막 배포하면 어쩌지?" 하는 걱정은 안하셔도 됩니다.
Java 코드 없이는 어플로 쓸 수 없을테니까요...
13. 참고 사항 및 끝내는 말
제가 강좌 첫 부분에 프로젝트 경로명을 길게 잡지 말라고 말씀드렸습니다.
너무 길게 잡으면 아래와 같은 오류가 뜰 수 있습니다.
make: execvp: /cygdrive/c/android-ndk-r5b/ [...중략...]: Argument list too long
한마디로 make 하는데 명령이 너무 길어서 처리를 못하겠다는 뜻입니다.
경로가 길어지면 이런 현상이 발생할 수 있으므로
이런 에러가 발생하면 c:/FFmpegBasic 과 같이 매우 간단한 경로로 프로젝트 폴더를 복사해서 다시 컴파일 해보시기 바랍니다.
강좌를 정신없이 적었는데 잘 되었는지 모르겠습니다.
관련 어플을 기획하거나 제작하고 계신 분들에게 조금이라도 도움이 되었으면 합니다.
긴 강좌 읽어주셔서 감사합니다 ^^

NDK 하면 할 수록 재미가 있는거 같습니다.
물론 성공해야 재미있지만요 ^^
좋은 자료 감사합니다. 나중에 함 해봐야 겠네요 ㅋㅋ
그런의미에서 추천한방~
안녕하세요. 강좌는 잘 보았습니다. ^^
컴파일 과정을 그대로 따라해보았는데요.
ndk-build 명령을 내리면 common.mk를 찾을 수 없다고 나와서. ffmpeg폴더안으로 common.mk파일을 올려놓고 다시 build명령을 내리니 아무일도 일어나질 않네요.... 아무일도 일어나지 않아서.. 어느부분을 손대야 할 지 막막해서 그런데 답변 주시면 감사하겠습니다..
^^ 읽어주셔서 감사합니다.
덧붙이기전에는 build 가 잘됬는데요 소스를 확인하기 위해서 파일을 덧붙이고 나니까.
BasicPlayer.h 104줄에서 sws_getCachedContext 과 sws_scale undefined reference 라는
오류로 인해 진행이 되지 않고 있습니다.
그래서 2개의 정의부를 찾아봤는데 안보여서 그러는데 어떻게 해야 하나요?
컴파일 마지막즈음에 에러가 나서 질문을 드립니다.
컴파일 마지막부분에 cannot find -ljnigraphics라고 나오는데요..
android내의 platform 폴더를 찾아보니 /usr/lib안에 존재하는데.. 계속 찾을 수 없다고 나오네요...
해결방법을 아신다면 답변 주시면 정말 감사하겠습니다.. ^^.
글쎄요...
C:/android-ndk-r5b/docs/STABLE-APIS.html 에 보시면 jnigraphics library 사용법이 나옵니다.
읽어보시면 알겠지만
Android.mk에
LOCAL_LDLIBS += -ljnigraphics 추가하는 거랑
c 코드에서
<android/bitmap.h> include 하는 것 외에 다른 추가할 사항은 없습니다만...
참고로 jnigraphics library는 Android API level 8 이상에서만 지원됩니다. (2.2 프로요 이상)
혹시 프로젝트를 만드실때 2.2 미만 버전을 타겟으로 하신건 아닌지요?
MAC에서 작업했는데 잘 됩니다! 감사합니다
(이상하게 0.71이나 0.8은 이전부터 컴파일 후에 안되서 0.63이용했습니다 iOS에서는 잘 되는데 모르겠네요)
실제로 소프트웨어 디코딩보다 2배정도 성능향상이 있네요 (감동)
decode랑 sws_scale 양쪽 모두 향상이 되네요
다시 한번 감사 인사 드립니다.
make 에서
Argument list too long
에러가 나왔는데요. 이것을 path 가 긴 경우인가요?
c:\workspace\FFmpegBasic 이라고 만들었는데요.
ndk-build 에서 명령어를 찾을 수 없다고 나와서
/cygdrive/c/android-ndk-r6/ndk-build
라고 넣었거든요.
혹시 이 문제인가 싶어서 컴퓨터 설정에 Path 에 ndk 디렉토리 추가하고, C:/ 밑으로 디렉토리 옮기고 다시 시도중입니다 ^^
컴파일 완료되었네요.
감사합니다.
System.loadLibrary("basicplayer");
이 부분에서 계속 죽는데... 무슨 일 때문일가요?!
올려주신 프로젝트도 동일한 부분에서 죽는데...ㅠㅠ
음 잘 사용하고 있었는데 갚자기 다음과 같은 에러가 나네요. 왜 그럴까요?
In file included from C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/intmath.h:31,
from C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/common.h:99,
from C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/avutil.h:125,
from C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/imgutils.h:27,
from C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/vp8.c:25:
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h: In function 'get_quants':
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: warning: asm operand 2 probably doesn't match constraints
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: warning: asm operand 2 probably doesn't match constraints
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: warning: asm operand 2 probably doesn't match constraints
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: warning: asm operand 2 probably doesn't match constraints
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: warning: asm operand 2 probably doesn't match constraints
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: warning: asm operand 2 probably doesn't match constraints
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: error: impossible constraint in 'asm'
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: error: impossible constraint in 'asm'
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: error: impossible constraint in 'asm'
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: error: impossible constraint in 'asm'
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: error: impossible constraint in 'asm'
C:/SRC/FFmpegBasic/jni/ffmpeg/libavcodec/../libavutil/arm/intmath.h:81: error: impossible constraint in 'asm'
make: *** [/cygdrive/c/SRC/FFmpegBasic/obj/local/armeabi-v7a/objs-debug/avcodec/vp8.o] Error 1
어제 MinGW/MSYS를 설치하긴 했습니다만 혹시 그 것과 관련이 있는것일까요?
android sdk-r12-windows
android-ndk-r6-windows
FFmpeg0.8.1
에서 컴파일 성공하였습니다.
개발환경 여기까지 지장없었습니다.^^
음 제 개발환경에서는 0.8.1 소스가 계속 위에서 말했던 컴파일 에러가 나네요.
그래서 이것저것 다 해보고 있습니다. 그러다 발견한것이 컴파일 할때 -mfpu=neon 옵션이 적용되고 있지 않더라구요.
config.sh의 결과로 나오는...
config.log에서는 확실히 -march=armv7-a -mfloat-abi=softfp -mfpu=neon 옵션이 정상적으로 적용되어서
테스트컴파일이 되고 있는것이 확인되었습니다.
하지만 실제 빌드를 해보면...아래 빨강색처럼 neon 옵션이 vfp 옵션으로 변경되어 있습니다.
ndk-build V=1 -B <== 로그 보는 명령어
/cygdrive/c/android_ndk/android-ndk-r6/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/bin/arm-linux-androideabi-gcc -MMD -MP -MF C:/FFmpegBasic/obj/local/armeabi-v7a/objs-debug/basicplayer/BasicPlayer.o.d.org -fpic -ffunction-sections -funwind-tables -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv7-a -mfloat-abi=softfp -mfpu=vfp -O2 -fomit-frame-pointer -fstrict-aliasing -funswitch-loops -finline-limit
=300 -IC:/FFmpegBasic/jni/BasicPlayer/../ffmpeg/ -IC:/FFmpegBasic/jni/BasicPlaye
r/../ffmpeg/libavcodec -IC:/FFmpegBasic/jni/BasicPlayer/../ffmpeg/libavformat -I
C:/FFmpegBasic/jni/BasicPlayer/../ffmpeg/libswscale -IC:/android_ndk/android-ndk
-r6/sources/android/cpufeatures -IC:/FFmpegBasic/jni/BasicPlayer -DANDROID -Wa,
--noexecstack -O0 -g -IC:/android_ndk/android-ndk-r6/platforms/android-8/arch-ar
m/usr/include -c C:/FFmpegBasic/jni/BasicPlayer/BasicPlayer.c -o C:/FFmpegBasic
/obj/local/armeabi-v7a/objs-debug/basicplayer/BasicPlayer.o
이거 저만 그런건가요? 정말 강좌 (1/4 ~4/4)까지 꼼꼼히 읽어보고 비교해봤는데..안되네요. ㅠㅠ
cofig.sh를 하고 나면 다음과 같은 warning이 나긴 하는데...이거 때문인가요? 저만 나나요?
WARNING: /cygdrive/c/android_toolchain/bin/arm-linux-androideabi-pkg-config not
found, library detection may fail.
WARNING: Compiler does not indicate floating-point ABI, guessing soft.
좀 지났는데 지금은 잘 되시는지 모르겠네요~ ^^
저는 잘 컴파일 됩니다만...
일단 neon 옵션의 경우에는...
config.sh 한 로그에 -mfpu=neon 이 있던 없던 그건 실제 빌드시에는 별 관계가 없구요 (안드로이드 빌드 시스템을 쓰므로)
Android.mk를 작성할 때 해당 소스파일 뒤에 .neon 접미사가 붙어야만 합니다.
(3/4 강좌 참고, common.mk 가 하는 역할이 적절한 접미사 자동으로 붙여주고 그런 것 입니다.)
맨 아래 warning은 저도 뜹니다. ^^
신경 안쓰서도 될 듯 합니다.
정말 대단한 열정입니다.
남은그루터기님의 열정이 존경스럽네요.
늘 행복하세요..^^