mirror of
https://github.com/albfan/miraclecast.git
synced 2025-03-09 23:38:56 +00:00
init copy & modify
This commit is contained in:
parent
9cd56f3eff
commit
5f39158f45
6 changed files with 1674 additions and 0 deletions
|
@ -46,6 +46,32 @@ target_link_libraries(miracle-sinkctl miracle-shared)
|
|||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
set(miracle-srcctl_SRCS ctl.h
|
||||
ctl-cli.c
|
||||
ctl-src.h
|
||||
ctl-src.c
|
||||
ctl-wifi.c
|
||||
srcctl.c
|
||||
wfd.c)
|
||||
|
||||
add_executable(miracle-srcctl ${miracle-srcctl_SRCS})
|
||||
|
||||
install(TARGETS miracle-srcctl DESTINATION bin)
|
||||
|
||||
if(READLINE_FOUND)
|
||||
message(STATUS "Compiling with Readline support")
|
||||
set_property(TARGET miracle-srcctl
|
||||
APPEND
|
||||
PROPERTY COMPILE_DEFINITIONS HAVE_READLINE)
|
||||
target_link_libraries(miracle-srcctl ${READLINE_LIBRARY})
|
||||
endif(READLINE_FOUND)
|
||||
|
||||
target_link_libraries(miracle-srcctl miracle-shared)
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
|
||||
|
||||
########### install files ###############
|
||||
|
||||
|
||||
|
|
609
src/ctl/ctl-src.c
Normal file
609
src/ctl/ctl-src.c
Normal file
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "ctl-src.h"
|
||||
#include "util.h"
|
||||
|
||||
///*
|
||||
// * RTSP Session
|
||||
// */
|
||||
//
|
||||
//static int sink_req_fn(struct rtsp *bus, struct rtsp_message *m, void *data)
|
||||
//{
|
||||
// cli_debug("INCOMING: %s\n", rtsp_message_get_raw(m));
|
||||
// return 0;
|
||||
//}
|
||||
//
|
||||
//static void sink_handle_options(struct ctl_sink *s,
|
||||
// struct rtsp_message *m)
|
||||
//{
|
||||
// _rtsp_message_unref_ struct rtsp_message *rep = NULL;
|
||||
// int r;
|
||||
//
|
||||
// r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// r = rtsp_message_append(rep, "<s>",
|
||||
// "Public",
|
||||
// "org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER");
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// rtsp_message_seal(rep);
|
||||
// cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
|
||||
//
|
||||
// r = rtsp_send(s->rtsp, rep);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// rtsp_message_unref(rep);
|
||||
// rep = NULL;
|
||||
//
|
||||
// r = rtsp_message_new_request(s->rtsp,
|
||||
// &rep,
|
||||
// "OPTIONS",
|
||||
// "*");
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// r = rtsp_message_append(rep, "<s>",
|
||||
// "Require",
|
||||
// "org.wfa.wfd1.0");
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// rtsp_message_seal(rep);
|
||||
// cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
|
||||
//
|
||||
// r = rtsp_call_async(s->rtsp, rep, sink_req_fn, NULL, 0, NULL);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//}
|
||||
//
|
||||
//static void sink_handle_get_parameter(struct ctl_sink *s,
|
||||
// struct rtsp_message *m)
|
||||
//{
|
||||
// _rtsp_message_unref_ struct rtsp_message *rep = NULL;
|
||||
// int r;
|
||||
//
|
||||
// r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// /* wfd_content_protection */
|
||||
// if (rtsp_message_read(m, "{<>}", "wfd_content_protection") >= 0) {
|
||||
// r = rtsp_message_append(rep, "{&}",
|
||||
// "wfd_content_protection: none");
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
// }
|
||||
// /* wfd_video_formats */
|
||||
// if (rtsp_message_read(m, "{<>}", "wfd_video_formats") >= 0) {
|
||||
// char wfd_video_formats[128];
|
||||
// sprintf(wfd_video_formats,
|
||||
// "wfd_video_formats: 00 00 03 10 %08x %08x %08x 00 0000 0000 10 none none",
|
||||
// s->resolutions_cea, s->resolutions_vesa, s->resolutions_hh);
|
||||
// r = rtsp_message_append(rep, "{&}", wfd_video_formats);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
// }
|
||||
// /* wfd_audio_codecs */
|
||||
// if (rtsp_message_read(m, "{<>}", "wfd_audio_codecs") >= 0) {
|
||||
// r = rtsp_message_append(rep, "{&}",
|
||||
// "wfd_audio_codecs: AAC 00000007 00");
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
// }
|
||||
// /* wfd_client_rtp_ports */
|
||||
// if (rtsp_message_read(m, "{<>}", "wfd_client_rtp_ports") >= 0) {
|
||||
// char wfd_client_rtp_ports[128];
|
||||
// sprintf(wfd_client_rtp_ports,
|
||||
// "wfd_client_rtp_ports: RTP/AVP/UDP;unicast %d 0 mode=play", rstp_port);
|
||||
// r = rtsp_message_append(rep, "{&}",
|
||||
// wfd_client_rtp_ports);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
// }
|
||||
//
|
||||
// /* wfd_uibc_capability */
|
||||
// if (rtsp_message_read(m, "{<>}", "wfd_uibc_capability") >= 0 && uibc) {
|
||||
// char wfd_uibc_capability[512];
|
||||
// sprintf(wfd_uibc_capability,
|
||||
// "wfd_uibc_capability: input_category_list=GENERIC;"
|
||||
// "generic_cap_list=Mouse,SingleTouch;"
|
||||
// "hidc_cap_list=none;port=none");
|
||||
// r = rtsp_message_append(rep, "{&}", wfd_uibc_capability);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
// }
|
||||
// rtsp_message_seal(rep);
|
||||
// cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
|
||||
//
|
||||
// r = rtsp_send(s->rtsp, rep);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//}
|
||||
//
|
||||
//static int sink_setup_fn(struct rtsp *bus, struct rtsp_message *m, void *data)
|
||||
//{
|
||||
// _rtsp_message_unref_ struct rtsp_message *rep = NULL;
|
||||
// struct ctl_sink *s = data;
|
||||
// const char *session;
|
||||
// char *ns, *next;
|
||||
// int r;
|
||||
//
|
||||
// cli_debug("INCOMING: %s\n", rtsp_message_get_raw(m));
|
||||
//
|
||||
// r = rtsp_message_read(m, "<s>", "Session", &session);
|
||||
// if (r < 0)
|
||||
// return cli_ERR(r);
|
||||
//
|
||||
// ns = strdup(session);
|
||||
// if (!ns)
|
||||
// return cli_ENOMEM();
|
||||
//
|
||||
// next = strchr(ns, ';');
|
||||
// if (next)
|
||||
// *next = '\0';
|
||||
//
|
||||
// free(s->session);
|
||||
// s->session = ns;
|
||||
//
|
||||
// r = rtsp_message_new_request(s->rtsp,
|
||||
// &rep,
|
||||
// "PLAY",
|
||||
// s->url);
|
||||
// if (r < 0)
|
||||
// return cli_ERR(r);
|
||||
//
|
||||
// r = rtsp_message_append(rep, "<s>", "Session", s->session);
|
||||
// if (r < 0)
|
||||
// return cli_ERR(r);
|
||||
//
|
||||
// rtsp_message_seal(rep);
|
||||
// cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
|
||||
//
|
||||
// r = rtsp_call_async(s->rtsp, rep, sink_req_fn, NULL, 0, NULL);
|
||||
// if (r < 0)
|
||||
// return cli_ERR(r);
|
||||
//
|
||||
// return 0;
|
||||
//}
|
||||
//
|
||||
//static int sink_set_format(struct ctl_sink *s,
|
||||
// unsigned int cea_res,
|
||||
// unsigned int vesa_res,
|
||||
// unsigned int hh_res)
|
||||
//{
|
||||
// int hres, vres;
|
||||
//
|
||||
// if ((vfd_get_cea_resolution(cea_res, &hres, &vres) == 0) ||
|
||||
// (vfd_get_vesa_resolution(vesa_res, &hres, &vres) == 0) ||
|
||||
// (vfd_get_hh_resolution(hh_res, &hres, &vres) == 0)) {
|
||||
// if (hres && vres) {
|
||||
// s->hres = hres;
|
||||
// s->vres = vres;
|
||||
// ctl_fn_sink_resolution_set(s);
|
||||
// return 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return -EINVAL;
|
||||
//}
|
||||
//
|
||||
//static void sink_handle_set_parameter(struct ctl_sink *s,
|
||||
// struct rtsp_message *m)
|
||||
//{
|
||||
// _rtsp_message_unref_ struct rtsp_message *rep = NULL;
|
||||
// const char *trigger;
|
||||
// const char *url;
|
||||
// const char *uibc_config;
|
||||
// const char *uibc_setting;
|
||||
// char *nu;
|
||||
// unsigned int cea_res, vesa_res, hh_res;
|
||||
// int r;
|
||||
//
|
||||
// r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// rtsp_message_seal(rep);
|
||||
// cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
|
||||
//
|
||||
// r = rtsp_send(s->rtsp, rep);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// rtsp_message_unref(rep);
|
||||
// rep = NULL;
|
||||
//
|
||||
// /* M4 (or any other) can pass presentation URLs */
|
||||
// r = rtsp_message_read(m, "{<s>}", "wfd_presentation_URL", &url);
|
||||
// if (r >= 0) {
|
||||
// if (!s->url || strcmp(s->url, url)) {
|
||||
// nu = strdup(url);
|
||||
// if (!nu)
|
||||
// return cli_vENOMEM();
|
||||
//
|
||||
// free(s->url);
|
||||
// s->url = nu;
|
||||
// cli_debug("Got URL: %s\n", s->url);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /* M4 (or any other) can pass presentation URLs */
|
||||
// r = rtsp_message_read(m, "{<s>}", "wfd_uibc_capability", &uibc_config);
|
||||
// if (r >= 0) {
|
||||
// if (!s->uibc_config || strcmp(s->uibc_config, uibc_config)) {
|
||||
// nu = strdup(uibc_config);
|
||||
// if (!nu)
|
||||
// return cli_vENOMEM();
|
||||
//
|
||||
// free(s->uibc_config);
|
||||
// s->uibc_config = nu;
|
||||
//
|
||||
// char* token = strtok(uibc_config, ";");
|
||||
//
|
||||
// while (token) {
|
||||
// if (sscanf(token, "port=%d", &uibc_port)) {
|
||||
// break;
|
||||
// }
|
||||
// token = strtok(0, ";");
|
||||
// }
|
||||
//
|
||||
// cli_debug("Got URL: %s\n", s->url);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /* M4 (or any other) can pass presentation URLs */
|
||||
// r = rtsp_message_read(m, "{<s>}", "wfd_uibc_setting", &uibc_setting);
|
||||
// if (r >= 0) {
|
||||
// if (!s->uibc_setting || strcmp(s->uibc_setting, uibc_setting)) {
|
||||
// nu = strdup(uibc_setting);
|
||||
// if (!nu)
|
||||
// return cli_vENOMEM();
|
||||
//
|
||||
// free(s->uibc_setting);
|
||||
// s->uibc_setting = nu;
|
||||
// cli_debug("uibc setting: %s\n", s->uibc_setting);
|
||||
// }
|
||||
// }
|
||||
// /* M4 again */
|
||||
// r = rtsp_message_read(m, "{<****hhh>}", "wfd_video_formats",
|
||||
// &cea_res, &vesa_res, &hh_res);
|
||||
// if (r == 0) {
|
||||
// r = sink_set_format(s, cea_res, vesa_res, hh_res);
|
||||
// if (r)
|
||||
// return cli_vERR(r);
|
||||
// }
|
||||
//
|
||||
// /* M5 */
|
||||
// r = rtsp_message_read(m, "{<s>}", "wfd_trigger_method", &trigger);
|
||||
// if (r < 0)
|
||||
// return;
|
||||
//
|
||||
// if (!strcmp(trigger, "SETUP")) {
|
||||
// if (!s->url) {
|
||||
// cli_error("No valid wfd_presentation_URL\n");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// r = rtsp_message_new_request(s->rtsp,
|
||||
// &rep,
|
||||
// "SETUP",
|
||||
// s->url);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// char rtsp_setup[128];
|
||||
// sprintf(rtsp_setup, "RTP/AVP/UDP;unicast;client_port=%d", rstp_port);
|
||||
// r = rtsp_message_append(rep, "<s>", "Transport", rtsp_setup);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
//
|
||||
// rtsp_message_seal(rep);
|
||||
// cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
|
||||
//
|
||||
// r = rtsp_call_async(s->rtsp, rep, sink_setup_fn, s, 0, NULL);
|
||||
// if (r < 0)
|
||||
// return cli_vERR(r);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static void sink_handle(struct ctl_sink *s,
|
||||
// struct rtsp_message *m)
|
||||
//{
|
||||
// const char *method;
|
||||
//
|
||||
// cli_debug("INCOMING: %s\n", rtsp_message_get_raw(m));
|
||||
//
|
||||
// method = rtsp_message_get_method(m);
|
||||
// if (!method)
|
||||
// return;
|
||||
//
|
||||
// if (!strcmp(method, "OPTIONS")) {
|
||||
// sink_handle_options(s, m);
|
||||
// } else if (!strcmp(method, "GET_PARAMETER")) {
|
||||
// sink_handle_get_parameter(s, m);
|
||||
// } else if (!strcmp(method, "SET_PARAMETER")) {
|
||||
// sink_handle_set_parameter(s, m);
|
||||
// }
|
||||
//}
|
||||
|
||||
static int src_rtsp_fn(struct rtsp *bus,
|
||||
struct rtsp_message *m,
|
||||
void *data)
|
||||
{
|
||||
// struct ctl_sink *s = data;
|
||||
//
|
||||
// if (!m)
|
||||
// s->hup = true;
|
||||
// else
|
||||
// sink_handle(s, m);
|
||||
//
|
||||
// if (s->hup) {
|
||||
// ctl_sink_close(s);
|
||||
// ctl_fn_sink_disconnected(s);
|
||||
// }
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Source I/O
|
||||
*/
|
||||
|
||||
static void src_connected(struct ctl_src *s)
|
||||
{
|
||||
int r, val;
|
||||
socklen_t len;
|
||||
|
||||
cli_printf("got incomming connection request\n");
|
||||
|
||||
if (s->connected || s->hup)
|
||||
return;
|
||||
|
||||
sd_event_source_set_enabled(s->fd_source, SD_EVENT_OFF);
|
||||
|
||||
len = sizeof(val);
|
||||
r = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &len);
|
||||
if (r < 0) {
|
||||
s->hup = true;
|
||||
cli_vERRNO();
|
||||
return;
|
||||
} else if (val) {
|
||||
s->hup = true;
|
||||
errno = val;
|
||||
cli_error("cannot connect to remote host (%d): %m",
|
||||
errno);
|
||||
return;
|
||||
}
|
||||
|
||||
cli_debug("connection established");
|
||||
|
||||
r = rtsp_open(&s->rtsp, s->fd);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = rtsp_attach_event(s->rtsp, s->event, 0);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = rtsp_add_match(s->rtsp, src_rtsp_fn, s);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
s->connected = true;
|
||||
ctl_fn_src_connected(s);
|
||||
return;
|
||||
|
||||
error:
|
||||
s->hup = true;
|
||||
cli_vERR(r);
|
||||
}
|
||||
|
||||
static void src_io(struct ctl_src *s, uint32_t mask)
|
||||
{
|
||||
cli_notice("src_io: %u", mask);
|
||||
|
||||
if (mask & EPOLLIN) {
|
||||
src_connected(s);
|
||||
}
|
||||
|
||||
if (mask & EPOLLERR) {
|
||||
cli_notice("ERR on socket");
|
||||
s->hup = true;
|
||||
}
|
||||
|
||||
if (s->hup) {
|
||||
ctl_src_close(s);
|
||||
ctl_fn_src_disconnected(s);
|
||||
}
|
||||
}
|
||||
|
||||
static int src_io_fn(sd_event_source *source,
|
||||
int fd,
|
||||
uint32_t mask,
|
||||
void *data)
|
||||
{
|
||||
src_io(data, mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_listen(struct ctl_src *s)
|
||||
{
|
||||
int fd, r;
|
||||
|
||||
if (!s)
|
||||
return cli_EINVAL();
|
||||
if (s->fd >= 0)
|
||||
return 0;
|
||||
if (!s->addr.ss_family || !s->addr_size)
|
||||
return cli_EINVAL();
|
||||
|
||||
fd = socket(s->addr.ss_family,
|
||||
SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
||||
0);
|
||||
if (fd < 0)
|
||||
return cli_ERRNO();
|
||||
|
||||
cli_printf("RTSP server socket created\n");
|
||||
|
||||
r = bind(fd, (struct sockaddr*)&s->addr, s->addr_size);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
cli_vERR(r);
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
cli_printf("RTSP server socket bound\n");
|
||||
|
||||
r = listen(fd, 1);
|
||||
if (r < 0) {
|
||||
r = -errno;
|
||||
if (r != -EINPROGRESS) {
|
||||
cli_vERR(r);
|
||||
goto err_close;
|
||||
}
|
||||
}
|
||||
|
||||
cli_printf("RTSP server socket listening\n");
|
||||
|
||||
r = sd_event_add_io(s->event,
|
||||
&s->fd_source,
|
||||
fd,
|
||||
EPOLLERR | EPOLLIN | EPOLLET,
|
||||
src_io_fn,
|
||||
s);
|
||||
if (r < 0) {
|
||||
cli_vERR(r);
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
s->fd = fd;
|
||||
return 0;
|
||||
|
||||
err_close:
|
||||
close(fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void src_close(struct ctl_src *s)
|
||||
{
|
||||
if (!s || s->fd < 0)
|
||||
return;
|
||||
|
||||
rtsp_remove_match(s->rtsp, src_rtsp_fn, s);
|
||||
rtsp_detach_event(s->rtsp);
|
||||
rtsp_unref(s->rtsp);
|
||||
s->rtsp = NULL;
|
||||
sd_event_source_unref(s->fd_source);
|
||||
s->fd_source = NULL;
|
||||
close(s->fd);
|
||||
s->fd = -1;
|
||||
s->connected = false;
|
||||
// s->hup = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Source Management
|
||||
*/
|
||||
|
||||
int ctl_src_new(struct ctl_src **out,
|
||||
sd_event *event)
|
||||
{
|
||||
struct ctl_src *s;
|
||||
|
||||
if (!out || !event)
|
||||
return cli_EINVAL();
|
||||
|
||||
s = calloc(1, sizeof(*s));
|
||||
if (!s)
|
||||
return cli_ENOMEM();
|
||||
|
||||
s->event = sd_event_ref(event);
|
||||
s->fd = -1;
|
||||
|
||||
*out = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ctl_src_free(struct ctl_src *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
ctl_src_close(s);
|
||||
free(s->local);
|
||||
free(s->session);
|
||||
sd_event_unref(s->event);
|
||||
free(s);
|
||||
}
|
||||
|
||||
int ctl_src_listen(struct ctl_src *s, const char *local)
|
||||
{
|
||||
struct sockaddr_in addr = { };
|
||||
char *l;
|
||||
int r;
|
||||
|
||||
if (!s || !local || s->fd >= 0)
|
||||
return cli_EINVAL();
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(7236);
|
||||
r = inet_pton(AF_INET, local, &addr.sin_addr);
|
||||
if (r != 1)
|
||||
return cli_EINVAL();
|
||||
|
||||
l = strdup(local);
|
||||
if (!l)
|
||||
return cli_ENOMEM();
|
||||
|
||||
free(s->local);
|
||||
s->local = l;
|
||||
|
||||
memcpy(&s->addr, &addr, sizeof(addr));
|
||||
s->addr_size = sizeof(addr);
|
||||
|
||||
return src_listen(s);
|
||||
}
|
||||
|
||||
void ctl_src_close(struct ctl_src *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
src_close(s);
|
||||
}
|
||||
|
||||
bool ctl_src_is_connecting(struct ctl_src *s)
|
||||
{
|
||||
return s && s->fd >= 0 && !s->connected;
|
||||
}
|
||||
|
||||
bool ctl_src_is_connected(struct ctl_src *s)
|
||||
{
|
||||
return s && s->connected;
|
||||
}
|
||||
|
||||
bool ctl_src_is_closed(struct ctl_src *s)
|
||||
{
|
||||
return !s || s->fd < 0;
|
||||
}
|
98
src/ctl/ctl-src.h
Normal file
98
src/ctl/ctl-src.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* 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 CTL_SRC_H
|
||||
#define CTL_SRC_H
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "ctl.h"
|
||||
|
||||
#include "rtsp.h"
|
||||
#include "shl_macro.h"
|
||||
#include "shl_util.h"
|
||||
#include "wfd.h"
|
||||
|
||||
struct ctl_src {
|
||||
sd_event *event;
|
||||
|
||||
char *local;
|
||||
char *session;
|
||||
// char *url;
|
||||
// char *uibc_config;
|
||||
// char *uibc_setting;
|
||||
struct sockaddr_storage addr;
|
||||
size_t addr_size;
|
||||
int fd;
|
||||
sd_event_source *fd_source;
|
||||
|
||||
struct rtsp *rtsp;
|
||||
|
||||
bool connected : 1;
|
||||
bool hup : 1;
|
||||
//
|
||||
// uint32_t resolutions_cea;
|
||||
// uint32_t resolutions_vesa;
|
||||
// uint32_t resolutions_hh;
|
||||
//
|
||||
// int hres;
|
||||
// int vres;
|
||||
};
|
||||
|
||||
//extern int rstp_port;
|
||||
//extern bool uibc;
|
||||
//extern int uibc_port;
|
||||
//
|
||||
//struct ctl_sink {
|
||||
// sd_event *event;
|
||||
//
|
||||
// char *target;
|
||||
// char *session;
|
||||
// char *url;
|
||||
// char *uibc_config;
|
||||
// char *uibc_setting;
|
||||
// struct sockaddr_storage addr;
|
||||
// size_t addr_size;
|
||||
// int fd;
|
||||
// sd_event_source *fd_source;
|
||||
//
|
||||
// struct rtsp *rtsp;
|
||||
//
|
||||
// bool connected : 1;
|
||||
// bool hup : 1;
|
||||
//
|
||||
// uint32_t resolutions_cea;
|
||||
// uint32_t resolutions_vesa;
|
||||
// uint32_t resolutions_hh;
|
||||
//
|
||||
// int hres;
|
||||
// int vres;
|
||||
//};
|
||||
|
||||
#endif /* CTL_SRC_H */
|
|
@ -121,6 +121,7 @@ static int ctl_peer_parse_properties(struct ctl_peer *p,
|
|||
'e',
|
||||
"sv")) > 0) {
|
||||
r = sd_bus_message_read(m, "s", &t);
|
||||
log_info("==== sd_bus_message name: %s", t);
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
||||
|
@ -760,6 +761,8 @@ static int ctl_wifi_parse_peer(struct ctl_wifi *w,
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_info("======= peer created: p=%p, l=%p, label=%s", p, l, label);
|
||||
|
||||
r = sd_bus_message_enter_container(m, 'a', "{sa{sv}}");
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
@ -771,6 +774,8 @@ static int ctl_wifi_parse_peer(struct ctl_wifi *w,
|
|||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
||||
log_info("ctl_wifi_parse_peer: %p, %t", m, t);
|
||||
|
||||
if (strcmp(t, "org.freedesktop.miracle.wifi.Peer")) {
|
||||
r = sd_bus_message_skip(m, "a{sv}");
|
||||
if (r < 0)
|
||||
|
@ -812,6 +817,8 @@ static int ctl_wifi_parse_object(struct ctl_wifi *w,
|
|||
const char *t;
|
||||
int r;
|
||||
|
||||
log_info("======== ctl_wifi_parse_object");
|
||||
|
||||
r = sd_bus_message_read(m, "o", &t);
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
@ -866,6 +873,8 @@ static int ctl_wifi_object_fn(sd_bus_message *m,
|
|||
struct ctl_wifi *w = data;
|
||||
bool added;
|
||||
|
||||
log_info("ooooooo");
|
||||
|
||||
added = !strcmp(sd_bus_message_get_member(m), "InterfacesAdded");
|
||||
|
||||
return ctl_wifi_parse_object(w, m, added);
|
||||
|
|
|
@ -104,6 +104,20 @@ struct ctl_peer *ctl_wifi_find_peer(struct ctl_wifi *w,
|
|||
struct ctl_peer *ctl_wifi_search_peer(struct ctl_wifi *w,
|
||||
const char *real_label);
|
||||
|
||||
/* source handling */
|
||||
|
||||
struct ctl_src;
|
||||
|
||||
int ctl_src_new(struct ctl_src **out,
|
||||
sd_event *event);
|
||||
void ctl_src_free(struct ctl_src *s);
|
||||
|
||||
int ctl_src_listen(struct ctl_src *s, const char *local);
|
||||
void ctl_src_close(struct ctl_src *s);
|
||||
bool ctl_src_is_connecting(struct ctl_src *s);
|
||||
bool ctl_src_is_connected(struct ctl_src *s);
|
||||
bool ctl_src_is_closed(struct ctl_src *s);
|
||||
|
||||
/* sink handling */
|
||||
|
||||
struct ctl_sink;
|
||||
|
@ -239,6 +253,9 @@ void ctl_fn_peer_disconnected(struct ctl_peer *p);
|
|||
void ctl_fn_link_new(struct ctl_link *l);
|
||||
void ctl_fn_link_free(struct ctl_link *l);
|
||||
|
||||
void ctl_fn_src_connected(struct ctl_src *s);
|
||||
void ctl_fn_src_disconnected(struct ctl_src *s);
|
||||
|
||||
void ctl_fn_sink_connected(struct ctl_sink *s);
|
||||
void ctl_fn_sink_disconnected(struct ctl_sink *s);
|
||||
void ctl_fn_sink_resolution_set(struct ctl_sink *s);
|
||||
|
|
915
src/ctl/srcctl.c
Normal file
915
src/ctl/srcctl.c
Normal file
|
@ -0,0 +1,915 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <sys/time.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include <systemd/sd-journal.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "ctl.h"
|
||||
//#include "ctl-sink.h"
|
||||
#include "wfd.h"
|
||||
#include "shl_macro.h"
|
||||
#include "shl_util.h"
|
||||
#include "config.h"
|
||||
|
||||
static sd_bus *bus;
|
||||
static struct ctl_wifi *wifi;
|
||||
static struct ctl_src *src;
|
||||
static sd_event_source *scan_timeout;
|
||||
static sd_event_source *src_timeout;
|
||||
static unsigned int src_timeout_time;
|
||||
static bool src_connected;
|
||||
//static pid_t sink_pid;
|
||||
//
|
||||
//static char *selected_ link;
|
||||
static struct ctl_link *running_link;
|
||||
static struct ctl_peer *running_peer;
|
||||
static struct ctl_peer *pending_peer;
|
||||
//
|
||||
//void launch_player(struct ctl_sink *s);
|
||||
//
|
||||
//char *gst_scale_res;
|
||||
int gst_audio_en = 1;
|
||||
static const int DEFAULT_RSTP_PORT = 1991;
|
||||
//bool uibc;
|
||||
int rstp_port;
|
||||
//int uibc_port;
|
||||
//
|
||||
//unsigned int wfd_supported_res_cea = 0x0000001f; /* up to 720x576 */
|
||||
//unsigned int wfd_supported_res_vesa = 0x00000003; /* up to 800x600 */
|
||||
//unsigned int wfd_supported_res_hh = 0x00000000; /* not supported */
|
||||
|
||||
/*
|
||||
* cmd: select
|
||||
*/
|
||||
|
||||
static int cmd_select(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l;
|
||||
|
||||
if (!n) {
|
||||
if (running_link) {
|
||||
cli_printf("link %s deselected\n",
|
||||
running_link->label);
|
||||
running_link = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
l = ctl_wifi_search_link(wifi, args[0]);
|
||||
if (!l) {
|
||||
cli_error("unknown link %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
running_link = l;
|
||||
cli_printf("link %s selected\n", running_link->label);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd list
|
||||
*/
|
||||
|
||||
static int cmd_list(char **args, unsigned int n)
|
||||
{
|
||||
size_t link_cnt = 0, peer_cnt = 0;
|
||||
struct shl_dlist *i, *j;
|
||||
struct ctl_link *l;
|
||||
struct ctl_peer *p;
|
||||
|
||||
/* list links */
|
||||
|
||||
cli_printf("%6s %-24s %-30s\n",
|
||||
"LINK", "INTERFACE", "FRIENDLY-NAME");
|
||||
|
||||
shl_dlist_for_each(i, &wifi->links) {
|
||||
l = link_from_dlist(i);
|
||||
++link_cnt;
|
||||
|
||||
cli_printf("%6s %-24s %-30s\n",
|
||||
l->label,
|
||||
shl_isempty(l->ifname) ?
|
||||
"<unknown>" : l->ifname,
|
||||
shl_isempty(l->friendly_name) ?
|
||||
"<unknown>" : l->friendly_name);
|
||||
}
|
||||
|
||||
cli_printf("\n");
|
||||
|
||||
/* list peers */
|
||||
|
||||
cli_printf("%6s %-24s %-30s %-10s\n",
|
||||
"LINK", "PEER-ID", "FRIENDLY-NAME", "CONNECTED");
|
||||
|
||||
shl_dlist_for_each(i, &wifi->links) {
|
||||
l = link_from_dlist(i);
|
||||
|
||||
shl_dlist_for_each(j, &l->peers) {
|
||||
p = peer_from_dlist(j);
|
||||
++peer_cnt;
|
||||
|
||||
cli_printf("%6s %-24s %-30s %-10s\n",
|
||||
p->l->label,
|
||||
p->label,
|
||||
shl_isempty(p->friendly_name) ?
|
||||
"<unknown>" : p->friendly_name,
|
||||
p->connected ? "yes" : "no");
|
||||
}
|
||||
}
|
||||
|
||||
cli_printf("\n %u peers and %u links listed.\n", peer_cnt, link_cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: show
|
||||
*/
|
||||
|
||||
static int cmd_show(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l = NULL;
|
||||
struct ctl_peer *p = NULL;
|
||||
|
||||
if (n > 0) {
|
||||
if (!(l = ctl_wifi_find_link(wifi, args[0])) &&
|
||||
!(p = ctl_wifi_find_peer(wifi, args[0])) &&
|
||||
!(l = ctl_wifi_search_link(wifi, args[0])) &&
|
||||
!(p = ctl_wifi_search_peer(wifi, args[0]))) {
|
||||
cli_error("unknown link or peer %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (l) {
|
||||
cli_printf("Link=%s\n", l->label);
|
||||
if (l->ifindex > 0)
|
||||
cli_printf("InterfaceIndex=%u\n", l->ifindex);
|
||||
if (l->ifname && *l->ifname)
|
||||
cli_printf("InterfaceName=%s\n", l->ifname);
|
||||
if (l->friendly_name && *l->friendly_name)
|
||||
cli_printf("FriendlyName=%s\n", l->friendly_name);
|
||||
cli_printf("P2PScanning=%d\n", l->p2p_scanning);
|
||||
if (l->wfd_subelements && *l->wfd_subelements)
|
||||
cli_printf("WfdSubelements=%s\n", l->wfd_subelements);
|
||||
} else if (p) {
|
||||
cli_printf("Peer=%s\n", p->label);
|
||||
if (p->p2p_mac && *p->p2p_mac)
|
||||
cli_printf("P2PMac=%s\n", p->p2p_mac);
|
||||
if (p->friendly_name && *p->friendly_name)
|
||||
cli_printf("FriendlyName=%s\n", p->friendly_name);
|
||||
cli_printf("Connected=%d\n", p->connected);
|
||||
if (p->interface && *p->interface)
|
||||
cli_printf("Interface=%s\n", p->interface);
|
||||
if (p->local_address && *p->local_address)
|
||||
cli_printf("LocalAddress=%s\n", p->local_address);
|
||||
if (p->remote_address && *p->remote_address)
|
||||
cli_printf("RemoteAddress=%s\n", p->remote_address);
|
||||
if (p->wfd_subelements && *p->wfd_subelements)
|
||||
cli_printf("WfdSubelements=%s\n", p->wfd_subelements);
|
||||
} else {
|
||||
cli_printf("Show what?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: set-friendly-name
|
||||
*/
|
||||
|
||||
static int cmd_set_friendly_name(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l = NULL;
|
||||
const char *name;
|
||||
|
||||
if (n < 1) {
|
||||
cli_printf("To what?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n > 1) {
|
||||
l = ctl_wifi_search_link(wifi, args[0]);
|
||||
if (!l) {
|
||||
cli_error("unknown link %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
name = args[1];
|
||||
} else {
|
||||
name = args[0];
|
||||
}
|
||||
|
||||
l = l ? : running_link;
|
||||
if (!l) {
|
||||
cli_error("no link selected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ctl_link_set_friendly_name(l, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: p2p-scan
|
||||
*/
|
||||
|
||||
static int cmd_p2p_scan(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_link *l = NULL;
|
||||
unsigned int i;
|
||||
bool stop = false;
|
||||
int r;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (!strcmp(args[i], "stop")) {
|
||||
stop = true;
|
||||
} else {
|
||||
l = ctl_wifi_search_link(wifi, args[i]);
|
||||
if (!l) {
|
||||
cli_error("unknown link %s", args[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l = l ? : running_link;
|
||||
if (!l) {
|
||||
cli_error("no link selected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctl_link_set_wfd_subelements(l, "00000600101c440032");
|
||||
r = ctl_link_set_p2p_scanning(l, !stop);
|
||||
if(!r && !running_link) {
|
||||
running_link = l;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: connect
|
||||
*/
|
||||
|
||||
static bool is_valid_prov(const char *prov)
|
||||
{
|
||||
return prov && (!strcmp(prov, "auto") ||
|
||||
!strcmp(prov, "pbc") ||
|
||||
!strcmp(prov, "display") ||
|
||||
!strcmp(prov, "pin"));
|
||||
}
|
||||
|
||||
static int cmd_connect(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_peer *p;
|
||||
const char *prov, *pin;
|
||||
|
||||
if (n < 1) {
|
||||
cli_printf("To whom?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = ctl_wifi_search_peer(wifi, args[0]);
|
||||
if (!p) {
|
||||
cli_error("unknown peer %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n > 2) {
|
||||
prov = args[1];
|
||||
pin = args[2];
|
||||
} else if (n > 1) {
|
||||
if (is_valid_prov(args[1])) {
|
||||
prov = args[1];
|
||||
pin = "";
|
||||
} else {
|
||||
prov = "auto";
|
||||
pin = args[1];
|
||||
}
|
||||
} else {
|
||||
prov = "auto";
|
||||
pin = "";
|
||||
}
|
||||
|
||||
return ctl_peer_connect(p, prov, pin);
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: disconnect
|
||||
*/
|
||||
|
||||
static int cmd_disconnect(char **args, unsigned int n)
|
||||
{
|
||||
struct ctl_peer *p;
|
||||
|
||||
if (n < 1) {
|
||||
cli_printf("From whom?\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = ctl_wifi_search_peer(wifi, args[0]);
|
||||
if (!p) {
|
||||
cli_error("unknown peer %s", args[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ctl_peer_disconnect(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd: quit/exit
|
||||
*/
|
||||
|
||||
static int cmd_quit(char **args, unsigned int n)
|
||||
{
|
||||
cli_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* main
|
||||
*/
|
||||
|
||||
static void schedule_timeout(sd_event_source **out,
|
||||
uint64_t rel_usec,
|
||||
sd_event_time_handler_t timeout_fn,
|
||||
void *data)
|
||||
{
|
||||
int r;
|
||||
|
||||
rel_usec += shl_now(CLOCK_MONOTONIC);
|
||||
|
||||
if (*out) {
|
||||
r = sd_event_source_set_time(*out, rel_usec);
|
||||
if (r < 0)
|
||||
cli_vERR(r);
|
||||
} else {
|
||||
r = sd_event_add_time(cli_event,
|
||||
out,
|
||||
CLOCK_MONOTONIC,
|
||||
rel_usec,
|
||||
0,
|
||||
timeout_fn,
|
||||
data);
|
||||
if (r < 0)
|
||||
cli_vERR(r);
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_timeout(sd_event_source **out)
|
||||
{
|
||||
if (*out) {
|
||||
sd_event_source_set_enabled(*out, SD_EVENT_OFF);
|
||||
sd_event_source_unref(*out);
|
||||
*out = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int scan_timeout_fn(sd_event_source *s, uint64_t usec, void *data)
|
||||
{
|
||||
stop_timeout(&scan_timeout);
|
||||
|
||||
if (pending_peer) {
|
||||
if (cli_running()) {
|
||||
cli_printf("[" CLI_RED "TIMEOUT" CLI_DEFAULT "] waiting for %s\n",
|
||||
pending_peer->friendly_name);
|
||||
}
|
||||
pending_peer = NULL;
|
||||
}
|
||||
|
||||
if (running_link)
|
||||
ctl_link_set_p2p_scanning(running_link, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int src_timeout_fn(sd_event_source *s, uint64_t usec, void *data)
|
||||
{
|
||||
int r;
|
||||
|
||||
stop_timeout(&src_timeout);
|
||||
|
||||
cli_printf("src_timeout_fn(): %p, %d, %d\n",
|
||||
running_peer,
|
||||
running_peer ? running_peer->connected : 0,
|
||||
ctl_src_is_closed(src));
|
||||
|
||||
if (running_peer &&
|
||||
running_peer->connected &&
|
||||
ctl_src_is_closed(src)) {
|
||||
r = ctl_src_listen(src, running_peer->local_address);
|
||||
if (r < 0) {
|
||||
if (src_timeout_time++ >= 3)
|
||||
cli_vERR(r);
|
||||
else
|
||||
schedule_timeout(&src_timeout,
|
||||
src_timeout_time * 1000ULL * 1000ULL,
|
||||
src_timeout_fn,
|
||||
NULL);
|
||||
}
|
||||
|
||||
log_info("listening on %s", running_peer->local_address);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cli_cmd cli_cmds[] = {
|
||||
{ "list", NULL, CLI_M, CLI_LESS, 0, cmd_list, "List all objects" },
|
||||
{ "select", "[link]", CLI_Y, CLI_LESS, 1, cmd_select, "Select default link" },
|
||||
{ "show", "<link|peer>", CLI_M, CLI_LESS, 1, cmd_show, "Show detailed object information" },
|
||||
{ "set-friendly-name", "[link] <name>", CLI_M, CLI_LESS, 2, cmd_set_friendly_name, "Set friendly name of an object" },
|
||||
{ "p2p-scan", "[link] [stop]", CLI_Y, CLI_LESS, 2, cmd_p2p_scan, "Control neighborhood P2P scanning" },
|
||||
{ "connect", "<peer> [provision] [pin]", CLI_M, CLI_LESS, 3, cmd_connect, "Connect to peer" },
|
||||
{ "disconnect", "<peer>", CLI_M, CLI_EQUAL, 1, cmd_disconnect, "Disconnect from peer" },
|
||||
{ "quit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, "Quit program" },
|
||||
{ "exit", NULL, CLI_Y, CLI_MORE, 0, cmd_quit, NULL },
|
||||
{ "help", NULL, CLI_M, CLI_MORE, 0, NULL, "Print help" },
|
||||
{ },
|
||||
};
|
||||
//
|
||||
//static void spawn_gst(struct ctl_sink *s)
|
||||
//{
|
||||
// pid_t pid;
|
||||
// int fd_journal;
|
||||
// sigset_t mask;
|
||||
//
|
||||
// if (sink_pid > 0)
|
||||
// return;
|
||||
//
|
||||
// pid = fork();
|
||||
// if (pid < 0) {
|
||||
// return cli_vERRNO();
|
||||
// } else if (!pid) {
|
||||
// /* child */
|
||||
//
|
||||
// sigemptyset(&mask);
|
||||
// sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
//
|
||||
// /* redirect stdout/stderr to journal */
|
||||
// fd_journal = sd_journal_stream_fd("miracle-sinkctl-gst",
|
||||
// LOG_DEBUG,
|
||||
// false);
|
||||
// if (fd_journal >= 0) {
|
||||
// /* dup journal-fd to stdout and stderr */
|
||||
// dup2(fd_journal, 1);
|
||||
// dup2(fd_journal, 2);
|
||||
// } else {
|
||||
// /* no journal? redirect stdout to parent's stderr */
|
||||
// dup2(2, 1);
|
||||
// }
|
||||
//
|
||||
// launch_player(s);
|
||||
// _exit(1);
|
||||
// } else {
|
||||
// sink_pid = pid;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//void launch_player(struct ctl_sink *s) {
|
||||
// char *argv[64];
|
||||
// char resolution[64];
|
||||
// char port[64];
|
||||
// char uibc_portStr[64];
|
||||
// int i = 0;
|
||||
// char* player;
|
||||
// if (uibc) {
|
||||
// player = "uibc-viewer";
|
||||
// } else {
|
||||
// player = "miracle-gst";
|
||||
// }
|
||||
//
|
||||
// argv[i++] = player;
|
||||
// if (uibc) {
|
||||
// argv[i++] = s->target;
|
||||
// sprintf(uibc_portStr, "%d", uibc_port);
|
||||
// argv[i++] = uibc_portStr;
|
||||
// }
|
||||
// if (cli_max_sev >= 7)
|
||||
// argv[i++] = "-d 3";
|
||||
// if (gst_audio_en)
|
||||
// argv[i++] = "-a";
|
||||
// if (gst_scale_res) {
|
||||
// argv[i++] = "-s";
|
||||
// argv[i++] = gst_scale_res;
|
||||
// }
|
||||
// argv[i++] = "-p";
|
||||
// sprintf(port, "%d", rstp_port);
|
||||
// argv[i++] = port;
|
||||
//
|
||||
// if (s->hres && s->vres) {
|
||||
// sprintf(resolution, "%dx%d", s->hres, s->vres);
|
||||
// argv[i++] = "-r";
|
||||
// argv[i++] = resolution;
|
||||
// }
|
||||
//
|
||||
// argv[i] = NULL;
|
||||
//
|
||||
// i = 0;
|
||||
// int size = 0;
|
||||
// while (argv[i]) {
|
||||
// size += strlen(argv[i++] + 1);
|
||||
// }
|
||||
//
|
||||
// char* player_command = malloc(size);
|
||||
// i = 0;
|
||||
// strcpy(player_command, argv[i++]);
|
||||
// while (argv[i]) {
|
||||
// strcat(player_command, " ");
|
||||
// strcat(player_command, argv[i++]);
|
||||
// }
|
||||
// log_debug("player command: %s", player_command);
|
||||
// //free(player_command);
|
||||
// if (execvpe(argv[0], argv, environ) < 0) {
|
||||
// cli_debug("stream player failed (%d): %m", errno);
|
||||
// int i = 0;
|
||||
// cli_debug("printing environment: ");
|
||||
// while (environ[i]) {
|
||||
// cli_debug("%s", environ[i++]);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//void launch_uibc_daemon(int port) {
|
||||
// char *argv[64];
|
||||
// char portStr[64];
|
||||
// int i = 0;
|
||||
// argv[i++] = "miracle-uibcctl";
|
||||
// argv[i++] = "localhost";
|
||||
// sprintf(portStr, "%d", port);
|
||||
// argv[i++] = portStr;
|
||||
// argv[i] = NULL;
|
||||
//
|
||||
// cli_debug("uibc daemon: %s", argv[0]);
|
||||
// execvpe(argv[0], argv, environ);
|
||||
//}
|
||||
//
|
||||
//static void kill_gst(void)
|
||||
//{
|
||||
// if (sink_pid <= 0)
|
||||
// return;
|
||||
//
|
||||
// kill(sink_pid, SIGTERM);
|
||||
// sink_pid = 0;
|
||||
//}
|
||||
|
||||
void ctl_fn_src_connected(struct ctl_src *s)
|
||||
{
|
||||
cli_notice("SOURCE connected");
|
||||
src_connected = true;
|
||||
}
|
||||
|
||||
void ctl_fn_src_disconnected(struct ctl_src *s)
|
||||
{
|
||||
if (!src_connected) {
|
||||
/* treat HUP as timeout */
|
||||
src_timeout_fn(src_timeout, 0, NULL);
|
||||
} else {
|
||||
cli_notice("SRC disconnected");
|
||||
src_connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
//void ctl_fn_sink_resolution_set(struct ctl_sink *s)
|
||||
//{
|
||||
// cli_printf("SINK set resolution %dx%d\n", s->hres, s->vres);
|
||||
// if (sink_connected)
|
||||
// spawn_gst(s);
|
||||
//}
|
||||
|
||||
void ctl_fn_peer_new(struct ctl_peer *p)
|
||||
{
|
||||
if (p->l != running_link || shl_isempty(p->wfd_subelements))
|
||||
return;
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
}
|
||||
|
||||
void ctl_fn_peer_free(struct ctl_peer *p)
|
||||
{
|
||||
if (p->l != running_link || shl_isempty(p->wfd_subelements))
|
||||
return;
|
||||
|
||||
if (p == pending_peer) {
|
||||
cli_printf("no longer waiting for peer %s (%s)\n",
|
||||
p->friendly_name, p->label);
|
||||
pending_peer = NULL;
|
||||
stop_timeout(&scan_timeout);
|
||||
ctl_link_set_p2p_scanning(p->l, true);
|
||||
}
|
||||
|
||||
if (p == running_peer) {
|
||||
cli_printf("no longer running on peer %s\n",
|
||||
running_peer->label);
|
||||
stop_timeout(&src_timeout);
|
||||
//kill_gst();
|
||||
ctl_src_close(src);
|
||||
running_peer = NULL;
|
||||
stop_timeout(&scan_timeout);
|
||||
ctl_link_set_p2p_scanning(p->l, true);
|
||||
}
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_RED "REMOVE" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
}
|
||||
//
|
||||
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
|
||||
const char *prov,
|
||||
const char *pin)
|
||||
{
|
||||
if (p->l != running_link || shl_isempty(p->wfd_subelements))
|
||||
return;
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_YELLOW "PROV" CLI_DEFAULT "] Peer: %s Type: %s PIN: %s\n",
|
||||
p->label, prov, pin);
|
||||
}
|
||||
|
||||
void ctl_fn_peer_go_neg_request(struct ctl_peer *p,
|
||||
const char *prov,
|
||||
const char *pin)
|
||||
{
|
||||
if (p->l != running_link || shl_isempty(p->wfd_subelements))
|
||||
return;
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_YELLOW "GO NEG" CLI_DEFAULT "] Peer: %s Type: %s PIN: %s\n",
|
||||
p->label, prov, pin);
|
||||
|
||||
if (!running_peer) {
|
||||
/* auto accept any incoming connection attempt */
|
||||
ctl_peer_connect(p, "auto", "");
|
||||
pending_peer = p;
|
||||
|
||||
/* 60s timeout in case the connect fails. Yes, stupid wpas does
|
||||
* not catch this and notify us.. and as it turns out, DHCP
|
||||
* negotiation with some proprietary devices can take up to 30s
|
||||
* so lets be safe. */
|
||||
schedule_timeout(&scan_timeout,
|
||||
60 * 1000ULL * 1000ULL,
|
||||
scan_timeout_fn,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void ctl_fn_peer_formation_failure(struct ctl_peer *p, const char *reason)
|
||||
{
|
||||
if (p->l != running_link || shl_isempty(p->wfd_subelements))
|
||||
return;
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_YELLOW "FAIL" CLI_DEFAULT "] Peer: %s Reason: %s\n",
|
||||
p->label, reason);
|
||||
|
||||
if (!running_peer) {
|
||||
stop_timeout(&scan_timeout);
|
||||
ctl_link_set_p2p_scanning(p->l, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ctl_fn_peer_connected(struct ctl_peer *p)
|
||||
{
|
||||
if (p->l != running_link || shl_isempty(p->wfd_subelements))
|
||||
return;
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_GREEN "CONNECT" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
|
||||
pending_peer = NULL;
|
||||
|
||||
if (!running_peer) {
|
||||
running_peer = p;
|
||||
cli_printf("now running on peer %s\n", running_peer->label);
|
||||
stop_timeout(&scan_timeout);
|
||||
|
||||
src_connected = false;
|
||||
src_timeout_time = 1;
|
||||
cli_printf("src_timeout_time=%llu\n", src_timeout_time);
|
||||
schedule_timeout(&src_timeout,
|
||||
src_timeout_time * 1000ULL * 1000ULL,
|
||||
src_timeout_fn,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
//
|
||||
void ctl_fn_peer_disconnected(struct ctl_peer *p)
|
||||
{
|
||||
if (p->l != running_link || shl_isempty(p->wfd_subelements))
|
||||
return;
|
||||
|
||||
if (p == running_peer) {
|
||||
cli_printf("no longer running on peer %s\n",
|
||||
running_peer->label);
|
||||
stop_timeout(&src_timeout);
|
||||
//kill_gst();
|
||||
ctl_src_close(src);
|
||||
running_peer = NULL;
|
||||
stop_timeout(&scan_timeout);
|
||||
ctl_link_set_p2p_scanning(p->l, true);
|
||||
}
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_YELLOW "DISCONNECT" CLI_DEFAULT "] Peer: %s\n",
|
||||
p->label);
|
||||
}
|
||||
//
|
||||
void ctl_fn_link_new(struct ctl_link *l)
|
||||
{
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_GREEN "ADD" CLI_DEFAULT "] Link: %s\n",
|
||||
l->label);
|
||||
}
|
||||
|
||||
void ctl_fn_link_free(struct ctl_link *l)
|
||||
{
|
||||
if (l == running_link) {
|
||||
cli_printf("no longer running on link %s\n",
|
||||
running_link->label);
|
||||
running_link = NULL;
|
||||
stop_timeout(&scan_timeout);
|
||||
}
|
||||
|
||||
if (cli_running())
|
||||
cli_printf("[" CLI_RED "REMOVE" CLI_DEFAULT "] Link: %s\n",
|
||||
l->label);
|
||||
}
|
||||
|
||||
void cli_fn_help()
|
||||
{
|
||||
/*
|
||||
* 80-char barrier:
|
||||
* 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
*/
|
||||
printf("%s [OPTIONS...] ...\n\n"
|
||||
"Control a dedicated local sink via MiracleCast.\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --log-level <lvl> Maximum level for log messages\n"
|
||||
" --log-journal-level <lvl> Maximum level for journal log messages\n"
|
||||
" --audio <0/1> Enable audio support (default %d)\n"
|
||||
"\n"
|
||||
, program_invocation_short_name, gst_audio_en
|
||||
);
|
||||
/*
|
||||
* 80-char barrier:
|
||||
* 01234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||
*/
|
||||
}
|
||||
|
||||
static int ctl_interactive(char **argv, int argc)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = cli_init(bus, cli_cmds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ctl_src_new(&src, cli_event);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = ctl_wifi_fetch(wifi);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (argc > 0) {
|
||||
r = cli_do(cli_cmds, argv, argc);
|
||||
if (r == -EAGAIN)
|
||||
cli_error("unknown operation %s", argv[0]);
|
||||
}
|
||||
|
||||
r = cli_run();
|
||||
|
||||
error:
|
||||
ctl_src_free(src);
|
||||
cli_destroy();
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ctl_main(int argc, char *argv[])
|
||||
{
|
||||
struct shl_dlist *i;
|
||||
struct ctl_link *l;
|
||||
int r, left;
|
||||
|
||||
r = ctl_wifi_new(&wifi, bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
left = argc - optind;
|
||||
r = ctl_interactive(argv + optind, left <= 0 ? 0 : left);
|
||||
|
||||
/* stop all scans */
|
||||
shl_dlist_for_each(i, &wifi->links) {
|
||||
l = link_from_dlist(i);
|
||||
if (l->have_p2p_scan)
|
||||
ctl_link_set_p2p_scanning(l, false);
|
||||
}
|
||||
|
||||
ctl_wifi_free(wifi);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[])
|
||||
{
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_LOG_LEVEL,
|
||||
ARG_JOURNAL_LEVEL,
|
||||
ARG_AUDIO,
|
||||
};
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
|
||||
{ "log-journal-level", required_argument, NULL, ARG_JOURNAL_LEVEL },
|
||||
{ "audio", required_argument, NULL, ARG_AUDIO },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
|
||||
rstp_port = DEFAULT_RSTP_PORT;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
return cli_help(cli_cmds);
|
||||
case ARG_VERSION:
|
||||
puts(PACKAGE_STRING);
|
||||
return 0;
|
||||
case ARG_LOG_LEVEL:
|
||||
cli_max_sev = log_parse_arg(optarg);
|
||||
break;
|
||||
case ARG_JOURNAL_LEVEL:
|
||||
log_max_sev = log_parse_arg(optarg);
|
||||
break;
|
||||
case ARG_AUDIO:
|
||||
gst_audio_en = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
return EXIT_FAILURE;
|
||||
if (!r)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
r = sd_bus_default_system(&bus);
|
||||
if (r < 0) {
|
||||
cli_error("cannot connect to system bus: %s", strerror(-r));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
r = ctl_main(argc, argv);
|
||||
sd_bus_unref(bus);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue