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

run as source

This commit is contained in:
Alberto Fanjul 2022-11-01 09:11:47 +01:00
parent 439dac09c5
commit fba5eb4a11
72 changed files with 9577 additions and 399 deletions

View file

@ -1,14 +1,130 @@
INSTALL(
PROGRAMS miracle-gst gstplayer uibc-viewer
DESTINATION bin
)
include(ValaPrecompile)
pkg_check_modules(GIO2 REQUIRED gio-2.0)
pkg_check_modules(GDK3 REQUIRED gdk-3.0)
pkg_check_modules(GST1 REQUIRED gstreamer-1.0)
INSTALL(
FILES org.freedesktop.miracle.conf
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
)
include_directories(
${GST1_INCLUDE_DIRS}
${GDK3_INCLUDE_DIRS}
${GIO_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}
)
INSTALL(
set(CMAKE_C_FLAGS "-Wno-deprecated-declarations ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-Wno-unused-but-set-variable ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-Wno-missing-braces ${CMAKE_C_FLAGS}")
if(VALA_VERSION VERSION_GREATER 0.34.0 AND NOT GDK_VERSION_NEWER_THEN_3_22)
list(APPEND VALA_EXTRA_OPTIONS -D GDK3_HAS_MONITOR_CLASS)
endif()
vala_precompile(
GSTENCODER_SRC gstencoder
gstencoder.vala
OPTIONS
--target-glib=2.50
PACKAGES
gstreamer-1.0
gio-2.0
posix
)
add_executable(gstencoder ${GSTENCODER_SRC})
target_link_libraries(
gstencoder
${GST1_LIBRARIES}
${GDK3_LIBRARIES}
${GIO2_LIBRARIES}
)
vala_precompile(
DISPCTL_SRC dispctl
dispctl.vala
networkmanager.vala
miracle-wifi.vala
miracle-wfd.vala
GENERATE_HEADER
dispctl.h
CUSTOM_VAPIS
sigint.vapi
OPTIONS
--target-glib=2.50
${VALA_EXTRA_OPTIONS}
PACKAGES
gio-2.0
gdk-3.0
)
add_executable(miracle-dispctl ${DISPCTL_SRC} sigint.c)
target_link_libraries(miracle-dispctl ${GIO2_LIBRARIES} ${GDK3_LIBRARIES})
########### install files ###############
install(TARGETS gstencoder DESTINATION bin)
install(TARGETS miracle-dispctl DESTINATION bin)
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=system_bus_services_dir dbus-1
OUTPUT_VARIABLE DBUS_SYSTEM_SERVICES_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=systemdsystemunitdir systemd
OUTPUT_VARIABLE SYSTEMD_SYSTEM_UNIT_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
configure_file(
miracle-wifid.service.cmake
miracle-wifid.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/miracle-wifid.service
DESTINATION ${SYSTEMD_SYSTEM_UNIT_DIR}/
)
install(
CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink miracle-wifid.service \$ENV{DESTDIR}${SYSTEMD_SYSTEM_UNIT_DIR}/dbus-org.freedesktop.miracle.wifi.service)"
)
configure_file(
miracle-dispd.service.cmake
miracle-dispd.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/miracle-dispd.service
DESTINATION ${SYSTEMD_SYSTEM_UNIT_DIR}/
)
install(
CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink miracle-dispd.service \$ENV{DESTDIR}${SYSTEMD_SYSTEM_UNIT_DIR}/dbus-org.freedesktop.miracle.wfd.service)"
)
configure_file(
org.freedesktop.miracle.wifi.service.cmake
org.freedesktop.miracle.wifi.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.miracle.wifi.service
DESTINATION $ENV{DESTDIR}${DBUS_SYSTEM_SERVICES_DIR}/
)
configure_file(
org.freedesktop.miracle.wfd.service.cmake
org.freedesktop.miracle.wfd.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.miracle.wfd.service
DESTINATION $ENV{DESTDIR}${DBUS_SYSTEM_SERVICES_DIR}/
)
install(
PROGRAMS miracle-gst gstplayer uibc-viewer
DESTINATION bin
)
install(
FILES org.freedesktop.miracle.conf
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
)
install(
FILES miracle-wifid miracle-sinkctl miracle-wifictl
DESTINATION ${DATADIR}/bash-completion/completions
)

6
res/create-symlinks Executable file
View file

@ -0,0 +1,6 @@
#!/bin/sh
SYSTEMD_SYSTEM_UNIT_DIR=${DESTDIR}`pkg-config --variable=systemdsystemunitdir systemd`
ln -sfv miracle-wifid.service \
${SYSTEMD_SYSTEM_UNIT_DIR}/dbos-org.freedesktop.miracle.wifi.service
ln -sfv miracle-dispd.service \
${SYSTEMD_SYSTEM_UNIT_DIR}/dbos-org.freedesktop.miracle.wfd.service

824
res/dispctl.vala Normal file
View file

@ -0,0 +1,824 @@
/*
* 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/>.
*/
using Org.Freedesktop.NetworkManager;
using Org.Freedesktop.Miracle.Wifi;
using Org.Freedesktop.Miracle.Wfd;
const string BUS_NAME_NETWORK_MANAGER = "org.freedesktop.NetworkManager";
const string BUS_NAME_WIFID = "org.freedesktop.miracle.wifi";
const string BUS_NAME_DISPD = "org.freedesktop.miracle.wfd";
const string OBJ_PATH_DEVICE = "/org/freedesktop/NetworkManager/Devices";
const string OBJ_PATH_LINK = "/org/freedesktop/miracle/wifi/link";
const string OBJ_PATH_PEER = "/org/freedesktop/miracle/wifi/peer";
const string OBJ_PATH_SINK = "/org/freedesktop/miracle/wfd/sink";
const string OBJ_PATH_SESSION = "/org/freedesktop/miracle/wfd/session";
const string IFACE_DEVICE = "org.freedesktop.NetworkManager.Device";
const string IFACE_LINK = "org.freedesktop.miracle.wifi.Link";
const string IFACE_PEER = "org.freedesktop.miracle.wifi.Peer";
const string IFACE_SINK = "org.freedesktop.miracle.wfd.Sink";
const string IFACE_SESSION = "org.freedesktop.miracle.wfd.Session";
errordomain DispCtlError
{
NO_SUCH_NIC,
TIMEOUT,
MONITOR_GONE,
FORMATION_ERROR,
NO_P2P_SUPPORT,
}
private void print(string format, ...)
{
stderr.printf("%s: ", Environment.get_prgname());
stderr.vprintf(format, va_list());
stderr.printf("\n");
}
// to deal with sd_bus_path_encode/decode()ed path
private string decode_path(string s)
{
char c;
StringBuilder d = new StringBuilder();
for(var i = 0; i < s.length; i ++) {
if(s.data[i] == (uint8) '_') {
c = (char) s.substring(i + 1, 2).to_long(null, 16);
i += 2;
}
else {
c = (char) s.data[i];
}
d.append_c(c);
}
return d.str;
}
private class DispCtl : GLib.Application
{
static string opt_iface;
static string opt_wfd_subelems;
static string opt_peer_mac;
static string opt_display;
static string opt_authority;
static int opt_monitor_num;
static string opt_audio_device;
static bool opt_dont_borrow_wnic = false;
static bool opt_dont_return_wnic = false;
protected signal void link_added(string index, Link link);
protected signal void link_removed(string index, Link link);
protected signal void peer_added(string label, Peer peer);
protected signal void peer_removed(string label, Peer peer);
protected signal void sink_added(string label, Sink sink);
protected signal void sink_removed(string label, Sink sink);
protected signal void session_added(string id, Session session);
protected signal void session_removed(string id, Session session);
DBusObjectManagerClient nm;
DBusObjectManagerClient wifi;
DBusObjectManagerClient wfd;
HashTable<string, Device> devices;
HashTable<string, Link> links;
HashTable<string, Peer> peers;
HashTable<string, Sink> sinks;
HashTable<string, Session> sessions;
string curr_sink_mac;
Gdk.Display display;
Peer curr_peer;
Sink curr_sink;
Session curr_session;
Cancellable cancellable;
const GLib.OptionEntry[] option_entries = {
{ "interface", 'i', 0, OptionArg.STRING, ref opt_iface, "name of wireless network interface", "WNIC name" },
{ "wfd-subelems", 'w', 0, OptionArg.STRING, ref opt_wfd_subelems, "device infomation. default: 000600901c4400c8", "device info subelems" },
{ "peer-mac", 'p', 0, OptionArg.STRING, ref opt_peer_mac, "MAC address of target peer", "peer MAC" },
{ "authority", 'x', 0, OptionArg.STRING, ref opt_authority, "authority to capture from display. default: XAUTHORITY environment variable", "display authority" },
{ "display", 'd', 0, OptionArg.STRING, ref opt_display, "display name. default: DISPLAY environment variable", "display name" },
{ "monitor-num", 'm', 0, OptionArg.INT, ref opt_monitor_num, "monitor number. default: -1, primary monitor", "monitor number" },
{ "audio-device", 'a', 0, OptionArg.STRING, ref opt_audio_device, "pulseaudio device name", "audio device name" },
{ "dont-borrow", 'b', 0, OptionArg.NONE, ref opt_dont_borrow_wnic, "do not acquire the ownership of WNIC before using it", "don't borrow WNIC" },
{ "dont-return", 'r', 0, OptionArg.NONE, ref opt_dont_return_wnic, "do not release the ownership of WNIC after using it", "don't release WNIC" },
{ null },
};
public DispCtl()
{
Object(application_id: "org.freedesktop.miracle.DispCtl",
flags: ApplicationFlags.FLAGS_NONE);
devices = new HashTable<string, Device>(str_hash, str_equal);
links = new HashTable<string, Link>(str_hash, str_equal);
peers = new HashTable<string, Peer>(str_hash, str_equal);
sinks = new HashTable<string, Sink>(str_hash, str_equal);
sessions = new HashTable<string, Session>(str_hash, str_equal);
cancellable = new Cancellable();
add_main_option_entries(option_entries);
}
private DBusProxy? add_object(string path) throws Error
{
int sep = path.last_index_of_char('/');
string prefix = path.substring(0, sep);
string key = path.substring(sep + 1);
switch(prefix) {
case OBJ_PATH_DEVICE:
Device d = Bus.get_proxy_sync(BusType.SYSTEM,
BUS_NAME_NETWORK_MANAGER,
path);
if(is_wnic(d.interface) && !devices.contains(d.interface)) {
devices.insert(d.interface, d);
return d as DBusProxy;
}
break;
case OBJ_PATH_LINK:
key = decode_path(key);
Link l = links.lookup(key);
if(null == l) {
l = Bus.get_proxy_sync(BusType.SYSTEM,
BUS_NAME_WIFID,
path);
links.insert(key, l);
info("found wireless interface: %s", l.interface_name);
link_added(key, l);
}
return l as DBusProxy;
case OBJ_PATH_PEER:
key = decode_path(key);
Peer p = peers.lookup(key);
if(null == p) {
p = Bus.get_proxy_sync(BusType.SYSTEM,
BUS_NAME_WIFID,
path);
peers.insert(key, p);
info("peer added: %s (%s)", key, p.friendly_name);
peer_added(key, p);
}
return p as DBusProxy;
case OBJ_PATH_SINK:
key = decode_path(key);
Sink s = sinks.lookup(key);
if(null == s) {
s = Bus.get_proxy_sync(BusType.SYSTEM,
BUS_NAME_DISPD,
path);
sinks.insert(key, s);
info("sink added: %s", key);
sink_added(key, s);
}
return s as DBusProxy;
case OBJ_PATH_SESSION:
key = decode_path(key);
Session s = sessions.lookup(key);
if(null == s) {
s = Bus.get_proxy_sync(BusType.SYSTEM,
BUS_NAME_DISPD,
path);
sessions.insert(key, s);
info("session added: %s", key);
session_added(key, s);
}
return s as DBusProxy;
}
return null;
}
private void remove_object(string path)
{
int sep = path.last_index_of_char('/');
string prefix = path.substring(0, sep);
string key = path.substring(sep + 1);
switch(prefix) {
case OBJ_PATH_DEVICE:
devices.remove(key);
break;
case OBJ_PATH_LINK:
key = decode_path(key);
Link l = links.lookup(key);
if(null == l) {
break;
}
links.remove(key);
link_removed(key, l);
break;
case OBJ_PATH_PEER:
key = decode_path(key);
Peer p = peers.lookup(key);
if(null == p) {
info("removing stray peer: %s", key);
break;
}
if(p == curr_peer) {
curr_peer = null;
}
peers.remove(key);
peer_removed(key, p);
break;
case OBJ_PATH_SINK:
key = decode_path(key);
Sink s = sinks.lookup(key);
if(null == s) {
info("removing stray sink: %s", key);
break;
}
if(s == curr_sink) {
curr_sink = null;
}
sinks.remove(key);
sink_removed(key, s);
break;
case OBJ_PATH_SESSION:
key = decode_path(key);
Session s = sessions.lookup(key);
if(null == s) {
info("removing stray session: %s", key);
break;
}
session_removed(key, s);
sessions.remove(key);
break;
}
}
private void on_object_added(DBusObjectManager m, DBusObject o)
{
try {
add_object(o.get_object_path());
}
catch(Error e) {
print("failed to fetch information from DBus for object: %s",
o.get_object_path());
}
}
private void on_object_removed(DBusObjectManager m, DBusObject o)
{
remove_object(o.get_object_path());
}
private void fetch_info_from_dbus() throws Error
{
info("connecting to wifid...");
wifi = new DBusObjectManagerClient.for_bus_sync(
BusType.SYSTEM,
DBusObjectManagerClientFlags.NONE,
BUS_NAME_WIFID,
"/org/freedesktop/miracle/wifi",
null,
null);
wifi.object_added.connect(on_object_added);
wifi.object_removed.connect(on_object_removed);
info("connecting to nm...");
nm = new DBusObjectManagerClient.for_bus_sync(
BusType.SYSTEM,
DBusObjectManagerClientFlags.NONE,
BUS_NAME_NETWORK_MANAGER,
"/org/freedesktop",
null,
null);
nm.object_added.connect(on_object_added);
nm.object_removed.connect(on_object_removed);
info("connecting to dispd...");
wfd = new DBusObjectManagerClient.for_bus_sync(
BusType.SYSTEM,
DBusObjectManagerClientFlags.NONE,
BUS_NAME_DISPD,
"/org/freedesktop/miracle/wfd",
null,
null);
wfd.object_added.connect(on_object_added);
wfd.object_removed.connect(on_object_removed);
info("fetching from wifid...");
foreach(var o in wifi.get_objects()) {
add_object(o.get_object_path());
}
info("fetching from nm...");
foreach(var o in nm.get_objects()) {
add_object(o.get_object_path());
}
info("fetching from wfd...");
foreach(var o in wfd.get_objects()) {
add_object(o.get_object_path());
}
}
private async void acquire_wnic_ownership() throws Error
{
Device d = find_device_by_name(opt_iface);
if(null != d && d.managed) {
info("NetworkManager is releasing ownership of %s...", opt_iface);
d.managed = false;
yield wait_prop_changed(d, "Managed");
}
Link l = find_link_by_name(opt_iface);
if(null == l) {
throw new DispCtlError.NO_SUCH_NIC("no such wireless adapter: %s",
opt_iface);
}
if(l.managed) {
info("wifid is releasing ownership of %s...", opt_iface);
l.unmanage();
yield wait_prop_changed(l, "Managed");
}
info("wifid is acquiring ownership of %s...", opt_iface);
l.manage();
yield wait_prop_changed(l, "Managed");
}
private async void start_p2p_scan() throws Error
{
Link? l = find_link_by_name(opt_iface);
if(l.wfd_subelements != opt_wfd_subelems) {
info("update wfd_subelems to broadcast what kind of device we are");
l.wfd_subelements = opt_wfd_subelems;
yield wait_prop_changed(l, "WfdSubelements");
}
if(-1 == l.p2p_state) {
throw new DispCtlError.NO_P2P_SUPPORT("link %s has no P2P supporting", l.interface_name);
}
else if(0 == l.p2p_state) {
info("wait for P2P supporting status...");
yield wait_prop_changed(l, "P2PState", 3);
}
if(!l.p2p_scanning) {
info("start P2P scanning...");
l.p2p_scanning = true;
yield wait_prop_changed(l, "P2PScanning");
}
print("wait for peer '%s'...", opt_peer_mac);
}
private async void wait_for_target_sink() throws Error
{
if(null != find_sink_by_mac(opt_peer_mac)) {
return;
}
ulong id = sink_added.connect((l, s) => {
if(null != find_sink_by_mac(opt_peer_mac)) {
wait_for_target_sink.callback();
}
});
var cancel_id = cancellable.cancelled.connect(() => {
Idle.add(wait_for_target_sink.callback);
});
yield;
cancellable.disconnect(cancel_id);
disconnect(id);
}
private async void form_p2p_group() throws Error
{
if(null != curr_sink_mac) {
print("already hang out with sink: %s", curr_sink_mac);
return;
}
Sink s = find_sink_by_mac(opt_peer_mac);
curr_sink_mac = opt_peer_mac;
string l = s.peer;
l = decode_path(l.substring(l.last_index_of_char('/') + 1));
Peer p = peers.lookup(l);
info("forming P2P group with %s (%s)...", p.p2p_mac, p.friendly_name);
ulong id = p.formation_failure.connect((r) => {
info("failed to form P2P group: %s", r);
});
p.connect("auto", "");
yield wait_prop_changed(p, "Connected", 20);
(p as Object).disconnect(id);
curr_peer = p;
info("P2P group formed");
}
#if GDK3_HAS_MONITOR_CLASS
private void get_monitor_geometry(out Gdk.Rectangle g) throws Error
{
Gdk.Monitor m;
if(-1 == opt_monitor_num) {
m = display.get_primary_monitor();
}
else {
m = display.get_monitor(opt_monitor_num);
}
if(null == m) {
throw new DispCtlError.MONITOR_GONE("specified monitor disappeared");
}
g = m.geometry;
}
#else
private void get_monitor_geometry(out Gdk.Rectangle g) throws Error
{
var s = display.get_default_screen();
int m = (-1 == opt_monitor_num)
? s.get_primary_monitor()
: opt_monitor_num;
if(s.get_n_monitors() <= m) {
throw new DispCtlError.MONITOR_GONE("specified monitor disappeared");
}
s.get_monitor_geometry(m, out g);
}
#endif
private unowned string session_state_to_str(int s)
{
switch(s) {
case 1:
return "connecting";
case 2:
return "capabilities exchanging";
case 3:
return "established";
case 4:
return "seting up session parameters";
case 5:
return "paused";
case 6:
return "playing";
case 7:
return "tearing down";
case 8:
return "destroyed";
}
return "unknown";
}
private async void establish_session() throws Error
{
Gdk.Rectangle g;
get_monitor_geometry(out g);
info("establishing display session...");
curr_sink = find_sink_by_mac(opt_peer_mac);
string path = curr_sink.start_session(opt_authority,
@"x://$(opt_display)",
g.x,
g.y,
g.width,
g.height,
null == opt_audio_device ? "" : opt_audio_device);
curr_session = add_object(path) as Session;
var prop_change_id = (curr_session as DBusProxy).g_properties_changed.connect((props) => {
string k;
Variant v;
foreach(var prop in props) {
prop.get("{sv}", out k, out v);
if(k != "State") {
continue;
}
info("session status: %s", session_state_to_str(v.get_int32()));
if(6 == v.get_int32()) {
Idle.add(establish_session.callback);
}
break;
}
});
cancellable.set_error_if_cancelled();
var cancel_id = cancellable.cancelled.connect(() => {
Idle.add(establish_session.callback);
});
bool timed_out = false;
var timeout_src = new TimeoutSource(10);
timeout_src.set_callback(() => {
timed_out = true;
Idle.add(establish_session.callback);
return false;
});
yield;
timeout_src.destroy();
cancellable.disconnect(cancel_id);
(curr_session as DBusProxy).disconnect(prop_change_id);
if(timed_out) {
throw new DispCtlError.TIMEOUT("failed to establish session");
}
}
private async void wait_for_session_ending() throws Error
{
info("wait for session ending");
ulong id = session_removed.connect((id, s) => {
wait_for_session_ending.callback();
});
yield;
disconnect(id);
info("session ended");
}
private async void release_wnic_ownership() throws Error
{
if(opt_dont_return_wnic) {
return;
}
cancellable.reset();
Link l = find_link_by_name(opt_iface);
if(null == l) {
throw new DispCtlError.NO_SUCH_NIC("no such wireless adapter: %s",
opt_iface);
}
if(l.managed) {
info("wifid is releasing ownership of %s...", opt_iface);
l.unmanage();
yield wait_prop_changed(l, "Managed");
}
Device d = find_device_by_name(opt_iface);
if(null != d && !d.managed) {
info("NetworkManager is acquiring ownership of %s...", opt_iface);
d.managed = true;
yield wait_prop_changed(d, "Managed");
}
}
private async void start_wireless_display() throws Error
{
fetch_info_from_dbus();
cancellable.set_error_if_cancelled();
if(!opt_dont_borrow_wnic) {
yield acquire_wnic_ownership();
}
cancellable.set_error_if_cancelled();
yield start_p2p_scan();
cancellable.set_error_if_cancelled();
yield wait_for_target_sink();
cancellable.set_error_if_cancelled();
yield form_p2p_group();
cancellable.set_error_if_cancelled();
yield establish_session();
cancellable.set_error_if_cancelled();
yield wait_for_session_ending();
cancellable.set_error_if_cancelled();
}
public void stop_wireless_display()
{
info("received termination request");
if(null != curr_session) {
info("tearing down wireless display...");
try {
curr_session.teardown();
wait_prop_changed.begin(curr_sink, "Session", 3, () => {
cancellable.cancel();
});
}
catch(Error e) {
warning("failed to tearing down normally: %s", e.message);
}
}
else {
cancellable.cancel();
}
}
private bool check_options()
{
if(null == opt_peer_mac) {
print("please specify a peer MAC with -p option");
return false;
}
print("peer-mac=%s", opt_peer_mac);
if(null == opt_display) {
opt_display = Environment.get_variable("DISPLAY");
}
print("display=%s", opt_display);
if(null == opt_authority) {
opt_authority = Environment.get_variable("XAUTHORITY");
}
print("authority=%s", opt_authority);
if(null == opt_iface) {
opt_iface = "wlan0";
}
print("interface=%s", opt_iface);
if(null == opt_wfd_subelems) {
opt_wfd_subelems = "000600901c4400c8";
}
print("wfd_subelemens=%s", opt_wfd_subelems);
display = Gdk.Display.open(opt_display);
if(null == display) {
print("invalid display option: %s", opt_display);
return false;
}
int n_monitors;
#if GDK3_HAS_MONITOR_CLASS
n_monitors = display.get_n_monitors();
#else
n_monitors = display.get_default_screen().get_n_monitors();
#endif
if(-1 > opt_monitor_num || opt_monitor_num >= n_monitors) {
print("invalid screen number option: %d", opt_monitor_num);
return false;
}
print("monitor-num=%d", opt_monitor_num);
return true;
}
protected override void activate()
{
if(!check_options()) {
return;
}
start_wireless_display.begin((o, r) => {
try {
start_wireless_display.end(r);
}
catch(Error e) {
print("failed to cast to wireless display: %s", e.message);
}
release_wnic_ownership.begin((o, r) => {
try {
release_wnic_ownership.end(r);
}
catch(Error e) {
print("failed to release ownership of wnic: %s", e.message);
}
release();
});
});
hold();
}
private unowned Device? find_device_by_name(string nic_name)
{
foreach(var d in devices.get_values()) {
if(nic_name == d.interface) {
return d;
}
}
return null;
}
private unowned Link? find_link_by_name(string nic_name)
{
foreach(var l in links.get_values()) {
if(nic_name == l.interface_name) {
return l;
}
}
return null;
}
private unowned Sink? find_sink_by_mac(string m)
{
foreach(var l in sinks.get_keys()) {
if(l.has_prefix(m)) {
return sinks.lookup(l);
}
}
return null;
}
private bool is_wnic(string nic_name)
{
return find_link_by_name(nic_name) != null;
}
private async void wait_prop_changed<T>(T o,
string name,
uint timeout = 1) throws Error
{
ulong prop_changed_id = (o as DBusProxy).g_properties_changed.connect((props) => {
string k;
Variant v;
foreach(var prop in props) {
prop.get("{sv}", out k, out v);
if(k == name) {
wait_prop_changed.callback();
break;
}
}
});
var cancel_id = cancellable.cancelled.connect(() => {
Idle.add(wait_prop_changed.callback);
});
bool timed_out = false;
Source timeout_src = null;
if(0 < timeout) {
timeout_src = new TimeoutSource.seconds(timeout);
timeout_src.set_callback(() => {
timed_out = true;
wait_prop_changed.callback();
return false;
});
timeout_src.attach(null);
}
yield;
cancellable.disconnect(cancel_id);
if(null != timeout_src) {
timeout_src.destroy();
}
(o as DBusProxy).disconnect(prop_changed_id);
if(timed_out) {
throw new DispCtlError.TIMEOUT("timeout to wait for property %s change",
name);
}
cancellable.set_error_if_cancelled();
}
}
int main(string[]? argv)
{
Gdk.init(ref argv);
Intl.setlocale();
Environment.set_prgname(Path.get_basename(argv[0]));
Application app = new DispCtl();
Application.set_default(app);
Sigint.add_watch((app as DispCtl).stop_wireless_display);
try {
app.register();
}
catch(Error e) {
print("failed to startup: %s", e.message);
return 1;
}
int r = app.run(argv);
print("Bye");
return r;
}

426
res/gstencoder.vala Normal file
View file

@ -0,0 +1,426 @@
/*
* 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/>.
*/
public enum DispdEncoderConfig
{
DISPLAY_TYPE, /* string */
DISPLAY_NAME, /* string */
MONITOR_NUM, /* uint32 */
X, /* uint32 */
Y, /* uint32 */
WIDTH, /* uint32 */
HEIGHT, /* uint32 */
WINDOW_ID, /* uint32 */
FRAMERATE, /* uint32 */
SCALE_WIDTH, /* uint32 */
SCALE_HEIGHT, /* uint32 */
AUDIO_TYPE, /* string */
AUDIO_DEV, /* string */
PEER_ADDRESS, /* string */
RTP_PORT0, /* uint32 */
RTP_PORT1, /* uint32 */
PEER_RTCP_PORT, /* uint32 */
LOCAL_ADDRESS, /* uint32 */
LOCAL_RTCP_PORT, /* uint32 */
H264_PROFILE,
H264_LEVEL,
DEBUG_LEVEL,
}
[DBus (name = "org.freedesktop.miracle.encoder.error")]
public errordomain DispdEncoderError
{
CANT_RETURN_UNIQUE_NAME,
UNEXPECTED_EOS,
ENCODER_ERROR,
INVALID_STATE,
}
[DBus(name = "org.freedesktop.miracle.encoder.state")]
public enum DispdEncoderState
{
NULL,
CONFIGURED,
READY,
STARTED,
PAUSED,
}
[DBus(name = "org.freedesktop.miracle.encoder")]
public interface DispdEncoder : GLib.Object
{
public const string OBJECT_PATH = "/org/freedesktop/miracle/encoder";
public abstract DispdEncoderState state { get; protected set; }
public abstract signal void error(string reason);
public abstract void configure(HashTable<DispdEncoderConfig, Variant> configs) throws DispdEncoderError;
public abstract void start() throws DispdEncoderError;
public abstract void pause() throws DispdEncoderError;
public abstract void stop() throws DispdEncoderError;
}
internal class GstEncoder : DispdEncoder, GLib.Object
{
private DBusConnection conn;
private HashTable<DispdEncoderConfig, Variant> configs;
private Gst.Element pipeline;
private Gst.State pipeline_state = Gst.State.NULL;
private DispdEncoderState _state = DispdEncoderState.NULL;
public DispdEncoderState state {
get { return _state; }
protected set {
if(_state == value) {
return;
}
_state = value;
notify_state_changed();
}
}
private void notify_state_changed()
{
var builder = new VariantBuilder(VariantType.ARRAY);
var invalid_builder = new VariantBuilder(new VariantType ("as"));
Variant s = state;
builder.add ("{sv}", "State", s);
try {
conn.emit_signal(null,
"/org/freedesktop/miracle/encoder",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
new Variant ("(sa{sv}as)",
"org.freedesktop.miracle.encoder",
builder,
invalid_builder));
}
catch (Error e) {
warning("failed to emit signal: %s", e.message);
}
}
string gen_scaler_n_converter_desc(uint32 width, uint32 height)
{
if(null != Gst.ElementFactory.find("vaapih264enc")) {
return ("! vaapipostproc " +
"scale-method=2 " +
"format=3 " +
"force-aspect-ratio=true " +
"! video/x-raw, " +
"format=YV12, " +
"width=%u, " +
"height=%u ").printf(width, height);
}
info("since vaapih264enc is not available, vaapipostproc can't be " +
"trusted, use videoscale+videoconvert instead");
return ("! videoscale method=0 add-borders=true " +
"! video/x-raw, width=%u, height=%u " +
"! videoconvert " +
"! video/x-raw, format=YV12 ").printf(width, height);
}
string gen_encoder_desc(uint32 framerate)
{
if(null != Gst.ElementFactory.find("vaapih264enc")) {
return ("! vaapih264enc " +
"rate-control=1 " +
"num-slices=1 " + /* in WFD spec, one slice per frame */
"max-bframes=0 " + /* in H264 CHP, no bframe supporting */
"cabac=true " + /* in H264 CHP, CABAC entropy codeing is supported, but need more processing to decode */
"dct8x8=true " + /* in H264 CHP, DTC is supported */
"cpb-length=1000 " + /* shortent buffer in order to decrease latency */
"keyframe-period=%u ").printf(framerate);
}
info("vaapih264enc not available, use x264enc instead");
return ("! x264enc pass=4 b-adapt=false key-int-max=%u " +
"speed-preset=4 tune=4 ").printf(framerate);
}
public void configure(HashTable<DispdEncoderConfig, Variant> configs) throws DispdEncoderError
{
uint32 framerate = configs.contains(DispdEncoderConfig.FRAMERATE)
? configs.get(DispdEncoderConfig.FRAMERATE).get_uint32()
: 30;
uint32 width = configs.contains(DispdEncoderConfig.WIDTH)
? configs.get(DispdEncoderConfig.WIDTH).get_uint32()
: 1920;
uint32 height = configs.contains(DispdEncoderConfig.HEIGHT)
? configs.get(DispdEncoderConfig.HEIGHT).get_uint32()
: 1080;
StringBuilder desc = new StringBuilder();
desc.append_printf(
"ximagesrc " +
"name=vsrc " +
"use-damage=false " +
"show-pointer=false " +
"startx=%u starty=%u endx=%u endy=%u " +
"! video/x-raw, " +
"framerate=%u/1 " +
"%s" + /* scaling & color space convertion */
"%s" + /* encoding */
"! h264parse " +
"! video/x-h264, " +
"alignment=nal, " +
"stream-format=byte-stream " +
"%s " + /* add queue if audio enabled */
"! mpegtsmux " +
"name=muxer " +
"! rtpmp2tpay " +
"! .send_rtp_sink_0 " +
"rtpbin " +
"name=session " +
"rtp-profile=1 " + /* avp */
"do-retransmission=true " +
"do-sync-event=true " +
"do-lost=true " +
"ntp-time-source=3 " + /* pipeline clock time */
"buffer-mode=0 " +
"latency=40 " +
"max-misorder-time=50 " +
"! application/x-rtp " +
"! udpsink " +
"sync=false " +
"async=false " +
"host=\"%s\" " +
"port=%u ",
configs.contains(DispdEncoderConfig.X)
? configs.get(DispdEncoderConfig.X).get_uint32()
: 0,
configs.contains(DispdEncoderConfig.Y)
? configs.get(DispdEncoderConfig.Y).get_uint32()
: 0,
configs.get(DispdEncoderConfig.X).get_uint32() + width - 1,
configs.get(DispdEncoderConfig.Y).get_uint32() + height - 1,
framerate,
gen_scaler_n_converter_desc(width, height),
gen_encoder_desc(framerate),
configs.contains(DispdEncoderConfig.AUDIO_TYPE)
? "! queue max-size-buffers=0 max-size-bytes=0"
: "",
configs.contains(DispdEncoderConfig.PEER_ADDRESS)
? configs.get(DispdEncoderConfig.PEER_ADDRESS).get_string()
: "",
configs.contains(DispdEncoderConfig.RTP_PORT0)
? configs.get(DispdEncoderConfig.RTP_PORT0).get_uint32()
: 16384);
if(configs.contains(DispdEncoderConfig.LOCAL_RTCP_PORT)) {
desc.append_printf("udpsrc " +
"address=\"%s\" " +
"port=%u " +
"reuse=true " +
"! session.recv_rtcp_sink_0 " +
"session.send_rtcp_src_0 " +
"! udpsink " +
"host=\"%s\" " +
"port=%u " +
"sync=false " +
"async=false ",
configs.contains(DispdEncoderConfig.LOCAL_ADDRESS)
? configs.get(DispdEncoderConfig.LOCAL_ADDRESS).get_string()
: "",
configs.contains(DispdEncoderConfig.LOCAL_RTCP_PORT)
? configs.get(DispdEncoderConfig.LOCAL_RTCP_PORT).get_uint32()
: 16385,
configs.contains(DispdEncoderConfig.PEER_ADDRESS)
? configs.get(DispdEncoderConfig.PEER_ADDRESS).get_string()
: "",
configs.contains(DispdEncoderConfig.PEER_RTCP_PORT)
? configs.get(DispdEncoderConfig.PEER_RTCP_PORT).get_uint32()
: 16385);
}
if(configs.contains(DispdEncoderConfig.AUDIO_TYPE)) {
desc.append_printf("pulsesrc " +
"do-timestamp=true " +
"client-name=miraclecast " +
"device=\"%s\" " +
"! avenc_aac " +
"! audio/mpeg, " +
"channels=2, " +
"rate=48000 " +
// "base-profile=lc " +
"! queue " +
"max-size-buffers=0 " +
"max-size-bytes=0 " +
"max-size-time=0 " +
"! muxer. ",
configs.contains(DispdEncoderConfig.AUDIO_DEV)
? configs.get(DispdEncoderConfig.AUDIO_DEV).get_string()
: "");
}
info("final pipeline description: %s", desc.str);
this.configs = configs;
try {
pipeline = Gst.parse_launch(desc.str);
}
catch(Error e) {
throw new DispdEncoderError.ENCODER_ERROR("%s", e.message);
}
var bus = pipeline.get_bus();
bus.add_signal_watch();
bus.message.connect(on_pipeline_message);
pipeline.set_state(Gst.State.READY);
}
public void start() throws DispdEncoderError
{
check_configs();
pipeline.set_state(Gst.State.PLAYING);
}
public void pause() throws DispdEncoderError
{
check_configs();
pipeline.set_state(Gst.State.PAUSED);
}
public void stop() throws DispdEncoderError
{
if(null == pipeline) {
return;
}
pipeline.set_state(Gst.State.NULL);
state = DispdEncoderState.NULL;
defered_terminate();
}
public async void prepare() throws Error
{
conn = yield Bus.get(BusType.SESSION);
conn.register_object(DispdEncoder.OBJECT_PATH, this as DispdEncoder);
#if VALA_0_54
string bus_info = "%s\n%s".printf(conn.unique_name,
BusType.SESSION.get_address_sync ());
#else
string bus_info = "%s\n%s".printf(conn.unique_name,
BusType.get_address_sync(BusType.SESSION));
#endif
/* we are ready, tell parent how to communicate with us */
ssize_t r = Posix.write(3, (void *) bus_info.data, bus_info.length);
if(0 > r) {
throw new DispdEncoderError.CANT_RETURN_UNIQUE_NAME("%s",
Posix.strerror(Posix.errno));
}
Posix.fsync(3);
}
private void defered_terminate()
{
Timeout.add(100, () => {
loop.quit();
return false;
});
}
private void check_configs() throws DispdEncoderError
{
if(null == configs || null == pipeline) {
throw new DispdEncoderError.INVALID_STATE("not configure yet");
}
}
private void on_pipeline_message(Gst.Message m)
{
Error e;
string d;
if(m.src != pipeline) {
return;
}
switch(m.type) {
case Gst.MessageType.EOS:
error("unexpected EOS");
defered_terminate();
break;
case Gst.MessageType.ERROR:
m.parse_error(out e, out d);
error("unexpected error: %s\n%s".printf(e.message, d));
defered_terminate();
break;
case Gst.MessageType.STATE_CHANGED:
Gst.State oldstate;
m.parse_state_changed(out oldstate, out pipeline_state, null);
info("pipeline state chagned from %s to %s",
oldstate.to_string(),
pipeline_state.to_string());
switch(pipeline_state) {
case Gst.State.READY:
state = DispdEncoderState.CONFIGURED;
break;
case Gst.State.PLAYING:
state = DispdEncoderState.STARTED;
break;
case Gst.State.PAUSED:
if(Gst.State.PLAYING == oldstate) {
state = DispdEncoderState.PAUSED;
}
break;
}
break;
default:
debug("unhandled message: %s", m.type.to_string());
break;
}
}
}
private MainLoop loop;
int main(string[] argv)
{
Gst.init(ref argv);
var encoder = new GstEncoder();
encoder.prepare.begin((o, r) => {
try {
encoder.prepare.end(r);
}
catch(Error e) {
error("%s", e.message);
}
});
loop = new MainLoop();
loop.run();
Posix.close(3);
Gst.deinit();
info("bye");
return 0;
}

View file

@ -48,7 +48,9 @@ class Player(object):
if title:
self.window.set_title(title)
if hasattr(self,'width') and hasattr(self,'height'):
if kwargs.get("fullscreen"):
self.window.fullscreen()
elif hasattr(self,'width') and hasattr(self,'height'):
self.window.set_default_size(self.width, self.height)
self.drawingarea = Gtk.DrawingArea()
@ -96,10 +98,10 @@ class Player(object):
if scale:
gstcommand += "videoscale method=1 ! video/x-raw,width="+str(self.width)+",height="+str(self.height)+" ! "
gstcommand += "autovideosink "
gstcommand += "autovideosink sync=false "
if audio:
gstcommand += "demuxer. ! queue max-size-buffers=0 max-size-time=0 ! aacparse ! avdec_aac ! audioconvert ! audioresample ! autoaudiosink "
gstcommand += "demuxer. ! queue max-size-buffers=0 max-size-time=0 ! aacparse ! avdec_aac ! audioconvert ! audioresample ! autoaudiosink sync=false "
self.pipeline = Gst.parse_launch(gstcommand)
@ -206,6 +208,7 @@ if __name__ == '__main__':
parser.add_argument("--log-level", metavar="lvl", help="Maximum level for log messages")
parser.add_argument("-p", "--port", type=int, default=7236, help="Port for rtsp")
parser.add_argument("-a", "--audio", dest="audio", action="store_true", help="Enable audio support")
parser.add_argument("-f", "--full-screen", dest="fullscreen", action="store_true", help="Enter full screen mode")
parser.add_argument("-s", "--scale", metavar="WxH", help="Scale to resolution")
parser.add_argument("-d", "--debug", help="Debug")
parser.add_argument("--uibc", help="Enable UIBC")
@ -216,7 +219,8 @@ if __name__ == '__main__':
# " default VESA %08X\n"
# " default HH %08X\n"
parser.add_argument("-r", "--resolution", help="Resolution")
parser.set_defaults(audio=True)
parser.set_defaults(audio=False)
parser.set_defaults(fullscreen=False)
args = parser.parse_args()
p = Player(**vars(args))

8
res/install-scripts Executable file
View file

@ -0,0 +1,8 @@
#!/bin/sh
RESDIR="${MESON_SOURCE_ROOT}/res"
BINDIR="${MESON_INSTALL_DESTDIR_PREFIX}/$1"
install -dv "$BINDIR"
install -v "$RESDIR"/miracle-gst "$BINDIR"
install -v "$RESDIR"/gstplayer "$BINDIR"
install -v "$RESDIR"/uibc-viewer "$BINDIR"

View file

@ -1,3 +1,39 @@
dbus1 = dependency('dbus-1')
systemd = dependency('systemd')
dbus_system_services_dir = dbus1.get_pkgconfig_variable('system_bus_services_dir')
systemd_system_unit_dir = systemd.get_pkgconfig_variable('systemdsystemunitdir')
conf_data = configuration_data()
dbus_name_prefix = 'org.freedesktop.miracle'
conf_data.set('dbus_name_prefix', dbus_name_prefix)
conf_data.set('exec_path',
join_paths(get_option('prefix'), get_option('bindir'))
)
configure_file(input: 'miracle-wifid.service.in',
output: 'miracle-wifid.service',
configuration: conf_data,
install_dir: systemd_system_unit_dir
)
configure_file(input: 'miracle-dispd.service.in',
output: 'miracle-dispd.service',
configuration: conf_data,
install_dir: systemd_system_unit_dir
)
# create symlinks for DBus activiated services
meson.add_install_script('create-symlinks')
configure_file(input: 'org.freedesktop.miracle.wifi.service.in',
output: 'org.freedesktop.miracle.wifi.service',
configuration: conf_data,
install_dir: dbus_system_services_dir
)
configure_file(input: 'org.freedesktop.miracle.wfd.service.in',
output: 'org.freedesktop.miracle.wfd.service',
configuration: conf_data,
install_dir: dbus_system_services_dir
)
install_data(
'org.freedesktop.miracle.conf',
install_dir: join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d')
@ -11,3 +47,33 @@ install_data(
'miracle-wifid', 'miracle-sinkctl', 'miracle-wifictl',
install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions')
)
gio2 = dependency('gio-2.0')
gdk3 = dependency('gdk-3.0')
gst1 = dependency('gstreamer-1.0')
gst1_base = dependency('gstreamer-base-1.0')
add_languages('vala')
valac = meson.get_compiler('vala')
valac_extra_args = []
if valac.version().version_compare('>=0.34') and gdk3.version().version_compare('>=3.22')
valac_extra_args += ['-D', 'GDK3_HAS_MONITOR_CLASS']
endif
executable('gstencoder', 'gstencoder.vala',
dependencies: [gst1, gst1_base, gio2],
install: true,
vala_args: ['--pkg=posix'])
miracle_dispctl_src = ['dispctl.vala',
'sigint.vapi',
'sigint.c',
'networkmanager.vala',
'miracle-wifi.vala',
'miracle-wfd.vala'
]
executable('miracle-dispctl', miracle_dispctl_src,
dependencies: [gio2, gdk3],
vala_args: valac_extra_args,
install: true
)

View file

@ -0,0 +1,18 @@
[Unit]
Description=Miraclecast WiFi Display Service
After=dbus.service
Requires=miracle-wifid.service
After=miracle-wifid.service
[Service]
BusName=org.freedesktop.miracle.wfd
Environment=LOG_LEVEL=trace
ExecStart=@CMAKE_INSTALL_PREFIX@/bin/miracle-dispd
CapabilityBoundingSet=CAP_NET_BIND_SERVICE \
CAP_SETGID \
CAP_SETUID \
CAP_SETPCAP
[Install]
WantedBy=multi-user.target
Alias=dbus-org.freedesktop.miracle.wfd.service

View file

@ -0,0 +1,18 @@
[Unit]
Description=Miraclecast WiFi Display Service
After=dbus.service
Requires=miracle-wifid.service
After=miracle-wifid.service
[Service]
BusName=@dbus_name_prefix@.wfd
Environment=LOG_LEVEL=trace
ExecStart=@exec_path@/miracle-dispd
CapabilityBoundingSet=CAP_NET_BIND_SERVICE \
CAP_SETGID \
CAP_SETUID \
CAP_SETPCAP
[Install]
WantedBy=multi-user.target
Alias=dbus-@dbus_name_prefix@.wfd.service

50
res/miracle-wfd.vala Normal file
View file

@ -0,0 +1,50 @@
/* Generated by vala-dbus-binding-tool 0.4.0. Do not modify! */
/* Generated with: /usr/bin/vala-dbus-binding-tool --gdbus --no-synced --rename-namespace=org:Org --rename-namespace=freedesktop:Freedesktop --rename-namespace=miracle:Miracle --rename-namespace=wifi:Wifi --rename-namespace=wfd:Wfd --api-path=/home/derekdai/Projects/miraclecast/demo/dbus */
using GLib;
namespace Org {
namespace Freedesktop {
namespace Miracle {
namespace Wfd {
[DBus (name = "org.freedesktop.miracle.wfd.Sink")]
public interface Sink : GLib.Object {
[DBus (name = "StartSession", timeout = 120000)]
public abstract GLib.ObjectPath start_session(string param0, string param1, uint param2, uint param3, uint param4, uint param5, string param6) throws DBusError, IOError;
[DBus (name = "Session", timeout = 120000)]
public abstract GLib.ObjectPath session { owned get; }
[DBus (name = "Peer", timeout = 120000)]
public abstract GLib.ObjectPath peer { owned get; }
}
[DBus (name = "org.freedesktop.miracle.wfd.Session")]
public interface Session : GLib.Object {
[DBus (name = "Resume", timeout = 120000)]
public abstract void resume() throws DBusError, IOError;
[DBus (name = "Pause", timeout = 120000)]
public abstract void pause() throws DBusError, IOError;
[DBus (name = "Teardown", timeout = 120000)]
public abstract void teardown() throws DBusError, IOError;
[DBus (name = "Sink", timeout = 120000)]
public abstract GLib.ObjectPath sink { owned get; }
[DBus (name = "Url", timeout = 120000)]
public abstract string url { owned get; }
[DBus (name = "State", timeout = 120000)]
public abstract int state { get; }
}
}
}
}
}

92
res/miracle-wifi.vala Normal file
View file

@ -0,0 +1,92 @@
/* Generated by vala-dbus-binding-tool 0.4.0. Do not modify! */
/* Generated with: /usr/bin/vala-dbus-binding-tool --gdbus --no-synced --rename-namespace=org:Org --rename-namespace=freedesktop:Freedesktop --rename-namespace=miracle:Miracle --rename-namespace=wifi:Wifi --rename-namespace=wfd:Wfd --api-path=/home/derekdai/Projects/miraclecast/demo/dbus */
using GLib;
namespace Org {
namespace Freedesktop {
namespace Miracle {
namespace Wifi {
[DBus (name = "org.freedesktop.miracle.wifi.Peer")]
public interface Peer : GLib.Object {
[DBus (name = "Connect", timeout = 120000)]
public abstract void connect(string param0, string param1) throws DBusError, IOError;
[DBus (name = "Disconnect", timeout = 120000)]
public abstract void disconnect() throws DBusError, IOError;
[DBus (name = "Link", timeout = 120000)]
public abstract GLib.ObjectPath link { owned get; }
[DBus (name = "P2PMac", timeout = 120000)]
public abstract string p2p_mac { owned get; }
[DBus (name = "FriendlyName", timeout = 120000)]
public abstract string friendly_name { owned get; }
[DBus (name = "Connected", timeout = 120000)]
public abstract bool connected { get; }
[DBus (name = "Interface", timeout = 120000)]
public abstract string interface { owned get; }
[DBus (name = "LocalAddress", timeout = 120000)]
public abstract string local_address { owned get; }
[DBus (name = "RemoteAddress", timeout = 120000)]
public abstract string remote_address { owned get; }
[DBus (name = "WfdSubelements", timeout = 120000)]
public abstract string wfd_subelements { owned get; }
[DBus (name = "ProvisionDiscovery")]
public signal void provision_discovery(string param0, string param1);
[DBus (name = "GoNegRequest")]
public signal void go_neg_request(string param0, string param1);
[DBus (name = "FormationFailure")]
public signal void formation_failure(string param0);
}
[DBus (name = "org.freedesktop.miracle.wifi.Link")]
public interface Link : GLib.Object {
[DBus (name = "Manage", timeout = 120000)]
public abstract void manage() throws DBusError, IOError;
[DBus (name = "Unmanage", timeout = 120000)]
public abstract void unmanage() throws DBusError, IOError;
[DBus (name = "InterfaceIndex", timeout = 120000)]
public abstract uint interface_index { get; }
[DBus (name = "MACAddress", timeout = 120000)]
public abstract string m_a_c_address { owned get; }
[DBus (name = "InterfaceName", timeout = 120000)]
public abstract string interface_name { owned get; }
[DBus (name = "FriendlyName", timeout = 120000)]
public abstract string friendly_name { owned get; set; }
[DBus (name = "Managed", timeout = 120000)]
public abstract bool managed { get; }
[DBus (name = "P2PState", timeout = 120000)]
public abstract int p2p_state { get; }
[DBus (name = "P2PScanning", timeout = 120000)]
public abstract bool p2p_scanning { get; set; }
[DBus (name = "WfdSubelements", timeout = 120000)]
public abstract string wfd_subelements { owned get; set; }
}
}
}
}
}

View file

@ -0,0 +1,19 @@
[Unit]
Description=Miraclecast WiFi Daemon
After=dbus.service
Requires=network.target
[Service]
BusName=org.freedesktop.miracle.wifi
Environment=PATH=/sbin:/usr/bin
ExecStart=@CMAKE_INSTALL_PREFIX@/bin/miracle-wifid \
--use-dev \
--log-level trace \
--lazy-managed
CapabilityBoundingSet=CAP_NET_ADMIN \
CAP_NET_BIND_SERVICE \
CAP_NET_RAW
[Install]
WantedBy=multi-user.target
Alias=dbus-org.freedesktop.miracle.wifi.service

View file

@ -0,0 +1,19 @@
[Unit]
Description=Miraclecast WiFi Daemon
After=dbus.service
Requires=network.target
[Service]
BusName=@dbus_name_prefix@.wifi
Environment=PATH=/sbin:/usr/bin
ExecStart=@exec_path@/miracle-wifid \
--use-dev \
--log-level trace \
--lazy-managed
CapabilityBoundingSet=CAP_NET_ADMIN \
CAP_NET_BIND_SERVICE \
CAP_NET_RAW
[Install]
WantedBy=multi-user.target
Alias=dbus-@dbus_name_prefix@.wifi.service

114
res/networkmanager.vala Normal file
View file

@ -0,0 +1,114 @@
/* Generated by vala-dbus-binding-tool 0.4.0. Do not modify! */
/* Generated with: /usr/bin/vala-dbus-binding-tool --gdbus --no-synced --rename-namespace=org:Org --rename-namespace=freedesktop:Freedesktop --rename-namespace=miracle:Miracle --rename-namespace=wifi:Wifi --rename-namespace=wfd:Wfd --api-path=/home/derekdai/Projects/miraclecast/demo/dbus */
using GLib;
namespace Org {
namespace Freedesktop {
namespace NetworkManager {
[DBus (name = "org.freedesktop.NetworkManager.Device")]
public interface Device : GLib.Object {
[DBus (name = "Reapply", timeout = 120000)]
public abstract void reapply(GLib.HashTable<string, GLib.HashTable<string, GLib.Variant>> connection, uint64 version_id, uint flags) throws DBusError, IOError;
[DBus (name = "GetAppliedConnection", timeout = 120000)]
public abstract void get_applied_connection(uint flags, out GLib.HashTable<string, GLib.HashTable<string, GLib.Variant>> connection, out uint64 version_id) throws DBusError, IOError;
[DBus (name = "Disconnect", timeout = 120000)]
public abstract void disconnect() throws DBusError, IOError;
[DBus (name = "Delete", timeout = 120000)]
public abstract void delete() throws DBusError, IOError;
[DBus (name = "StateChanged", timeout = 120000)]
public signal void state_changed(uint new_state, uint old_state, uint reason);
[DBus (name = "Udi", timeout = 120000)]
public abstract string udi { owned get; }
[DBus (name = "Interface", timeout = 120000)]
public abstract string interface { owned get; }
[DBus (name = "IpInterface", timeout = 120000)]
public abstract string ip_interface { owned get; }
[DBus (name = "Driver", timeout = 120000)]
public abstract string driver { owned get; }
[DBus (name = "DriverVersion", timeout = 120000)]
public abstract string driver_version { owned get; }
[DBus (name = "FirmwareVersion", timeout = 120000)]
public abstract string firmware_version { owned get; }
[DBus (name = "Capabilities", timeout = 120000)]
public abstract uint capabilities { get; }
[DBus (name = "Ip4Address", timeout = 120000)]
public abstract uint ip4_address { get; }
[DBus (name = "State", timeout = 120000)]
public abstract uint state { get; }
[DBus (name = "StateReason", timeout = 120000)]
public abstract DeviceStateReasonStruct state_reason { owned get; }
[DBus (name = "ActiveConnection", timeout = 120000)]
public abstract GLib.ObjectPath active_connection { owned get; }
[DBus (name = "Ip4Config", timeout = 120000)]
public abstract GLib.ObjectPath ip4_config { owned get; }
[DBus (name = "Dhcp4Config", timeout = 120000)]
public abstract GLib.ObjectPath dhcp4_config { owned get; }
[DBus (name = "Ip6Config", timeout = 120000)]
public abstract GLib.ObjectPath ip6_config { owned get; }
[DBus (name = "Dhcp6Config", timeout = 120000)]
public abstract GLib.ObjectPath dhcp6_config { owned get; }
[DBus (name = "Managed", timeout = 120000)]
public abstract bool managed { get; set; }
[DBus (name = "Autoconnect", timeout = 120000)]
public abstract bool autoconnect { get; set; }
[DBus (name = "FirmwareMissing", timeout = 120000)]
public abstract bool firmware_missing { get; }
[DBus (name = "NmPluginMissing", timeout = 120000)]
public abstract bool nm_plugin_missing { get; }
[DBus (name = "DeviceType", timeout = 120000)]
public abstract uint device_type { get; }
[DBus (name = "AvailableConnections", timeout = 120000)]
public abstract GLib.ObjectPath[] available_connections { owned get; }
[DBus (name = "PhysicalPortId", timeout = 120000)]
public abstract string physical_port_id { owned get; }
[DBus (name = "Mtu", timeout = 120000)]
public abstract uint mtu { get; }
[DBus (name = "Metered", timeout = 120000)]
public abstract uint metered { get; }
[DBus (name = "LldpNeighbors", timeout = 120000)]
public abstract GLib.HashTable<string, GLib.Variant>[] lldp_neighbors { owned get; }
[DBus (name = "Real", timeout = 120000)]
public abstract bool real { get; }
public struct DeviceStateReasonStruct {
public uint attr1;
public uint attr2;
}
}
}
}
}

View file

@ -1,6 +1,6 @@
<?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!--
This file is part of MiracleCast.
@ -8,50 +8,26 @@
<busconfig>
<policy user="root">
<allow own="org.freedesktop.miracle"/>
<allow own="org.freedesktop.miracle.wifi"/>
<allow send_destination="org.freedesktop.miracle"/>
<allow send_destination="org.freedesktop.miracle.wifi"/>
<allow receive_sender="org.freedesktop.miracle"/>
<allow receive_sender="org.freedesktop.miracle.wifi"/>
</policy>
<policy user="root">
<allow own="org.freedesktop.miracle"/>
<allow own="org.freedesktop.miracle.wifi"/>
<allow own="org.freedesktop.miracle.wfd"/>
<allow send_destination="org.freedesktop.miracle"/>
<allow send_destination="org.freedesktop.miracle.wifi"/>
<allow send_destination="org.freedesktop.miracle.wfd"/>
<allow receive_sender="org.freedesktop.miracle"/>
<allow receive_sender="org.freedesktop.miracle.wifi"/>
<allow receive_sender="org.freedesktop.miracle.wfd"/>
</policy>
<policy context="default">
<deny send_destination="org.freedesktop.miracle"/>
<deny send_destination="org.freedesktop.miracle.wifi"/>
<policy context="default">
<allow send_destination="org.freedesktop.miracle"/>
<allow send_destination="org.freedesktop.miracle.wifi"/>
<allow send_destination="org.freedesktop.miracle.wfd"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.Introspectable"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.Peer"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.Peer"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.Properties"
send_member="Get"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.Properties"
send_member="Get"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
<allow receive_sender="org.freedesktop.miracle"/>
<allow receive_sender="org.freedesktop.miracle.wifi"/>
</policy>
<allow receive_sender="org.freedesktop.miracle"/>
<allow receive_sender="org.freedesktop.miracle.wifi"/>
<allow receive_sender="org.freedesktop.miracle.wfd"/>
</policy>
</busconfig>

View file

@ -0,0 +1,5 @@
[D-BUS Service]
Name=org.freedesktop.miracle.wfd
Exec=/bin/sh -c 'LOG_LEVEL=trace @CMAKE_INSTALL_PREFIX@/bin/miracle-dispd'
User=root
SystemdService=dbus-org.freedesktop.miracle.wfd.service

View file

@ -0,0 +1,5 @@
[D-BUS Service]
Name=@dbus_name_prefix@.wfd
Exec=/bin/sh -c 'LOG_LEVEL=trace @exec_path@/miracle-dispd'
User=root
SystemdService=dbus-@dbus_name_prefix@.wfd.service

View file

@ -0,0 +1,5 @@
[D-BUS Service]
Name=org.freedesktop.miracle.wifi
Exec=/bin/sh -c 'PATH=/sbin:/usr/bin @CMAKE_INSTALL_PREFIX@/bin/miracle-wifid --use-dev --log-level trace'
User=root
SystemdService=dbus-org.freedesktop.miracle.wifi.service

View file

@ -0,0 +1,5 @@
[D-BUS Service]
Name=@dbus_name_prefix@.wifi
Exec=/bin/sh -c 'PATH=/sbin:/usr/bin @exec_path@/miracle-wifid --use-dev --log-level trace'
User=root
SystemdService=dbus-@dbus_name_prefix@.wifi.service

64
res/sigint.c Normal file
View file

@ -0,0 +1,64 @@
/*
* 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 <sys/signalfd.h>
#include "sigint.h"
typedef struct _SigintDelegate SigintDelegate;
struct _SigintDelegate
{
SigintHandler handler;
gpointer user_data;
};
static gboolean sigint_on_signal(GIOChannel *c, GIOCondition e, gpointer d)
{
struct signalfd_siginfo siginfo;
g_io_channel_read_chars(c, (gchar *) &siginfo, sizeof(siginfo), NULL, NULL);
SigintDelegate *delegate = d;
(*delegate->handler)(delegate->user_data);
return FALSE;
}
void sigint_add_watch(SigintHandler handler, gpointer user_data)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_BLOCK, &mask, NULL);
SigintDelegate *d = g_malloc(sizeof(SigintDelegate));
*d = (SigintDelegate) {
handler = handler,
user_data = user_data
};
int fd = signalfd(-1, &mask, SFD_CLOEXEC);
GIOChannel *c = g_io_channel_unix_new(fd);
g_io_channel_set_encoding(c, NULL, NULL);
g_io_add_watch_full(c,
G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_ERR | G_IO_HUP,
sigint_on_signal,
d,
g_free);
g_io_channel_unref(c);
}

29
res/sigint.h Normal file
View file

@ -0,0 +1,29 @@
/*
* 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 <glib.h>
#ifndef DISP_SIGFD_H
#define DISP_SIGFD_H
typedef void (*SigintHandler)(gpointer user_data);
void sigint_add_watch(SigintHandler handler, gpointer user_data);
#endif /* DISP_SIGFD_H */

8
res/sigint.vapi Normal file
View file

@ -0,0 +1,8 @@
[CCode(cheader_filename="sigint.h")]
class Sigint
{
public delegate void Handler();
public static void add_watch(Handler handler);
public static void remove_watch();
}