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.
47 * @author Matthew Skinner - Initial contribution
50 public class RtspConnection {
51 private final Logger logger = LoggerFactory.getLogger(getClass());
52 private @Nullable Bootstrap rtspBootstrap;
53 private EventLoopGroup mainEventLoopGroup = new NioEventLoopGroup();
54 private IpCameraHandler ipCameraHandler;
55 String username, password;
57 public RtspConnection(IpCameraHandler ipCameraHandler, String username, String password) {
58 this.ipCameraHandler = ipCameraHandler;
59 this.username = username;
60 this.password = password;
63 public void connect() {
64 sendRtspRequest(getRTSPoptions());
67 public void processMessage(Object msg) {
68 logger.info("reply from RTSP is {}", msg);
69 if (msg.toString().contains("DESCRIBE")) {// getRTSPoptions
70 // Public: OPTIONS, DESCRIBE, ANNOUNCE, SETUP, PLAY, RECORD, PAUSE, TEARDOWN, SET_PARAMETER, GET_PARAMETER
71 sendRtspRequest(getRTSPdescribe());
72 } else if (msg.toString().contains("CSeq: 2")) {// getRTSPdescribe
76 // x-Accept-Dynamic-Rate: 1
78 // rtsp://192.168.xx.xx:554/cam/realmonitor?channel=1&subtype=1&unicast=true&proto=Onvif/
79 // Cache-Control: must-revalidate
80 // Content-Length: 582
81 // Content-Type: application/sdp
82 sendRtspRequest(getRTSPsetup());
83 } else if (msg.toString().contains("CSeq: 3")) {
84 sendRtspRequest(getRTSPplay());
88 HttpRequest getRTSPoptions() {
89 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS,
90 ipCameraHandler.rtspUri);
91 request.headers().add(RtspHeaderNames.CSEQ, "1");
95 HttpRequest getRTSPdescribe() {
96 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.DESCRIBE,
97 ipCameraHandler.rtspUri);
98 request.headers().add(RtspHeaderNames.CSEQ, "2");
102 HttpRequest getRTSPsetup() {
103 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.SETUP, ipCameraHandler.rtspUri);
104 request.headers().add(RtspHeaderNames.CSEQ, "3");
105 request.headers().add(RtspHeaderNames.TRANSPORT, "RTP/AVP;unicast;client_port=5000-5001");
109 HttpRequest getRTSPplay() {
110 HttpRequest request = new DefaultHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.PLAY, ipCameraHandler.rtspUri);
111 request.headers().add(RtspHeaderNames.CSEQ, "4");
112 // need session to match response from getRTSPsetup()
113 request.headers().add(RtspHeaderNames.SESSION, "12345678");
117 private RtspConnection getHandle() {
121 @SuppressWarnings("null")
122 public void sendRtspRequest(HttpRequest request) {
123 if (rtspBootstrap == null) {
124 rtspBootstrap = new Bootstrap();
125 rtspBootstrap.group(mainEventLoopGroup);
126 rtspBootstrap.channel(NioSocketChannel.class);
127 rtspBootstrap.option(ChannelOption.SO_KEEPALIVE, true);
128 rtspBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4500);
129 rtspBootstrap.option(ChannelOption.SO_SNDBUF, 1024 * 8);
130 rtspBootstrap.option(ChannelOption.SO_RCVBUF, 1024 * 1024);
131 rtspBootstrap.option(ChannelOption.TCP_NODELAY, true);
132 rtspBootstrap.handler(new ChannelInitializer<SocketChannel>() {
135 public void initChannel(SocketChannel socketChannel) throws Exception {
136 socketChannel.pipeline().addLast(new IdleStateHandler(18, 0, 0));
137 socketChannel.pipeline().addLast(new RtspDecoder());
138 socketChannel.pipeline().addLast(new RtspEncoder());
139 // Need to update the authhandler to work for multiple use cases, before this works.
140 // socketChannel.pipeline().addLast(new MyNettyAuthHandler(username, password, ipCameraHandler));
141 socketChannel.pipeline().addLast(new NettyRtspHandler(getHandle()));
146 rtspBootstrap.connect(new InetSocketAddress(ipCameraHandler.cameraConfig.getIp(), 554))
147 .addListener(new ChannelFutureListener() {
150 public void operationComplete(@Nullable ChannelFuture future) {
151 if (future == null) {
154 if (future.isDone() && future.isSuccess()) {
155 Channel ch = future.channel();
156 ch.writeAndFlush(request);
157 } else { // an error occured
158 logger.debug("Could not reach cameras rtsp on port 554.");