FFmpeg在Android上


207

我已经在Android上编译了FFmpeg(libffmpeg.so)。现在,我必须构建诸如RockPlayer之类的应用程序,或者使用现有的Android多媒体框架来调用FFmpeg。

  1. 您是否有在Android / StageFright上集成FFmpeg的步骤/过程/代码/示例?

  2. 您能指导我如何使用该库进行多媒体播放吗?

  3. 我有一个要求,我已经有音频和视频传输流,我需要将其馈送到FFmpeg并对其进行解码/渲染。由于IOMX API基于OMX,并且无法在此处插入FFmpeg,因此如何在Android上执行此操作?

  4. 另外,我找不到需要用于播放的FFmpeg API的文档。


7
这很有趣,我也很好奇
Axarydax 2011年

5
您如何编译ffmpeg以获取.so文件?您能否分享您遵循的步骤。我正在使用cygwin-1.7.9和ndk r5在Windows上工作。请帮我。
Swathi EP

这是针对Android的相对较新的FFmpeg:sourceforge.net/projects/ffmpeg4android
slhck 2012年

@slhck我已经从上面的链接下载了ffmpeg代码,并尝试对其进行编译,但是我无法获取.so文件。它显示了很多问题..
拉耶什

请为我提供帮助:stackoverflow.com/questions/14157030/…,我不知道在哪里包括此功能并运行!.....
TharakaNirmana 2013年

Answers:


109

这是我为使ffmpeg在Android上工作而经历的步骤:

  1. 为Android构建ffmpeg的静态库。这是通过使用Android Build System构建olvaffe的ffmpeg android端口(libffmpeg)来实现的。只需将源放在/ external下即可。您还需要从Android版本中提取bionic(libc)和zlib(libz),因为ffmpeg库依赖于它们。make
  2. 使用Android NDK创建包含ffmpeg功能的动态库。关于如何使用NDK,有很多文档。基本上,您需要编写一些C / C ++代码以将所需的功能从ffmpeg导出到Java可通过JNI进行交互的库中。使用NDK,您可以轻松地链接到在步骤1中生成的静态库,只需在Android.mk中添加类似于以下内容的行:LOCAL_STATIC_LIBRARIES := libavcodec libavformat libavutil libc libz

  3. 使用Java来源中的ffmpeg-wrapping动态库。有足够的有关JNI的文档,您应该很好。

关于使用ffmpeg进行播放,有很多示例(ffmpeg二进制本身就是一个很好的示例),是一个基本教程。最好的文档可以在标题中找到。

祝好运 :)


7
有很多链接可以链接到此答案,用于为Android构建ffmpeg。这仍然是最好的解决方案吗?Android Build System链接已损坏-应该是什么?有很多工具包可以帮助使用NDK进行构建。但是它们对我而言都因各种构建错误而失败,并且看起来有些旧。为什么有人不能仅仅发布内置的静态ffmpeg库?
罗伯·劳伦斯

7
为了回答我自己的问题,我发现此存储库对于构建ffmpeg和JNI包装器最有用-github.com/andynicholson/android-ffmpeg-x264
Rob Lourens

68

由于各种原因,在不影响效率的前提下,多媒体在实现任务方面曾经是,而且从未如此容易。ffmpeg是一项不断改进的工作。它支持不同格式的编解码器和容器。

现在回答如何使用该库的问题,我要说在这里编写它不是那么简单。但是我可以按照以下方式指导您。

1)在源代码的ffmpeg目录中,您具有output_example.capi_example.c。在这里,您可以看到完成编码/解码的代码。您将了解应该在ffmpeg中调用哪个API。这是您的第一步。

2)Dolphin Player是Android的开源项目。目前,它存在错误,但是开发人员正在持续工作。在该项目中,您已准备好完整的设置,可以用来继续进行调查。这是来自code.google.com 的项目链接,或在终端中运行命令“ git clone https://code.google.com/p/dolphin-player/ ”。您可以看到两个名为P和P86的项目。您可以使用它们之一。

我想提供的额外提示是,在构建ffmpeg代码时,在build.sh内,需要启用要使用的格式的复用器/解复用器/编码器/解码器。否则,相应的代码将不会包含在库中。我花了很多时间才意识到这一点。因此,想与您分享。

很少基础知识: 当我们说视频文件时,例如:avi,它是音频和视频的组合

视频文件=视频+音频


视频=编解码器+混合器+解复用器

编解码器=编码器+解码器

=> 视频=编码器+解码器+ Muxer + Demuxer(Mpeg4 + Mpeg4 + avi + avi-avi容器示例)


音频=编解码器+混音器+解复用器

编解码器=编码器+解码器

=> 音频=编码器+解码器+混合器+解复用器(mp2 + mp2 + avi + avi-avi容器示例)


编解码器(名称由en * co * der / * dec * oder的组合派生而来)只是格式的一部分,该格式定义了用于对帧进行编码/解码的算法。AVI不是编解码器,它是使用Mpeg4的视频编解码器和mp2的音频编解码器的容器。

混合器/解复用器用于将帧与编码/解码时使用的文件合并/分离。

因此,如果要使用avi格式,则需要启用视频组件+音频组件。

例如,对于avi,您需要启用以下功能。mpeg4编码器,mpeg4解码器,mp2编码器,mp2解码器,avi混合器,avi多路分解器。

phewwwwwww ...

以编程方式build.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)

希望我以后不要再让您感到困惑...

谢谢,需要任何帮助,请告诉我。


1
嘿,我非常感谢您提供的信息,您的确为我提供了很大帮助,如果以后需要我的帮助,您可以帮我吗?谢谢!
2012年

我可以通过skype / MSN或任何其他聊天平台添加您吗?我对此有一些疑问,谢谢。
2012年

2
当然..!!但是我的在线状态有点低。除非非常需要,否则我不会登录到Skype。您可以将任何重要的事情邮寄给我。电子邮件:mantykuma@gmail.com
MK ..


11

我做了一个小项目,使用Android NDK配置和构建X264和FFMPEG。缺少的主要内容是一个不错的JNI接口,可以通过Java访问它,但这是(相对)容易的部分。当我设法使JNI接口适合我自己的用途时,我将其付诸实践。

与olvaffe的构建系统相比,它的优势在于它不需要Android.mk文件来构建库,它仅使用常规的makefile和工具链。当您从FFMPEG或X264中进行新更改时,这使停止工作的可能性大大降低。

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


尼克,您的项目未在OS X 10.7上编译libx264.a(common.o):在函数x264_param_parse': common.c:(.text+0x2864): undefined reference to _DefaultRuneLocale'collect2中:ld返回了1个退出状态:*** [x264]错误1
Yuriy Solovyov


6

为了制作FFMPEG应用程序,我使用了这个项目(https://github.com/hiteshsondhi88/ffmpeg-android-java),因此,我不需要编译任何东西。我认为这是在我们的Android应用程序中使用FFMPEG的简便方法。

有关更多信息,请参见http://hiteshsondhi88.github.io/ffmpeg-android-java/


3
这个包装器非常非常非常非常慢。200张图像转换为视频需要50-60秒。。。但通常ffmpeg会在4-5秒钟内完成该任务。
Arsen Sench

该项目不再工作。您还有其他资源吗?
Ajeet

@ArsenSench您还有其他解决方案吗?
Akash Dubey

3

受Android上其他许多FFmpeg实现的启发(主要是guadianproject),我找到了一个解决方案(也支持Lame)。

(lame和FFmpeg:https : //github.com/intervigilium/liblamehttp://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.
        }
    }
}

您对Guardianproject的经验是什么?
XY 2014年


3

我遇到了同样的问题,我发现这里大多数答案都是过时的。我最终在FFMPEG上编写了一个包装程序,以单行代码即可从Android进行访问。

https://github.com/madhavanmalolan/ffmpegandroidlibrary


1
似乎您已编译FFmpeg v2.8.4,是否有计划升级FFmpeg?我们正在寻找具有FFmpeg最新版本(可能是3.2或3.4)的android解决方案。
sappu

是。我不打算把它移动到3.X github.com/madhavanmalolan/ffmpegandroidlibrary/milestone/1 你可以在这里尝试修改生成脚本和编译3.4 github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/...
Madhavan表示Malolan

谢谢@Madhvan。我在Windows上构建ffmpeg库。只是想知道在github.com/madhavanmalolan/ffmpegandroidlibrary/wiki/…中需要进行哪些更改才能进行构建?
sappu

0

首先,添加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();

}

还可以使用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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.