FFmpeg su Android


207

Ho compilato FFmpeg (libffmpeg.so) su Android. Ora devo creare un'applicazione come RockPlayer o utilizzare il framework multimediale Android esistente per invocare FFmpeg.

  1. Hai passaggi / procedure / codice / esempio sull'integrazione di FFmpeg su Android / StageFright?

  2. Potete per favore guidarmi su come posso usare questa libreria per la riproduzione multimediale?

  3. Ho un requisito in cui ho già flussi di trasporto audio e video, che devo alimentare con FFmpeg e farlo decodificare / renderizzare. Come posso farlo su Android, dal momento che le API IOMX sono basate su OMX e non possono plug-in FFmpeg qui?

  4. Inoltre non sono riuscito a trovare documentazione sulle API di FFmpeg che devono essere utilizzate per la riproduzione.


7
è interessante, sono curioso anche
io

5
come hai compilato ffmpeg per ottenere i file .so? puoi per favore condividere i passaggi che hai seguito. Sto lavorando su Windows con cygwin-1.7.9 e ndk r5. Mi aiuti per favore.
Swathi EP,

Ecco un FFmpeg relativamente nuovo per Android: sourceforge.net/projects/ffmpeg4android
slhck,

@slhck ho scaricato il codice ffmpeg dal link sopra e ho provato a compilarlo ma non riesco a ottenere i file .so. mostra molti problemi ..
RAJESH,

per favore aiutami con: stackoverflow.com/questions/14157030/… , non so dove includere questa funzione ed eseguire! .....
TharakaNirmana,

Risposte:


109

Ecco i passaggi che ho seguito per far funzionare ffmpeg su Android:

  1. Costruisci librerie statiche di ffmpeg per Android. Ciò è stato ottenuto costruendo la porta Android ffmpeg ( libffmpeg ) di olvaffe utilizzando Android Build System . Posiziona semplicemente le fonti sotto / esterno e makelontano. Dovrai estrarre anche bionic (libc) e zlib (libz) dalla build di Android, poiché le librerie ffmpeg dipendono da esse.
  2. Crea una libreria dinamica che avvolge le funzionalità di ffmpeg usando l' NDK di Android . C'è molta documentazione là fuori su come lavorare con NDK. Fondamentalmente dovrai scrivere del codice C / C ++ per esportare la funzionalità di cui hai bisogno da ffmpeg in una libreria con cui Java può interagire tramite JNI. NDK ti consente di collegarti facilmente alle librerie statiche che hai generato nel passaggio 1, basta aggiungere una linea simile a questa ad Android.mk:LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. Usa la libreria dinamica che avvolge ffmpeg dalle tue fonti java. C'è abbastanza documentazione su JNI là fuori, dovresti stare bene.

Per quanto riguarda l'utilizzo di ffmpeg per la riproduzione, ci sono molti esempi (lo stesso binario di ffmpeg è un buon esempio), ecco un tutorial di base. La migliore documentazione è disponibile nelle intestazioni.

In bocca al lupo :)


7
Ci sono alcuni link a questa risposta per creare ffmpeg per Android. Questa è ancora la soluzione migliore? Il link del sistema Android Build è interrotto: cosa dovrebbe essere? Ci sono un sacco di toolkit per aiutare a costruire con l'NDK. Tuttavia, tutti falliscono con vari errori di build per me e sembrano un po 'vecchi. C'è qualche motivo per cui qualcuno non può semplicemente pubblicare una libreria ffmpeg statica integrata?
Rob Lourens,

7
Per rispondere alla mia domanda, ho trovato questo repository più utile per la creazione di wrapper ffmpeg e JNI - github.com/andynicholson/android-ffmpeg-x264
Rob Lourens,

68

Per vari motivi, Multimedia è stato e non è mai facile in termini di realizzazione dell'attività senza compromettere l'efficienza. ffmpeg è uno sforzo per migliorarlo giorno dopo giorno. Supporta diversi formati di codec e container.

Ora, per rispondere alla domanda su come utilizzare questa libreria , direi che non è così semplice scriverlo qui. Ma posso guidarti nei seguenti modi .

1) All'interno della directory ffmpeg del codice sorgente, hai output_example.c o api_example.c . Qui puoi vedere il codice in cui viene eseguita la codifica / decodifica. Avrai un'idea di quale API all'interno di ffmpeg dovresti chiamare. Questo sarebbe il tuo primo passo.

2) Dolphin player è un progetto open source per Android. Attualmente sta riscontrando dei bug ma gli sviluppatori lavorano continuamente. In quel progetto hai pronto tutto il setup che puoi usare per continuare la tua indagine. Ecco un link al progetto da code.google.com o esegui il comando " git clone https://code.google.com/p/dolphin-player/ " in un terminale. Puoi vedere due progetti chiamati P e P86. Puoi usare uno di loro.

Un altro suggerimento che vorrei offrire è che quando si crea il codice ffmpeg, all'interno di build.sh è necessario abilitare i muxer / demuxer / encoder / decoder dei formati che si desidera utilizzare. Altrimenti il ​​codice corrispondente non sarà incluso nelle librerie. Mi ci è voluto molto tempo per rendermene conto. Quindi ho pensato di condividerlo con te.

Poche nozioni di base: quando diciamo un file video, ad esempio avi, è una combinazione di audio e video

File video = Video + Audio


Video = Codec + Muxer + Demuxer

codec = encoder + Decoder

=> Video = encoder + decoder + Muxer + Demuxer (Mpeg4 + Mpeg4 + avi + avi - Esempio per contenitore avi)


Audio = Codec + Muxer + Demuxer

codec = encoder + Decoder

=> Audio = encoder + decoder + Muxer + Demuxer (mp2 + mp2 + avi + avi - Esempio per contenitore avi)


Il codec (il nome deriva da una combinazione di en * co * der / * dec * oder) è solo una parte del formato che definisce gli algoritmi utilizzati per codificare / decodificare un frame. AVI non è un codec, è un contenitore che utilizza il codec video di Mpeg4 e il codec audio di mp2.

Muxer / demuxer viene utilizzato per combinare / separare i frame da un file utilizzato durante la codifica / decodifica.

Quindi, se si desidera utilizzare il formato avi, è necessario abilitare i componenti video + componenti audio.

Ad esempio, per avi, devi abilitare quanto segue. Encoder mpeg4, decoder mpeg4, encoder mp2, decoder mp2, avi muxer, avi demuxer.

phewwwwwww ...

A livello di codice build.sh dovrebbe contenere il seguente codice:

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

Spero di non averti più confuso dopo tutto questo ...

Grazie, qualsiasi assistenza necessaria, per favore fatemi sapere.


1
Ehi, vorrei ringraziarti moltissimo per quelle informazioni, mi hai davvero aiutato molto, è possibile che tu mi aiuti se ne avrò bisogno in seguito? Grazie!
pazzo

Posso aggiungerti per favore tramite skype / MSN o qualsiasi altra piattaforma di chat per favore? Ho qualche domanda a riguardo, grazie.
pazzo

2
Sicuro..!! Ma la mia presenza online è un po 'bassa .. A meno che non sia molto richiesto non accedo a skype. Puoi scrivermi per qualsiasi cosa importante. Email: mantykuma@gmail.com
mk ..

13

L'implementazione più facile da costruire, facile da usare che ho trovato è stata fatta dal team del progetto guardian: https://github.com/guardianproject/android-ffmpeg


Non sono sicuro, suppongo che lo sia, non viene in mente nulla nella nuova versione di iOS che potrebbe spezzare questo. Quando l'ho pubblicato, avevo ancora 10.7 o 10.6
Guy

sai, come posso convertire 3gp in audio, usando l'implementazione JNI
Mr.G

11

Ho fatto un piccolo progetto per configurare e costruire X264 e FFMPEG usando Android NDK. La cosa principale che manca è un'interfaccia JNI decente per renderla accessibile tramite Java, ma questa è la parte facile (relativamente). Quando cercherò di rendere l'interfaccia JNI adatta ai miei usi, la inserirò.

Il vantaggio rispetto al sistema di build di olvaffe è che non richiede i file Android.mk per compilare le librerie, utilizza solo i normali makefile e la toolchain. Ciò rende molto meno probabile che smetta di funzionare quando si estraggono nuove modifiche da FFMPEG o X264.

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


Nick, il tuo progetto non si sta compilando su OS X 10.7 libx264.a (common.o): nella funzione x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale 'collect2: ld ha restituito 1 stato di uscita make: *** [x264] Errore 1
Yuriy Solovyov il


6

Per realizzare la mia applicazione FFMPEG ho usato questo progetto ( https://github.com/hiteshsondhi88/ffmpeg-android-java ), quindi non devo compilare nulla. Penso che sia il modo semplice di utilizzare FFMPEG nelle nostre applicazioni Android.

Maggiori informazioni su http://hiteshsondhi88.github.io/ffmpeg-android-java/


3
Questo wrapper è molto, molto, molto lento. 200 immagini al video impiegano 50-60 secondi. . . ma normalmente ffmpeg gestisce quell'attività in 4-5 secondi.
Arsen Sench,

Questo progetto non funziona più. Hai altre risorse?
Ajeet

@ArsenSench hai un'altra soluzione?
Akash Dubey,

3

Ispirato da molte altre FFmpeg su implementazioni Android là fuori (principalmente il progetto guadian ), ho trovato una soluzione (anche con il supporto Lame).

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

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

e per gestire l'output della console:

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.
        }
    }
}

Qual è la tua esperienza con il progetto guardian?
XY

3

Strano che questo progetto non sia stato menzionato: AndroidFFmpeg di Appunite

Ha istruzioni dettagliate per copiare / incollare sulla riga di comando, per le persone pigre come me))


3

Ho avuto lo stesso problema, ho trovato la maggior parte delle risposte qui datate. Ho finito per scrivere un wrapper su FFMPEG per accedere da Android con una singola riga di codice.

https://github.com/madhavanmalolan/ffmpegandroidlibrary


1
Sembra che tu abbia compilato FFmpeg v2.8.4, ci sono piani per aggiornare FFmpeg? Stiamo cercando la soluzione Android con l'ultima versione (può essere 3.2 o 3.4) di FFmpeg.
Sappu,

Sì. Ho intenzione di spostarlo su 3.x github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 Puoi provare a modificare lo script di compilazione qui e compilare per 3.4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…
Madhavan Malolan

Grazie @Madhvan. Sto costruendo la libreria ffmpeg su Windows. Ti stai solo chiedendo cosa deve essere cambiato in github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/… per costruire?
Sappu,

1

Innanzitutto, aggiungi la dipendenza della libreria FFmpeg

implementation 'com.writingminds:FFmpegAndroid:0.3.2'

Quindi caricare in attività

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

}

Utilizza anche un'altra funzionalità di 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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.