IT

Android에서 FFmpeg

itgroup 2023. 9. 10. 12:09
반응형

Android에서 FFmpeg

저는 안드로이드에서 FFmpeg를 컴파일(libffmpeg.so )했습니다.이제 저는 RockPlayer와 같은 애플리케이션을 구축하거나 기존의 Android 멀티미디어 프레임워크를 사용하여 FFmpeg를 호출해야 합니다.

  1. Android / StageFright에서 FFmpeg를 통합하는 단계/절차/코드/예를 가지고 계십니까?

  2. 멀티미디어 재생을 위해 이 도서관을 어떻게 이용할 수 있는지 안내해 주실 수 있나요?

  3. 저는 오디오 및 비디오 전송 스트림이 이미 있는 요구사항이 있는데, 이 스트림을 FFmpeg에 공급하여 디코딩/렌더링해야 합니다.IOMX API는 OMX 기반이며 여기서 FFmpeg를 플러그인할 수는 없습니다. 안드로이드에서 어떻게 할 수 있습니까?

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

다음은 fmpeg가 Android에서 작동하도록 하기 위해 제가 겪은 단계입니다.

  1. Android용 fmpeg의 정적 라이브러리를 구축합니다.이것은 안드로이드 빌드 시스템을 사용하여 olvaffe의 fffmpeg 안드로이드 포트(libffmpeg)를 구축함으로써 달성되었습니다.소스를 /외부 아래에 배치하고make멀어져요. 안드로이드 빌드에서 바이오닉(libc)과 zlib(libz)를 추출해야 합니다. fmpeg 라이브러리가 의존하기 때문입니다.
  2. Android NDK를 사용하여 fmpeg 기능을 래핑하는 동적 라이브러리를 만듭니다.NDK를 사용하는 방법에 대한 문서가 많습니다. 기본적으로 fmpeg에서 필요한 기능을 JNI를 통해 상호 작용할 수 있는 라이브러리 자바로 내보내려면 C/C++ 코드를 작성해야 합니다.NDK를 사용하면 1단계에서 생성한 정적 라이브러리를 쉽게 링크할 수 있습니다. Android.mk 에 이와 유사한 줄을 추가하면 됩니다.LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. 자바 소스의 fmpeg-wrapping 동적 라이브러리를 사용합니다.JNI에 대한 자료가 충분하니 괜찮을 겁니다.

재생을 위해 ffmpeg를 사용하는 것과 관련하여 많은 예가 있습니다(ffmpeg 이진법 자체가 좋은 예입니다). 여기 기본 튜토리얼이 있습니다.최상의 설명서는 머리글에서 찾을 수 있습니다.

행운을 빌어요 :)

여러가지 이유로 멀티미디어는 효율성을 훼손하지 않고 과제를 달성한다는 측면에서 쉽지 않았고 결코 쉽지 않았습니다. fmpeg는 나날이 발전하는 노력입니다.다양한 형식의 코덱과 컨테이너를 지원합니다.

이제 도서관을 어떻게 사용할 것인가에 대한 답을 드리자면, 저는 이 도서관을 여기에 쓰는 것이 그리 간단하지 않다고 말하고 싶습니다.하지만 다음과 같은 방법으로 안내해 드릴 수 있습니다.

1) 소스 코드의 fmpeg 디렉터리 안에는 output_example.c 또는 api_example.c가 있습니다.여기서 인코딩/디코딩이 수행되는 코드를 확인할 수 있습니다.어떤 API의 내부 fmpeg를 호출해야 하는지에 대한 아이디어를 얻을 수 있을 것입니다.이것이 당신의 첫걸음이 될 것입니다.

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

추가적인 팁은 당신이 fmpeg 코드를 만들 때 인사이드 빌드를 한다는 것입니다.사용할 형식의 muxer/demuxer/encoder/decoder를 활성화해야 합니다.그렇지 않으면 해당 코드가 라이브러리에 포함되지 않습니다.이것을 깨닫기까지 많은 시간이 걸렸습니다.그래서 당신과 공유할까 생각 중입니다.

가지 기본 사항 : 비디오 파일이라고 할 때, ex : avi, 오디오와 비디오의 조합입니다.

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


비디오 = 코덱 + Muxer + Demuxer

코덱 = 인코더 + 디코더

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


오디오 = 코덱 + Muxer + 디먹서

코덱 = 인코더 + 디코더

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


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

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

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

예, avi의 경우 mPEG4 인코더, mPEG4 디코더, mp2 인코더, mp2 디코더, avi muxer, avi demuxer 등을 활성화해야 합니다.

휴우우우우우우우우우...

프로그래밍 방식으로 빌드합니다.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)

이 모든 일이 있은 후에 내가 더 혼란스럽게 하지 않았기를...

감사합니다, 도움이 필요하시면 말씀해주세요.

많은 연구 끝에 발견한 Android용 컴파일된 라이브러리 중 가장 업데이트된 것은 다음과 같습니다.

https://github.com/bravobit/FFmpeg-Android

  • 는 입니다를 합니다.FFmpeg release n4.0-39-gda39990
  • FFmpeg 및 FFProbe 포함
  • 명령을 실행하기 위한 Java 인터페이스가 포함되어 있습니다.
  • APK에서 FFprobe나 FFmpeg를 제거할 수 있습니다. wiki https://github.com/bravobit/FFmpeg-Android/wiki 에서 확인하십시오.

가장 쉽고 사용하기 쉬운 구현은 보호자 프로젝트 팀인 https://github.com/guardianproject/android-ffmpeg 에서 만든 것입니다.

안드로이드 NDK를 사용하여 X264와 FFMPEG를 구성하고 구축하는 작은 프로젝트를 수행했습니다.자바를 통해 접근할 수 있게 해주는 괜찮은 JNI 인터페이스가 가장 부족한 부분이지만, 그것은 (상대적으로) 쉬운 부분입니다.JNI 인터페이스를 내가 사용하기에 적합한 인터페이스로 만들기 시작하면, 그것을 밀어 넣을 것입니다.

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

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

FFMPEG 애플리케이션을 만들기 위해 이 프로젝트(https://github.com/hiteshsondhi88/ffmpeg-android-java) 를 사용했기 때문에 아무것도 컴파일할 필요가 없습니다.그것이 우리 안드로이드 애플리케이션에서 FFMPEG를 사용하는 쉬운 방법이라고 생각합니다.

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

Android 구현(주로 Guadian 프로젝트)에서 많은 다른 FFmpeg에 영감을 받아 해결책을 찾았습니다(Lame 지원도 있음).

(http: https://github.com/intervigilium/liblame 및 http://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.
        }
    }
}

이 프로젝트가 언급되지 않은 것이 이상합니다.안드로이드Appunite에서 FFmpeg

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

저도 같은 문제가 있었는데, 대부분의 답이 오래된 것을 발견했습니다.저는 코드 한 줄로 안드로이드에서 접속할 수 있도록 FFMPEG에 래퍼를 작성하게 되었습니다.

https://github.com/madhavanmalolan/ffmpegandroidlibrary

먼저 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();

}
    public String timeTrim(long milliseconds) {
        String finalTimerString = "";
        String minutString = "";
        String secondsString = "";

        // Convert total duration into time
        int hours = (int) (milliseconds / (1000 * 60 * 60));
        int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
        int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
        // Add hours if there

        if (hours < 10) {
            finalTimerString = "0" + hours + ":";
        } else {
            finalTimerString = hours + ":";
        }


        if (minutes < 10) {
            minutString = "0" + minutes;
        } else {
            minutString = "" + minutes;
        }

        // Prepending 0 to seconds if it is one digit
        if (seconds < 10) {
            secondsString = "0" + seconds;
        } else {
            secondsString = "" + seconds;
        }

        finalTimerString = finalTimerString + minutString + ":" + secondsString;

        // return timer string
        return finalTimerString;
    }

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()

언급URL : https://stackoverflow.com/questions/4725773/ffmpeg-on-android

반응형