diff --git a/src/main/java/com/inspect/tcpserver/sip/service/SipClientService.java b/src/main/java/com/inspect/tcpserver/sip/service/SipClientService.java
index 7bd0139..d247102 100644
--- a/src/main/java/com/inspect/tcpserver/sip/service/SipClientService.java
+++ b/src/main/java/com/inspect/tcpserver/sip/service/SipClientService.java
@@ -20,6 +20,7 @@ import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.io.BufferedReader;
+import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.*;
@@ -777,6 +778,14 @@ public class SipClientService implements SipListener {
return;
}
+ // 200 OK -> SIP Server
+ ServerTransaction st = requestEvent.getServerTransaction();
+ if (st == null) {
+ st = sipProvider.getNewServerTransaction(request);
+ }
+ Response ok = messageFactory.createResponse(Response.OK, request);
+ st.sendResponse(ok);
+
SipXmlEnvelope> envelope = SipXmlParser.parse(xml, itemClass);
//handleSipEvent(envelope);
}
@@ -804,20 +813,29 @@ public class SipClientService implements SipListener {
ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(ExpiresHeader.NAME);
int expires = (expiresHeader != null) ? expiresHeader.getExpires() : 0;
log.info("Received SUBSCRIBE expires: {}, XML:\n{}", expires, xml);
+ String eventType = SipXmlParser.peekEventType(xml);
+ log.info("SUBSCRIBE EventType: {}", eventType);
ServerTransaction transaction = sipProvider.getNewServerTransaction(request);
+ // 对应协议B.9.1.2 F2 前端系统返回200 OK响应,指示已经接受订阅请求
Response response = messageFactory.createResponse(Response.OK, request);
response.setExpires(expiresHeader);
transaction.sendResponse(response);
if (expires > 0) {
saveSubscription(request, expires);
+ // 对应协议B.9.1.2 F3 SIP客户端(前端系统)发送没有消息体的NOTIFY给平台,其中Subscription-State头部字段值为active, 指示订阅关系建立。
sendInitialNotify(request, transaction, "initial");
} else {
removeSubscription(request);
}
+ } else if (Request.INVITE.equals(request.getMethod())) {
+ handleInvite(requestEvent);
+ } else if(Request.ACK.equals(request.getMethod())) {
+ // RFC 3261: The ACK request does not generate a response.
+ // ACK 是“事务内请求”,不是普通 SIP 方法,不生成任何 Response,ACK 是“事务内请求”,不是普通 SIP 方法
+ startRtspToRtp();
}
else {
-
Response notImpl = messageFactory.createResponse(Response.NOT_IMPLEMENTED, request);
requestEvent.getServerTransaction().sendResponse(notImpl);
log.info("Method {} not implemented, replied 501", method);
@@ -833,6 +851,100 @@ public class SipClientService implements SipListener {
}
}
+ private Process ffmpegProcess;
+
+ private void startRtspToRtp() {
+ try {
+ String cmd =
+ "ffmpeg -rtsp_transport tcp " +
+ "-i \"rtsp://admin:wd19216811@192.168.1.244:554/h264/ch1/sub/av_stream\" " +
+ "-an -vcodec copy " +
+ "-f rtp -payload_type 96 " +
+ "\"rtp://192.168.1.116:50000?tcp\"";
+
+ ProcessBuilder pb = new ProcessBuilder(
+ "bash", "-c", cmd
+ );
+ pb.redirectErrorStream(true);
+
+ ffmpegProcess = pb.start();
+
+ new Thread(() -> {
+ try (BufferedReader br =
+ new BufferedReader(new InputStreamReader(
+ ffmpegProcess.getInputStream()))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ System.out.println("[FFmpeg] " + line);
+ }
+ } catch (IOException ignored) {}
+ }).start();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ private void handleInvite(RequestEvent requestEvent) throws Exception {
+ Request request = requestEvent.getRequest();
+ log.info("Received INVITE:\n{}", request);
+
+ SipProvider provider = (SipProvider) requestEvent.getSource();
+ ServerTransaction st = requestEvent.getServerTransaction();
+ if (st == null) {
+ st = provider.getNewServerTransaction(request);
+ }
+
+ // ① 100 Trying(非常推荐)
+ Response trying = messageFactory.createResponse(Response.TRYING, request);
+ st.sendResponse(trying);
+
+ // ② 解析对方 SDP(可先只打印)
+ byte[] raw = request.getRawContent();
+ if (raw != null) {
+ String sdp = new String(raw, StandardCharsets.UTF_8);
+ log.info("INVITE SDP:\n{}", sdp);
+ }
+
+ // ③ 构造 200 OK + SDP
+ Response ok = messageFactory.createResponse(Response.OK, request);
+
+ String localSipId = "29001002120107000001";
+ int localRtpPort = 554;
+ // Contact 必须
+ Address contactAddress = addressFactory.createAddress(
+ "sip:" + localSipId + "@" + localIp + ":" + localPort
+ );
+ ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
+ ok.addHeader(contactHeader);
+
+ // Content-Type
+ ContentTypeHeader contentType =
+ headerFactory.createContentTypeHeader("application", "sdp");
+
+ // 返回 SDP(示例)
+ String sdp =
+ "v=0\r\n" +
+ "o=" + localSipId + " 0 0 IN IP4 " + localIp + "\r\n" +
+ "s=Play\r\n" +
+ "c=IN IP4 " + localIp + "\r\n" +
+ "t=0 0\r\n" +
+ "m=video " + localRtpPort + " TCP/RTP/AVP 96\r\n" +
+ "a=sendonly\r\n" +
+ "a=setup:active\r\n" +
+ "a=connection:new\r\n" +
+ "a=rtpmap:96 PS/90000\r\n";
+
+ ok.setContent(sdp, contentType);
+
+ // ④ 发送 200 OK
+ st.sendResponse(ok);
+
+ log.info("INVITE handled: 200 OK sent");
+ }
+
+
@Override
public void processTimeout(TimeoutEvent timeoutEvent) {
log.warn("SIP timeout: {}", timeoutEvent);
@@ -977,12 +1089,12 @@ public class SipClientService implements SipListener {
notifyRequest.addHeader(ssHeader);
// 消息体
- String xmlBody = "\n" +
- "\n" +
- " " + bodyText + "\n" +
- "";
- ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "xml");
- notifyRequest.setContent(xmlBody, contentTypeHeader);
+// String xmlBody = "\n" +
+// "\n" +
+// " " + bodyText + "\n" +
+// "";
+// ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "xml");
+// notifyRequest.setContent(xmlBody, contentTypeHeader);
// 发送
ClientTransaction ct = sipProvider.getNewClientTransaction(notifyRequest);
@@ -995,6 +1107,54 @@ public class SipClientService implements SipListener {
}
}
+ private void sendSubscribeNotifyToSipServer(Request subscribeRequest) {
+ try {
+ // 获取必要头字段
+ CallIdHeader callIdHeader = (CallIdHeader) subscribeRequest.getHeader(CallIdHeader.NAME);
+ CSeqHeader cseqHeader = (CSeqHeader) subscribeRequest.getHeader(CSeqHeader.NAME);
+ FromHeader fromHeader = (FromHeader) subscribeRequest.getHeader(FromHeader.NAME);
+ ToHeader toHeader = (ToHeader) subscribeRequest.getHeader(ToHeader.NAME);
+ EventHeader eventHeader = (EventHeader) subscribeRequest.getHeader(EventHeader.NAME);
+ ContactHeader contactHeader = (ContactHeader) subscribeRequest.getHeader(ContactHeader.NAME);
+
+ List viaHeaders = new ArrayList<>();
+ ViaHeader viaHeader = headerFactory.createViaHeader(localIp, localPort, transport.toUpperCase(), null);
+ viaHeaders.add(viaHeader);
+ // 创建 NOTIFY 请求
+ Request notifyRequest = messageFactory.createRequest(
+ subscribeRequest.getRequestURI(),
+ Request.NOTIFY,
+ callIdHeader,
+ headerFactory.createCSeqHeader(cseqHeader.getSeqNumber() + 1, Request.NOTIFY),
+ fromHeader,
+ toHeader,
+ viaHeaders,
+ headerFactory.createMaxForwardsHeader(70)
+ );
+
+ // 加入 Contact
+ notifyRequest.addHeader(contactHeader);
+
+ // 必须的事件头
+ notifyRequest.addHeader(eventHeader);
+
+ // Subscription-State 头
+ SubscriptionStateHeader ssHeader = headerFactory.createSubscriptionStateHeader("active");
+ ssHeader.setExpires(3600);
+ notifyRequest.addHeader(ssHeader);
+
+ ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "xml");
+ notifyRequest.setContent(null, contentTypeHeader);
+
+ // 发送
+ ClientTransaction ct = sipProvider.getNewClientTransaction(notifyRequest);
+ ct.sendRequest();
+ log.info("sendSubscribeNotifyToSipServer: callId: {}", callIdHeader.getCallId());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
private void removeSubscription(Request request) {
CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);