Search code examples
androidvideomergeffmpegjavacv

Unable to merge videos in Android using JavaCV ("Sample Description" Error)


I am creating a video from images via FFMPEG and I am able to get the video from images. I am also making use of JavaCV to merge two videos and I am able to join videos using JavaCV without any issues provided both the videos are taken via camera, i.e, a video actually recorded via mobile camera.

Issue that I'm facing :

I am not able to merge the video that was generated from FFMPEG using the images along with the video user has chosen which will mostly be a video that was not generated and taken via mobile camera.

CODE : Code to generate Video via Images :

                  FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(path + "/" + "dec16.mp4", 800, 400);
                            try {
                                recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4);
                                //recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
                                recorder.setVideoCodecName("H264");
                                recorder.setVideoOption("preset", "ultrafast");
                                recorder.setFormat("mp4");
                                recorder.setFrameRate(frameRate);
                                recorder.setVideoBitrate(60);
                                recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
                                startTime = System.currentTimeMillis();
                                recorder.start();
                                for(int j=0;j<MomentsGetStarted.chosen_images_uri.size();j++)
                                {
                                    if(MomentsGetStarted.chosen_images_uri.get(j).video_id==0)
                                    {
                                        chosen_images.add(MomentsGetStarted.chosen_images_uri.get(j));
                                    }

                                }
                                for (int i = 0; i < chosen_images.size(); i++) {

                                    opencv_core.IplImage image = cvLoadImage(chosen_images.get(i).sdcardPath);

                                    long t = 3000 * (System.currentTimeMillis() - startTime);
                                    if (t > recorder.getTimestamp()) {

                                        recorder.setTimestamp(t);
                                        recorder.record(image);
                                    }
                                }
                                recorder.stop();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

Code to merge Videos :

 int count = file_path.size();
            System.out.println("final_joined_list size " + file_path.size());
            if (file_path.size() != 1) {
                try {
                    Movie[] inMovies = new Movie[count];
                    mediaStorageDir = new File(
                            Environment.getExternalStorageDirectory()
                                    + "/Pictures");

                    for (int i = 0; i < count; i++) {
                        File file = new File(file_path.get(i));
                        System.out.println("fileeeeeeeeeeeeeeee " + file);
                        System.out.println("file exists!!!!!!!!!!");

                        FileInputStream fis = new FileInputStream(file);
                        FileChannel fc = fis.getChannel();
                        inMovies[i] = MovieCreator.build(fc);
                        fis.close();
                        fc.close();

                    }
                    List<Track> videoTracks = new LinkedList<Track>();
                    List<Track> audioTracks = new LinkedList<Track>();
                    Log.d("Movies length", "isss  " + inMovies.length);
                    if (inMovies.length != 0) {

                        for (Movie m : inMovies) {

                            for (Track t : m.getTracks()) {
                                if (t.getHandler().equals("soun")) {
                                    audioTracks.add(t);
                                }
                                if (t.getHandler().equals("vide")) {
                                    videoTracks.add(t);
                                }
                                if (t.getHandler().equals("")) {

                                }
                            }

                        }
                    }

                    Movie result = new Movie();

                    System.out.println("audio and videoo tracks : "
                            + audioTracks.size() + " , " + videoTracks.size());
                    if (audioTracks.size() > 0) {
                        result.addTrack(new AppendTrack(audioTracks
                                .toArray(new Track[audioTracks.size()])));
                    }
                    if (videoTracks.size() > 0) {
                        result.addTrack(new AppendTrack(videoTracks
                                .toArray(new Track[videoTracks.size()])));
                    }
                    IsoFile out = null;
                    try {
                        out = (IsoFile) new DefaultMp4Builder().build(result);
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    long timestamp = new Date().getTime();
                    String timestampS = "" + timestamp;

                    File storagePath = new File(mediaStorageDir
                            + File.separator);
                    storagePath.mkdirs();
                    File myMovie = new File(storagePath, String.format("%s.mp4", timestampS));
                    FileOutputStream fos = new FileOutputStream(myMovie);
                    FileChannel fco = fos.getChannel();
                    fco.position(0);
                    out.getBox(fco);
                    fco.close();
                    fos.close();

                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                String mFileName = Environment.getExternalStorageDirectory()
                        .getAbsolutePath();
                // mFileName += "/output.mp4";

                File sdCardRoot = Environment.getExternalStorageDirectory();
                File yourDir = new File(mediaStorageDir + File.separator);
                for (File f : yourDir.listFiles()) {
                    if (f.isFile())
                        name = f.getName();
                    // make something with the name
                }
                mFileName = mediaStorageDir.getPath() + File.separator
                        + "output-%s.mp4";
                System.out.println("final filename : "
                        + mediaStorageDir.getPath() + File.separator
                        + "output-%s.mp4" + "names of files : " + name);
                single_video = false;
                return name;
            } else {
                single_video = true;
                name = file_path.get(0);
                return name;
            }

Error :

The Error that I am facing while trying to merge the videos generated via Images and a normal video is

12-15 12:26:06.155  26022-26111/? W/System.err﹕ java.io.IOException: Cannot append com.googlecode.mp4parser.authoring.Mp4TrackImpl@45417c38 to com.googlecode.mp4parser.authoring.Mp4TrackImpl@44ffac60 since their Sample Description Boxes differ
12-15 12:26:06.155  26022-26111/? W/System.err﹕ at com.googlecode.mp4parser.authoring.tracks.AppendTrack.<init>(AppendTrack.java:48)

Fix that I tried :

Google advised me to change the CODEC in JavaCV from avcodec.AV_CODEC_ID_MPEG4 to avcodec.AV_CODEC_ID_H264. But when I did that, I am not able to get the video from images thereby throwing the following error :

12-15 12:26:05.840  26022-26089/? W/linker﹕ libavcodec.so has text relocations. This is wasting memory and is a security risk. Please fix.
12-15 12:26:05.975  26022-26089/? W/System.err﹕ com.googlecode.javacv.FrameRecorder$Exception: avcodec_open2() error -1: Could not open video codec.
12-15 12:26:05.975  26022-26089/? W/System.err﹕ at com.googlecode.javacv.FFmpegFrameRecorder.startUnsafe(FFmpegFrameRecorder.java:492)
12-15 12:26:05.975  26022-26089/? W/System.err﹕ at com.googlecode.javacv.FFmpegFrameRecorder.start(FFmpegFrameRecorder.java:267)

What I need :

Creating video from Images is inevitable and that video will definitely be used to merge with other videos which might have any Codec Formats. So I need to find a way to merge any kind of videos irrespective of their Codecs or any other parameters. I am trying to keep it simple by just using the Jars and SO files and I dont want to drive myself crazy by going on a full scale implementation of FFMPEG Library. That being said, I am also ready to look into that library if I dont have any other ways to achieve what I want but a solid resource with an ALMOST working code would be much appreciated. Cheers.

Update : I looked upon the issues mentioned at GitHub of OpenCV, but didnt find anything solid from it. OpenCV Issues


Solution

  • You are using an MP4 parser which means it won't touch the encoding of the videos. This will only work if the two videos share the same encoder settings like resolution, framerate to list some of the most obvious ones.

    If you need to merge videos with different codecs or parameters then you must re-encode them to target a common format and set of parameters and in this case a simple MP4 parser won't do.

    You can achieve this directly with ffmpeg.