SyncSampleIntersectFinderImpl.java revision dd9eb897ee7c7b507cbdcf80263bb4b5de6966bf
100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy/* 200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * Copyright 2012 Sebastian Annies, Hamburg 300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * 400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * Licensed under the Apache License, Version 2.0 (the License); 500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * you may not use this file except in compliance with the License. 600524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * You may obtain a copy of the License at 700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * 881bd58838403fc8c4a63840f0af42deebe6d4a20Jan Engelhardt * http://www.apache.org/licenses/LICENSE-2.0 981bd58838403fc8c4a63840f0af42deebe6d4a20Jan Engelhardt * 1000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * Unless required by applicable law or agreed to in writing, software 1100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * distributed under the License is distributed on an AS IS BASIS, 1200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139921f2b9a241750e4730fc7d486687c6a32779f4Jan Engelhardt * See the License for the specific language governing permissions and 149921f2b9a241750e4730fc7d486687c6a32779f4Jan Engelhardt * limitations under the License. 159d69da4bdb1d546218d168b72f12ac8aa042e3d8Jan Engelhardt */ 169a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardtpackage com.googlecode.mp4parser.authoring.builder; 17ef18e8147903885708d1c264904129af4fb636d6Jan Engelhardt 1800524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardyimport com.coremedia.iso.boxes.TimeToSampleBox; 1900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardyimport com.coremedia.iso.boxes.sampleentry.AudioSampleEntry; 2000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardyimport com.googlecode.mp4parser.authoring.Movie; 21d62a9db1295608ef98394b830703389973346716Yasuyuki KOZAKAIimport com.googlecode.mp4parser.authoring.Track; 22d62a9db1295608ef98394b830703389973346716Yasuyuki KOZAKAI 2300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardyimport java.util.*; 2400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardyimport java.util.concurrent.ConcurrentHashMap; 2500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardyimport java.util.logging.Logger; 26abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 27abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphalimport static com.googlecode.mp4parser.util.Math.lcm; 28abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 29abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal/** 3000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * This <code>FragmentIntersectionFinder</code> cuts the input movie video tracks in 3100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * fragments of the same length exactly before the sync samples. Audio tracks are cut 3200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * into pieces of similar length. 3300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy */ 3468146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardtpublic class SyncSampleIntersectFinderImpl implements FragmentIntersectionFinder { 3568146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt 3668146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt private static Logger LOG = Logger.getLogger(SyncSampleIntersectFinderImpl.class.getName()); 3768146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt private static Map<CacheTuple, long[]> getTimesCache = new ConcurrentHashMap<CacheTuple, long[]>(); 38181dead3f13befe02769ef479bcbb51801b7fc4eJan Engelhardt private static Map<CacheTuple, long[]> getSampleNumbersCache = new ConcurrentHashMap<CacheTuple, long[]>(); 3900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 4000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy private final int minFragmentDurationSeconds; 418b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt 4200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy public SyncSampleIntersectFinderImpl() { 4300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy minFragmentDurationSeconds = 0; 4400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 4500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 4600524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy /** 4700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * Creates a <code>SyncSampleIntersectFinderImpl</code> that will not create any fragment 4800524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * smaller than the given <code>minFragmentDurationSeconds</code> 4900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * 5000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy * @param minFragmentDurationSeconds the smallest allowable duration of a fragment. 5100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy */ 528b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt public SyncSampleIntersectFinderImpl(int minFragmentDurationSeconds) { 538b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt this.minFragmentDurationSeconds = minFragmentDurationSeconds; 5400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 5500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 56fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt /** 57fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt * Gets an array of sample numbers that are meant to be the first sample of each 58fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt * chunk or fragment. 59fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt * 60fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt * @param track concerned track 61fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt * @param movie the context of the track 62fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt * @return an array containing the ordinal of each fragment's first sample 63fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt */ 64fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt public long[] sampleNumbers(Track track, Movie movie) { 65fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt final CacheTuple key = new CacheTuple(track, movie); 66fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt final long[] result = getSampleNumbersCache.get(key); 67fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt if (result != null) { 68fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt return result; 69abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 7068146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt 7168146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt if ("vide".equals(track.getHandler())) { 7268146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt if (track.getSyncSamples() != null && track.getSyncSamples().length > 0) { 73fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt List<long[]> times = getSyncSamplesTimestamps(movie, track); 74fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt final long[] commonIndices = getCommonIndices(track.getSyncSamples(), getTimes(track, movie), track.getTrackMetaData().getTimescale(), times.toArray(new long[times.size()][])); 759a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt getSampleNumbersCache.put(key, commonIndices); 769a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt return commonIndices; 779a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } else { 789a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt throw new RuntimeException("Video Tracks need sync samples. Only tracks other than video may have no sync samples."); 799a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 809a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } else if ("soun".equals(track.getHandler())) { 819a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt Track referenceTrack = null; 829a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt for (Track candidate : movie.getTracks()) { 839a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt if (candidate.getSyncSamples() != null && "vide".equals(candidate.getHandler()) && candidate.getSyncSamples().length > 0) { 849a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt referenceTrack = candidate; 859a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 869a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 879a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt if (referenceTrack != null) { 889a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt 899a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt // Gets the reference track's fra 909a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt long[] refSyncSamples = sampleNumbers(referenceTrack, movie); 919a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt 929a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt int refSampleCount = referenceTrack.getSamples().size(); 939a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt 949a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt long[] syncSamples = new long[refSyncSamples.length]; 959a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt long minSampleRate = 192000; 96fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt for (Track testTrack : movie.getTracks()) { 97fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt if ("soun".equals(testTrack.getHandler())) { 98fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt AudioSampleEntry ase = (AudioSampleEntry) testTrack.getSampleDescriptionBox().getSampleEntry(); 99de1f06dca906bfcb82d7c7c2d555fbf3229d12b6Jan Engelhardt if (ase.getSampleRate() < minSampleRate) { 100fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt minSampleRate = ase.getSampleRate(); 101abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal long sc = testTrack.getSamples().size(); 102fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt double stretch = (double) sc / refSampleCount; 103fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt TimeToSampleBox.Entry sttsEntry = testTrack.getDecodingTimeEntries().get(0); 104fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types 105fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt 106fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt for (int i = 0; i < syncSamples.length; i++) { 107fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1) * samplesPerFrame); 108fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt syncSamples[i] = start; 109fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt // The Stretch makes sure that there are as much audio and video chunks! 110fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 111fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt break; 112fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 113fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 114fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 115fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt AudioSampleEntry ase = (AudioSampleEntry) track.getSampleDescriptionBox().getSampleEntry(); 116fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt TimeToSampleBox.Entry sttsEntry = track.getDecodingTimeEntries().get(0); 117fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt long samplesPerFrame = sttsEntry.getDelta(); // Assuming all audio tracks have the same number of samples per frame, which they do for all known types 118fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt double factor = (double) ase.getSampleRate() / (double) minSampleRate; 119fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt if (factor != Math.rint(factor)) { // Not an integer 12000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy throw new RuntimeException("Sample rates must be a multiple of the lowest sample rate to create a correct file!"); 121fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 122fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt for (int i = 0; i < syncSamples.length; i++) { 123fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt syncSamples[i] = (long) (1 + syncSamples[i] * factor / (double) samplesPerFrame); 124fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 125fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt getSampleNumbersCache.put(key, syncSamples); 126fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt return syncSamples; 127fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 128fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!"); 129fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } else { 130fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt // Ok, my track has no sync samples - let's find one with sync samples. 131fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt for (Track candidate : movie.getTracks()) { 132fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt if (candidate.getSyncSamples() != null && candidate.getSyncSamples().length > 0) { 133abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal long[] refSyncSamples = sampleNumbers(candidate, movie); 134fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt int refSampleCount = candidate.getSamples().size(); 135fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt 136fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt long[] syncSamples = new long[refSyncSamples.length]; 137fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt long sc = track.getSamples().size(); 138fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt double stretch = (double) sc / refSampleCount; 139fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt 140fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt for (int i = 0; i < syncSamples.length; i++) { 141fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt long start = (long) Math.ceil(stretch * (refSyncSamples[i] - 1)) + 1; 142fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt syncSamples[i] = start; 143fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt // The Stretch makes sure that there are as much audio and video chunks! 144fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 145fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt getSampleNumbersCache.put(key, syncSamples); 146fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt return syncSamples; 147fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 148fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 149fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt throw new RuntimeException("There was absolutely no Track with sync samples. I can't work with that!"); 1509a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 151fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt 1529a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt 153abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 154abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 155abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal /** 156abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal * Calculates the timestamp of all tracks' sync samples. 157abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal * 158abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal * @param movie 159abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal * @param track 160abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal * @return 161abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal */ 162abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal public static List<long[]> getSyncSamplesTimestamps(Movie movie, Track track) { 163abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal List<long[]> times = new LinkedList<long[]>(); 164abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal for (Track currentTrack : movie.getTracks()) { 165abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal if (currentTrack.getHandler().equals(track.getHandler())) { 166abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal long[] currentTrackSyncSamples = currentTrack.getSyncSamples(); 167abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal if (currentTrackSyncSamples != null && currentTrackSyncSamples.length > 0) { 168abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal final long[] currentTrackTimes = getTimes(currentTrack, movie); 169abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal times.add(currentTrackTimes); 170abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 171abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 172abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 173abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal return times; 174abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 175abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 176abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal public long[] getCommonIndices(long[] syncSamples, long[] syncSampleTimes, long timeScale, long[]... otherTracksTimes) { 177abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal List<Long> nuSyncSamples = new LinkedList<Long>(); 178abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal List<Long> nuSyncSampleTimes = new LinkedList<Long>(); 179abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 180abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 181abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal for (int i = 0; i < syncSampleTimes.length; i++) { 182abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal boolean foundInEveryRef = true; 183abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal for (long[] times : otherTracksTimes) { 184abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal foundInEveryRef &= (Arrays.binarySearch(times, syncSampleTimes[i]) >= 0); 185abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 186abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 187abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal if (foundInEveryRef) { 188abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal // use sample only if found in every other track. 189abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal nuSyncSamples.add(syncSamples[i]); 190abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal nuSyncSampleTimes.add(syncSampleTimes[i]); 191abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 192abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 193abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal // We have two arrays now: 194abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal // nuSyncSamples: Contains all common sync samples 195abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal // nuSyncSampleTimes: Contains the times of all sync samples 196abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 197abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal // Start: Warn user if samples are not matching! 198abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal if (nuSyncSamples.size() < (syncSamples.length * 0.25)) { 199abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal String log = ""; 200abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal log += String.format("%5d - Common: [", nuSyncSamples.size()); 201abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal for (long l : nuSyncSamples) { 202abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal log += (String.format("%10d,", l)); 203abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 204abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal log += ("]"); 205abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal LOG.warning(log); 206abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal log = ""; 207abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 208abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal log += String.format("%5d - In : [", syncSamples.length); 209abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal for (long l : syncSamples) { 210abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal log += (String.format("%10d,", l)); 211abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 212abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal log += ("]"); 213abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal LOG.warning(log); 214abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal LOG.warning("There are less than 25% of common sync samples in the given track."); 215abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal throw new RuntimeException("There are less than 25% of common sync samples in the given track."); 216abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } else if (nuSyncSamples.size() < (syncSamples.length * 0.5)) { 217abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal LOG.fine("There are less than 50% of common sync samples in the given track. This is implausible but I'm ok to continue."); 218abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } else if (nuSyncSamples.size() < syncSamples.length) { 219abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal LOG.finest("Common SyncSample positions vs. this tracks SyncSample positions: " + nuSyncSamples.size() + " vs. " + syncSamples.length); 220abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal } 221abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal // End: Warn user if samples are not matching! 222abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 223abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 224abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 225abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 226abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal List<Long> finalSampleList = new LinkedList<Long>(); 227abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal 228abdef13f36b63758f8775eb86febd96bf062df6fFlorian Westphal if (minFragmentDurationSeconds > 0) { 22900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy // if minFragmentDurationSeconds is greater 0 23068146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt // we need to throw away certain samples. 23100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy long lastSyncSampleTime = -1; 23200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy Iterator<Long> nuSyncSamplesIterator = nuSyncSamples.iterator(); 2337ac405297ec38449b30e3b05fd6bf2082fd3d803Jan Engelhardt Iterator<Long> nuSyncSampleTimesIterator = nuSyncSampleTimes.iterator(); 23400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy while (nuSyncSamplesIterator.hasNext() && nuSyncSampleTimesIterator.hasNext()) { 23568146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt long curSyncSample = nuSyncSamplesIterator.next(); 23600524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy long curSyncSampleTime = nuSyncSampleTimesIterator.next(); 23700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy if (lastSyncSampleTime == -1 || (curSyncSampleTime - lastSyncSampleTime) / timeScale >= minFragmentDurationSeconds) { 23800524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy finalSampleList.add(curSyncSample); 23900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy lastSyncSampleTime = curSyncSampleTime; 24000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 24100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 24268146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt } else { 24300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy // the list of all samples is the final list of samples 24468146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt // since minFragmentDurationSeconds ist not used. 24500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy finalSampleList = nuSyncSamples; 24668146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt } 24700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 24868146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt 24900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy // transform the list to an array 25000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy long[] finalSampleArray = new long[finalSampleList.size()]; 25100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy for (int i = 0; i < finalSampleArray.length; i++) { 25200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy finalSampleArray[i] = finalSampleList.get(i); 25300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 25400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy return finalSampleArray; 25500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 25668146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt } 2579d69da4bdb1d546218d168b72f12ac8aa042e3d8Jan Engelhardt 2589d69da4bdb1d546218d168b72f12ac8aa042e3d8Jan Engelhardt 2599d69da4bdb1d546218d168b72f12ac8aa042e3d8Jan Engelhardt private static long[] getTimes(Track track, Movie m) { 2609d69da4bdb1d546218d168b72f12ac8aa042e3d8Jan Engelhardt final CacheTuple key = new CacheTuple(track, m); 2619d69da4bdb1d546218d168b72f12ac8aa042e3d8Jan Engelhardt final long[] result = getTimesCache.get(key); 2629d69da4bdb1d546218d168b72f12ac8aa042e3d8Jan Engelhardt if (result != null) { 26300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy return result; 26400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 26500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 266181dead3f13befe02769ef479bcbb51801b7fc4eJan Engelhardt long[] syncSamples = track.getSyncSamples(); 26700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy long[] syncSampleTimes = new long[syncSamples.length]; 26800524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy Queue<TimeToSampleBox.Entry> timeQueue = new LinkedList<TimeToSampleBox.Entry>(track.getDecodingTimeEntries()); 26900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 27000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy int currentSample = 1; // first syncsample is 1 27100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy long currentDuration = 0; 27200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy long currentDelta = 0; 27300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy int currentSyncSampleIndex = 0; 27400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy long left = 0; 2759a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt 2769a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt final long scalingFactor = calculateTracktimesScalingFactor(m, track); 2779a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt 2789a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt while (currentSample <= syncSamples[syncSamples.length - 1]) { 2799a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt if (currentSample++ == syncSamples[currentSyncSampleIndex]) { 2809a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt syncSampleTimes[currentSyncSampleIndex++] = currentDuration * scalingFactor; 2819a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 2829a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt if (left-- == 0) { 2839a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt TimeToSampleBox.Entry entry = timeQueue.poll(); 2849a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt left = entry.getCount() - 1; 2859a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt currentDelta = entry.getDelta(); 2869a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 2879a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt currentDuration += currentDelta; 2889a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 2899a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt getTimesCache.put(key, syncSampleTimes); 2909a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt return syncSampleTimes; 2919a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 2929a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt 2939a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt private static long calculateTracktimesScalingFactor(Movie m, Track track) { 2949a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt long timeScale = 1; 2959a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt for (Track track1 : m.getTracks()) { 29600524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy if (track1.getHandler().equals(track.getHandler())) { 29700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy if (track1.getTrackMetaData().getTimescale() != track.getTrackMetaData().getTimescale()) { 298fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt timeScale = lcm(timeScale, track1.getTrackMetaData().getTimescale()); 29900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 30000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 301dbb77543ad6afe29e9a1881b2d4fc212de621a55Jan Engelhardt } 30200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy return timeScale; 30300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 30400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 30500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy public static class CacheTuple { 30600524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy Track track; 30700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy Movie movie; 30800524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 30900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy public CacheTuple(Track track, Movie movie) { 3109a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt this.track = track; 31100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy this.movie = movie; 3129a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt } 31300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 3149a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt @Override 31500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy public boolean equals(Object o) { 3169a8c77fc8df3155747c34dcea79b7834a2a9a40aJan Engelhardt if (this == o) return true; 31700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy if (o == null || getClass() != o.getClass()) return false; 31800524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 31900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy CacheTuple that = (CacheTuple) o; 32000524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 32100524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy if (movie != null ? !movie.equals(that.movie) : that.movie != null) return false; 32200524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy if (track != null ? !track.equals(that.track) : that.track != null) return false; 32300524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy 32400524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy return true; 32500524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy } 326fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt 32700524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy @Override 328fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt public int hashCode() { 32900524b27b5e442d27414cf48e0d6e6372b6113aePatrick McHardy int result = track != null ? track.hashCode() : 0; 330fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt result = 31 * result + (movie != null ? movie.hashCode() : 0); 331fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt return result; 332fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt } 33368146dad91611bd8d6d12c8ba27219130d99607bJan Engelhardt } 334fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt} 335fe02f76e013941a7f65f57f297d3177bcfeb0623Jan Engelhardt