I'm using mp4parser to add two videos together in an android app, but the output is a file I can't play with my phone or my computer (Windows Media Player and VLC). This is the function i use
public void MergeVideos(String[] pathsToVideos, String pathToOutput) throws IOException, InterruptedException
{
List<Track> tracks = new LinkedList<Track>();
Movie outputVideo = new Movie();
for (int i = 0; i < pathsToVideos.length; i++)
{
Movie video = MovieCreator.build(pathsToVideos[i]);
List<Track> trackss = video.getTracks();
for (Track track : trackss)
{
if (track.getHandler().equals("vide"))
{
tracks.add(track);
}
}
}
outputVideo.addTrack(new AppendTrack(tracks.toArray(new Track[tracks.size()])));
Container out = new DefaultMp4Builder().build(outputVideo);
File outputFile = new File(pathToOutput);
if(!outputFile.exists())
{
outputFile.createNewFile();
}
//write mp4 file
FileChannel fc = new RandomAccessFile(String.format(pathToOutput), "rw").getChannel();
out.writeContainer(fc);
fc.close();
//Add to the android media gallery so i can see it on my computer
addToGallery(new File(pathToOutput));
}
Since the problem was coming from MP4Parser itself, I stepped over to FFMpeg, from which I can successfully combine videos.
I'm used the demuxer and for later reference; this is the command I used:
"ffmpeg -y -f concat -i temp.txt -c copy output.mp4"
Related
I try to merge multiple mp4 files via moviemaker like I saw in one post:
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();
}
}
For two files this works totally fine. But I have more then two files to merge. So I started adding a loop which merged two files and used the merged file to merge with another one. Theoretically it works and I see by the time stamp they are merged. But I cant open the file because its an mp4a with avc1 sound... Has anybody an idea how to solve it? The Code below is my attemp: just choose a directory with some mp4 files, the merged files are named output0, output1, 2, ...
The highest number is the final file. But while output0 works fine, already output1 wont work...
public void Merge(File folder){
File[] liste = folder.listFiles();
String path = jTextField1.getText();
for(int i = 0; i < liste.length-2; i++){
MovieCreator mc = new MovieCreator();
try{
Movie movie1 = mc.build(liste[i].toString());
System.out.println("Film 1: " + liste[i]);
Movie movie2 = mc.build(liste[i+1].toString());
System.out.println("Film 2: " + liste[i+1]);
//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 = (Container) mp4Builder.build(movieOutput);
//Writing the output file
FileOutputStream fos = new FileOutputStream(path + "\\output" + i + ".mp4");
c.writeContainer(fos.getChannel());
fos.close();
File f = new File(path + "\\output" + i + ".mp4");
liste[i+1] = f;
}catch(Exception e2){
e2.printStackTrace();
}
}
}
since i can't comment yet, i'm writing it as an answer. i don't really know about moviemaker, however i've been dealing with media files and thought it could be different perspective to your problem.
you can use ffmpeg to merge media files as answered in here. also if you want to stick with java, here's the java wrapper for ffmpeg command line tool.
I'm working a transfer file program and my program is working but I'm having a problem because when I select multiple files and put it on a textbox the source directory can't read what is on the textbox
this is my code
Opening file/files
btnSearchFile.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
FileDialog fd = new FileDialog(shell, SWT.MULTI);
Collection files = new ArrayList();
String firstFile = fd.open();
if (firstFile != null) {
String[] selectedFiles = fd.getFileNames();
File file = new File(firstFile);
for (int ii = 0; ii < selectedFiles.length; ii++ )
{
if (file.isFile())
{
displayFiles(new String[] { file.toString()});
}
else
displayFiles(file.list());
}
}
}
});
Displaying Files on textbox
public void displayFiles(String[] files) {
for (int i = 0; files != null && i < files.length; i++) {
txtSource.append(files[i]);
txtSource.setEditable(false);
}
}
Copy Files
public static void copyFile(File src, File dest) throws IOException
{
InputStream oInStream = new FileInputStream(src);
OutputStream oOutStream = new FileOutputStream(dest);
// Transfer bytes from in to out
byte[] oBytes = new byte[1024];
int nLength;
BufferedInputStream oBuffInputStream = new BufferedInputStream( oInStream );
while ((nLength = oBuffInputStream.read(oBytes)) > 0)
{
oOutStream.write(oBytes, 0, nLength);
}
oInStream.close();
oOutStream.close();
}
PS: One file is okay but if multiple files are selected and put on the textbox the source directory can't be found
In order to be completely helpful, we could really use some more detail (specific exceptions, a complete MCVE, which SWT widgets are used, etc.).
That said, I think you've provided enough to see that there are some issues with your code:
For starters, when you have multiple files selected, you're displaying the same file name (the name of the first one) over and over. Perhaps this is intentional, but worth mentioning:
String[] selectedFiles = fd.getFileNames();
File file = new File(firstFile);
for (int ii = 0; ii < selectedFiles.length; ii++ )
{
// You've used a FileDialog, so this should always be true
if (file.isFile())
{
// Will always be the first file
displayFiles(new String[] { file.toString()});
}
else
displayFiles(file.list());
}
Based on the context, I'm assuming txtSource is a Text widget. With that in mind, if we look at your displayFiles() method, you have the following:
txtSource.append(files[i]);
When you call displayFiles() repeatedly, you will be tacking on a file name after all the others, effectively building one long String which is the combination of all file names. When you go to copy the files listed, splitting that String back into valid file paths will be tricky.
My guess is that when you say:
"the source directory can't be found"
...you're just grabbing the content of txtSource. Something like this:
new File(txtSource.getText());
"...One file is okay..."
That will certainly work if there's only one file name in the Text object, but if there are multiple names it will result in a non-existent File.
For example, if you've selected two files:
C:\Users\me\FileA
C:\Users\me\FileB
Your txtSource would display C:\Users\me\FileAC:\Users\me\FileB. And the path C:\Users\me\FileAC:\Users\me\FileB most likely does not exist.
In that case, new File(txtSource.getText()).exists() would return false, and using that File in the constructor for FileInputStream (inside copyFile()) would result in a FileNotFoundException.
In short, just make sure that when you make your call to copyFile() and create the source File object that you're giving the path that you think you are, and not the concatenation of all files selected.
I am looking to take a large video files (3hours+) and pass in segments of the video that I would like to split.
For example, I could pass in a 3 hour video - and want from 00:10 to 00:11 to be split into a separate file.
I currently have the below code - that takes my video and splits it into a split no. of segments, but how would I go about splitting the video by time instead?
Code:
try {
File file = new File("//Users//robeves//Desktop//Videos to split//TestVideo.mp4");//File read from Source folder to Split.
if (file.exists()) {
String videoFileName = file.getName().substring(0, file.getName().lastIndexOf(".")); // Name of the videoFile without extension
File splitFile = new File("//Users//robeves//Desktop//Videos to split//Converted//"+ videoFileName);//Destination folder to save.
if (!splitFile.exists()) {
splitFile.mkdirs();
System.out.println("Directory Created -> "+ splitFile.getAbsolutePath());
}
int i = 01;// Files count starts from 1
InputStream inputStream = new FileInputStream(file);
String videoFile = splitFile.getAbsolutePath() +"/"+ String.format("%02d", i) +"_"+ file.getName();// Location to save the files which are Split from the original file.
OutputStream outputStream = new FileOutputStream(videoFile);
System.out.println("File Created Location: "+ videoFile);
int totalPartsToSplit = 20;// Total files to split.
int splitSize = inputStream.available() / totalPartsToSplit;
int streamSize = 0;
int read = 0;
while ((read = inputStream.read()) != -1) {
if (splitSize == streamSize) {
if (i != totalPartsToSplit) {
i++;
String fileCount = String.format("%02d", i); // output will be 1 is 01, 2 is 02
videoFile = splitFile.getAbsolutePath() +"/"+ fileCount +"_"+ file.getName();
outputStream = new FileOutputStream(videoFile);
System.out.println("File Created Location: "+ videoFile);
streamSize = 0;
}
}
outputStream.write(read);
streamSize++;
}
inputStream.close();
outputStream.close();
System.out.println("Total files Split ->"+ totalPartsToSplit);
} else {
System.err.println(file.getAbsolutePath() +" File Not Found.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
If you do want to be able to play the segments individually, then the above code may not work if it is just splitting the file at arbitrary points, as many video formats need to finish on a good 'boundary' to enable proper playback.
As Binkan suggests, using a video library like ffmpeg, either in cmd line, wrapped cmd line or by using its associated C libraries, will allow you safely split a video in most common formats.
For example the following ffmpeg cmd line will create a segment from an mp4 video:
ffmpeg -i inputVideo.mp4 -ss 00:00:00 -t 00:00:10 -c copy outputVideoSegment.mp4
The following code uses this utility in a 'wrapper' to segment a video file into chunks:
int chunkSize = videoDurationSecs/(numberOfChunks + 1);
int startSecs = 0;
for (int i=0; i<numberOfChunks; i++) {
//Create video chunk
String startTime = convertSecsToTimeString(startSecs);
int endSecs = startSecs + ((i+1)*chunkSize);
if (endSecs > videoDurationSecs) {
//make sure rounding does not mean we go beyond end of video
endSecs = videoDurationSecs;
}
String endTime = convertSecsToTimeString(endSecs);
//Call ffmpeg to create this chunk of the video using a ffmpeg wrapper
String argv[] = {"ffmpeg", "-i", videoPath,
"-ss",startTime, "-t", endTime,
"-c","copy", segmentVideoPath[i]};
int ffmpegWrapperReturnCode = ffmpegWrapper(argv);
}
String convertSecsToTimeString(int timeSeconds) {
//Convert number of seconds into hours:mins:seconds string
int hours = timeSeconds / 3600;
int mins = (timeSeconds % 3600) / 60;
int secs = timeSeconds % 60;
String timeString = String.format("%02d:%02d:%02d", hours, mins, secs);
return timeString;
}
Examples of wrappers are here, but you can also use the ffmpeg libraries directly if you would rather avoid the wrapper approach (which does have the disadvantage that ffmpeg cmd line is not really intended to be wrapped in this way):
http://www.sauronsoftware.it/projects/jave/
https://github.com/jhotovy/android-ffmpeg (Android based)
I have a problem with playing an MP3 file with JMF, it displays the following error :
Error:
Unable to realize com.sun.media.amovie.AMController#80669d Exception in thread "main"
javax.media.CannotRealizeException at javax.media.Manager.blockingCall(Manager.java:2005) at
javax.media.Manager.createRealizedPlayer(Manager.java:528) at tp.Main.main(Main.java:44)
Error value: 80070020
here is my code :
public class Main {
public static void main(String[] args) throws NoPlayerException, CannotRealizeException, MalformedURLException, IOException, URISyntaxException {
Fenetre F1 = new Fenetre();
F1.setVisible(true);
InputStream is = Main.class.getClass().getResourceAsStream("/data/gmu.mp3");
File temp=File.createTempFile("temp", ".mp3");
OutputStream os = new FileOutputStream(temp);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = is.read(bytes)) != -1) {
os.write(bytes, 0, read);
}
final Player p=Manager.createRealizedPlayer(temp.toURI().toURL());
p.start();
while(true){
if(p.getMediaTime().getSeconds()==p.getDuration().getSeconds()){
p.stop();
p.setMediaTime(new Time(0));
p.start();
}
}
}
}
normally it works If I don't use the InputStream, and use simply
File f =new File(Main.class.getResource("/data/gmu.mp3").getFile());
final Player p=Manager.createRealizedPlayer(temp.toURI().toURL());
but this way It doesn't work when I pack my JAR file, so I'm trying to use InputStream, the aim is to make a JAR with a WORKING music
Actually I just had to convert my MP3 file to WAV and it worked !!! I don't know why the player exception was caught with the MP3 temporary file but nothing goes wrong with WAV. this is not really the answer I sought since the WAV files takes a lot more space but at least it works.
I also tried OGG format, which makes the same problem of MP3 ...
any other answer, suggestion, or discussion is welcome.
This is my function for converting blob to mp3:
private void convertByyeToMP3(byte[] bytearray,String trackName) {
try {
ContextWrapper c = new ContextWrapper(getApplicationContext());
File directory = new File(c.getFilesDir().getAbsolutePath()
+ "/Music");
if (!directory.exists()){
directory.mkdir();
}
File tempMp3 = File.createTempFile(trackName, ".mp3",
directory);
FileOutputStream fos = new FileOutputStream(tempMp3);
fos.write(bytearray);
fos.close();
Log.d("Byte array to mp3 conversion: ", "successfull");
} catch (Exception ex) {
Log.d("In convertToByteToMp3 Function:", ex.toString());
}
}
When I execute this function ,I can see the created mp3 files in my app folder but when I try to play them Using my own code or using ES File Explorer, they both can't play it.
This is the function I use play my music:
private MediaPlayer mp = new MediaPlayer();
private void playSong(String songPath) {
try {
mp.reset();
mp.setDataSource(songPath);
mp.prepare();
mp.start();
} catch (IOException e) {
Log.v(getString(R.string.app_name), e.getMessage());
}
}
And I use this sample code to play the track:
ContextWrapper c = new ContextWrapper(getApplicationContext());
File directory = new File(c.getFilesDir().getAbsolutePath() + "/Music");
playSong(directory.getPath() + File.separator + "kurchina");
This is where I read database and send the blob:
cursor = mDbHelper.GetTables();
byte[] blob = null;
DATAS data = new DATAS();
while (cursor.moveToNext()) {
blob = cursor.getBlob(cursor.getColumnIndex("data"));
if (blob != null) {convertByyeToMP3(blob,data_MusicName);}
db.addDATAS(data);
}
FYIs:
-Read and Write permissions added to manifest.
-Path and filename are check and they exist
-blob byte is not corrupted
There are all sorts of things that might have gone wrong, either in the code that you have shown us or elsewhere. So you need to do your own troubleshooting. Methodically.
Figure out if the problem is with the song file you have extracted or the way you are playing it. For example, try to play the extracted file using a free-standing mp3 player utility.
Assuming that the problem is the extracted file, the next thing is to figure out if the file is the same as the one that you originally inserted into the database. Compare the file sizes and the checksums using the relevant external applications.
and so on.
Found the problem.
It didn't play because the music files were stored in my app folder which is only accessible using a rooted device.
When I copied the music to my sdcard they played well, but in my app folder using rooted nexus 7 I couldn't play it even with an mp3-player app.