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

UIBC support

Basic support for UIBC (single mouse events and key events)

 - Option --uibc on miracle-sinkctl to enable it
 - Option --log-journal-level to see player execution on journalctl
 - Controller for uibc miracle-uibcctl
 - Player based on PyGtk to receive and communicate mouse and keyevents

closes #57
This commit is contained in:
albfan 2016-07-02 15:35:58 +02:00
parent 4637409d0b
commit 1e69dd6dae
21 changed files with 1258 additions and 79 deletions

View file

@ -24,11 +24,11 @@ target_link_libraries(miracle-wifictl miracle-shared)
set(miracle-sinkctl_SRCS ctl.h
ctl-cli.c
ctl-sink.h
ctl-sink.c
ctl-wifi.c
sinkctl.c
wfd.c)
add_executable(miracle-sinkctl ${miracle-sinkctl_SRCS})

View file

@ -17,6 +17,7 @@ miracle_wifictl_LDADD = \
miracle_sinkctl_SOURCES = \
ctl.h \
ctl-cli.c \
ctl-sink.h \
ctl-sink.c \
ctl-wifi.c \
wfd.c \

View file

@ -17,52 +17,8 @@
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
*/
#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"
#include "ctl-sink.h"
struct ctl_sink {
sd_event *event;
char *target;
char *session;
char *url;
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;
};
static const int DEFAULT_UIBC_PORT = 7239;
static const int DEFAULT_RSTP_PORT = 1991;
/*
* RTSP Session
*/
@ -158,7 +114,7 @@ static void sink_handle_get_parameter(struct ctl_sink *s,
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", DEFAULT_RSTP_PORT);
"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)
@ -166,12 +122,12 @@ static void sink_handle_get_parameter(struct ctl_sink *s,
}
/* wfd_uibc_capability */
if (rtsp_message_read(m, "{<>}", "wfd_uibc_capability") >= 0) {
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,Keyboard,Camera;"
"hidc_cap_list=none;port=%d", DEFAULT_UIBC_PORT);
"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);
@ -243,7 +199,7 @@ static int sink_set_format(struct ctl_sink *s,
if (hres && vres) {
s->hres = hres;
s->vres = vres;
ctl_fn_sink_resolution_set(s, hres, vres);
ctl_fn_sink_resolution_set(s);
return 0;
}
}
@ -257,6 +213,7 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
_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;
@ -290,6 +247,30 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
}
}
/* 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) {
@ -330,9 +311,9 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
if (r < 0)
return cli_vERR(r);
r = rtsp_message_append(rep, "<s>",
"Transport",
"RTP/AVP/UDP;unicast;client_port=1991");
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);

72
src/ctl/ctl-sink.h Normal file
View file

@ -0,0 +1,72 @@
/*
* 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_SINK_H
#define CTL_SINK_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"
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_SINK_H */

View file

@ -241,7 +241,7 @@ void ctl_fn_link_free(struct ctl_link *l);
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, int hres, int vres);
void ctl_fn_sink_resolution_set(struct ctl_sink *s);
void cli_fn_help(void);

View file

@ -34,6 +34,7 @@
#include <time.h>
#include <unistd.h>
#include "ctl.h"
#include "ctl-sink.h"
#include "wfd.h"
#include "shl_macro.h"
#include "shl_util.h"
@ -53,8 +54,15 @@ 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 */
@ -335,12 +343,10 @@ static const struct cli_cmd cli_cmds[] = {
{ },
};
static void spawn_gst(int hres, int vres)
static void spawn_gst(struct ctl_sink *s)
{
char *argv[64];
char resolution[64];
pid_t pid;
int fd_journal, i;
int fd_journal;
sigset_t mask;
if (sink_pid > 0)
@ -357,7 +363,7 @@ static void spawn_gst(int hres, int vres)
/* redirect stdout/stderr to journal */
fd_journal = sd_journal_stream_fd("miracle-sinkctl-gst",
LOG_INFO,
LOG_DEBUG,
false);
if (fd_journal >= 0) {
/* dup journal-fd to stdout and stderr */
@ -368,8 +374,34 @@ static void spawn_gst(int hres, int vres)
dup2(2, 1);
}
i = 0;
argv[i++] = (char*) "/miracle-gst.sh";
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 = "gstplayer";
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;
// argv[i++] = "--daemon";
}
if (cli_max_sev >= 7)
argv[i++] = "-d 3";
if (gst_audio_en)
@ -378,18 +410,55 @@ static void spawn_gst(int hres, int vres)
argv[i++] = "-s";
argv[i++] = gst_scale_res;
}
if (hres && vres) {
sprintf(resolution, "%dx%d", hres, vres);
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;
execve(argv[0], argv, environ);
_exit(1);
} else {
sink_pid = pid;
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)
@ -418,11 +487,11 @@ void ctl_fn_sink_disconnected(struct ctl_sink *s)
}
}
void ctl_fn_sink_resolution_set(struct ctl_sink *s, int hres, int vres)
void ctl_fn_sink_resolution_set(struct ctl_sink *s)
{
cli_printf("SINK set resolution %dx%d\n", hres, vres);
cli_printf("SINK set resolution %dx%d\n", s->hres, s->vres);
if (sink_connected)
spawn_gst(hres, vres);
spawn_gst(s);
}
void ctl_fn_peer_new(struct ctl_peer *p)
@ -604,14 +673,17 @@ void cli_fn_help()
" -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"
" --scale WxH Scale to resolution\n"
" --port <port> Port for rtsp (default %d)\n"
" --uibc Enables UIBC\n"
" --res <n,n,n> Supported resolutions masks (CEA, VESA, HH)\n"
" default CEA %08X\n"
" default VESA %08X\n"
" default HH %08X\n"
"\n"
, program_invocation_short_name, gst_audio_en,
, program_invocation_short_name, gst_audio_en, DEFAULT_RSTP_PORT,
wfd_supported_res_cea, wfd_supported_res_vesa, wfd_supported_res_hh
);
wfd_print_resolutions();
@ -680,21 +752,30 @@ static int parse_argv(int argc, char *argv[])
enum {
ARG_VERSION = 0x100,
ARG_LOG_LEVEL,
ARG_JOURNAL_LEVEL,
ARG_AUDIO,
ARG_SCALE,
ARG_RES,
ARG_PORT,
ARG_UIBC,
};
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 },
{ "scale", required_argument, NULL, ARG_SCALE },
{ "res", required_argument, NULL, ARG_RES },
{ "port", required_argument, NULL, ARG_PORT },
{ "uibc", no_argument, NULL, ARG_UIBC },
{}
};
int c;
uibc = false;
rstp_port = DEFAULT_RSTP_PORT;
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) {
case 'h':
@ -705,6 +786,9 @@ static int parse_argv(int argc, char *argv[])
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;
@ -717,6 +801,12 @@ static int parse_argv(int argc, char *argv[])
&wfd_supported_res_vesa,
&wfd_supported_res_hh);
break;
case ARG_PORT:
rstp_port = atoi(optarg);
break;
case ARG_UIBC:
uibc = true;
break;
case '?':
return -EINVAL;
}