Javacv使用ffmpeg实现音视频同步播放

最近用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程序,但是其他进程不会影响。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java基础知识杂文

    1.基本概念 IO是主存和外部设备(硬盘.终端和网络等)拷贝数据的过程.IO是操作系统的底层功能实现,底层通过I/O指令进行完成. 所有语言运行时系统提供执行I/O较高级别的工具.(c的printfscanf,java的面向对象封装) 2.Java标准io回顾 Java标准IO类库是io面向对象的一种抽象.基于本地方法的底层实现,我们无须关注底层实现.InputStream\OutputStream(字节流):一次传送一个字节.Reader\Writer(字符流):一次一个字符. 3.nio简介

  • java IO流读取图片供前台显示代码分享

    最近项目中需要用到IO流来读取图片以提供前台页面展示,由于以前一直是用url路径的方式进行图片展示,一听说要项目要用IO流读取图片感觉好复杂一样,但任务下达下来了,做为程序员只有选择去执行喽,于是找了点资料看了会api, 嘿感觉挺简单的,由于是第一次采用IO流的方式进行读取图片供页面显示,所以把以下代码记录一下 后台代码: /** * IO流读取图片 by:long * @return */ @RequestMapping(value = "/IoReadImage/{imgName}"

  • java语言描述Redis分布式锁的正确实现方式

    分布式锁一般有三种实现方式:1.数据库乐观锁:2.基于Redis的分布式锁:3.基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性.在任意时刻,只有一个客户端能持有锁. 不会发生死锁.即使有一个客户端在持有锁的期间

  • java的IO流详细解读

    流,就是一系列的数据. 当不同介质之间有数据交互的时候,JAVA就使用流来实现.数据源可以是文件,还可以是数据库.网络甚至其他的程序. 比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流. 字节流(以字节的形式读取和写入数据) InputStream字节输入流同时也是抽象类,只提供方法声明,不提供方法的具体实现. FileInputStream是InputStream的子类,下面以FileInputStream为例进行文件读取 package testIO; import java.i

  • java通过JFrame做一个登录系统的界面完整代码示例

    在java的JFrame内通过创建匿名对象的方式做登录界面 package com.sxt; import java.awt.Container; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.J

  • Java文件读写IO/NIO及性能比较详细代码及总结

    干Java这么久,一直在做WEB相关的项目,一些基础类差不多都已经忘记.经常想得捡起,但总是因为一些原因,不能如愿. 其实不是没有时间,只是有些时候疲于总结,今得空,下定决心将丢掉的都给捡起来. 文件读写是一个在项目中经常遇到的工作,有些时候是因为维护,有些时候是新功能开发.我们的任务总是很重,工作节奏很快,快到我们不能停下脚步去总结. 文件读写有以下几种常用的方法 1.字节读写(InputStream/OutputStream) 2.字符读取(FileReader/FileWriter) 3.

  • java中的arrays.sort()代码详解

    Arrays.sort(T[], Comparator < ? super T > c) 方法用于对象数组按用户自定义规则排序. 官方Java文档只是简要描述此方法的作用,并未进行详细的介绍,本文将深入解析此方法. 1. 简单示例 sort方法的使用非常的简单明了,下面的例子中,先定义一个比较Dog大小的Comparator,然后将其实例对象作为参数传给sort方法,通过此示例,你应该能够快速掌握Arrays.sort()的使用方法. import java.util.Arrays; impo

  • java编程进行动态编译加载代码分享

    简述 该类使用javax.tools.ToolProvider自带的JavaCompiler进行编译,使用IO的File及NIO的Files进行对应的路径创建.读取及拷贝,使用正则表达式进行包名与目录的转换,我只是将这些东西做了个容错整合,没什么技术含量,就为个方便吧. 模块API class DynamicReactor://空参构造 public Class<?> dynamicCompile(String srcPath);//输入一个指定的源文件路径,若编译.拷贝成功则返回该类对应的C

  • Javacv使用ffmpeg实现音视频同步播放

    最近用javaCV的ffmpeg包的FFmpegFrameGrabber帧捕捉器对捕捉到的音频帧和视频帧做了同步的播放.采用的同步方法是视频向音频同步. 程序和源码 具体的思路如下: (1)首先介绍ffmpeg是如何捕捉视频文件的图像和声音的 FFmpegFrameGrabber fg = new FFmpegFrameGrabber("a video file path or a url); 得到帧捕捉器对象后,调用它的grab()方法就会返回捕捉到的Frame对象.这个Frame可以是视频帧

  • Qt利用ffmpeg实现音视频同步

    目录 一.前言 二.效果图 三.体验地址 四.相关代码 五.功能特点 5.1 基础功能 5.2 特色功能 5.3 视频控件 5.4 内核ffmpeg 一.前言 用ffmpeg来做音视频同步,个人认为这个是ffmpeg基础处理中最难的一个,无数人就卡在这里,怎么也不准,本人也是尝试过网上各种demo,基本上都是渣渣,要么仅仅支持极其少量的视频文件比如收到的数据包是一帧视频一帧音频的,要么根本没法同步歪七八糟的,要么进度跳过去直接蹦蹦蹦崩溃的,其实最完美的音视频同步处理demo就是ffplay,我亲

  • C# 调用FFmpeg处理音视频的示例

    FFmpeg 开源.跨平台.体积小.功能强大,提供了录制.转换以及流化音视频的完整解决方案. 官网:https://www.ffmpeg.org/ 百科:https://baike.baidu.com/item/ffmpeg/2665727?fr=aladdin FFmpeg 应用非常广泛,可以用来播放本地视频甚至网络视频,查看音视频信息,还可以用于从视频中提取音频,转换音视频文件格式等等,本文主要介绍如何调用 FFmpeg 来查看音视频信息.从视频中提取音频.转换音视频格式等. 1. 调用FF

  • C语言结合ffmpeg打印音视频信息

    目录 一.通过此文可以得到什么 二.实现思路 三.实现效果 四.实现源代码  一.通过此文可以得到什么 通过此练习: 1.知道了如何计算一个音频和视频的播放时间: 2.知道了音视频解码的思路的大体流程,之后无非就是在这个流程上进行扩充细节: 3.知道了如何通过C语言或者C++编程语言结合ffmpeg拿到一些音视频的关键信息,例如:帧率等: 二.实现思路 三.实现效果 zhenghui@zh-pc:/data/project/VSCProject/ffmpegStudy$ make make al

  • android采用FFmpeg实现音视频合成与分离

    上一篇文章谈到音频剪切.混音.拼接与转码,也详细介绍cMake配置与涉及FFmpeg文件的导入: android端采用FFmpeg进行音频混合与拼接剪切.现在接着探讨音视频的合成与分离. 1.音频提取 从多媒体文件中提取音频,关键命令为"-acodec copy -vn",其中"-acodec copy"是采用音频编码器拷贝音频流,"-vn"是去掉video视频流: /** * 使用ffmpeg命令行进行抽取音频 * @param srcFile

  • C#调用FFmpeg操作音视频的实现示例

    目录 项目背景 FFmpeg介绍 FFmpeg相关教程 博客示例源码 下载FFmpeg.exe安装包 C#进程调用FFmpeg操作音视频 项目背景 因为公司需要对音视频做一些操作,比如说对系统用户的发音和背景视频进行合成,以及对多个音视频之间进行合成,还有就是在指定的源背景音频中按照对应的规则在视频的多少秒钟内插入一段客户发音等一些复杂的音视频操作.本篇文章主要讲解的是使用C#进程(Process)调用FFmpeg.exe进行视频合并,音频合并,音频与视频合并成视频这几个简单的音视频操作,还有些

  • php使用FFmpeg接口获取视频的播放时长、码率、缩略图以及创建时间

    FFmpeg是一个视频插件,我们可以利用调用FFmpeg接口来获取视频的相关信息,包括视频的播放时长,视频的码率,视频的缩略图以及视频创建时间,本文章向大家介绍php如何使用FFmpeg接口获取视频信息,需要的朋友可以参考一下. FFmpeg获得视频文件的缩略图: function getVideoCover($file,$time,$name) { if(empty($time))$time = '1';//默认截取第一秒第一帧 $strlen = strlen($file); // $vid

  • Qt音视频开发之利用ffmpeg实现倍速播放

    目录 一.前言 二.功能特点 2.1 基础功能 2.2 特色功能 2.3 视频控件 2.4 内核ffmpeg 三.体验地址 四.效果图 五.相关代码 一.前言 用ffmpeg做倍速播放,是好多年都一直没有实现的功能,有个做法是根据倍速参数,不断切换播放位置,实现效果不是很好,ffplay中的倍速就做得很好,而且声音无论倍速多少还非常柔和,有特别的降噪处理啥的,ffplay中的倍速使用的滤镜去实现,并动态调整pts/dts的值,整个处理过程看起来比较复杂,想着有没有稍微简单一点的办法,在经过一个朋

  • C# 使用SDL2实现Mp4文件播放音视频操作

    播放音视频的关键:视频的格式是H264,音频的格式是AAC.使用ffmpeg探测流的方式来实现音视频流的解码播放. 数据处理逻辑:H264->YUV AAC->PCM. SDL2工具类 using SDL2; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threadi

  • 浅谈音视频 pts dts基本概念及理解

    目录 I.P.B 帧 DTS.PTS 的概念 音视频的同步 PTS和DTS的时间基 视频的播放过程可以简单理解为一帧一帧的画面按照时间顺序呈现出来的过程,就像在一个本子的每一页画上画,然后快速翻动的感觉.但是在实际应用中,并不是每一帧都是完整的画面,因为如果每一帧画面都是完整的图片,那么一个视频的体积就会很大,这样对于网络传输或者视频数据存储来说成本太高,所以通常会对视频流中的一部分画面进行压缩(编码)处理.由于压缩处理的方式不同,视频中的画面帧就分为了不同的类别,其中包括:I 帧.P 帧.B

随机推荐