最近用javaCV的ffmpeg包的FFmpegFrameGrabber帧捕捉器对捕捉到的音频帧和视频帧做了同步的播放。采用的同步方法是视频向音频同步。
程序和源码
具体的思路如下:
(1)首先介绍ffmpeg是如何捕捉视频文件的图像和声音的
FFmpegFrameGrabber fg = new FFmpegFrameGrabber("a video file path or a url);
得到帧捕捉器对象后,调用它的grab()方法就会返回捕捉到的Frame对象。这个Frame可以是视频帧或者是音频帧,这是因为音视频帧时按照时间戳在播放时间先上排列的。当然捕捉到的帧都是已经译码过的,并且存储在java.nio.Buffer对象中,对于视频帧,Buffer是储存图像的像素数据比如RGB,然后通过
BufferedImage bi = (new Java2DFrameConverter()).getBufferedImage(f);
就可以得到图片,得到的图片可以进行一系列的处理或者不处理直接显示在swing组件上。对应音频帧,Buffer是储存音频的PCM数据,这个PCM可以是float或者short的,然后用java.sounds.sample里面的sourceDataLine.write方法就可以将这些音频PCM数据写入到扬声器中。
(2)接着介绍如何不断得将得到的帧播放出来。首先是单独播放视频:
while(true) { Frame f = fg.grab(); if(f.image!=null) label.setIcon(new ImageIcon((new Java2DFrameConverter()).getBufferedImage(f))); Thread.sleep(1000/视频帧率); }
单独播放音频同理,将数据写入到声卡即可。例子
(3)生产消费者模式。
上图是程序实现的方法,采用生产者模式将捕获到的帧进行判断,如果是视频帧就生产到视频FIFO中,如果是音频帧就生产到音频FIFO中,然后音频播放线程和视频播放线程分别从各自的帧仓库消费里面的帧。之所以采用生产消费者模式是因为帧捕获的速度是大于帧的消耗的,所以我们优先捕获帧来缓冲,或者进一步对捕获的帧进行预处理,而视频和音频播放线程只需要将处理过的帧直接播放显示即可。
(4)实现音视频同步的方法:播放两帧音频里面的所有视频帧。
想要实现音视频同步,必须要有帧的时间戳,这里捕获到的帧只有播放的时间戳PTS,没有译码时间戳DTS,所以我们只需要根据播放时间戳来决定播放即可。
程序的实现是根据上图来的, 当音频线程开始播放音频帧A1时,就调用视频线程的setRun方法,并且传递当前要播放的音频帧时间戳curTime和下一帧音频帧A2的时间戳nextTime给处于wait态的视频线程,然后视频线程启动,开始从视频FIFO中取出视频帧G1,然后计算G1和A1的时间差,作为播放的延时,Thread.sleep(t1)后,视频线程就将图片显示在swing组件上,比如JLabel.setIcon(image)。然后视频线程再取出一帧图像G2,比较G2的时间戳和A2的时间戳,如果G2时间戳小于A2,那么视频线程继续延时t2以后,播放这个G2图像,接着G3同理,直到取得G4,和A2比较发现G4时间戳大于A2,那么视频线程就进入wait态,等待下一次启动。然后音频线程播放完A1音频帧以后,就从仓库取出音频帧A3,然后将A2的时间戳和A3的时间戳传递给视频线程,然后开始播放A2,然后堵塞的视频线程同理继续播放。
(5)动态调节延时时间
由于个人PC都不是实时操作系统,也就是Thread.sleep是不精确的,并且受到声卡播放声音的制约,所以上面的基本实现思路是需要加以完善的。首先java的sourceDataLine的方法是依照一定的速度从内部缓冲区取出音频线程写入的数据,如果音频写入的数据被取光了,那么音频播放就会发生卡顿,但是如果一次音频数据写入过多,那么就会发生音视频可能就会不同步,所以要确保sourceDataLine的内部缓冲区是留有一定数据的,否则就会造成卡顿,但是数据量又不能过多,所以我们在G3到A2这段时间来进行声音播放的调节,由于延时的不精准性,写入的A1帧的数据可能时间还没满t6就可能被声卡取光了,所以在播放完G3图像以后,声音线程会判断根据sourceDataLine.available()返回的数据量进行判断,如果数据量快要完了,就减少G3到A2的延时时间t4。这样子就可以保证数据量是不会变为0造成声音卡顿。
(6)下面是程序在window64下测试和ubuntu14下测试的结果图: 播放是比较流畅的,同步也是可以的,但是开着播放比企鹅在IDE如IDEA中写代码的话,会卡,毕竟IDEA也是用java开发的,所以IDEA的运行会影响其他java程序,但是其他进程不会影响。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
我目前有问题,使我的音频和视频流保持同步。 以下是我正在使用的AVCodecContexts: 视频: 这演奏得很完美。 然而,当我对音频做同样的操作时,视频以慢动作播放,首先播放音频,然后在没有声音的情况下继续播放视频。 我找不到任何地方的例子如何设置PTS/DTS位置的视频/音频在MP4文件。任何帮助的例子都将是伟大的! 而且,我首先写视频帧,然后(一旦它们都写好了)我写音频。我已经用注释中建
使用ffmpeg concat连接多个文件似乎会导致音频的时间戳或偏移不匹配。我试过几个视频,发现H.264/MP4也有同样的问题。 使用并对视频进行编码似乎可以很好地工作。当ffmpeg执行全部转换计算时,音频保持同步,似乎可以得到所有正确的结果。 然而,简单地将视频级联而不进行任何转换或编码会导致同步问题缓慢增加。显然,对视频进行编码而不是简单地加入它们将导致信息/质量的损失,所以我宁愿找到一
我把很久以前在一次演讲中拍的一组电影连在一起。现在我要为每一个问题/答案剪掉它们。 我是这样做的。 ffmpeg-ss 00:00:34.7-t 00:10:44.6-y-i输入_movie.mp4-vcodec拷贝-acodec拷贝输出_1.mp4 ffmpeg-ss 00:11:22.2-y-i输入_movie.mp4-vcodec拷贝-acodec拷贝输出_2.mp4 然而,对于第二部分,我
大家好,我正在尝试使用两个独立的媒体播放器实例播放音频和视频文件。当我从一开始播放它时,它工作得很好。但当我寻找时,我可以看到音频和视频的延迟 这是我寻找音频和视频的代码 //sikAudio(msec); if(media播放器!=null) { // 没有直播流,如果(medialayer.get持续时间() }
我试图在一个单独的文件中每40毫秒生成一个视频和音频,并将其发送到云端进行直播,但创建的视频和音频无法使用ffplay播放。 命令: ffmpeg-f alsa-thread_queue_size1024-i hw: 0-f video o4linux2-i /dev/video0-c: a aac-ar48k-t 0:10-segment_time00:00.04-f段sample-3d.aac
当试图将source.mp4编码为多个比特率和大小以便自适应HLSv3回放时,所产生的音频和视频逐渐不同步。当擦洗到稍后的点,它似乎重置,并保持同步。 我正在使用最新的FFMPEG4.2.1通过自制-ffmpeg,使用libfdk-aac编解码器。注意,这也发生在FFMPEG4.1中。 音频和视频流很少,这会导致这个问题吗? 以下是源文件中的一些元数据: 和编码命令: