学习使用Java开发语言做计算机音频数据采集、压缩、转发功能,从而实现双向通话功能。采集数据频率为8KHz、16bit、单通道、小端格式,数据转发采用G711A压缩传输。
为了测试语音通话,音频采样率为8KHz即可满足要求。
为了记录学习流程,接下来仅介绍核心代码部分,具体完整程序参考源码
本项目主要的业务逻辑分为两个部分:一是音频采集部分,二是数据通讯转发和接收部分。为了保证两部分的业务松耦合,需要对通讯和采集播放做程序设计上分离。
为了保证底层通讯的和上层业务的松耦合,通讯部分做以下程序设计:
定义消息上层业务转发统一接口,需要音频采集处理部分传递该处理部分。
package com.david.test.socket;
import java.nio.ByteBuffer;
public interface SocketMessageHandler {
/**
* 获取消息
* @param byteBuffer
*/
public void onMessage(ByteBuffer byteBuffer);
}
该类定义一个底层通讯的Connector的一些对外统一接口。
package com.david.test.socket;
import java.nio.ByteBuffer;
public interface SocketConnector {
/**
* 获取Connector ID
* @return
*/
public String getId();
/**
* 启动一个客户端
* @throws Exception
*/
public void startClient(SocketConnectorParams socketConnectorParams,SocketMessageHandler socketMessageHandler) throws Exception;
/**
* 关闭客户端
* @throws Exception
*/
public void stopClient() throws Exception;
/**
* 发送消息
* @param byteBuffer
* @throws Exception
*/
public void sendMessage(ByteBuffer byteBuffer) throws Exception;
/**
* 连接是否存在
* @throws Exception
*/
public boolean isActive() throws Exception;
}
基于Mina实现的缺省的UDP通讯Connector,源码如下:
package com.david.test.socket.connector;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Date;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.proxy.utils.ByteUtilities;
import org.apache.mina.transport.socket.nio.NioDatagramConnector;
import org.springframework.util.StringUtils;
import com.david.test.socket.SocketConnector;
import com.david.test.socket.SocketConnectorParams;
import com.david.test.socket.SocketMessageHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class DefaultMinaUDPSocketConnector implements SocketConnector{
private IoConnector connector;
private IoSession ioSession;
private String connectorId;
@Override
public String getId() {
// TODO Auto-generated method stub
return this.connectorId;
}
@Override
public boolean isActive() throws Exception {
// TODO Auto-generated method stub
return null == ioSession?false:ioSession.isActive();
}
@Override
public void stopClient() throws Exception {
// TODO Auto-generated method stub
try {
if(null != ioSession){
ioSession.closeNow();
ioSession = null;
}
if(null != connector){
connector.dispose();
connector = null;
}
log.info("STOP UDP CLIENT SUCCESS, ID:{}",this.connectorId);
} catch (Exception e) {
log.error("Stop UDP Client Error", e);
}
}
@Override
public void startClient(SocketConnectorParams socketConnectorParams,SocketMessageHandler socketMessageHandler) throws Exception {
// TODO Auto-generated method stub
if(null == socketConnectorParams || StringUtils.isEmpty(socketConnectorParams.getHost())
|| socketConnectorParams.getPort() < 1){
throw new Exception("参数配置有误");
}
if(null == socketMessageHandler){
throw new Exception("Socket Message Handler 消息解析器配置异常");
}
//connector Id
this.connectorId = Thread.currentThread().getName()+"_"+new Date().getTime();
try {
InetSocketAddress inetSocketAddress = new InetSocketAddress(socketConnectorParams.getHost(), socketConnectorParams.getPort());
connector = new NioDatagramConnector();
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.setHandler(new MinaUDPIoHandler(socketConnectorParams,socketMessageHandler));
ConnectFuture connectFuture = connector.connect(inetSocketAddress);
// 等待是否连接成功,相当于是转异步执行为同步执行。
connectFuture.awaitUninterruptibly();
log.info("Mina start UDP Client SUCCESS, ID:{}",this.connectorId);
} catch (Exception e) {
log.error("Mina start UDP Client Error", e);
}
}
@Override
public void sendMessage(ByteBuffer byteBuffer) throws Exception {
// TODO Auto-generated method stub
if(null == ioSession){
throw new Exception("UDP session is released");
}
ioSession.write(IoBuffer.wrap(byteBuffer.array()));
log.info("ID:{},SOCKET SEND:{}",this.connectorId,ByteUtilities.asHex(byteBuffer.array()).toUpperCase());
}
// Mina UDP 数据处理
class MinaUDPIoHandler extends IoHandlerAdapter{
private SocketConnectorParams socketConnectorParams;
private SocketMessageHandler socketMessageHandler;
public MinaUDPIoHandler(SocketConnectorParams socketConnectorParams, SocketMessageHandler socketMessageHandler) {
this.socketConnectorParams = socketConnectorParams;
this.socketMessageHandler = socketMessageHandler;
}
@Override
public void sessionOpened(IoSession session) throws Exception {
// TODO Auto-generated method stub
super.sessionOpened(session);
ioSession = session;
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
// TODO Auto-generated method stub
super.sessionIdle(session, status);
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
// TODO Auto-generated method stub
super.exceptionCaught(session, cause);
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
// TODO Auto-generated method stub
super.messageReceived(session, message);
try {
IoBuffer ioBuffer = (IoBuffer)message;
int capacity = ioBuffer.capacity();
int limit = ioBuffer.limit();
byte[] data = new byte[limit];
ioBuffer.get(data, 0, limit);
log.info("RECV DATA: capacity[{}] limit[{}] data:{}",capacity,limit,ByteUtilities.asHex(data).toUpperCase());
if(null != this.socketMessageHandler){
this.socketMessageHandler.onMessage(ByteBuffer.wrap(data));
}
} catch (Exception e) {
log.error("Mina UDP RECV Message Error", e);
}
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {
// TODO Auto-generated method stub
super.messageSent(session, message);
}
}
}
统一管理系统中的所有的底层通讯Connector,提供服务。
package com.david.test.socket;
import java.util.LinkedList;
import java.util.List;
import com.david.test.socket.connector.DefaultMinaUDPSocketConnector;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SocketConnectorFactory {
private static SocketConnectorFactory socketConnectorFactory = new SocketConnectorFactory();
private static List<SocketConnector> socketConnectors = new LinkedList<>();
public static SocketConnectorFactory getInstance(){
return socketConnectorFactory;
}
/**
* 创建一个UDPClient
* @return
*/
public SocketConnector createUDPClient(){
SocketConnector socketConnector = new DefaultMinaUDPSocketConnector();
socketConnectors.add(socketConnector);
return socketConnector;
}
/**
* 启动一个UDPClient
* @param socketConnectorParams
* @param socketMessageHandler
* @return
* @throws Exception
*/
public SocketConnector startUDPClient(SocketConnectorParams socketConnectorParams,
SocketMessageHandler socketMessageHandler) throws Exception{
SocketConnector socketConnector = new DefaultMinaUDPSocketConnector();
socketConnector.startClient(socketConnectorParams, socketMessageHandler);
socketConnectors.add(socketConnector);
return socketConnector;
}
/**
* 根据Connector ID移除Socket连接
* @param id
* @throws Exception
*/
public void releaseSocketConnector(String id) throws Exception{
for(SocketConnector socketConnector:socketConnectors){
try {
if(socketConnector.getId().equals(id)){
socketConnector.stopClient();
socketConnectors.remove(socketConnector);
break;
}
} catch (Exception e) {
log.error("关闭客户端异常", e);
}
}
log.info("移除Socket监听器,剩余socket监听器数量:{}",socketConnectors.size());
}
/**
* 释放所有的socket连接
* @throws Exception
*/
public void releaseAllSocketConnectors() throws Exception{
List<SocketConnector> toRemovedSocketConnectors = new LinkedList<>();
for(SocketConnector socketConnector:socketConnectors){
try {
socketConnector.stopClient();
toRemovedSocketConnectors.add(socketConnector);
} catch (Exception e) {
log.error("关闭客户端异常", e);
}
}
socketConnectors.removeAll(toRemovedSocketConnectors);
log.info("移除Socket监听器数量:{},剩余socket监听器数量:{}",
toRemovedSocketConnectors.size(),socketConnectors.size());
}
}
以上1.1-1.4部分即完成了通讯服务的业务处理规则,此时还不涉及到业务开发,仅为通讯部分数据传输的统一标准。方便以后的底层通讯框架的切换等。
音频采集是一个持续的过程,需要在子线程中处理。整体的思路如下:采集转发在子线程中处理,接收播放在主线程中处理即可(因为接收播放是异步通讯框架传递过来的数据)
定义音频采集部分的规范,因为音频的采样率不同及压缩方式不同会存在不同的实现方式,定义统一的接口,便于扩展。
package com.david.test.audio;
import com.david.test.socket.SocketMessageHandler;
public interface UserAudioMedia extends SocketMessageHandler,Runnable{
/**
* 开始
*/
public void start();
/**
* 停止
*/
public void stop();
}
PCM原始音频流数据采集转发及接收播放
package com.david.test.audio.media;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import com.david.test.audio.UserAudioMedia;
import com.david.test.socket.SocketConnector;
import com.david.test.socket.SocketConnectorFactory;
import com.david.test.socket.SocketConnectorParams;
import com.david.test.utils.GsonUtils;
import lombok.extern.slf4j.Slf4j;
/**
* 传输:PCM传输
* 播放:PCM原始流播放
* @author 作者 :david E-mail:[email protected]
* @date 创建时间:2020年4月18日 上午10:24:37
* @version 1.0
* @since 2020年4月18日 上午10:24:37
*/
@Slf4j
public class DefaultUserAudioMedia implements UserAudioMedia{
private SocketConnectorParams socketConnectorParams;
private RegistParam registParam;
private SocketConnector socketConnector;
private boolean isStop;
private SourceDataLine sourceDataLine;
private TargetDataLine targetDataLine;
private Thread curThread = null;
public DefaultUserAudioMedia(SocketConnectorParams socketConnectorParams,RegistParam registParam) throws Exception{
this.socketConnectorParams = socketConnectorParams;
this.registParam = registParam;
this.socketConnector = SocketConnectorFactory.getInstance().startUDPClient(socketConnectorParams, this);
}
/**
* 停止
*/
@Override
public void stop(){
this.isStop = true;
}
/**
* 发起注册监听
* @param registParam
*/
@Override
public void start() {
// TODO Auto-generated method stub
try {
//发起注册
String paramStr = GsonUtils.toJson(registParam);
log.info("SocketConnector Status:{},Info:{},REGIST:{}",
this.socketConnector.isActive(),registParam.getContent(),paramStr);
this.socketConnector.sendMessage(ByteBuffer.wrap(paramStr.getBytes()));
//启动音频获取
curThread = new Thread(this);
curThread.start();
} catch (Exception e) {
log.error("发起注册请求异常", e);
}
}
@Override
public void onMessage(ByteBuffer byteBuffer) {
// TODO Auto-generated method stub
try {
if(null == sourceDataLine){
AudioFormat audioFormat = new AudioFormat(8000, 16, 1, true ,false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
//set voice
FloatControl fc = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
double value = 2;
float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
fc.setValue(dB);
}
//play
byte[] data = byteBuffer.array();
sourceDataLine.write(data, 0, data.length);
} catch (Exception e) {
log.error("播放音频异常", e);
}
}
@Override
public void run() {
AudioInputStream audioInputStream = null;
try {
AudioFormat audioFormat = new AudioFormat(8000, 16, 1, true ,false);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
targetDataLine.open(audioFormat);
targetDataLine.start();
audioInputStream = new AudioInputStream(targetDataLine);
int len = 0;
byte[] buffer = new byte[1024];
while(!isStop && -1 != (len = audioInputStream.read(buffer))){
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer,0,len);
this.socketConnector.sendMessage(byteBuffer);
}
} catch (Exception e) {
log.error("采集音频异常",e);
} finally {
if(null != sourceDataLine){
sourceDataLine.close();
sourceDataLine.stop();
}
if(null != audioInputStream){
try {
audioInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != targetDataLine){
targetDataLine.close();
targetDataLine.stop();
}
if(null != socketConnector){
try {
socketConnector.stopClient();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("UserAudioMedia [socketConnectorParams=");
builder.append(socketConnectorParams);
builder.append(", registParam=");
builder.append(registParam);
builder.append(", socketConnector=");
builder.append(socketConnector);
builder.append(", isStop=");
builder.append(isStop);
builder.append("]");
return builder.toString();
}
}
基于G711A编码的PCM音频流数据采集转发及播放,G711A的音频数据压缩率在50%,传输播放效率较好。
package com.david.test.audio.media;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import com.david.test.audio.UserAudioMedia;
import com.david.test.socket.SocketConnector;
import com.david.test.socket.SocketConnectorFactory;
import com.david.test.socket.SocketConnectorParams;
import com.david.test.utils.G711Code;
import com.david.test.utils.GsonUtils;
import lombok.extern.slf4j.Slf4j;
/**
* 传输:PCM流压缩转为G711A传输
* 播放:G711A流转为PCM原始流播放
* @author 作者 :david E-mail:[email protected]
* @date 创建时间:2020年4月18日 上午10:22:53
* @version 1.0
* @since 2020年4月18日 上午10:22:53
*/
@Slf4j
public class G711AUserAudioMedia implements UserAudioMedia{
private SocketConnectorParams socketConnectorParams;
private RegistParam registParam;
private SocketConnector socketConnector;
private boolean isStop;
private SourceDataLine sourceDataLine;
private TargetDataLine targetDataLine;
private Thread curThread = null;
public G711AUserAudioMedia(SocketConnectorParams socketConnectorParams,RegistParam registParam) throws Exception{
this.socketConnectorParams = socketConnectorParams;
this.registParam = registParam;
this.socketConnector = SocketConnectorFactory.getInstance().startUDPClient(socketConnectorParams, this);
}
/**
* 停止
*/
@Override
public void stop(){
this.isStop = true;
}
/**
* 发起注册监听
* @param registParam
*/
@Override
public void start() {
// TODO Auto-generated method stub
try {
//发起注册
String paramStr = GsonUtils.toJson(registParam);
log.info("SocketConnector Status:{},Info:{},REGIST:{}",
this.socketConnector.isActive(),registParam.getContent(),paramStr);
this.socketConnector.sendMessage(ByteBuffer.wrap(paramStr.getBytes()));
//启动音频获取
curThread = new Thread(this);
curThread.start();
} catch (Exception e) {
log.error("发起注册请求异常", e);
}
}
@Override
public void onMessage(ByteBuffer byteBuffer) {
// TODO Auto-generated method stub
try {
if(null == sourceDataLine){
AudioFormat audioFormat = new AudioFormat(8000, 16, 1, true ,false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(info);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
//set voice
FloatControl fc = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
double value = 2;
float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
fc.setValue(dB);
}
//play
byte[] data = byteBuffer.array();
//g711a -> pcm
short[] pcm = new short[data.length];
G711Code.G711aDecoder(pcm, data, data.length);
byte[] orginPcm = new byte[data.length*2];
ByteBuffer.wrap(orginPcm).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(pcm);
sourceDataLine.write(orginPcm, 0, orginPcm.length);
} catch (Exception e) {
log.error("播放音频异常", e);
}
}
@Override
public void run() {
AudioInputStream audioInputStream = null;
try {
AudioFormat audioFormat = new AudioFormat(8000, 16, 1, true ,false);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
targetDataLine.open(audioFormat);
targetDataLine.start();
audioInputStream = new AudioInputStream(targetDataLine);
int len = 0;
byte[] buffer = new byte[1024];
while(!isStop && -1 != (len = audioInputStream.read(buffer))){
//pcm -> g711a
short[] shortBytes = new short[len/2];
ByteBuffer.wrap(buffer,0,len)
.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()
.get(shortBytes);
byte[] g711a = new byte[shortBytes.length];
G711Code.G711aEncoder(shortBytes, g711a, shortBytes.length);
this.socketConnector.sendMessage(ByteBuffer.wrap(g711a));
}
} catch (Exception e) {
log.error("采集音频异常",e);
} finally {
if(null != sourceDataLine){
sourceDataLine.close();
sourceDataLine.stop();
}
if(null != audioInputStream){
try {
audioInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != targetDataLine){
targetDataLine.close();
targetDataLine.stop();
}
if(null != socketConnector){
try {
socketConnector.stopClient();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("UserAudioMedia [socketConnectorParams=");
builder.append(socketConnectorParams);
builder.append(", registParam=");
builder.append(registParam);
builder.append(", socketConnector=");
builder.append(socketConnector);
builder.append(", isStop=");
builder.append(isStop);
builder.append("]");
return builder.toString();
}
}
对外提供统一的业务接口管理服务,设计为单例模式,用于控制服务的音频采集实例。提供创建一个音频采集实例(默认为G711A)及停止采集等。
package com.david.test.audio;
import java.util.LinkedList;
import java.util.List;
import com.david.test.audio.media.G711AUserAudioMedia;
import com.david.test.audio.media.RegistParam;
import com.david.test.socket.SocketConnectorParams;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AudioControl {
private static AudioControl instance = new AudioControl();
private static List<UserAudioMedia> userAudioMedias = new LinkedList<UserAudioMedia>();
public static AudioControl getInstance(){
return instance;
}
/**
* 获取启动的设备信息
* @return
*/
public List<String> getUserAudioMedias() {
List<String> infos = new LinkedList<>();
for(UserAudioMedia userAudioMedia:userAudioMedias){
infos.add(userAudioMedia.toString());
}
return infos;
}
/**
* 清除数据
*/
public void stopUserAudioMedias(){
for(UserAudioMedia userAudioMedia:userAudioMedias){
userAudioMedia.stop();
}
userAudioMedias.clear();
}
/**
*
* @return
*/
public void startUserAudioMedia(SocketConnectorParams socketConnectorParams,RegistParam registParam) throws Exception{
UserAudioMedia userAudioMedia = new G711AUserAudioMedia(socketConnectorParams,registParam);
userAudioMedia.start();
userAudioMedias.add(userAudioMedia);
}
}
提供简单的web接口启动服务、关闭服务等。
package com.david.test.web;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.david.test.audio.AudioControl;
import com.david.test.audio.media.RegistParam;
import com.david.test.audio.media.RegistParamClientContent;
import com.david.test.audio.media.RegistParamDeviceContent;
import com.david.test.socket.SocketConnectorParams;
import com.david.test.web.common.WebRetCodes;
import com.david.test.web.common.WebRetUtils;
/**
* 通话web接口
* @author 作者 :david E-mail:[email protected]
* @date 创建时间:2020年4月16日 下午4:57:05
* @version 1.0
* @since 2020年4月16日 下午4:57:05
* --start
* http://localhost/start?host=192.168.0.80&port=9898&company=&lineCode=&binddevice=123&ignoredevices=
*
* --startDevice
* http://localhost/startDevice?host=192.168.0.80&port=9898&company=&lineCode=&id=123&password=
*
* --stop
* http://localhost/stop
*
* --connectors
* http://localhost/connectors
*/
@RestController
public class AudioToolWeb {
/**
* PC端开启通话
* @param request
* @param host
* @param port
* @param company
* @param lineCode
* @param binddevice
* @param ignoredevices
* @return
* @throws Exception
*/
@RequestMapping(value = "start", method = { RequestMethod.GET, RequestMethod.POST })
public @ResponseBody Map<String, ? extends Object> start(HttpServletRequest request,
@RequestParam String host,@RequestParam Integer port,@RequestParam String company,
@RequestParam String lineCode,@RequestParam String binddevice,@RequestParam String ignoredevices)
throws Exception {
try {
SocketConnectorParams socketConnectorParams = new SocketConnectorParams();
socketConnectorParams.setHost(host);
socketConnectorParams.setPort(port);
RegistParam registParam = new RegistParam();
RegistParamClientContent content = new RegistParamClientContent();
content.setCompany(company);
content.setLineCode(lineCode);
content.setBinddevice(binddevice);
content.setIgnoredevices(ignoredevices);
registParam.setContent(content);
AudioControl.getInstance().startUserAudioMedia(socketConnectorParams,registParam);
return WebRetUtils.mapRet(WebRetCodes.OK);
} catch (Exception e) {
return WebRetUtils.mapRet(WebRetCodes.INNER_ERROR);
}
}
/**
* 设备开启通话
* @param request
* @param host
* @param port
* @param company
* @param lineCode
* @param id
* @param password
* @return
* @throws Exception
*/
@RequestMapping(value = "startDevice", method = { RequestMethod.GET, RequestMethod.POST })
public @ResponseBody Map<String, ? extends Object> startDevice(HttpServletRequest request,
@RequestParam String host,@RequestParam Integer port,@RequestParam String company,
@RequestParam String lineCode,@RequestParam String id,@RequestParam String password)
throws Exception {
try {
SocketConnectorParams socketConnectorParams = new SocketConnectorParams();
socketConnectorParams.setHost(host);
socketConnectorParams.setPort(port);
RegistParam registParam = new RegistParam();
RegistParamDeviceContent content = new RegistParamDeviceContent();
content.setCompany(company);
content.setLineCode(lineCode);
content.setPassword(password);
content.setId(id);
registParam.setContent(content);
AudioControl.getInstance().startUserAudioMedia(socketConnectorParams,registParam);
return WebRetUtils.mapRet(WebRetCodes.OK);
} catch (Exception e) {
return WebRetUtils.mapRet(WebRetCodes.INNER_ERROR);
}
}
/**
* 停止所有通话
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value = "stop", method = { RequestMethod.GET, RequestMethod.POST })
public @ResponseBody Map<String, ? extends Object> stop(HttpServletRequest request)
throws Exception {
try {
AudioControl.getInstance().stopUserAudioMedias();
return WebRetUtils.mapRet(WebRetCodes.OK);
} catch (Exception e) {
return WebRetUtils.mapRet(WebRetCodes.INNER_ERROR);
}
}
/**
* 所有通话Socket连接
* @param request
* @return
* @throws Exception
*/
@RequestMapping(value = "connectors", method = { RequestMethod.GET, RequestMethod.POST })
public @ResponseBody Map<String, ? extends Object> connectors(HttpServletRequest request)
throws Exception {
try {
return WebRetUtils.mapRet(WebRetCodes.OK,AudioControl.getInstance().getUserAudioMedias());
} catch (Exception e) {
return WebRetUtils.mapRet(WebRetCodes.INNER_ERROR);
}
}
}
本次因项目需要做了音频采集播放相关的测试项目,同时也对相关的业务做了封装处理,便于扩展等,仅供学习参考!项目源码
参考服务转发网关
本项目仅为客户端程序,为了项目完整运行还需要有个转发的Server,如需可私聊