mirror of
https://github.com/albfan/miraclecast.git
synced 2025-03-09 23:38:56 +00:00
miracle-wfdctl: refactoring to remove repeated snippets and centralize the
dispatching of request and reply
This commit is contained in:
parent
6bcfe4e541
commit
6c55a332d2
9 changed files with 915 additions and 497 deletions
|
@ -52,6 +52,7 @@ set(miracle-wfdctl_SRCS ctl-cli.c
|
|||
wfd-sink.c
|
||||
wfd-dbus.c
|
||||
wfd-session.c
|
||||
wfd-out-session.c
|
||||
wfdctl.c
|
||||
wfd.c)
|
||||
|
||||
|
|
|
@ -137,31 +137,20 @@ bool ctl_sink_is_connected(struct ctl_sink *s);
|
|||
bool ctl_sink_is_closed(struct ctl_sink *s);
|
||||
|
||||
/* wfd session */
|
||||
#define wfd_session(s) (assert(s), (struct wfd_session *) (s))
|
||||
#define wfd_session(s) ((struct wfd_session *) (s))
|
||||
#define wfd_is_session(s) ( \
|
||||
(s) && \
|
||||
(WFD_SESSION_DIR_OUT == wfd_session(s)->dir || \
|
||||
WFD_SESSION_DIR_IN == wfd_session(s)->dir) \
|
||||
)
|
||||
#define wfd_is_out_session(s) (WFD_SESSION_DIR_OUT == wfd_session(s)->dir)
|
||||
#define wfd_is_in_session(s) (WFD_SESSION_DIR_IN == wfd_session(s)->dir)
|
||||
#define wfd_session_to_htable(s) (&(s)->id)
|
||||
#define wfd_session_from_htable(s) (shl_htable_entry(s, struct wfd_session, id))
|
||||
#define wfd_session_has_id(s) (0 < wfd_session_get_id(s))
|
||||
#define wfd_is_out_session(s) (WFD_SESSION_DIR_OUT == wfd_session_get_dir(s))
|
||||
#define wfd_is_in_session(s) (WFD_SESSION_DIR_IN == wfd_session_get_dir(s))
|
||||
#define _wfd_session_free_ _shl_cleanup_(wfd_session_freep)
|
||||
|
||||
struct wfd_sink;
|
||||
struct wfd_out_session;
|
||||
|
||||
enum wfd_session_state
|
||||
{
|
||||
WFD_SESSION_STATE_NULL,
|
||||
WFD_SESSION_STATE_CONNECTING,
|
||||
WFD_SESSION_STATE_CAPS_EXCHAING,
|
||||
WFD_SESSION_STATE_SETING_UP,
|
||||
WFD_SESSION_STATE_PLAYING,
|
||||
WFD_SESSION_STATE_PAUSED,
|
||||
WFD_SESSION_STATE_TEARING_DOWN,
|
||||
};
|
||||
struct wfd_session;
|
||||
struct rtsp_dispatch_entry;
|
||||
|
||||
enum wfd_session_dir
|
||||
{
|
||||
|
@ -169,29 +158,32 @@ enum wfd_session_dir
|
|||
WFD_SESSION_DIR_IN,
|
||||
};
|
||||
|
||||
struct wfd_session
|
||||
enum wfd_session_state
|
||||
{
|
||||
enum wfd_session_dir dir;
|
||||
enum wfd_session_state state;
|
||||
uint64_t id;
|
||||
char *url;
|
||||
struct rtsp *rtsp;
|
||||
|
||||
bool hup: 1;
|
||||
WFD_SESSION_STATE_NULL,
|
||||
WFD_SESSION_STATE_CONNECTING,
|
||||
WFD_SESSION_STATE_CAPS_EXCHAING,
|
||||
WFD_SESSION_STATE_ESTABLISHED,
|
||||
WFD_SESSION_STATE_SETING_UP,
|
||||
WFD_SESSION_STATE_PLAYING,
|
||||
WFD_SESSION_STATE_PAUSED,
|
||||
WFD_SESSION_STATE_TEARING_DOWN,
|
||||
};
|
||||
|
||||
int wfd_out_session_new(struct wfd_session **out, struct wfd_sink *sink);
|
||||
int wfd_session_start(struct wfd_session *s, uint64_t id);
|
||||
enum wfd_session_dir wfd_session_get_dir(struct wfd_session *s);
|
||||
uint64_t wfd_session_get_id(struct wfd_session *s);
|
||||
const char * wfd_session_get_url(struct wfd_session *s);
|
||||
enum wfd_session_state wfd_session_get_state(struct wfd_session *s);
|
||||
int wfd_session_is_started(struct wfd_session *s);
|
||||
void wfd_session_end(struct wfd_session *s);
|
||||
void wfd_session_free(struct wfd_session *s);
|
||||
uint64_t wfd_session_get_id(struct wfd_session *s);
|
||||
struct wfd_sink * wfd_out_session_get_sink(struct wfd_session *s);
|
||||
static inline void wfd_session_freep(struct wfd_session **s)
|
||||
{
|
||||
wfd_session_free(*s);
|
||||
*s = NULL;
|
||||
}
|
||||
void wfd_session_freep(struct wfd_session **s);
|
||||
uint64_t * wfd_session_to_htable(struct wfd_session *s);
|
||||
struct wfd_session * wfd_session_from_htable(uint64_t *e);
|
||||
|
||||
/* wfd sink */
|
||||
#define _wfd_sink_free_ _shl_cleanup_(wfd_sink_freep)
|
||||
|
|
|
@ -93,7 +93,7 @@ static inline int wfd_dbus_get_sink_path(struct wfd_sink *s, char **out)
|
|||
static inline int wfd_dbus_get_session_path(struct wfd_session *s, char **out)
|
||||
{
|
||||
char buf[64];
|
||||
int r = snprintf(buf, sizeof(buf), "%" PRIu64, s->id);
|
||||
int r = snprintf(buf, sizeof(buf), "%" PRIu64, wfd_session_get_id(s));
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
@ -476,7 +476,7 @@ static int wfd_dbus_session_get_sink(sd_bus *bus,
|
|||
_shl_free_ char *sink_path;
|
||||
int r;
|
||||
|
||||
if(s->dir != WFD_SESSION_DIR_OUT) {
|
||||
if(wfd_session_get_id(s) != WFD_SESSION_DIR_OUT) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -498,7 +498,7 @@ static int wfd_dbus_session_get_url(sd_bus *bus,
|
|||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct wfd_session *s = userdata;
|
||||
return sd_bus_message_append(reply, "s", s->url);
|
||||
return sd_bus_message_append(reply, "s", wfd_session_get_url(s));
|
||||
}
|
||||
|
||||
static int wfd_dbus_session_get_state(sd_bus *bus,
|
||||
|
@ -510,7 +510,7 @@ static int wfd_dbus_session_get_state(sd_bus *bus,
|
|||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct wfd_session *s = userdata;
|
||||
return sd_bus_message_append(reply, "i", s->state);
|
||||
return sd_bus_message_append(reply, "i", wfd_session_get_state(s));
|
||||
}
|
||||
|
||||
int _wfd_fn_session_properties_changed(struct wfd_session *s, char **names)
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
struct wfd_dbus;
|
||||
struct wfd_session;
|
||||
struct wfd_sink;
|
||||
|
||||
struct wfd_dbus * wfd_dbus_get();
|
||||
int wfd_dbus_new(struct wfd_dbus **out, sd_event *loop, sd_bus *bus);
|
||||
|
|
503
src/ctl/wfd-out-session.c
Normal file
503
src/ctl/wfd-out-session.c
Normal file
|
@ -0,0 +1,503 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define LOG_SUBSYSTEM "wfd-session"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include "wfd-session.h"
|
||||
#include "shl_log.h"
|
||||
#include "rtsp.h"
|
||||
|
||||
struct wfd_out_session
|
||||
{
|
||||
struct wfd_session parent;
|
||||
struct wfd_sink *sink;
|
||||
int fd;
|
||||
|
||||
bool sink_has_video: 1;
|
||||
bool sink_has_audio: 1;
|
||||
bool sink_has_pref_disp_mode: 1;
|
||||
bool sink_has_3d: 1;
|
||||
bool sink_has_uibc: 1;
|
||||
};
|
||||
|
||||
static const struct rtsp_dispatch_entry out_session_rtsp_disp_tbl[];
|
||||
|
||||
int wfd_out_session_new(struct wfd_session **out, struct wfd_sink *sink)
|
||||
{
|
||||
struct wfd_out_session *s = calloc(1, sizeof(struct wfd_out_session));
|
||||
if(!s) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wfd_session(s)->dir = WFD_SESSION_DIR_OUT;
|
||||
wfd_session(s)->rtsp_disp_tbl = out_session_rtsp_disp_tbl;
|
||||
s->fd = -1;
|
||||
s->sink = sink;
|
||||
|
||||
*out = wfd_session(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct wfd_sink * wfd_out_session_get_sink(struct wfd_session *s)
|
||||
{
|
||||
assert(wfd_is_out_session(s));
|
||||
|
||||
return wfd_out_session(s)->sink;
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_io(struct wfd_session *s,
|
||||
int error,
|
||||
int *out_fd)
|
||||
{
|
||||
socklen_t len;
|
||||
struct sockaddr_storage addr;
|
||||
_shl_close_ int fd = -1;
|
||||
struct wfd_out_session *os = wfd_out_session(s);
|
||||
|
||||
log_debug("accepting incoming RTSP connection\n");
|
||||
|
||||
len = sizeof(addr);
|
||||
fd = accept4(os->fd,
|
||||
(struct sockaddr *) &addr,
|
||||
&len,
|
||||
SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
if(0 > fd) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
log_info("RTSP connection established");
|
||||
|
||||
//wfd_session_set_state(s, WFD_SESSION_STATE_CAPS_EXCHAING);
|
||||
|
||||
close(os->fd);
|
||||
os->fd = -1;
|
||||
*out_fd = fd;
|
||||
fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfd_out_session_initiate_io(struct wfd_session *s,
|
||||
int *out_fd,
|
||||
uint32_t *out_mask)
|
||||
{
|
||||
struct wfd_out_session *os = wfd_out_session(s);
|
||||
union wfd_sube sube;
|
||||
struct sockaddr_in addr = {};
|
||||
struct ctl_peer *p = os->sink->peer;
|
||||
_shl_close_ int fd = -1;
|
||||
int enable;
|
||||
int r;
|
||||
|
||||
if(!os->sink->peer->connected) {
|
||||
log_info("peer not connected yet");
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
r = wfd_sube_parse_with_id(WFD_SUBE_ID_DEVICE_INFO,
|
||||
p->l->wfd_subelements,
|
||||
&sube);
|
||||
if(0 > r) {
|
||||
log_warning("WfdSubelements property of link must be set before P2P scan");
|
||||
return -EINVAL;
|
||||
}
|
||||
else if(WFD_SUBE_ID_DEVICE_INFO != sube.id) {
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
if(-1 != os->fd) {
|
||||
return EINPROGRESS;
|
||||
}
|
||||
|
||||
r = inet_pton(AF_INET, p->local_address, &addr.sin_addr);
|
||||
if (!r) {
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(wfd_sube_device_get_rtsp_port(&sube));
|
||||
|
||||
fd = socket(addr.sin_family,
|
||||
SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
||||
0);
|
||||
if (0 > fd) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
enable = true;
|
||||
r = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable));
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
|
||||
if (0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = listen(fd, 10);
|
||||
if (0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
log_trace("socket listening on %s:%hu",
|
||||
p->local_address,
|
||||
wfd_sube_device_get_rtsp_port(&sube));
|
||||
|
||||
os->fd = fd;
|
||||
*out_fd = fd;
|
||||
fd = -1;
|
||||
*out_mask = EPOLLIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wfd_out_session_end(struct wfd_session *s)
|
||||
{
|
||||
struct wfd_out_session *os = wfd_out_session(s);
|
||||
if(0 <= os->fd) {
|
||||
close(os->fd);
|
||||
os->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void wfd_out_session_distruct(struct wfd_session *s)
|
||||
{
|
||||
}
|
||||
|
||||
static int wfd_out_session_initiate_request(struct wfd_session *s)
|
||||
{
|
||||
return wfd_session_request(s, RTSP_M1_REQUEST_SINK_OPTIONS);
|
||||
}
|
||||
|
||||
/*static int wfd_out_session_handle_get_parameter_reply(struct rtsp *bus,*/
|
||||
/*struct rtsp_message *m,*/
|
||||
/*void *userdata)*/
|
||||
/*{*/
|
||||
/*struct wfd_session *s = userdata;*/
|
||||
|
||||
/*log_debug("received GET_PARAMETER reply (M3): %s\n",*/
|
||||
/*(char *) rtsp_message_get_raw(m));*/
|
||||
/*if(RTSP_CODE_OK != rtsp_message_get_code(m)) {*/
|
||||
/*wfd_session_end(wfd_session(userdata));*/
|
||||
/*return -EPROTO;*/
|
||||
/*}*/
|
||||
|
||||
/*return 0;*/
|
||||
/*}*/
|
||||
|
||||
/*static int wfd_out_session_send_get_parameter(sd_event_source *source,*/
|
||||
/*void *userdata)*/
|
||||
/*{*/
|
||||
/*struct wfd_session *s = userdata;*/
|
||||
/*_rtsp_message_unref_ struct rtsp_message *m = NULL;*/
|
||||
/*int r = rtsp_message_new_request(s->rtsp,*/
|
||||
/*&m,*/
|
||||
/*"GET_PARAMETER",*/
|
||||
/*s->url);*/
|
||||
/*if (0 > r) {*/
|
||||
/*goto error;*/
|
||||
/*}*/
|
||||
|
||||
/*r = rtsp_message_append(m, "{&}",*/
|
||||
/*"wfd_video_formats\n"*/
|
||||
/*"wfd_audio_codecs\n"*/
|
||||
/*"wfd_client_rtp_ports"*/
|
||||
/*//"wfd_uibc_capability"*/
|
||||
/*);*/
|
||||
/*if (0 > r) {*/
|
||||
/*goto error;*/
|
||||
/*}*/
|
||||
|
||||
/*rtsp_message_seal(m);*/
|
||||
|
||||
/*r = rtsp_call_async(s->rtsp,*/
|
||||
/*m,*/
|
||||
/*wfd_out_session_handle_get_parameter_reply,*/
|
||||
/*s,*/
|
||||
/*0,*/
|
||||
/*NULL);*/
|
||||
/*if (r < 0) {*/
|
||||
/*goto error;*/
|
||||
/*}*/
|
||||
|
||||
/*log_debug("Sending GET_PARAMETER (M3): %s\n",*/
|
||||
/*(char *) rtsp_message_get_raw(m));*/
|
||||
|
||||
/*return 0;*/
|
||||
|
||||
/*error:*/
|
||||
/*wfd_session_end(s);*/
|
||||
|
||||
/*return r;*/
|
||||
/*}*/
|
||||
|
||||
static bool find_strv(const char *str, char **strv)
|
||||
{
|
||||
while(strv) {
|
||||
if(!strcmp(str, *strv)) {
|
||||
return true;
|
||||
}
|
||||
++strv;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_options_request(struct wfd_session *s,
|
||||
struct rtsp_message *req,
|
||||
struct rtsp_message **out_rep)
|
||||
{
|
||||
const char *require;
|
||||
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
|
||||
int r;
|
||||
|
||||
r = rtsp_message_read(req, "<s>", "Require", &require);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if(strcmp("org.wfa.wfd1.0", require)) {
|
||||
return rtsp_message_new_reply_for(req,
|
||||
out_rep,
|
||||
RTSP_CODE_OPTION_NOT_SUPPORTED,
|
||||
"Invalid specification");
|
||||
}
|
||||
|
||||
r = rtsp_message_new_reply_for(req,
|
||||
&rep,
|
||||
RTSP_CODE_OK,
|
||||
NULL);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = rtsp_message_append(rep,
|
||||
"<s>",
|
||||
"Public", "org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER");
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
*out_rep = rep;
|
||||
rep = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfd_out_session_check_options_reply(struct wfd_session *s,
|
||||
struct rtsp_message *m)
|
||||
{
|
||||
int r;
|
||||
const char *public;
|
||||
char *methods[4];
|
||||
|
||||
r = rtsp_message_read(m, "<&>", "Public", &public);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sscanf(public, "%m[^,], %m[^,], %ms", &methods[0], &methods[1], &methods[2]);
|
||||
if(3 != r) {
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
methods[3] = NULL;
|
||||
r = find_strv("org.wfa.wfd1.0", methods) &&
|
||||
find_strv("SET_PARAMETER", methods) &&
|
||||
find_strv("GET_PARAMETER", methods);
|
||||
free(methods[2]);
|
||||
free(methods[1]);
|
||||
free(methods[0]);
|
||||
if(!r) {
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfd_out_session_request_options(struct wfd_session *s,
|
||||
struct rtsp_message **out)
|
||||
{
|
||||
_rtsp_message_unref_ struct rtsp_message *m = NULL;
|
||||
int r = rtsp_message_new_request(s->rtsp,
|
||||
&m,
|
||||
"OPTIONS", "*");
|
||||
if (0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = rtsp_message_append(m,
|
||||
"<s>",
|
||||
"Require", "org.wfa.wfd1.0");
|
||||
if (0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
*out = m;
|
||||
m = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*static int wfd_out_session_handle_options_request(struct rtsp *bus,*/
|
||||
/*struct rtsp_message *request,*/
|
||||
/*struct wfd_session *s)*/
|
||||
/*{*/
|
||||
/*}*/
|
||||
|
||||
static int wfd_out_session_handle_play_reply(struct rtsp *bus,
|
||||
struct rtsp_message *m,
|
||||
void *userdata)
|
||||
{
|
||||
/*struct wfd_session *s = userdata;*/
|
||||
/*log_trace("received SETUP (M5) reply: %s",*/
|
||||
/*(char *) rtsp_message_get_raw(m));*/
|
||||
|
||||
/*wfd_out_session_send_play(s);*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfd_out_session_send_play(struct wfd_session *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_setup_reply(struct rtsp *bus,
|
||||
struct rtsp_message *m,
|
||||
void *userdata)
|
||||
{
|
||||
/*struct wfd_session *s = userdata;*/
|
||||
/*log_trace("received SETUP (M5) reply: %s",*/
|
||||
/*(char *) rtsp_message_get_raw(m));*/
|
||||
|
||||
/*wfd_out_session_send_play(s);*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_set_parameter_reply(struct rtsp *bus,
|
||||
struct rtsp_message *m,
|
||||
void *userdata)
|
||||
{
|
||||
/*struct wfd_session *s = userdata;*/
|
||||
/*log_trace("received SET_PARAMETER (M4) reply: %s",*/
|
||||
/*(char *) rtsp_message_get_raw(m));*/
|
||||
|
||||
/*wfd_out_session_send_setup(s);*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wfd_out_session_send_set_parameter_request(struct wfd_session *s)
|
||||
{
|
||||
/*_rtsp_message_unref_ struct rtsp_message *req;*/
|
||||
int r;
|
||||
/*const static char tmp[] =*/
|
||||
/*"wfd_video_formats: 38 00 02 10 00000080 00000000 00000000 00 0000 0000 11 none none\n"*/
|
||||
/*//"wfd_audio_codecs: AAC 00000001 00\n"*/
|
||||
/*//"wfd_uibc_capability: input_category_list=GENERIC\n;generic_cap_list=SingleTouch;hidc_cap_list=none;port=5100\n"*/
|
||||
/*//"wfd_uibc_setting: disable\n"*/
|
||||
/*"wfd_presentation_URL: %s/streamid=0 none\n"*/
|
||||
/*"wfd_client_rtp_ports: %s %d %d mode=play";*/
|
||||
|
||||
/*r = rtsp_message_new_request(s->rtsp,*/
|
||||
/*&req,*/
|
||||
/*"SET_PARAMETER",*/
|
||||
/*s->url);*/
|
||||
/*if (r < 0) {*/
|
||||
/*cli_vERR(r);*/
|
||||
/*goto error;*/
|
||||
/*}*/
|
||||
|
||||
/*snprintf(buf, sizeof(buf), tmp, s->url, s->sink.rtp_ports.profile,*/
|
||||
/*s->sink.rtp_ports.port0, s->sink.rtp_ports.port1);*/
|
||||
|
||||
/*r = rtsp_message_append(req, "{&}", buf);*/
|
||||
/*if (r < 0) {*/
|
||||
/*cli_vERR(r);*/
|
||||
/*goto error;*/
|
||||
/*}*/
|
||||
|
||||
/*rtsp_message_seal(req);*/
|
||||
/*cli_debug("OUTGOING (M4): %s\n", rtsp_message_get_raw(req));*/
|
||||
|
||||
/*r = rtsp_call_async(s->rtsp, req, src_set_parameter_rep_fn, s, 0, NULL);*/
|
||||
/*if (r < 0) {*/
|
||||
/*cli_vERR(r);*/
|
||||
/*goto error;*/
|
||||
/*}*/
|
||||
|
||||
/*return 0;*/
|
||||
|
||||
/*error:*/
|
||||
/*wfd_src_close(s);*/
|
||||
/*wfd_fn_src_disconnected(s);*/
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
const struct wfd_session_vtable session_vtables[] = {
|
||||
[WFD_SESSION_DIR_OUT] = {
|
||||
.initiate_io = wfd_out_session_initiate_io,
|
||||
.handle_io = wfd_out_session_handle_io,
|
||||
.initiate_request = wfd_out_session_initiate_request,
|
||||
.end = wfd_out_session_end,
|
||||
.distruct = wfd_out_session_distruct,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct rtsp_dispatch_entry out_session_rtsp_disp_tbl[] = {
|
||||
[RTSP_M1_REQUEST_SINK_OPTIONS] = {
|
||||
.request = wfd_out_session_request_options,
|
||||
.handle_reply = wfd_out_session_check_options_reply
|
||||
},
|
||||
[RTSP_M2_REQUEST_SRC_OPTIONS] = {
|
||||
.handle_request = wfd_out_session_handle_options_request
|
||||
},
|
||||
[RTSP_M3_GET_PARAMETER] = {
|
||||
},
|
||||
[RTSP_M4_SET_PARAMETER] = {
|
||||
},
|
||||
[RTSP_M5_TRIGGER] = {
|
||||
},
|
||||
[RTSP_M6_SETUP] = {
|
||||
},
|
||||
[RTSP_M7_PLAY] = {
|
||||
},
|
||||
[RTSP_M8_TEARDOWN] = {
|
||||
},
|
||||
[RTSP_M9_PAUSE] = {
|
||||
},
|
||||
[RTSP_M10_SET_ROUTE] = {
|
||||
},
|
||||
[RTSP_M11_SET_CONNECTOR_TYPE] = {
|
||||
},
|
||||
[RTSP_M12_SET_STANDBY] = {
|
||||
},
|
||||
[RTSP_M13_REQUEST_IDR] = {
|
||||
},
|
||||
[RTSP_M14_ESTABLISH_UIBC] = {
|
||||
},
|
||||
[RTSP_M15_ENABLE_UIBC] = {
|
||||
},
|
||||
[RTSP_M16_KEEPALIVE] = {
|
||||
},
|
||||
};
|
|
@ -18,68 +18,49 @@
|
|||
*/
|
||||
#define LOG_SUBSYSTEM "wfd-session"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include "ctl.h"
|
||||
#include "rtsp.h"
|
||||
#include "wfd-dbus.h"
|
||||
#include "wfd-session.h"
|
||||
#include "shl_macro.h"
|
||||
|
||||
#define wfd_out_session(s) (assert(wfd_is_out_session(s)), (struct wfd_out_session *) (s))
|
||||
#define wfd_in_session(s) (assert(wfd_is_in_session(s)), (struct wfd_in_session *) (s))
|
||||
#define wfd_message_id_valide(id) ( \
|
||||
(id) >= RTSP_M1_REQUEST_SINK_OPTIONS && \
|
||||
(id) <= RTSP_M16_KEEPALIVE \
|
||||
)
|
||||
|
||||
struct wfd_session_vtable
|
||||
static const char *rtsp_message_names[];
|
||||
extern const struct wfd_session_vtable session_vtables[];
|
||||
extern const struct rtsp_dispatch_entry out_session_rtsp_disp_tbl[];
|
||||
|
||||
static inline int wfd_session_do_request(struct wfd_session *s,
|
||||
enum rtsp_message_id id,
|
||||
struct rtsp_message **out)
|
||||
{
|
||||
int (*start)(struct wfd_session *s);
|
||||
void (*end)(struct wfd_session *s);
|
||||
void (*distruct)(struct wfd_session *s);
|
||||
};
|
||||
assert(wfd_message_id_valide(id));
|
||||
assert(s->rtsp_disp_tbl[id].request);
|
||||
|
||||
struct wfd_out_session
|
||||
{
|
||||
struct wfd_session parent;
|
||||
struct wfd_sink *sink;
|
||||
int fd;
|
||||
|
||||
bool sink_has_video: 1;
|
||||
bool sink_has_audio: 1;
|
||||
bool sink_has_pref_disp_mode: 1;
|
||||
bool sink_has_3d: 1;
|
||||
bool sink_has_uibc: 1;
|
||||
};
|
||||
|
||||
static void wfd_session_set_state(struct wfd_session *s, enum wfd_session_state state);
|
||||
static int wfd_out_session_start(struct wfd_session *s);
|
||||
static void wfd_out_session_end(struct wfd_session *s);
|
||||
static void wfd_out_session_distruct(struct wfd_session *s);
|
||||
|
||||
static const struct wfd_session_vtable session_vtables[] = {
|
||||
[WFD_SESSION_DIR_OUT] = {
|
||||
.start = wfd_out_session_start,
|
||||
.end = wfd_out_session_end,
|
||||
.distruct = wfd_out_session_distruct,
|
||||
}
|
||||
};
|
||||
|
||||
int wfd_out_session_new(struct wfd_session **out, struct wfd_sink *sink)
|
||||
{
|
||||
struct wfd_out_session *s = calloc(1, sizeof(struct wfd_out_session));
|
||||
if(!s) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
wfd_session(s)->dir = WFD_SESSION_DIR_OUT;
|
||||
s->fd = -1;
|
||||
s->sink = sink;
|
||||
|
||||
*out = wfd_session(s);
|
||||
|
||||
return 0;
|
||||
return (*s->rtsp_disp_tbl[id].request)(s, out);
|
||||
}
|
||||
|
||||
struct wfd_sink * wfd_out_session_get_sink(struct wfd_session *s)
|
||||
static inline int wfd_session_do_handle_request(struct wfd_session *s,
|
||||
enum rtsp_message_id id,
|
||||
struct rtsp_message *req,
|
||||
struct rtsp_message **out_rep)
|
||||
{
|
||||
assert(wfd_is_out_session(s));
|
||||
assert(wfd_message_id_valide(id));
|
||||
assert(s->rtsp_disp_tbl[id].handle_request);
|
||||
|
||||
return wfd_out_session(s)->sink;
|
||||
return (*s->rtsp_disp_tbl[id].handle_request)(s, req, out_rep);
|
||||
}
|
||||
|
||||
static inline int wfd_session_do_handle_reply(struct wfd_session *s,
|
||||
enum rtsp_message_id id,
|
||||
struct rtsp_message *m)
|
||||
{
|
||||
assert(wfd_message_id_valide(id));
|
||||
assert(s->rtsp_disp_tbl[id].handle_reply);
|
||||
|
||||
return (*s->rtsp_disp_tbl[id].handle_reply)(s, m);
|
||||
}
|
||||
|
||||
uint64_t wfd_session_get_id(struct wfd_session *s)
|
||||
|
@ -87,6 +68,11 @@ uint64_t wfd_session_get_id(struct wfd_session *s)
|
|||
return s->id;
|
||||
}
|
||||
|
||||
enum wfd_session_state wfd_session_get_state(struct wfd_session *s)
|
||||
{
|
||||
return s->state;
|
||||
}
|
||||
|
||||
static void wfd_session_set_state(struct wfd_session *s,
|
||||
enum wfd_session_state state)
|
||||
{
|
||||
|
@ -99,26 +85,6 @@ static void wfd_session_set_state(struct wfd_session *s,
|
|||
wfd_fn_session_properties_changed(s, "State");
|
||||
}
|
||||
|
||||
int wfd_session_start(struct wfd_session *s, uint64_t id)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(wfd_is_session(s));
|
||||
assert(id);
|
||||
|
||||
if(wfd_session_is_started(s)) {
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
r = (*session_vtables[s->dir].start)(s);
|
||||
if(0 <= r) {
|
||||
s->id = id;
|
||||
wfd_session_set_state(s, WFD_SESSION_STATE_CONNECTING);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int wfd_session_is_started(struct wfd_session *s)
|
||||
{
|
||||
assert(wfd_is_session(s));
|
||||
|
@ -130,7 +96,7 @@ void wfd_session_end(struct wfd_session *s)
|
|||
{
|
||||
assert(wfd_is_session(s));
|
||||
|
||||
if(!wfd_session_is_started(s)) {
|
||||
if(WFD_SESSION_STATE_NULL == s->state) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -148,8 +114,6 @@ void wfd_session_end(struct wfd_session *s)
|
|||
s->url = NULL;
|
||||
}
|
||||
|
||||
s->hup = false;
|
||||
|
||||
wfd_session_set_state(s, WFD_SESSION_STATE_NULL);
|
||||
|
||||
if(wfd_is_out_session(s)) {
|
||||
|
@ -159,20 +123,16 @@ void wfd_session_end(struct wfd_session *s)
|
|||
|
||||
void wfd_session_free(struct wfd_session *s)
|
||||
{
|
||||
enum wfd_session_dir dir;
|
||||
|
||||
if(!wfd_is_session(s)) {
|
||||
if(wfd_session_is_destructed(s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(wfd_is_out_session(s)) {
|
||||
wfd_out_session(s)->sink = NULL;
|
||||
}
|
||||
s->destructed = true;
|
||||
|
||||
wfd_session_end(s);
|
||||
|
||||
if(session_vtables[dir].distruct) {
|
||||
(*session_vtables[dir].distruct)(s);
|
||||
if(session_vtables[s->dir].distruct) {
|
||||
(*session_vtables[s->dir].distruct)(s);
|
||||
}
|
||||
|
||||
free(s);
|
||||
|
@ -188,7 +148,17 @@ const char * wfd_session_get_url(struct wfd_session *s)
|
|||
return s->url;
|
||||
}
|
||||
|
||||
static int wfd_session_gen_url(struct wfd_session *s, const char *addr)
|
||||
uint64_t * wfd_session_to_htable(struct wfd_session *s)
|
||||
{
|
||||
return &s->id;
|
||||
}
|
||||
|
||||
struct wfd_session * wfd_session_from_htable(uint64_t *e)
|
||||
{
|
||||
return shl_htable_entry(e, struct wfd_session, id);
|
||||
}
|
||||
|
||||
static int wfd_session_set_url(struct wfd_session *s, const char *addr)
|
||||
{
|
||||
char *url;
|
||||
int r = asprintf(&url, "rtsp://%s/wfd1.0", addr);
|
||||
|
@ -200,117 +170,160 @@ static int wfd_session_gen_url(struct wfd_session *s, const char *addr)
|
|||
return r;
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_get_parameter_reply(struct rtsp *bus,
|
||||
static enum rtsp_message_id wfd_session_message_to_id(struct wfd_session *s,
|
||||
struct rtsp_message *m)
|
||||
{
|
||||
|
||||
const char *method = m ? rtsp_message_get_method(m) : NULL;
|
||||
if(!method) {
|
||||
return RTSP_M_UNKNOWN;
|
||||
}
|
||||
|
||||
if(!strcmp(method, "SET_PARAMETER")) {
|
||||
if(0 >= rtsp_message_read(m, "{<>}", "wfd_trigger_method")) {
|
||||
return RTSP_M5_TRIGGER;
|
||||
}
|
||||
|
||||
if(0 >= rtsp_message_read(m, "{<>}", "wfd_route")) {
|
||||
return RTSP_M10_SET_ROUTE;
|
||||
}
|
||||
|
||||
if(0 >= rtsp_message_read(m, "{<>}", "wfd_connector_type")) {
|
||||
return RTSP_M11_SET_CONNECTOR_TYPE;
|
||||
}
|
||||
|
||||
if(0 >= rtsp_message_read(m, "{<>}", "wfd_standby")) {
|
||||
return RTSP_M12_SET_STANDBY;
|
||||
}
|
||||
|
||||
if(0 >= rtsp_message_read(m, "{<>}", "wfd_idr_request")) {
|
||||
return RTSP_M13_REQUEST_IDR;
|
||||
}
|
||||
|
||||
if(0 >= rtsp_message_read(m, "{<>}", "wfd_uibc_capability") && s->url) {
|
||||
return RTSP_M14_ESTABLISH_UIBC;
|
||||
}
|
||||
|
||||
if(0 >= rtsp_message_read(m, "{<>}", "wfd_uibc_capability")) {
|
||||
return RTSP_M15_ENABLE_UIBC;
|
||||
}
|
||||
|
||||
if(!s->url) {
|
||||
return RTSP_M4_SET_PARAMETER;
|
||||
}
|
||||
|
||||
return RTSP_M_UNKNOWN;
|
||||
}
|
||||
|
||||
if(!strcmp(method, "OPTIONS")) {
|
||||
return wfd_is_out_session(s)
|
||||
? (RTSP_MESSAGE_REPLY == rtsp_message_get_type(m))
|
||||
? RTSP_M1_REQUEST_SINK_OPTIONS
|
||||
: RTSP_M2_REQUEST_SRC_OPTIONS
|
||||
: (RTSP_MESSAGE_REPLY == rtsp_message_get_type(m))
|
||||
? RTSP_M2_REQUEST_SRC_OPTIONS
|
||||
: RTSP_M1_REQUEST_SINK_OPTIONS;
|
||||
}
|
||||
else if(!strcmp(method, "GET_PARAMETER")) {
|
||||
if(rtsp_message_get_body_size(m)) {
|
||||
return RTSP_M3_GET_PARAMETER;
|
||||
}
|
||||
|
||||
return RTSP_M16_KEEPALIVE;
|
||||
}
|
||||
else if(!strcmp(method, "SETUP")) {
|
||||
return RTSP_M6_SETUP;
|
||||
}
|
||||
else if(!strcmp(method, "PLAY")) {
|
||||
return RTSP_M7_PLAY;
|
||||
}
|
||||
else if(!strcmp(method, "TEARDOWN")) {
|
||||
return RTSP_M8_TEARDOWN;
|
||||
}
|
||||
else if(!strcmp(method, "PAUSE")) {
|
||||
return RTSP_M9_PAUSE;
|
||||
}
|
||||
|
||||
return RTSP_M_UNKNOWN;
|
||||
}
|
||||
|
||||
static int wfd_session_dispatch_request(struct rtsp *bus,
|
||||
struct rtsp_message *m,
|
||||
void *userdata)
|
||||
{
|
||||
log_debug("Received GET_PARAMETER reply (M4): %s\n",
|
||||
(char *) rtsp_message_get_raw(m));
|
||||
if(RTSP_CODE_OK == rtsp_message_get_code(m)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_warning("Sink reply GET_PARAMETER (M4) with code: %d",
|
||||
rtsp_message_get_code(m));
|
||||
|
||||
wfd_session_end(wfd_session(userdata));
|
||||
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
static int wfd_out_session_send_get_parameter(sd_event_source *source,
|
||||
void *userdata)
|
||||
{
|
||||
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
|
||||
struct wfd_session *s = userdata;
|
||||
_rtsp_message_unref_ struct rtsp_message *m = NULL;
|
||||
int r = rtsp_message_new_request(s->rtsp,
|
||||
&m,
|
||||
"GET_PARAMETER",
|
||||
s->url);
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
enum rtsp_message_id id;
|
||||
int r;
|
||||
|
||||
id = wfd_session_message_to_id(s, m);
|
||||
if(RTSP_M_UNKNOWN == id) {
|
||||
r = -EPROTO;
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = rtsp_message_append(m, "{&}",
|
||||
"wfd_video_formats\n"
|
||||
"wfd_audio_codecs\n"
|
||||
"wfd_client_rtp_ports"
|
||||
//"wfd_uibc_capability"
|
||||
);
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rtsp_message_seal(m);
|
||||
|
||||
r = rtsp_call_async(s->rtsp,
|
||||
m,
|
||||
wfd_out_session_handle_get_parameter_reply,
|
||||
s,
|
||||
0,
|
||||
NULL);
|
||||
if (r < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
log_debug("Sending GET_PARAMETER (M3): %s\n",
|
||||
log_trace("received %s (M%d) request: %s", rtsp_message_id_to_string(id),
|
||||
id,
|
||||
(char *) rtsp_message_get_raw(m));
|
||||
|
||||
return 0;
|
||||
r = wfd_session_do_handle_request(s, id, m, &rep);
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
error:
|
||||
wfd_session_end(s);
|
||||
r = rtsp_message_set_cookie(rep, id);
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = rtsp_message_seal(rep);
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = rtsp_send(bus, rep);
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
log_trace("sending %s (M%d) reply: %s", rtsp_message_id_to_string(id),
|
||||
id,
|
||||
(char *) rtsp_message_get_raw(rep));
|
||||
|
||||
end:
|
||||
if (0 > r) {
|
||||
wfd_session_end(s);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_options_reply(struct rtsp *bus,
|
||||
static int wfd_session_dispatch_reply(struct rtsp *bus,
|
||||
struct rtsp_message *m,
|
||||
void *userdata)
|
||||
{
|
||||
int r = 0, i;
|
||||
int r;
|
||||
enum rtsp_message_id id;
|
||||
struct wfd_session *s = userdata;
|
||||
char *public, *methods[3] = { NULL };
|
||||
|
||||
log_trace("received OPTIONS reply (M1): %s",
|
||||
(char *) rtsp_message_get_raw(m));
|
||||
if(!m) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(!rtsp_message_is_reply(m, RTSP_CODE_OK, NULL)) {
|
||||
r = -EPROTO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = rtsp_message_read(m, "<&>", "Public", &public);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
id = (enum rtsp_message_id) rtsp_message_get_cookie(m);
|
||||
|
||||
r = sscanf(public, "%m[^,], %m[^,], %ms", &methods[0], &methods[1], &methods[2]);
|
||||
if(3 != r) {
|
||||
r = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
log_trace("received %s (M%d) reply: %s",
|
||||
rtsp_message_id_to_string(id),
|
||||
id,
|
||||
(char *) rtsp_message_get_raw(m));
|
||||
|
||||
for(i = 0; i < SHL_ARRAY_LENGTH(methods); i ++) {
|
||||
if(strcmp("org.wfa.wfd1.0", methods[i]) &&
|
||||
strcmp("SET_PARAMETER", methods[i]) &&
|
||||
strcmp("GET_PARAMETER", methods[i]))
|
||||
{
|
||||
r = -EINVAL;
|
||||
log_info("Got invalid method from sink: %s", methods[i]);
|
||||
}
|
||||
|
||||
free(methods[i]);
|
||||
}
|
||||
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_event_add_defer(ctl_wfd_get_loop(),
|
||||
NULL,
|
||||
wfd_out_session_send_get_parameter,
|
||||
s);
|
||||
r = wfd_session_do_handle_reply(s, id, m);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -319,351 +332,172 @@ static int wfd_out_session_handle_options_reply(struct rtsp *bus,
|
|||
|
||||
error:
|
||||
wfd_session_end(s);
|
||||
|
||||
log_info("error occured while handling reply of OPTIONS");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wfd_out_session_send_options(sd_event_source *source,
|
||||
void *userdata)
|
||||
int wfd_session_request(struct wfd_session *s, enum rtsp_message_id id)
|
||||
{
|
||||
struct wfd_session *s = userdata;
|
||||
int r;
|
||||
_rtsp_message_unref_ struct rtsp_message *m = NULL;
|
||||
int r = rtsp_message_new_request(s->rtsp,
|
||||
&m,
|
||||
"OPTIONS", "*");
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
uint64_t cookie = id;
|
||||
|
||||
assert(s);
|
||||
|
||||
r = wfd_session_do_request(s, id, &m);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = rtsp_message_append(m,
|
||||
"<s>",
|
||||
"Require", "org.wfa.wfd1.0");
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = rtsp_message_seal(m);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
return r;
|
||||
}
|
||||
|
||||
r = rtsp_call_async(s->rtsp,
|
||||
m,
|
||||
wfd_out_session_handle_options_reply,
|
||||
wfd_session_dispatch_reply,
|
||||
s,
|
||||
0,
|
||||
NULL);
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
log_debug("sending OPTIONS (M1): %s", (char *) rtsp_message_get_raw(m));
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
log_info("failed to send OPTIONS request: %s", strerror(errno));
|
||||
wfd_session_end(s);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_options_request(struct rtsp *bus,
|
||||
struct rtsp_message *request,
|
||||
struct wfd_session *s)
|
||||
{
|
||||
const char *require;
|
||||
_rtsp_message_unref_ struct rtsp_message *m = NULL;
|
||||
int r = rtsp_message_read(request, "<s>", "Require", &require);
|
||||
&cookie);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
return r;
|
||||
}
|
||||
|
||||
if(strcmp("org.wfa.wfd1.0", require)) {
|
||||
r = rtsp_message_new_reply_for(request,
|
||||
&m,
|
||||
RTSP_CODE_OPTION_NOT_SUPPORTED,
|
||||
"Invalid specification");
|
||||
}
|
||||
else {
|
||||
r = rtsp_message_new_reply_for(request,
|
||||
&m,
|
||||
RTSP_CODE_OK,
|
||||
NULL);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = rtsp_message_append(m,
|
||||
"<s>",
|
||||
"Public", "org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER");
|
||||
}
|
||||
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = rtsp_message_seal(m);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = rtsp_send(bus, m);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
log_debug("sending OPTIONS reply (M2): %s",
|
||||
log_trace("sending %s (M%d) request: %s",
|
||||
rtsp_message_id_to_string(id),
|
||||
id,
|
||||
(char *) rtsp_message_get_raw(m));
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
s->hup = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wfd_out_session_dispatch_request(struct rtsp *bus,
|
||||
struct rtsp_message *m,
|
||||
void *userdata)
|
||||
{
|
||||
const char *method;
|
||||
struct wfd_session *s = userdata;
|
||||
int r = 0;
|
||||
|
||||
if (!m) {
|
||||
s->hup = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
method = rtsp_message_get_method(m);
|
||||
if(!method) {
|
||||
log_info("unexpected message: %s", (char *) rtsp_message_get_raw(m));
|
||||
r = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!strcmp(method, "OPTIONS")) {
|
||||
r = wfd_out_session_handle_options_request(bus, m, s);
|
||||
}
|
||||
/*else if (!strcmp(method, "GET_PARAMETER")) {*/
|
||||
/*wfd_out_session_handle_get_parameter_reply(s, m);*/
|
||||
/*}*/
|
||||
/*[>else if (!strcmp(method, "SETUP")) {<]*/
|
||||
/*[>wfd_out_session_handle_setup_reply(s, m);<]*/
|
||||
/*[>}<]*/
|
||||
/*[>else if (!strcmp(method, "PLAY")) {<]*/
|
||||
/*[>wfd_out_session_handle_play_reply(s, m);<]*/
|
||||
/*[>}<]*/
|
||||
/*[>else if (!strcmp(method, "PAUSE")) {<]*/
|
||||
/*[>wfd_out_session_handle_pause_reply(s, m);<]*/
|
||||
/*[>}<]*/
|
||||
/*[>else if (!strcmp(method, "TEARDOWN")) {<]*/
|
||||
/*[>wfd_out_session_handle_teardown_reply(s, m);<]*/
|
||||
/*[>}<]*/
|
||||
|
||||
end:
|
||||
if (s->hup) {
|
||||
wfd_session_end(s);
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
static int wfd_out_session_accept_connection(struct wfd_out_session *os)
|
||||
{
|
||||
int r;
|
||||
socklen_t len;
|
||||
struct sockaddr_storage addr;
|
||||
_shl_close_ int fd = -1;
|
||||
sd_event *loop = ctl_wfd_get_loop();
|
||||
struct wfd_session *s = wfd_session(os);
|
||||
_rtsp_unref_ struct rtsp *rtsp = NULL;
|
||||
|
||||
log_debug("accepting incoming RTSP connection\n");
|
||||
|
||||
len = sizeof(addr);
|
||||
fd = accept4(os->fd,
|
||||
(struct sockaddr *) &addr,
|
||||
&len,
|
||||
SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
if(0 > fd) {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
log_info("RTSP connection established");
|
||||
|
||||
r = rtsp_open(&rtsp, fd);
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
fd = -1;
|
||||
|
||||
r = rtsp_attach_event(rtsp, loop, 0);
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = rtsp_add_match(rtsp, wfd_out_session_dispatch_request, s);
|
||||
if (0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_event_add_defer(loop, NULL, wfd_out_session_send_options, s);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
wfd_session_set_state(s, WFD_SESSION_STATE_CAPS_EXCHAING);
|
||||
|
||||
s->rtsp = rtsp;
|
||||
rtsp = NULL;
|
||||
|
||||
close(os->fd);
|
||||
os->fd = -1;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
s->hup = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wfd_out_session_handle_io(sd_event_source *source,
|
||||
static int wfd_session_handle_io(sd_event_source *source,
|
||||
int fd,
|
||||
uint32_t mask,
|
||||
void *userdata)
|
||||
{
|
||||
int r, val;
|
||||
int r, err = 0, conn;
|
||||
socklen_t len;
|
||||
struct wfd_session *s = userdata;
|
||||
_rtsp_unref_ struct rtsp *rtsp = NULL;
|
||||
|
||||
sd_event_source_set_enabled(source, SD_EVENT_OFF);
|
||||
|
||||
if (mask & EPOLLERR) {
|
||||
r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len);
|
||||
s->hup = true;
|
||||
if(0 <= r) {
|
||||
r = -val;
|
||||
errno = val;
|
||||
r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (mask & EPOLLIN) {
|
||||
r = wfd_out_session_accept_connection(userdata);
|
||||
if (mask & EPOLLIN || mask & EPOLLOUT) {
|
||||
r = (*session_vtables[s->dir].handle_io)(s, err, &conn);
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = rtsp_open(&rtsp, conn);
|
||||
if (0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
conn = -1;
|
||||
|
||||
r = rtsp_attach_event(rtsp, ctl_wfd_get_loop(), 0);
|
||||
if (0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = rtsp_add_match(rtsp, wfd_session_dispatch_request, s);
|
||||
if (0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
s->rtsp = rtsp;
|
||||
rtsp = NULL;
|
||||
|
||||
wfd_session_set_state(s, WFD_SESSION_STATE_CAPS_EXCHAING);
|
||||
|
||||
r = (*session_vtables[s->dir].initiate_request)(s);
|
||||
}
|
||||
|
||||
if(mask & EPOLLHUP) {
|
||||
r = -ESHUTDOWN;
|
||||
}
|
||||
|
||||
end:
|
||||
if (s->hup) {
|
||||
log_info("failed to accept remote connection: %s", strerror(errno));
|
||||
if (0 > r) {
|
||||
wfd_session_end(s);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int wfd_out_session_start(struct wfd_session *s)
|
||||
int wfd_session_start(struct wfd_session *s, uint64_t id)
|
||||
{
|
||||
struct wfd_out_session *os = wfd_out_session(s);
|
||||
union wfd_sube sube;
|
||||
struct sockaddr_in addr = {};
|
||||
struct ctl_peer *p = os->sink->peer;
|
||||
_shl_close_ int fd = -1;
|
||||
int enable;
|
||||
int r;
|
||||
_shl_close_ int fd = -1;
|
||||
uint32_t mask;
|
||||
|
||||
if(!os->sink->peer->connected) {
|
||||
log_info("peer not connected yet");
|
||||
return -ENOTCONN;
|
||||
assert(wfd_is_session(s));
|
||||
assert(id);
|
||||
|
||||
if(wfd_session_is_started(s)) {
|
||||
return -EINPROGRESS;
|
||||
}
|
||||
|
||||
r = wfd_sube_parse_with_id(WFD_SUBE_ID_DEVICE_INFO,
|
||||
p->l->wfd_subelements,
|
||||
&sube);
|
||||
if(0 > r) {
|
||||
log_warning("WfdSubelements property of link must be set before P2P scan");
|
||||
return -EINVAL;
|
||||
}
|
||||
else if(WFD_SUBE_ID_DEVICE_INFO != sube.id) {
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
if(-1 != os->fd) {
|
||||
return EINPROGRESS;
|
||||
}
|
||||
|
||||
r = inet_pton(AF_INET, p->local_address, &addr.sin_addr);
|
||||
if (!r) {
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(wfd_sube_device_get_rtsp_port(&sube));
|
||||
|
||||
fd = socket(addr.sin_family,
|
||||
SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
||||
0);
|
||||
if (0 > fd) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
enable = true;
|
||||
r = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable));
|
||||
r = (*session_vtables[s->dir].initiate_io)(s, &fd, &mask);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
|
||||
if (0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = listen(fd, 10);
|
||||
if (0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
log_trace("socket listening on %s:%hu",
|
||||
p->local_address,
|
||||
wfd_sube_device_get_rtsp_port(&sube));
|
||||
|
||||
r = sd_event_add_io(ctl_wfd_get_loop(),
|
||||
NULL,
|
||||
fd,
|
||||
EPOLLIN,
|
||||
wfd_out_session_handle_io,
|
||||
mask,
|
||||
wfd_session_handle_io,
|
||||
s);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = wfd_session_gen_url(s, p->local_address);
|
||||
if(0 <= r) {
|
||||
os->fd = fd;
|
||||
fd = -1;
|
||||
}
|
||||
fd = -1;
|
||||
|
||||
return r;
|
||||
s->id = id;
|
||||
wfd_session_set_state(s, WFD_SESSION_STATE_CONNECTING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wfd_out_session_end(struct wfd_session *s)
|
||||
void wfd_session_freep(struct wfd_session **s)
|
||||
{
|
||||
struct wfd_out_session *os = wfd_out_session(s);
|
||||
if(os->fd) {
|
||||
close(os->fd);
|
||||
os->fd = -1;
|
||||
}
|
||||
wfd_session_free(*s);
|
||||
}
|
||||
|
||||
static void wfd_out_session_distruct(struct wfd_session *s)
|
||||
const char * rtsp_message_id_to_string(enum rtsp_message_id id)
|
||||
{
|
||||
struct wfd_out_session *os = wfd_out_session(s);
|
||||
if(0 <= os->fd) {
|
||||
close(os->fd);
|
||||
}
|
||||
assert(id >= 0 && 16 >= id);
|
||||
|
||||
return rtsp_message_names[id];
|
||||
}
|
||||
|
||||
static const char *rtsp_message_names[] = {
|
||||
"UNKNOWN",
|
||||
"OPTIONS(src->sink)",
|
||||
"OPTIONS(sink->src)",
|
||||
"GET_PARAM",
|
||||
"SET_PARAM",
|
||||
"SET_PARAM(wfd-trigger-method)",
|
||||
"SETUP",
|
||||
"PLAY",
|
||||
"TEARDOWN",
|
||||
"PAUSE",
|
||||
"SET_PARAM(wfd-route)",
|
||||
"SET_PARAM(wfd-connector-type)",
|
||||
"SET_PARAM(wfd-standby)",
|
||||
"SET_PARAM(wfd-idr-request)",
|
||||
"SET_PARAM(wfd-uibc-cability)",
|
||||
"SET_PARAM(wfd-uibc-setting)",
|
||||
"GET_PARAM(keepalive)",
|
||||
};
|
||||
|
||||
|
|
91
src/ctl/wfd-session.h
Normal file
91
src/ctl/wfd-session.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef MIRACLE_OUT_SESSION_H
|
||||
#define MIRACLE_OUT_SESSION_H
|
||||
|
||||
#include "ctl.h"
|
||||
|
||||
#define wfd_out_session(s) (assert(wfd_is_out_session(s)), (struct wfd_out_session *) (s))
|
||||
#define wfd_in_session(s) (assert(wfd_is_in_session(s)), (struct wfd_in_session *) (s))
|
||||
#define wfd_session_is_destructed(s) (!(s) || (s)->destructed)
|
||||
|
||||
struct wfd_session;
|
||||
struct wfd_sink;
|
||||
struct rtsp;
|
||||
struct rtsp_message;
|
||||
|
||||
enum rtsp_message_id
|
||||
{
|
||||
RTSP_M_UNKNOWN,
|
||||
RTSP_M1_REQUEST_SINK_OPTIONS,
|
||||
RTSP_M2_REQUEST_SRC_OPTIONS,
|
||||
RTSP_M3_GET_PARAMETER,
|
||||
RTSP_M4_SET_PARAMETER,
|
||||
RTSP_M5_TRIGGER,
|
||||
RTSP_M6_SETUP,
|
||||
RTSP_M7_PLAY,
|
||||
RTSP_M8_TEARDOWN,
|
||||
RTSP_M9_PAUSE,
|
||||
RTSP_M10_SET_ROUTE,
|
||||
RTSP_M11_SET_CONNECTOR_TYPE,
|
||||
RTSP_M12_SET_STANDBY,
|
||||
RTSP_M13_REQUEST_IDR,
|
||||
RTSP_M14_ESTABLISH_UIBC,
|
||||
RTSP_M15_ENABLE_UIBC,
|
||||
RTSP_M16_KEEPALIVE,
|
||||
};
|
||||
|
||||
struct rtsp_dispatch_entry
|
||||
{
|
||||
union {
|
||||
int (*request)(struct wfd_session *s, struct rtsp_message **out);
|
||||
int (*handle_request)(struct wfd_session *s,
|
||||
struct rtsp_message *req,
|
||||
struct rtsp_message **out_rep);
|
||||
};
|
||||
int (*handle_reply)(struct wfd_session *s, struct rtsp_message *m);
|
||||
enum rtsp_message_id next_request;
|
||||
};
|
||||
|
||||
struct wfd_session_vtable
|
||||
{
|
||||
int (*initiate_io)(struct wfd_session *s, int *out_fd, uint32_t *out_mask);
|
||||
int (*handle_io)(struct wfd_session *s, int error, int *out_fd);
|
||||
int (*initiate_request)(struct wfd_session *s);
|
||||
void (*end)(struct wfd_session *s);
|
||||
void (*distruct)(struct wfd_session *s);
|
||||
};
|
||||
|
||||
struct wfd_session
|
||||
{
|
||||
enum wfd_session_dir dir;
|
||||
enum wfd_session_state state;
|
||||
uint64_t id;
|
||||
char *url;
|
||||
struct rtsp *rtsp;
|
||||
const struct rtsp_dispatch_entry *rtsp_disp_tbl;
|
||||
|
||||
bool destructed: 1;
|
||||
};
|
||||
|
||||
const char * rtsp_message_id_to_string(enum rtsp_message_id id);
|
||||
struct wfd_sink * wfd_out_session_get_sink(struct wfd_session *s);
|
||||
int wfd_session_request(struct wfd_session *s, enum rtsp_message_id id);
|
||||
|
||||
#endif /* MIRACLE_OUT_SESSION_H */
|
|
@ -121,15 +121,17 @@ int wfd_sink_start_session(struct wfd_sink *sink, struct wfd_session **out)
|
|||
|
||||
int wfd_fn_out_session_ended(struct wfd_session *s)
|
||||
{
|
||||
struct wfd_sink *sink;
|
||||
|
||||
assert(wfd_is_out_session(s));
|
||||
|
||||
struct wfd_sink *sink = wfd_out_session_get_sink(s);
|
||||
if(sink) {
|
||||
wfd_fn_sink_properties_changed(sink, "Session");
|
||||
ctl_wfd_remove_session_by_id(ctl_wfd_get(), s->id, NULL);
|
||||
sink->session = NULL;
|
||||
wfd_session_free(s);
|
||||
}
|
||||
sink = wfd_out_session_get_sink(s);
|
||||
wfd_fn_sink_properties_changed(sink, "Session");
|
||||
ctl_wfd_remove_session_by_id(ctl_wfd_get(),
|
||||
wfd_session_get_id(s),
|
||||
NULL);
|
||||
sink->session = NULL;
|
||||
wfd_session_free(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -191,8 +191,8 @@ int ctl_wfd_add_session(struct ctl_wfd *wfd, struct wfd_session *s)
|
|||
int r;
|
||||
|
||||
assert(wfd);
|
||||
assert(s && s->id);
|
||||
assert(!ctl_wfd_find_session_by_id(wfd, s->id, NULL));
|
||||
assert(s && wfd_session_get_id(s));
|
||||
assert(!ctl_wfd_find_session_by_id(wfd, wfd_session_get_id(s), NULL));
|
||||
|
||||
r = shl_htable_insert_u64(&wfd->sessions, wfd_session_to_htable(s));
|
||||
if(0 > r) {
|
||||
|
@ -258,16 +258,10 @@ static int ctl_wfd_handle_signal(sd_event_source *s,
|
|||
const struct signalfd_siginfo *si,
|
||||
void *userdata)
|
||||
{
|
||||
int r;
|
||||
sd_event_source *exit_source;
|
||||
struct ctl_wfd *wfd = userdata;
|
||||
ctl_wfd_destroy(wfd);
|
||||
|
||||
sd_event_exit(wfd->loop, 0);
|
||||
|
||||
sd_event_source_unref(s);
|
||||
|
||||
return 0;
|
||||
return sd_event_exit(wfd->loop, 0);
|
||||
}
|
||||
|
||||
static int ctl_wfd_init(struct ctl_wfd *wfd, sd_bus *bus)
|
||||
|
@ -372,7 +366,7 @@ void ctl_fn_peer_new(struct ctl_peer *p)
|
|||
void ctl_fn_peer_free(struct ctl_peer *p)
|
||||
{
|
||||
struct wfd_sink *s;
|
||||
_shl_free_ char *label;
|
||||
_shl_free_ char *label = NULL;
|
||||
int r = ctl_wfd_remove_sink_by_label(wfd, p->label, &s);
|
||||
if(!r) {
|
||||
return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue