1
0
Fork 0
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:
Derek Dai 2017-02-14 10:09:40 +08:00
parent 6bcfe4e541
commit 6c55a332d2
No known key found for this signature in database
GPG key ID: E109CC97553EF009
9 changed files with 915 additions and 497 deletions

View file

@ -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)",
};