'How to combine multiple mp4 videos into one video using jcodec in Java?
I have multiple mp4 files which are parts of a whole mp4 file. They have been just split up to smaller files.
I want to combine those files programmatically into one mp4 file using jcodec in Java. Currently, I'm using the following code to do this:
import org.jcodec.common.DemuxerTrack;
import org.jcodec.common.MuxerTrack;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Packet;
import org.jcodec.containers.mp4.Brand;
import org.jcodec.containers.mp4.demuxer.MP4Demuxer;
import org.jcodec.containers.mp4.muxer.MP4Muxer;
import java.io.File;
import java.util.List;
import static org.jcodec.common.io.NIOUtils.readableChannel;
import static org.jcodec.common.io.NIOUtils.writableChannel;
public class Main {
public static void main(String[] args) throws Exception {
File outputFile = new File("file_path_output.mp4");
outputFile.mkdirs();
outputFile.delete();
File[] inputFiles = new File[]{
new File("file_path_1.mp4"),
new File("file_path_2.mp4"),
new File("file_path_3.mp4"),
};
SeekableByteChannel output = writableChannel(outputFile);
MP4Muxer mp4Muxer = MP4Muxer.createMP4Muxer(output, Brand.MP4);
MuxerTrack mixedVideoTrack = null;
MuxerTrack mixedAudioTrack = null;
for (File input : inputFiles) {
SeekableByteChannel byteChannel = readableChannel(input);
MP4Demuxer demuxer = MP4Demuxer.createMP4Demuxer(byteChannel);
List<DemuxerTrack> videoTracks = demuxer.getVideoTracks();
List<DemuxerTrack> audioTracks = demuxer.getAudioTracks();
if (mixedVideoTrack == null) {
mixedVideoTrack = mp4Muxer.addVideoTrack(videoTracks.get(0).getMeta().getCodec(), videoTracks.get(0).getMeta().getVideoCodecMeta());
}
if (mixedAudioTrack == null) {
mixedAudioTrack = mp4Muxer.addAudioTrack(audioTracks.get(0).getMeta().getCodec(), audioTracks.get(0).getMeta().getAudioCodecMeta());
}
for (DemuxerTrack videoTrack : videoTracks) {
Packet packet;
while ((packet = videoTrack.nextFrame()) != null) {
mixedVideoTrack.addFrame(packet);
}
}
for (DemuxerTrack audioTrack : audioTracks) {
Packet packet;
while ((packet = audioTrack.nextFrame()) != null) {
mixedAudioTrack.addFrame(packet);
}
}
demuxer.close();
}
mp4Muxer.finish();
output.close();
}
}
Unfortunately, it does not work as the video generated does not contain audio files nor correct frames. For example, all visual frames I get are like one below:
What is the problem and how can I fix it? Thank you very much.
Solution 1:[1]
You can do this with ffmpeg.
- First you will need to write the file paths to a txt file
txt file with :
- file '/sdcard/video/video1.mp4'
- file '/sdcard/video/video2.mp4'
- file '/sdcard/video/video3.mp4'
- file '/sdcard/video/video4.mp4'
You can use following command.
ffmpeg -f concat -i mp4list.txt -c:v copy -c:a copy merging.mp4
- You can write a wrapper over the Java code. Additionally, you can use the code below.
Resource to help : link
String[] cmd = new String[]{"-f", "concat", "-safe", "0", "-i", "mp4list.txt", "-c", "copy", "-preset", "ultrafast", outFile};
ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler(){
@Override
public void onFailure(String message){
}
@Override
public void onSuccess(String message){
}
@Override
public void onProgress(String message){
}
@Override
public void onStart(){
}
@Override
public void onFinish(){
}
}
Solution 2:[2]
I don't really know jcodec
, but I've used MP4parser
in the past. If you need to merge mp4 files, regardless of the library, then I suggest you this one. I'm just pitching it to you because I don't know if you're having problems in merging the files due to the complexity of the library. Might be worth to give it a shot. MP4Parser is quite easy and straightforward. I'll also leave you the link to the maven repository.
https://search.maven.org/artifact/com.googlecode.mp4parser/isoparser/1.1.22/jar
Here there's an implementation I've done to show you how to merge 2 files from the same original video (to replicate your exact case). Basically, you have a Movie
class, which represents your mp4 file, containing a List
of Track
(only 2); one representing the video while the other the audio.
First, I've collected from the movies all the video and audio tracks in two separate arrays.
Then, I've created a
List
with the merged audioTrack
and the merged videoTrack
in order to set it to the output file.Finally, I've built the
Movie
inside aContainer
and saved it with aFileOutputStream
.
public class Main {
public static void main(String[] args) throws IOException {
MovieCreator mc = new MovieCreator();
Movie movie1 = mc.build("./test1.mp4");
Movie movie2 = mc.build("./test2.mp4");
//Fetching the video tracks from the movies and storing them into an array
Track[] vetTrackVideo = new Track[0];
vetTrackVideo = Stream.of(movie1, movie2)
.flatMap(movie -> movie.getTracks().stream())
.filter(movie -> movie.getHandler().equals("vide"))
.collect(Collectors.toList())
.toArray(vetTrackVideo);
//Fetching the audio tracks from the movies and storing them into an array
Track[] vetTrackAudio = new Track[0];
vetTrackAudio = Stream.of(movie1, movie2)
.flatMap(movie -> movie.getTracks().stream())
.filter(movie -> movie.getHandler().equals("soun"))
.collect(Collectors.toList())
.toArray(vetTrackAudio);
//Creating the output movie by setting a list with both video and audio tracks
Movie movieOutput = new Movie();
List<Track> listTracks = new ArrayList<>(List.of(new AppendTrack(vetTrackVideo), new AppendTrack(vetTrackAudio)));
movieOutput.setTracks(listTracks);
//Building the output movie and storing it into a Container
DefaultMp4Builder mp4Builder = new DefaultMp4Builder();
Container c = mp4Builder.build(movieOutput);
//Writing the output file
FileOutputStream fos = new FileOutputStream("output.mp4");
c.writeContainer(fos.getChannel());
fos.close();
}
}
Here, I'll leave you also a link to my github repository with the test mp4 files so that you can test the merging sample.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Arda Kazancı |
Solution 2 | Dan |