Android의 FFmpeg


207

Android에서 FFmpeg 컴파일 (libffmpeg.so)을 받았습니다. 이제 RockPlayer와 같은 응용 프로그램을 구축하거나 기존 Android 멀티미디어 프레임 워크를 사용하여 FFmpeg를 호출해야합니다.

  1. Android / StageFright에서 FFmpeg 통합에 대한 단계 / 절차 / 코드 / 예가 있습니까?

  2. 이 라이브러리를 멀티미디어 재생에 어떻게 사용할 수 있는지 안내해 주시겠습니까?

  3. 나는 오디오 및 비디오 전송 스트림을 이미 가지고 있어야하며 FFmpeg에 피드하고 디코딩 / 렌더링해야합니다. IOMX API는 OMX 기반이며 여기에서 FFmpeg를 플러그인 할 수 없으므로 Android에서 어떻게해야합니까?

  4. 또한 재생에 사용해야하는 FFmpeg API에 대한 설명서를 찾을 수 없습니다.


7
이것도 흥미 롭습니다. 궁금합니다
Axarydax

5
ffmpeg를 어떻게 컴파일하여 .so 파일을 얻었습니까? 수행 한 단계를 공유 할 수 있습니까? cygwin-1.7.9 및 ndk r5가있는 Windows에서 작업하고 있습니다. 도와주세요.
Swathi EP

여기에 안드로이드에 대한 비교적 새로운는 FFmpeg의 : sourceforge.net/projects/ffmpeg4android
slhck은

@ slhck 위의 링크에서 ffmpeg 코드를 다운로드하여 컴파일하려고했지만 .so 파일을 얻을 수 없습니다. 그것은 많은 문제를 보여줍니다 ..
RAJESH

도와주세요 : stackoverflow.com/questions/14157030/… ,이 기능을 포함하고 실행할 위치를 모르겠습니다! .....
TharakaNirmana

답변:


109

ffmpeg가 Android에서 작동하게하는 단계는 다음과 같습니다.

  1. Android 용 ffmpeg의 정적 라이브러리를 빌드하십시오. 이는 Android Build System을 사용하여 olvaffe의 ffmpeg Android 포트 ( libffmpeg )를 빌드 함으로써 달성되었습니다 . 소스를 / external 및 makeaway 아래에두기 만하면 됩니다. ffmpeg 라이브러리가 의존하므로 Android 빌드에서 bionic (libc) 및 zlib (libz)를 추출해야합니다.
  2. Android NDK를 사용하여 ffmpeg 기능을 동적 라이브러리 래핑하십시오 . NDK로 작업하는 방법에 대한 많은 문서가 있습니다. 기본적으로 ffmpeg에서 필요한 기능을 라이브러리로 내보내려면 일부 C / C ++ 코드를 작성해야합니다. java는 JNI를 통해 상호 작용할 수 있습니다. NDK를 사용하면 1 단계에서 생성 한 정적 라이브러리와 쉽게 연결할 수 있습니다. Android.mk와 비슷한 줄을 추가하면됩니다.LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. Java 소스에서 ffmpeg-wrapping 동적 라이브러리를 사용하십시오. JNI에 대한 문서가 충분하므로 괜찮을 것입니다.

재생는 FFmpeg를 사용에 관한 많은 예 (ffmpeg라는 바이너리 자체가 좋은 예이다)가 여기에 '기본 튜토리얼이야. 최상의 문서는 헤더에서 찾을 수 있습니다.

행운을 빕니다 :)


7
Android 용 ffmpeg를 빌드하기위한이 답변에 대한 링크가 꽤 있습니다. 이것이 여전히 최고의 솔루션입니까? Android 빌드 시스템 링크가 끊어졌습니다. 그게 무엇입니까? NDK로 구축하는 데 도움이되는 툴킷이 많이 있습니다. 그러나 그들은 모두 나를 위해 다양한 빌드 오류로 실패하고 조금 오래된 것 같습니다. 누군가가 정적 ffmpeg 라이브러리를 게시 할 수없는 이유가 있습니까?
Rob Lourens

7
- 내 자신의 질문에 대답하기 위해, 내가는 FFmpeg 및 JNI 래퍼를 구축하기위한 가장 유용이 REPO 발견 github.com/andynicholson/android-ffmpeg-x264
롭 Lourens 보낸

68

여러 가지 이유로, 멀티미디어는 효율성을 떨어 뜨리지 않고 작업을 수행하는 측면에서 쉽지 않았습니다. ffmpeg는 매일 그것을 개선하기위한 노력입니다. 다양한 형식의 코덱과 컨테이너를 지원합니다.

이제이 라이브러리를 사용하는 방법에 대한 질문에 대답 하기 위해 여기에 작성하는 것이 그렇게 간단하지 않다고 말하고 싶습니다. 그러나 나는 다음과 같은 방법으로 당신을 안내 할 수 있습니다 .

1) 소스 코드의 ffmpeg 디렉토리 안에 output_example.c 또는 api_example.c가 있습니다. 여기에서 인코딩 / 디코딩이 수행 된 코드를 볼 수 있습니다. ffmpeg 내부에서 호출해야하는 API에 대한 아이디어를 얻을 수 있습니다. 이것이 첫 번째 단계입니다.

2) 돌고래 플레이어는 안드로이드를위한 오픈 소스 프로젝트입니다. 현재 버그가 있지만 개발자는 지속적으로 노력하고 있습니다. 이 프로젝트에는 조사를 계속하는 데 사용할 수있는 전체 설정이 준비되어 있습니다. 다음은 code.google.com 에서 프로젝트에 대한 링크 이거나 터미널에서 " git clone https://code.google.com/p/dolphin-player/ " 명령을 실행하는 것 입니다. P와 P86이라는 두 개의 프로젝트를 볼 수 있습니다. 둘 중 하나를 사용할 수 있습니다.

내가 제공하고 싶은 추가 팁은 ffmpeg 코드를 빌드 할 때 build.sh 내부에서 사용하려는 형식의 muxers / demuxers / encoders / decoders를 활성화해야한다는 것입니다. 그렇지 않으면 해당 코드가 라이브러리에 포함되지 않습니다. 이것을 깨닫는 데 많은 시간이 걸렸습니다. 그래서 당신과 그것을 공유하는 것을 생각했습니다.

몇 가지 기본 사항 : 우리가 비디오 파일을 말할 때, 예 : avi는 오디오와 비디오의 조합입니다

비디오 파일 = 비디오 + 오디오


비디오 = 코덱 + Muxer + Demuxer

코덱 = 인코더 + 디코더

=> 비디오 = 인코더 + 디코더 + Muxer + Demuxer (Mpeg4 + Mpeg4 + avi + avi-avi 컨테이너의 예)


오디오 = 코덱 + Muxer + Demuxer

코덱 = 인코더 + 디코더

=> 오디오 = 인코더 + 디코더 + Muxer + Demuxer (mp2 + mp2 + avi + avi-avi 컨테이너의 예)


코덱 (이름은 en * co * der / * dec * oder의 조합에서 제외됨)은 프레임을 인코딩 / 디코딩하는 데 사용되는 알고리즘을 정의하는 형식의 일부일뿐입니다. AVI는 코덱이 아니며 Mpeg4의 비디오 코덱과 mp2의 오디오 코덱을 사용하는 컨테이너입니다.

Muxer / demuxer는 인코딩 / 디코딩하는 동안 사용되는 파일에서 프레임을 결합 / 분리하는 데 사용됩니다.

따라서 avi 형식을 사용하려면 비디오 구성 요소 + 오디오 구성 요소를 활성화해야합니다.

예를 들어, avi의 경우 다음을 활성화해야합니다. mpeg4 인코더, mpeg4 디코더, mp2 인코더, mp2 디코더, avi muxer, avi demuxer.

휴 wwwwww ...

프로그래밍 방식으로 build.sh에는 다음 코드가 포함되어야합니다.

--enable-muxer=avi --enable-demuxer=avi (Generic for both audio/video. generally Specific to a container)
--enable-encoder=mpeg4 --enable-decoder=mpeg4(For video support)
--enable-encoder=mp2 --enable-decoder=mp2 (For Audio support)

이 모든 일이 끝나고 난 당신을 혼동하지 않기를 바랍니다 ...

감사합니다. 도움이 필요하면 알려주십시오.


1
이 정보에 대해 대단히 감사하고 싶습니다. 정말 많은 도움을주었습니다. 나중에 도움이 필요하다면 저를 도와 줄 수 있습니까? 감사합니다!
idish

skype / MSN 또는 다른 채팅 플랫폼을 통해 추가 할 수 있습니까? 그것에 대해 몇 가지 질문이 있습니다. 감사합니다.
idish

2
확실한..!! 그러나 온라인 상태는 약간 낮습니다. 필요한 경우가 아니라면 스카이프에 로그인하지 않습니다. 당신은 중요한 것들을 저에게 우편으로 보낼 수 있습니다. 이메일 : mantykuma@gmail.com
mk ..

13

내가 찾은 가장 쉽고 사용하기 쉬운 구현은 theguardianproject 팀이 만듭니다 : https://github.com/guardianproject/android-ffmpeg


확실하지 않습니다. 새로운 iOS 버전에서는 이것을 깨뜨릴 수있는 것이 없습니다. 내가 이것을 게시했을 때, 나는 여전히 10.7 또는 10.6을 가지고 있었다
Guy

JNI 구현을 사용하여 어떻게 3gp를 오디오로 변환 할 수
있습니까?

11

Android NDK를 사용하여 X264 및 FFMPEG를 구성하고 빌드하는 작은 프로젝트를 수행했습니다. 누락 된 주요 사항은 Java를 통해 액세스 할 수있는 괜찮은 JNI 인터페이스이지만, 상대적으로 쉬운 부분입니다. JNI 인터페이스를 자신의 용도에 적합하게 만들면 그에 대해 설명하겠습니다.

olvaffe의 빌드 시스템에 대한 이점은 라이브러리를 빌드하는 데 Android.mk 파일이 필요하지 않으며 일반 makefile과 툴체인 만 사용한다는 것입니다. 따라서 FFMPEG 또는 X264에서 새로운 변경 사항을 가져올 때 작동을 멈출 가능성이 훨씬 줄어 듭니다.

https://github.com/halfninja/android-ffmpeg-x264


Nick, 프로젝트가 OS X 10.7 libx264.a (common.o)에서 컴파일되지 않습니다 : x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale 'collect2 함수에서 : ld가 1 개의 종료 상태를 반환했습니다 : *** [x264] 오류 1
Yuriy Solovyov


6

내 FFMPEG 응용 프로그램을 만들기 위해이 프로젝트 ( https://github.com/hiteshsondhi88/ffmpeg-android-java )를 사용 했으므로 아무것도 컴파일 할 필요가 없습니다. Android 응용 프로그램에서 FFMPEG를 사용하는 쉬운 방법이라고 생각합니다.

http://hiteshsondhi88.github.io/ffmpeg-android-java/에 대한 자세한 정보


3
이 래퍼는 매우 매우 느립니다. 비디오에 200 개의 이미지가 50-60 초 걸립니다. . . 그러나 일반적으로 ffmpeg는 4-5 초 안에 해당 작업을 처리합니다.
Arsen Sench

이 프로젝트는 더 이상 작동하지 않습니다. 다른 자료가 있습니까?
Ajeet

@ArsenSench 다른 솔루션이 있습니까?
Akash Dubey

3

안드로이드 구현 (주로 guadianproject ) 에 대한 다른 많은 FFmpeg에서 영감을 얻은 솔루션을 찾았습니다 (Lame 지원 포함).

(lame 및 FFmpeg : https://github.com/intervigilium/liblamehttp://bambuser.com/opensource )

FFmpeg를 호출하려면 :

new Thread(new Runnable() {

    @Override
    public void run() {

        Looper.prepare();

        FfmpegController ffmpeg = null;

        try {
            ffmpeg = new FfmpegController(context);
        } catch (IOException ioe) {
            Log.e(DEBUG_TAG, "Error loading ffmpeg. " + ioe.getMessage());
        }

        ShellDummy shell = new ShellDummy();
        String mp3BitRate = "192";

        try {
            ffmpeg.extractAudio(in, out, audio, mp3BitRate, shell);
        } catch (IOException e) {
            Log.e(DEBUG_TAG, "IOException running ffmpeg" + e.getMessage());
        } catch (InterruptedException e) {
            Log.e(DEBUG_TAG, "InterruptedException running ffmpeg" + e.getMessage());
        }

        Looper.loop();

    }

}).start();

콘솔 출력을 처리합니다.

private class ShellDummy implements ShellCallback {

    @Override
    public void shellOut(String shellLine) {
        if (someCondition) {
            doSomething(shellLine);
        }
        Utils.logger("d", shellLine, DEBUG_TAG);
    }

    @Override
    public void processComplete(int exitValue) {
        if (exitValue == 0) {
            // Audio job OK, do your stuff: 

                            // i.e.             
                            // write id3 tags,
                            // calls the media scanner,
                            // etc.
        }
    }

    @Override
    public void processNotStartedCheck(boolean started) {
        if (!started) {
                            // Audio job error, as above.
        }
    }
}

후견인 프로젝트 경험이 어떻습니까?
XY

3

이 프로젝트에 대해 언급되지 않은 것이 이상합니다 : Appunite의 AndroidFFmpeg

나 같은 게으른 사람들을 위해 명령 줄에 복사 / 붙여 넣기에 대한 자세한 단계별 지침이 있습니다)


3

나는 같은 문제가 있었으며 대부분의 답변이 구식이라는 것을 알았습니다. FFMPEG에 래퍼를 작성하여 한 줄의 코드로 Android에서 액세스했습니다.

https://github.com/madhavanmalolan/ffmpegandroidlibrary


1
FFmpeg v2.8.4를 컴파일 한 것 같습니다. FFmpeg를 업그레이드 할 계획이 있습니까? 우리는 FFmpeg의 최신 버전 (3.2 또는 3.4 일 수 있음)을 가진 안드로이드 솔루션을 찾고 있습니다.
sappu

예. 3.x로 옮길 생각입니다. github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 여기에서 빌드 스크립트를 수정하고 3.4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…로
Madhavan Malolan

감사합니다 @Madhvan. Windows에서 ffmpeg 라이브러리를 작성 중입니다. github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/… 에서 모든 것을 변경해야하는 것이 궁금 하십니까?
sappu

1

먼저 FFmpeg 라이브러리의 종속성을 추가하십시오.

implementation 'com.writingminds:FFmpegAndroid:0.3.2'

그런 다음 활동에로드

FFmpeg ffmpeg;
    private void trimVideo(ProgressDialog progressDialog) {

    outputAudioMux = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES).getAbsolutePath()
            + "/VidEffectsFilter" + "/" + new SimpleDateFormat("ddMMyyyy_HHmmss").format(new Date())
            + "filter_apply.mp4";

    if (startTrim.equals("")) {
        startTrim = "00:00:00";
    }

    if (endTrim.equals("")) {
        endTrim = timeTrim(player.getDuration());
    }

    String[] cmd = new String[]{"-ss", startTrim + ".00", "-t", endTrim + ".00", "-noaccurate_seek", "-i", videoPath, "-codec", "copy", "-avoid_negative_ts", "1", outputAudioMux};


    execFFmpegBinary1(cmd, progressDialog);
    }



    private void execFFmpegBinary1(final String[] command, ProgressDialog prpg) {

    ProgressDialog progressDialog = prpg;

    try {
        ffmpeg.execute(command, new ExecuteBinaryResponseHandler() {
            @Override
            public void onFailure(String s) {
                progressDialog.dismiss();
                Toast.makeText(PlayerTestActivity.this, "Fail to generate video", Toast.LENGTH_SHORT).show();
                Log.d(TAG, "FAILED with output : " + s);
            }

            @Override
            public void onSuccess(String s) {
                Log.d(TAG, "SUCCESS wgith output : " + s);

//                    pathVideo = outputAudioMux;
                String finalPath = outputAudioMux;
                videoPath = outputAudioMux;
                Toast.makeText(PlayerTestActivity.this, "Storage Path =" + finalPath, Toast.LENGTH_SHORT).show();

                Intent intent = new Intent(PlayerTestActivity.this, ShareVideoActivity.class);
                intent.putExtra("pathGPU", finalPath);
                startActivity(intent);
                finish();
                MediaScannerConnection.scanFile(PlayerTestActivity.this, new String[]{finalPath}, new String[]{"mp4"}, null);

            }

            @Override
            public void onProgress(String s) {
                Log.d(TAG, "Started gcommand : ffmpeg " + command);
                progressDialog.setMessage("Please Wait video triming...");
            }

            @Override
            public void onStart() {
                Log.d(TAG, "Startedf command : ffmpeg " + command);

            }

            @Override
            public void onFinish() {
                Log.d(TAG, "Finished f command : ffmpeg " + command);
                progressDialog.dismiss();
            }
        });
    } catch (FFmpegCommandAlreadyRunningException e) {
        // do nothing for now
    }
}

  private void loadFFMpegBinary() {
    try {
        if (ffmpeg == null) {
            ffmpeg = FFmpeg.getInstance(this);
        }
        ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
            @Override
            public void onFailure() {
                showUnsupportedExceptionDialog();
            }

            @Override
            public void onSuccess() {
                Log.d("dd", "ffmpeg : correct Loaded");
            }
        });
    } catch (FFmpegNotSupportedException e) {
        showUnsupportedExceptionDialog();
    } catch (Exception e) {
        Log.d("dd", "EXception no controlada : " + e);
    }
}

private void showUnsupportedExceptionDialog() {
    new AlertDialog.Builder(this)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setTitle("Not Supported")
            .setMessage("Device Not Supported")
            .setCancelable(false)
            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            })
            .create()
            .show();

}

FFmpeg의 다른 기능도 사용하십시오.

===> merge audio to video
String[] cmd = new String[]{"-i", yourRealPath, "-i", arrayList.get(posmusic).getPath(), "-map", "1:a", "-map", "0:v", "-codec", "copy", "-shortest", outputcrop};


===> Flip vertical :
String[] cm = new String[]{"-i", yourRealPath, "-vf", "vflip", "-codec:v", "libx264", "-preset", "ultrafast", "-codec:a", "copy", outputcrop1};


===> Flip horizontally :  
String[] cm = new String[]{"-i", yourRealPath, "-vf", "hflip", "-codec:v", "libx264", "-preset", "ultrafast", "-codec:a", "copy", outputcrop1};


===> Rotate 90 degrees clockwise:
String[] cm=new String[]{"-i", yourRealPath, "-c", "copy", "-metadata:s:v:0", "rotate=90", outputcrop1};


===> Compress Video
String[] complexCommand = {"-y", "-i", yourRealPath, "-strict", "experimental", "-vcodec", "libx264", "-preset", "ultrafast", "-crf", "24", "-acodec", "aac", "-ar", "22050", "-ac", "2", "-b", "360k", "-s", "1280x720", outputcrop1};


===> Speed up down video
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=2.0*PTS[v];[0:a]atempo=0.5[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=1.0*PTS[v];[0:a]atempo=1.0[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=0.75*PTS[v];[0:a]atempo=1.5[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};
String[] complexCommand = {"-y", "-i", yourRealPath, "-filter_complex", "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]", "-map", "[v]", "-map", "[a]", "-b:v", "2097k", "-r", "60", "-vcodec", "mpeg4", outputcrop1};



===> Add two mp3 files 

StringBuilder sb = new StringBuilder();
sb.append("-i ");
sb.append(textSngname);
sb.append(" -i ");
sb.append(mAudioFilename);
sb.append(" -filter_complex [0:0][1:0]concat=n=2:v=0:a=1[out] -map [out] ");
sb.append(finalfile);
---> ffmpeg.execute(sb.toString().split(" "), new ExecuteBinaryResponseHandler()




===> Add three mp3 files

StringBuilder sb = new StringBuilder();
sb.append("-i ");
sb.append(firstSngname);
sb.append(" -i ");
sb.append(textSngname);
sb.append(" -i ");
sb.append(mAudioFilename);
sb.append(" -filter_complex [0:0][1:0][2:0]concat=n=3:v=0:a=1[out] -map [out] ");
sb.append(finalfile);
---> ffmpeg.execute(sb.toString().split(" "), new ExecuteBinaryResponseHandler()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.