4

I would like to record video and audio together and have got much of the way there but there's an issue with the audio playing before the video and possibly not the same length...

Partially solved from https://stackoverflow.com/questions/19238732/adding-audio-to-xuggler-webacm-capture

I have a feeling my problem is the length of the audio samples but i'm not sure how best to fix it; please could you help? Code:

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;
import com.xuggle.ferry.IBuffer;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;

import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.MediaListenerAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.IAddStreamEvent;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.IAudioSamples.Format;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPixelFormat;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.TestAudioSamplesGenerator;
import com.xuggle.xuggler.io.XugglerIO;
import com.xuggle.xuggler.video.ConverterFactory;
import com.xuggle.xuggler.video.IConverter;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.util.List;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class Encoder {
    public static int audioTime = 0;
    public static long start = System.currentTimeMillis();
    public static IMediaWriter writer;

    public static void main(String[] args) throws Throwable {
        JFrame window = new JFrame("vid");
        window.setVisible(true);

        File file = new File("output.mov");

        List<Webcam> list = Webcam.getWebcams();

        for (int i = 0; i < list.size(); i++) {
                try {
                    Webcam cam = list.get(i);
                    System.out.println("Found this Camera : "+cam.getName());
                    BufferedImage image = cam.getImage();
                } catch (Exception e) {
                    System.out.println("Exception in cam : " + i);
                }
        }


        writer = ToolFactory.makeWriter(file.getName());            
        Dimension size = WebcamResolution.QVGA.getSize();

        Webcam webcam = Webcam.getDefault();
        webcam.setViewSize(size);
        webcam.open(true);

        writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_H264, size.width, size.height);
        writer.addAudioStream(1, 0, ICodec.ID.CODEC_ID_AAC, 2, 44100);


        long time = (System.currentTimeMillis() - start) * 1000;
        for (int i = 0; i < 100; i++) {

                System.out.println("Capture frame " + i);

                BufferedImage image = ConverterFactory.convertToType(webcam.getImage(), BufferedImage.TYPE_3BYTE_BGR);
                IConverter converter = ConverterFactory.createConverter(image, IPixelFormat.Type.YUV420P);

                long len = (System.currentTimeMillis() - start) * 1000 - time;
                time = (System.currentTimeMillis() - start) * 1000;

                IVideoPicture frame = converter.toPicture(image, time);
                frame.setKeyFrame(i == 0);
                frame.setQuality(0);

                writer.encodeVideo(0, frame);


                IAudioSamples samples = customAudioStream(len);
                writer.encodeAudio(1, samples);


                window.setSize(200,200);
                window.getContentPane().removeAll();
                window.getContentPane().add(new JLabel(new ImageIcon(image)));
                window.getContentPane().validate();
                window.getContentPane().invalidate();
                window.getContentPane().repaint();

                // 10 FPS
                Thread.sleep(0,500);

            }

            writer.close();
            window.dispose();

            System.out.println("Video recorded in file: " + file.getAbsolutePath());
        }

        public static IAudioSamples customAudioStream(long length){

            // audio parameters
            int channelCount = 2;
            int sampleRate = 44100;
            int len = (int)length/1000;

            IContainer container = writer.getContainer();
            IStream stream = container.getStream(1);
            int sampleCount = stream.getStreamCoder().getDefaultAudioFrameSize();

            // create a place for audio samples
            IAudioSamples samples = IAudioSamples.make(1024*5, channelCount, IAudioSamples.Format.FMT_S16);

            TargetDataLine line = null;
            AudioFormat audioFormat = new AudioFormat(sampleRate, (int)16, channelCount, true, false);
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
            try {
                    line = (TargetDataLine) AudioSystem.getLine(info);
                    line.open(audioFormat);
            } catch (LineUnavailableException ex) {
                    System.out.println("ERROR: "+ex.toString());
            }

            line.start();
            byte[] data = new byte[4096*5];
            int sz = line.read(data, 0, data.length);

            samples.put(data, 0, 0, sz);
            audioTime  += (sz);

            double sAudioTime = (audioTime)/44.1000;

            samples.setComplete(true, sz/4, sampleRate, channelCount, Format.FMT_S16, audioTime/4);

   System.out.println(len + " : " + sampleCount + " : " + data.length + " : " + sz + " : " + audioTime + " : " + line.available());
            return(samples);
        }

}

If there's a better way to do this I'm all ears also. Thanks! Ed

Community
  • 1
  • 1
ThatGuy
  • 1,225
  • 10
  • 28

0 Answers0