2

Using JAVA and Xuggler - the following code combines an MP3 audio file and a MP4 movie file and outputs a combined mp4 file.

I want outputVideo file should be play automatically while combining audio and video file.

String inputVideoFilePath = "in.mp4";
String inputAudioFilePath = "in.mp3";
String outputVideoFilePath = "out.mp4";

IMediaWriter mWriter = ToolFactory.makeWriter(outputVideoFilePath);

IContainer containerVideo = IContainer.make();
IContainer containerAudio = IContainer.make();

// check files are readable
if (containerVideo.open(inputVideoFilePath, IContainer.Type.READ, null) < 0)
    throw new IllegalArgumentException("Cant find " + inputVideoFilePath);
if (containerAudio.open(inputAudioFilePath, IContainer.Type.READ, null) < 0)
    throw new IllegalArgumentException("Cant find " + inputAudioFilePath);

// read video file and create stream
IStreamCoder coderVideo = containerVideo.getStream(0).getStreamCoder();
if (coderVideo.open(null, null) < 0)
    throw new RuntimeException("Cant open video coder");
IPacket packetvideo = IPacket.make();
int width = coderVideo.getWidth();
int height = coderVideo.getHeight();

// read audio file and create stream
IStreamCoder coderAudio = containerAudio.getStream(0).getStreamCoder();
if (coderAudio.open(null, null) < 0)
    throw new RuntimeException("Cant open audio coder");
IPacket packetaudio = IPacket.make();

mWriter.addAudioStream(1, 0, coderAudio.getChannels(), coderAudio.getSampleRate());
mWriter.addVideoStream(0, 0, width, height);

while (containerVideo.readNextPacket(packetvideo) >= 0) {

    containerAudio.readNextPacket(packetaudio);

    // video packet
    IVideoPicture picture = IVideoPicture.make(coderVideo.getPixelType(), width, height);
    coderVideo.decodeVideo(picture, packetvideo, 0);
    if (picture.isComplete()) 
        mWriter.encodeVideo(0, picture);

    // audio packet 
    IAudioSamples samples = IAudioSamples.make(512, coderAudio.getChannels(), IAudioSamples.Format.FMT_S32);
    coderAudio.decodeAudio(samples, packetaudio, 0);
    if (samples.isComplete()) 
        mWriter.encodeAudio(1, samples);

}

coderAudio.close();
coderVideo.close();
containerAudio.close();
containerVideo.close();
mWriter.close();

If anybody knows play video file automatically when combining audio and video file using java xuggler.. please please help me..It would be really appreciable..

Rajesh K
  • 45
  • 1
  • 10
  • Hey When I try to add song in a video file it shows.com.xuggler.mediaTool.ToolFactory NoClassDefFoundError.How can I resolve this error.Please help me. – Robin Royal Oct 25 '14 at 17:55
  • 1
    Robin thnkx fr ur response.. If ur running through maven project would u please add the below dependency xuggle xuggle-xuggler 5.4 If ur not running through maven download xuggler 5.4 jar to lib path. – Rajesh K Oct 27 '14 at 07:23
  • I'm not using project through maven. :'( And I'm using 3.4 lib.Is there an otherway except running through maven. – Robin Royal Oct 27 '14 at 15:35
  • 1
    Yeah u can run without using maven but should use correct library. Your using 3.4 lib(itseems that version is not correct) u can download correct jar file with this link ========================> (http://www.java2s.com/Code/Jar/x/Downloadxugglexugglernoarch54jar.htm) and run the project.. – Rajesh K Oct 28 '14 at 05:50
  • Thanks for your help Rajesh k. after adding this lib I faced same error.:( how can I resolve this? – Robin Royal Oct 28 '14 at 16:53
  • Rajesh I desperately needs your help,I'm stuck here. – Robin Royal Oct 28 '14 at 19:41
  • Robin would u pls share ur mail id. I'll send the whole project in which i'm running here and also i'll attach documentation guide to run that project. I'm sure the problem is library only. – Rajesh K Oct 29 '14 at 08:18
  • 1
    Or Else try to run using Maven project its too simple if ur using maven u just need to add two things in pom.xml file. 1) add this repository==> xuggle repo http://xuggle.googlecode.com/svn/trunk/repo/share/java/ and one depedency===> xuggle xuggle-xuggler 5.4 . Would u pls share ur mail-id if your not comfortable with maven – Rajesh K Oct 29 '14 at 08:22
  • Thanks Rajesh.I appreciate your help. robinroyal15@gmail.com my email Id.I'll try to work on maven but if there is another way of doing this than please tell me.Thanks again. – Robin Royal Oct 29 '14 at 13:48
  • Robin I've shared all the details to your mail regarding helping to run in which i posted above program. It would be really helpful for me if you solved the problem.. Thanks for helping to solve my problem Robin. – Rajesh K Oct 30 '14 at 11:45

2 Answers2

4

Here is the complete code. I also did some changes: if audio is longer, then write the rest separately, and added progress of audio and video in title bar. You may need to check if there is really video or audio stream in your files to prevent errors.

import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.Global;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
 *
 * @author Pasban
 */
public class MergeVideoAudio extends JDialog {

    Image image;
    private double video_duration = 0.00001, video_read = 0, audio_duration = 0.00001, audio_read = 0;

    public static void main(String[] args) {
        final MergeVideoAudio merge = new MergeVideoAudio();
        Thread thread = new Thread() {

            @Override
            public void run() {
                merge.perform("y:/a.mp4", "y:/b.mp3", "y:/c.mp4");
                merge.setVisible(false);
                System.exit(0);
            }
        };
        thread.run();
    }

    public void perform(String path_video, String path_audio, String path_output) {

        IMediaWriter mWriter = ToolFactory.makeWriter(path_output);
        IContainer containerVideo = IContainer.make();
        IContainer containerAudio = IContainer.make();

// check files are readable
        if (containerVideo.open(path_video, IContainer.Type.READ, null) < 0) {
            throw new IllegalArgumentException("Cant find " + path_video);
        }

        if (containerAudio.open(path_audio, IContainer.Type.READ, null) < 0) {
            throw new IllegalArgumentException("Cant find " + path_audio);
        }

// read video file and create stream
        IStreamCoder coderVideo = containerVideo.getStream(0).getStreamCoder();

        if (coderVideo.open(null, null) < 0) {
            throw new RuntimeException("Cant open video coder");
        }
        int width = coderVideo.getWidth();
        int height = coderVideo.getHeight();

        this.setSize(width, height);
        this.setLocationRelativeTo(null);
        this.setVisible(true);

// read audio file and create stream
        IStreamCoder coderAudio = containerAudio.getStream(0).getStreamCoder();

        if (coderAudio.open(null, null) < 0) {
            throw new RuntimeException("Cant open audio coder");
        }

        IPacket packet = IPacket.make();

        mWriter.addAudioStream(1, 0, coderAudio.getChannels(), coderAudio.getSampleRate());
        mWriter.addVideoStream(0, 0, width, height);

        video_duration = 0.000001 + (containerVideo.getDuration() == Global.NO_PTS ? 0 : (containerVideo.getDuration() / 1000.0));
        audio_duration = 0.000001 + (containerAudio.getDuration() == Global.NO_PTS ? 0 : (containerAudio.getDuration() / 1000.0));

        while (containerVideo.readNextPacket(packet) >= 0) {
            video_read = (packet.getTimeStamp() * packet.getTimeBase().getDouble() * 1000);

// video packet
            IVideoPicture picture = IVideoPicture.make(coderVideo.getPixelType(), width, height);
            coderVideo.decodeVideo(picture, packet, 0);
            if (picture.isComplete()) {
                mWriter.encodeVideo(0, picture);
                IConverter converter = ConverterFactory.createConverter(ConverterFactory.XUGGLER_BGR_24, picture);
                this.setImage(converter.toImage(picture));
                this.setProgress(video_duration, video_read, audio_duration, audio_read);
            }

// audio packet 
            containerAudio.readNextPacket(packet);
            audio_read = (packet.getTimeStamp() * packet.getTimeBase().getDouble() * 1000);
            IAudioSamples samples = IAudioSamples.make(512, coderAudio.getChannels(), IAudioSamples.Format.FMT_S32);
            coderAudio.decodeAudio(samples, packet, 0);
            if (samples.isComplete()) {
                mWriter.encodeAudio(1, samples);
                this.setProgress(video_duration, video_read, audio_duration, audio_read);
            }

        }

//write the remaining audio, if your audio is longer than your video
        while (containerAudio.readNextPacket(packet) >= 0) {
            audio_read = (packet.getTimeStamp() * packet.getTimeBase().getDouble() * 1000);
            IAudioSamples samples = IAudioSamples.make(512, coderAudio.getChannels(), IAudioSamples.Format.FMT_S32);
            coderAudio.decodeAudio(samples, packet, 0);
            if (samples.isComplete()) {
                mWriter.encodeAudio(1, samples);
                this.setProgress(video_duration, video_read, audio_duration, audio_read);
            }
        }

        coderAudio.close();
        coderVideo.close();
        containerAudio.close();
        containerVideo.close();

        mWriter.close();
    }

    public MergeVideoAudio() {
        this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    }

    public void setImage(final Image image) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                MergeVideoAudio.this.image = image;
                repaint();
            }
        });
    }

    @Override
    public synchronized void paint(Graphics g) {
        if (image != null) {
            g.drawImage(image, 0, 0, null);
        }
    }

    public static String convertDurationHMSm(double time) {
        long elapsed = (long) (time * 1000);
        long duration = elapsed / 1000;
        long ms = elapsed % 1000;
        return String.format("%02d:%02d:%02d.%02d", duration / 3600, (duration % 3600) / 60, (duration % 60), ms / 10);
    }

    private void setProgress(double video_duration, double video_read, double audio_duration, double audio_read) {
        this.setTitle("Video: " + (int) (100 * video_read / video_duration) + "%, Audio " + (int) (100 * audio_read / audio_duration) + "%");
    }
}

If you need to show frames based on video frame rate, then add the following code after every time you read your video packet, or else leave it for fast merging.

try {
    Thread.sleep((long) (1000 / coderVideo.getFrameRate().getDouble()));
} catch (InterruptedException ex) {
    ex.printStackTrace();
}
Soley
  • 1,716
  • 1
  • 19
  • 33
  • This doesn't work for me. I can hear the audio, but I don't see the images, just a black screen – Para Apr 10 '18 at 11:14
1

It might be old, but all you need to do is to paint your picture on a jpanel. Something like this:

//Instead of 
if (picture.isComplete()){ 
    mWriter.encodeVideo(0, picture);
}

//Use
if (picture.isComplete()) {
    mWriter.encodeVideo(0, picture);
    IConverter converter = ConverterFactory.createConverter(ConverterFactory.XUGGLER_BGR_24, picture);
    repaint_on_jpanel_thread(converter.toImage(picture));
    //this is a function to paint the buffered image on your jpanel
}
Soley
  • 1,716
  • 1
  • 19
  • 33
  • Hi Salivan, Thanks for helping to solve my problem. Would u please share this function code==> needrepaint_on_jpanel_thread(converter.toImage(picture)); just share that function code because i'm not familiar with jpanel. – Rajesh K Nov 28 '14 at 12:30
  • Thanks a lot Salivan. Yup video is displaying while combining audio and video..Once again big thankx man – Rajesh K Dec 01 '14 at 06:57
  • Hey Salivan still i need one clarification where should i use this code==> try { Thread.sleep((long) (1000 / coderVideo.getFrameRate().getDouble())); } catch (InterruptedException ex) { ex.printStackTrace(); } in order to show frames based on video frame rate – Rajesh K Dec 03 '14 at 06:55
  • 1
    You can put it anywhere within the **while (video pocket reader ) ** you may put it after here if you like: this.setImage(converter.toImage(picture)); really no difference, because you will show the image you have in your hand and you will wait until you pass the frame delay to read the next frame/image. – Soley Dec 03 '14 at 23:16