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

Merge branch 'agusakov'

This commit is contained in:
albfan 2015-03-30 12:29:37 +02:00
commit 640f731d44
18 changed files with 562 additions and 44 deletions

View file

@ -19,6 +19,7 @@ miracle_sinkctl_SOURCES = \
ctl-cli.c \
ctl-sink.c \
ctl-wifi.c \
wfd.c \
sinkctl.c
miracle_sinkctl_CPPFLAGS = \
$(AM_CPPFLAGS) \

View file

@ -97,11 +97,10 @@ int cli_help(const struct cli_cmd *cmds)
{
unsigned int i;
if (is_cli()) {
cli_printf("Available commands:\n");
} else {
if (!is_cli()) {
cli_fn_help();
}
cli_printf("Available commands:\n");
for (i = 0; cmds[i].cmd; ++i) {
if (!cmds[i].desc)

View file

@ -35,6 +35,7 @@
#include "rtsp.h"
#include "shl_macro.h"
#include "shl_util.h"
#include "wfd.h"
struct ctl_sink {
sd_event *event;
@ -51,6 +52,13 @@ struct ctl_sink {
bool connected : 1;
bool hup : 1;
uint32_t resolutions_cea;
uint32_t resolutions_vesa;
uint32_t resolutions_hh;
int hres;
int vres;
};
/*
@ -120,13 +128,37 @@ static void sink_handle_get_parameter(struct ctl_sink *s,
if (r < 0)
return cli_vERR(r);
r = rtsp_message_append(rep, "{&&&&}",
"wfd_content_protection: none",
"wfd_video_formats: 00 00 01 01 0000007f 003fffff 00000000 00 0000 0000 00 none none",
"wfd_audio_codecs: AAC 00000007 00",
"wfd_client_rtp_ports: RTP/AVP/UDP;unicast 1991 0 mode=play");
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) {
r = rtsp_message_append(rep, "{&}",
"wfd_client_rtp_ports: RTP/AVP/UDP;unicast 1991 0 mode=play");
if (r < 0)
return cli_vERR(r);
}
rtsp_message_seal(rep);
cli_debug("OUTGOING: %s\n", rtsp_message_get_raw(rep));
@ -182,6 +214,27 @@ static int sink_setup_fn(struct rtsp *bus, struct rtsp_message *m, void *data)
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, hres, vres);
return 0;
}
}
return -EINVAL;
}
static void sink_handle_set_parameter(struct ctl_sink *s,
struct rtsp_message *m)
{
@ -189,6 +242,7 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
const char *trigger;
const char *url;
char *nu;
unsigned int cea_res, vesa_res, hh_res;
int r;
r = rtsp_message_new_reply_for(m, &rep, RTSP_CODE_OK, NULL);
@ -219,16 +273,19 @@ static void sink_handle_set_parameter(struct ctl_sink *s,
}
}
rtsp_message_exit_header(m);
rtsp_message_exit_body(m);
/* 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) {
rtsp_message_exit_header(m);
rtsp_message_exit_body(m);
if (r < 0)
return;
}
if (!strcmp(trigger, "SETUP")) {
if (!s->url) {
@ -453,6 +510,9 @@ int ctl_sink_new(struct ctl_sink **out,
s->event = sd_event_ref(event);
s->fd = -1;
s->resolutions_cea = wfd_supported_res_cea;
s->resolutions_vesa = wfd_supported_res_vesa;
s->resolutions_hh = wfd_supported_res_hh;
*out = s;
return 0;

View file

@ -975,6 +975,17 @@ static int ctl_wifi_peer_fn(sd_bus *bus,
return cli_log_parser(r);
ctl_fn_peer_provision_discovery(p, prov, pin);
} else if (sd_bus_message_is_signal(m,
"org.freedesktop.miracle.wifi.Peer",
"GoNegRequest")) {
/* connection request */
const char *prov, *pin;
r = sd_bus_message_read(m, "ss", &prov, &pin);
if (r < 0)
return cli_log_parser(r);
ctl_fn_peer_go_neg_request(p, prov, pin);
} else if (sd_bus_message_is_signal(m,
"org.freedesktop.miracle.wifi.Peer",
"FormationFailure")) {

View file

@ -236,6 +236,10 @@ struct cli_cmd {
extern sd_event *cli_event;
extern sd_bus *cli_bus;
extern unsigned int wfd_supported_res_cea;
extern unsigned int wfd_supported_res_vesa;
extern unsigned int wfd_supported_res_hh;
int cli_init(sd_bus *bus, const struct cli_cmd *cmds);
void cli_destroy(void);
int cli_run(void);
@ -252,6 +256,9 @@ void ctl_fn_peer_free(struct ctl_peer *p);
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
const char *prov,
const char *pin);
void ctl_fn_peer_go_neg_request(struct ctl_peer *p,
const char *prov,
const char *pin);
void ctl_fn_peer_formation_failure(struct ctl_peer *p, const char *reason);
void ctl_fn_peer_connected(struct ctl_peer *p);
void ctl_fn_peer_disconnected(struct ctl_peer *p);
@ -260,6 +267,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 cli_fn_help(void);

View file

@ -34,6 +34,7 @@
#include <time.h>
#include <unistd.h>
#include "ctl.h"
#include "wfd.h"
#include "shl_macro.h"
#include "shl_util.h"
@ -49,9 +50,13 @@ static pid_t sink_pid;
static char *bound_link;
static struct ctl_link *running_link;
static struct ctl_peer *running_peer;
static struct ctl_peer *pending_peer;
char *gst_scale_res;
int gst_audio_en = 1;
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 list
@ -280,6 +285,14 @@ 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);
@ -321,9 +334,10 @@ static const struct cli_cmd cli_cmds[] = {
{ },
};
static void spawn_gst(void)
static void spawn_gst(int hres, int vres)
{
char *argv[64];
char resolution[64];
pid_t pid;
int fd_journal, i;
sigset_t mask;
@ -363,6 +377,11 @@ static void spawn_gst(void)
argv[i++] = "-s";
argv[i++] = gst_scale_res;
}
if (hres && vres) {
sprintf(resolution, "%dx%d", hres, vres);
argv[i++] = "-r";
argv[i++] = resolution;
}
argv[i] = NULL;
execve(argv[0], argv, environ);
@ -385,7 +404,6 @@ void ctl_fn_sink_connected(struct ctl_sink *s)
{
cli_notice("SINK connected");
sink_connected = true;
spawn_gst();
}
void ctl_fn_sink_disconnected(struct ctl_sink *s)
@ -399,6 +417,13 @@ void ctl_fn_sink_disconnected(struct ctl_sink *s)
}
}
void ctl_fn_sink_resolution_set(struct ctl_sink *s, int hres, int vres)
{
cli_printf("SINK set resolution %dx%d\n", hres, vres);
if (sink_connected)
spawn_gst(hres, vres);
}
void ctl_fn_peer_new(struct ctl_peer *p)
{
if (p->l != running_link || shl_isempty(p->wfd_subelements))
@ -414,6 +439,14 @@ 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);
@ -440,10 +473,23 @@ void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
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
@ -480,6 +526,8 @@ void ctl_fn_peer_connected(struct ctl_peer *p)
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);
@ -557,9 +605,15 @@ void cli_fn_help()
" --log-level <lvl> Maximum level for log messages\n"
" --audio <0/1> Enable audio support (default %d)\n"
" --scale WxH Scale to resolution\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"
"Commands:\n"
, program_invocation_short_name, gst_audio_en);
, program_invocation_short_name, gst_audio_en,
wfd_supported_res_cea, wfd_supported_res_vesa, wfd_supported_res_hh
);
wfd_print_resolutions();
/*
* 80-char barrier:
* 01234567890123456789012345678901234567890123456789012345678901234567890123456789
@ -627,6 +681,7 @@ static int parse_argv(int argc, char *argv[])
ARG_LOG_LEVEL,
ARG_AUDIO,
ARG_SCALE,
ARG_RES,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
@ -634,6 +689,7 @@ static int parse_argv(int argc, char *argv[])
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "audio", required_argument, NULL, ARG_AUDIO },
{ "scale", required_argument, NULL, ARG_SCALE },
{ "res", required_argument, NULL, ARG_RES },
{}
};
int c;
@ -654,6 +710,12 @@ static int parse_argv(int argc, char *argv[])
case ARG_SCALE:
gst_scale_res = optarg;
break;
case ARG_RES:
sscanf(optarg, "%x,%x,%x",
&wfd_supported_res_cea,
&wfd_supported_res_vesa,
&wfd_supported_res_hh);
break;
case '?':
return -EINVAL;
}

220
src/ctl/wfd.c Normal file
View file

@ -0,0 +1,220 @@
/*
* MiracleCast - Wifi-Display/Miracast Implementation
*
* Copyright (c) 2014 Andrey Gusakov <andrey.gusakov@cogentembedded.com>
* 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 <stdio.h>
#include <stdint.h>
#include "ctl.h"
struct resolution_bitmap {
int index;
int hres;
int vres;
int fps;
};
/*
* CEA resolutions and refrash rate bitmap/index table
* also used in native resolution field
*/
struct resolution_bitmap resolutions_cea[] = {
{0, 640, 480, 60}, /* p60 */
{1, 720, 480, 60}, /* p60 */
{2, 720, 480, 60}, /* i60 */
{3, 720, 576, 50}, /* p50 */
{4, 720, 576, 50}, /* i50 */
{5, 1280, 720, 30}, /* p30 */
{6, 1280, 720, 60}, /* p60 */
{7, 1920, 1080, 30}, /* p30 */
{8, 1920, 1080, 60}, /* p60 */
{9, 1920, 1080, 60}, /* i60 */
{10, 1280, 720, 25}, /* p25 */
{11, 1280, 720, 50}, /* p50 */
{12, 1920, 1080, 25}, /* p25 */
{13, 1920, 1080, 50}, /* p50 */
{14, 1920, 1080, 50}, /* i50 */
{15, 1280, 720, 24}, /* p24 */
{16, 1920, 1080, 24}, /* p24 */
{0, 0, 0, 0},
};
struct resolution_bitmap resolutions_vesa[] = {
{0, 800, 600, 30}, /* p30 */
{1, 800, 600, 60}, /* p60 */
{2, 1024, 768, 30}, /* p30 */
{3, 1024, 768, 60}, /* p60 */
{4, 1152, 854, 30}, /* p30 */
{5, 1152, 854, 60}, /* p60 */
{6, 1280, 768, 30}, /* p30 */
{7, 1280, 768, 60}, /* p60 */
{8, 1280, 800, 30}, /* p30 */
{9, 1280, 800, 60}, /* p60 */
{10, 1360, 768, 30}, /* p30 */
{11, 1360, 768, 60}, /* p60 */
{12, 1366, 768, 30}, /* p30 */
{13, 1366, 768, 60}, /* p60 */
{14, 1280, 1024, 30}, /* p30 */
{15, 1280, 1024, 60}, /* p60 */
{16, 1440, 1050, 30}, /* p30 */
{17, 1440, 1050, 60}, /* p60 */
{18, 1440, 900, 30}, /* p30 */
{19, 1440, 900, 60}, /* p60 */
{20, 1600, 900, 30}, /* p30 */
{21, 1600, 900, 60}, /* p60 */
{22, 1600, 1200, 30}, /* p30 */
{23, 1600, 1200, 60}, /* p60 */
{24, 1680, 1024, 30}, /* p30 */
{25, 1680, 1024, 60}, /* p60 */
{26, 1680, 1050, 30}, /* p30 */
{27, 1680, 1050, 60}, /* p60 */
{28, 1920, 1200, 30}, /* p30 */
{0, 0, 0, 0},
};
struct resolution_bitmap resolutions_hh[] = {
{0, 800, 480, 30}, /* p30 */
{1, 800, 480, 60}, /* p60 */
{2, 854, 480, 30}, /* p30 */
{3, 854, 480, 60}, /* p60 */
{4, 864, 480, 30}, /* p30 */
{5, 864, 480, 60}, /* p60 */
{6, 640, 360, 30}, /* p30 */
{7, 640, 360, 60}, /* p60 */
{8, 960, 540, 30}, /* p30 */
{9, 960, 540, 60}, /* p60 */
{10, 848, 480, 30}, /* p30 */
{11, 848, 480, 60}, /* p60 */
{0, 0, 0, 0},
};
void wfd_print_resolutions(void)
{
int i;
printf("CEA resolutions:\n");
for (i = 0; resolutions_cea[i].hres != 0; i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
resolutions_cea[i].hres, resolutions_cea[i].vres,
resolutions_cea[i].fps);
}
printf("VESA resolutions:\n");
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
resolutions_vesa[i].hres, resolutions_vesa[i].vres,
resolutions_vesa[i].fps);
}
printf("HH resolutions:\n");
for (i = 0; resolutions_hh[i].hres != 0; i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
resolutions_hh[i].hres, resolutions_hh[i].vres,
resolutions_hh[i].fps);
}
}
uint32_t vfd_generate_resolution_mask(unsigned int index)
{
return ((1 << (index + 1)) - 1);
}
void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mask)
{
int i;
if (cea_mask) {
cli_debug("CEA resolutions:");
for (i = 0; resolutions_cea[i].hres != 0; i++)
if ((1 << resolutions_cea[i].index) & cea_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
resolutions_cea[i].hres, resolutions_cea[i].vres,
resolutions_cea[i].fps);
}
if (vesa_mask) {
cli_debug("VESA resolutions:");
for (i = 0; resolutions_vesa[i].hres != 0; i++)
if ((1 << resolutions_vesa[i].index) & vesa_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
resolutions_vesa[i].hres, resolutions_vesa[i].vres,
resolutions_vesa[i].fps);
}
if (hh_mask) {
cli_debug("HH resolutions:");
for (i = 0; resolutions_hh[i].hres != 0; i++)
if ((1 << resolutions_hh[i].index) & hh_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
resolutions_hh[i].hres, resolutions_hh[i].vres,
resolutions_hh[i].fps);
}
}
int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres)
{
int i;
if (!mask)
return -EINVAL;
for (i = 0; resolutions_cea[i].hres != 0; i++) {
if ((1 << resolutions_cea[i].index) & mask) {
*vres = resolutions_cea[i].vres;
*hres = resolutions_cea[i].hres;
return 0;
}
}
return -EINVAL;
}
int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres)
{
int i;
if (!mask)
return -EINVAL;
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
if ((1 << resolutions_vesa[i].index) & mask) {
*vres = resolutions_vesa[i].vres;
*hres = resolutions_vesa[i].hres;
return 0;
}
}
return -EINVAL;
}
int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
{
int i;
if (!mask)
return -EINVAL;
for (i = 0; resolutions_hh[i].hres != 0; i++) {
if ((1 << resolutions_hh[i].index) & mask) {
*vres = resolutions_hh[i].vres;
*hres = resolutions_hh[i].hres;
return 0;
}
}
return -EINVAL;
}

29
src/ctl/wfd.h Normal file
View file

@ -0,0 +1,29 @@
/*
* MiracleCast - Wifi-Display/Miracast Implementation
*
* Copyright (c) 2014 Andrey Gusakov <andrey.gusakov@cogentembedded.com>
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
*
* MiracleCast is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* MiracleCast is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WFD_H
#define WFD_H
void wfd_print_resolutions(void);
int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres);
#endif /* WFD_H */

View file

@ -363,7 +363,11 @@ void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
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)
{
}
void ctl_fn_peer_formation_failure(struct ctl_peer *p, const char *reason)
{
if (cli_running())