private void parse(InputStream fin) throws IOException { inputStream = fin; bitRateAndFrameSizeTable = new int[19][2][3][2]; stts = new LinkedList<TimeToSampleBox.Entry>(); initBitRateAndFrameSizeTable(); if (!readVariables()) { throw new IOException(); } sampleDescriptionBox = new SampleDescriptionBox(); AudioSampleEntry audioSampleEntry = new AudioSampleEntry("ac-3"); audioSampleEntry.setChannelCount(2); // According to ETSI TS 102 366 Annex F audioSampleEntry.setSampleRate(samplerate); audioSampleEntry.setDataReferenceIndex(1); audioSampleEntry.setSampleSize(16); AC3SpecificBox ac3 = new AC3SpecificBox(); ac3.setAcmod(acmod); ac3.setBitRateCode(frmsizecod >> 1); ac3.setBsid(bsid); ac3.setBsmod(bsmod); ac3.setFscod(fscod); ac3.setLfeon(lfeon); ac3.setReserved(0); audioSampleEntry.addBox(ac3); sampleDescriptionBox.addBox(audioSampleEntry); trackMetaData.setCreationTime(new Date()); trackMetaData.setModificationTime(new Date()); trackMetaData.setLanguage(lang); trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale samples = new LinkedList<ByteBuffer>(); if (!readSamples()) { throw new IOException(); } }
/** * Gets an array of sample numbers that are meant to be the first sample of each chunk or * fragment. * * @param track concerned track * @param movie the context of the track * @return an array containing the ordinal of each fragment's first sample */ public long[] sampleNumbers(Track track, Movie movie) { if ("vide".equals(track.getHandler())) { if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) { List<long[]> times = getSyncSamplesTimestamps(movie, track); return getCommonIndices( track.getSyncSamples(), getTimes(movie, track), times.toArray(new long[times.size()][])); } else { throw new RuntimeException( "Video Tracks need sync samples. Only tracks other than video may have no sync samples."); } } else if ("soun".equals(track.getHandler())) { Track referenceTrack = null; for (Track candidate : movie.getTracks()) { if (candidate.getSyncSamples() != null && "vide".equals(candidate.getHandler()) && candidate.getSyncSamples().length > 0) { referenceTrack = candidate; } } if (referenceTrack != null) { // Gets the reference track's fra long[] refSyncSamples = sampleNumbers(referenceTrack, movie); int refSampleCount = referenceTrack.getSamples().size(); long[] syncSamples = new long[refSyncSamples.length]; long minSampleRate = 192000; for (Track testTrack : movie.getTracks()) { if ("soun".equals(testTrack.getHandler())) { AudioSampleEntry ase = (AudioSampleEntry) testTrack.getSampleDescriptionBox().getSampleEntry(); if (ase.getSampleRate() < minSampleRate) { minSampleRate = ase.getSampleRate(); long sc = testTrack.getSamples().size(); double stretch = (double) sc / refSampleCount; for (int i = 0; i < syncSamples.length; i++) { int start = (int) Math.ceil(stretch * (refSyncSamples[i] - 1)) + 1; syncSamples[i] = start; // The Stretch makes sure that there are as much audio and video chunks! } } } } AudioSampleEntry ase = (AudioSampleEntry) track.getSampleDescriptionBox().getSampleEntry(); double factor = (double) ase.getSampleRate() / (double) minSampleRate; if (factor != Math.rint(factor)) { // Not an integer throw new RuntimeException( "Sample rates must be a multiple of the lowest sample rate to create a correct file!"); } for (int i = 1; i < syncSamples.length; i++) { syncSamples[i] = (int) (1 + (syncSamples[i] - 1) * factor); } return syncSamples; } throw new RuntimeException( "There was absolutely no Track with sync samples. I can't work with that!"); } else { // Ok, my track has no sync samples - let's find one with sync samples. for (Track candidate : movie.getTracks()) { if (candidate.getSyncSamples() != null && candidate.getSyncSamples().length > 0) { long[] refSyncSamples = sampleNumbers(candidate, movie); int refSampleCount = candidate.getSamples().size(); long[] syncSamples = new long[refSyncSamples.length]; long sc = track.getSamples().size(); double stretch = (double) sc / refSampleCount; for (int i = 0; i < syncSamples.length; i++) { int start = (int) Math.ceil(stretch * (refSyncSamples[i] - 1)) + 1; syncSamples[i] = start; // The Stretch makes sure that there are as much audio and video chunks! } return syncSamples; } } throw new RuntimeException( "There was absolutely no Track with sync samples. I can't work with that!"); } }