115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu/*
215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * Copyright (C) 2012 The Android Open Source Project
315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu *
415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * Licensed under the Apache License, Version 2.0 (the "License");
515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * you may not use this file except in compliance with the License.
615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * You may obtain a copy of the License at
715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu *
815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu *      http://www.apache.org/licenses/LICENSE-2.0
915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu *
1015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * Unless required by applicable law or agreed to in writing, software
1115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * distributed under the License is distributed on an "AS IS" BASIS,
1215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * See the License for the specific language governing permissions and
1415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu * limitations under the License.
1515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu */
1615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
1715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu// Modified example based on mp4parser google code open source project.
1815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu// http://code.google.com/p/mp4parser/source/browse/trunk/examples/src/main/java/com/googlecode/mp4parser/ShortenExample.java
1915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
2015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhupackage com.android.gallery3d.app;
2115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
228b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport android.media.MediaCodec.BufferInfo;
238b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport android.media.MediaExtractor;
248b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport android.media.MediaFormat;
258b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport android.media.MediaMetadataRetriever;
268b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport android.media.MediaMuxer;
278b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport android.util.Log;
288b9de91b3c01664b24deda202acb5db8594db503ztenghui
298b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport com.android.gallery3d.common.ApiHelper;
30648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhuimport com.android.gallery3d.util.SaveVideoFileInfo;
3115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport com.coremedia.iso.IsoFile;
3215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport com.coremedia.iso.boxes.TimeToSampleBox;
3315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport com.googlecode.mp4parser.authoring.Movie;
3415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport com.googlecode.mp4parser.authoring.Track;
3515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
3615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
3715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport com.googlecode.mp4parser.authoring.tracks.CroppedTrack;
3815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
3915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.io.File;
408b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport java.io.FileNotFoundException;
4115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.io.FileOutputStream;
4215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.io.IOException;
4315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.io.RandomAccessFile;
448b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport java.nio.ByteBuffer;
4515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.nio.channels.FileChannel;
4615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.util.Arrays;
478b9de91b3c01664b24deda202acb5db8594db503ztenghuiimport java.util.HashMap;
4815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.util.LinkedList;
4915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhuimport java.util.List;
5015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
51648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhupublic class VideoUtils {
528b9de91b3c01664b24deda202acb5db8594db503ztenghui    private static final String LOGTAG = "VideoUtils";
538b9de91b3c01664b24deda202acb5db8594db503ztenghui    private static final int DEFAULT_BUFFER_SIZE = 1 * 1024 * 1024;
548b9de91b3c01664b24deda202acb5db8594db503ztenghui
558b9de91b3c01664b24deda202acb5db8594db503ztenghui    /**
568b9de91b3c01664b24deda202acb5db8594db503ztenghui     * Remove the sound track.
578b9de91b3c01664b24deda202acb5db8594db503ztenghui     */
588b9de91b3c01664b24deda202acb5db8594db503ztenghui    public static void startMute(String filePath, SaveVideoFileInfo dstFileInfo)
598b9de91b3c01664b24deda202acb5db8594db503ztenghui            throws IOException {
608b9de91b3c01664b24deda202acb5db8594db503ztenghui        if (ApiHelper.HAS_MEDIA_MUXER) {
618b9de91b3c01664b24deda202acb5db8594db503ztenghui            genVideoUsingMuxer(filePath, dstFileInfo.mFile.getPath(), -1, -1,
628b9de91b3c01664b24deda202acb5db8594db503ztenghui                    false, true);
638b9de91b3c01664b24deda202acb5db8594db503ztenghui        } else {
648b9de91b3c01664b24deda202acb5db8594db503ztenghui            startMuteUsingMp4Parser(filePath, dstFileInfo);
658b9de91b3c01664b24deda202acb5db8594db503ztenghui        }
668b9de91b3c01664b24deda202acb5db8594db503ztenghui    }
6715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
688b9de91b3c01664b24deda202acb5db8594db503ztenghui    /**
698b9de91b3c01664b24deda202acb5db8594db503ztenghui     * Shortens/Crops tracks
708b9de91b3c01664b24deda202acb5db8594db503ztenghui     */
718b9de91b3c01664b24deda202acb5db8594db503ztenghui    public static void startTrim(File src, File dst, int startMs, int endMs)
728b9de91b3c01664b24deda202acb5db8594db503ztenghui            throws IOException {
738b9de91b3c01664b24deda202acb5db8594db503ztenghui        if (ApiHelper.HAS_MEDIA_MUXER) {
748b9de91b3c01664b24deda202acb5db8594db503ztenghui            genVideoUsingMuxer(src.getPath(), dst.getPath(), startMs, endMs,
758b9de91b3c01664b24deda202acb5db8594db503ztenghui                    true, true);
768b9de91b3c01664b24deda202acb5db8594db503ztenghui        } else {
778b9de91b3c01664b24deda202acb5db8594db503ztenghui            trimUsingMp4Parser(src, dst, startMs, endMs);
788b9de91b3c01664b24deda202acb5db8594db503ztenghui        }
798b9de91b3c01664b24deda202acb5db8594db503ztenghui    }
808b9de91b3c01664b24deda202acb5db8594db503ztenghui
818b9de91b3c01664b24deda202acb5db8594db503ztenghui    private static void startMuteUsingMp4Parser(String filePath,
828b9de91b3c01664b24deda202acb5db8594db503ztenghui            SaveVideoFileInfo dstFileInfo) throws FileNotFoundException, IOException {
83648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        File dst = dstFileInfo.mFile;
84648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        File src = new File(filePath);
85648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
86648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        Movie movie = MovieCreator.build(randomAccessFile.getChannel());
87648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu
88648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        // remove all tracks we will create new tracks from the old
89648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        List<Track> tracks = movie.getTracks();
90648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        movie.setTracks(new LinkedList<Track>());
91648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu
92648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        for (Track track : tracks) {
93648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu            if (track.getHandler().equals("vide")) {
94648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu                movie.addTrack(track);
95648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu            }
96648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        }
97648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        writeMovieIntoFile(dst, movie);
98648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        randomAccessFile.close();
99648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu    }
100648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu
101648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu    private static void writeMovieIntoFile(File dst, Movie movie)
102648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu            throws IOException {
103648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        if (!dst.exists()) {
104648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu            dst.createNewFile();
105648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        }
106648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu
107648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        IsoFile out = new DefaultMp4Builder().build(movie);
108648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        FileOutputStream fos = new FileOutputStream(dst);
109648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        FileChannel fc = fos.getChannel();
1108b9de91b3c01664b24deda202acb5db8594db503ztenghui        out.getBox(fc); // This one build up the memory.
111648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu
112648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        fc.close();
113648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        fos.close();
114648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu    }
115648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu
116648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu    /**
1178b9de91b3c01664b24deda202acb5db8594db503ztenghui     * @param srcPath the path of source video file.
1188b9de91b3c01664b24deda202acb5db8594db503ztenghui     * @param dstPath the path of destination video file.
1198b9de91b3c01664b24deda202acb5db8594db503ztenghui     * @param startMs starting time in milliseconds for trimming. Set to
1208b9de91b3c01664b24deda202acb5db8594db503ztenghui     *            negative if starting from beginning.
1218b9de91b3c01664b24deda202acb5db8594db503ztenghui     * @param endMs end time for trimming in milliseconds. Set to negative if
1228b9de91b3c01664b24deda202acb5db8594db503ztenghui     *            no trimming at the end.
1238b9de91b3c01664b24deda202acb5db8594db503ztenghui     * @param useAudio true if keep the audio track from the source.
1248b9de91b3c01664b24deda202acb5db8594db503ztenghui     * @param useVideo true if keep the video track from the source.
1258b9de91b3c01664b24deda202acb5db8594db503ztenghui     * @throws IOException
126648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu     */
1278b9de91b3c01664b24deda202acb5db8594db503ztenghui    private static void genVideoUsingMuxer(String srcPath, String dstPath,
1288b9de91b3c01664b24deda202acb5db8594db503ztenghui            int startMs, int endMs, boolean useAudio, boolean useVideo)
1298b9de91b3c01664b24deda202acb5db8594db503ztenghui            throws IOException {
1308b9de91b3c01664b24deda202acb5db8594db503ztenghui        // Set up MediaExtractor to read from the source.
1318b9de91b3c01664b24deda202acb5db8594db503ztenghui        MediaExtractor extractor = new MediaExtractor();
1328b9de91b3c01664b24deda202acb5db8594db503ztenghui        extractor.setDataSource(srcPath);
1338b9de91b3c01664b24deda202acb5db8594db503ztenghui
1348b9de91b3c01664b24deda202acb5db8594db503ztenghui        int trackCount = extractor.getTrackCount();
1358b9de91b3c01664b24deda202acb5db8594db503ztenghui
1368b9de91b3c01664b24deda202acb5db8594db503ztenghui        // Set up MediaMuxer for the destination.
1378b9de91b3c01664b24deda202acb5db8594db503ztenghui        MediaMuxer muxer;
1388b9de91b3c01664b24deda202acb5db8594db503ztenghui        muxer = new MediaMuxer(dstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
1398b9de91b3c01664b24deda202acb5db8594db503ztenghui
1408b9de91b3c01664b24deda202acb5db8594db503ztenghui        // Set up the tracks and retrieve the max buffer size for selected
1418b9de91b3c01664b24deda202acb5db8594db503ztenghui        // tracks.
1428b9de91b3c01664b24deda202acb5db8594db503ztenghui        HashMap<Integer, Integer> indexMap = new HashMap<Integer,
1438b9de91b3c01664b24deda202acb5db8594db503ztenghui                Integer>(trackCount);
1448b9de91b3c01664b24deda202acb5db8594db503ztenghui        int bufferSize = -1;
1458b9de91b3c01664b24deda202acb5db8594db503ztenghui        for (int i = 0; i < trackCount; i++) {
1468b9de91b3c01664b24deda202acb5db8594db503ztenghui            MediaFormat format = extractor.getTrackFormat(i);
1478b9de91b3c01664b24deda202acb5db8594db503ztenghui            String mime = format.getString(MediaFormat.KEY_MIME);
1488b9de91b3c01664b24deda202acb5db8594db503ztenghui
1498b9de91b3c01664b24deda202acb5db8594db503ztenghui            boolean selectCurrentTrack = false;
1508b9de91b3c01664b24deda202acb5db8594db503ztenghui
1518b9de91b3c01664b24deda202acb5db8594db503ztenghui            if (mime.startsWith("audio/") && useAudio) {
1528b9de91b3c01664b24deda202acb5db8594db503ztenghui                selectCurrentTrack = true;
1538b9de91b3c01664b24deda202acb5db8594db503ztenghui            } else if (mime.startsWith("video/") && useVideo) {
1548b9de91b3c01664b24deda202acb5db8594db503ztenghui                selectCurrentTrack = true;
1558b9de91b3c01664b24deda202acb5db8594db503ztenghui            }
1568b9de91b3c01664b24deda202acb5db8594db503ztenghui
1578b9de91b3c01664b24deda202acb5db8594db503ztenghui            if (selectCurrentTrack) {
1588b9de91b3c01664b24deda202acb5db8594db503ztenghui                extractor.selectTrack(i);
1598b9de91b3c01664b24deda202acb5db8594db503ztenghui                int dstIndex = muxer.addTrack(format);
1608b9de91b3c01664b24deda202acb5db8594db503ztenghui                indexMap.put(i, dstIndex);
1618b9de91b3c01664b24deda202acb5db8594db503ztenghui                if (format.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
1628b9de91b3c01664b24deda202acb5db8594db503ztenghui                    int newSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
1638b9de91b3c01664b24deda202acb5db8594db503ztenghui                    bufferSize = newSize > bufferSize ? newSize : bufferSize;
1648b9de91b3c01664b24deda202acb5db8594db503ztenghui                }
1658b9de91b3c01664b24deda202acb5db8594db503ztenghui            }
1668b9de91b3c01664b24deda202acb5db8594db503ztenghui        }
1678b9de91b3c01664b24deda202acb5db8594db503ztenghui
1688b9de91b3c01664b24deda202acb5db8594db503ztenghui        if (bufferSize < 0) {
1698b9de91b3c01664b24deda202acb5db8594db503ztenghui            bufferSize = DEFAULT_BUFFER_SIZE;
1708b9de91b3c01664b24deda202acb5db8594db503ztenghui        }
1718b9de91b3c01664b24deda202acb5db8594db503ztenghui
1728b9de91b3c01664b24deda202acb5db8594db503ztenghui        // Set up the orientation and starting time for extractor.
1738b9de91b3c01664b24deda202acb5db8594db503ztenghui        MediaMetadataRetriever retrieverSrc = new MediaMetadataRetriever();
1748b9de91b3c01664b24deda202acb5db8594db503ztenghui        retrieverSrc.setDataSource(srcPath);
1758b9de91b3c01664b24deda202acb5db8594db503ztenghui        String degreesString = retrieverSrc.extractMetadata(
1768b9de91b3c01664b24deda202acb5db8594db503ztenghui                MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
1778b9de91b3c01664b24deda202acb5db8594db503ztenghui        if (degreesString != null) {
1788b9de91b3c01664b24deda202acb5db8594db503ztenghui            int degrees = Integer.parseInt(degreesString);
1798b9de91b3c01664b24deda202acb5db8594db503ztenghui            if (degrees >= 0) {
1808b9de91b3c01664b24deda202acb5db8594db503ztenghui                muxer.setOrientationHint(degrees);
1818b9de91b3c01664b24deda202acb5db8594db503ztenghui            }
1828b9de91b3c01664b24deda202acb5db8594db503ztenghui        }
1838b9de91b3c01664b24deda202acb5db8594db503ztenghui
1848b9de91b3c01664b24deda202acb5db8594db503ztenghui        if (startMs > 0) {
1858b9de91b3c01664b24deda202acb5db8594db503ztenghui            extractor.seekTo(startMs * 1000, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
1868b9de91b3c01664b24deda202acb5db8594db503ztenghui        }
1878b9de91b3c01664b24deda202acb5db8594db503ztenghui
1888b9de91b3c01664b24deda202acb5db8594db503ztenghui        // Copy the samples from MediaExtractor to MediaMuxer. We will loop
1898b9de91b3c01664b24deda202acb5db8594db503ztenghui        // for copying each sample and stop when we get to the end of the source
1908b9de91b3c01664b24deda202acb5db8594db503ztenghui        // file or exceed the end time of the trimming.
1918b9de91b3c01664b24deda202acb5db8594db503ztenghui        int offset = 0;
1928b9de91b3c01664b24deda202acb5db8594db503ztenghui        int trackIndex = -1;
1938b9de91b3c01664b24deda202acb5db8594db503ztenghui        ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
1948b9de91b3c01664b24deda202acb5db8594db503ztenghui        BufferInfo bufferInfo = new BufferInfo();
19571e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui        try {
19671e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui            muxer.start();
19771e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui            while (true) {
19871e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                bufferInfo.offset = offset;
19971e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                bufferInfo.size = extractor.readSampleData(dstBuf, offset);
20071e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                if (bufferInfo.size < 0) {
20171e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                    Log.d(LOGTAG, "Saw input EOS.");
20271e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                    bufferInfo.size = 0;
2038b9de91b3c01664b24deda202acb5db8594db503ztenghui                    break;
2048b9de91b3c01664b24deda202acb5db8594db503ztenghui                } else {
20571e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                    bufferInfo.presentationTimeUs = extractor.getSampleTime();
20671e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                    if (endMs > 0 && bufferInfo.presentationTimeUs > (endMs * 1000)) {
20771e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                        Log.d(LOGTAG, "The current sample is over the trim end time.");
20871e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                        break;
20971e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                    } else {
21071e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                        bufferInfo.flags = extractor.getSampleFlags();
21171e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                        trackIndex = extractor.getSampleTrackIndex();
21271e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui
21371e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                        muxer.writeSampleData(indexMap.get(trackIndex), dstBuf,
21471e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                                bufferInfo);
21571e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                        extractor.advance();
21671e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui                    }
2178b9de91b3c01664b24deda202acb5db8594db503ztenghui                }
2188b9de91b3c01664b24deda202acb5db8594db503ztenghui            }
2198b9de91b3c01664b24deda202acb5db8594db503ztenghui
22071e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui            muxer.stop();
22171e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui        } catch (IllegalStateException e) {
22271e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui            // Swallow the exception due to malformed source.
22371e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui            Log.w(LOGTAG, "The source video file is malformed");
22471e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui        } finally {
22571e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui            muxer.release();
22671e27598c6c7cbba1792fffceaf653946a3bd2e4ztenghui        }
2278b9de91b3c01664b24deda202acb5db8594db503ztenghui        return;
2288b9de91b3c01664b24deda202acb5db8594db503ztenghui    }
2298b9de91b3c01664b24deda202acb5db8594db503ztenghui
2308b9de91b3c01664b24deda202acb5db8594db503ztenghui    private static void trimUsingMp4Parser(File src, File dst, int startMs, int endMs)
2318b9de91b3c01664b24deda202acb5db8594db503ztenghui            throws FileNotFoundException, IOException {
23215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
23315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        Movie movie = MovieCreator.build(randomAccessFile.getChannel());
23415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
23515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        // remove all tracks we will create new tracks from the old
23615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        List<Track> tracks = movie.getTracks();
23715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        movie.setTracks(new LinkedList<Track>());
23815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
2398b9de91b3c01664b24deda202acb5db8594db503ztenghui        double startTime = startMs / 1000;
2408b9de91b3c01664b24deda202acb5db8594db503ztenghui        double endTime = endMs / 1000;
24115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
24215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        boolean timeCorrected = false;
24315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
2448b9de91b3c01664b24deda202acb5db8594db503ztenghui        // Here we try to find a track that has sync samples. Since we can only
2458b9de91b3c01664b24deda202acb5db8594db503ztenghui        // start decoding at such a sample we SHOULD make sure that the start of
2468b9de91b3c01664b24deda202acb5db8594db503ztenghui        // the new fragment is exactly such a frame.
24715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        for (Track track : tracks) {
24815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) {
24915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                if (timeCorrected) {
2508b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // This exception here could be a false positive in case we
2518b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // have multiple tracks with sync samples at exactly the
2528b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // same positions. E.g. a single movie containing multiple
2538b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // qualities of the same video (Microsoft Smooth Streaming
2548b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // file)
2558b9de91b3c01664b24deda202acb5db8594db503ztenghui                    throw new RuntimeException(
2568b9de91b3c01664b24deda202acb5db8594db503ztenghui                            "The startTime has already been corrected by" +
2578b9de91b3c01664b24deda202acb5db8594db503ztenghui                            " another track with SyncSample. Not Supported.");
25815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                }
25915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                startTime = correctTimeToSyncSample(track, startTime, false);
26015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                endTime = correctTimeToSyncSample(track, endTime, true);
26115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                timeCorrected = true;
26215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            }
26315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        }
26415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
26515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        for (Track track : tracks) {
26615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            long currentSample = 0;
26715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            double currentTime = 0;
26815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            long startSample = -1;
26915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            long endSample = -1;
27015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
27115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
27215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
27315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                for (int j = 0; j < entry.getCount(); j++) {
2748b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // entry.getDelta() is the amount of time the current sample
2758b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // covers.
27615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
27715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    if (currentTime <= startTime) {
27815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                        // current sample is still before the new starttime
27915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                        startSample = currentSample;
28015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    }
28115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    if (currentTime <= endTime) {
2828b9de91b3c01664b24deda202acb5db8594db503ztenghui                        // current sample is after the new start time and still
2838b9de91b3c01664b24deda202acb5db8594db503ztenghui                        // before the new endtime
28415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                        endSample = currentSample;
28515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    } else {
28615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                        // current sample is after the end of the cropped video
28715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                        break;
28815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    }
2898b9de91b3c01664b24deda202acb5db8594db503ztenghui                    currentTime += (double) entry.getDelta()
2908b9de91b3c01664b24deda202acb5db8594db503ztenghui                            / (double) track.getTrackMetaData().getTimescale();
29115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    currentSample++;
29215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                }
29315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            }
29415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            movie.addTrack(new CroppedTrack(track, startSample, endSample));
29515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        }
296648b339c74da2b863304ffc61c8528cc74c2afb3Teng-Hui Zhu        writeMovieIntoFile(dst, movie);
29715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        randomAccessFile.close();
29815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu    }
29915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
3008b9de91b3c01664b24deda202acb5db8594db503ztenghui    private static double correctTimeToSyncSample(Track track, double cutHere,
3018b9de91b3c01664b24deda202acb5db8594db503ztenghui            boolean next) {
30215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        double[] timeOfSyncSamples = new double[track.getSyncSamples().length];
30315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        long currentSample = 0;
30415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        double currentTime = 0;
30515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
30615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
30715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            for (int j = 0; j < entry.getCount(); j++) {
30815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                if (Arrays.binarySearch(track.getSyncSamples(), currentSample + 1) >= 0) {
3098b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // samples always start with 1 but we start with zero
3108b9de91b3c01664b24deda202acb5db8594db503ztenghui                    // therefore +1
3118b9de91b3c01664b24deda202acb5db8594db503ztenghui                    timeOfSyncSamples[Arrays.binarySearch(
3128b9de91b3c01664b24deda202acb5db8594db503ztenghui                            track.getSyncSamples(), currentSample + 1)] = currentTime;
31315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                }
3148b9de91b3c01664b24deda202acb5db8594db503ztenghui                currentTime += (double) entry.getDelta()
3158b9de91b3c01664b24deda202acb5db8594db503ztenghui                        / (double) track.getTrackMetaData().getTimescale();
31615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                currentSample++;
31715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            }
31815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        }
31915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        double previous = 0;
32015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        for (double timeOfSyncSample : timeOfSyncSamples) {
32115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            if (timeOfSyncSample > cutHere) {
32215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                if (next) {
32315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    return timeOfSyncSample;
32415ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                } else {
32515ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                    return previous;
32615ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu                }
32715ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            }
32815ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu            previous = timeOfSyncSample;
32915ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        }
33015ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu        return timeOfSyncSamples[timeOfSyncSamples.length - 1];
33115ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu    }
33215ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu
33315ff1b1ca52bea348b8a490b5b5abe53fa43eaf2Teng-Hui Zhu}
334