微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用java代码录制时视频/音频不同步

如何解决使用java代码录制时视频/音频不同步

我创建了 java 应用,它可以同时从网络摄像头和模拟音频中捕获视频。下一步应该是同时捕获 MIDI 信号(通过 USB),但这暂时不是重点。

我注意到视频(音频后面)的延迟随着时间的推移而增加,在捕获 90 分钟后,延迟已经是几秒钟了。如果我捕获网络摄像头音频信号而不是外部电子鼓,则会发生类似的延迟。

音频被记录到单独的 wav 文件中,jawax.sound API 作为一个单独的线程。 使用 IMediaWriter(xuggler 代码)作为单独的线程将视频录制到单独的 mp4 文件中。 网络摄像头通过 sarxos 代码访问。

我的代码(相关部分)在这里

public class VideoAudioCapture {
    TargetDataLine line;
    AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
    File wavFile = null;
    File mp4File = null;
    File csvFile = null;
    public static IMediaWriter writer;
    Webcam webcam;

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

    public void run(){
        
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime());
            //Prepare files for writing
            String prefix = "D:/";
            wavFile = new File(prefix + "/RecordAudio" + "_" + timeStamp + ".wav");
            mp4File = new File(prefix + "/RecordVideo" + "_" + timeStamp + ".mp4");
            csvFile = new File(prefix + "/RecordVideo" + "_" + timeStamp + ".csv");
            
            //Starts 2 threads,for mp4 and wav separate capture
            Runnable mp4Task = new Runnable(){
                    @Override
                    public void run(){
                        try {
                            startRecordMp4();
                        } catch (InterruptedException ex) {
                            Logger.getLogger(WebcamGUI.class.getName()).log(Level.SEVERE,null,ex);
                        } catch (IOException ex) {
                            Logger.getLogger(WebcamGUI.class.getName()).log(Level.SEVERE,ex);
                        }
                    }
                };
            Thread t3 = new Thread(mp4Task);
            t3.start();
            
            Runnable wavTask = new Runnable(){
                    @Override
                    public void run(){
                        startRecordWav();
                    }
                };
                Thread t2 = new Thread(wavTask);
                t2.start();
    }

    public void startRecordWav(){
        int selectedmixerIndex = jComboBoxAudioInput.getSelectedindex();
        mixer.Info[] mixerInfo = AudioSystem.getmixerInfo();
        mixer mixer = AudioSystem.getmixer(mixerInfo[ selectedmixerIndex ]);
        AudioFormat audioFormat = new AudioFormat(44100.0F,16,2,true,false); 
        DataLine.Info dataLineInfo = new DataLine.Info( TargetDataLine.class,audioFormat );
        try {
            line = (TargetDataLine) mixer.getLine(dataLineInfo);
            line.open();
            line.start();
            System.out.println("Audio line microsecond position = " + line.getMicrosecondPosition());
             AudioInputStream ais = new AudioInputStream(line);
                    try {
                        AudioSystem.write(ais,fileType,wavFile);
                        System.out.println("AudioSystem frame length = " + ais.getFrameLength());
                    } catch (IOException ex) {
                        Logger.getLogger(WebcamGUI.class.getName()).log(Level.SEVERE,ex);
                    }
            } catch (LineUnavailableException ex) {
            Logger.getLogger(WebcamGUI.class.getName()).log(Level.SEVERE,ex);
        }
    }
    
    public void startRecordMp4() throws InterruptedException,IOException{
        writer = ToolFactory.makeWriter(mp4File.getAbsolutePath());
        FileWriter csvfileWriter = new FileWriter(csvFile);
        csvfileWriter.append("time (ms)" + "," + "video - audio delay (ms)");
        csvfileWriter.append("\n");
        
        Dimension size = WebcamResolution.VGA.getSize();
        writer.addVideoStream(0,ICodec.ID.CODEC_ID_H264,size.width,size.height);        
        
        long start = System.currentTimeMillis();
        long time = (System.currentTimeMillis() - start) * 1000;
        long len = (System.currentTimeMillis() - start) * 1000 - time;

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            BufferedImage image = ConverterFactory.convertToType(webcam.getimage(),BufferedImage.TYPE_3BYTE_BGR);
            IConverter converter = ConverterFactory.createConverter(image,IPixelFormat.Type.YUV420P);

            System.out.println("Line microsecon position = " + (long) (line.getMicrosecondPosition())/1000);
            System.out.println("Video time = " + (System.currentTimeMillis() - start));
            System.out.println("Video - audio delay = " + ((System.currentTimeMillis() - start)-(long) (line.getMicrosecondPosition())/1000));
            IVideoPicture frame = converter.toPicture(image,(System.currentTimeMillis() - start ) * 1000);
            frame.setKeyFrame(i == 0);
            frame.setQuality(100);
            writer.encodeVideo(0,frame);
            long currentTime = System.currentTimeMillis();
            long runningTime = (currentTime - startTime);
            String iString = String.valueOf(i);
            if (iString.endsWith("0")){
                csvfileWriter.append(String.valueOf(runningTime) + "," + String.valueOf(((System.currentTimeMillis() - start)-(long) (line.getMicrosecondPosition())/1000)));
                csvfileWriter.append("\n");
            }
            String fps = String.format("%.2f",webcam.getFPS());
            System.out.println("Current fps = " + fps);
            if(jButtonCapture.getModel().ispressed())
                {
                    break;
                }
        }
        writer.close();
        line.stop();
        line.close();

        csvfileWriter.flush();
        csvfileWriter.close();
    }
}

我做了一些挖掘并跟踪了延迟如何随时间演变:我将延迟定义为实际的“视频时间戳” - 实际的“音频时间戳” = ((System.currentTimeMillis() - start) - (long) (line. getMicrosecondPosition())/1000。然后我绘制了随时间变化的延迟值并得到了这些步骤(没有平滑曲线): Plot of delay values in time

我有几个与我的问题相关的问题

  1. 延迟从何而来?是不是因为不同的硬件有不同的内部时钟?
  2. 如何解释/理解时间延迟的阶梯图?
  3. 如何处理这个问题?一定有办法,周围有大量的视频/音频应用程序,没有明显的延迟。
  4. 我已经尝试过将音频作为曲目捕获到 mp4 文件中的替代方法(通过 writer.encodeAudio(...) 和音频样本) - 也出现了延迟

感谢您提供任何提示/答案。

解决方法

IDK,如果这有帮助与否,但我能够保持准确计时的唯一方法(例如,编写一个不错的节拍器,将音频与图形更新协调)是在最接近系统的点计算帧数.对于音频输出,这是在将字节输出到 SourceDataLine 的例程中。 write() 方法阻塞,因此是与系统消耗音频数据的速率最密切相关的点。这提供了我发现的最好的“实时”结果。

然后通过将帧数与帧速率相乘来计算时序。

鉴于 Java 的多线程(cpu 管理多个任务流),Java 不提供“实时保证”。 IDK,如果这是您遇到的滑点的来源。

我没有处理过视频,但我认为它也必须具有定义的帧速率。也许您可以创建一个工作单元,其中包含一组缓冲区维度,其中传入数据的音频和视频处理结合在一起?可以使用一种松散耦合和缓冲的形式将源数据提供给那个轭点,这样两个流的处理器时序的各个变幻莫测就不会相互干扰。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。