由于开发摄像头项目用到了wvp这个框架,但是目前还未集成语音广播功能,所以我就自己写死了一个,不影响视频的预览和上下左右移动,其他影响未知,说步骤
分为2个部分,第一部分贴代码,第二部分就是说明原理
/**
* 打开音频通道
*
* @param device 视频设备
*/
@Override
public boolean openCameraVoice(Device device,String channelId) {
try {
StringBuffer catalogXml = new StringBuffer(400);
catalogXml.append("<?xml version=\"1.0\"?>\r\n");
catalogXml.append("<Notify>\r\n");
catalogXml.append("<CmdType>Broadcast</CmdType>\r\n");
catalogXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
catalogXml.append("<SourceID>" + device.getDeviceId() + "</SourceID>\r\n");
catalogXml.append("<TargetID>" + channelId + "</TargetID>\r\n");
catalogXml.append("</Notify>\r\n");
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), "ViaRecordInfoBranch", "FromRecordInfoTag", null);
transmitRequest(device, request, null);
} catch (SipException | ParseException | InvalidArgumentException e) {
e.printStackTrace();
return false;
}
return true;
}
/***
* 回复200 OK
* @param evt
* @throws SipException
* @throws InvalidArgumentException
* @throws ParseException
*/
private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
String content0=evt.getRequest().toString().replaceAll("(\r\n|\n)","<br/>");
String content1[]=content0.split("<br/>");
HashMap<String,String> mapContent=new HashMap<>();
String channeld="";
for(int i=0;i<content1.length;i++){
if(content1[i].length()>2){
if("=".equals(content1[i].substring(1,2))){
String content2[]=content1[i].split("=");
mapContent.put(content2[0],content2[1]);
}else{
if(content1[i].startsWith("From")){
String content2[]=content1[i].split(":");
mapContent.put("From",content2[2]);
}else if(content1[i].startsWith("To")){
String content2[]=content1[i].split(":");
mapContent.put("To",content2[2]);
}else if(content1[i].startsWith("Subject")){
channeld=content1[i].split(",")[1].split(":")[0];
}
}
}else{
continue;
}
}
String to[]=mapContent.get("To").split("@");
String from[]=mapContent.get("From").split("@");
String deviceId=from[0];
//保存port到redis
String m=mapContent.get("m").substring(6,11).replaceAll(" ","");
portAudio=m;
String portAudio0=m;
// 添加Contact头
Address concatAddress = SipFactory.getInstance().createAddressFactory().
createAddress(SipFactory.getInstance().createAddressFactory().
createSipURI(to[0], to[1]+":"+"5060"));
response.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
response.addHeader(evt.getRequest().getHeader(ContentType.NAME));
//response.addHeader(evt.getRequest().getHeader(UserAgent.NAME));
ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("application", "SDP");
Random random = new Random();
int random0=(int)Math.floor((random.nextDouble()*10000.0))+10000;
String portWvp=String.valueOf(random0);
StringBuffer contentEnd=new StringBuffer("v=0\r\n")
.append("o="+to[0]+" 0 0 IN IP4 "+to[1]+"\r\n")
.append("s=Play\r\n")
.append("c=IN IP4 "+to[1]+"\r\n")
.append("t=0 0\r\n")
.append("m=audio "+portWvp+" RTP/AVP 8\r\n")
.append("a=rtpmap:8 PCMA/"+portWvp+"\r\n")
.append("a=sendonly\r\n")
.append("y="+mapContent.get("y")+"\r\n")
.append("f="+mapContent.get("f").replaceAll("0","")+"\r\n");
logger.info("content返回结果:"+contentEnd);
IVideoManagerStorager videoManagerStorager=new VideoManagerRedisStoragerImpl();
logger.info("wvp往jeecgboot项目发送修改端口命令deviceId="+deviceId+",channeld="+channeld+",portAudio0="+portAudio0);
videoManagerStorager.updateAudioPort(deviceId,channeld,portAudio0,portWvp);
response.setContent(contentEnd,contentTypeHeader);
getServerTransaction(evt).sendResponse(response);
}
前端收到的mp3文件转g711a文件
/**
* MP3转换PCMA文件方法
*
* @param mp3filePath 原始文件路径
* @param pcmFilePath 转换文件的保存路径
*/
public static boolean mp32pcma(String mp3filePath, String pcmFilePath) {
// String command = "ffmpeg -y -i mp3filePath -acodec pcm_s16le -f s16le -ac 1 -ar 16000 pcmFilePath";
try {
String command1 = "ffmpeg -i ";
String command2 = " -acodec pcm_alaw -f alaw -ac 1 -ar 8000 ";
Runtime runtime = Runtime.getRuntime();
Process exec = runtime.exec(command1 + mp3filePath + command2 + pcmFilePath);
exec.waitFor();
exec.destroy();
return true;
} catch (Exception e) {
System.out.println("MP3转换PCM文件 失败");
}
return false;
}
发送rtp文件
public static void sendRtp(int localPort,String cameraIP,int port,String audioFile) throws IOException {
SendRtp sendRtp=new SendRtp();
sendRtp.startKillPort(port);
long ssrc = 255;
int send_interval_ms = 50;
int audio_need_len = 400;
int peerPort = port;
DatagramSocket ds = null;
InetAddress peerAddress = null;
InputStream audio_stream = new FileInputStream(audioFile);
//ByteArrayInputStream audio_stream = new ByteArrayInputStream(audioFile.getBytes());
//InputStream audio_stream = new FileInputStream(audioFile);
try {
ds = new DatagramSocket(localPort);
} catch (SocketException e) {
e.printStackTrace();
System.exit(1);
}
try {
peerAddress = InetAddress.getByName(cameraIP);
} catch (UnknownHostException e) {
e.printStackTrace();
System.exit(1);
}
RtpPacket rtp = new RtpPacket(ssrc);
byte[] audio_data = new byte[audio_need_len];
int i=0;
while (audio_stream.read(audio_data) == audio_need_len) {
byte[] payload = rtp.packet_g711a(audio_data, audio_data.length);
DatagramPacket dp = new DatagramPacket(payload, payload.length, peerAddress, peerPort);
ds.send(dp);
System.out.println(Arrays.toString(payload));
System.out.println("发送"+i+"消息-----------------");
i++;
try {
Thread.sleep(send_interval_ms);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
ds.close();
}
以上的步骤图片
所用工具
有关资料下载
wvp-GB28181集成语音广播demo.rar
https://pan.baidu.com/s/171hz_z8yeyV5miJUpBHNDA gbk2
wvp集成语音广播信令记录Report.pdf
https://pan.baidu.com/s/1gttyaF8xTwnnY9YT7vx-ZA gbk2
GB28181-2016 IPC模拟设备 自动化测试工具.rar