使用mp4parser Library从电影中剪切多个剪辑时出现的问题

时间:2013-03-27 10:05:21

标签: android mp4parser

我正在使用mp4parser Library从剪辑的视频中剪切多个剪辑。它正常工作如果我从视频中剪切了一部分。但是当我尝试从视频中剪切多个剪辑时,只有第一个剪辑被正确剪切。其他只有0或1秒。 以下是我的代码:

import android.app.ProgressDialog;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.TimeToSampleBox;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.CroppedTrack;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import uk.org.humanfocus.hfi.Beans.TrimPoint;
import uk.org.humanfocus.hfi.Utils.Constants;
import uk.org.humanfocus.hfi.Utils.SimpleThreadFactory;
import uk.org.humanfocus.hfi.Utils.Ut;

/**
 * Shortens/Crops a track
 */
public class ShortenExample {

    private static final String TAG = "ShortenExample";
    private final Context mCxt;
    private ExecutorService mThreadExecutor = null;
    private SimpleInvalidationHandler mHandler;
    private ProgressDialog mProgressDialog;
    String filePath;
    ArrayList<TrimPoint> mTrimPoints;
    int videoLength;
    ArrayList<String> trimVideos;
    private class SimpleInvalidationHandler extends Handler {

        @Override
        public void handleMessage(final Message msg) {
            switch (msg.what) {
            case R.id.shorten:
                mProgressDialog.dismiss();

                if (msg.arg1 == 0)
                    Toast.makeText(mCxt,
                            mCxt.getString(R.string.message_error) + " " + (String) msg.obj,
                            Toast.LENGTH_LONG).show();
                else
                    Toast.makeText(mCxt,
                            mCxt.getString(R.string.message_shortened) + " " + (String) msg.obj,
                            Toast.LENGTH_LONG).show();
                break;
            }
        }
    }

    public ShortenExample(Context context) {
        mCxt = context;
        mHandler = new SimpleInvalidationHandler();
        //mProgressDialog = new ProgressDialog(mCxt);
        //mProgressDialog.setMessage("Wait Saving..");
        //mProgressDialog.setCancelable(false);
    }

    public void shorten(String filePath,ArrayList<TrimPoint> trimPoints, int endTime) {
        trimVideos = new ArrayList<String>();
        this.filePath = filePath;
        this.videoLength = endTime;
        this.mTrimPoints = trimPoints;
        Log.d(Constants.TAG,"End Time: "+endTime+" Trim Points: "+mTrimPoints.size());
        for (int i=0;i<trimPoints.size();i++){
            TrimPoint point = trimPoints.get(i);
            int start=0;
            int end = 0;
            if(point.getTime()-5<0){
                start = 0;
            }else{
                start = point.getTime()-5;
            }

            if(point.getTime()+5>videoLength){
                end = videoLength-1;
            }else {
                end = point.getTime() + 5;
            }
            Log.d(Constants.TAG,"Clip: "+start+" : "+end);
            doShorten(start,end);   
        }
        Log.d(Constants.TAG,"Done: "+trimVideos.size());
    }

    private void doShorten(final int _startTime, final int _endTime) {
        //mProgressDialog = Ut.ShowWaitDialog(mCxt, 0);

        //mProgressDialog.show();


        if(mThreadExecutor == null)
            mThreadExecutor = Executors.newSingleThreadExecutor(new SimpleThreadFactory("doShorten"));

        //this.mThreadExecutor.execute(new Runnable() {
        //  public void run() {
                try {
                    File folder = Ut.getTestMp4ParserVideosDir(mCxt);
                    //File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),"HFVideos"+File.separator+"TEMP");
                    //Log.d(Constants.TAG, folder.toString());
                    if (!folder.exists()) {
                        Log.d(TAG, "failed to create directory");
                    }

                    //Movie movie = new MovieCreator().build(new RandomAccessFile("/home/sannies/suckerpunch-distantplanet_h1080p/suckerpunch-distantplanet_h1080p.mov", "r").getChannel());
//                  Movie movie = MovieCreator.build(new FileInputStream("/home/sannies/CSI.S13E02.HDTV.x264-LOL.mp4").getChannel());
                    Movie movie = MovieCreator.build(new FileInputStream(new File(filePath)).getChannel());
                    //Log.d(Constants.TAG,"Movie: "+movie.toString());
                    List<Track> tracks = movie.getTracks();
                    movie.setTracks(new LinkedList<Track>());
                    // remove all tracks we will create new tracks from the old

                    double startTime = _startTime;
                    double endTime = _endTime;//(double) getDuration(tracks.get(0)) / tracks.get(0).getTrackMetaData().getTimescale();

                    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.");
                            }
                            startTime = correctTimeToSyncSample(track, startTime, false);
                            endTime = correctTimeToSyncSample(track, endTime, true);
                            timeCorrected = true;
                        }
                    }

                    for (Track track : tracks) {
                        long currentSample = 0;
                        double currentTime = 0;
                        long startSample = -1;
                        long endSample = -1;

                        for (int i = 0; i < track.getDecodingTimeEntries().size(); i++) {
                            TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
                            for (int j = 0; j < entry.getCount(); j++) {
                                // entry.getDelta() is the amount of time the current sample covers.

                                if (currentTime <= startTime) {
                                    // current sample is still before the new starttime
                                    startSample = currentSample;
                                }
                                if (currentTime <= endTime) {
                                    // current sample is after the new start time and still before the new endtime
                                    endSample = currentSample;
                                } else {
                                    // current sample is after the end of the cropped video
                                    break;
                                }
                                currentTime += (double) entry.getDelta() / (double) track.getTrackMetaData().getTimescale();
                                currentSample++;
                            }
                        }
                        movie.addTrack(new CroppedTrack(track, startSample, endSample));
                    }
                    long start1 = System.currentTimeMillis();
                    IsoFile out = new DefaultMp4Builder().build(movie);
                    long start2 = System.currentTimeMillis();

//                  FileOutputStream fos = new FileOutputStream(String.format("output-%f-%f.mp4", startTime, endTime));
                    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                    String filename = folder.getPath() + File.separator + String.format("TMP4_APP_OUT-%f-%f", startTime, endTime) + "_" + timeStamp + ".mp4";
                    trimVideos.add(filename);
                    FileOutputStream fos = new FileOutputStream(filename);

                    FileChannel fc = fos.getChannel();
                    out.getBox(fc);
                    fc.close();
                    fos.close();
                    long start3 = System.currentTimeMillis();
                    System.err.println("Building IsoFile took : " + (start2 - start1) + "ms");
                    System.err.println("Writing IsoFile took  : " + (start3 - start2) + "ms");
                    System.err.println("Writing IsoFile speed : " + (new File(String.format("TMP4_APP_OUT-%f-%f", startTime, endTime)).length() / (start3 - start2) / 1000) + "MB/s");

                    Message.obtain(mHandler, R.id.shorten, 1, 0, filename).sendToTarget();
                } catch (FileNotFoundException e) {
                    Message.obtain(mHandler, R.id.shorten, 0, 0, e.getMessage()).sendToTarget();
                    e.printStackTrace();
                } catch (IOException e) {
                    Message.obtain(mHandler, R.id.shorten, 0, 0, e.getMessage()).sendToTarget();
                    e.printStackTrace();
                }
                //mProgressDialog.dismiss();
        //  }
        //});

    }

    protected static long getDuration(Track track) {
        long duration = 0;
        for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
            duration += entry.getCount() * entry.getDelta();
        }
        return duration;
    }

    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.getDecodingTimeEntries().size(); i++) {
            TimeToSampleBox.Entry entry = track.getDecodingTimeEntries().get(i);
            for (int j = 0; j < entry.getCount(); j++) {
                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) entry.getDelta() / (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];
    }


}

2 个答案:

答案 0 :(得分:3)

问题仅出现在模拟器中。当我们在模拟器中录制视频时,它会记录实际时间的秒数。代码在实际设备上工作正常。

答案 1 :(得分:0)

BTW,持续时间以秒为单位,您需要除以时间刻度。我发现相同持续时间的音频和视频轨道之间的条目(或样本)计数不同。但我是一个完全的菜鸟,不要听我的。

protected static double getDuration(Track track) {
    double duration = 0;
    for (TimeToSampleBox.Entry entry : track.getDecodingTimeEntries()) {
        duration += entry.getCount() * entry.getDelta() / (double) track.getTrackMetaData().getTimescale();
    }
    return duration;
}