안드로이드

Android NDK FFmpeg 컴파일 강좌 (4/4)

raulyo 2012. 4. 17. 21:33

안녕하세요 ^^

 

마지막 강좌로 실제 컴파일을 완료하고 간단한 플레이어를 만들어 동영상을 돌려보겠습니다.

 

 

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 과 같이 매우 간단한 경로로 프로젝트 폴더를 복사해서 다시 컴파일 해보시기 바랍니다.

 

강좌를 정신없이 적었는데 잘 되었는지 모르겠습니다.

관련 어플을 기획하거나 제작하고 계신 분들에게 조금이라도 도움이 되었으면 합니다.

 

긴 강좌 읽어주셔서 감사합니다 ^^

 


 
댓글
2011.07.11 09:48:36
id: 자바개발자자바개발자
profile

정말 대단한 열정입니다.

남은그루터기님의 열정이 존경스럽네요.

늘 행복하세요..^^

댓글
2011.07.11 19:01:56
남은그루터기

감사합니다. 뭘요... ^^ 저도 이제 시작입니다.

자바개발자 님이야 말로 여러 팁들 종종 정리해주시고~ 감사합니다.

댓글
2011.07.11 10:15:11
id: 아즈라엘아즈라엘

NDK 하면 할 수록 재미가 있는거 같습니다. 

물론 성공해야 재미있지만요 ^^ 


좋은 자료 감사합니다. 나중에 함 해봐야 겠네요 ㅋㅋ

그런의미에서 추천한방~

댓글
2011.07.11 19:02:46
남은그루터기

네 ^^ 감사합니다.

오랜 삽질 후에 성공하니 눈물이 다 나더군요... ㅠㅡㅠ

댓글
2011.07.11 14:07:44
안드뉴비

안녕하세요. 강좌는 잘 보았습니다. ^^

컴파일 과정을 그대로 따라해보았는데요.

ndk-build 명령을 내리면 common.mk를 찾을 수 없다고 나와서. ffmpeg폴더안으로 common.mk파일을 올려놓고 다시 build명령을 내리니 아무일도 일어나질 않네요.... 아무일도 일어나지 않아서.. 어느부분을 손대야 할 지 막막해서 그런데 답변 주시면 감사하겠습니다..

^^ 읽어주셔서 감사합니다.

댓글
2011.07.11 14:48:07
남은그루터기
3번째 강좌에 있는대로 common.mk 파일을 작성해서 ffmpeg 폴더에 넣으시는게 맞습니다.

4번째 강좌의 "8. 간단한 Player 소스 작성 (1)"을 올바로 다 하시고 컴파일 하셨는지 궁금합니다.
그걸 안하시면 컴파일이 안됩니다.

현재 제가 컴파일한 구조는
libbasicplayer Library가
libavformat, libavcodec, libswscale, libavutil 네 개의 ffmpeg library 파일을 static link 하는 형식입니다.

basicplayer 를 구성하는
BasicPlayer.c, BasicPlayer.h, Interface.c 등이 제대로 작성되지 않으면
ffmpeg를 컴파일 할 이유가 없으므로 아무 일도 안 일어 납니다.
댓글
2011.07.11 14:57:18
안드뉴비

g:/Root/FFmpegBasic/jni 폴더에 common.mk 파일을 만듭니다.

이 부분을 보고 제가 오해했던 것 같네요.;; 

경로 이동하고, 말씀대로 진행하니 잘됩니다. ^^...

다음부턴 한번씩 더 읽어보고 확인해봐야겠네요.

좋은 강좌 감사드립니다. ^^

댓글
2011.07.11 16:26:40
남은그루터기

맞네요 ^^ 오타네요~

수정하였습니다.

댓글
2011.07.11 15:59:53
ffmpeg은어려워

덧붙이기전에는 build 가 잘됬는데요 소스를 확인하기 위해서 파일을 덧붙이고 나니까.

BasicPlayer.h 104줄에서 sws_getCachedContext 과 sws_scale undefined reference 라는

오류로 인해 진행이 되지 않고 있습니다.

그래서 2개의 정의부를 찾아봤는데 안보여서 그러는데 어떻게 해야 하나요?

댓글
2011.07.11 18:53:28
남은그루터기
두 함수는 ffmpeg/libswscale의 swscale.h 헤더 파일에 정의되어 있습니다.

FFmpeg는 여러개의 라이브러리로 나누어져 있습니다.
이 강좌에서 필요한 것은
libavcodec, libavformat, libavutil, libswscale 요 네 개 입니다.

libswscale이 잘 컴파일 되었는지 확인해보시기 바랍니다.
댓글
2011.07.12 11:14:16
ffmpeg은어려워

답변 감사합니다. 그런데 말씀하신것처럼 헤더파일 다 넣었는데도 동일한 오류가 나네요

어떻게 해야 하나요?

그리고 헤더 앞에 ../ 이거 붙인이유는 그냥 쓰면 디렉토리를 못찾아서 ../ 을 붙인것입니다.

댓글
2011.07.12 14:09:55
남은그루터기

Android.mk에 include path 설정을 하였으므로 c에서 헤더앞에 ../ 이렇게 안하셔도 됩니다.

제 마지막 강좌 소스 코드 올리신 것 받아서 컴파일 해보시고 비교해보시면 될 것 같네요 ^^

댓글
2011.07.11 16:06:29
안드뉴비

컴파일 마지막즈음에 에러가 나서 질문을 드립니다.

컴파일 마지막부분에 cannot find -ljnigraphics라고 나오는데요..

android내의 platform 폴더를 찾아보니 /usr/lib안에 존재하는데.. 계속 찾을 수 없다고 나오네요...

해결방법을 아신다면 답변 주시면 정말 감사하겠습니다.. ^^.


댓글
2011.07.11 18:59:26
남은그루터기

글쎄요...

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 미만 버전을 타겟으로 하신건 아닌지요?

댓글
2011.07.13 20:28:11
볼레로

2.2 미만에서는 안되는 군요. 


2.2 미만 사용자가  16.5% 가량되는데... 

음 이분들을 포기해야 하나... ^^


댓글
2011.07.12 09:15:46
유령c

MAC에서 작업했는데 잘 됩니다! 감사합니다

(이상하게 0.71이나 0.8은 이전부터 컴파일 후에 안되서 0.63이용했습니다 iOS에서는 잘 되는데 모르겠네요) 


실제로 소프트웨어 디코딩보다 2배정도 성능향상이 있네요 (감동) 

decode랑 sws_scale 양쪽 모두 향상이 되네요

다시 한번 감사 인사 드립니다. 

댓글
2011.07.13 20:50:33
볼레로

make 에서  


Argument list too long 


에러가 나왔는데요.  이것을  path 가 긴 경우인가요? 


c:\workspace\FFmpegBasic  이라고 만들었는데요. 

댓글
2011.07.14 05:02:42
남은그루터기

아마 그럴 겁니다.

저 같은 경우에는 FFmpeg 컴파일 할 때에는 무조건 C:/ 같은데다가 이동해서 컴파일 하고

FFmpeg 컴파일 끝나면 다시 원래 제가 작업하던 폴더로 옮기지요...

어차피 FFmpeg 는 초반 한 번 컴파일 하면 다시 컴파일 할 일이 없으니까요... ^^

댓글
2011.07.14 10:13:25
볼레로

ndk-build 에서  명령어를 찾을 수 없다고 나와서 


/cygdrive/c/android-ndk-r6/ndk-build


라고 넣었거든요.  


혹시 이 문제인가 싶어서   컴퓨터 설정에  Path 에 ndk 디렉토리 추가하고,   C:/ 밑으로 디렉토리 옮기고 다시 시도중입니다 ^^ 


컴파일 완료되었네요. 

감사합니다.

댓글
2011.07.21 19:55:39
키네스

System.loadLibrary("basicplayer");

이 부분에서 계속 죽는데... 무슨 일 때문일가요?!


올려주신 프로젝트도 동일한 부분에서 죽는데...ㅠㅠ


댓글
2011.08.02 15:03:53
앱솔루션

그거...jni쪽에서 구현이 잘못되어서 그래요.

혹시 Interface.c 파일에서 이름이 잘못되었거나 빼먹은 인터페이스 함수가 있는지 확인해보세요.

혹은 함수이름을 헝가리안 표기법으로 안하고 '_'를 넣은 함수명을 사용하면 jni 인터페이스 함수로

인식이 안되요

댓글
2011.07.26 17:08:57
앱솔루션

음 잘 사용하고 있었는데 갚자기 다음과 같은 에러가 나네요. 왜 그럴까요?


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를 설치하긴 했습니다만 혹시 그 것과 관련이 있는것일까요?

댓글
2011.08.02 18:09:16
앱솔루션

FFMpeg 0.63 버전에서는 관련 에러가 없네요. 그래서 0.63으로 전환했습니다.


댓글
2011.08.03 19:50:26
Jdeveloper

android sdk-r12-windows

android-ndk-r6-windows

FFmpeg0.8.1

에서 컴파일 성공하였습니다.

개발환경 여기까지 지장없었습니다.^^

 

댓글
2011.08.04 16:17:44
앱솔루션

음 제 개발환경에서는 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.

댓글
2011.08.12 22:07:24
남은그루터기

좀 지났는데 지금은 잘 되시는지 모르겠네요~ ^^

저는 잘 컴파일 됩니다만...


일단 neon 옵션의 경우에는...

config.sh 한 로그에 -mfpu=neon 이 있던 없던 그건 실제 빌드시에는 별 관계가 없구요 (안드로이드 빌드 시스템을 쓰므로)

Android.mk를 작성할 때 해당 소스파일 뒤에 .neon 접미사가 붙어야만 합니다.

(3/4 강좌 참고, common.mk 가 하는 역할이 적절한 접미사 자동으로 붙여주고 그런 것 입니다.)


맨 아래 warning은 저도 뜹니다. ^^

신경 안쓰서도 될 듯 합니다.

댓글
2011.11.13 17:37:27
xanadu

혹시 BasicPlayer컴파일 중에 android/bitmap.h: No such file... 이라고 에러 발생되는 분 없으신가요?

이 에러를 어떻게 해결해야할지 감이 안옵니다. 누구 도움 좀 부탁드립니다.

댓글
2012.01.13 15:32:14
마사키k

저같은 경우 해당폴더 밑에 android 폴더 만들고 bitmap.h파일 붙여넣으니까 되더라구요..

댓글
2012.01.10 17:30:01
lanevo

BasicPlayer.c 의 av_open_input_file() 을 av_open_input_stream으로 수정할경우 네트워크로 받은 데이타를 재생시킬 수 있나요? 


configure할때 네트워크 enable 시키고요;;