14

我已经设法使用 NDK 版本 r5c 为 Android NDK编译halfninja ffmpeg 脚本。(不幸的是,任何使用早期 NDK 编译的尝试都会产生一些错误),而且我对整个 NDK 过程也不是很了解,所以这对我来说有点命中注定。

他的脚本正在编译 ffmpeg 版本 N-30996-gf925b24(他为其编写脚本的具体提交)

继续我的实际应用程序。我设法毫无问题地修剪视频,现在我需要加入/连接它们,但是任何尝试使用在这 3 个链接(link1link2link3 )上找到的命令的任何和几种组合都会生成错误,例如cat is not valid,或试图覆盖一些输入文件。> is undefininedunknown option filter_complex

有谁知道是否有可能以及(如何做),在 Android 上使用 ffmpeg 的半忍者编译来加入/连接 mp4 视频(所有相同的编解码器、大小、质量等),或者如何编译/获取 ffmpeg Android 使用最新的源代码?

我还快速尝试了mp4Parser,但没有取得多大成功。

最终我试图让这个伪方法起作用:

public static File concatenate(String[] inputPaths, String outputPath){

    // ... do stuff do generate ffmpeg commands....
    VideoKit v = new VideoKit();
    v.run(cmds);

    File f = new File(outputPath);
    return f;
}
4

5 回答 5

4

LordNeckbeard提供的答案确实是要走的路。

如何将flv文件合并为一个?

处理您的限制

  • -f concat
  • -c
  • -bsf
ffmpeg -i q.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb q.ts
ffmpeg -i r.mp4 -vcodec copy -acodec copy -vbsf h264_mp4toannexb r.ts
ffmpeg -i 'concat:q.ts|r.ts' -vcodec copy -acodec copy -absf aac_adtstoasc qr.mp4

加入 H264 *无需*重新编码

于 2013-04-23T19:19:16.047 回答
2

此序列将在 CLI 上查找 mp4。它来自连接的ffmpeg常见问题页面......

$FFMPEG_HOME/ffmpeg  -i gpsclip_seg1.mp4  -vn -f u16le -acodec pcm_s16le -ac 1 -ar 44100 - > temp1.a < /dev/null
$FFMPEG_HOME/ffmpeg  -i gpsclip_seg2.mp4  -vn -f u16le -acodec pcm_s16le -ac 1 -ar 44100 - > temp2.a < /dev/null
$FFMPEG_HOME/ffmpeg  -i gpsclip_seg3.mp4  -vn -f u16le -acodec pcm_s16le -ac 1 -ar 44100 - > temp3.a < /dev/null
cat temp1.a temp2.a temp3.a > all.a

$FFMPEG_HOME/ffmpeg -i gpsclip_seg1.mp4 -an -f yuv4mpegpipe - > temp1.v < /dev/null &
$FFMPEG_HOME/ffmpeg -i gpsclip_seg2.mp4 -an -f yuv4mpegpipe - < /dev/null | tail -n +2 > temp2.v
$FFMPEG_HOME/ffmpeg -i gpsclip_seg3.mp4 -an -f yuv4mpegpipe - < /dev/null | tail -n +2 > temp3.v
cat temp1.v temp2.v temp3.v > all.v

$FFMPEG_HOME/ffmpeg -f u16le -acodec pcm_s16le -ac 1 -ar 44100 -i all.a -f yuv4mpegpipe -i all.v -same_quant -y output.mp4

我查看了 halfninja 的“Android.mk”...为了进行测试,您应该能够使用 adb 将“ffmpeg”可执行文件从 halfninja 构建推送到手机上的 /data/local/...。在构建项目时,我认为可执行文件将位于项目中 JNI 文件夹上方的 ../output 文件夹中。

假设您可以在设备上获取 root,然后您可以通过获取 shell 来在手机上的 CLI 界面上进行测试,然后使用“su”获取 root,然后从 ffmpeg/MP4/concat 线程中复制 cli 表达式,例如这个和在手机上运行它们并输出到您有权访问的文件夹。

在测试模式下,如果您可以使用 step at a time CLI 调用获得所需的结果,如链接接受的答案所示,则可以返回 JNI 接口,调用 halfninja 的“videokit”包,实现相同的命令序列你在测试中使用的。

添加了关于多个呼叫的注释...

由于您将在 JNI 中多次调用 ffmpeg lib,因此您应该注意这个问题,它可能会影响通过 JNI 多次调用 ffmpeg。如果 halfninja 尚未解决此问题,您可能必须更改 Android.mk 结构以实现线程中讨论的包装库,以便您可以在每次调用 ffmpeg 之间通过 JNI 加载/卸载所需的共享库.

机器人和“猫”

你应该在手机上的 /system/bin 中有一个符号链接

lrwxr-xr-x root     shell             2012-07-09 13:02 cat -> toolbox

如果没有,请尝试在手机上安装“busybox”,这样您就可以在手机上的 cli 上模拟脚本。

于 2013-04-23T17:39:46.113 回答
1

由于 FFmpeg 的 halfninja 版本无法使用连接功能,我建议您将 FFmpeg 库更新到至少 1.1 版。

在我看来,你有两个选择:

  • 尝试使用这两个Compiling FFmpeg on Android指南之一来编译更新版本的 FFmpeg。那么您可能还需要更新版本的 Android NDK。这是最简单的解决方案。

  • 或者尝试在 halfninja 库中实现更新版本的 FFmpeg,这更难,但您可以保持几乎相同的界面。

于 2013-04-25T11:15:54.380 回答
1

ffmpeg concat demuxer 已添加到 FFMPEG Fire Flower(1.1 版)中。使用 FFmpeg Fire Flower 或 Magic 来获得此功能。构建 ffmpeg 后,使用解复用器。在此http://ffmpeg.org/trac/ffmpeg/wiki/How%20to%20concatenate%20(join,%20merge)%20media%20files 站点中将其解释为 concat demuxer。

于 2013-04-25T09:34:32.960 回答
0

你好,我有那个灵魂。我使用 Mp4parser 库

public class Mp4ParserWrapper {

    public static final String TAG = Mp4ParserWrapper.class.getSimpleName();

    public static final int FILE_BUFFER_SIZE = 1024;

    /**
     * Appends mp4 audio/video from {@code anotherFileName} to {@code mainFileName}.
     */
    public static boolean append(String mainFileName, String anotherFileName) {
        boolean rvalue = false;
        try {
            File targetFile = new File(mainFileName);
            File anotherFile = new File(anotherFileName);
            if (targetFile.exists() && targetFile.length()>0) {
                String tmpFileName = mainFileName + ".tmp";
                //mainfile=vishal0
                //another file=vishal1
                //tmpfile=vishal0.tmp

                append(mainFileName, anotherFileName, tmpFileName);
                copyFile(tmpFileName, mainFileName);
                anotherFile.delete();
                new File(tmpFileName).delete();
                rvalue = true;
            } else if ( targetFile.createNewFile() ) {
                copyFile(anotherFileName, mainFileName);
                anotherFile.delete();
                rvalue = true;
            }
        } catch (IOException e) {
            Log.e(TAG, "Append two mp4 files exception", e);
        }
        return rvalue;
    }


    public static void copyFile(final String from, final String destination)
            throws IOException {
        FileInputStream in = new FileInputStream(from);
        FileOutputStream out = new FileOutputStream(destination);
        copy(in, out);
        in.close();
        out.close();
    }

    public static void copy(FileInputStream in, FileOutputStream out) throws IOException {
        byte[] buf = new byte[FILE_BUFFER_SIZE];
        int len;
        while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
        }
    }

    public static void append(
            final String firstFile,
            final String secondFile,
            final String newFile) throws IOException {


        final FileInputStream fisOne = new FileInputStream(new File(secondFile));
        final FileInputStream fisTwo = new FileInputStream(new File(firstFile));
        final FileOutputStream fos = new FileOutputStream(new File(String.format(newFile)));

        append(fisOne, fisTwo, fos);

        fisOne.close();
        fisTwo.close();
        fos.close();
    }



    // FIXME remove deprecated code
    public static void append(
            final FileInputStream fisOne,
            final FileInputStream fisTwo,
            final FileOutputStream out) throws IOException {

        final Movie movieOne = MovieCreator.build(Channels.newChannel(fisOne));
        final Movie movieTwo = MovieCreator.build(Channels.newChannel(fisTwo));
        final Movie finalMovie = new Movie();

        final List<Track> movieOneTracks = movieOne.getTracks();
        final List<Track> movieTwoTracks = movieTwo.getTracks();

        for (int i = 0; i <movieOneTracks.size() || i < movieTwoTracks.size(); ++i) {
            finalMovie.addTrack(new AppendTrack(movieTwoTracks.get(i), movieOneTracks.get(i)));
        }

        final IsoFile isoFile = new DefaultMp4Builder().build(finalMovie);
        isoFile.getBox(out.getChannel());
    }

}

并调用:

Mp4ParserWrapper.append(firstfilename,secondfilename);
于 2014-01-20T14:18:19.473 回答