Concatenate mp4 files in Android using halfninja ffmpeg
Asked Answered
C

5

14

I've manage to compile halfninja ffmpeg scripts for Android NDK using NDK version r5c. (Unfortunately any attempt to compile with earlier NDK generated some error), also I'm not very knowledgeable on the whole NDK process, so it's a bit hit-n-miss for me.

His scripts are compiling ffmpeg version N-30996-gf925b24 (the specific commit he did the scripts for)

Moving forward to my actual app. I manage to trim videos without problems, now I need to join/concatenate them but any attemp at using any and several combinations of the commands found on those 3 links (link1, link2, link3) generate errors such as cat is not valid, > is undefinined, unknown option filter_complex or trying to override some of the input files.

Does anyone know if it's possible and (how to do it), to join/concatenate mp4 videos (all same codec, size, quality, etc) using half-ninja compile of ffmpeg on Android, or how to compile/get a ffmpeg for Android using latest source codes?

I've also gave a quick try on the mp4Parser without much success.

ultimately I was trying to get this pseudo-method to work:

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;
}
Cotton answered 19/4, 2013 at 15:48 Comment(1)
i managed to compile half-ninja project , but now i want to compress large video files , but first of all i tried to get audio from a audio file, unfortutaly run command doesnt work , gives following error , 12-20 13:31:26.958: W/ActivityManager(290): Force removing ActivityRecord{2c0322f0 uk.co.halfninja.videokit/.MainActivity}: app died, no saved state any particular reason ??Metternich
D
4

The answer provided by LordNeckbeard is really the way to go.

How to concatenate flv file into one?

Working with your restrictions

  • no -f concat
  • no -c
  • no -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

Joining H264 *without* re-encoding

Dissect answered 23/4, 2013 at 19:19 Comment(2)
hi. I just have a try on this one. -c always fail, I replaced with -vcodec and -acodec, then it gave me Unrecognised option bsf and then without the bsf I get a av_interleaved_write_frame(): Operation not permitted. I'll give a try on @Robert Rowntree comment to use this topic https://mcmap.net/q/901804/-ffmpeg-1-0-android-ndk-r8b but it will take me some time because 1) it's a personal project that I'm doing in my free time and I'm moving country so free time won't happen soon and 2) until I get my head around it and understand what's up with all the commands.Cotton
hi Steve, I'm really happy you earned the half/bounty as I could see your effort and I really really appreciate it. Unfortunately my life decided to turn a bit upside down because I'm moving country and I simply couldn't test it. But I'll certainly test and let you know. Also if/when I manage to get something working I'll put an answer.Cotton
H
2

this sequence will cat mp4's on the CLI. its from the ffmpeg faq page on concatenation...

$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

i looked at halfninja's 'Android.mk' ... and for testing, you should be able to use adb to push 'ffmpeg' executable from halfninja build to /data/local/... on the phone. On building the project, I think the executable will be in ../output folder , above the JNI folder in his project.

Assuming you can get root on the device, you can then do testing on the CLI interface on the phone by getting a shell, then getting root using 'su', then copying cli expressions from ffmpeg/MP4/concat threads such as this one and running them on the phone with outputs to a folder where you have access.

In test mode, if you can get the desired result using step at a time CLI invocations as shown in the link's accepted answer, you can then move back to your JNI interfaces , calling into halfninja's 'videokit' package, implementing the same sequence of commands that you used in test.

Added Note on multiple calls...

Since you will be calling in to ffmpeg lib in the JNI multiple times, you should be aware of this issue that can impact multiple calls into ffmpeg thru JNI. If halfninja has not already done mediated this issue, you may have to change the Android.mk structure to implement the wrapper library talked about in the thread so that you can load/unload the required shared libs in between each call to ffmpeg via the JNI.

android and 'cat'

you should have a symlink in /system/bin on the phone

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

if not , try 'busybox' install on the phone so you can simulate scripting on the cli on the phone.

Hendrickson answered 23/4, 2013 at 17:39 Comment(5)
hi. Thanks for the attempt but I'm not sure you understood the whole question. I have no problems compiling, installing or executing halfninja build of ffmpeg. And I can easily call public native void run(String[] args); from VideoKit.java to trim videos for example. The question is: what is the specific String[] I can pass to it to make it merge videos?Cotton
https://mcmap.net/q/901804/-ffmpeg-1-0-android-ndk-r8b U can try this to build more recent ffmpeg on android containing 'concat'... the reason halfninja embedded specific version of ffmpeg is that it is a messy process to go swapping in diff source trees for ffmpeg and expecting the link edits in Android.mk to work.Hendrickson
have u seen SF? sourceforge.net/projects/ffmpeg4android/?source=directoryHendrickson
hi Robert, yes I saw it. Thanks. I already manage to compile that one, but as I said, I'm very newbie on NDK matters and it will take me time to get around it and understand how to make the JNI wrapper, especially one that overcomes this (#10649619) issue. But in the meantime I'll try Steve's latest answer and will update this post whenever I get it working. Thanks.Cotton
the dlopen/dlclose issue is pretty well know - lots of posts. you should verify the exit() call in ffmpeg.c by looking at the 'main' routine at the very bottom. The reason some posts say that in the halfninja project, that you need to replace ffmpeg.c in the 'videokit' is, i think in part due to the exit() call at the end.Hendrickson
I
1

ffmpeg concat demuxer has been added to FFMPEG Fire Flower (Version 1.1). Use FFmpeg fire flower or Magic to get this feature. Once you build ffmpeg, use the demuxer. Which is explained in this http://ffmpeg.org/trac/ffmpeg/wiki/How%20to%20concatenate%20(join,%20merge)%20media%20files site as concat demuxer.

Indestructible answered 25/4, 2013 at 9:34 Comment(0)
M
1

Since the halfninja version of FFmpeg isn't able to use the concatenate functionality, I advice you to update the FFmpeg library to at least version 1.1.

In my opinion you have two options:

  • Try to compile a newer version of FFmpeg using one of these two Compiling FFmpeg on Android guides. Then you possibly also need a newer revision of the Android NDK. This is the most simple solution.

  • Or try to implement a newer version of FFmpeg in the halfninja libraries, which is harder, but then you can keep almost the same interface.

Mafala answered 25/4, 2013 at 11:15 Comment(0)
I
0

Hello I have got that soultion. I Use Mp4parser library

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

}

And invoke with:

Mp4ParserWrapper.append(firstfilename,secondfilename);
Isidora answered 20/1, 2014 at 14:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.