客户端实现语音的采样并发送,同时接收服务器发来的语音数据并播放

代码如下:

public class Audioclient
{@SuppressWarnings({ "unused", "resource", "static-access" })
public static void recordAndRecieveMicrophone(String INET_ADDRESS, int INET_PORT,int FRAME_RATE) throws InterruptedException, LineUnavailableException, UnknownHostException, IOException {Socket socket = new Socket(INET_ADDRESS, INET_PORT);AudioInfo audioInfo=new AudioInfo(new AudioFormat(44100.0F, 16, 2, true, false));audioInfo.writestart();if(socket.isConnected()) {//server端使用readline来获取唯一标识符String uuid = UUID.randomUUID().toString().replaceAll("-", "")+'\n';OutputStream outputstream=socket.getOutputStream();outputstream.write(uuid.getBytes());System.out.println(uuid);}new Thread(new Runnable() {@Overridepublic void run() {ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);exec.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {audioInfo.write(socket);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}, 0, (long) 1000 / FRAME_RATE, TimeUnit.MILLISECONDS);}}).start();AudioInfo audioRead=new AudioInfo(new AudioFormat(44100.0F, 16, 2, true, false));audioRead.readstart();new Thread(new Runnable() {@Overridepublic void run() {ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);exec.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {byte[] buffer=new byte[1024];InputStream is = null;try {is = socket.getInputStream();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}int size=0;while(true) {int ss1;try {is.read(buffer);audioRead.read(buffer);} catch (IOException e) {// TODO Auto-generated catch blockSystem.out.println("读取错误");}}}}, 0, (long) 1000 / FRAME_RATE, TimeUnit.MILLISECONDS);}}).start();
}
}

为方便后期更改添加编码封装方法,将语音的发送提取出来

public class AudioInfo {AudioFormat audioFormat;SourceDataLine line;ByteBuffer outBuffer;final int AUDIO_DEVICE_INDEX=4;byte[] audioBytes;TargetDataLine recordLine;public int length;public AudioInfo(AudioFormat audioFormat1) {this.audioFormat=audioFormat1;this.length=(int) (audioFormat.getSampleRate()*audioFormat.getChannels());}@SuppressWarnings("unused")public  void readstart() throws LineUnavailableException {final DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);final SourceDataLine soundLine = (SourceDataLine) AudioSystem.getLine(info);soundLine.open(audioFormat);soundLine.start();this.line=soundLine;   }public void writestart() throws LineUnavailableException {DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);TargetDataLine sline = (TargetDataLine) AudioSystem.getLine(dataLineInfo);sline.open(audioFormat);sline.start();this.recordLine=sline;this.audioBytes=new byte[this.length];}public void stop() {if(line!=null)line.close();if(recordLine!=null)recordLine.close();}public void read(byte[] samples) {outBuffer = ByteBuffer.wrap(samples);line.write(outBuffer.array(), 0, outBuffer.capacity());outBuffer.clear();}/** 把缓冲区中的音频数据通过套接字发出去*/public  void write(Socket s) throws IOException {int nBytesRead = recordLine.read(audioBytes, 0, recordLine.available());//16位音频格式,需要将byte[]转成short[]int nSamplesRead = nBytesRead / 2;short[] samples = new short[nSamplesRead];ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);OutputStream outputstream=s.getOutputStream();byte[] simples=new byte[nBytesRead];ByteBuffer.wrap(simples).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(sBuff.array());outputstream.write(simples);}
}

同时,要想实现排己性的语音转发,需要服务器拥有套接字的唯一标识

import java.net.Socket;public class OnlySocket{public  Socket socket;public  String uuid;public OnlySocket(Socket s, String uuid) {this.socket=s;this.uuid=uuid;}public Socket getSocket() {return socket;}public String getUUid() {return uuid;}
}

sever端

public class pushAndpoll {static int i=0;private static Set<OnlySocket> clientList=new HashSet<>();@SuppressWarnings({ "resource", "null" })public static void main(String[] args) throws IOException, LineUnavailableException {ExecutorService es=Executors.newFixedThreadPool(40);ServerSocket ss = null;try {ss=new ServerSocket(5000);} catch (IOException e) {// TODO Auto-generated catch blockSystem.out.println("服务器启动失败");}if(ss!=null) {System.out.println("服务器已启动");System.out.println(ss.getLocalSocketAddress());}while(true) {Socket s=ss.accept();if(s!=null) {System.out.println((++i)+"个客户端链接");InputStream inStream = s.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));String uuid=reader.readLine();OnlySocket onlysocket=new OnlySocket(s,uuid);       clientList.add(onlysocket);System.out.println(onlysocket.uuid);es.execute(receive(onlysocket));}}}@SuppressWarnings("unused")private static Runnable receive(final OnlySocket s) throws LineUnavailableException, UnknownHostException, IOException {AudioInfo audioInfo=new AudioInfo(new AudioFormat(44100.0F, 16, 2, true, false));return new Runnable() {@Overridepublic void run() {BufferedInputStream infile;BufferedOutputStream outfile;try {infile = new BufferedInputStream(s.socket.getInputStream());for(OnlySocket onlysocket:clientList) {if(onlysocket!=null && onlysocket.uuid !=s.uuid) {outfile = new BufferedOutputStream(onlysocket.socket.getOutputStream());byte[] b = new byte[1024];int i;i = infile.read(b);while (i!=-1) {outfile.write(b, 0, i);outfile.flush();i = infile.read(b);}outfile.flush();}}} catch (IOException e) {// TODO Auto-generated catch blocSystem.exit(1);}}};}
}

关于视频的推流编码封装拉流解码 在javacv的仓库上有很多demo,这里不再分享 ,其中有几点浅薄的经验想与大家分享,目前有三种主流流媒体服务协议,分别为rtmp,rtsp以及HLS。其中,我所用过rtmp以及HLS。

nginx加rtmp插件形成的流媒体服务器

支持直播流转HLS,rtmp点播,rtmp直播:其中根据我的多次尝试,如果在虚拟机建构服务器的前提下,通过内网穿透,有一定几率(直播的情况下)直流会直接断开,由于多次调试和查询无果,以至于一度放弃。在成功拉到流的前提下,(rtmp直播 仅开启live选项)延迟为6.7秒,或许是rtmp插件的缓存问题,但nginx的反向代理及负载均衡功能确实很强大。

SRS 简单的rtmp服务器

同样支持上述的功能,不过不存在,由于帧的丢失导致直流的断开情况,很稳定且快速,且为阿里的开源项目,中文文档全面。
RTMP协议下的拉流推流,由本地内网穿透到杭州的服务器,再穿透回来,时间约为3.4秒,与nginx-rtmp约两倍的差距。

尝试通过socket分别传输图像与语音,创建自己的私有协议

语音的编码与传输很快能够通过,然而到图像时,虽然能够拉到流,但是由于javacv的蹩脚,或许是我不够熟练,无法进行更一步的优化,在本地运行服务器,端口转发也会出现闪屏的现象,更别说网络服务器了。
查询调试后发现,原来我发的是一张张图片,也就是i帧,通过javacv的现有的编码器仅能发送推流给rtmp或者本地路径,于是放弃了。。。。后续打算通过c++深入底层,尝试实现私有协议的服务器。
RTMP本质上是将音频封装为flv,这也是flash能够播发rtmp链接的原因,发的是一小段一小段的flv。
HLS本质上是一个目录包含每一个切片,一个切片为一个短小的播放文件,自然播放文件越小,推流就越快,但如果过于小,就会导致解码时间比一个视频播放时间要长,可能会导致缓存区的覆盖,这时就需要设置好缓冲区。

关于文件以及黏包的现象,就可以进行切片,对于文件的中断传输与开始传输可以定义发送关键帧,以及通过MD5效验。

多人语言聊天以及多人视频聊天相关推荐

  1. 基于web视频聊天技术归纳

    本文转载于http://zhidao.baidu.com/link?url=brsWhD7CoFno5-Lojb-lpz7Vc8VeD6WPI_4Eh5cWnVdfYRUJoIGCyYCzO_J3tx ...

  2. 视频聊天网站的研究、发展以及趋势时间

    摘要: 此文讲述了视频聊天网站相关的技术.发展过程以及未来的发展趋势.我长时间从事外包业务开发和技术开发的,从客户那里了解到了很多的视频聊天网站相关的需求,经过自己长时间对视频聊天网站运营模式.盈利模 ...

  3. 【转帖】视频聊天网站的研究、发展以及趋势

    摘要: 此文讲述了视频聊天网站相关的技术.发展过程以及未来的发展趋势.我长时间从事外包业务开发和技术开发的,从客户那里了解到了很多的视频聊天网站相关的需求,经过自己长时间对视频聊天网站运营模式.盈利模 ...

  4. 视频聊天网站的技术与发展

    视频聊天网站的技术与发展 摘要: 此文讲述了视频聊天网站相关的技术.发展过程.从客户那里了解到了很多的视频聊天相关的需求,经过自己长时间对视频聊天网站运营模式.盈利模式.系统架构以及相关技术的研究,写 ...

  5. 视频聊天网站的研究、发展以及趋势

    摘要: 此文讲述了视频聊天网站相关的技术.发展过程以及未来的发展趋势.我长时间从事外包业务开发和技术开发的,从客户那里了解到了很多的视频聊天网站相关的需求,经过自己长时间对视频聊天网站运营模式.盈利模 ...

  6. 视频聊天网站的研究、发展以及趋势(转)

    此文讲述了视频聊天网站相关的技术.发展过程以及未来的发展趋势.我长时间从事外包业务开发和技术开发的,从客户那里了解到了很多的视频聊天网站相关的需求,经过自己长时间对视频聊天网站运营模式.盈利模式.系统 ...

  7. 类似51vv视频聊天室方案建设

    一.前言 1.类似新浪视频聊天室和呱呱视频聊天室,99CU等体现方式,聊天大厅和聊天房间能够自主切换. 2. 数据库最好运用MS SQL,代码模块化开拓,有充足的扩大性以及接口,二次开拓更简单. 3. ...

  8. 视频聊天软件的技术实现

    社会经济的发展使得人民生活水平不断提高,越来月注重精神享受.而科技的发展新媒体和自媒体的突飞猛进,给人们的生活带来了便利.网络视频聊天娱乐就是在这种蓬勃发展的互联网市场经济下的产物,也是现代更多草根通 ...

  9. 视频聊天开发参考资料

    本文阐述视频聊天网站相关的技术.发展过程以及未来的发展趋势.我长时间从事外包业务开发和技术开发的,从客户那里了解到了很多的视频聊天网站相关的需求,经过自己长时间对视频聊天网站运营模式.盈利模式.系统架 ...

  10. 【教程】使用腾讯云轻量应用服务器搭建Mirotalk,让自己拥有一个视频聊天、屏幕共享平台!

    前言 现在这个时代,个人不论小白还是大佬,搭建网站都有许多方法与平台,但是在服务器的选择上,当然是本文的主角:腾讯云轻量应用服务器更具性价比,为什么?下面准备工作一一为你介绍,所以今天,小俊继续给大家 ...

最新文章

  1. [置顶]       强大的jquery选择器
  2. C语言回调函数Demo - Win32版
  3. jQuery validate表单验证demo
  4. 实验计算机控制器的实验结论,计算机毕业论文控制器实验报告.doc
  5. 【转载】C#使用is关键字检查对象是否与给定类型兼容
  6. nginx同一域名下部署多个vue项目
  7. 拉普拉斯变换学习笔记
  8. MSDTC报错怎么破
  9. 计算机鼠标没有安装驱动,鼠标动不了怎么安装驱动_电脑鼠标不能动怎么安装驱动...
  10. 王阳明:志不立,天下无可成之事
  11. 1468. 计算税后工资
  12. 四种常用的ps抠图方法
  13. diskgenius linux 分区,DiskGenius怎么分区,DiskGenius分区教程
  14. ICPC 2018 焦作 C题 Supreme Command
  15. dellr710服务器(DellR710服务器做完raid安装系统找不到磁盘)
  16. 数字通信中为什么需要时钟线
  17. pyinstaller打包前后os.path.abspath(__file__)和os.path.realpath(sys.executable)的区别
  18. mysql is双竖线_MySQL 5.0 新特性教程 存储过程:第二讲
  19. CSS floats来创建三栏网页布局的方法
  20. android系统日志如何查看,Android如何查看系统recovery日志,从而找到系统程序、刷机异常…...

热门文章

  1. win10pe制作方法
  2. isNaN()与Number.isNaN()的区别
  3. CentOS7部署文件双向同步工具(unison)
  4. 目前android版本最好,当前主流的7个手机系统,你认为哪个最好用?
  5. 坐着打游戏也能减肥?1小时=200千卡,竞技游戏、解谜游戏效果更佳
  6. 大数据计算系统 Blink 在端侧的应用实践
  7. 微软面试100题(含全部答案)
  8. RSD 教程 —— §3.3 观察图像
  9. 苏州信访OA项目配置搭建
  10. 学习Typescript1(基础类型)