FFmpeg en Android

207

Tengo compilado FFmpeg (libffmpeg.so) en Android. Ahora tengo que construir una aplicación como RockPlayer o usar el marco multimedia Android existente para invocar FFmpeg.

  1. ¿Tiene pasos / procedimientos / código / ejemplo sobre la integración de FFmpeg en Android / StageFright?

  2. ¿Pueden guiarme sobre cómo puedo usar esta biblioteca para la reproducción multimedia?

  3. Tengo un requisito donde ya tengo transmisiones de audio y video, que necesito alimentar a FFmpeg y decodificarlo / renderizarlo. ¿Cómo puedo hacer esto en Android, ya que las API de IOMX están basadas en OMX y no pueden enchufar FFmpeg aquí?

  4. Además, no pude encontrar documentación sobre las API de FFmpeg que deben usarse para la reproducción.

Punta
fuente
77
esto es interesante, también tengo curiosidad
Axarydax
55
¿Cómo compiló ffmpeg para obtener archivos .so? ¿Puedes por favor compartir los pasos que seguiste? Estoy trabajando en Windows con cygwin-1.7.9 y ndk r5. Por favor, ayúdame.
Swathi EP
Aquí hay un FFmpeg relativamente nuevo para Android: sourceforge.net/projects/ffmpeg4android
slhck
@slhck He descargado el código ffmpeg del enlace anterior y he intentado compilarlo, pero no puedo obtener los archivos .so. muestra muchos problemas ..
RAJESH
por favor , ayúdenme con: stackoverflow.com/questions/14157030/… , ¡no sé dónde incluir esta función y ejecutar! .....
TharakaNirmana

Respuestas:

109

Estos son los pasos que seguí para lograr que ffmpeg funcione en Android:

  1. Construye bibliotecas estáticas de ffmpeg para Android. Esto se logró mediante la construcción del puerto de Android ffmpeg de olvaffe ( libffmpeg ) utilizando el sistema de compilación de Android . Simplemente coloque las fuentes debajo / makeafuera y lejos. Deberá extraer también bionic (libc) y zlib (libz) de la compilación de Android, ya que las bibliotecas ffmpeg dependen de ellos.
  2. Cree una funcionalidad de ffmpeg de envoltura de biblioteca dinámica utilizando el NDK de Android . Hay mucha documentación sobre cómo trabajar con el NDK. Básicamente, necesitará escribir un código C / C ++ para exportar la funcionalidad que necesita de ffmpeg a una biblioteca con la que Java pueda interactuar a través de JNI. El NDK le permite enlazar fácilmente con las bibliotecas estáticas que ha generado en el paso 1, simplemente agregue una línea similar a Android.mk:LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. Use la biblioteca dinámica ffmpeg-wrapping de sus fuentes java. Hay suficiente documentación sobre JNI, deberías estar bien.

Con respecto al uso de ffmpeg para la reproducción, hay muchos ejemplos (el binario ffmpeg en sí mismo es un buen ejemplo), aquí hay un tutorial básico. La mejor documentación se puede encontrar en los encabezados.

Buena suerte :)

yonilevy
fuente
77
Hay bastantes enlaces a esta respuesta para construir ffmpeg para Android. ¿Sigue siendo la mejor solución? El enlace del sistema de compilación de Android está roto: ¿qué se supone que es eso? Hay un montón de herramientas para ayudar a construir con el NDK. Sin embargo, todos fallan con varios errores de compilación para mí y parecen un poco viejos. ¿Hay alguna razón por la que alguien no pueda simplemente publicar una versión estática de ffmpeg lib?
Rob Lourens
77
Para responder a mi propia pregunta, encontré que este repositorio es el más útil para construir envolturas ffmpeg y JNI - github.com/andynicholson/android-ffmpeg-x264
Rob Lourens
68

Por varias razones, Multimedia fue y nunca es fácil en términos de lograr la tarea sin comprometer la eficiencia. ffmpeg es un esfuerzo para mejorarlo día a día. Es compatible con diferentes formatos de códecs y contenedores.

Ahora, para responder la pregunta de cómo usar esta biblioteca , diría que no es tan simple escribirla aquí. Pero puedo guiarte de las siguientes maneras .

1) Dentro del directorio ffmpeg del código fuente, tiene output_example.c o api_example.c . Aquí puede ver el código donde se realiza la codificación / decodificación. Tendrás una idea de qué API dentro de ffmpeg debes llamar. Este sería tu primer paso.

2) Dolphin Player es un proyecto de código abierto para Android. Actualmente tiene errores, pero los desarrolladores están trabajando continuamente. En ese proyecto, tiene lista toda la configuración que puede usar para continuar su investigación. Aquí hay un enlace al proyecto desde code.google.com o ejecute el comando " git clone https://code.google.com/p/dolphin-player/ " en una terminal. Puede ver dos proyectos llamados P y P86. Puedes usar cualquiera de ellos.

Un consejo adicional que me gustaría ofrecer es que cuando está creando el código ffmpeg, dentro de build.sh necesita habilitar los muxers / demuxers / encoders / decoders de los formatos que desea usar. De lo contrario, el código correspondiente no se incluirá en las bibliotecas. Me llevó mucho tiempo darme cuenta de esto. Así que pensé en compartirlo contigo.

Algunos conceptos básicos: cuando decimos un archivo de video, por ejemplo: avi, es una combinación de audio y video

Archivo de video = Video + Audio


Video = Códec + Muxer + Demuxer

códec = codificador + decodificador

=> Video = codificador + decodificador + Muxer + Demuxer (Mpeg4 + Mpeg4 + avi + avi - Ejemplo para contenedor avi)


Audio = Códec + Muxer + Demuxer

códec = codificador + decodificador

=> Audio = codificador + decodificador + Muxer + Demuxer (mp2 + mp2 + avi + avi - Ejemplo para contenedor avi)


El códec (el nombre se deriva de una combinación de en * co * der / * dec * oder) es solo una parte del formato que define los algoritmos utilizados para codificar / decodificar un marco. AVI no es un códec, es un contenedor que utiliza códec de video de Mpeg4 y códec de audio de mp2.

Muxer / demuxer se usa para combinar / separar los cuadros de un archivo usado mientras se codifica / decodifica.

Entonces, si desea utilizar el formato avi, debe habilitar los componentes de video + componentes de audio.

Por ejemplo, para avi, debe habilitar lo siguiente. Codificador mpeg4, decodificador mpeg4, codificador mp2, decodificador mp2, avi muxer, avi demuxer.

phewwwwwww ...

Programáticamente build.sh debe contener el siguiente código:

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

Espero no confundirte más después de todo esto ...

Gracias, cualquier ayuda necesaria, por favor hágamelo saber.

mk ..
fuente
1
Hola, me gustaría agradecerles mucho por esa información, realmente me ayudaron mucho. ¿Es posible que me ayuden si necesito algo más adelante? ¡Gracias!
idish
¿Puedo agregarlo por skype / MSN o cualquier otra plataforma de chat? Tengo algunas preguntas al respecto, gracias.
idish
2
Por supuesto..!! Pero mi presencia en línea es un poco baja. A menos que sea muy necesario, no me conecto a Skype. Puedes enviarme un correo para cualquier cosa importante. Correo electrónico: [email protected]
mk ..
13

La implementación más fácil de construir y fácil de usar que he encontrado está hecha por el equipo del proyecto guardian: https://github.com/guardianproject/android-ffmpeg

Chico
fuente
No estoy seguro, supongo que es así, no se me ocurre nada en la nueva versión de iOS que pueda romper esto. Cuando publiqué esto, todavía tenía 10.7 o 10.6
Guy
hacer u saber, ¿Cómo puedo convertir 3GP a audio, utilizando JNI aplicación
Mr.G
11

He hecho un pequeño proyecto para configurar y construir X264 y FFMPEG usando el NDK de Android. Lo principal que falta es una interfaz JNI decente para que sea accesible a través de Java, pero esa es la parte fácil (relativamente). Cuando tenga la oportunidad de hacer que la interfaz JNI sea buena para mis propios usos, la presionaré.

El beneficio sobre el sistema de compilación de Olvaffe es que no requiere archivos Android.mk para compilar las bibliotecas, solo usa los archivos MAKE regulares y la cadena de herramientas. Esto hace que sea mucho menos probable que deje de funcionar cuando realiza un nuevo cambio de FFMPEG o X264.

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

Mella
fuente
Nick, tu proyecto no se está compilando en OS X 10.7 libx264.a (common.o): En la función x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale 'collect2: ld devolvió 1 estado de salida make: *** [x264] Error 1
Yuriy Solovyov
6

Para hacer mi aplicación FFMPEG, utilicé este proyecto ( https://github.com/hiteshsondhi88/ffmpeg-android-java ), así que no tengo que compilar nada. Creo que es la manera fácil de usar FFMPEG en nuestras aplicaciones de Android.

Más información en http://hiteshsondhi88.github.io/ffmpeg-android-java/

jmartinalonso
fuente
3
Este contenedor es muy muy muy muy muy lento. 200 imágenes en video toman 50-60 segundos. . . pero normalmente ffmpeg maneja esa tarea en 4-5 segundos.
Arsen Sench
Este proyecto ya no funciona. ¿Tienes algunos otros recursos?
Ajeet
@ArsenSench ¿tienes otra solución?
Akash Dubey
3

Inspirado por muchas otras implementaciones de FFmpeg en Android (principalmente el proyecto guadian ), encontré una solución (con soporte Lame también).

(cojo y FFmpeg: https://github.com/intervigilium/liblame y http://bambuser.com/opensource )

para llamar a 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();

y para manejar la salida de la consola:

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
fuente
¿Cuál es su experiencia con el proyecto guardian?
XY
3

Es extraño que este proyecto no haya sido mencionado: AndroidFFmpeg de Appunite

Tiene instrucciones paso a paso bastante detalladas para copiar / pegar en la línea de comandos, para gente perezosa como yo))

Mixaz
fuente
3

Tuve el mismo problema, encontré la mayoría de las respuestas aquí anticuadas. Terminé escribiendo un contenedor en FFMPEG para acceder desde Android con una sola línea de código.

https://github.com/madhavanmalolan/ffmpegandroidlibrary

Madhavan Malolan
fuente
1
Parece que has compilado FFmpeg v2.8.4, ¿hay algún plan para actualizar FFmpeg? Estamos buscando la solución de Android que tenga la última versión (puede ser 3.2 o 3.4) de FFmpeg.
Sappu
Si. Tengo la intención de moverlo a 3.x github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 Puede intentar modificar el script de compilación aquí y compilar 3.4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…
Madhavan Malolan
Gracias @Madhvan. Estoy construyendo la biblioteca ffmpeg en windows. ¿ Me pregunto qué se necesita cambiar en github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/… para construir?
Sappu
1

Primero, agregue la dependencia de la biblioteca FFmpeg

implementation 'com.writingminds:FFmpegAndroid:0.3.2'

Luego carga en actividad

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

}

Utilice también otra función de 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
fuente