0

My original video is 10.3 seconds. I want to start cutting from sec 2.7 to sec 5.7

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

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


private static void genVideoUsingMp4Parser(@NonNull File src, @NonNull File dst, long startMs, long endMs, @NonNull OnTrimVideoListener callback) 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 ;  //2.7
    double endTime1 = endMs,       //5.7  

    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;

        }
    }

    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++;
        }
        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();
    if (callback != null)
        callback.getResult(Uri.parse(dst.toString()));
}

But after the method correctTimeToSyncSample is finished the startTime1 gets value 2.08... and endTime1 gets value 5.18...

startTime1 = 2.0830555555555557 endTime1 = 5.182877777777778

  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];
}

The video is successfully saved but not in the exact time I wanted..

Can anyone please help me with this

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Erald Haka
  • 182
  • 2
  • 10

2 Answers2

1

Video can only be cut at keyframes (called sync samples in mp4). A key frame is uasually every 1 to 10 seconds. To get an exact frame, you need to transcode using a tool like ffmpeg.

szatmary
  • 29,969
  • 8
  • 44
  • 57
  • thanks for your reply, but is there a posibility to cut in exact frames without re-encoding the video (because this process last to long in my case) ? thank you – Erald Haka Mar 01 '18 at 09:38
  • No. Not possible. – szatmary Mar 01 '18 at 18:07
  • then how it works in whatsapp android? the trimming of video – NehaK Jun 13 '19 at 15:23
  • @NehaK I believe WhatsApp uses `MediaCodec` in combination with `MediaMuxer` and `MediaExtractor` to achieve this. WhatsApp also cuts the video at the keyframe. – HB. Feb 03 '20 at 04:33
  • You saved my day. I changed my video frame interval to 1s and it's cropping as expected. Thanks a ton. – vikoo Feb 03 '21 at 12:17
0

You can add edts/elst/stss/stsh/sdtp box to do it.

  1. add edts/elst box to indicate the media-time and segment-duration, for your case, media-time of 'elst' box is set to the media time of 2.7s, and segment duration is set to 3 seconds with the unit of time-scale of movie.
  2. Of course, you need add Sync Sample box, Shadow Sync Sample Box and Independent and Disposable Samples Box, to specify the dependency of your first frame if it is not a key frame.

The qualified mp4 player will find the dependent sync sample before the frame at your start time, and decode all of frames edited out by means of an edit list which is used for decoding your first dependent frame, but not to present them until the first frame you specified.

ravin.wang
  • 1,122
  • 1
  • 9
  • 26