2020-06-07 16:56:16 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2005-2020 Team Kodi
|
|
|
|
* https://kodi.tv
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
* See LICENSE.md for more information.
|
|
|
|
*/
|
|
|
|
|
2016-10-05 10:45:05 +02:00
|
|
|
#include "rtsp_client.hpp"
|
2020-06-07 16:56:16 +02:00
|
|
|
|
|
|
|
#include "Socket.h"
|
|
|
|
|
2016-10-05 10:45:05 +02:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cctype>
|
2020-06-07 16:56:16 +02:00
|
|
|
#include <cstring>
|
2016-10-05 10:45:05 +02:00
|
|
|
#include <iterator>
|
|
|
|
#include <sstream>
|
|
|
|
|
2016-10-05 16:46:40 +02:00
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
|
|
#define strtok_r strtok_s
|
|
|
|
#define strncasecmp _strnicmp
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
int vasprintf(char** sptr, char* fmt, va_list argv)
|
|
|
|
{
|
|
|
|
int wanted = vsnprintf(*sptr = nullptr, 0, fmt, argv);
|
|
|
|
if ((wanted < 0) || ((*sptr = (char*)malloc(1 + wanted)) == nullptr))
|
|
|
|
return -1;
|
|
|
|
return vsprintf(*sptr, fmt, argv);
|
2016-10-05 16:46:40 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
int asprintf(char** sptr, char* fmt, ...)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
va_list argv;
|
|
|
|
va_start(argv, fmt);
|
|
|
|
retval = vasprintf(sptr, fmt, argv);
|
|
|
|
va_end(argv);
|
|
|
|
return retval;
|
2016-10-05 16:46:40 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-10-05 10:45:05 +02:00
|
|
|
#define RTSP_DEFAULT_PORT 554
|
|
|
|
#define RTSP_RECEIVE_BUFFER 2048
|
|
|
|
#define RTP_HEADER_SIZE 12
|
|
|
|
#define VLEN 100
|
|
|
|
#define KEEPALIVE_INTERVAL 60
|
|
|
|
#define KEEPALIVE_MARGIN 5
|
|
|
|
#define UDP_ADDRESS_LEN 16
|
2016-11-09 15:32:39 +01:00
|
|
|
#define RTCP_BUFFER_SIZE 1024
|
2016-10-05 10:45:05 +02:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace OCTO;
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
enum rtsp_state
|
|
|
|
{
|
|
|
|
RTSP_IDLE,
|
|
|
|
RTSP_DESCRIBE,
|
|
|
|
RTSP_SETUP,
|
|
|
|
RTSP_PLAY,
|
|
|
|
RTSP_RUNNING
|
2016-10-05 10:45:05 +02:00
|
|
|
};
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
enum rtsp_result
|
|
|
|
{
|
|
|
|
RTSP_RESULT_OK = 200,
|
2016-10-05 10:45:05 +02:00
|
|
|
};
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
struct rtsp_client
|
|
|
|
{
|
|
|
|
char* content_base;
|
|
|
|
char* control;
|
|
|
|
char session_id[64];
|
|
|
|
uint16_t stream_id;
|
|
|
|
int keepalive_interval;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
char udp_address[UDP_ADDRESS_LEN];
|
|
|
|
uint16_t udp_port;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
Socket tcp_sock;
|
|
|
|
Socket udp_sock;
|
|
|
|
Socket rtcp_sock;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
enum rtsp_state state;
|
|
|
|
int cseq;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
size_t fifo_size;
|
|
|
|
uint16_t last_seq_nr;
|
2016-11-09 15:32:39 +01:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
string name;
|
|
|
|
int level;
|
|
|
|
int quality;
|
2016-10-05 10:45:05 +02:00
|
|
|
};
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
struct url
|
|
|
|
{
|
|
|
|
string protocol;
|
|
|
|
string host;
|
|
|
|
int port;
|
|
|
|
string path;
|
2016-10-05 10:45:05 +02:00
|
|
|
};
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
struct rtcp_app
|
|
|
|
{
|
|
|
|
uint8_t subtype;
|
|
|
|
uint8_t pt;
|
|
|
|
uint16_t len;
|
|
|
|
uint32_t ssrc;
|
|
|
|
char name[4];
|
|
|
|
uint16_t identifier;
|
|
|
|
uint16_t string_len;
|
2016-11-09 15:32:39 +01:00
|
|
|
};
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static rtsp_client* rtsp = nullptr;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static url parse_url(const std::string& str)
|
|
|
|
{
|
|
|
|
static const string prot_end = "://";
|
|
|
|
static const string host_end = "/";
|
|
|
|
url result;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
string::const_iterator begin = str.begin();
|
|
|
|
string::const_iterator end = search(begin, str.end(), prot_end.begin(), prot_end.end());
|
|
|
|
result.protocol.reserve(distance(begin, end));
|
|
|
|
transform(begin, end, back_inserter(result.protocol), ::tolower);
|
|
|
|
advance(end, prot_end.size());
|
|
|
|
begin = end;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
end = search(begin, str.end(), host_end.begin(), host_end.end());
|
|
|
|
result.host.reserve(distance(begin, end));
|
|
|
|
transform(begin, end, back_inserter(result.host), ::tolower);
|
|
|
|
advance(end, host_end.size());
|
|
|
|
begin = end;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
result.port = RTSP_DEFAULT_PORT;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
result.path.reserve(distance(begin, str.end()));
|
|
|
|
transform(begin, str.end(), back_inserter(result.path), ::tolower);
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
return result;
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
void split_string(const string& s, char delim, vector<string>& elems)
|
|
|
|
{
|
|
|
|
stringstream ss;
|
|
|
|
ss.str(s);
|
|
|
|
|
|
|
|
string item;
|
|
|
|
while (getline(ss, item, delim))
|
|
|
|
{
|
|
|
|
elems.push_back(item);
|
|
|
|
}
|
2016-11-09 15:34:05 +01:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static int tcp_sock_read_line(string& line)
|
|
|
|
{
|
|
|
|
static string buf;
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
string::size_type pos = buf.find("\r\n");
|
|
|
|
if (pos != string::npos)
|
|
|
|
{
|
|
|
|
line = buf.substr(0, pos);
|
|
|
|
buf.erase(0, pos + 2);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
char tmp_buf[2048];
|
|
|
|
int size = rtsp->tcp_sock.receive(tmp_buf, sizeof(tmp_buf), 1);
|
|
|
|
if (size <= 0)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.append(&tmp_buf[0], &tmp_buf[size]);
|
|
|
|
}
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static string compose_url(const url& u)
|
|
|
|
{
|
2020-06-07 16:56:16 +02:00
|
|
|
stringstream res;
|
|
|
|
res << u.protocol << "://" << u.host;
|
|
|
|
if (u.port > 0)
|
|
|
|
res << ":" << u.port;
|
|
|
|
res << "/" << u.path;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
return res.str();
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static void parse_session(char* request_line, char* session, unsigned max, int* timeout)
|
|
|
|
{
|
|
|
|
char* state;
|
|
|
|
char* tok;
|
|
|
|
|
|
|
|
tok = strtok_r(request_line, ";", &state);
|
|
|
|
if (tok == nullptr)
|
|
|
|
return;
|
|
|
|
strncpy(session, tok, min(strlen(tok), (size_t)(max - 1)));
|
|
|
|
|
|
|
|
while ((tok = strtok_r(nullptr, ";", &state)) != nullptr)
|
|
|
|
{
|
|
|
|
if (strncmp(tok, "timeout=", 8) == 0)
|
|
|
|
{
|
|
|
|
*timeout = atoi(tok + 8);
|
|
|
|
if (*timeout > 5)
|
|
|
|
*timeout -= KEEPALIVE_MARGIN;
|
|
|
|
else if (*timeout > 0)
|
|
|
|
*timeout = 1;
|
|
|
|
}
|
|
|
|
}
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static int parse_port(char* str, uint16_t* port)
|
2016-10-05 10:45:05 +02:00
|
|
|
{
|
2020-06-07 16:56:16 +02:00
|
|
|
int p = atoi(str);
|
|
|
|
if (p < 0 || p > UINT16_MAX)
|
|
|
|
return -1;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
*port = p;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
return 0;
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static int parse_transport(char* request_line)
|
|
|
|
{
|
|
|
|
char* state;
|
|
|
|
char* tok;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
tok = strtok_r(request_line, ";", &state);
|
|
|
|
if (tok == nullptr || strncmp(tok, "RTP/AVP", 7) != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
tok = strtok_r(nullptr, ";", &state);
|
|
|
|
if (tok == nullptr || strncmp(tok, "multicast", 9) != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while ((tok = strtok_r(nullptr, ";", &state)) != nullptr)
|
|
|
|
{
|
|
|
|
if (strncmp(tok, "destination=", 12) == 0)
|
|
|
|
{
|
|
|
|
strncpy(rtsp->udp_address, tok + 12, min(strlen(tok + 12), (size_t)(UDP_ADDRESS_LEN - 1)));
|
|
|
|
}
|
|
|
|
else if (strncmp(tok, "port=", 5) == 0)
|
|
|
|
{
|
|
|
|
char port[6];
|
|
|
|
char* end;
|
|
|
|
|
|
|
|
memset(port, 0x00, 6);
|
|
|
|
strncpy(port, tok + 5, min(strlen(tok + 5), (size_t)5));
|
|
|
|
if ((end = strstr(port, "-")) != nullptr)
|
|
|
|
*end = '\0';
|
|
|
|
err = parse_port(port, &rtsp->udp_port);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
#define skip_whitespace(x) \
|
|
|
|
while (*x == ' ') \
|
|
|
|
x++
|
|
|
|
static enum rtsp_result rtsp_handle()
|
|
|
|
{
|
|
|
|
uint8_t buffer[512];
|
|
|
|
int rtsp_result = 0;
|
|
|
|
bool have_header = false;
|
|
|
|
size_t content_length = 0;
|
|
|
|
size_t read = 0;
|
|
|
|
char *in, *val;
|
|
|
|
string in_str;
|
|
|
|
|
|
|
|
/* Parse header */
|
|
|
|
while (!have_header)
|
|
|
|
{
|
|
|
|
if (tcp_sock_read_line(in_str) < 0)
|
|
|
|
break;
|
|
|
|
in = const_cast<char*>(in_str.c_str());
|
|
|
|
|
|
|
|
if (strncmp(in, "RTSP/1.0 ", 9) == 0)
|
|
|
|
{
|
|
|
|
rtsp_result = atoi(in + 9);
|
|
|
|
}
|
|
|
|
else if (strncmp(in, "Content-Base:", 13) == 0)
|
|
|
|
{
|
|
|
|
free(rtsp->content_base);
|
|
|
|
|
|
|
|
val = in + 13;
|
|
|
|
skip_whitespace(val);
|
|
|
|
|
|
|
|
rtsp->content_base = strdup(val);
|
|
|
|
}
|
|
|
|
else if (strncmp(in, "Content-Length:", 15) == 0)
|
|
|
|
{
|
|
|
|
val = in + 16;
|
|
|
|
skip_whitespace(val);
|
|
|
|
|
|
|
|
content_length = atoi(val);
|
|
|
|
}
|
|
|
|
else if (strncmp("Session:", in, 8) == 0)
|
|
|
|
{
|
|
|
|
val = in + 8;
|
|
|
|
skip_whitespace(val);
|
|
|
|
|
|
|
|
parse_session(val, rtsp->session_id, 64, &rtsp->keepalive_interval);
|
|
|
|
}
|
|
|
|
else if (strncmp("Transport:", in, 10) == 0)
|
|
|
|
{
|
|
|
|
val = in + 10;
|
|
|
|
skip_whitespace(val);
|
|
|
|
|
|
|
|
if (parse_transport(val) != 0)
|
|
|
|
{
|
|
|
|
rtsp_result = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strncmp("com.ses.streamID:", in, 17) == 0)
|
|
|
|
{
|
|
|
|
val = in + 17;
|
|
|
|
skip_whitespace(val);
|
|
|
|
|
|
|
|
rtsp->stream_id = atoi(val);
|
|
|
|
}
|
|
|
|
else if (in[0] == '\0')
|
|
|
|
{
|
|
|
|
have_header = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Discard further content */
|
|
|
|
while (content_length > 0 && (read = rtsp->tcp_sock.receive((char*)buffer, sizeof(buffer),
|
|
|
|
min(sizeof(buffer), content_length))))
|
|
|
|
content_length -= read;
|
|
|
|
|
|
|
|
return (enum rtsp_result)rtsp_result;
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2016-11-10 11:33:56 +01:00
|
|
|
bool rtsp_open(const string& name, const string& url_str)
|
2016-10-05 10:45:05 +02:00
|
|
|
{
|
2020-06-07 16:56:16 +02:00
|
|
|
string setup_url_str;
|
|
|
|
const char* psz_setup_url;
|
|
|
|
stringstream setup_ss;
|
|
|
|
stringstream play_ss;
|
|
|
|
url setup_url;
|
|
|
|
|
|
|
|
rtsp_close();
|
|
|
|
rtsp = new rtsp_client();
|
|
|
|
if (rtsp == nullptr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
rtsp->name = name;
|
|
|
|
rtsp->level = 0;
|
|
|
|
rtsp->quality = 0;
|
|
|
|
|
2020-06-07 19:06:40 +02:00
|
|
|
kodi::Log(ADDON_LOG_DEBUG, "try to open '%s'", url_str.c_str());
|
2020-06-07 16:56:16 +02:00
|
|
|
|
|
|
|
url dst = parse_url(url_str);
|
2020-06-07 19:06:40 +02:00
|
|
|
kodi::Log(ADDON_LOG_DEBUG, "connect to host '%s'", dst.host.c_str());
|
2020-06-07 16:56:16 +02:00
|
|
|
|
|
|
|
if (!rtsp->tcp_sock.connect(dst.host, dst.port))
|
|
|
|
{
|
2020-06-07 19:06:40 +02:00
|
|
|
kodi::Log(ADDON_LOG_ERROR, "Failed to connect to RTSP server %s:%d", dst.host.c_str(),
|
|
|
|
dst.port);
|
2020-06-07 16:56:16 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: tcp keep alive?
|
|
|
|
|
|
|
|
if (asprintf(&rtsp->content_base, "rtsp://%s:%d/", dst.host.c_str(), dst.port) < 0)
|
|
|
|
{
|
|
|
|
rtsp->content_base = nullptr;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtsp->last_seq_nr = 0;
|
|
|
|
rtsp->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN);
|
|
|
|
|
|
|
|
setup_url = dst;
|
|
|
|
|
|
|
|
// reverse the satip protocol trick, as SAT>IP believes to be RTSP
|
|
|
|
if (!strncasecmp(setup_url.protocol.c_str(), "satip", 5))
|
|
|
|
{
|
|
|
|
setup_url.protocol = "rtsp";
|
|
|
|
}
|
|
|
|
|
|
|
|
setup_url_str = compose_url(setup_url);
|
|
|
|
psz_setup_url = setup_url_str.c_str();
|
|
|
|
|
|
|
|
// TODO: Find available port
|
|
|
|
rtsp->udp_sock = Socket(af_inet, pf_inet, sock_dgram, udp);
|
|
|
|
rtsp->udp_port = 6785;
|
|
|
|
if (!rtsp->udp_sock.bind(rtsp->udp_port))
|
|
|
|
{
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
setup_ss << "SETUP " << setup_url_str << " RTSP/1.0\r\n";
|
|
|
|
setup_ss << "CSeq: " << rtsp->cseq++ << "\r\n";
|
|
|
|
setup_ss << "Transport: RTP/AVP;unicast;client_port=" << rtsp->udp_port << "-"
|
|
|
|
<< (rtsp->udp_port + 1) << "\r\n\r\n";
|
|
|
|
rtsp->tcp_sock.send(setup_ss.str());
|
|
|
|
|
|
|
|
if (rtsp_handle() != RTSP_RESULT_OK)
|
|
|
|
{
|
2020-06-07 19:06:40 +02:00
|
|
|
kodi::Log(ADDON_LOG_ERROR, "Failed to setup RTSP session");
|
2020-06-07 16:56:16 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asprintf(&rtsp->control, "%sstream=%d", rtsp->content_base, rtsp->stream_id) < 0)
|
|
|
|
{
|
|
|
|
rtsp->control = nullptr;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
play_ss << "PLAY " << rtsp->control << " RTSP/1.0\r\n";
|
|
|
|
play_ss << "CSeq: " << rtsp->cseq++ << "\r\n";
|
|
|
|
play_ss << "Session: " << rtsp->session_id << "\r\n\r\n";
|
|
|
|
rtsp->tcp_sock.send(play_ss.str());
|
|
|
|
|
|
|
|
if (rtsp_handle() != RTSP_RESULT_OK)
|
|
|
|
{
|
2020-06-07 19:06:40 +02:00
|
|
|
kodi::Log(ADDON_LOG_ERROR, "Failed to play RTSP session");
|
2020-06-07 16:56:16 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtsp->rtcp_sock = Socket(af_inet, pf_inet, sock_dgram, udp);
|
|
|
|
if (!rtsp->rtcp_sock.bind(rtsp->udp_port + 1))
|
|
|
|
{
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (!rtsp->rtcp_sock.set_non_blocking(true))
|
|
|
|
{
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2016-10-05 10:45:05 +02:00
|
|
|
|
|
|
|
error:
|
2020-06-07 16:56:16 +02:00
|
|
|
rtsp_close();
|
|
|
|
return false;
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static void parse_rtcp(const char* buf, int size)
|
|
|
|
{
|
|
|
|
int offset = 0;
|
|
|
|
while (size > 4)
|
|
|
|
{
|
|
|
|
const rtcp_app* app = reinterpret_cast<const rtcp_app*>(buf + offset);
|
|
|
|
uint16_t len = 4 * (ntohs(app->len) + 1);
|
|
|
|
|
|
|
|
if ((app->pt != 204) || (memcmp(app->name, "SES1", 4) != 0))
|
|
|
|
{
|
|
|
|
size -= len;
|
|
|
|
offset += len;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t string_len = ntohs(app->string_len);
|
|
|
|
string app_data(&buf[offset + sizeof(rtcp_app)], string_len);
|
|
|
|
|
|
|
|
vector<string> elems;
|
|
|
|
split_string(app_data, ';', elems);
|
|
|
|
if (elems.size() != 4)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string> tuner;
|
|
|
|
split_string(elems[2], ',', tuner);
|
|
|
|
if (tuner.size() < 4)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtsp->level = atoi(tuner[1].c_str());
|
|
|
|
rtsp->quality = atoi(tuner[3].c_str());
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2016-11-09 15:34:05 +01:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
int rtsp_read(void* buf, unsigned buf_size)
|
|
|
|
{
|
|
|
|
sockaddr addr;
|
|
|
|
socklen_t addr_len = sizeof(addr);
|
|
|
|
int ret = rtsp->udp_sock.recvfrom((char*)buf, buf_size, (sockaddr*)&addr, &addr_len);
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
char rtcp_buf[RTCP_BUFFER_SIZE];
|
|
|
|
int rtcp_len = rtsp->rtcp_sock.recvfrom(rtcp_buf, RTCP_BUFFER_SIZE, (sockaddr*)&addr, &addr_len);
|
|
|
|
parse_rtcp(rtcp_buf, rtcp_len);
|
2016-11-09 15:34:05 +01:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
// TODO: check ip
|
2016-10-05 10:45:05 +02:00
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
return ret;
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
2020-06-07 16:56:16 +02:00
|
|
|
static void rtsp_teardown()
|
|
|
|
{
|
|
|
|
if (!rtsp->tcp_sock.is_valid())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rtsp->session_id[0] > 0)
|
|
|
|
{
|
|
|
|
char* msg;
|
|
|
|
int len;
|
|
|
|
stringstream ss;
|
|
|
|
|
|
|
|
rtsp->udp_sock.close();
|
|
|
|
|
|
|
|
ss << "TEARDOWN " << rtsp->control << " RTSP/1.0\r\n";
|
|
|
|
ss << "CSeq: " << rtsp->cseq++ << "\r\n";
|
|
|
|
ss << "Session: " << rtsp->session_id << "\r\n\r\n";
|
|
|
|
rtsp->tcp_sock.send(ss.str());
|
|
|
|
|
|
|
|
if (rtsp_handle() != RTSP_RESULT_OK)
|
|
|
|
{
|
2020-06-07 19:06:40 +02:00
|
|
|
kodi::Log(ADDON_LOG_ERROR, "Failed to teardown RTSP session");
|
2020-06-07 16:56:16 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void rtsp_close()
|
|
|
|
{
|
2020-06-07 16:56:16 +02:00
|
|
|
if (rtsp)
|
|
|
|
{
|
|
|
|
rtsp_teardown();
|
|
|
|
rtsp->tcp_sock.close();
|
|
|
|
rtsp->udp_sock.close();
|
|
|
|
rtsp->rtcp_sock.close();
|
|
|
|
delete rtsp;
|
|
|
|
rtsp = nullptr;
|
|
|
|
}
|
2016-10-05 10:45:05 +02:00
|
|
|
}
|
2016-11-09 15:43:01 +01:00
|
|
|
|
2020-06-07 19:06:40 +02:00
|
|
|
void rtsp_fill_signal_status(kodi::addon::PVRSignalStatus& signal_status)
|
2020-06-07 16:56:16 +02:00
|
|
|
{
|
|
|
|
if (rtsp)
|
|
|
|
{
|
2020-06-07 19:06:40 +02:00
|
|
|
signal_status.SetAdapterName(rtsp->name);
|
|
|
|
signal_status.SetSNR(0x1111 * rtsp->quality);
|
|
|
|
signal_status.SetSignal(0x101 * rtsp->level);
|
2020-06-07 16:56:16 +02:00
|
|
|
}
|
2016-11-09 15:43:01 +01:00
|
|
|
}
|