FFmpeg no Android

207

Eu tenho o FFmpeg compilado (libffmpeg.so) no Android. Agora eu tenho que criar um aplicativo como o RockPlayer ou usar a estrutura multimídia existente do Android para chamar o FFmpeg.

  1. Você tem etapas / procedimentos / código / exemplo para integrar o FFmpeg no Android / StageFright?

  2. Você pode me orientar sobre como posso usar esta biblioteca para reprodução de multimídia?

  3. Eu tenho um requisito em que já tenho fluxos de transporte de áudio e vídeo, que eu preciso alimentar no FFmpeg e decodificá-lo / renderizado. Como posso fazer isso no Android, já que as APIs IOMX são baseadas em OMX e não podem conectar o FFmpeg aqui?

  4. Também não consegui encontrar documentação nas APIs do FFmpeg que precisam ser usadas para reprodução.

Entalhe
fonte
7
isso é interessante, estou curioso demais
Axarydax
5
como você compilou o ffmpeg para obter arquivos .so? você pode compartilhar as etapas que você seguiu? Estou trabalhando no windows com cygwin-1.7.9 e ndk r5. Por favor me ajude.
Swathi EP
Aqui está um relativamente novo FFmpeg para Android: sourceforge.net/projects/ffmpeg4android
slhck
@ sllck Eu baixei o código ffmpeg no link acima e tentei compilá-lo, mas não consigo obter os arquivos .so. ele mostra monte de problemas ..
RAJESH
por favor me ajudar com: stackoverflow.com/questions/14157030/... , eu não sei onde deseja incluir esta função e correr .....!
TharakaNirmana

Respostas:

109

Aqui estão as etapas que eu fiz para que o ffmpeg funcione no Android:

  1. Crie bibliotecas estáticas do ffmpeg para Android. Isso foi conseguido com a criação da porta android ffmpeg ( libffmpeg ) da olvaffe usando o Android Build System . Simplesmente coloque as fontes em / externas e makeafastadas. Você também precisará extrair biônico (libc) e zlib (libz) da versão do Android, pois as bibliotecas ffmpeg dependem delas.
  2. Crie uma funcionalidade dinâmica ffmpeg de agrupamento de biblioteca usando o NDK do Android . Existe muita documentação disponível sobre como trabalhar com o NDK. Basicamente, você precisará escrever algum código C / C ++ para exportar a funcionalidade necessária do ffmpeg para uma biblioteca na qual o Java possa interagir através do JNI. O NDK permite que você se vincule facilmente às bibliotecas estáticas que você gerou na etapa 1, basta adicionar uma linha semelhante a esta ao Android.mk:LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. Use a biblioteca dinâmica ffmpeg-wrapping de suas fontes java. Há documentação suficiente no JNI por aí, você deve ficar bem.

Quanto ao uso do ffmpeg para reprodução, existem muitos exemplos (o próprio binário do ffmpeg é um bom exemplo), aqui está um tutorial básico. A melhor documentação pode ser encontrada nos cabeçalhos.

Boa sorte :)

yonilevy
fonte
7
Existem alguns links para esta resposta para a criação do ffmpeg para Android. Essa ainda é a melhor solução? O link do Android Build System está quebrado - o que é que deveria ser? Existem vários kits de ferramentas para ajudar na criação do NDK. No entanto, todos eles falham com vários erros de compilação para mim e parecem um pouco antigos. Existe alguma razão pela qual alguém não pode simplesmente postar uma biblioteca ffmpeg estática construída?
Rob Lourens
7
Para responder a minha própria pergunta, eu encontrei este repo para ser o mais útil para a construção ffmpeg e invólucros JNI - github.com/andynicholson/android-ffmpeg-x264
Rob Lourens
68

Por várias razões, a multimídia nunca foi fácil em termos de realização da tarefa sem comprometer a eficiência. ffmpeg é um esforço para melhorá-lo dia a dia. Ele suporta diferentes formatos de codecs e contêineres.

Agora, para responder à pergunta de como usar esta biblioteca , eu diria que não é tão simples escrever aqui. Mas eu posso guiá-lo das seguintes maneiras .

1) Dentro do diretório ffmpeg do código fonte, você tem output_example.c ou api_example.c . Aqui, você pode ver o código onde a codificação / decodificação é feita. Você terá uma idéia de quais APIs dentro do ffmpeg você deve chamar. Este seria o seu primeiro passo.

2) O Dolphin player é um projeto de código aberto para o Android. Atualmente, ele está com erros, mas os desenvolvedores estão trabalhando continuamente. Nesse projeto, você tem toda a configuração pronta, que pode ser usada para continuar sua investigação. Aqui está um link para o projeto em code.google.com ou execute o comando " git clone https://code.google.com/p/dolphin-player/ " em um terminal. Você pode ver dois projetos chamados P e P86. Você pode usar qualquer um deles.

Dica adicional que eu gostaria de oferecer é que, quando você estiver criando o código ffmpeg, dentro do build.sh, você precisará ativar os muxers / desmuxers / codificadores / decodificadores dos formatos que deseja usar. Senão, o código correspondente não será incluído nas bibliotecas. Demorou muito tempo para eu perceber isso. Então, pensei em compartilhar com você.

Alguns princípios básicos: quando dizemos que um arquivo de vídeo, por exemplo: avi, é uma combinação de áudio e vídeo

Arquivo de vídeo = Vídeo + Áudio


Vídeo = Codec + Muxer + Demuxer

codec = codificador + decodificador

=> Vídeo = codificador + decodificador + Muxer + Demuxer (Mpeg4 + Mpeg4 + avi + avi - Exemplo para contêiner avi)


Áudio = Codec + Muxer + Demuxer

codec = codificador + decodificador

=> Áudio = codificador + decodificador + Muxer + Demuxer (mp2 + mp2 + avi + avi - Exemplo para contêiner avi)


Codec (o nome é derivado de uma combinação de en * co * der / * dec * oder) é apenas uma parte do formato que define os algoritmos usados ​​para codificar / decodificar um quadro. AVI não é um codec, é um contêiner que usa o codec de vídeo Mpeg4 e o codec de áudio do mp2.

O Muxer / desmuxer é usado para combinar / separar os quadros de um arquivo usado durante a codificação / decodificação.

Portanto, se você deseja usar o formato avi, precisa ativar os componentes de vídeo + componentes de áudio.

Por exemplo, para avi, é necessário ativar o seguinte. codificador mpeg4, decodificador mpeg4, codificador mp2, decodificador mp2, avi muxer, avi demuxer.

phewwwwwww ...

Programaticamente build.sh deve conter o seguinte 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 não ter te confundido mais depois de tudo isso ...

Obrigado, qualquer assistência necessária, por favor me avise.

mk ..
fonte
1
Ei, eu gostaria de agradecer muito por essa informação, você realmente me ajudou muito. É possível que você me ajude se eu precisar de algo mais tarde? Obrigado!
Idish
Posso adicionar você por skype / MSN ou qualquer outra plataforma de bate-papo, por favor? Eu tenho algumas perguntas sobre isso, obrigado.
Idish
2
Certo..!! Mas minha presença on-line é um pouco baixa. A menos que seja muito necessário, não faço login no skype. Você pode me enviar um email para qualquer coisa importante. E-mail: [email protected]
mk ..
13

A implementação mais fácil de construir e de usar que encontrei foi feita pela equipe do projeto guardian: https://github.com/guardianproject/android-ffmpeg

Cara
fonte
Não tenho certeza, acho que sim, nada na nova versão do iOS vem à mente que possa quebrar isso. Quando eu postei isso, eu ainda tinha 10,7 ou 10,6
Guy
fazer u saber, como posso converter 3gp para áudio, usando a implementação JNI
Mr.G
11

Fiz um pequeno projeto para configurar e construir o X264 e o FFMPEG usando o NDK do Android. A principal coisa que falta é uma interface JNI decente para torná-la acessível via Java, mas essa é a parte mais fácil (relativamente). Quando eu tentar melhorar a interface JNI para meus próprios usos, eu insistirei nisso.

O benefício do sistema de compilação do olvaffe é que ele não requer arquivos Android.mk para criar as bibliotecas, apenas usa os makefiles regulares e o conjunto de ferramentas. Isso torna muito menos provável que pare de funcionar quando você obtém novas alterações do FFMPEG ou X264.

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

usuario
fonte
Nick, seu projeto não está compilando no OS X 10.7 libx264.a (common.o): Na função x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale 'collect2: ld retornou 1 status de saída make: *** [x264] Erro 1
Yuriy Solovyov
6

Para fazer meu aplicativo FFMPEG, usei este projeto ( https://github.com/hiteshsondhi88/ffmpeg-android-java ), portanto, não preciso compilar nada. Eu acho que é a maneira mais fácil de usar o FFMPEG em nossos aplicativos Android.

Mais informações em http://hiteshsondhi88.github.io/ffmpeg-android-java/

jmartinalonso
fonte
3
Este wrapper é muito, muito, muito, muito, muito lento. 200 imagens em vídeo estão demorando de 50 a 60 segundos. . . mas normalmente o ffmpeg lida com essa tarefa em 4-5 segundos.
Arsen Sench
Este projeto não está mais funcionando. Você tem outros recursos?
Ajeet
@ArsenSench você tem outra solução?
Akash Dubey
3

Inspirado por muitas outras implementações do FFmpeg em Android por aí (principalmente o projeto guadiano ), encontrei uma solução (com suporte para o Lame também).

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

chamar 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 para lidar com a saída do 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.
        }
    }
}
dentex
fonte
Qual é a sua experiência com o projeto guardian?
XY
3

Estranho que este projeto não tenha sido mencionado: AndroidFFmpeg from Appunite

Possui instruções passo a passo bastante detalhadas para copiar / colar na linha de comando, para pessoas preguiçosas como eu))

Mixaz
fonte
3

Eu tive o mesmo problema, encontrei a maioria das respostas aqui fora datadas. Acabei escrevendo um wrapper no FFMPEG para acessar do Android com uma única linha de código.

https://github.com/madhavanmalolan/ffmpegandroidlibrary

Madhavan Malolan
fonte
1
Parece que você compilou o FFmpeg v2.8.4, existem planos para atualizar o FFmpeg? Estamos procurando a solução Android com a versão mais recente (pode ser 3.2 ou 3.4) do FFmpeg.
sappu
Sim. Pretendo movê-lo para 3.x github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 Você pode tentar modificar o script de construção aqui e compilar para 3.4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…
Madhavan Malolan
Obrigado @Madhvan. Estou construindo a biblioteca ffmpeg no windows. Imaginando o que tudo precisa ser alterado no github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/… para criar?
sappu
1

Primeiro, adicione a dependência da biblioteca FFmpeg

implementation 'com.writingminds:FFmpegAndroid:0.3.2'

Então carregue na atividade

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

}

Use também outro recurso do 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
fonte