2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.ipcamera.internal.rtsp;
15 import java.net.InetSocketAddress;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.ipcamera.internal.handler.IpCameraHandler;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
23 import io.netty.bootstrap.Bootstrap;
24 import io.netty.channel.Channel;
25 import io.netty.channel.ChannelFuture;
26 import io.netty.channel.ChannelFutureListener;
27 import io.netty.channel.ChannelInitializer;
28 import io.netty.channel.ChannelOption;
29 import io.netty.channel.EventLoopGroup;
30 import io.netty.channel.nio.NioEventLoopGroup;
31 import io.netty.channel.socket.SocketChannel;
32 import io.netty.channel.socket.nio.NioSocketChannel;
33 import io.netty.handler.codec.http.DefaultHttpRequest;
34 import io.netty.handler.codec.http.HttpRequest;
35 import io.netty.handler.codec.rtsp.RtspDecoder;
36 import io.netty.handler.codec.rtsp.RtspEncoder;
37 import io.netty.handler.codec.rtsp.RtspHeaderNames;
38 import io.netty.handler.codec.rtsp.RtspMethods;
39 import io.netty.handler.codec.rtsp.RtspVersions;
40 import io.netty.handler.timeout.IdleStateHandler;
43 * The {@link RtspConnection} is a WIP and not currently used, but will talk directly to RTSP and collect information
44 * about the camera and streams.
46 * @author Matthew Skinner - Initial contribution
49 public class RtspConnection {
50 private final Logger logger = LoggerFactory.getLogger(getClass());
51 private @Nullable Bootstrap rtspBootstrap;
52 private EventLoopGroup mainEventLoopGroup = new NioEventLoopGroup();
53 private IpCameraHandler ipCameraHandler;
54 String username, password;
56 public RtspConnection(IpCameraHandler ipCameraHandler, String username, String password) {
57 this.ipCameraHandler = ipCameraHandler;
58 this.username = username;
59 this.password = password;
62 public void connect() {
63 sendRtspRequest(getRTSPoptions());
66 public void processMessage(Object msg) {
67 logger.info("reply from RTSP is {}", msg);
68 if (msg.toString().contains("DESCRIBE")) {// getRTSPoptions
69 // Public: OPTIONS, DESCRIBE, ANNOUNCE, SETUP, PLAY, RECORD, PAUSE, TEARDOWN, SET_PARAMETER, GET_PARAMETER
70 sendRtspRequest(getRTSPdescribe());
71 } else if (msg.toString().contains("CSeq: 2")) {// getRTSPdescribe
75 // x-Accept-Dynamic-Rate: 1
77 // rtsp://192.168.xx.xx:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif/
78 // Cache-Control: must-revalidate
79 // Content-Length: 582
80 // Content-Type: application/sdp
81 sendRtspRequest(getRTSPsetup());
82 } else if (msg.toString().contains("CSeq: 3")) {
83 sendRtspRequest(getRTSPplay());
87 HttpRequest getRTSPoptions() {
88 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS,
89 ipCameraHandler.rtspUri);
90 request.headers().add(RtspHeaderNames.CSEQ, "1");
94 HttpRequest getRTSPdescribe() {
95 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.DESCRIBE,
96 ipCameraHandler.rtspUri);
97 request.headers().add(RtspHeaderNames.CSEQ, "2");
101 HttpRequest getRTSPsetup() {
102 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.SETUP, ipCameraHandler.rtspUri);
103 request.headers().add(RtspHeaderNames.CSEQ, "3");
104 request.headers().add(RtspHeaderNames.TRANSPORT, "RTP/AVP;unicast;client_port=5000-5001");
108 HttpRequest getRTSPplay() {
109 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.PLAY, ipCameraHandler.rtspUri);
110 request.headers().add(RtspHeaderNames.CSEQ, "4");
111 // need session to match response from getRTSPsetup()
112 request.headers().add(RtspHeaderNames.SESSION, "12345678");
116 private RtspConnection getHandle() {
120 @SuppressWarnings("null")
121 public void sendRtspRequest(HttpRequest request) {
122 if (rtspBootstrap == null) {
123 rtspBootstrap = new Bootstrap();
124 rtspBootstrap.group(mainEventLoopGroup);
125 rtspBootstrap.channel(NioSocketChannel.class);
126 rtspBootstrap.option(ChannelOption.SO_KEEPALIVE, true);
127 rtspBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4500);
128 rtspBootstrap.option(ChannelOption.SO_SNDBUF, 1024 * 8);
129 rtspBootstrap.option(ChannelOption.SO_RCVBUF, 1024 * 1024);
130 rtspBootstrap.option(ChannelOption.TCP_NODELAY, true);
131 rtspBootstrap.handler(new ChannelInitializer<SocketChannel>() {
134 public void initChannel(SocketChannel socketChannel) throws Exception {
135 socketChannel.pipeline().addLast(new IdleStateHandler(18, 0, 0));
136 socketChannel.pipeline().addLast(new RtspDecoder());
137 socketChannel.pipeline().addLast(new RtspEncoder());
138 // Need to update the authhandler to work for multiple use cases, before this works.
139 // socketChannel.pipeline().addLast(new MyNettyAuthHandler(username, password, ipCameraHandler));
140 socketChannel.pipeline().addLast(new NettyRtspHandler(getHandle()));
145 rtspBootstrap.connect(new InetSocketAddress(ipCameraHandler.cameraConfig.getIp(), 554))
146 .addListener(new ChannelFutureListener() {
149 public void operationComplete(@Nullable ChannelFuture future) {
150 if (future == null) {
153 if (future.isDone() && future.isSuccess()) {
154 Channel ch = future.channel();
155 ch.writeAndFlush(request);
156 } else { // an error occured
157 logger.debug("Could not reach cameras rtsp on port 554.");