1
0
Fork 0
mirror of https://github.com/albfan/miraclecast.git synced 2025-03-09 23:38:56 +00:00

remove unused files

This commit is contained in:
Derek Dai 2017-02-15 18:37:33 +08:00
parent 9dd805378e
commit 11016d9e03
No known key found for this signature in database
GPG key ID: E109CC97553EF009
9 changed files with 0 additions and 2665 deletions

View file

@ -48,7 +48,6 @@ include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_D
########### next target ###############
set(miracle-wfdctl_SRCS ctl-cli.c
ctl-wifi.c
wfd-src.c
wfd-sink.c
wfd-dbus.c
wfd-session.c

View file

@ -1,873 +0,0 @@
/*
* 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 <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <gio/gio.h>
#include "ctl.h"
#include "ctl-src.h"
#include "wfd.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "config.h"
#include "sender-iface.h"
void launch_sender(struct ctl_src *s);
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 char *selected_ link;
static struct ctl_link *running_link;
static struct ctl_peer *running_peer;
static struct ctl_peer *pending_peer;
//
//char *gst_scale_res;
static int gst_audio_en = 1;
static const int DEFAULT_RTSP_PORT = 1991;
//bool uibc;
static int rtsp_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 */
static Sender *sender;
/*
* 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, "000600101c4400c8");
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);
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 stop_sender(void)
{
GError *error = NULL;
if (!sender)
return;
sender_call_stop_sync(sender, NULL, &error);
if(error) {
cli_error("SOURCE failed to stop sender: %s", error->message);
g_error_free(error);
}
g_object_unref(sender);
sender = NULL;
}
void ctl_fn_wfd_connected(struct ctl_src *s)
{
cli_notice("SOURCE connected");
src_connected = true;
}
void ctl_fn_wfd_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;
if(sender) {
GError *error = NULL;
if(sender_call_stop_sync(sender, NULL, &error)) {
cli_error("SRC failed to stop sender: %s", error->message);
g_error_free(error);
}
g_object_unref(sender);
sender = NULL;
}
}
}
void ctl_fn_wfd_setup(struct ctl_src *s)
{
GError *error = NULL;
cli_printf("SRC got setup request\n");
if(!sender) {
sender = sender_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
"org.freedesktop.miracle",
"/org/freedesktop/miracle/Sender/0",
NULL,
&error);
if(!sender) {
cli_error("SRC failed to connect to sender: %s", error->message);
g_error_free(error);
return;
}
}
sender_call_prepare_sync(sender,
inet_ntoa(((struct sockaddr_in *) &s->addr)->sin_addr),
s->sink.rtp_ports.port0,
":0",
1920,
1080,
25,
FALSE,
NULL,
&error);
if(error) {
cli_error("SRC failed to setup: %s", error->message);
g_error_free(error);
return;
}
g_info("SRC sender prepared");
}
void ctl_fn_wfd_playing(struct ctl_src *s)
{
GError *error = NULL;
cli_printf("SRC got play request\n");
if(!sender) {
cli_error("SRC not setup yet");
return;
}
if(!sender_call_play_sync(sender, NULL, &error)) {
cli_error("SRC failed to play: %s", error->message);
g_error_free(error);
return;
}
cli_printf("SRC sender playing\n");
}
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);
stop_sender();
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;
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);
stop_sender();
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:
stop_sender();
ctl_src_free(src);
src = NULL;
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;
rtsp_port = DEFAULT_RTSP_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;
}

View file

@ -1,851 +0,0 @@
/*
* 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 "wfd-src.h"
#include "util.h"
#define DEFAULT_RTSP_PORT (7236)
/*
* RTSP Session
*/
static void src_handle_options(struct wfd_src *s,
struct rtsp_message *m)
{
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
int r;
cli_debug("INCOMING (M2): %s\n", rtsp_message_get_raw(m));
r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_append(rep, "<s>",
"Public",
"org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER, "
"SETUP, PLAY, PAUSE, TEARDOWN");
if (r < 0) {
cli_vERR(r);
goto error;
}
rtsp_message_seal(rep);
cli_debug("OUTGOING (M2): %s\n", rtsp_message_get_raw(rep));
r = rtsp_send(s->rtsp, rep);
if (r < 0) {
cli_vERR(r);
goto error;
}
return;
error:
wfd_src_close(s);
wfd_fn_src_disconnected(s);
}
static int src_trigger_play_rep_fn(struct rtsp *bus,
struct rtsp_message *m,
void *data)
{
cli_debug("INCOMING (M5): %s\n", rtsp_message_get_raw(m));
return 0;
}
static void src_handle_setup(struct wfd_src *s,
struct rtsp_message *m)
{
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
_rtsp_message_unref_ struct rtsp_message *req = NULL;
int r;
cli_debug("INCOMING (M6): %s\n", rtsp_message_get_raw(m));
r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_append(rep, "<s>",
"Session", "0;timeout=30");
if (r < 0) {
cli_vERR(r);
goto error;
}
char buf[256];
snprintf(buf, sizeof(buf), "RTP/AVP/UDP;unicast;client_port=%d", s->sink.rtp_ports.port0);
r = rtsp_message_append(rep, "<s>",
"Transport", buf);
if (r < 0) {
cli_vERR(r);
goto error;
}
rtsp_message_seal(rep);
cli_debug("OUTGOING (M6): %s\n", rtsp_message_get_raw(rep));
r = rtsp_send(s->rtsp, rep);
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_new_request(s->rtsp,
&req,
"SET_PARAMETER",
s->url);
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_append(req, "{&}", "wfd_trigger_method: PLAY");
if (r < 0) {
cli_vERR(r);
goto error;
}
rtsp_message_seal(req);
cli_debug("OUTGOING (M5): %s\n", rtsp_message_get_raw(req));
r = rtsp_call_async(s->rtsp, req, src_trigger_play_rep_fn, s, 0, NULL);
if (r < 0) {
cli_vERR(r);
goto error;
}
wfd_fn_src_setup(s);
return;
error:
wfd_src_close(s);
wfd_fn_src_disconnected(s);
}
static void src_handle_play(struct wfd_src *s,
struct rtsp_message *m)
{
_rtsp_message_unref_ struct rtsp_message *rep = NULL;
_rtsp_message_unref_ struct rtsp_message *req = NULL;
int r;
cli_debug("INCOMING (M7): %s\n", rtsp_message_get_raw(m));
r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_append(rep, "<s>",
"Session", "0;timeout=30");
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_append(rep, "<s>",
"Range", "ntp=now-");
if (r < 0) {
cli_vERR(r);
goto error;
}
rtsp_message_seal(rep);
cli_debug("OUTGOING (M7): %s\n", rtsp_message_get_raw(rep));
r = rtsp_send(s->rtsp, rep);
if (r < 0) {
cli_vERR(r);
goto error;
}
wfd_fn_src_playing(s);
return;
error:
wfd_src_close(s);
wfd_fn_src_disconnected(s);
}
static void src_handle_pause(struct wfd_src *s,
struct rtsp_message *m)
{
cli_debug("INCOMING (M9): %s\n", rtsp_message_get_raw(m));
}
static void src_handle_teardown(struct wfd_src *s,
struct rtsp_message *m)
{
cli_debug("INCOMING (M8): %s\n", rtsp_message_get_raw(m));
}
static bool parse_video_formats(struct rtsp_message *m,
struct video_formats *formats)
{
const char *param;
int r;
r = rtsp_message_read(m, "{<&>}", "wfd_video_formats", &param);
if(r < 0) {
goto error;
}
else if(!strncmp("none", param, 4) || strlen(param) < 55) {
return false;
}
r = sscanf(param, "%hhx %hhx %hhx %hhx %x %x %x %hhx %hx %hx %hhx",
&formats->native_disp_mode,
&formats->pref_disp_mode,
&formats->codec_profile,
&formats->codec_level,
&formats->resolutions_cea,
&formats->resolutions_vesa,
&formats->resolutions_hh,
&formats->latency,
&formats->min_slice_size,
&formats->slice_enc_params,
&formats->frame_rate_control);
if(r < 11) {
goto error;
}
formats->hres = -1;
formats->vres = -1;
sscanf(param + 55, "%x %x", &formats->hres, &formats->vres);
return true;
error:
cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] Invalid video formats\n");
return false;
}
static bool parse_audio_codecs(struct rtsp_message *m,
struct audio_codecs *codecs)
{
const char *param;
int r;
r = rtsp_message_read(m, "{<&>}", "wfd_audio_codecs", &param);
if(r < 0) {
goto error;
}
else if(!strncmp("none", param, 4) || strlen(param) < 4) {
return false;
}
cli_printf("audio codecs: %s\n", param);
if(!strncmp("LPCM", param, 4)) {
codecs->format = AUDIO_FORMAT_LPCM;
}
else if(!strncmp("AAC", param, 3)) {
codecs->format = AUDIO_FORMAT_AAC;
}
else if(!strncmp("AC3", param, 3)) {
codecs->format = AUDIO_FORMAT_AC3;
}
else {
goto error;
}
r = sscanf(param, "%x %hhx",
&codecs->modes,
&codecs->latency);
if(r < 2) {
goto error;
}
return true;
error:
cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] Invalid audio codecs\n");
return false;
}
static bool parse_client_rtp_ports(struct rtsp_message *m,
struct client_rtp_ports *ports)
{
const char *param;
int r;
char mode[10] = "";
r = rtsp_message_read(m, "{<&>}", "wfd_client_rtp_ports", &param);
if(r < 0) {
goto error;
}
r = sscanf(param, "%ms %hu %hu %9s",
&ports->profile,
&ports->port0,
&ports->port1,
mode);
if(r < 4 || strcmp("mode=play", mode)) {
goto error;
}
return true;
error:
cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] Invalid client RTP ports\n");
return false;
}
static int src_trigger_setup_rep_fn(struct rtsp *bus,
struct rtsp_message *m,
void *data)
{
struct wfd_src *s = data;
_rtsp_message_unref_ struct rtsp_message *req = NULL;
cli_debug("INCOMING (M5): %s\n", rtsp_message_get_raw(m));
if(rtsp_message_is_reply(m, RTSP_CODE_OK, NULL)) {
return 0;
}
cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] Sink failed to SETUP\n");
wfd_src_close(s);
wfd_fn_src_disconnected(s);
return 0;
}
static int src_set_parameter_rep_fn(struct rtsp *bus,
struct rtsp_message *m,
void *data)
{
struct wfd_src *s = data;
_rtsp_message_unref_ struct rtsp_message *req = NULL;
int r;
cli_debug("INCOMING (M4): %s\n", rtsp_message_get_raw(m));
if(!rtsp_message_is_reply(m, RTSP_CODE_OK, NULL)) {
r = -1;
goto error;
}
r = rtsp_message_new_request(s->rtsp,
&req,
"SET_PARAMETER",
s->url);
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_append(req, "{&}", "wfd_trigger_method: SETUP");
if (r < 0) {
cli_vERR(r);
goto error;
}
rtsp_message_seal(req);
cli_debug("OUTGOING (M5): %s\n", rtsp_message_get_raw(req));
r = rtsp_call_async(s->rtsp, req, src_trigger_setup_rep_fn, s, 0, NULL);
if (r < 0) {
cli_vERR(r);
goto error;
}
return 0;
error:
cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] SETUP failed\n");
return r;
}
char buf[1024];
static int src_send_set_parameter(struct wfd_src *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;
}
static int src_get_parameter_rep_fn(struct rtsp *bus,
struct rtsp_message *m,
void *data)
{
struct wfd_src *s = data;
cli_debug("INCOMING (M3): %s\n", rtsp_message_get_raw(m));
if (!rtsp_message_is_reply(m, RTSP_CODE_OK, NULL)) {
cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] GET_PARAMETER failed\n");
wfd_src_close(s);
wfd_fn_src_disconnected(s);
return -EINVAL;
}
free(s->sink.rtp_ports.profile);
s->sink.rtp_ports.profile = NULL;
s->sink.has_video_formats = parse_video_formats(m, &s->sink.video_formats);
//s->sink.has_audio_codecs = parse_audio_codecs(m, &s->sink.audio_codecs);
s->sink.has_rtp_ports = parse_client_rtp_ports(m, &s->sink.rtp_ports);
return src_send_set_parameter(s);
}
static int src_options_rep_fn(struct rtsp *bus,
struct rtsp_message *m,
void *data)
{
struct wfd_src *s = data;
_rtsp_message_unref_ struct rtsp_message *req = NULL;
int r;
cli_debug("INCOMING (M1): %s\n", rtsp_message_get_raw(m));
if(!rtsp_message_is_reply(m, RTSP_CODE_OK, NULL)) {
cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] Failed to get OPTIONS from sink\n");
goto error;
}
r = rtsp_message_new_request(s->rtsp,
&req,
"GET_PARAMETER",
s->url);
if (r < 0) {
cli_vERR(r);
goto error;
}
r = rtsp_message_append(req, "{&}",
"wfd_video_formats\n"
//"wfd_audio_codecs\n"
"wfd_client_rtp_ports\n"
//"wfd_uibc_capability"
);
if (r < 0) {
cli_vERR(r);
goto error;
}
rtsp_message_seal(req);
cli_debug("OUTGOING (M3): %s\n", rtsp_message_get_raw(req));
r = rtsp_call_async(s->rtsp, req, src_get_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;
}
static void src_handle(struct wfd_src *s,
struct rtsp_message *m)
{
const char *method;
if(!m) {
wfd_src_close(s);
wfd_fn_src_disconnected(s);
return;
}
method = rtsp_message_get_method(m);
if(!method) {
cli_debug("INCOMING: Unexpected message (%d): %s\n",
rtsp_message_get_type(m),
rtsp_message_get_raw(m));
}
else if (!strcmp(method, "OPTIONS")) {
src_handle_options(s, m);
} else if (!strcmp(method, "SETUP")) {
src_handle_setup(s, m);
} else if (!strcmp(method, "PLAY")) {
src_handle_play(s, m);
} else if (!strcmp(method, "PAUSE")) {
src_handle_pause(s, m);
} else if (!strcmp(method, "TEARDOWN")) {
src_handle_teardown(s, m);
}
}
static int src_rtsp_fn(struct rtsp *bus,
struct rtsp_message *m,
void *data)
{
struct wfd_src *s = data;
if (!m)
s->hup = true;
else
src_handle(s, m);
if (s->hup) {
wfd_src_close(s);
wfd_fn_src_disconnected(s);
}
return 0;
}
static void src_send_options(struct wfd_src *s)
{
_rtsp_message_unref_ struct rtsp_message *req = NULL;
int r;
r = rtsp_message_new_request(s->rtsp,
&req,
"OPTIONS",
"*");
if (r < 0)
return cli_vERR(r);
r = rtsp_message_append(req, "<s>",
"Require",
"org.wfa.wfd1.0");
if (r < 0)
return cli_vERR(r);
rtsp_message_seal(req);
r = rtsp_call_async(s->rtsp, req, src_options_rep_fn, s, 0, NULL);
if (r < 0)
return cli_vERR(r);
cli_debug("OUTGOING (M1): %s\n", rtsp_message_get_raw(req));
}
/*
* Source I/O
*/
static void src_connected(struct wfd_src *s)
{
int r, val;
struct sockaddr_storage addr;
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(addr);
int fd = accept4(s->fd, (struct sockaddr *) &addr, &len, SOCK_CLOEXEC);
r = getsockopt(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");
close(s->fd);
s->fd = fd;
s->addr = addr;
s->addr_size = len;
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;
wfd_fn_src_connected(s);
src_send_options(s);
return;
error:
s->hup = true;
cli_vERR(r);
}
static void src_io(struct wfd_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) {
wfd_src_close(s);
wfd_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 wfd_src *s)
{
int fd, r, enable = 1;
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();
r = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable));
if(r < 0) {
r = -errno;
cli_vERR(r);
goto err_close;
}
r = bind(fd, (struct sockaddr*)&s->addr, s->addr_size);
if (r < 0) {
r = -errno;
cli_vERR(r);
goto err_close;
}
r = listen(fd, 1);
if (r < 0) {
r = -errno;
if (r != -EINPROGRESS) {
cli_vERR(r);
goto err_close;
}
}
cli_printf("Wait for RTSP connection request from sink...\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 wfd_src *s)
{
if (!s || s->fd < 0)
return;
free(s->sink.rtp_ports.profile);
s->sink.rtp_ports.profile = NULL;
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 wfd_src_new(struct wfd_src **out,
sd_event *event)
{
struct wfd_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 wfd_src_free(struct wfd_src *s)
{
if (!s)
return;
wfd_src_close(s);
free(s->local);
free(s->session);
sd_event_unref(s->event);
free(s);
}
int wfd_src_listen(struct wfd_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(DEFAULT_RTSP_PORT);
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);
snprintf(s->url, sizeof(s->url), "rtsp://%s/wfd1.0", local);
return src_listen(s);
}
void wfd_src_close(struct wfd_src *s)
{
if (!s)
return;
src_close(s);
}
bool wfd_src_is_connecting(struct wfd_src *s)
{
return s && s->fd >= 0 && !s->connected;
}
bool wfd_src_is_connected(struct wfd_src *s)
{
return s && s->connected;
}
bool wfd_src_is_closed(struct wfd_src *s)
{
return !s || s->fd < 0;
}

View file

@ -1,106 +0,0 @@
/*
* 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"
enum audio_format {
AUDIO_FORMAT_UNKNOWN,
AUDIO_FORMAT_LPCM,
AUDIO_FORMAT_AAC,
AUDIO_FORMAT_AC3,
};
struct video_formats {
uint8_t native_disp_mode;
uint8_t pref_disp_mode;
uint8_t codec_profile;
uint8_t codec_level;
unsigned int resolutions_cea;
unsigned int resolutions_vesa;
unsigned int resolutions_hh;
uint8_t latency;
unsigned short min_slice_size;
unsigned short slice_enc_params;
uint8_t frame_rate_control;
int hres;
int vres;
};
struct audio_codecs {
enum audio_format format;
unsigned int modes;
uint8_t latency;
};
struct client_rtp_ports {
char *profile;
unsigned short port0;
unsigned short port1;
};
struct wfd_src {
sd_event *event;
char *local;
char *session;
char url[256];
struct sockaddr_storage addr;
size_t addr_size;
int fd;
sd_event_source *fd_source;
sd_event_source *req_source;
struct rtsp *rtsp;
struct {
struct video_formats video_formats;
struct audio_codecs audio_codecs;
struct client_rtp_ports rtp_ports;
bool has_video_formats : 1;
bool has_audio_codecs : 1;
bool has_rtp_ports : 1;
} sink;
bool connected : 1;
bool hup : 1;
};
#endif /* CTL_SRC_H */