sannies / mp4parser

A Java API to read, write and create MP4 files
Apache License 2.0
2.74k stars 563 forks source link

Trim Video #301

Open Allansk2 opened 6 years ago

Allansk2 commented 6 years ago

Does anyone know how to trim a video with the library. I find some solution in stackoverflow, but it always return the video from 0 second to 15 second, not matter what startMs and endMs. Does anyone know how to fix it? Thanks!

String src = "/storage/emulated/0/Video/my.mp4";
        String des = FileUtil.getVideoDirectory();
        File file = new File(src);
        long startMs = 15000;
        long endMs = 25000;
        try {
            VideoUtil.startTrim(file, des, startMs, endMs);

        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG,"startTrim error " + e.getMessage());
        }

@SuppressWarnings("ResultOfMethodCallIgnored")
    public static void startTrim(@NonNull File src, @NonNull String dst, long startMs, long endMs) throws IOException {
        final String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
        final String fileName = "MP4_" + timeStamp + ".mp4";
        final String filePath = dst + fileName;

        File file = new File(filePath);
        file.getParentFile().mkdirs();
        Log.e(TAG, "Generated file path " + filePath);
        genVideoUsingMp4Parser(src, file, startMs, endMs);
    }

    @SuppressWarnings("ResultOfMethodCallIgnored")
    private static void genVideoUsingMp4Parser(@NonNull File src, @NonNull File dst, long startMs, long endMs) throws IOException {
        // NOTE: Switched to using FileDataSourceViaHeapImpl since it does not use memory mapping (VM).
        // Otherwise we get OOM with large movie files.
        Movie movie = MovieCreator.build(new FileDataSourceViaHeapImpl(src.getAbsolutePath()));

        List<Track> tracks = movie.getTracks();
        movie.setTracks(new LinkedList<Track>());
        // remove all tracks we will create new tracks from the old

        double startTime1 = startMs / 1000;
        double endTime1 = endMs / 1000;
        Log.e(TAG,"startTime1 " + startTime1 + ", endTime1 " + endTime1 );

        boolean timeCorrected = false;

        // Here we try to find a track that has sync samples. Since we can only start decoding
        // at such a sample we SHOULD make sure that the start of the new fragment is exactly
        // such a frame
        for (Track track : tracks) {
            if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
                if (timeCorrected) {
                    // This exception here could be a false positive in case we have multiple tracks
                    // with sync samples at exactly the same positions. E.g. a single movie containing
                    // multiple qualities of the same video (Microsoft Smooth Streaming file)

                    throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");
                }
                startTime1 = correctTimeToSyncSample(track, startTime1, false);
                endTime1 = correctTimeToSyncSample(track, endTime1, true);
                timeCorrected = true;
            }
        }

        Log.e(TAG,"after correctTimeToSyncSample startTime1 " + startTime1 + ", endTime1 " + endTime1 );

        for (Track track : tracks) {
            long currentSample = 0;
            double currentTime = 0;
            double lastTime = -1;
            long startSample1 = -1;
            long endSample1 = -1;

            for (int i = 0; i < track.getSampleDurations().length; i++) {
                long delta = track.getSampleDurations()[i];

                if (currentTime > lastTime && currentTime <= startTime1) {
                    // current sample is still before the new starttime
                    startSample1 = currentSample;
                }
                if (currentTime > lastTime && currentTime <= endTime1) {
                    // current sample is after the new start time and still before the new endtime
                    endSample1 = currentSample;
                }
                lastTime = currentTime;
                currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
                currentSample++;
            }
            Log.e(TAG,"startSample1 " + startSample1 + ", endSample1 " + endSample1 );
            movie.addTrack(new AppendTrack(new CroppedTrack(track, startSample1, endSample1)));
        }

        dst.getParentFile().mkdirs();

        if (!dst.exists()) {
            dst.createNewFile();
        }

        Container out = new DefaultMp4Builder().build(movie);

        FileOutputStream fos = new FileOutputStream(dst);
        FileChannel fc = fos.getChannel();
        out.writeContainer(fc);

        fc.close();
        fos.close();

        Log.e(TAG,"startTrim success " );

    }

    private static double correctTimeToSyncSample(@NonNull Track track, double cutHere, boolean next) {
        double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
        long currentSample = 0;
        double currentTime = 0;
        for (int i = 0; i < track.getSampleDurations().length; i++) {
            long delta = track.getSampleDurations()[i];

            if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
                // samples always start with 1 but we start with zero therefore +1
                timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
            }
            currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
            currentSample++;

        }
        double previous = 0;
        for (double timeOfSyncSample : timeOfSyncSamples) {
            if (timeOfSyncSample > cutHere) {
                if (next) {
                    return timeOfSyncSample;
                } else {
                    return previous;
                }
            }
            previous = timeOfSyncSample;
        }
        return timeOfSyncSamples[timeOfSyncSamples.length - 1];
    }
avanibhatnagar commented 5 years ago

@Allansk2 Ever figure this out? Also the video that is created, is it H264 codec or MPEG4?

kunall17 commented 5 years ago

@avanibhatnagar

        try {

            Movie orig_movie = MovieCreator.build(videoInput);

            File audioFile = new File(audioPath);

            AACTrackImpl aacTrack = new AACTrackImpl(new FileDataSourceImpl(audioFile));

//get duration of video
            IsoFile isoFile = new IsoFile(videoPath);
            double lengthInSeconds = (double)
                    isoFile.getMovieBox().getMovieHeaderBox().getDuration() /
                    isoFile.getMovieBox().getMovieHeaderBox().getTimescale();

            Track track = (Track) orig_movie.getTracks().get(0);

            Track audioTrack = (Track) aacTrack;

            double startTime1 = 0;
            double endTime1 = lengthInSeconds;

            boolean timeCorrected = false;

            if (audioTrack.getSyncSamples() != null && audioTrack.getSyncSamples().length > 0) {
                if (timeCorrected) {

                    throw new RuntimeException("The startTime has already been corrected by another track with SyncSample. Not Supported.");
                }
                startTime1 = correctTimeToSyncSample(audioTrack, startTime1, false);
                endTime1 = correctTimeToSyncSample(audioTrack, endTime1, true);
                timeCorrected = true;
            }

            long currentSample = 0;
            double currentTime = 0;
            double lastTime = -1;
            long startSample1 = -1;
            long endSample1 = -1;

            for (int i = 0; i < audioTrack.getSampleDurations().length; i++) {
                long delta = audioTrack.getSampleDurations()[i];

                if (currentTime > lastTime && currentTime <= startTime1) {
                    // current sample is still before the new starttime
                    startSample1 = currentSample;
                }
                if (currentTime > lastTime && currentTime <= endTime1) {
                    // current sample is after the new start time and still before the new endtime
                    endSample1 = currentSample;
                }

                lastTime = currentTime;
                currentTime += (double) delta / (double) audioTrack.getTrackMetaData().getTimescale();
                currentSample++;
            }

            CroppedTrack cropperAacTrack = new CroppedTrack(aacTrack, startSample1, endSample1);

            Movie movie = new Movie();

            movie.addTrack(track);
            movie.addTrack(cropperAacTrack);

            Container mp4file = new DefaultMp4Builder().build(movie);

            FileChannel fc = new FileOutputStream(new File(output)).getChannel();
            mp4file.writeContainer(fc);
            fc.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
}
   private static double correctTimeToSyncSample(Track track, double cutHere, boolean next) {
        double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
        long currentSample = 0;
        double currentTime = 0;
        for (int i = 0; i < track.getSampleDurations().length; i++) {
            long delta = track.getSampleDurations()[i];

            if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
                timeOfSyncSamples[Arrays.binarySearch(track.getSyncSamples(), currentSample + 1)] = currentTime;
            }
            currentTime += (double) delta / (double) track.getTrackMetaData().getTimescale();
            currentSample++;

        }
        double previous = 0;
        for (double timeOfSyncSample : timeOfSyncSamples) {
            if (timeOfSyncSample > cutHere) {
                if (next) {
                    return timeOfSyncSample;
                } else {
                    return previous;
                }
            }
            previous = timeOfSyncSample;
        }
        return timeOfSyncSamples[timeOfSyncSamples.length - 1];
    }
vijendrapatidar commented 3 years ago

When I have to trim the 6-sec video like startTime on 1sec and endTime 5sec then getSyncSamples give the timeOfSyncSample always 0.0

kgnkbyl commented 2 years ago

Hey @vijendrapatidar. It is a bit late but were you able to solve your problem here?