Created
May 21, 2020 10:36
-
-
Save buntupana/554059a40e23a72d73fdabbd369d5032 to your computer and use it in GitHub Desktop.
Functions to append mp4 files with no out of sync problem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Throws(Exception::class) | |
fun appendVideos(videoPathList: List<String>, targetFilePath: String) { | |
val movies = videoPathList.flatMap { file -> listOf(MovieCreator.build(file)) } | |
val finalMovie = Movie() | |
val videoTracksTotal = mutableListOf<Track>() | |
val audioTracksTotal = mutableListOf<Track>() | |
var audioDuration = 0.0 | |
var videoDuration = 0.0 | |
movies.forEach { movie -> | |
val videoTracks = mutableListOf<Track>() | |
val audioTracks = mutableListOf<Track>() | |
movie.tracks.forEach { track -> | |
val trackDuration = track.sampleDurations.toList() | |
.map { t -> t.toDouble() / track.trackMetaData.timescale }.sum() | |
if (track.handler == "vide") { | |
videoDuration += trackDuration | |
videoTracks.add(track) | |
} else if (track.handler == "soun") { | |
audioDuration += trackDuration | |
audioTracks.add(track) | |
} | |
} | |
// Adjusting Durations | |
adjustDurations(videoTracks, audioTracks, videoDuration, audioDuration).let { | |
audioDuration = it.audioDuration | |
videoDuration = it.videoDuration | |
} | |
videoTracksTotal.addAll(videoTracks) | |
audioTracksTotal.addAll(audioTracks) | |
} | |
if (videoTracksTotal.isNotEmpty() && audioTracksTotal.isNotEmpty()) { | |
finalMovie.addTrack(AppendTrack(*videoTracksTotal.toTypedArray())) | |
finalMovie.addTrack(AppendTrack(*audioTracksTotal.toTypedArray())) | |
} | |
val container = DefaultMp4Builder().build(finalMovie) | |
val fos = FileOutputStream(targetFilePath) | |
val bb = Channels.newChannel(fos) | |
container.writeContainer(bb) | |
fos.close() | |
} | |
class Durations(val audioDuration: Double, val videoDuration: Double) | |
private fun adjustDurations( | |
videoTracks: MutableList<Track>, | |
audioTracks: MutableList<Track>, | |
videoDuration: Double, | |
audioDuration: Double | |
): Durations { | |
var diff = audioDuration - videoDuration | |
val tracks: MutableList<Track> | |
var durationOperator: Double | |
val isAudioProblem: Boolean | |
when { | |
// audio and video match, no operations to perform | |
diff == 0.0 -> { | |
return Durations(audioDuration, videoDuration) | |
} | |
// audio tracks are longer than video | |
diff > 0 -> { | |
tracks = audioTracks | |
durationOperator = audioDuration | |
isAudioProblem = true | |
} | |
// video tracks are longer than audio | |
else -> { | |
tracks = videoTracks | |
durationOperator = videoDuration | |
diff *= -1.0 | |
isAudioProblem = false | |
} | |
} | |
// Getting the last track in order to operate with it | |
var track: Track = tracks.last() | |
var counter: Long = 0 | |
// Reversing SampleDuration list | |
track.sampleDurations.toList().asReversed().forEach { sampleDuration -> | |
// Calculating how much this track need to be re-adjusted | |
if (sampleDuration.toDouble() / track.trackMetaData.timescale > diff) { | |
return@forEach | |
} | |
diff -= sampleDuration.toDouble() / track.trackMetaData.timescale | |
durationOperator -= sampleDuration.toDouble() / track.trackMetaData.timescale | |
counter++ | |
} | |
if (counter != 0L) { | |
// Cropping track | |
track = CroppedTrack(track, 0, track.samples.size - counter) | |
//update the original reference | |
tracks.removeAt(tracks.lastIndex) | |
tracks.add(track) | |
} | |
// Returning durations | |
return if (isAudioProblem) { | |
Durations(durationOperator, videoDuration) | |
} else { | |
Durations(audioDuration, durationOperator) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment