2014-09-15 3 views
5

Pracuję nad aplikacją, która łączy klipy mp4 przy użyciu biblioteki mp4parser (isoparser-1.0-RC-27.jar i aspectjrt-1.8.0.jar). Kiedy dwa klipy są scalane, stają się pojedynczym klipem, ale wraz z dodawaniem do niego kolejnych klipów, wyjście mp4 ma dźwięk za wideo.Scalanie klipów MP4 z mp4parser sprawia, że ​​audio kryje się za wideo

Oto kod:

Movie[] clips = new Movie[2]; 

    //location of the movie clip storage 
    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
      Environment.DIRECTORY_PICTURES), "TestMerge"); 

    //Build the two clips into movies 
    Movie firstClip = MovieCreator.build(first); 
    Movie secondClip = MovieCreator.build(second); 

    //Add both movie clips 
    clips[0] = firstClip; 
    clips[1] = secondClip; 

    //List for audio and video tracks 
    List<Track> videoTracks = new LinkedList<Track>(); 
    List<Track> audioTracks = new LinkedList<Track>(); 

    //Iterate all the movie clips and find the audio and videos 
    for (Movie movie: clips) { 
     for (Track track : movie.getTracks()) { 
      if (track.getHandler().equals("soun")) 
       audioTracks.add(track);     
      if (track.getHandler().equals("vide")) 
       videoTracks.add(track); 
     } 
    } 

    //Result movie from putting the audio and video together from the two clips 
    Movie result = new Movie(); 

    //Append all audio and video 
    if (videoTracks.size() > 0) 
     result.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()]))); 

    if (audioTracks.size() > 0) 
     result.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()]))); 

    //Output the resulting movie to a new mp4 file 
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 
    String outputLocation = mediaStorageDir.getPath()+timeStamp; 
    Container out = new DefaultMp4Builder().build(result); 
    FileChannel fc = new RandomAccessFile(String.format(outputLocation), "rw").getChannel(); 
    out.writeContainer(fc); 
    fc.close(); 

    //Now set the active URL to play as the combined videos! 
    setURL(outputLocation); 
} 

Domyślam się, że im więcej klipów są dodawane, synchronizacja wideo do audio jest pomieszane, ponieważ jeśli dwie dłuższe klipy są połączone wówczas audio/wideo jest w porządku. Czy jest jednak tak, aby zapobiec tej słabej synchronizacji wideo i audio w wielu mniejszych klipach, czy ktoś znalazł rozwiązanie tego przy użyciu mp4parser ?? FFMpeg to kolejne rozwiązanie, które rozważam, ale nie znalazłem nikogo innego, kto użyłby go do wykonania tej czynności.

EDYCJA: Odkryłem, że dźwięk jest zwykle dłuższy niż wideo, dlatego właśnie to powoduje powstanie końcowego wideo tak bardzo się skompensować, gdy coraz więcej klipów jest dodawanych, aby utworzyć jeden klip. Mam zamiar rozwiązać przez odcięcie próbek audio

Odpowiedz

0

Udało mi się naprawić ten problem za pomocą techniki z powyższą zmianą. Sztuką jest śledzenie, ile klipów jest łączonych, i usuwanie próbek z końca ścieżki dźwiękowej ostatnio dodanego klipu. Jako że wynikowe wyjście MP4 rośnie wraz z większą ilością klipów, musisz coraz więcej odrywać. Częściowo wynika to z różnicy w taktowaniu ścieżek audio i wideo, ponieważ ścieżka audio może wynosić 1020ms, a wideo 1000ms, a 5 dodanych klipów będzie miało przesunięcie wynoszące około 100ms dla długości audio i wideo, więc musisz to zrekompensować.

+0

Należy dodać kod do przycinania ścieżek audio dla innych osób, które znajdują swoje rozwiązanie. Dzięki! – Episodex

+0

Napisałem to jakiś czas temu, pozwól mi wykopać moje rozwiązanie –

+0

Dzięki za zainteresowanie :). Dowiedziałem się, jak technicznie przyciąć dźwięk, ale wciąż mam problem z wyborem odpowiedniej liczby próbek do usunięcia. Zastanawiam się, czy znalazłeś rozwiązanie tego problemu. – Episodex

1

tylko wprowadzenie kodu do odpowiedzi Lucasa powyżej:

1.

LinkedList<Track> videoTracks = new LinkedList<>(); 
      LinkedList<Track> audioTracks = new LinkedList<>(); 
      double[] audioDuration = {0}, videoDuration = {0}; 
      for (Movie m : clips) { 
       for (Track t : m.getTracks()) { 
        if (t.getHandler().equals("soun")) { 
         for (long a : t.getSampleDurations()) audioDuration[0] += ((double) a)/t.getTrackMetaData().getTimescale(); 
         audioTracks.add(t); 
        } else if (t.getHandler().equals("vide")) { 
         for (long v : t.getSampleDurations()) videoDuration[0] += ((double) v)/t.getTrackMetaData().getTimescale(); 
         videoTracks.add(t); 
        } 
       } 

       adjustDurations(videoTracks, audioTracks, videoDuration, audioDuration); 
      } 

2.

private void adjustDurations(LinkedList<Track> videoTracks, LinkedList<Track> audioTracks, double[] videoDuration, double[] audioDuration) { 
    double diff = audioDuration[0] - videoDuration[0]; 

    //nothing to do 
    if (diff == 0) { 
     return; 
    } 

    //audio is longer 
    LinkedList<Track> tracks = audioTracks; 

    //video is longer 
    if (diff < 0) { 
     tracks = videoTracks; 
     diff *= -1; 
    } 

    Track track = tracks.getLast(); 
    long[] sampleDurations = track.getSampleDurations(); 
    long counter = 0; 
    for (int i = sampleDurations.length - 1; i > -1; i--) { 
     if (((double) (sampleDurations[i])/track.getTrackMetaData().getTimescale()) > diff) { 
      break; 
     } 
     diff -= ((double) (sampleDurations[i])/track.getTrackMetaData().getTimescale()); 
     audioDuration[0] -= ((double) (sampleDurations[i])/track.getTrackMetaData().getTimescale()); 
     counter++; 
    } 

    if (counter == 0) { 
     return; 
    } 

    track = new CroppedTrack(track, 0, track.getSamples().size() - counter); 

    //update the original reference 
    tracks.removeLast(); 
    tracks.addLast(track); 
} 
+0

Tylko jedna sugestia, przy obliczaniu czasu trwania próbki, powinna być podzielona według skali czasu ścieżki, aby uzyskać faktyczny czas trwania. –