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:
parent
439dac09c5
commit
fba5eb4a11
72 changed files with 9577 additions and 399 deletions
|
@ -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
6
res/create-symlinks
Executable 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
824
res/dispctl.vala
Normal 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
426
res/gstencoder.vala
Normal 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;
|
||||
}
|
|
@ -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
8
res/install-scripts
Executable 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"
|
|
@ -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
|
||||
)
|
||||
|
|
18
res/miracle-dispd.service.cmake
Normal file
18
res/miracle-dispd.service.cmake
Normal 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
|
18
res/miracle-dispd.service.in
Normal file
18
res/miracle-dispd.service.in
Normal 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
50
res/miracle-wfd.vala
Normal 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
92
res/miracle-wifi.vala
Normal 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
res/miracle-wifid.service.cmake
Normal file
19
res/miracle-wifid.service.cmake
Normal 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
|
19
res/miracle-wifid.service.in
Normal file
19
res/miracle-wifid.service.in
Normal 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
114
res/networkmanager.vala
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
5
res/org.freedesktop.miracle.wfd.service.cmake
Normal file
5
res/org.freedesktop.miracle.wfd.service.cmake
Normal 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
|
5
res/org.freedesktop.miracle.wfd.service.in
Normal file
5
res/org.freedesktop.miracle.wfd.service.in
Normal 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
|
5
res/org.freedesktop.miracle.wifi.service.cmake
Normal file
5
res/org.freedesktop.miracle.wifi.service.cmake
Normal 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
|
5
res/org.freedesktop.miracle.wifi.service.in
Normal file
5
res/org.freedesktop.miracle.wifi.service.in
Normal 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
64
res/sigint.c
Normal 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
29
res/sigint.h
Normal 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
8
res/sigint.vapi
Normal 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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue