FFmpeg na Androida

207

Mam skompilowaną FFmpeg (libffmpeg.so) na Androida. Teraz muszę zbudować aplikację taką jak RockPlayer lub użyć istniejącej platformy multimediów Androida, aby wywołać FFmpeg.

  1. Czy masz kroki / procedury / kod / przykład integracji FFmpeg na Androidzie / StageFright?

  2. Czy możesz mi pomóc, jak korzystać z tej biblioteki do odtwarzania multimediów?

  3. Mam wymaganie, w którym mam już strumienie transportu audio i wideo, które muszę przesłać do FFmpeg i dekodować / renderować. Jak mogę to zrobić na Androidzie, skoro interfejsy API IOMX są oparte na OMX i nie można tutaj podłączyć FFmpeg?

  4. Nie mogłem też znaleźć dokumentacji dotyczącej interfejsów API FFmpeg, które należy wykorzystać do odtwarzania.

Występ
źródło
7
to ciekawe, ja też jestem ciekawy
Axarydax,
5
jak skompilowałeś ffmpeg, aby uzyskać pliki .so? czy możesz podzielić się krokami, które wykonałeś? Pracuję na systemie Windows z cygwin-1.7.9 i ndk r5. Proszę pomóż mi.
Swathi EP,
Oto stosunkowo nowy FFmpeg dla Androida: sourceforge.net/projects/ffmpeg4android
slhck
@slhck Pobrałem kod ffmpeg z powyższego łącza i próbowałem go skompilować, ale nie mogę uzyskać plików .so. pokazuje wiele problemów ..
RAJESH
pomóżcie mi: stackoverflow.com/questions/14157030/… , nie wiem, gdzie włączyć tę funkcję i uruchomić! .....
TharakaNirmana

Odpowiedzi:

109

Oto kroki, które przeszedłem, aby ffmpeg działał na Androidzie:

  1. Twórz statyczne biblioteki ffmpeg dla Androida. Osiągnięto to, budując port ffmpeg systemu Android ( libffmpeg ) firmy Olvaffe za pomocą systemu Android Build System . Po prostu umieść źródła pod / zewnętrzne imake oddalone. Musisz także wyodrębnić bionic (libc) i zlib (libz) z kompilacji Androida, ponieważ biblioteki ffmpeg od nich zależą.
  2. Utwórz dynamiczną funkcję zawijania biblioteki ffmpeg za pomocą Androida NDK . Istnieje wiele dokumentacji na temat pracy z NDK. Zasadniczo musisz napisać trochę kodu C / C ++, aby wyeksportować funkcjonalność, której potrzebujesz z ffmpeg do biblioteki java, z którą może współpracować JNI. NDK pozwala łatwo łączyć się z bibliotekami statycznymi wygenerowanymi w kroku 1, wystarczy dodać wiersz podobny do tego do Android.mk:LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. Użyj dynamicznej biblioteki owijającej ffmpeg ze źródeł Java. Jest wystarczająco dużo dokumentacji na temat JNI, powinieneś być w porządku.

Jeśli chodzi o używanie ffmpeg do odtwarzania, istnieje wiele przykładów (sam plik binarny ffmpeg jest dobrym przykładem), tutaj podstawowy samouczek. Najlepsza dokumentacja znajduje się w nagłówkach.

Powodzenia :)

Yonilevy
źródło
7
Istnieje wiele linków do tej odpowiedzi na temat budowania ffmpeg na Androida. Czy to wciąż najlepsze rozwiązanie? Link do systemu Android Build jest zepsuty - co to ma być? Istnieje wiele zestawów narzędzi, które pomagają w budowaniu za pomocą NDK. Jednak wszystkie one zawodzą z różnymi błędami kompilacji i wydają się trochę stare. Czy jest jakiś powód, dla którego ktoś nie może po prostu opublikować wbudowanej biblioteki statycznej ffmpeg?
Rob Lourens
7
Aby odpowiedzieć na moje pytanie, uznałem, że to repozytorium jest najbardziej przydatne do budowania opakowań ffmpeg i JNI - github.com/andynicholson/android-ffmpeg-x264
Rob Lourens
68

Z różnych powodów Multimedia były i nigdy nie są łatwe do osiągnięcia, bez obniżenia wydajności. ffmpeg to wysiłek ulepszenia go z dnia na dzień. Obsługuje różne formaty kodeków i kontenerów.

Teraz, aby odpowiedzieć na pytanie, jak korzystać z tej biblioteki , powiedziałbym, że napisanie jej tutaj nie jest takie proste. Ale mogę cię poprowadzić w następujący sposób .

1) W katalogu ffmpeg kodu źródłowego masz plik_wyjściowy.c lub przykładowy_api . C. Tutaj możesz zobaczyć kod, w którym odbywa się kodowanie / dekodowanie. Dowiesz się, które API w ffmpeg powinieneś wywołać. To byłby twój pierwszy krok.

2) Dolphin player to projekt typu open source dla Androida. Obecnie występują błędy, ale programiści pracują nieprzerwanie. W tym projekcie masz przygotowaną całą konfigurację, której możesz użyć do kontynuowania dochodzenia. Oto link do projektu z code.google.com lub uruchom polecenie „ git clone https://code.google.com/p/dolphin-player/ ” w terminalu. Możesz zobaczyć dwa projekty o nazwach P i P86. Możesz użyć jednego z nich.

Dodatkową wskazówką, którą chciałbym zaoferować, jest to, że kiedy budujesz kod ffmpeg, wewnątrz build.sh musisz włączyć multipleksery / demuxery / kodery / dekodery formatów, których chcesz używać. W przeciwnym razie odpowiedni kod nie zostanie uwzględniony w bibliotekach. Zrozumienie tego zajęło mi dużo czasu. Więc pomyślałem o podzieleniu się tym z tobą.

Kilka podstaw: kiedy mówimy plik wideo, np. Avi, jest to połączenie zarówno audio, jak i wideo

Plik wideo = wideo + audio


Wideo = Kodek + Muxer + Demuxer

kodek = koder + dekoder

=> Wideo = koder + dekoder + Muxer + Demuxer (Mpeg4 + Mpeg4 + avi + avi - przykład dla kontenera avi)


Audio = Kodek + Muxer + Demuxer

kodek = koder + dekoder

=> Audio = koder + dekoder + Muxer + Demuxer (mp2 + mp2 + avi + avi - przykład dla kontenera avi)


Kodek (nazwa pochodzi od kombinacji en * co * der / * dec * oder) jest tylko częścią formatu, który definiuje algorytmy używane do kodowania / dekodowania ramki. AVI nie jest kodekiem, jest kontenerem, który wykorzystuje kodek wideo MPEG4 i kodek audio MP2.

Muxer / demuxer służy do łączenia / oddzielania ramek od pliku używanego podczas kodowania / dekodowania.

Więc jeśli chcesz używać formatu avi, musisz włączyć komponenty wideo + komponenty audio.

Np. Dla avi musisz włączyć następujące opcje. koder mpeg4, dekoder mpeg4, koder mp2, dekoder mp2, avi muxer, avi demuxer.

phewwwwwww ...

Programowo build.sh powinien zawierać następujący kod:

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

Mam nadzieję, że nie pomyliłem cię bardziej po tym wszystkim ...

Dzięki, Każda potrzebna pomoc, proszę dać mi znać.

mk ..
źródło
1
Hej, bardzo dziękuję za te informacje, naprawdę mi bardzo pomogłeś. Czy to możliwe, że możesz mi pomóc, jeśli będę potrzebować później? Dziękuję Ci!
idish
Czy mogę dodać cię proszę przez skype / MSN lub inną platformę czatu? Mam kilka pytań na ten temat, dziękuję.
idish
2
Pewnie..!! Ale moja obecność w Internecie jest trochę niska. O ile nie jest to bardzo wymagane, nie loguję się na skype. Możesz wysłać mi e-maila w sprawie wszelkich ważnych rzeczy. E-mail: [email protected]
mk ..
13

Najłatwiejszą do zbudowania i łatwą w użyciu implementację, którą znalazłem, przygotował zespół projektu guguianian: https://github.com/guardianproject/android-ffmpeg

Chłopak
źródło
Nie jestem pewien, domyślam się, że tak, nic w nowej wersji iOS nie przychodzi mi na myśl, co mogłoby to zepsuć. Kiedy to opublikowałem, nadal miałem 10,7 lub 10,6
Guy
czy wiesz, jak mogę przekonwertować 3gp na audio, używając implementacji JNI
Mr.G
11

Zrobiłem mały projekt, aby skonfigurować i zbudować X264 i FFMPEG przy użyciu Androida NDK. Najważniejsze, czego brakuje, to przyzwoity interfejs JNI, aby był dostępny przez Javę, ale jest to łatwa część (stosunkowo). Kiedy zacznę przygotowywać interfejs JNI do własnych celów, wrzucę to.

Zaletą systemu kompilacji olvaffe jest to, że nie wymaga plików Android.mk do budowania bibliotek, po prostu używa zwykłych plików makefile i łańcucha narzędzi. To znacznie zmniejsza prawdopodobieństwo, że przestanie działać po wyciągnięciu nowej zmiany z FFMPEG lub X264.

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

Nacięcie
źródło
Nick, twój projekt nie kompiluje się w systemie OS X 10.7 libx264.a (common.o): W funkcji x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale 'collect2: ld zwrócił 1 status wyjścia make: *** [x264] Błąd 1
Yuriy Solovyov
6

Aby utworzyć aplikację FFMPEG, skorzystałem z tego projektu ( https://github.com/hiteshsondhi88/ffmpeg-android-java ), więc nie muszę niczego kompilować. Myślę, że to prosty sposób na użycie FFMPEG w naszych aplikacjach na Androida.

Więcej informacji na http://hiteshsondhi88.github.io/ffmpeg-android-java/

jmartinalonso
źródło
3
To opakowanie jest bardzo, bardzo, bardzo, bardzo wolne. 200 zdjęć do filmu zajmuje 50–60 sekund. . . ale normalnie ffmpeg obsługuje to zadanie w 4-5 sekund.
Arsen Sench
Ten projekt już nie działa. Czy masz jakieś inne zasoby?
Ajeet
@ArsenSench czy masz inne rozwiązanie?
Akash Dubey
3

Zainspirowany wieloma innymi aplikacjami FFmpeg na Androida (głównie projekt guadian ) znalazłem rozwiązanie (również z obsługą Lame).

(lame i FFmpeg: https://github.com/intervigilium/liblame i http://bambuser.com/opensource )

aby zadzwonić do 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();

i do obsługi wyjścia konsoli:

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.
        }
    }
}
dentex
źródło
Jakie masz doświadczenia z Guardianproject?
XY
3

Dziwne, że ten projekt nie został wspomniany: AndroidFFmpeg z Appunite

Ma dość szczegółowe instrukcje krok po kroku, aby skopiować / wkleić do wiersza poleceń, dla leniwych ludzi takich jak ja))

Mixaz
źródło
3

Miałem ten sam problem, większość odpowiedzi znalazłem tutaj z datą. Skończyło się na napisaniu opakowania na FFMPEG, aby uzyskać dostęp z Androida za pomocą jednego wiersza kodu.

https://github.com/madhavanmalolan/ffmpegandroidlibrary

Madhavan Malolan
źródło
1
Wygląda na to, że skompilowałeś FFmpeg v2.8.4, czy są jakieś plany aktualizacji FFmpeg? Szukamy rozwiązania dla systemu Android, które ma najnowszą (może być 3.2 lub 3.4) wersję FFmpeg.
sappu
Tak. Zamierzam przenieść go na wersję 3.x github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 Możesz spróbować zmodyfikować skrypt kompilacji tutaj i skompilować dla wersji 3.4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…
Madhavan Malolan
Dzięki @Madhvan. Buduję bibliotekę ffmpeg w systemie Windows. Zastanawiam się tylko, co trzeba zmienić w github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…, aby móc budować?
sappu
1

Najpierw dodaj zależność biblioteki FFmpeg

implementation 'com.writingminds:FFmpegAndroid:0.3.2'

Następnie załaduj aktywność

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

}

Użyj także innej funkcji 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()
Jay Patoliya
źródło