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

source-impl: Squashed derekdai fork

fix byte order of DHCP_SERVER_ID option

If miraclecat is GO, miracle-dhcp runs as server, it send out SERVER_ID
option in little endian, causes non-GO peer connect to wrong IP address, so
RTSP connection can't be established.

In scenario like, say Android (GO) <-> miracalcast (non-GO), the DHCP service
is provided by Android, in this case, the SERVER_ID is in expected byte order
(big endian).

set audio=False by default

since gstplayer has argument --audio to enable audio support but no
way to disable it, audio=False might a better choise.

If we left audio=True and incoming stream has video only, the whole
pipeline will be blocked and wait for audio stream.

init copy & modify

calculate array size of resolutions at build time instead of tailing null element

match resolution from high to low

init source side implementation

add option [-f|--full-screen] to switch to full screen mode at start up

initial sender implementation

integrate miracle-sender with miracast-srcctl

stop sender when peer freed

source implement + sender talk via dbus

miracle-wifid: fix some leaks of wpas_message

stability: increase gcc warning level

miracle-sender: change bus name from org.freedesktop.miracle.Sender to org.freedesktop.miracle

gstplayer: set sync=false to reduce latency

miracle-wifid: fix call waitid() with NULL pointer issue reported by valgrind

rename dbus name back to org.freedesktop.miracle.Sender and variety kinds of tunning

encode video with x264enc temperarily to prevent decoding issue on dongle

stop sender properly after session end

miracle-srcctl: fix memory management issues

Make miracle-wifid conexists with other network tools

The new option --lazy-managed will let miracle-wifid don't managed the
links automatically. Instead, the link will be managed only when the new
DBus property Managed was set to true. So this will be possible that
miracle-wifid could be conexists with other network tools like
networkmanager.

For example, unmange the device in networkmanager with setting the DBus
property org.freedesktop.NetworkManager.Device.Managed to false and
manage it in miracle-wifid with setting
org.freedesktop.miracle.wifi.Link.Managed to true, then both them could
works and don't need to kill each other.

Besides, there is new command named make-managed in miracle-wifictl and
miracle-sinkctla.

miracle-wifid: add dbus & systemd service files to support dbus activation

miracle-wifid: remove duplicated link & peer dbus nodes by checking prefix

miracle-srcctl: fix some warming

miracle-wifid: add MACAddress property to link

miracle-wfdctl: init commit

miracle-*ctl: remove some debugging log messages

miracle-*ctl: resolve object parsing issue in ctl_wifi_fetch()

while parsing ctl_peer and ctl_link objects, if a ctl_peer appear prior
to its related ctl_link, -EINVAL is reported, and miracle-*ctl will quit
immediately.

miracle-*ctl: fix label leak

miracle-wifid: let the format of wfd_dev_info compliant with wfd_subelems to ease parsing

miracle-*ctl: remove N_ELEMENTS() and use SHL_ARRAY_LENGTH() instead

miracle-*ctl: rename resolution_bitmap to wfd_resolution and add progressive field

remove vim settings from files

miracle-srcctl: rename ctl_src to wfd_src

miracle-wfdctl: first revision which can create TCP connection of WFD session between peers, controled through DBus

In this revision, the procedure of session negotiation is not yet
completed but only TCP connection can be established between peers.

You can try it by run script `res/miracle-wfdctl-demo` to see how it
work.

miracle-wfdctl-demo: wait for property chainging and object apperance by
busctl instead of sleep

miracle-*ctl: update links and peers when wifid up or down

miracle-wfdctl-demo: cleanup before exit

ignore .vimrc from being managed by git

miracle-wfdctl: now we can establish WFD session multiple times

in this revision, the source side RTSP protocol is not implemented
completely, the focus is on stability, DBus interfaces.

add debian support prepared by Guo Shuang

miracle-wfdctl-demo: add an optional argument to specify link index of
NetworkManager

miracle-wfdctl: refactoring to remove repeated snippets and centralize the
dispatching of request and reply

add -fstack-protector-strong to capture more stack issues

add miraclecast.links to create symlink for DBus activated systemd
services

miracle-wfdctl: return -EINVAL instead of being aborted by assert() when
rtsp message ID is invalid

miracle-*ctl: add *_from_string() and *_to_string() for wfd-video-formats and
wfd-audio-codecs

miracle-wfdctl: finish migration of source side rtsp protocol

remove unused files

fix links of services for debian package

move down trap command to prevent triggered by runing with no argument

remove unused #include

miracle-wfdctl: add timeout value and Range header to the reply of PLAY request

test compiler flag -fstack-protector-strong before set

miracle-wfdctl: run gst-launch to generate video stream with hardcoded
environment

miracle-wfdctl: fix return uninitialized variable value issue

add struct wfd_arg to generalize passing of arguments

miracle-*ctl: fix and enhance wfd_subelement parser

miracle-wfdctl: remove unused function and improve log message for
peer new & delete

rename macro arguement from v to _v to prevent name confliction

add support for signed and unsigned int for wfd_arg

refactoring getter/setter for dict type of wfd_arg to remove declaration
of temp variable

add wfd_arg_list type of wfd_arg

miracle_wfdctl: control flow and passing arguments with wfd_arg instead
of keep adding new structure fields

miracle-wfdctl: print friendly name when peer shows up

miracle-wfdctl: fix mapping from rtsp_message to rtsp_message_id

miracle-wfctl: reply 'request not implemented' instead of abort by assert()

check gcc version to ensure c11 generic select feature

update dependencies for debian based distro

miracle-wfdctl: acquire DISPLAY, XAUTHORITY and XDG_RUNTIME_DIR from
environment, instead of hardcode

miracle-wfdctl: fix wfd_session_end() recursive calling issue

miracle-wfdctl: add handler for PAUSE, PLAY and TEARDOWN methods

miracle-wfdctl: clarify teardown procedure of session

miracle-wfdctl: delay for 100ms before start sending stream to sink

miracle-wfdctl: compare wfd-standby and wfd-idr-request by message body

miracle-wfdctl: user now can select video mode through DBus interface

miracle-wfdctl: fix typo in systemd service file

miracle-wfdctl: change session type from uint64_t to unsigned int

miracle-wfdctl: do not launch gstreamer if DO_NOT_LAUNCH_GST environment
variable is set while miracle-wfdctl running

miracle-wfdctl: remove unused Range header from reply

add compiler flag to do sanity check of undefined behaviours.

relax permissions checking to let non-privileged use miracalcast.

we will enhance this by fine grained permission conrol latter.

since we relaxed the permissions to talk to wifid and wfdctl, so there is
no need to run miracle-wfdctl-demo with sudo. but to kill these two
privileged services, the killall still need sudo

miracle-wfdctl: integrate sd_event with GMainLoop for running gstreamer
in process instead of fork n exec gst-launch

miracle-wfdctl: encoding video stream with gstreamer library instead of
gst-launch-1.0

miracle-wfdctl-demo: run tcpdump with sudo

miracle-wifid: change managed property of link by method call instead of
set property due to wpa_supplicant needs time to get ready

miracle-wfdctl: include header <gst/gst.h> for resolving compiler warning

miracle-wfdctl: add rtcp and audio supporting to gstreamer pipeline

miracle-wfdctl: fix session removal dbus notification

miracle-wfdctl: fix variety kinds of dbus interface issues

miracle-wfdctl: fix sink removal notifcation

fix typo in miracle-wifid.service

miracle-wfdctl: set XAUTHORITY and DISPLAY before create gstreamer
pipeline, and now we can pass device name to pulseaudio

miraclw-wfdctl: fix typo of dbus signal names

miraclw-wfdctl: now you can cast only specified xid or xname to sink

miracle-wfdctl: fine tune gstreamer pipeline

miracle-wfdctl: fix argument passing for ximagesrc

miracle-wfdctl: send video only stream if no audio device specified

update dependencies of systemd services

miracle-wfdctl: reply rtsp request with date header in locale
en_US.UTF-8

miracle-wrdctl-demo: capture packets on p2p wnic with tshark instead of
tcpdump

miracle-wfdctl: set ximagesrc as live mode to prvent it preroll in pause
state

miracle-wfdctl: fine tune codec to compliant with WFD spec + H264
profile/level

miracle-wfdctl: disable audio encoding temparary

miracle-wfdctl: save captured packets to .pcap file instead of print out
on console

update dependency information

miracle-wfdctl: remove extra NULL parameter

miracle-wfdctl: do not expose rtsp_message_id_to_string() to outside
world

miracle-wfdctl: handle gst messages and reflect state onto session

miracle-wfdctl: encode audio with voaacenc instead of avenc_aac to get
closer to WFD spec

rename miracle-wfdctl to miracle-dispd

change argument types of wfd_sink_start_session()

To workaround valac issue

miracle-wfdctl: fix pointer check issue

add a demostration for wifid + dispd

reorganize demo, make it looks like running in sequential instead of trigger by async events

miracle-dispd: fix monitor geometry calculation issue

remove miracle-wfdctl-demo, replaced by demo/miracle-wfdctl

demo/miracle-wfdctl: fix some build issues for GDK version older then
3.22

update dependencies in debian/ for demo/

update cmake minimum requirement from 2.8 to 3.0.2 due to usage of version comparasion of if command

check valac version to decide whether or not to use new Gdk.Monitor class

add install() for demo/miracle-wfdctl

add mission dependencies to debian/control

miracle-dispd: merge patch from Meelis

acquire ownership through Link.Manage()/Link.Unmanage() instead of set Link.Managed property

demo/miracle-wfdctl: better handling session tearing down

move definitions related to miracle-dispd from src/ctl to src/disp

demo/miracle-wfdctl: handle SIGINT to exit gracefully

miracle-dispd: relax checking for peer with empty wfd_subelements

miracle-dispd: fix symlink for dbus-org.freedesktop.miracle.wfd.service
which is an alias of miracle-dispd.service

demo/miracle-wfdctl: log message tweaks

miracle-dispd: add watchdog and SIGKILL for it

to prevent no reponse and unable to be terminated issue. this is a temp solution

miracle-wifid: fix signal emittion of FormationFailure

miracle-wfdctl: update description of DBus interfaces

miracle-dispd: notify systemd about the service readiness

miracle-wifid: postpone the emittion of managed signal after the status
query

demo/miracle-wfdctl: maintain DBus interfaces by .vala files instead of generate from .xml which have only sync methods

miracle-dispd: use software codec instead of vaapi accelerated codec

miracle-dispd: add error report log to handle_replay()

miracle-dispd, demo/miracle-wfdctl: improve error handling and reporting

miracle-dispd: fix output resolution to 1080p@30

miracle-wifid: add Link.P2PState for checking P2P supporting status

demo/miracle-wfdctl: Wait for P2PState changing before do P2P scanning

add missing build-time dependencies

fix loop boundary checking

miracle-dispd: fix some uninitialized variable accessing issues

demo/miracle-wfdctl: remove unused files

add meson build supporting

remove install script and install scripts with install_data()

handle meson optoins properly

quote string entries with configuration_data.set_quoted() instead of by ourself with configureation_data.set()

remove starting `/` in front of installation path of DBus policy file

demo/miracle-wfdctl: rollback method definitions in dbus interfaces from async to sync

demo/miracle-wfdctl: teardown session more gracefully

miracle-dispd: stablizing and removing memory leak

miracle-dispd: put back gstreamer

in this version, the pipeline can be stopped properly, but the gst &
glib allocated memory can't be release cleanly (which won't
accumulate)

Change-Id: I242b106158db647070e7852b84f6f90b6bbd96f4

miracle-dispd: remove some compiler warmings

Change-Id: Ief2ae8e2992c2fee34fe560af73deb955a9186de

demo/miracle-wfdctl: log more info about DBus operation

Change-Id: Ic44a6a2ca4585e6aa1983885b4730ccee6fd3d33

remove address sanitizer flag from default options

let user or packaging system to decide whether or not to use it

Change-Id: I27e7f7eae3f79431a1ec7dc69d5f372677dadbad

miracle-wifid: rename link_set_managed()

rename to prevent confusing with managed event notification

Change-Id: I0f0d01f97156044bbb225e199b2717d5ea9c1a24

miracle-wifid: remove unused function

Change-Id: Ic2709a3661914fd9e0708c33649118feededa7f6

miracle-wifid: cleanup change notification of Link.Managed property

Change-Id: Ia5b056151e07219f0e6cc34dbc9fc2149f929a36

miracle-wifid: minor tweaks of log messages and identation

Change-Id: Id9e98583f3256c83cf48f18cf08740849ded3cca

miracle-wifid: fix crashing on link removing issue

wifid crashes when link removing (either unplug usb wlan dongle or
unload kernel module) due to double

Change-Id: I9480a095659752343e505eb41b380d1ab3cd322a

miracle-wifid: fine tune P2P state notification timing

Change-Id: Idb111b90109010733b96d653c2d6d7855ebc781f

miracle-disp: extracting encoder, part1

Change-Id: Ic9293e9cf379d352c75701834f17fa553f0bf52d

miracle-dispd: refactory session startup function

Change-Id: If1940a5823171d35230d29051d79c9827efc054f

use meson default build type instead of debugoptimized

Change-Id: I39bdc3e9df61c681b7ec5800a9e57fc7e9971cb0

migrate more gstreamer code to res/gstencoder

Change-Id: Id5098e37fb1eabb2906505f26969e6d8cedad519

miracle-dispd: add struct dispd_encoder as encoder client

since wait for the availability of newly spawned process and communicate
with it is a highly async task, wrapping in a isolated class is a better
choise.

Change-Id: I7201da49f379c65014123269ed9e0279bcb8c918

miracle-dispd: cleanup and reorganize header

Change-Id: I0067d59d89f79af53ae7291894191edb33789ab2

miracle-dispd: rename field from ref_count to ref

Change-Id: Ia8138c0609b0100dd2784734287a4b3964beacbd

miracle-dispd: unify log message

Change-Id: I17c7cb0c64ddaecd09ff59f65dcb0e745f6b7174

miracle-dispd: log if a function call returns error

it is easier to trace back to the location where the error occured
by logging the return code instead of simply return a error number

Change-Id: Ic711af32a1e80d9da9317269521a4745099e30cc

miracle-dispd: extract encoder from dispd, part 2

Change-Id: Ie61daa87a83b875013c525fe3f15222bf9057926

miracle-dispd: complete cmake build for extracted encoder

Change-Id: I5635bffdb8e1a0a693893e22837de6a8a0071fbe

miracle-dispd: improve encoder lifecycle management

Change-Id: Ic651e4795e4c9579978fe3e3529d8d61e739ffd1

miracle-dispd: replace assert() with assert_?ret?()

for capturing more information about buggy behaviours, more
    validations and loggings are added

Change-Id: I57e11d201324ed10b2218c9e72627759aae619c9

remove valgrind checking since now it is optional

Change-Id: I39dc6b3e23307fd039775983b6d68878547523ba

miracle-wifid: fix the timing of notification of P2P availability

Change-Id: I86c453256b614022db5d941d64984f9eecd4ef39

miracle-dispd: fix definition of wfd_session_is_established()

Change-Id: I61638bcb4e17059b5b9e37252894ab35d524d7cf

gstencoder: exit if pipeline encounter error, EOS or enter NULL state

Change-Id: Id9934d144bf023b301d92b24cc1cafcf2bac3b8e

miracle-dispd: add timer to ensure the termination of encoder process

Change-Id: I282136c79392137f2501f82009154087ee9649be

gstencoder: add license declaration

Change-Id: Ia2d7458d028e7d0406c3eb2489211c5daa86149d

gstencoder: print final pipeline description to ease debugging

Change-Id: Ib5d9bfea0760f401033b7e8982a14ae23f70d601

miracle-dispd: do not link with gstreamer libraries

Change-Id: Ie178235579429647de6e7e4cf738ce814e835424

miracle-dispd: minor value returning style tunning

Change-Id: I608154ffcbe0f25b4bfa9f78d0f457afe1e4fb89

miracle-dispd: more logging and assertions

Change-Id: I0450b9144390b12eed0646c4a4eacf6beed6ae91

miracle-dispd: improve session life cycle management

Change-Id: Icb43664439002f76d7f64252f4ba79533d04ba43

miracle-dispd: run encoder with user privilege

now we run encoder with unprivileged user and talk through session bus

Change-Id: I09b3b8b15e5a7c7e9b883b7c9dbac601c13f458c

gstencoder: fix untested execution path which has pipeline syntax issue

Change-Id: I1d170666852c33e96c989468fc733d065f777bd4

miracle-wfdctl: add options -b and -r

add options -b and -r to control whether or not to acquiring or
releasing ownershipt of WNIC

Change-Id: Iaa9fdac15a7d1e23053b57500b978e0c026fa50c

miracle-dispd: fix session leaking issue

Change-Id: I1ececf598fedf601edfd7c7208ffce72a750f8cd

miracle-dispd: terminate with SIGTERM instead of SIGKILL

Change-Id: I3b46d3d50fc11c8f3487cc241947b556196e8ba6

miracle-dispd: ignore peers which has no valid wfd_supplicant

Change-Id: Ifd7225a944fb0312eb3fe0c575b82a9c95c6ab14

miracle-dispd: minor log message tweaks

Change-Id: I75cf47bb2640270729d1643487ae5cc7f8617446

fix creation paths of symlinks of DBus activated systemd services

prepend $ENV{DESTDIR} to the path which is abs path get from systemd.pc

Change-Id: Id07801a1f513fb421f653cb4b2c943d8841a74f6

demo/miracle-wfdctl: broadcast as source only device

Change-Id: Id159332fc644e09990abdebffa8fa4b9f8b2fc28

reload systemd units after installation

Change-Id: I98cf764f5dbbe5fe06a136e3633a3304e8e4401e

miracle-dispd: add hardware accelection video encoding back to gstencoder

Change-Id: I577082a70da61b6266e67c0b8611662ae9bc316c

build vala code with cmake modules instead of custom_command()

Change-Id: Ib0bc561441c3664c00e73e66864597343f3f75e8

remove 'org.freedesktop.' prefix from dbus interface source files

Change-Id: Ice1ef49ca1ad3b8f36c8b4a5f8f35769df262eb9

move miracle-wfdctl from demo/ to res/

Change-Id: I37df75135e4713a73d3c63353c9a17dc248e2d7e

gstencoder: fallback to sw encoder if vaapi encoder not available

Change-Id: I8329fc191d60188c2098434d5db8a4aa2b3fbec2

miracle-dispd: minor interface tweaks betwen wfd_session and wfd_out_session

Change-Id: Ib0c65ab64d543d5fce8908f6c47dbfc2acf47763

miracle-dispd: add keep-alive messages

Change-Id: Ib8fc055789a1bd0a6df3075b176385407c17a74c

miracle-dispd: remove unused log messages

Change-Id: I7460f6173815f054c0dc51accf96a531f6faf4b1

miracle-dispd: minor tweaks of object life cycle transfering

Change-Id: Iafefe39652d9ade06c58e41f091a30df274cb091

update gstreamer1.0-vaapi version we depend on

Change-Id: Iacf53af83c151c6746f778a97201ed3f7bd55504

miracle-wifid: reduce caps from .service file

Change-Id: I8434fe9dfcf141156635d7a6f8a9c2aaa4469263

miracle-dispd: reduce caps from .service file

Change-Id: Ibfc14aca1c03b8cd6b50a49d5128da8597acb898

miracle-dispd: set no new priviledge flag before exec encoder

Change-Id: I1b7f18a704203fc60a35b67f09936095ae6398f0

miracle-dispd: rename files and class names to make naming consistent

Change-Id: Id2f5a8d6bbfbbd205dbe25e8b8bc0ddd289591e5

remove tests for security related compiler flags

Change-Id: Ia51167abc10c4c8bc03f58883aca06e05879c6e9

miracle-dispctl: fine tune ctrl+c handling

Change-Id: Ic5752cea7b64307c3c2d8dd0a8389d3b18ab3339

miracle-wfdctl: enhance handling of user termination (ctrl+c)

Change-Id: Ifa2b1ee85b2abf4cc0d9d7f40c452517cb94761d

miracle-wfdctl: fix GIOChannel ref leak

Change-Id: I12e647f9b4f13aaa0aedf81907aa251675d2be32

miracle-dispd: fix session life-cycle managed issue

when a session is destroyed due to peer free event, dispd_out_session must
detach from sink to it won't cause double free.

Change-Id: I71ad7e977fa71ec84f12f5e914786aa24a650f34

miracle-sinkctl: do not quote the value of 'Public' header of OPTION method

Change-Id: I1e3994517f492aa7d9996494110077c81acc41b4

miracle-dispd, gstencoder: enable audio supporting

Change-Id: I4d72782516ffff487010cd94cc47ec68133ff257

relates #4
This commit is contained in:
Derek Dai 2016-09-30 23:04:53 +08:00 committed by Alberto Fanjul
parent 59df9a4a3f
commit 8b455e1ce4
71 changed files with 9451 additions and 330 deletions

2
.gitignore vendored
View file

@ -6,6 +6,7 @@
*.swp
*.tar.xz
*.trs
*.pcap
.deps/
.dirstamp
.libs/
@ -42,3 +43,4 @@ CMakeCache.txt
libmiracle-shared.a
install_manifest.txt
/build/
.vimrc

View file

@ -1,15 +1,12 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.0.2)
project(Miraclecast)
SET(PACKAGE_NAME miraclecast)
SET(PACKAGE_VERSION 1)
SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake
${CMAKE_MODULE_PATH})
set(CMAKE_C_FLAGS "-std=gnu11 ${CMAKE_C_FLAGS}")
add_definitions(-D_GNU_SOURCE)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
SET(BUILD_BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
OPTION(BUILD_ENABLE_DEBUG "Enable Debug" ON )
@ -27,6 +24,19 @@ find_package(PkgConfig)
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
pkg_check_modules (UDEV REQUIRED libudev)
pkg_check_modules (SYSTEMD REQUIRED libsystemd)
pkg_check_modules (GSTREAMER REQUIRED gstreamer-1.0)
pkg_check_modules (GSTREAMER_BASE REQUIRED gstreamer-base-1.0)
set(CMAKE_C_FLAGS "-std=gnu11 -Wall ${CMAKE_C_FLAGS}")
add_definitions(-D_GNU_SOURCE)
if(CMAKE_COMPILER_IS_GNUCC)
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
if(GCC_VERSION VERSION_LESS 4.9)
message(FATAL_ERROR "gcc >= 4.9 is requred")
endif()
endif()
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h)

View file

@ -21,10 +21,20 @@ The MiracleCast projects requires the following software to be installed:
- **glib**: A utility library. Used by the current DHCP implementation. Will be removed once sd-dns gains DHCP-server capabilities.
*required*: ~=glib2-2.38 (might work with older releases, untested..)
- **readline**: A library which is used to provide command line interface to control wifid, sink, etc..
- **check**: Test-suite for C programs. Used for optional tests of the MiracleCast code base.
*optional*: ~=check-0.9.11 (might work with older releases, untested..)
- **gstreamer**: MiracleCast rely on gstreamer to show cast its output. You can test if all needed is installed launching [res/test-viewer.sh](https://github.com/albfan/miraclecast/blob/master/res/test-viewer.sh)
- **gstreamer**: MiracleCast rely on gstreamer to show cast its output. You can test if all needed is installed launching [res/test-viewer.sh](https://github.com/albfan/miraclecast/blob/master/res/test-viewer.sh).
- gstreamer plugins: plugins you need in order to run sinkctl or dispctl
- gstreamer-plugins-base
- gstreamer-plugins-good
- gstreamer-plugins-bad
- gstreamer-plugins-ugly
- gstreamer-plugins-vaapi
- gstreamer-plugins-libav
- **P2P Wi-Fi device** Although widespread these days, there are some devices not compatible with [Wi-Fi Direct](http://en.wikipedia.org/wiki/Wi-Fi_Direct) (prior know as Wi-Fi P2P). Test yours with [res/test-hardware-capabilities.sh](https://github.com/albfan/miraclecast/blob/master/res/test-hardware-capabilities.sh)

65
cmake/FindVala.cmake Normal file
View file

@ -0,0 +1,65 @@
##
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of Jakob Westhoff
##
##
# Find module for the Vala compiler (valac)
#
# This module determines wheter a Vala compiler is installed on the current
# system and where its executable is.
#
# Call the module using "find_package(Vala) from within your CMakeLists.txt.
#
# The following variables will be set after an invocation:
#
# VALA_FOUND Whether the vala compiler has been found or not
# VALA_EXECUTABLE Full path to the valac executable if it has been found
# VALA_VERSION Version number of the available valac
##
# Search for the valac executable in the usual system paths.
find_program(VALA_EXECUTABLE
NAMES valac)
# Handle the QUIETLY and REQUIRED arguments, which may be given to the find call.
# Furthermore set VALA_FOUND to TRUE if Vala has been found (aka.
# VALA_EXECUTABLE is set)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Vala DEFAULT_MSG VALA_EXECUTABLE)
mark_as_advanced(VALA_EXECUTABLE)
# Determine the valac version
if(VALA_FOUND)
execute_process(COMMAND ${VALA_EXECUTABLE} "--version"
OUTPUT_VARIABLE "VALA_VERSION")
string(REPLACE "Vala" "" "VALA_VERSION" ${VALA_VERSION})
string(STRIP ${VALA_VERSION} "VALA_VERSION")
endif(VALA_FOUND)

View file

@ -0,0 +1,36 @@
##
# This is a helper Macro to parse optional arguments in Macros/Functions
# It has been taken from the public CMake wiki.
# See http://www.cmake.org/Wiki/CMakeMacroParseArguments for documentation and
# licensing.
##
macro(parse_arguments prefix arg_names option_names)
set(DEFAULT_ARGS)
foreach(arg_name ${arg_names})
set(${prefix}_${arg_name})
endforeach(arg_name)
foreach(option ${option_names})
set(${prefix}_${option} FALSE)
endforeach(option)
set(current_arg_name DEFAULT_ARGS)
set(current_arg_list)
foreach(arg ${ARGN})
set(larg_names ${arg_names})
list(FIND larg_names "${arg}" is_arg_name)
if(is_arg_name GREATER -1)
set(${prefix}_${current_arg_name} ${current_arg_list})
set(current_arg_name ${arg})
set(current_arg_list)
else(is_arg_name GREATER -1)
set(loption_names ${option_names})
list(FIND loption_names "${arg}" is_option)
if(is_option GREATER -1)
set(${prefix}_${arg} TRUE)
else(is_option GREATER -1)
set(current_arg_list ${current_arg_list} ${arg})
endif(is_option GREATER -1)
endif(is_arg_name GREATER -1)
endforeach(arg)
set(${prefix}_${current_arg_name} ${current_arg_list})
endmacro(parse_arguments)

314
cmake/ValaPrecompile.cmake Normal file
View file

@ -0,0 +1,314 @@
##
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
# Copyright 2012 elementary.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of Jakob Westhoff
##
include(ParseArguments)
find_package(Vala REQUIRED)
##
# Compile vala files to their c equivalents for further processing.
#
# The "vala_precompile" macro takes care of calling the valac executable on the
# given source to produce c files which can then be processed further using
# default cmake functions.
#
# The first parameter provided is a variable, which will be filled with a list
# of c files outputted by the vala compiler. This list can than be used in
# conjunction with functions like "add_executable" or others to create the
# necessary compile rules with CMake.
#
# The initial variable is followed by a list of .vala files to be compiled.
# Please take care to add every vala file belonging to the currently compiled
# project or library as Vala will otherwise not be able to resolve all
# dependencies.
#
# The following sections may be specified afterwards to provide certain options
# to the vala compiler:
#
# LIBRARY
# Indicates that this is to be compiled as a library.
#
# PACKAGES
# A list of vala packages/libraries to be used during the compile cycle. The
# package names are exactly the same, as they would be passed to the valac
# "--pkg=" option.
#
# OPTIONS
# A list of optional options to be passed to the valac executable. This can be
# used to pass "--thread" for example to enable multi-threading support.
#
# CUSTOM_VAPIS
# A list of custom vapi files to be included for compilation. This can be
# useful to include freshly created vala libraries without having to install
# them in the system.
#
# DEPENDS
# Additional files that may change the results of the outputed C files.
#
# GENERATE_VAPI [INTERNAL]
# Pass all the needed flags to the compiler to create a vapi for
# the compiled library. The provided name will be used for this and a
# <provided_name>.vapi file will be created. If INTERNAL is specified,
# an internal vapi <provided_name>_internal.vapi will be created as well.
# This option implies GENERATE_HEADER, so there is not need use GENERATE_HEADER
# in addition to GENERATE_VAPI unless they require different names. Requires
# that LIBRARY is set.
#
# GENERATE_HEADER [INTERNAL]
# Let the compiler generate a header file for the compiled code. There will
# be a header file being generated called <provided_name>.h. If INTERNAL
# is specified, an internal header <provided_name>_internal.h will be created
# as well.
#
# GENERATE_GIR [TYPELIB]
# Have the compiler generate a GObject-Introspection repository file with
# name: <provided_name>.gir. If TYPELIB is specified, the compiler will also
# create a binary typelib using the GI compiler. Requires that LIBRARY is set.
#
# TYPELIB_OPTIONS
# Additional options to pass to the GI compiler. Requires that GENERATE_GIR
# TYPELIB is set.
#
# GENERATE_SYMBOLS
# Output a <provided_name>.symbols file containing all the exported symbols.
#
# The following call is a simple example to the vala_precompile macro showing
# an example to every of the optional sections:
#
# vala_precompile(VALA_C mytargetname
# LIBRARY
# source1.vala
# source2.vala
# source3.vala
# PACKAGES
# gtk+-2.0
# gio-1.0
# posix
# DIRECTORY
# gen
# OPTIONS
# --thread
# CUSTOM_VAPIS
# some_vapi.vapi
# GENERATE_VAPI
# myvapi
# GENERATE_HEADER
# myheader
# GENERATE_GIR TYPELIB
# mygir
# TYPELIB_OPTIONS
# --includedir=some/dir
# GENERATE_SYMBOLS
# mysymbols
# )
#
# Most important is the variable VALA_C which will contain all the generated c
# file names after the call.
##
macro(vala_precompile output target_name)
parse_arguments(ARGS
"TARGET;PACKAGES;OPTIONS;TYPELIB_OPTIONS;DIRECTORY;GENERATE_GIR;GENERATE_SYMBOLS;GENERATE_HEADER;GENERATE_VAPI;CUSTOM_VAPIS;DEPENDS"
"LIBRARY" ${ARGN})
if(ARGS_DIRECTORY)
set(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_DIRECTORY})
else(ARGS_DIRECTORY)
set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif(ARGS_DIRECTORY)
include_directories(${DIRECTORY})
set(vala_pkg_opts "")
foreach(pkg ${ARGS_PACKAGES})
list(APPEND vala_pkg_opts "--pkg=${pkg}")
endforeach(pkg ${ARGS_PACKAGES})
set(in_files "")
set(out_files "")
set(out_files_display "")
set(${output} "")
foreach(src ${ARGS_DEFAULT_ARGS})
# this string(REPLACE ...) is a workaround for a strange behavior when
# the cmake binary directory is a subdirectory of the source directory
# and you include a vala source file from the cmake binary directory.
# For a yet to be determined reason, cmake deletes the generated c file
# before it is compiled, resulting in an error. We fix this by making
# any absolute path that is in the source directory a relative path.
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" src ${src})
string(REGEX MATCH "^/" IS_MATCHED ${src})
if(${IS_MATCHED} MATCHES "/")
set(src_file_path ${src})
else()
set(src_file_path ${CMAKE_CURRENT_SOURCE_DIR}/${src})
endif()
list(APPEND in_files ${src_file_path})
string(REPLACE ".vala" ".c" src ${src})
string(REPLACE ".gs" ".c" src ${src})
if(${IS_MATCHED} MATCHES "/")
get_filename_component(VALA_FILE_NAME ${src} NAME)
set(out_file "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
list(APPEND out_files "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
else()
set(out_file "${DIRECTORY}/${src}")
list(APPEND out_files "${DIRECTORY}/${src}")
endif()
list(APPEND ${output} ${out_file})
list(APPEND out_files_display "${src}")
endforeach(src ${ARGS_DEFAULT_ARGS})
set(custom_vapi_arguments "")
if(ARGS_CUSTOM_VAPIS)
foreach(vapi ${ARGS_CUSTOM_VAPIS})
if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
list(APPEND custom_vapi_arguments ${vapi})
else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi})
endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
endforeach(vapi ${ARGS_CUSTOM_VAPIS})
endif(ARGS_CUSTOM_VAPIS)
set(library_arguments "")
if(ARGS_LIBRARY)
list(APPEND library_arguments "--library=${target_name}")
endif(ARGS_LIBRARY)
set(vapi_arguments "")
if(ARGS_GENERATE_VAPI)
parse_arguments(ARGS_GENERATE_VAPI "" "INTERNAL" ${ARGS_GENERATE_VAPI})
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI_DEFAULT_ARGS}.vapi")
list(APPEND out_files_display "${ARGS_GENERATE_VAPI_DEFAULT_ARGS}.vapi")
list(APPEND vapi_arguments "--vapi=${ARGS_GENERATE_VAPI_DEFAULT_ARGS}.vapi")
list(APPEND vapi_arguments "--vapi-comments")
# Header and internal header is needed to generate internal vapi
if (NOT ARGS_GENERATE_HEADER)
set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI_DEFAULT_ARGS})
endif(NOT ARGS_GENERATE_HEADER)
if(ARGS_GENERATE_VAPI_INTERNAL)
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI_DEFAULT_ARGS}_internal.vapi")
list(APPEND out_files_display "${ARGS_GENERATE_VAPI_DEFAULT_ARGS}_internal.vapi")
list(APPEND vapi_arguments "--internal-vapi=${ARGS_GENERATE_VAPI_DEFAULT_ARGS}_internal.vapi")
list(APPEND ARGS_GENERATE_HEADER "INTERNAL")
endif(ARGS_GENERATE_VAPI_INTERNAL)
endif(ARGS_GENERATE_VAPI)
set(header_arguments "")
if(ARGS_GENERATE_HEADER)
parse_arguments(ARGS_GENERATE_HEADER "" "INTERNAL" ${ARGS_GENERATE_HEADER})
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER_DEFAULT_ARGS}.h")
list(APPEND out_files_display "${ARGS_GENERATE_HEADER_DEFAULT_ARGS}.h")
list(APPEND header_arguments "--header=${ARGS_GENERATE_HEADER_DEFAULT_ARGS}.h")
if(ARGS_GENERATE_HEADER_INTERNAL)
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER_DEFAULT_ARGS}_internal.h")
list(APPEND out_files_display "${ARGS_GENERATE_HEADER_DEFAULT_ARGS}_internal.h")
list(APPEND header_arguments "--internal-header=${ARGS_GENERATE_HEADER_DEFAULT_ARGS}_internal.h")
endif(ARGS_GENERATE_HEADER_INTERNAL)
endif(ARGS_GENERATE_HEADER)
set(gir_arguments "")
set(gircomp_command "")
if(ARGS_GENERATE_GIR)
parse_arguments(ARGS_GENERATE_GIR "" "TYPELIB" ${ARGS_GENERATE_GIR})
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir")
list(APPEND out_files_display "${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir")
list(APPEND gir_arguments "--gir=${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir")
if(ARGS_GENERATE_GIR_TYPELIB)
include (FindGirCompiler)
find_package(GirCompiler REQUIRED)
add_custom_command(
OUTPUT
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.typelib"
COMMAND
${G_IR_COMPILER_EXECUTABLE}
ARGS
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir"
"--shared-library=$<TARGET_SONAME_FILE_NAME:${target_name}>"
"--output=${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.typelib"
${ARGS_TYPELIB_OPTIONS}
DEPENDS
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.gir"
COMMENT
"Genterating typelib.")
add_custom_target("${target_name}-typelib"
ALL
DEPENDS
"${DIRECTORY}/${ARGS_GENERATE_GIR_DEFAULT_ARGS}.typelib")
endif(ARGS_GENERATE_GIR_TYPELIB)
endif(ARGS_GENERATE_GIR)
set(symbols_arguments "")
if(ARGS_GENERATE_SYMBOLS)
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_SYMBOLS}.symbols")
list(APPEND out_files_display "${ARGS_GENERATE_SYMBOLS}.symbols")
set(symbols_arguments "--symbols=${ARGS_GENERATE_SYMBOLS}.symbols")
endif(ARGS_GENERATE_SYMBOLS)
# Workaround for a bug that would make valac run twice. This file is written
# after the vala compiler generates C source code.
set(OUTPUT_STAMP ${CMAKE_CURRENT_BINARY_DIR}/${target_name}_valac.stamp)
add_custom_command(
OUTPUT
${OUTPUT_STAMP}
COMMAND
${VALA_EXECUTABLE}
ARGS
"-C"
${header_arguments}
${library_arguments}
${vapi_arguments}
${gir_arguments}
${symbols_arguments}
"-b" ${CMAKE_CURRENT_SOURCE_DIR}
"-d" ${DIRECTORY}
${vala_pkg_opts}
${ARGS_OPTIONS}
"--debug"
${in_files}
${custom_vapi_arguments}
COMMAND
touch
ARGS
${OUTPUT_STAMP}
DEPENDS
${in_files}
${ARGS_CUSTOM_VAPIS}
${ARGS_DEPENDS}
COMMENT
"Generating ${out_files_display}"
)
# This command will be run twice for some reason (pass a non-empty string to COMMENT
# in order to see it). Since valac is not executed from here, this won't be a problem.
add_custom_command(OUTPUT ${out_files} DEPENDS ${OUTPUT_STAMP} COMMENT "")
endmacro(vala_precompile)

96
cmake/ValaVersion.cmake Normal file
View file

@ -0,0 +1,96 @@
##
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of Jakob Westhoff
##
include(ParseArguments)
find_package(Vala REQUIRED)
##
# Ensure a certain valac version is available
#
# The initial argument is the version to check for
#
# It may be followed by a optional parameter to specifiy a version range. The
# following options are valid:
#
# EXACT
# Vala needs to be available in the exact version given
#
# MINIMUM
# The provided version is the minimum version. Therefore Vala needs to be
# available in the given version or any higher version
#
# MAXIMUM
# The provided version is the maximum. Therefore Vala needs to be available
# in the given version or any version older than this
#
# If no option is specified the version will be treated as a minimal version.
##
macro(ensure_vala_version version)
parse_arguments(ARGS "" "MINIMUM;MAXIMUM;EXACT" ${ARGN})
set(compare_message "")
set(error_message "")
if(ARGS_MINIMUM)
set(compare_message "a minimum ")
set(error_message "or greater ")
elseif(ARGS_MAXIMUM)
set(compare_message "a maximum ")
set(error_message "or less ")
endif(ARGS_MINIMUM)
message(STATUS
"checking for ${compare_message}Vala version of ${version}"
)
unset(version_accepted)
# MINIMUM is the default if no option is specified
if(ARGS_EXACT)
if(${VALA_VERSION} VERSION_EQUAL ${version} )
set(version_accepted TRUE)
endif(${VALA_VERSION} VERSION_EQUAL ${version})
elseif(ARGS_MAXIMUM)
if(${VALA_VERSION} VERSION_LESS ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
set(version_accepted TRUE)
endif(${VALA_VERSION} VERSION_LESS ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
else(ARGS_MAXIMUM)
if(${VALA_VERSION} VERSION_GREATER ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
set(version_accepted TRUE)
endif(${VALA_VERSION} VERSION_GREATER ${version} OR ${VALA_VERSION} VERSION_EQUAL ${version})
endif(ARGS_EXACT)
if (NOT version_accepted)
message(FATAL_ERROR
"Vala version ${version} ${error_message}is required."
)
endif(NOT version_accepted)
message(STATUS
" found Vala, version ${VALA_VERSION}"
)
endmacro(ensure_vala_version)

11
debian/changelog vendored Normal file
View file

@ -0,0 +1,11 @@
miraclecast (1.0+r67) experimental; urgency=medium
* Jenkins Auto Build
-- <jenkins@45cd608b91cd> Wed, 04 Jan 2017 10:27:21 +0000
miraclecast (1.0) unstable; urgency=medium
* Initial release
-- Deepin Packages Builder <packages@deepin.com> Mon, 05 Dec 2016 13:43:00 +0800

1
debian/compat vendored Normal file
View file

@ -0,0 +1 @@
9

12
debian/control vendored Normal file
View file

@ -0,0 +1,12 @@
Source: miraclecast
Section: utils
Priority: optional
Maintainer: Deepin Packages Builder <packages@deepin.com>
Build-Depends: debhelper (>= 9), cmake, check, valac, vala-dbus-binding-tool, systemd, libsystemd-dev, libglib2.0-bin, libgtk-3-dev, libgstreamer1.0-dev, libreadline-dev, libudev-dev
Standards-Version: 3.9.8
Homepage: https://github.com/derekdai/miraclecast
Package: miraclecast
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libsystemd0, libudev1, libglib2.0-0, libgstreamer1.0-0, gstreamer1.0-pulseaudio, gstreamer1.0-vaapi (>= 1.10.2), gstreamer1.0-plugins-good, gstreamer1.0-plugins-bad, gstreamer1.0-plugins-ugly, mesa-va-drivers, i965-va-driver, vdpau-va-driver
Description: Connect external monitors to your system via Wifi-Display specification also known as Miracast

38
debian/copyright vendored Normal file
View file

@ -0,0 +1,38 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: miraclecast
Source: <url://example.com>
Files: *
Copyright: <years> <put author's name and email here>
<years> <likewise for another author>
License: <special license>
<Put the license of the package here indented by 1 space>
<This follows the format of Description: lines in control file>
.
<Including paragraphs>
# If you want to use GPL v2 or later for the /debian/* files use
# the following clauses, or change it to suit. Delete these two lines
Files: debian/*
Copyright: 2016 Deepin Packages Builder <packages@deepin.com>
License: GPL-2+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
.
This package 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 General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
# Please also look if there are files or directories which have a
# different copyright/license attached and list them here.
# Please avoid picking licenses with terms that are more restrictive than the
# packaged work, as it may make Debian's contributions unacceptable upstream.

1
debian/postinst vendored Normal file
View file

@ -0,0 +1 @@
systemctl daemon-reload

1
debian/postrm vendored Normal file
View file

@ -0,0 +1 @@
systemctl daemon-reload

22
debian/rules vendored Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#export DH_VERBOSE = 1
# see FEATURE AREAS in dpkg-buildflags(1)
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# see ENVIRONMENT in dpkg-buildflags(1)
# package maintainers to append CFLAGS
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# package maintainers to append LDFLAGS
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
%:
dh $@
# dh_make generated override targets
# This is example for Cmake (See https://bugs.debian.org/641051 )

1
debian/source/format vendored Normal file
View file

@ -0,0 +1 @@
3.0 (native)

View file

@ -1,14 +1,141 @@
INSTALL(
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)
include_directories(
${GST1_INCLUDE_DIRS}
${GDK3_INCLUDE_DIRS}
${GIO_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}
)
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(
PROGRAMS miracle-gst gstplayer uibc-viewer
DESTINATION bin
)
INSTALL(
install(
FILES org.freedesktop.miracle.conf
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
)
INSTALL(
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 $ENV{DESTDIR}/etc/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: 000600001c4400c8", "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 = "000600001c4400c8";
}
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();
//app.set_default();
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;
}

421
res/gstencoder.vala Normal file
View file

@ -0,0 +1,421 @@
/*
* 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);
string bus_info = "%s\n%s".printf(conn.unique_name,
BusType.get_address_sync(BusType.SESSION));
/* 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()
else if 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)
@ -154,7 +156,6 @@ class Player(object):
pos_event_x = event.x
pos_event_y = event.y
if min_hor_pos <= pos_event_x <= max_hor_pos and min_ver_pos <= pos_event_y <= max_ver_pos:
uibc_x = int(pos_event_x - (half_area_width - half_def_width))
uibc_y = int(pos_event_y - (half_area_height - half_def_height))
@ -206,6 +207,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=1991, 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")
@ -217,6 +219,7 @@ if __name__ == '__main__':
# " default HH %08X\n"
parser.add_argument("-r", "--resolution", help="Resolution")
parser.set_defaults(audio=True)
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')
@ -7,6 +43,36 @@ install_data('miracle-gst', 'gstplayer', 'uibc-viewer',
install_dir: get_option('bindir'),
install_mode: 'rwxr-xr-x')
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
)
install_data(
'miracle-wifid', 'miracle-sinkctl', 'miracle-wifictl',
install_dir: join_paths(get_option('datadir'), 'bash-completions', 'completions')

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

@ -11,47 +11,23 @@
<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"/>
<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 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>
</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();
}

View file

@ -5,6 +5,7 @@ add_subdirectory(wifi)
add_subdirectory(dhcp)
add_subdirectory(ctl)
add_subdirectory(uibc)
add_subdirectory(disp)
set(miracled_SRCS miracled.h miracled.c)
add_executable(miracled ${miracled_SRCS})

View file

@ -2,7 +2,7 @@ find_package(PkgConfig)
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
link_directories( ${GLIB2_LIBRARY_DIRS})
include_directories( ${GLIB2_INCLUDE_DIRS})
find_package(Readline)
find_package(Readline REQUIRED)
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
########### next target ###############
@ -50,12 +50,8 @@ endif(READLINE_FOUND)
target_link_libraries(miracle-sinkctl miracle-shared)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
########### install files ###############
include_directories(${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
#original Makefile.am contents follow:

View file

@ -39,7 +39,7 @@ static void sink_handle_options(struct ctl_sink *s,
if (r < 0)
return cli_vERR(r);
r = rtsp_message_append(rep, "<s>",
r = rtsp_message_append(rep, "<&>",
"Public",
"org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER");
if (r < 0)

View file

@ -637,6 +637,7 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
_sd_bus_message_unref_ sd_bus_message *m = NULL;
_sd_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
_shl_free_ char *node = NULL;
const char *method = val ? "Manage" : "Unmanage";
int r;
if (!l)
@ -661,7 +662,7 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
r = sd_bus_message_append(m, "ss",
"org.freedesktop.miracle.wifi.Link",
"Managed");
method);
if (r < 0)
return cli_log_create(r);
@ -901,6 +902,9 @@ static int ctl_wifi_parse_object(struct ctl_wifi *w,
}
}
free(label);
label = NULL;
r = sd_bus_path_decode(t,
"/org/freedesktop/miracle/wifi/peer",
&label);
@ -1068,6 +1072,38 @@ static int ctl_wifi_peer_fn(sd_bus_message *m,
return 0;
}
static void ctl_wifi_unlink_all_links(struct ctl_wifi *w)
{
struct ctl_link *l;
while (!shl_dlist_empty(&w->links)) {
l = shl_dlist_last_entry(&w->links, struct ctl_link, list);
ctl_link_free(l);
}
}
static int ctl_wifi_wifid_up_or_down_fn(sd_bus_message *m,
void *data,
sd_bus_error *err)
{
struct ctl_wifi *w = data;
char *from = NULL, *to = NULL;
int r;
r = sd_bus_message_read(m, "sss", NULL, &from, &to);
if(r < 0) {
return r;
}
if(*from && !*to) {
ctl_wifi_unlink_all_links(w);
}
else if(!*from && *to) {
r = ctl_wifi_fetch(w);
}
return r;
}
static int ctl_wifi_init(struct ctl_wifi *w)
{
int r;
@ -1099,6 +1135,18 @@ static int ctl_wifi_init(struct ctl_wifi *w)
if (r < 0)
return r;
r = sd_bus_add_match(w->bus, NULL,
"type='signal',"
"sender='org.freedesktop.DBus',"
"path='/org/freedesktop/DBus',"
"interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',"
"arg0namespace='org.freedesktop.miracle.wifi'",
ctl_wifi_wifid_up_or_down_fn,
w);
if (r < 0)
return r;
return 0;
}
@ -1135,21 +1183,52 @@ int ctl_wifi_new(struct ctl_wifi **out, sd_bus *bus)
void ctl_wifi_free(struct ctl_wifi *w)
{
struct ctl_link *l;
if (!w)
return;
while (!shl_dlist_empty(&w->links)) {
l = shl_dlist_last_entry(&w->links, struct ctl_link, list);
ctl_link_free(l);
}
ctl_wifi_unlink_all_links(w);
ctl_wifi_destroy(w);
sd_bus_unref(w->bus);
free(w);
}
static int ctl_wifi_parse_objects(struct ctl_wifi *w,
sd_bus_message *m,
bool ignore_link_not_found)
{
bool again = false;
int r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
if (r < 0)
return cli_log_parser(r);
while ((r = sd_bus_message_enter_container(m,
'e',
"oa{sa{sv}}")) > 0) {
r = ctl_wifi_parse_object(w, m, true);
if(ignore_link_not_found && -EINVAL == r) {
r = sd_bus_message_skip(m, "a{sa{sv}}");
if(0 > r)
return cli_log_parser(r);
again = true;
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return cli_log_parser(r);
}
if (r < 0)
return cli_log_parser(r);
r = sd_bus_message_exit_container(m);
if (r < 0)
return cli_log_parser(r);
return again ? -EAGAIN : 0;
}
int ctl_wifi_fetch(struct ctl_wifi *w)
{
_sd_bus_message_unref_ sd_bus_message *m = NULL;
@ -1173,29 +1252,20 @@ int ctl_wifi_fetch(struct ctl_wifi *w)
return r;
}
r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
if (r < 0)
return cli_log_parser(r);
while ((r = sd_bus_message_enter_container(m,
'e',
"oa{sa{sv}}")) > 0) {
r = ctl_wifi_parse_object(w, m, true);
if (r < 0)
return r;
r = sd_bus_message_exit_container(m);
if (r < 0)
return cli_log_parser(r);
r = ctl_wifi_parse_objects(w, m, true);
if(0 <= r || -EAGAIN != r) {
goto end;
}
if (r < 0)
return cli_log_parser(r);
r = sd_bus_message_exit_container(m);
if (r < 0)
return cli_log_parser(r);
r = sd_bus_message_rewind(m, true);
if(0 > r) {
return r;
}
return 0;
r = ctl_wifi_parse_objects(w, m, false);
end:
return r;
}
struct ctl_link *ctl_wifi_find_link(struct ctl_wifi *w,

View file

@ -27,7 +27,9 @@
#include <sys/types.h>
#include <systemd/sd-bus.h>
#include "shl_dlist.h"
#include "shl_htable.h"
#include "shl_log.h"
#include "wfd.h"
#ifndef CTL_CTL_H
#define CTL_CTL_H
@ -248,3 +250,4 @@ void ctl_fn_sink_resolution_set(struct ctl_sink *s);
void cli_fn_help(void);
#endif /* CTL_CTL_H */

View file

@ -1,14 +1,14 @@
inc = include_directories('../..')
deps = [libsystemd, libmiracle_shared_dep, glib2]
miracle_wifictl_deps = [libsystemd, libmiracle_shared_dep, glib2]
if readline.found()
deps += readline
miracle_wifictl_deps += readline
endif
miracle_wifictl_srcs = ['ctl-cli.c', 'ctl-wifi.c', 'wifictl.c']
executable('miracle-wifictl', miracle_wifictl_srcs,
install: true,
include_directories: inc,
dependencies: deps
dependencies: miracle_wifictl_deps
)
miracle_sinkctl_srcs = ['ctl-cli.c',
@ -20,5 +20,5 @@ miracle_sinkctl_srcs = ['ctl-cli.c',
executable('miracle-sinkctl', miracle_sinkctl_srcs,
install: true,
include_directories: inc,
dependencies: deps
dependencies: miracle_wifictl_deps
)

View file

@ -212,10 +212,8 @@ static int cmd_run(char **args, unsigned int n)
return 0;
}
if (!l->managed) {
cli_printf("link %s not managed\n", l->label);
return 0;
}
if (!l->managed)
return log_EUNMANAGED();
run_on(l);
@ -247,10 +245,8 @@ static int cmd_bind(char **args, unsigned int n)
if (!l)
return 0;
if (!l->managed) {
cli_printf("link %s not managed\n", l->label);
return 0;
}
if (!l->managed)
return log_EUNMANAGED();
run_on(l);
@ -843,7 +839,7 @@ static int parse_argv(int argc, char *argv[])
case ARG_HELP_COMMANDS:
return cli_help(cli_cmds, 20);
case ARG_HELP_RES:
wfd_print_resolutions("");
wfd_print_resolutions();
return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);

View file

@ -20,110 +20,153 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "ctl.h"
#include "wfd.h"
#include "util.h"
struct resolution_bitmap {
int index;
int hres;
int vres;
int fps;
typedef int (*wfd_sube_parse_func)(const char *in, union wfd_sube *out);
struct wfd_sube_info
{
wfd_sube_parse_func parser;
uint8_t len;
};
static int wfd_sube_parse_device_info(const char *in, union wfd_sube *out);
static int wfd_sube_parse_audio_formats(const char *in, union wfd_sube *out);
static int wfd_sube_parse_video_formats(const char *in, union wfd_sube *out);
static int wfd_sube_parse_ext_caps(const char *in, union wfd_sube *out);
/*
* CEA resolutions and refrash rate bitmap/index table
* also used in native resolution field
*/
struct resolution_bitmap resolutions_cea[] = {
{0, 640, 480, 60}, /* p60 */
{1, 720, 480, 60}, /* p60 */
{2, 720, 480, 60}, /* i60 */
{3, 720, 576, 50}, /* p50 */
{4, 720, 576, 50}, /* i50 */
{5, 1280, 720, 30}, /* p30 */
{6, 1280, 720, 60}, /* p60 */
{7, 1920, 1080, 30}, /* p30 */
{8, 1920, 1080, 60}, /* p60 */
{9, 1920, 1080, 60}, /* i60 */
{10, 1280, 720, 25}, /* p25 */
{11, 1280, 720, 50}, /* p50 */
{12, 1920, 1080, 25}, /* p25 */
{13, 1920, 1080, 50}, /* p50 */
{14, 1920, 1080, 50}, /* i50 */
{15, 1280, 720, 24}, /* p24 */
{16, 1920, 1080, 24}, /* p24 */
{0, 0, 0, 0},
static const struct wfd_resolution resolutions_cea[] = {
{0, 640, 480, 60, 1}, /* p60 */
{1, 720, 480, 60, 1}, /* p60 */
{2, 720, 480, 60, 0}, /* i60 */
{3, 720, 576, 50, 1}, /* p50 */
{4, 720, 576, 50, 0}, /* i50 */
{5, 1280, 720, 30, 1}, /* p30 */
{6, 1280, 720, 60, 1}, /* p60 */
{7, 1920, 1080, 30, 1}, /* p30 */
{8, 1920, 1080, 60, 1}, /* p60 */
{9, 1920, 1080, 60, 0}, /* i60 */
{10, 1280, 720, 25, 1}, /* p25 */
{11, 1280, 720, 50, 1}, /* p50 */
{12, 1920, 1080, 25, 1}, /* p25 */
{13, 1920, 1080, 50, 1}, /* p50 */
{14, 1920, 1080, 50, 0}, /* i50 */
{15, 1280, 720, 24, 1}, /* p24 */
{16, 1920, 1080, 24, 1}, /* p24 */
};
struct resolution_bitmap resolutions_vesa[] = {
{0, 800, 600, 30}, /* p30 */
{1, 800, 600, 60}, /* p60 */
{2, 1024, 768, 30}, /* p30 */
{3, 1024, 768, 60}, /* p60 */
{4, 1152, 854, 30}, /* p30 */
{5, 1152, 854, 60}, /* p60 */
{6, 1280, 768, 30}, /* p30 */
{7, 1280, 768, 60}, /* p60 */
{8, 1280, 800, 30}, /* p30 */
{9, 1280, 800, 60}, /* p60 */
{10, 1360, 768, 30}, /* p30 */
{11, 1360, 768, 60}, /* p60 */
{12, 1366, 768, 30}, /* p30 */
{13, 1366, 768, 60}, /* p60 */
{14, 1280, 1024, 30}, /* p30 */
{15, 1280, 1024, 60}, /* p60 */
{16, 1440, 1050, 30}, /* p30 */
{17, 1440, 1050, 60}, /* p60 */
{18, 1440, 900, 30}, /* p30 */
{19, 1440, 900, 60}, /* p60 */
{20, 1600, 900, 30}, /* p30 */
{21, 1600, 900, 60}, /* p60 */
{22, 1600, 1200, 30}, /* p30 */
{23, 1600, 1200, 60}, /* p60 */
{24, 1680, 1024, 30}, /* p30 */
{25, 1680, 1024, 60}, /* p60 */
{26, 1680, 1050, 30}, /* p30 */
{27, 1680, 1050, 60}, /* p60 */
{28, 1920, 1200, 30}, /* p30 */
{0, 0, 0, 0},
static const struct wfd_resolution resolutions_vesa[] = {
{0, 800, 600, 30, 1}, /* p30 */
{1, 800, 600, 60, 1}, /* p60 */
{2, 1024, 768, 30, 1}, /* p30 */
{3, 1024, 768, 60, 1}, /* p60 */
{4, 1152, 854, 30, 1}, /* p30 */
{5, 1152, 854, 60, 1}, /* p60 */
{6, 1280, 768, 30, 1}, /* p30 */
{7, 1280, 768, 60, 1}, /* p60 */
{8, 1280, 800, 30, 1}, /* p30 */
{9, 1280, 800, 60, 1}, /* p60 */
{10, 1360, 768, 30, 1}, /* p30 */
{11, 1360, 768, 60, 1}, /* p60 */
{12, 1366, 768, 30, 1}, /* p30 */
{13, 1366, 768, 60, 1}, /* p60 */
{14, 1280, 1024, 30, 1}, /* p30 */
{15, 1280, 1024, 60, 1}, /* p60 */
{16, 1440, 1050, 30, 1}, /* p30 */
{17, 1440, 1050, 60, 1}, /* p60 */
{18, 1440, 900, 30, 1}, /* p30 */
{19, 1440, 900, 60, 1}, /* p60 */
{20, 1600, 900, 30, 1}, /* p30 */
{21, 1600, 900, 60, 1}, /* p60 */
{22, 1600, 1200, 30, 1}, /* p30 */
{23, 1600, 1200, 60, 1}, /* p60 */
{24, 1680, 1024, 30, 1}, /* p30 */
{25, 1680, 1024, 60, 1}, /* p60 */
{26, 1680, 1050, 30, 1}, /* p30 */
{27, 1680, 1050, 60, 1}, /* p60 */
{28, 1920, 1200, 30, 1}, /* p30 */
};
struct resolution_bitmap resolutions_hh[] = {
{0, 800, 480, 30}, /* p30 */
{1, 800, 480, 60}, /* p60 */
{2, 854, 480, 30}, /* p30 */
{3, 854, 480, 60}, /* p60 */
{4, 864, 480, 30}, /* p30 */
{5, 864, 480, 60}, /* p60 */
{6, 640, 360, 30}, /* p30 */
{7, 640, 360, 60}, /* p60 */
{8, 960, 540, 30}, /* p30 */
{9, 960, 540, 60}, /* p60 */
{10, 848, 480, 30}, /* p30 */
{11, 848, 480, 60}, /* p60 */
{0, 0, 0, 0},
static const struct wfd_resolution resolutions_hh[] = {
{0, 800, 480, 30, 1}, /* p30 */
{1, 800, 480, 60, 1}, /* p60 */
{2, 854, 480, 30, 1}, /* p30 */
{3, 854, 480, 60, 1}, /* p60 */
{4, 864, 480, 30, 1}, /* p30 */
{5, 864, 480, 60, 1}, /* p60 */
{6, 640, 360, 30, 1}, /* p30 */
{7, 640, 360, 60, 1}, /* p60 */
{8, 960, 540, 30, 1}, /* p30 */
{9, 960, 540, 60, 1}, /* p60 */
{10, 848, 480, 30, 1}, /* p30 */
{11, 848, 480, 60, 1}, /* p60 */
};
void wfd_print_resolutions(char * prefix)
static const struct wfd_sube_info parser_tbl[WFD_SUBE_ID_RESERVED] = {
[WFD_SUBE_ID_DEVICE_INFO] = { .parser = wfd_sube_parse_device_info, .len = 6 },
[WFD_SUBE_ID_AUDIO_FORMATS] = { .parser = wfd_sube_parse_audio_formats, .len = 15 },
[WFD_SUBE_ID_VIDEO_FORMATS] = { .parser = wfd_sube_parse_video_formats, .len = 21 },
[WFD_SUBE_ID_WFD_EXT_CAPS] = { .parser = wfd_sube_parse_ext_caps, .len = 2 },
};
int wfd_get_resolutions(enum wfd_resolution_standard std,
int index,
struct wfd_resolution *out)
{
switch(std) {
case WFD_RESOLUTION_STANDARD_CEA:
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_cea)) {
break;
}
*out = resolutions_cea[index];
return 0;
case WFD_RESOLUTION_STANDARD_VESA:
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_vesa)) {
break;
}
*out = resolutions_vesa[index];
return 0;
case WFD_RESOLUTION_STANDARD_HH:
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_hh)) {
break;
}
*out = resolutions_hh[index];
return 0;
default:
break;
}
return -EINVAL;
}
void wfd_print_resolutions(void)
{
int i;
printf("%sCEA resolutions:\n", prefix);
for (i = 0; resolutions_cea[i].hres != 0; i++) {
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
printf("CEA resolutions:\n");
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_cea); i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
resolutions_cea[i].hres, resolutions_cea[i].vres,
resolutions_cea[i].fps);
}
printf("%sVESA resolutions:\n", prefix);
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
printf("VESA resolutions:\n");
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_vesa); i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
resolutions_vesa[i].hres, resolutions_vesa[i].vres,
resolutions_vesa[i].fps);
}
printf("%sHH resolutions:\n", prefix);
for (i = 0; resolutions_hh[i].hres != 0; i++) {
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
printf("HH resolutions:\n");
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_hh); i++) {
printf("\t%2d %08x %4dx%4d@%d\n",
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
resolutions_hh[i].hres, resolutions_hh[i].vres,
resolutions_hh[i].fps);
@ -141,7 +184,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
if (cea_mask) {
cli_debug("CEA resolutions:");
for (i = 0; resolutions_cea[i].hres != 0; i++)
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_cea); i++)
if ((1 << resolutions_cea[i].index) & cea_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
@ -150,7 +193,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
}
if (vesa_mask) {
cli_debug("VESA resolutions:");
for (i = 0; resolutions_vesa[i].hres != 0; i++)
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_vesa); i++)
if ((1 << resolutions_vesa[i].index) & vesa_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
@ -159,7 +202,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
}
if (hh_mask) {
cli_debug("HH resolutions:");
for (i = 0; resolutions_hh[i].hres != 0; i++)
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_hh); i++)
if ((1 << resolutions_hh[i].index) & hh_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
@ -175,7 +218,7 @@ int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres)
if (!mask)
return -EINVAL;
for (i = 0; resolutions_cea[i].hres != 0; i++) {
for (i = SHL_ARRAY_LENGTH(resolutions_cea) - 1; i >= 0; --i) {
if ((1 << resolutions_cea[i].index) & mask) {
*vres = resolutions_cea[i].vres;
*hres = resolutions_cea[i].hres;
@ -192,7 +235,7 @@ int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres)
if (!mask)
return -EINVAL;
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
for (i = SHL_ARRAY_LENGTH(resolutions_vesa) - 1; i >= 0; --i) {
if ((1 << resolutions_vesa[i].index) & mask) {
*vres = resolutions_vesa[i].vres;
*hres = resolutions_vesa[i].hres;
@ -209,7 +252,7 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
if (!mask)
return -EINVAL;
for (i = 0; resolutions_hh[i].hres != 0; i++) {
for (i = SHL_ARRAY_LENGTH(resolutions_hh); i >= 0; --i) {
if ((1 << resolutions_hh[i].index) & mask) {
*vres = resolutions_hh[i].vres;
*hres = resolutions_hh[i].hres;
@ -218,3 +261,436 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
}
return -EINVAL;
}
int vfd_get_mask_from_resolution(int hres,
int vres,
enum wfd_resolution_standard *out_std,
uint32_t *out_mask)
{
struct {
const struct wfd_resolution *entries;
size_t n_entries;
} tbls[] = {
[WFD_RESOLUTION_STANDARD_CEA] = {
resolutions_cea,
SHL_ARRAY_LENGTH(resolutions_cea)
},
[WFD_RESOLUTION_STANDARD_VESA] = {
resolutions_vesa,
SHL_ARRAY_LENGTH(resolutions_vesa)
},
[WFD_RESOLUTION_STANDARD_HH] = {
resolutions_hh,
SHL_ARRAY_LENGTH(resolutions_hh)
},
};
enum wfd_resolution_standard std = WFD_RESOLUTION_STANDARD_CEA;
uint32_t mask = 0;
size_t n;
for(; std <= WFD_RESOLUTION_STANDARD_HH; ++ std) {
for(n = 0; n < tbls[std].n_entries; ++ n) {
if(hres == tbls[std].entries[n].hres &&
vres == tbls[std].entries[n].vres) {
mask |= 1 << n;
}
}
if(mask) {
if(out_mask) {
*out_mask = mask;
}
if(out_std) {
*out_std = std;
}
return 0;
}
}
return -ENOENT;
}
static int wfd_sube_parse_device_info(const char *in, union wfd_sube *out)
{
int r = sscanf(in, "%4hx%4hx%4hx",
&out->dev_info.dev_info,
&out->dev_info.rtsp_port,
&out->dev_info.max_throughput);
return 3 == r ? 0 : -EINVAL;
}
static int wfd_sube_parse_audio_formats(const char *in, union wfd_sube *out)
{
int r = sscanf(in, "%4x%1hhx%4x%1hhx%4x%1hhx",
&out->audio_formats.lpcm_modes,
&out->audio_formats.lpcm_dec_latency,
&out->audio_formats.aac_modes,
&out->audio_formats.aac_dec_latency,
&out->audio_formats.ac3_modes,
&out->audio_formats.ac3_dec_latency);
return 6 == r ? 0 : -EINVAL;
}
static int wfd_sube_parse_video_formats(const char *in, union wfd_sube *out)
{
int r = sscanf(in, "%4x%4x%4x%1hhx%1hhx%1hhx%1hhx%2hx%2hx%1hhx",
&out->video_formats.cea,
&out->video_formats.vesa,
&out->video_formats.hh,
&out->video_formats.native,
&out->video_formats.profiles,
&out->video_formats.levels,
&out->video_formats.latency,
&out->video_formats.min_slice_size,
&out->video_formats.slice_enc_params,
&out->video_formats.video_frame_rate_ctl);
return 12 == r ? 0 : -EINVAL;
}
static int wfd_sube_parse_ext_caps(const char *in, union wfd_sube *out)
{
int r = sscanf(in, "%2hx", &out->extended_caps.caps);
return 1 == r ? 0 : -EINVAL;
}
int wfd_sube_parse(const char *in, union wfd_sube *out)
{
uint8_t id;
int r;
r = sscanf(in, "%2hhx", &id);
if(1 > r) {
return -EINVAL;
}
return wfd_sube_parse_with_id(id, in + 2, out);
}
int wfd_sube_parse_with_id(enum wfd_sube_id id,
const char *in,
union wfd_sube *out)
{
uint16_t len;
union wfd_sube sube;
int r;
if(SHL_ARRAY_LENGTH(parser_tbl) <= id) {
return -EINVAL;
}
r = sscanf(in, "%4hx", &len);
if(1 > r) {
return -EINVAL;
}
if(parser_tbl[id].len != len) {
return -EINVAL;
}
if(!parser_tbl[id].parser) {
return -ENOTSUP;
}
r = (*parser_tbl[id].parser)(in + 4, &sube);
if(0 > r) {
return r;
}
sube.id = id;
if(out) {
*out = sube;
}
return r;
}
int wfd_video_formats_from_string(const char *l,
struct wfd_video_formats **out)
{
_shl_free_ struct wfd_video_formats *f = NULL;
uint8_t native, pref_disp_mode_sup;
int r, i, n_codecs;
const char *p;
char max_hres[5], max_vres[5];
assert(l);
if(!strncmp("none", l, 4)) {
if(out) {
*out = NULL;
}
return 0;
}
r = sscanf(l, "%2hhx %2hhx", &native, &pref_disp_mode_sup);
if(2 != r) {
return -EINVAL;
}
l += 6;
for(p = l, n_codecs = 1; (p = strchrnul(p, ','), *p); ++ n_codecs, ++ p);
f = malloc(sizeof(*f) + (sizeof(f->h264_codecs[0]) * n_codecs));
if(!f) {
return -ENOMEM;
}
for(i = 0; i < n_codecs; i ++) {
r = sscanf(l,
"%2hhx %2hhx %8x %8x %8x %2hhx %4hx %4hx %2hhx %4s %4s",
&f->h264_codecs[i].profile,
&f->h264_codecs[i].level,
&f->h264_codecs[i].cea_sup,
&f->h264_codecs[i].vesa_sup,
&f->h264_codecs[i].hh_sup,
&f->h264_codecs[i].latency,
&f->h264_codecs[i].min_slice_size,
&f->h264_codecs[i].slice_enc_params,
&f->h264_codecs[i].frame_rate_ctrl_sup,
max_hres,
max_vres);
if(11 != r) {
return -EINVAL;
}
errno = 0;
f->h264_codecs[i].max_hres = !strncmp("none", max_hres, 4)
? 0
: strtoul(max_hres, NULL, 16);
if(errno) {
return -errno;
}
f->h264_codecs[i].max_vres = !strncmp("none", max_vres, 4)
? 0
: strtoul(max_vres, NULL, 16);
if(errno) {
return -errno;
}
l += 60;
}
f->native = native;
f->pref_disp_mode_sup = pref_disp_mode_sup;
f->n_h264_codecs = n_codecs;
if(out) {
*out = f;
f = NULL;
}
return 0;
}
static inline const char * int16_to_res(int16_t v, char *b)
{
if(!v) {
return "none";
}
sprintf(b, "%04hX", v);
return b;
}
int wfd_video_formats_to_string(struct wfd_video_formats *f, char **out)
{
_shl_free_ char *s = NULL;
char *p, b1[5], b2[5];
size_t len = 6;
int r, i;
assert(f);
assert(out);
len += (f->n_h264_codecs ? f->n_h264_codecs * 60 : 6);
p = s = malloc(len);
if(!s) {
return -ENOMEM;
}
r = snprintf(p, len, "%02hhX %02hhX ", f->native, f->pref_disp_mode_sup);
if(0 > r) {
return r;
}
p += r;
len -= r;
if(!f->n_h264_codecs) {
strcat(p, " none");
goto end;
}
for(i = 0; i < f->n_h264_codecs; ++ i) {
r = snprintf(p, len,
"%02hhX %02hhX %08X %08X %08X %02hhX %04hX %04hX %02hhX %s %s, ",
f->h264_codecs[i].profile,
f->h264_codecs[i].level,
f->h264_codecs[i].cea_sup,
f->h264_codecs[i].vesa_sup,
f->h264_codecs[i].hh_sup,
f->h264_codecs[i].latency,
f->h264_codecs[i].min_slice_size,
f->h264_codecs[i].slice_enc_params,
f->h264_codecs[i].frame_rate_ctrl_sup,
int16_to_res(f->h264_codecs[i].max_hres, b1),
int16_to_res(f->h264_codecs[i].max_vres, b2));
if(0 > r) {
return r;
}
p += r;
len -= r;
}
p[-2] = '\0';
end:
*out = s;
s = NULL;
return 0;
}
int wfd_audio_format_from_string(const char *s, enum wfd_audio_format *f)
{
enum wfd_audio_format t = WFD_AUDIO_FORMAT_UNKNOWN;
if(s) {
if(!strncmp("LPCM", s, 4)) {
t = WFD_AUDIO_FORMAT_LPCM;
}
else if(!strncmp("AAC", s, 3)) {
t = WFD_AUDIO_FORMAT_AAC;
}
else if(!strncmp("AC3", s, 3)) {
t = WFD_AUDIO_FORMAT_AC3;
}
if(WFD_AUDIO_FORMAT_UNKNOWN != t) {
if(f) {
*f = t;
}
return 0;
}
}
return -EINVAL;
}
const char * wfd_audio_format_to_string(enum wfd_audio_format f)
{
switch(f) {
case WFD_AUDIO_FORMAT_LPCM:
return "LPCM";
case WFD_AUDIO_FORMAT_AAC:
return "AAC";
case WFD_AUDIO_FORMAT_AC3:
return "AC3";
default:
return NULL;
}
}
int wfd_audio_codecs_from_string(const char *l,
struct wfd_audio_codecs **out)
{
_shl_free_ struct wfd_audio_codecs *c = NULL;
_shl_free_ char *f = NULL;
int r, i, n_caps;
const char *p;
assert(l);
if(!strncmp("none", l, 4)) {
if(out) {
*out = NULL;
}
return 0;
}
for(p = l, n_caps = 1; (p = strchrnul(p, ','), *p); ++ n_caps, ++ p);
c = malloc(sizeof(struct wfd_audio_codecs)
+ (sizeof(c->caps[0]) * n_caps));
for(i = 0; i < n_caps; i ++) {
r = sscanf(l, "%ms %8x %2hhx",
&f,
&c->caps[i].modes,
&c->caps[i].latency);
if(r != 3) {
return -EINVAL;
}
r = wfd_audio_format_from_string(f, &c->caps[i].format);
if(0 > r) {
return r;
}
l += 16;
if(WFD_AUDIO_FORMAT_LPCM == c->caps[i].format) {
++ l;
}
free(f);
f = NULL;
}
c->n_caps = n_caps;
if(out) {
*out = c;
c = NULL;
}
return 0;
}
int wfd_audio_codecs_to_string(struct wfd_audio_codecs *c, char **out)
{
_shl_free_ char *s = NULL;
char *p;
int r, i;
size_t len;
assert(c);
assert(out);
len = c->n_caps * 18;
p = s = malloc(len);
if(!s) {
return -ENOMEM;
}
for(i = 0; i < c->n_caps; i ++) {
r = snprintf(p, len, "%s %08X %02hhX, ",
wfd_audio_format_to_string(c->caps[i].format),
c->caps[i].modes,
c->caps[i].latency);
if(0 > r) {
return r;
}
p += r;
len -= r;
}
p[-2] = '\n';
*out = s;
s = NULL;
return 0;
}

View file

@ -21,9 +21,200 @@
#ifndef WFD_H
#define WFD_H
void wfd_print_resolutions(char * prefix);
#include <stdbool.h>
#include <assert.h>
#include <stdlib.h>
#define wfd_sube_is_device_info(w) (WFD_SUBE_ID_DEVICE_INFO == (w)->id)
#define WFD_DEVINFO_DEV_TYPE_MASK (0x3 << 0)
#define WFD_DEVINFO_SRC_COUPLED_SINK_MASK (0x1 << 2)
#define WFD_DEVINFO_SINK_COUPLED_SINK_MASK (0x1 << 3)
#define WFD_DEVINFO_SESSION_MASK (0x3 << 4)
#define WFD_DEVINFO_WSD_MASK (0x1 << 6)
#define WFD_DEVINFO_PC_MASK (0x1 << 7)
#define WFD_DEVINFO_CP_MASK (0x1 << 8)
#define WFD_DEVINFO_TIME_SYNC_MASK (0x1 << 9)
#define WFD_DEVINFO_PRI_SINK_AUDIO_MASK (0x1 << 10)
#define WFD_DEVINFO_SRC_AUDIO_ONLY_MASK (0x1 << 11)
enum wfd_sube_id {
WFD_SUBE_ID_DEVICE_INFO,
WFD_SUBE_ID_ASSOCIATED_BSSID,
WFD_SUBE_ID_AUDIO_FORMATS,
WFD_SUBE_ID_VIDEO_FORMATS,
WFD_SUBE_ID_3D_VIDEO_FORMATS,
WFD_SUBE_ID_CONTENT_PROTECTION,
WFD_SUBE_ID_COUPLED_SINK_INFO,
WFD_SUBE_ID_WFD_EXT_CAPS,
WFD_SUBE_ID_LOCAL_IP_ADDR,
WFD_SUBE_ID_RESERVED,
};
enum wfd_resolution_standard
{
WFD_RESOLUTION_STANDARD_CEA,
WFD_RESOLUTION_STANDARD_VESA,
WFD_RESOLUTION_STANDARD_HH,
};
enum wfd_audio_format
{
WFD_AUDIO_FORMAT_UNKNOWN,
WFD_AUDIO_FORMAT_LPCM,
WFD_AUDIO_FORMAT_AAC,
WFD_AUDIO_FORMAT_AC3
};
union wfd_sube
{
enum wfd_sube_id id;
struct {
enum wfd_sube_id id;
uint16_t dev_info;
uint16_t rtsp_port;
uint16_t max_throughput;
} dev_info;
struct {
enum wfd_sube_id id;
uint32_t cea;
uint32_t vesa;
uint32_t hh;
uint8_t native;
uint8_t profiles;
uint8_t levels;
uint8_t latency;
uint16_t min_slice_size;
uint16_t slice_enc_params;
uint8_t video_frame_rate_ctl;
} video_formats;
struct {
enum wfd_sube_id id;
uint32_t lpcm_modes;
uint8_t lpcm_dec_latency;
uint32_t aac_modes;
uint8_t aac_dec_latency;
uint32_t ac3_modes;
uint8_t ac3_dec_latency;
} audio_formats;
struct {
enum wfd_sube_id id;
uint16_t caps;
} extended_caps;
};
struct wfd_video_formats
{
uint8_t native;
uint8_t pref_disp_mode_sup;
size_t n_h264_codecs;
struct {
uint8_t profile;
uint8_t level;
uint32_t cea_sup;
uint32_t vesa_sup;
uint32_t hh_sup;
uint8_t latency;
uint16_t min_slice_size;
uint16_t slice_enc_params;
uint8_t frame_rate_ctrl_sup;
uint16_t max_hres;
uint16_t max_vres;
} h264_codecs[0];
};
struct wfd_audio_codecs
{
size_t n_caps;
struct {
enum wfd_audio_format format;
uint32_t modes;
uint8_t latency;
} caps[0];
};
struct wfd_resolution
{
uint16_t index;
uint16_t hres;
uint16_t vres;
uint16_t fps;
bool progressive: 1;
};
void wfd_print_resolutions(void);
int wfd_get_resolutions(enum wfd_resolution_standard std,
int index,
struct wfd_resolution *out);
int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_mask_from_resolution(int hres,
int vres,
enum wfd_resolution_standard *out_std,
uint32_t *out_mask);
int wfd_sube_parse(const char *in, union wfd_sube *out);
int wfd_sube_parse_with_id(enum wfd_sube_id id,
const char *in,
union wfd_sube *out);
int wfd_video_formats_from_string(const char *l,
struct wfd_video_formats **out);
static inline void wfd_video_formats_free(struct wfd_video_formats *f) { free(f); }
int wfd_video_formats_to_string(struct wfd_video_formats *f, char **out);
int wfd_audio_codecs_from_string(const char *l,
struct wfd_audio_codecs **out);
static inline void wfd_audio_codecs_free(struct wfd_audio_codecs *c) { free(c); }
int wfd_audio_codecs_to_string(struct wfd_audio_codecs *c, char **out);
int wfd_audio_format_from_string(const char *s, enum wfd_audio_format *f);
const char * wfd_audio_format_to_string(enum wfd_audio_format f);
static inline int wfd_sube_device_get_type(const union wfd_sube *sube)
{
assert(wfd_sube_is_device_info(sube));
return (WFD_DEVINFO_DEV_TYPE_MASK & sube->dev_info.dev_info);
}
static inline bool wfd_sube_device_is_source(const union wfd_sube *sube)
{
switch(wfd_sube_device_get_type(sube)) {
case 0:
case 3:
return true;
}
return false;
}
static inline bool wfd_sube_device_is_sink(const union wfd_sube *sube)
{
switch(wfd_sube_device_get_type(sube)) {
case 1:
case 2:
case 3:
return true;
}
return false;
}
static inline int wfd_sube_src_support_coupled_sink(const union wfd_sube *sube)
{
assert(WFD_SUBE_ID_DEVICE_INFO == sube->id);
return !!(WFD_DEVINFO_SRC_COUPLED_SINK_MASK & sube->dev_info.dev_info);
}
static inline uint16_t wfd_sube_device_get_rtsp_port(const union wfd_sube *sube)
{
assert(wfd_sube_is_device_info(sube));
return sube->dev_info.rtsp_port;
}
#endif /* WFD_H */

View file

@ -214,10 +214,8 @@ static int cmd_set_friendly_name(char **args, unsigned int n)
return 0;
}
if (!l->managed) {
cli_printf("link %s not managed\n", l->label);
return 0;
}
if (!l->managed)
return log_EUNMANAGED();
return ctl_link_set_friendly_name(l, name);
}
@ -289,10 +287,8 @@ static int cmd_p2p_scan(char **args, unsigned int n)
return 0;
}
if (!l->managed) {
cli_printf("link %s not managed\n", l->label);
return 0;
}
if (!l->managed)
return log_EUNMANAGED();
return ctl_link_set_p2p_scanning(l, !stop);
}
@ -341,10 +337,8 @@ static int cmd_connect(char **args, unsigned int n)
pin = "";
}
if (!p->l->managed) {
cli_printf("link %s not managed\n", p->l->label);
return 0;
}
if (!p->l->managed)
return log_EUNMANAGED();
return ctl_peer_connect(p, prov, pin);
}
@ -368,10 +362,8 @@ static int cmd_disconnect(char **args, unsigned int n)
return 0;
}
if (!p->l->managed) {
cli_printf("link %s not managed\n", p->l->label);
return 0;
}
if (!p->l->managed)
return log_EUNMANAGED();
return ctl_peer_disconnect(p);
}

View file

@ -456,7 +456,7 @@ static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
packet->gateway_nip = client_packet->gateway_nip;
packet->ciaddr = client_packet->ciaddr;
dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
dhcp_server->server_nip);
get_be32(&dhcp_server->server_nip));
}
static void add_option(gpointer key, gpointer value, gpointer user_data)
@ -675,7 +675,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
if (server_id_option) {
uint32_t server_nid = get_be32(server_id_option);
uint32_t server_nid = get_unaligned((uint32_t *) server_id_option);
if (server_nid != dhcp_server->server_nip)
return TRUE;

37
src/disp/CMakeLists.txt Normal file
View file

@ -0,0 +1,37 @@
set(miracle-dispd_SRCS
../ctl/ctl-cli.c
../ctl/ctl-wifi.c
../ctl/wfd.c
dispd-sink.c
dispd-dbus.c
dispd-session.c
dispd-out-session.c
dispd.c
dispd-encoder.c
dispd-arg.c
)
include_directories(${CMAKE_SOURCE_DIR}/src/ctl
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/shared
)
add_executable(miracle-dispd ${miracle-dispd_SRCS})
install(TARGETS miracle-dispd DESTINATION bin)
if(READLINE_FOUND)
message(STATUS "Compiling with Readline support")
set_property(TARGET miracle-dispd
APPEND
PROPERTY COMPILE_DEFINITIONS HAVE_READLINE
)
target_link_libraries(miracle-dispd ${READLINE_LIBRARY})
endif(READLINE_FOUND)
target_link_libraries(miracle-dispd
miracle-shared
${READLINE_LIBRARY}
)

61
src/disp/dispd-arg.c Normal file
View file

@ -0,0 +1,61 @@
/*
* MiracleCast - Wifi-Display/Miracast Implementation
*
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
*
* MiracleCast is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* MiracleCast is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <assert.h>
#include <string.h>
#include "dispd-arg.h"
int dispd_arg_list_new(struct dispd_arg_list **out)
{
assert(out);
struct dispd_arg_list *l = calloc(1, sizeof(struct dispd_arg_list));
if(!l) {
return -ENOMEM;
}
l->dynamic = true;
*out = l;
return 0;
}
void dispd_arg_list_clear(struct dispd_arg_list *l)
{
int i;
struct dispd_arg *arg;
if(!l || !l->dynamic) {
return;
}
arg = l->discrete ? l->argv : l->args;
for(i = 0; i < l->len; i ++) {
if((DISPD_ARG_STR == arg->type || DISPD_ARG_PTR == arg->type)
&& arg->ptr && arg->free) {
(*arg->free)(arg->ptr);
}
}
if(l->discrete) {
free(l->argv);
}
}

242
src/disp/dispd-arg.h Normal file
View file

@ -0,0 +1,242 @@
/*
* 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 <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "shl_macro.h"
#ifndef DISPD_ARG_H
#define DISPD_ARG_H
#define dispd_arg_i8(_v) { .type = DISPD_ARG_I8, .i8 = (_v) }
#define dispd_arg_u8(_v) { .type = DISPD_ARG_U8, .u8 = (_v) }
#define dispd_arg_i16(_v) { .type = DISPD_ARG_I16, .i16 = (_v) }
#define dispd_arg_u16(_v) { .type = DISPD_ARG_U16, .u16 = (_v) }
#define dispd_arg_i32(_v) { .type = DISPD_ARG_I32, .i32 = (_v) }
#define dispd_arg_u32(_v) { .type = DISPD_ARG_U32, .u32 = (_v) }
#define dispd_arg_i64(_v) { .type = DISPD_ARG_I64, .i64 = (_v) }
#define dispd_arg_u64(_v) { .type = DISPD_ARG_U64, .u64 = (_v) }
#define dispd_arg_cstr(_v) { .type = DISPD_ARG_CSTR, .ptr = (_v) }
#define dispd_arg_cptr(_v) { .type = DISPD_ARG_CPTR, .ptr = (_v) }
#define dispd_arg_arg_list(_v) { \
.type = DISPD_ARG_ARG_LIST, \
.ptr = &(struct dispd_arg_list) dispd_arg_list(_v) \
}
#define dispd_arg_dict(_k, _v) { \
.type = DISPD_ARG_DICT, \
.k = (struct dispd_arg[]){_k}, \
.v = (struct dispd_arg[]){_v} \
}
#if INT_MAX == INT64_MAX
#define dispd_arg_i(_v) dispd_arg_i64(_v)
#define dispd_arg_u(_v) dispd_arg_u64(_v)
#elif INT_MAX == INT32_MAX
#define dispd_arg_i(_v) dispd_arg_i32(_v)
#define dispd_arg_u(_v) dispd_arg_u32(_v)
#else
#error unsupported int size
#endif
#define dispd_arg_type_id(_t) _Generic((_t), \
int8_t: DISPD_ARG_I8, \
uint8_t: DISPD_ARG_U8, \
int16_t: DISPD_ARG_I16, \
uint16_t: DISPD_ARG_U16, \
int32_t: DISPD_ARG_I32, \
uint32_t: DISPD_ARG_U32, \
int64_t: DISPD_ARG_I64, \
uint64_t: DISPD_ARG_U64, \
const char *: DISPD_ARG_CSTR, \
const dispd_arg_list *: DISPD_ARG_ARG_LIST, \
char *: DISPD_ARG_STR, \
void *: DISPD_ARG_PTR, \
default: DISPD_ARG_CPTR \
)
#define dispd_arg_list(...) { \
.argv = (struct dispd_arg[]) { \
__VA_ARGS__ \
}, \
.discrete = true, \
.dynamic = false, \
.len = (sizeof((struct dispd_arg[]){ __VA_ARGS__ })/sizeof(struct dispd_arg)) \
}
#define dispd_arg_get(_a, _v) ({ \
*(_v) = _Generic(*(_v), \
int8_t: dispd_arg_get_i8, \
uint8_t: dispd_arg_get_u8, \
int16_t: dispd_arg_get_i16, \
uint16_t: dispd_arg_get_u16, \
int32_t: dispd_arg_get_i32, \
uint32_t: dispd_arg_get_u32, \
int64_t: dispd_arg_get_i64, \
uint64_t: dispd_arg_get_u64, \
char *: dispd_arg_get_str, \
const struct dispd_arg_list *: dispd_arg_get_arg_list, \
const char *: dispd_arg_get_cstr, \
void *: dispd_arg_get_ptr, \
default: dispd_arg_get_cptr \
)(_a); \
})
#define dispd_arg_get_dictk(_a, _k) ({ \
assert(_a); \
assert(DISPD_ARG_DICT == (_a)->type); \
dispd_arg_get((_a)->k, (_k)); \
})
#define dispd_arg_get_dictv(_a, _v) ({ \
assert(_a); \
assert(DISPD_ARG_DICT == (_a)->type); \
dispd_arg_get((_a)->v, (_v)); \
})
#define dispd_arg_get_dict(_a, _k, _v) ({ \
assert(_a); \
assert(DISPD_ARG_DICT == (_a)->type); \
dispd_arg_get_dictk(_a, _k); \
dispd_arg_get_dictv(_a, _v); \
})
#define dispd_arg_list_get(_l, _i, _v) ({ \
dispd_arg_get(dispd_arg_list_at((_l), (_i)), (_v)); \
})
#define dispd_arg_list_get_dictk(_l, _i, _k) ({ \
dispd_arg_get_dictk(dispd_arg_list_at((_l), (_i)), (_k)); \
})
#define dispd_arg_list_get_dictv(_l, _i, _v) ({ \
dispd_arg_get_dictv(dispd_arg_list_at((_l), (_i)), (_v)); \
})
#define dispd_arg_list_get_dict(_l, _i, _k, _v) ({ \
dispd_arg_get_dict(dispd_arg_list_at((_l), (_i)), (_k), (_v)); \
})
enum dispd_arg_type
{
DISPD_ARG_NONE,
DISPD_ARG_I8,
DISPD_ARG_I16,
DISPD_ARG_I32,
DISPD_ARG_I64,
DISPD_ARG_U8,
DISPD_ARG_U16,
DISPD_ARG_U32,
DISPD_ARG_U64,
DISPD_ARG_STR,
DISPD_ARG_CSTR,
DISPD_ARG_PTR,
DISPD_ARG_CPTR,
DISPD_ARG_DICT,
DISPD_ARG_ARG_LIST,
};
struct dispd_arg
{
enum dispd_arg_type type;
union
{
int8_t i8;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
uint32_t u32;
int64_t i64;
uint64_t u64;
struct {
void *ptr;
void (*free)(void *);
};
struct {
struct dispd_arg *k;
struct dispd_arg *v;
};
};
};
struct dispd_arg_list
{
size_t len: sizeof(size_t) - 2;
bool discrete: 1;
bool dynamic: 1;
union {
struct dispd_arg * argv;
struct dispd_arg args[0];
};
};
int dispd_arg_list_new(struct dispd_arg_list **out);
void dispd_arg_list_clear(struct dispd_arg_list *l);
static inline void dispd_arg_list_free(struct dispd_arg_list *l);
static inline const struct dispd_arg * dispd_arg_list_at(const struct dispd_arg_list *l,
int i);
static inline enum dispd_arg_type dispd_arg_get_type(struct dispd_arg *a);
static inline void dispd_arg_free_ptr(struct dispd_arg *a);
static inline void dispd_arg_clear(struct dispd_arg *a);
static inline int8_t dispd_arg_get_i8(const struct dispd_arg *a);
static inline void dispd_arg_set_i8(struct dispd_arg *a, int8_t v);
static inline uint8_t dispd_arg_get_u8(const struct dispd_arg *a);
static inline void dispd_arg_set_u8(struct dispd_arg *a, uint8_t v);
static inline int16_t dispd_arg_get_i16(const struct dispd_arg *a);
static inline void dispd_arg_set_i16(struct dispd_arg *a, int16_t v);
static inline uint16_t dispd_arg_get_u16(const struct dispd_arg *a);
static inline void dispd_arg_set_u16(struct dispd_arg *a, uint16_t v);
static inline int32_t dispd_arg_get_i32(const struct dispd_arg *a);
static inline void dispd_arg_set_i32(struct dispd_arg *a, int32_t v);
static inline uint32_t dispd_arg_get_u32(const struct dispd_arg *a);
static inline void dispd_arg_set_u32(struct dispd_arg *a, uint32_t v);
static inline int64_t dispd_arg_get_i64(const struct dispd_arg *a);
static inline void dispd_arg_set_i64(struct dispd_arg *a, int64_t v);
static inline uint64_t dispd_arg_get_u64(const struct dispd_arg *a);
static inline void dispd_arg_set_u64(struct dispd_arg *a, uint64_t v);
static inline const char * dispd_arg_get_cstr(const struct dispd_arg *a);
static inline void dispd_arg_set_cstr(struct dispd_arg *a, const char * v);
static inline void dispd_arg_take_str(struct dispd_arg *a, char *v);
static inline char * dispd_arg_get_str(const struct dispd_arg *a);
static inline int dispd_arg_set_str(struct dispd_arg *a, const char *v);;
static inline const void * dispd_arg_get_cptr(const struct dispd_arg *a);
static inline void dispd_arg_set_cptr(struct dispd_arg *a, const void * v);
static inline void dispd_arg_take_ptr(struct dispd_arg *a, void *v, void (*f)(void *));
static inline void * dispd_arg_get_ptr(const struct dispd_arg *a);
static inline void dispd_arg_take_arg_list(struct dispd_arg *a, struct dispd_arg_list *l);
static inline const struct dispd_arg_list * dispd_arg_get_arg_list(const struct dispd_arg *a);
#include "dispd-arg.inc"
#endif /* DISPD_ARG_H */

192
src/disp/dispd-arg.inc Normal file
View file

@ -0,0 +1,192 @@
/*
* 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/>.
*/
#define dispd_arg_getter(_t, _s, _S) \
static inline _t dispd_arg_get_##_s(const struct dispd_arg *a) \
{ \
assert(a); \
assert(DISPD_ARG_##_S == a->type); \
return a->_s; \
}
#define dispd_arg_setter(_t, _s, _S) \
static inline void dispd_arg_set_##_s(struct dispd_arg *a, _t v) \
{ \
assert(a); \
assert(!a->type || DISPD_ARG_##_S == a->type); \
*a = (struct dispd_arg) { .type = DISPD_ARG_##_S, ._s = v }; \
}
dispd_arg_getter(int8_t, i8, I8)
dispd_arg_setter(int8_t, i8, I8)
dispd_arg_getter(uint8_t, u8, U8)
dispd_arg_setter(uint8_t, u8, U8)
dispd_arg_getter(int16_t, i16, I16)
dispd_arg_setter(int16_t, i16, I16)
dispd_arg_getter(uint16_t, u16, U16)
dispd_arg_setter(uint16_t, u16, U16)
dispd_arg_getter(int32_t, i32, I32)
dispd_arg_setter(int32_t, i32, I32)
dispd_arg_getter(uint32_t, u32, U32)
dispd_arg_setter(uint32_t, u32, U32)
dispd_arg_getter(int64_t, i64, I64)
dispd_arg_setter(int64_t, i64, I64)
dispd_arg_getter(uint64_t, u64, U64)
dispd_arg_setter(uint64_t, u64, U64)
static inline void dispd_arg_list_free(struct dispd_arg_list *l)
{
dispd_arg_list_clear(l);
free(l);
}
static inline const struct dispd_arg * dispd_arg_list_at(const struct dispd_arg_list *l,
int i)
{
assert(l);
assert(i >= 0 && i < l->len);
return l->discrete ? &l->argv[i] : &l->args[i];
}
static inline enum dispd_arg_type dispd_arg_get_type(struct dispd_arg *a)
{
assert(a);
return a->type;
}
static inline void dispd_arg_free_ptr(struct dispd_arg *a)
{
if(!a || (DISPD_ARG_STR != a->type && DISPD_ARG_PTR != a->type)) {
return;
}
if(a->ptr && a->free) {
(*a->free)(a->ptr);
}
}
static inline void dispd_arg_clear(struct dispd_arg *a)
{
if(a) {
dispd_arg_free_ptr(a);
memset(a, 0, sizeof(*a));
}
}
static inline const char * dispd_arg_get_cstr(const struct dispd_arg *a)
{
assert(a);
assert(DISPD_ARG_CSTR == a->type || DISPD_ARG_STR == a->type);
return a->ptr;
}
static inline void dispd_arg_set_cstr(struct dispd_arg *a, const char * v)
{
assert(a);
assert(!a->type || DISPD_ARG_CSTR == a->type);
*a = (struct dispd_arg) { .type = DISPD_ARG_CSTR, .ptr = (void *) v };
}
static inline char * dispd_arg_get_str(const struct dispd_arg *a)
{
assert(a);
assert(DISPD_ARG_STR == a->type);
return a->ptr;
}
static inline void dispd_arg_take_str(struct dispd_arg *a, char *v)
{
assert(a);
assert(!a->type || DISPD_ARG_STR == a->type || DISPD_ARG_CSTR == a->type);
dispd_arg_free_ptr(a);
*a = (struct dispd_arg) { .type = DISPD_ARG_STR, .ptr = v, .free = free };
}
static inline int dispd_arg_set_str(struct dispd_arg *a, const char *v)
{
char *s;
assert(a);
assert(!a->type || DISPD_ARG_STR == a->type);
s = strdup(v);
if(!s) {
return -ENOMEM;
}
dispd_arg_take_str(a, s);
return 0;
}
static inline const void * dispd_arg_get_cptr(const struct dispd_arg *a)
{
assert(a);
assert(DISPD_ARG_PTR <= a->type && DISPD_ARG_CPTR == a->type);
return a->ptr;
}
static inline void dispd_arg_set_cptr(struct dispd_arg *a, const void * v)
{
assert(a);
assert(!a->type || DISPD_ARG_CSTR == a->type);
*a = (struct dispd_arg) { .type = DISPD_ARG_CPTR, .ptr = (void *) v };
}
static inline void * dispd_arg_get_ptr(const struct dispd_arg *a)
{
assert(a);
assert(DISPD_ARG_PTR == a->type || DISPD_ARG_STR == a->type);
return a->ptr;
}
static inline void dispd_arg_take_ptr(struct dispd_arg *a, void *v, void (*f)(void *))
{
assert(a);
assert(!a->type || DISPD_ARG_PTR == a->type);
dispd_arg_free_ptr(a);
*a = (struct dispd_arg) { .type = DISPD_ARG_PTR, .ptr = v, .free = f };
}
static inline void dispd_arg_take_arg_list(struct dispd_arg *a, struct dispd_arg_list *l)
{
assert(a);
assert(!a->type || DISPD_ARG_ARG_LIST == a->type);
dispd_arg_free_ptr(a);
*a = (struct dispd_arg) { .type = DISPD_ARG_ARG_LIST,
.ptr = l,
.free = (void (*)(void *)) dispd_arg_list_free };
}
static inline const struct dispd_arg_list * dispd_arg_get_arg_list(const struct dispd_arg *a)
{
assert(a);
assert(DISPD_ARG_ARG_LIST == a->type);
return a->ptr;
}

943
src/disp/dispd-dbus.c Normal file
View file

@ -0,0 +1,943 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include <systemd/sd-bus.h>
#include "dispd.h"
#include "util.h"
#include "shl_log.h"
#include "dispd-dbus.h"
#define dispd_dbus_object_added(o, argv...) ({ \
const char *ifaces[] = { argv }; \
_dispd_dbus_object_added(dispd_dbus_get(), \
(o), \
ifaces, \
SHL_ARRAY_LENGTH(ifaces)); \
})
#define dispd_dbus_object_removed(o, argv...) ({ \
const char *ifaces[] = { argv }; \
_dispd_dbus_object_removed(dispd_dbus_get(), \
(o), \
ifaces, \
SHL_ARRAY_LENGTH(ifaces)); \
})
struct dispd_dbus
{
sd_bus *bus;
sd_event *loop;
bool exposed : 1;
};
int dispd_dbus_new(struct dispd_dbus **out, sd_event *loop, sd_bus *bus)
{
struct dispd_dbus *dispd_dbus = calloc(1, sizeof(struct dispd_dbus));
if(!dispd_dbus) {
return log_ENOMEM();
}
dispd_dbus->bus = sd_bus_ref(bus);
dispd_dbus->loop = sd_event_ref(loop);
*out = dispd_dbus;
return 0;
}
void dispd_dbus_free(struct dispd_dbus *dispd_dbus)
{
if(!dispd_dbus) {
return;
}
if(dispd_dbus->exposed) {
sd_bus_release_name(dispd_dbus->bus, "org.freedesktop.miracle.wfd");
}
if(dispd_dbus->bus) {
sd_bus_unref(dispd_dbus->bus);
}
if(dispd_dbus->loop) {
sd_event_unref(dispd_dbus->loop);
}
free(dispd_dbus);
}
static inline int dispd_dbus_get_sink_path(struct dispd_sink *s, char **out)
{
int r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/sink",
dispd_sink_get_label(s),
out);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
static inline int dispd_dbus_get_session_path(struct dispd_session *s, char **out)
{
char buf[64];
int r = snprintf(buf, sizeof(buf), "%u", dispd_session_get_id(s));
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/session",
buf,
out);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
static int dispd_dbus_enum(sd_bus *bus,
const char *path,
void *userdata,
char ***out,
sd_bus_error *out_error)
{
int r = 0, i = 0;
char **nodes, *node;
struct dispd_sink *sink;
struct dispd_session *session;
struct dispd *dispd = dispd_get();
if(strcmp("/org/freedesktop/miracle/wfd", path)) {
return 0;
}
if(!dispd->n_sinks) {
return 0;
}
nodes = malloc((dispd->n_sinks + dispd->n_sessions + 1) * sizeof(char *));
if(!nodes) {
return log_ENOMEM();
}
dispd_foreach_sink(sink, dispd) {
r = dispd_dbus_get_sink_path(sink, &node);
if(0 > r) {
goto free_nodes;
}
nodes[i ++] = node;
}
dispd_foreach_session(session, dispd) {
r = dispd_dbus_get_session_path(session, &node);
if(0 > r) {
goto free_nodes;
}
nodes[i ++] = node;
}
nodes[i ++] = NULL;
*out = nodes;
return 0;
free_nodes:
while(i --) {
free(nodes[i]);
}
free(nodes);
return log_ERRNO();
}
int _dispd_dbus_object_removed(struct dispd_dbus *dispd_dbus,
const char *path,
const char **ifaces,
size_t n_ifaces)
{
int i, r;
_sd_bus_message_unref_ sd_bus_message *m = NULL;
if(!dispd_dbus) {
return -ECANCELED;
}
r = sd_bus_message_new_signal(dispd_dbus->bus,
&m,
"/org/freedesktop/miracle/wfd",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved");
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_append(m, "o", path);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_open_container(m, 'a', "s");
if(0 > r) {
return log_ERRNO();
}
for(i = 0; i < n_ifaces; i ++) {
r = sd_bus_message_append(m, "s", ifaces[i]);
if(0 > r) {
return log_ERRNO();
}
}
r = sd_bus_message_close_container(m);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_send(dispd_dbus->bus, m, NULL);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
int _dispd_dbus_object_added(struct dispd_dbus *dispd_dbus,
const char *path,
const char **ifaces,
size_t n_ifaces)
{
int i, r;
_sd_bus_message_unref_ sd_bus_message *m = NULL;
if(!dispd_dbus) {
return -ECANCELED;
}
r = sd_bus_message_new_signal(dispd_dbus->bus,
&m,
"/org/freedesktop/miracle/wfd",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded");
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_append(m, "o", path);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
if(0 > r) {
return log_ERRNO();
}
for(i = 0; i < n_ifaces; i ++) {
r = sd_bus_message_append(m, "{sa{sv}}", ifaces[i], 0);
if(0 > r) {
return log_ERRNO();
}
}
r = sd_bus_message_close_container(m);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_send(dispd_dbus->bus, m, NULL);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
int dispd_fn_sink_new(struct dispd_sink *s)
{
_shl_free_ char *path = NULL;
int r = dispd_dbus_get_sink_path(s, &path);
if(0 > r) {
return log_ERRNO();
}
r = dispd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Sink");
if(0 > r) {
return log_ERRNO();
}
return 0;
}
int dispd_fn_sink_free(struct dispd_sink *s)
{
_shl_free_ char *path = NULL;
int r = dispd_dbus_get_sink_path(s, &path);
if(0 > r) {
return log_ERRNO();
}
r = dispd_dbus_object_removed(path, "org.freedesktop.miracle.wfd.Sink");
if(0 > r) {
return log_ERRNO();
}
return 0;
}
int _dispd_fn_sink_properties_changed(struct dispd_sink *s, char **names)
{
_shl_free_ char *path = NULL;
int r;
struct dispd_dbus *dispd_dbus = dispd_dbus_get();
if(!dispd_dbus) {
return log_ERR(-ECANCELED);
}
r = dispd_dbus_get_sink_path(s, &path);
if(0 > r) {
return log_ERR(r);
}
r = sd_bus_emit_properties_changed_strv(dispd_dbus->bus,
path,
"org.freedesktop.miracle.wfd.Sink",
names);
if(0 > r) {
return log_ERR(r);
}
return 0;
}
static int dispd_dbus_find_sink(sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **ret_found,
sd_bus_error *ret_error)
{
_shl_free_ char *node = NULL;
struct dispd_sink *sink;
int r = sd_bus_path_decode(path,
"/org/freedesktop/miracle/wfd/sink",
&node);
if(0 >= r || !node) {
return r;
}
r = dispd_find_sink_by_label(dispd_get(), node, &sink);
if(r) {
*ret_found = sink;
}
return r;
}
int dispd_fn_session_new(struct dispd_session *s)
{
_shl_free_ char *path = NULL;
int r = dispd_dbus_get_session_path(s, &path);
if(0 > r) {
return log_ERRNO();
}
r = dispd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Session");
if(0 > r) {
return log_ERRNO();
}
return 0;
}
int dispd_fn_session_free(struct dispd_session *s)
{
_shl_free_ char *path = NULL;
int r = dispd_dbus_get_session_path(s, &path);
if(0 > r) {
return log_ERRNO();
}
r = dispd_dbus_object_removed(path, "org.freedesktop.miracle.wfd.Session");
if(0 > r) {
return log_ERRNO();
}
return 0;
}
static int dispd_dbus_find_session(sd_bus *bus,
const char *path,
const char *interface,
void *userdata,
void **ret_found,
sd_bus_error *ret_error)
{
struct dispd_session *s;
_shl_free_ char *node = NULL;
int r = sd_bus_path_decode(path,
"/org/freedesktop/miracle/wfd/session",
&node);
if(0 > r) {
return log_ERRNO();
}
r = dispd_find_session_by_id(dispd_get(),
strtoull(node, NULL, 10),
&s);
if(r) {
*ret_found = s;
}
return r;
}
static int get_user_runtime_path(char **out, sd_bus *bus, uid_t uid)
{
_sd_bus_message_unref_ sd_bus_message *rep = NULL;
_sd_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
char p[256];
char *rp;
int r;
assert_ret(out);
assert_ret(bus);
assert_ret(0 < uid);
r = snprintf(p, sizeof(p), "/org/freedesktop/login1/user/_%d", uid);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_call_method(bus,
"org.freedesktop.login1",
p,
"org.freedesktop.DBus.Properties",
"Get",
&error,
&rep,
"ss",
"org.freedesktop.login1.User",
"RuntimePath");
if(0 > r) {
log_warning("%s: %s", error.name, error.message);
return r;
}
r = sd_bus_message_read(rep, "v", "s", &rp);
if(0 > r) {
return log_ERR(r);
}
*out = strdup(rp);
if(!*out) {
return log_ENOMEM();
}
return 0;
}
static int dispd_dbus_sink_start_session(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
_dispd_session_unref_ struct dispd_session *sess = NULL;
_shl_free_ char *path = NULL, *disp_type_name = NULL, *disp_name = NULL;
_sd_bus_creds_unref_ sd_bus_creds *creds = NULL;
_shl_free_ char *runtime_path = NULL;
struct dispd_sink *sink = userdata;
char *disp_params;
const char *disp, *disp_auth;
const char *audio_dev;
struct dispd_rectangle rect;
pid_t pid;
uid_t uid;
gid_t gid;
int r;
r = sd_bus_message_read(m,
"ssuuuus",
&disp_auth,
&disp,
&rect.x,
&rect.y,
&rect.width,
&rect.height,
&audio_dev);
if(0 > r) {
return log_ERR(r);
}
r = sscanf(disp, "%m[^:]://%ms",
&disp_type_name,
&disp_name);
if(r != 2) {
return log_EINVAL();
}
if(strcmp("x", disp_type_name)) {
return log_EINVAL();
}
r = dispd_sink_create_session(sink, &sess);
if(0 > r) {
return log_ERR(r);
}
dispd_session_set_disp_type(sess, DISPD_DISPLAY_SERVER_TYPE_X);
if(0 > r) {
return log_ERR(r);
}
disp_params = strchr(disp_name, '?');
if(disp_params) {
*disp_params ++ = '\0';
}
r = dispd_session_set_disp_name(sess, disp_name);
if(0 > r) {
return log_ERR(r);
}
r = dispd_session_set_disp_params(sess, disp_params);
if(0 > r) {
return log_ERR(r);
}
r = dispd_session_set_disp_auth(sess, disp_auth);
if(0 > r) {
return log_ERR(r);
}
r = dispd_session_set_disp_dimension(sess, &rect);
if(0 > r) {
return log_ERR(r);
}
if(*audio_dev) {
r = dispd_session_set_audio_type(sess, DISPD_AUDIO_SERVER_TYPE_PULSE_AUDIO);
if(0 > r) {
return log_ERR(r);
}
r = dispd_session_set_audio_dev_name(sess, audio_dev);
if(0 > r) {
return log_ERR(r);
}
}
r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_PID, &creds);
if(0 > r) {
return log_ERR(r);
}
r = sd_bus_creds_get_pid(creds, &pid);
if(0 > r) {
return log_ERR(r);
}
sd_bus_creds_unref(creds);
creds = NULL;
r = sd_bus_creds_new_from_pid(&creds,
pid,
SD_BUS_CREDS_UID | SD_BUS_CREDS_GID);
if(0 > r) {
return log_ERR(r);
}
dispd_session_set_client_pid(sess, gid);
r = sd_bus_creds_get_uid(creds, &uid);
if(0 > r) {
return log_ERR(r);
}
dispd_session_set_client_uid(sess, uid);
sd_bus_creds_get_gid(creds, &gid);
if(0 > r) {
return log_ERR(r);
}
dispd_session_set_client_gid(sess, gid);
r = get_user_runtime_path(&runtime_path,
sd_bus_message_get_bus(m),
uid);
if(0 > r) {
return log_ERR(r);
}
r = dispd_session_set_runtime_path(sess, runtime_path);
if(0 > r) {
return log_ERR(r);
}
r = dispd_session_start(sess);
if(0 > r) {
return log_ERR(r);
}
r = dispd_dbus_get_session_path(sess, &path);
if(0 > r) {
return log_ERR(r);
}
r = sd_bus_reply_method_return(m, "o", path);
if(0 > r) {
return log_ERR(r);
}
return 0;
}
static int dispd_dbus_sink_get_session(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_sink *s = userdata;
_shl_free_ char *session_path = NULL;
int r;
if(s->session) {
r = dispd_dbus_get_session_path(s->session, &session_path);
if(0 > r) {
return log_ERRNO();
}
}
else {
session_path = strdup("/");
if(!session_path) {
return log_ENOMEM();
}
}
r = sd_bus_message_append(reply, "o", session_path);
if(0 > r) {
return log_ERRNO();
}
return 1;
}
static int dispd_dbus_sink_get_peer(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_sink *s = userdata;
_shl_free_ char *peer_path = NULL;
int r = sd_bus_path_encode("/org/freedesktop/miracle/wifi/peer",
s->label,
&peer_path);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_append(reply, "o", peer_path);
if(0 > r) {
return log_ERRNO();
}
return 1;
}
//static int dispd_dbus_sink_has_audio(sd_bus *bus,
// const char *path,
// const char *interface,
// const char *property,
// sd_bus_message *reply,
// void *userdata,
// sd_bus_error *ret_error)
//{
// return 0;
//}
static int dispd_dbus_session_resume(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_session *s = userdata;
int r;
if(dispd_session_is_established(s)) {
r = dispd_session_resume(s);
if(0 > r) {
return log_ERRNO();
}
}
else {
return -ENOTCONN;
}
r = sd_bus_reply_method_return(m, NULL);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
static int dispd_dbus_session_pause(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_session *s = userdata;
int r;
if(dispd_session_is_established(s)) {
r = dispd_session_pause(s);
if(0 > r) {
return log_ERRNO();
}
}
else {
return -ENOTCONN;
}
r = sd_bus_reply_method_return(m, NULL);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
static int dispd_dbus_session_teardown(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_session *s = userdata;
int r = 0;
if(dispd_session_is_established(s)) {
r = dispd_session_teardown(s);
}
if(0 > r) {
dispd_session_destroy(s);
}
r = sd_bus_reply_method_return(m, NULL);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
static int dispd_dbus_session_get_sink(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_session *s = userdata;
_shl_free_ char *sink_path = NULL;
int r;
if(dispd_session_get_dir(s) != DISPD_SESSION_DIR_OUT) {
sink_path = strdup("/");
}
else {
dispd_dbus_get_sink_path(dispd_out_session_get_sink(s), &sink_path);
}
if(!sink_path) {
return log_ENOMEM();
}
r = sd_bus_message_append(reply, "o", sink_path);
if(0 > r) {
return log_ERRNO();
}
return 1;
}
static int dispd_dbus_get_session_presentation_url(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_session *s = userdata;
int r = sd_bus_message_append(reply,
"s",
dispd_session_get_stream_url(s) ? : "");
if(0 > r) {
return log_ERRNO();
}
return 1;
}
static int dispd_dbus_get_session_state(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_session *s = userdata;
int r = sd_bus_message_append(reply, "i", dispd_session_get_state(s));
if(0 > r) {
return log_ERRNO();
}
return 1;
}
int _dispd_fn_session_properties_changed(struct dispd_session *s, char **names)
{
_shl_free_ char *path = NULL;
int r;
struct dispd_dbus *dispd_dbus = dispd_dbus_get();
if(!dispd_dbus) {
return -ECANCELED;
}
r = dispd_dbus_get_session_path(s, &path);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_emit_properties_changed_strv(dispd_dbus_get()->bus,
path,
"org.freedesktop.miracle.wfd.Session",
names);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
static int dispd_dbus_shutdown(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
int r;
dispd_shutdown(dispd_get());
r = sd_bus_reply_method_return(m, NULL);
if(0 > r) {
return log_ERR(r);
}
return 0;
}
static const sd_bus_vtable dispd_dbus_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("Shutdown", NULL, NULL, dispd_dbus_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
static const sd_bus_vtable dispd_dbus_sink_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("StartSession", "ssuuuus", "o", dispd_dbus_sink_start_session, SD_BUS_VTABLE_UNPRIVILEGED),
/*SD_BUS_PROPERTY("AudioFormats", "a{sv}", dispd_dbus_sink_get_audio_formats, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
/*SD_BUS_PROPERTY("VideoFormats", "a{sv}", dispd_dbus_sink_get_video_formats, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
/*SD_BUS_PROPERTY("HasAudio", "b", dispd_dbus_sink_has_audio, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
/*SD_BUS_PROPERTY("HasVideo", "b", dispd_dbus_sink_has_video, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
SD_BUS_PROPERTY("Session", "o", dispd_dbus_sink_get_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Peer", "o", dispd_dbus_sink_get_peer, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END,
};
static const sd_bus_vtable dispd_dbus_session_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("Resume", NULL, NULL, dispd_dbus_session_resume, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Pause", NULL, NULL, dispd_dbus_session_pause, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Teardown", NULL, NULL, dispd_dbus_session_teardown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("Sink", "o", dispd_dbus_session_get_sink, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Url", "s", dispd_dbus_get_session_presentation_url, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("State", "i", dispd_dbus_get_session_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_END,
};
int dispd_dbus_expose(struct dispd_dbus *dispd_dbus)
{
int r = sd_bus_add_object_vtable(dispd_dbus->bus,
NULL,
"/org/freedesktop/miracle/wfd",
"org.freedesktop.miracle.wfd",
dispd_dbus_vtable,
dispd_dbus);
if(0 > r) {
return r;
}
r = sd_bus_add_fallback_vtable(dispd_dbus->bus,
NULL,
"/org/freedesktop/miracle/wfd/sink",
"org.freedesktop.miracle.wfd.Sink",
dispd_dbus_sink_vtable,
dispd_dbus_find_sink,
dispd_dbus);
if(0 > r) {
return r;
}
r = sd_bus_add_fallback_vtable(dispd_dbus->bus,
NULL,
"/org/freedesktop/miracle/wfd/session",
"org.freedesktop.miracle.wfd.Session",
dispd_dbus_session_vtable,
dispd_dbus_find_session,
dispd_dbus);
if(0 > r) {
return r;
}
r = sd_bus_add_node_enumerator(dispd_dbus->bus,
NULL,
"/org/freedesktop/miracle/wfd",
dispd_dbus_enum,
dispd_dbus);
if(0 > r) {
return r;
}
r = sd_bus_add_object_manager(dispd_dbus->bus, NULL, "/org/freedesktop/miracle/wfd");
if(0 > r) {
return r;
}
r = sd_bus_request_name(dispd_dbus->bus, "org.freedesktop.miracle.wfd", 0);
if(0 < r) {
dispd_dbus->exposed = true;
}
return r;
}

47
src/disp/dispd-dbus.h Normal file
View file

@ -0,0 +1,47 @@
/*
* 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 <systemd/sd-bus.h>
#ifndef CTL_WFD_DBUS_H
#define CTL_WFD_DBUS_H
#define dispd_fn_sink_properties_changed(s, namev...) ({ \
char *names[] = { namev, NULL }; \
_dispd_fn_sink_properties_changed((s), names); \
})
#define dispd_fn_session_properties_changed(s, namev...) ({ \
char *names[] = { namev, NULL }; \
_dispd_fn_session_properties_changed((s), names); \
})
struct dispd_dbus;
struct dispd_session;
struct dispd_sink;
struct dispd_dbus * dispd_dbus_get();
int dispd_dbus_new(struct dispd_dbus **out, sd_event *loop, sd_bus *bus);
void dispd_dbus_free(struct dispd_dbus *dispd_dbus);
int dispd_dbus_expose(struct dispd_dbus *dispd_dbus);
int _dispd_fn_sink_properties_changed(struct dispd_sink *s, char **names);
int _dispd_fn_session_properties_changed(struct dispd_session *s, char **names);
#endif

982
src/disp/dispd-encoder.c Normal file
View file

@ -0,0 +1,982 @@
/*
* 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 <systemd/sd-event.h>
#include <systemd/sd-bus.h>
#include <sys/prctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <fcntl.h>
#include "dispd-encoder.h"
#include "shl_macro.h"
#include "shl_log.h"
#include "dispd-session.h"
#include "dispd.h"
#include "util.h"
struct dispd_encoder
{
int ref;
sd_event *loop;
sd_event_source *child_source;
sd_event_source *child_term_time_source;
sd_event_source *pipe_source;
sd_bus *bus;
sd_bus_slot *name_disappeared_slot;
sd_bus_slot *state_change_notify_slot;
uid_t bus_owner;
uid_t bus_group;
char *bus_name;
enum dispd_encoder_state state;
dispd_encoder_state_change_handler handler;
void *userdata;
};
static int dispd_encoder_new(struct dispd_encoder **out,
uid_t bus_owner,
gid_t bus_group);
static int on_bus_info_readable(sd_event_source *source,
int fd,
uint32_t events,
void *userdata);
static void dispd_encoder_set_state(struct dispd_encoder *e,
enum dispd_encoder_state state);
static void dispd_encoder_exec(const char *cmd, int fd, struct dispd_session *s)
{
int r;
sigset_t mask;
char disp[16], runtime_path[256];
log_info("child forked with pid %d", getpid());
/* restore to default signal handler */
sigemptyset(&mask);
sigprocmask(SIG_SETMASK, &mask, NULL);
snprintf(disp, sizeof(disp), "DISPLAY=%s", dispd_session_get_disp_name(s));
snprintf(runtime_path, sizeof(runtime_path), "XDG_RUNTIME_DIR=%s", dispd_session_get_runtime_path(s));
/* after encoder connected to DBus, write unique name to fd 3,
* so we can controll it through DBus
*/
r = dup2(fd, 3);
if(0 > r) {
log_vERRNO();
goto error;
}
if(fd != 3) {
close(fd);
}
// TODO drop caps and don't let user raises thier caps
r = setgid(dispd_session_get_client_gid(s));
if(0 > r) {
log_vERRNO();
goto error;
}
r = setuid(dispd_session_get_client_uid(s));
if(0 > r) {
log_vERRNO();
goto error;
}
r = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
if(0 > r) {
log_vERRNO();
goto error;
}
r = execvpe(cmd,
(char *[]){ (char *) cmd, NULL },
(char *[]){ disp,
runtime_path,
"G_MESSAGES_DEBUG=all",
NULL
});
if(0 > r) {
log_vERRNO();
goto error;
}
error:
_exit(1);
}
static void dispd_encoder_close_pipe(struct dispd_encoder *e)
{
if(!e->pipe_source) {
return;
}
close(sd_event_source_get_io_fd(e->pipe_source));
sd_event_source_unref(e->pipe_source);
e->pipe_source = NULL;
dispd_encoder_unref(e);
}
static int dispd_encoder_kill_child(struct dispd_encoder *e)
{
int r;
pid_t pid;
if(!e->child_source) {
return 0;
}
// TODO add timer in case child can't be terminated by SIGTERM
sd_event_source_get_child_pid(e->child_source, &pid);
r = kill(pid, SIGTERM);
if(0 > r) {
return log_ERRNO();
}
return 1;
}
static void dispd_encoder_notify_state_change(struct dispd_encoder *e,
enum dispd_encoder_state state)
{
assert_vret(e);
if(!e->handler) {
return;
}
dispd_encoder_ref(e);
(*e->handler)(e, state, e->userdata);
dispd_encoder_unref(e);
}
static void dispd_encoder_cleanup(struct dispd_encoder *e)
{
if(e->child_source) {
sd_event_source_unref(e->child_source);
e->child_source = NULL;
dispd_encoder_unref(e);
}
if(e->child_term_time_source) {
sd_event_source_unref(e->child_term_time_source);
e->child_term_time_source = NULL;
dispd_encoder_unref(e);
}
if(e->child_source) {
sd_event_source_unref(e->child_source);
e->child_source = NULL;
dispd_encoder_unref(e);
}
dispd_encoder_close_pipe(e);
if(e->name_disappeared_slot) {
sd_bus_slot_unref(e->name_disappeared_slot);
e->name_disappeared_slot = NULL;
dispd_encoder_unref(e);
}
if(e->state_change_notify_slot) {
sd_bus_slot_unref(e->state_change_notify_slot);
e->state_change_notify_slot = NULL;
dispd_encoder_unref(e);
}
}
static int on_child_terminated(sd_event_source *source,
const siginfo_t *si,
void *userdata)
{
struct dispd_encoder *e = userdata;
log_info("encoder process %d terminated", si->si_pid);
dispd_encoder_set_state(e, DISPD_ENCODER_STATE_TERMINATED);
dispd_encoder_cleanup(e);
return 0;
}
int dispd_encoder_spawn(struct dispd_encoder **out, struct dispd_session *s)
{
_dispd_encoder_unref_ struct dispd_encoder *e = NULL;
int fds[2] = { -1, -1 };
pid_t pid;
int r;
assert_ret(out);
assert_ret(s);
r = dispd_encoder_new(&e,
dispd_session_get_client_uid(s),
dispd_session_get_client_gid(s));
if(0 > r) {
goto end;
}
r = pipe2(fds, O_NONBLOCK);
if(0 > r) {
goto end;
}
pid = fork();
if(0 > pid) {
r = log_ERRNO();
goto kill_encoder;
}
else if(!pid) {
close(fds[0]);
dispd_encoder_exec("gstencoder", fds[1], s);
}
r = sd_event_add_child(dispd_get_loop(),
&e->child_source,
pid,
WEXITED,
on_child_terminated,
dispd_encoder_ref(e));
if(0 > r) {
goto close_pipe;
}
r = sd_event_add_io(dispd_get_loop(),
&e->pipe_source,
fds[0],
EPOLLIN,
on_bus_info_readable,
dispd_encoder_ref(e));
if(0 > r) {
goto close_pipe;
}
close(fds[1]);
*out = dispd_encoder_ref(e);
return 0;
close_pipe:
close(fds[0]);
close(fds[1]);
kill_encoder:
// dispd will do the cleanup
kill(pid, SIGKILL);
end:
return log_ERRNO();
}
static int dispd_encoder_new(struct dispd_encoder **out,
uid_t bus_owner,
gid_t bus_group)
{
_dispd_encoder_unref_ struct dispd_encoder *e = NULL;
assert_ret(out);
e = calloc(1, sizeof(struct dispd_encoder));
if(!e) {
return log_ENOMEM();
}
e->ref = 1;
e->bus_owner = bus_owner;
e->bus_group = bus_group;
*out = dispd_encoder_ref(e);
return 0;
}
struct dispd_encoder * dispd_encoder_ref(struct dispd_encoder *e)
{
assert_retv(e, e);
assert_retv(0 < e->ref, e);
++ e->ref;
return e;
}
void dispd_encoder_unrefp(struct dispd_encoder **e)
{
if(*e) {
dispd_encoder_unref(*e);
}
}
void dispd_encoder_unref(struct dispd_encoder *e)
{
assert_vret(e);
assert_vret(0 < e->ref);
--e->ref;
if(e->ref) {
return;
}
/* since we encrease ref count at creation of every sources and slots,
* once we get here, it means no sources and slots exist anymore */
if(e->bus) {
sd_bus_detach_event(e->bus);
sd_bus_unref(e->bus);
}
if(e->bus_name) {
free(e->bus_name);
}
// if(e->bus_addr) {
// free(e->bus_addr);
// }
free(e);
}
void dispd_encoder_set_handler(struct dispd_encoder *e,
dispd_encoder_state_change_handler handler,
void *userdata)
{
assert_vret(e);
e->handler = handler;
e->userdata = userdata;
}
dispd_encoder_state_change_handler dispd_encoder_get_handler(struct dispd_encoder *e)
{
assert_retv(e, NULL);
return e->handler;
}
enum dispd_encoder_state dispd_encoder_get_state(struct dispd_encoder *e)
{
assert_retv(e, DISPD_ENCODER_STATE_NULL);
return e->state;
}
static const char * state_to_name(enum dispd_encoder_state s)
{
const char *names[] = {
"NULL",
"SPAWNED",
"CONFIGURED",
"READY",
"STARTED",
"PAUSED",
"TERMINATED"
};
if(0 > s || DISPD_ENCODER_STATE_TERMINATED < s) {
return "unknown encoder state";
}
return names[s];
}
static void dispd_encoder_set_state(struct dispd_encoder *e,
enum dispd_encoder_state state)
{
assert_vret(e);
if(e->state == state) {
return;
}
log_debug("state change from %s to %s",
state_to_name(e->state),
state_to_name(state));
e->state = state;
dispd_encoder_notify_state_change(e, state);
}
static int on_encoder_properties_changed(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_encoder *e = userdata;
const char *name;
int value;
enum dispd_encoder_state s;
int r;
r = sd_bus_message_skip(m, "s");
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_enter_container(m, 'a', "{sv}");
if(0 > r) {
return log_ERRNO();
}
while(!sd_bus_message_at_end(m, true)) {
r = sd_bus_message_read(m, "{sv}", &name, "i", &value);
if(0 > r) {
return log_ERRNO();
}
if(strcmp("State", name)) {
continue;
}
switch(value) {
case 0:
s = DISPD_ENCODER_STATE_NULL;
break;
case 1:
s = DISPD_ENCODER_STATE_CONFIGURED;
break;
case 2:
s = DISPD_ENCODER_STATE_READY;
break;
case 3:
s = DISPD_ENCODER_STATE_STARTED;
break;
case 4:
s = DISPD_ENCODER_STATE_PAUSED;
break;
case 5:
s = DISPD_ENCODER_STATE_TERMINATED;
break;
default:
log_error("encoder enter unknown state: %d", value);
return 0;
}
dispd_encoder_set_state(e, s);
break;
}
return 0;
}
static int on_encoder_disappeared(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
struct dispd_encoder *e = userdata;
int r;
log_info("encoder %s disappered from bus", e->bus_name);
r = dispd_encoder_kill_child(e);
if(0 > r) {
return log_ERRNO();
}
else if(r) {
return 0;
}
dispd_encoder_cleanup(e);
return 0;
}
static int read_line(int fd, char *b, size_t len)
{
int r;
char *p = b;
assert_ret(0 <= fd);
assert_ret(b);
assert_ret(len);
while((p - b) < (len - 1)) {
r = read(fd, p, 1);
if(0 > r) {
if(EINTR == errno) {
continue;
}
else if(EAGAIN == errno) {
break;
}
return log_ERRNO();
}
else if(!r || '\n' == *p) {
break;
}
++ p;
}
*p = '\0';
return p - b;
}
static int on_bus_info_readable(sd_event_source *source,
int fd,
uint32_t events,
void *userdata)
{
struct dispd_encoder *e = userdata;
char buf[512];
int r;
r = read_line(fd, buf, sizeof(buf) - 1);
if(0 > r) {
log_vERR(r);
goto error;
}
else if(!r) {
log_warning("no bus name returned from encoder");
r = -ENOENT;
goto error;
}
// TODO remove heading and trailing speces from buf before strdup()
log_info("got bus name from encoder: %s", buf);
e->bus_name = strdup(buf);
if(!e->bus_name) {
log_vERRNO();
goto error;
}
r = read_line(fd, buf, sizeof(buf) - 1);
if(0 > r) {
log_vERR(r);
goto error;
}
else if(!r) {
log_warning("no bus address returned from encoder");
r = -ENOENT;
goto error;
}
log_info("got bus address from encoder: %s", buf);
r = seteuid(e->bus_owner);
if(0 > r) {
log_vERRNO();
goto error;
}
r = sd_bus_new(&e->bus);
if(0 > r) {
log_vERR(r);
goto error;
}
r = sd_bus_set_address(e->bus, buf);
if(0 > r) {
log_vERR(r);
goto error;
}
r = sd_bus_set_bus_client(e->bus, true);
if(0 > r) {
log_vERR(r);
goto error;
}
r = sd_bus_start(e->bus);
if(0 > r) {
log_vERR(r);
goto error;
}
r = sd_bus_attach_event(e->bus, dispd_get_loop(), 0);
if(0 > r) {
log_vERR(r);
goto error;
}
r = seteuid(0);
if(0 > r) {
log_vERRNO();
goto error;
}
snprintf(buf, sizeof(buf),
"type='signal',"
"sender='%s',"
"path='/org/freedesktop/miracle/encoder',"
"interface='org.freedesktop.DBus.Properties',"
"member='PropertiesChanged',"
"arg0='org.freedesktop.miracle.encoder'",
e->bus_name);
if(0 > r) {
log_vERRNO();
goto error;
}
r = sd_bus_add_match(e->bus,
&e->state_change_notify_slot,
buf,
on_encoder_properties_changed,
dispd_encoder_ref(e));
if(0 > r) {
log_vERRNO();
goto error;
}
r = snprintf(buf, sizeof(buf),
"type='signal',"
"sender='org.freedesktop.DBus',"
"path='/org/freedesktop/DBus',"
"interface='org.freedesktop.DBus',"
"member='NameOwnerChanged',"
"arg0namespace='%s'",
e->bus_name);
if(0 > r) {
log_vERRNO();
goto error;
}
r = sd_bus_add_match(e->bus,
&e->name_disappeared_slot,
buf,
on_encoder_disappeared,
dispd_encoder_ref(e));
if(0 > r) {
log_vERRNO();
goto error;
}
dispd_encoder_set_state(e, DISPD_ENCODER_STATE_SPAWNED);
goto end;
error:
seteuid(0);
dispd_encoder_kill_child(e);
end:
dispd_encoder_close_pipe(e);
return r;
}
static int config_append(sd_bus_message *m,
enum wfd_encoder_config k,
const char *t,
...)
{
int r;
va_list argv;
assert_ret(m);
assert_ret(t);
r = sd_bus_message_open_container(m, 'e', "iv");
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_append(m, "i", k);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_open_container(m, 'v', t);
if(0 > r) {
return log_ERRNO();
}
va_start(argv, t);
switch(*t) {
case 's':
r = sd_bus_message_append(m, t, va_arg(argv, char *));
break;
case 'u':
r = sd_bus_message_append(m, t, va_arg(argv, uint32_t));
break;
default:
abort();
}
va_end(argv);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_close_container(m);
if(0 > r) {
return log_ERRNO();
}
r = sd_bus_message_close_container(m);
if(0 > r) {
return log_ERRNO();
}
return 0;
}
int dispd_encoder_configure(struct dispd_encoder *e, struct dispd_session *s)
{
_cleanup_sd_bus_message_ sd_bus_message *call = NULL;
_cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
const struct dispd_rectangle *rect;
struct dispd_sink *sink;
int r;
assert_ret(e);
assert_ret(e->bus);
assert_ret(s);
assert_ret(dispd_is_out_session(s));
r = sd_bus_message_new_method_call(e->bus,
&call,
e->bus_name,
"/org/freedesktop/miracle/encoder",
"org.freedesktop.miracle.encoder",
"Configure");
if(0 > r) {
return log_ERR(r);
}
r = sd_bus_message_open_container(call, 'a', "{iv}");
if(0 > r) {
return log_ERR(r);
}
sink = dispd_out_session_get_sink(s);
r = config_append(call,
WFD_ENCODER_CONFIG_PEER_ADDRESS,
"s",
sink->peer->remote_address);
if(0 > r) {
return log_ERR(r);
}
r = config_append(call,
WFD_ENCODER_CONFIG_RTP_PORT0,
"u",
s->stream.rtp_port);
if(0 > r) {
return log_ERR(r);
}
if(s->stream.rtcp_port) {
r = config_append(call,
WFD_ENCODER_CONFIG_PEER_RTCP_PORT,
"u",
s->stream.rtcp_port);
if(0 > r) {
return log_ERR(r);
}
}
r = config_append(call,
WFD_ENCODER_CONFIG_LOCAL_ADDRESS,
"s",
sink->peer->local_address);
if(0 > r) {
return log_ERR(r);
}
if(s->stream.rtcp_port) {
r = config_append(call,
WFD_ENCODER_CONFIG_LOCAL_RTCP_PORT,
"u",
s->stream.rtcp_port);
if(0 > r) {
return log_ERR(r);
}
}
rect = dispd_session_get_disp_dimension(s);
if(rect) {
r = config_append(call,
WFD_ENCODER_CONFIG_X,
"u",
rect->x);
if(0 > r) {
return log_ERR(r);
}
r = config_append(call,
WFD_ENCODER_CONFIG_Y,
"u",
rect->y);
if(0 > r) {
return log_ERR(r);
}
r = config_append(call,
WFD_ENCODER_CONFIG_WIDTH,
"u",
rect->width);
if(0 > r) {
return log_ERR(r);
}
r = config_append(call,
WFD_ENCODER_CONFIG_HEIGHT,
"u",
rect->height);
if(0 > r) {
return log_ERR(r);
}
}
if(dispd_session_has_audio(s) && dispd_session_get_audio_dev_name(s)) {
r = config_append(call,
WFD_ENCODER_CONFIG_AUDIO_TYPE,
"u",
dispd_session_get_audio_type(s));
if(0 > r) {
return log_ERR(r);
}
r = config_append(call,
WFD_ENCODER_CONFIG_AUDIO_DEV,
"s",
dispd_session_get_audio_dev_name(s));
if(0 > r) {
return log_ERR(r);
}
}
r = sd_bus_message_close_container(call);
if(0 > r) {
return log_ERR(r);
}
if(0 > r) {
log_vERR(r);
goto end;
}
r = sd_bus_call(e->bus, call, 0, &error, &reply);
if(0 > r) {
log_warning("%s: %s", error.name, error.message);
log_vERR(r);
}
end:
return r;
}
static int dispd_encoder_call(struct dispd_encoder *e, const char *method)
{
_cleanup_sd_bus_message_ sd_bus_message *call = NULL;
_cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert_ret(e);
assert_ret(method);
assert_ret(e->bus);
r = sd_bus_message_new_method_call(e->bus,
&call,
e->bus_name,
"/org/freedesktop/miracle/encoder",
"org.freedesktop.miracle.encoder",
method);
if(0 > r) {
log_vERR(r);
goto error;
}
if(0 > r) {
log_vERR(r);
goto error;
}
r = sd_bus_call(e->bus, call, 0, &error, &reply);
if(0 > r) {
log_warning("%s: %s", error.name, error.message);
goto error;
}
return 0;
error:
dispd_encoder_kill_child(e);
return r;
}
int dispd_encoder_start(struct dispd_encoder *e)
{
assert_ret(e);
return dispd_encoder_call(e, "Start");
}
int dispd_encoder_pause(struct dispd_encoder *e)
{
assert_ret(e);
return dispd_encoder_call(e, "Pause");
}
static int on_child_term_timeout(sd_event_source *s,
uint64_t usec,
void *userdata)
{
struct dispd_encoder *e = userdata;
dispd_encoder_kill_child(e);
return 0;
}
int dispd_encoder_stop(struct dispd_encoder *e)
{
uint64_t now;
sd_event *loop;
int r;
assert_ret(e);
r = dispd_encoder_call(e, "Stop");
if(0 > r) {
return r;
}
loop = dispd_get_loop();
r = sd_event_now(loop, CLOCK_MONOTONIC, &now);
if(0 > r) {
log_vERR(r);
goto error;
}
r = sd_event_add_time(loop,
&e->child_term_time_source,
CLOCK_MONOTONIC,
now + (1000 * 1000),
0,
on_child_term_timeout,
dispd_encoder_ref(e));
if(0 > r) {
log_vERR(r);
goto error;
}
error:
dispd_encoder_kill_child(e);
return r;
}

87
src/disp/dispd-encoder.h Normal file
View file

@ -0,0 +1,87 @@
/*
* 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 "shl_macro.h"
#ifndef DISPD_ENCODER_H
#define DISPD_ENCODER_H
#define _dispd_encoder_unref_ _shl_cleanup_(dispd_encoder_unrefp)
enum wfd_encoder_config
{
WFD_ENCODER_CONFIG_DISPLAY_TYPE, /* string */
WFD_ENCODER_CONFIG_DISPLAY_NAME, /* string */
WFD_ENCODER_CONFIG_MONITOR_NUM, /* uint32 */
WFD_ENCODER_CONFIG_X, /* uint32 */
WFD_ENCODER_CONFIG_Y, /* uint32 */
WFD_ENCODER_CONFIG_WIDTH, /* uint32 */
WFD_ENCODER_CONFIG_HEIGHT, /* uint32 */
WFD_ENCODER_CONFIG_WINDOW_ID, /* uint32 */
WFD_ENCODER_CONFIG_FRAMERATE, /* uint32 */
WFD_ENCODER_CONFIG_SCALE_WIDTH, /* uint32 */
WFD_ENCODER_CONFIG_SCALE_HEIGHT, /* uint32 */
WFD_ENCODER_CONFIG_AUDIO_TYPE, /* string */
WFD_ENCODER_CONFIG_AUDIO_DEV, /* string */
WFD_ENCODER_CONFIG_PEER_ADDRESS, /* string */
WFD_ENCODER_CONFIG_RTP_PORT0, /* uint32 */
WFD_ENCODER_CONFIG_RTP_PORT1, /* uint32 */
WFD_ENCODER_CONFIG_PEER_RTCP_PORT, /* uint32 */
WFD_ENCODER_CONFIG_LOCAL_ADDRESS, /* uint32 */
WFD_ENCODER_CONFIG_LOCAL_RTCP_PORT, /* uint32 */
WFD_ENCODER_CONFIG_H264_PROFILE,
WFD_ENCODER_CONFIG_H264_LEVEL,
WFD_ENCODER_CONFIG_DEBUG_LEVEL,
};
enum dispd_encoder_state
{
DISPD_ENCODER_STATE_NULL = 0,
DISPD_ENCODER_STATE_SPAWNED,
DISPD_ENCODER_STATE_CONFIGURED,
DISPD_ENCODER_STATE_READY,
DISPD_ENCODER_STATE_STARTED,
DISPD_ENCODER_STATE_PAUSED,
DISPD_ENCODER_STATE_TERMINATED,
};
struct dispd_session;
struct dispd_encoder;
typedef void (*dispd_encoder_state_change_handler)(struct dispd_encoder *e,
enum dispd_encoder_state state,
void *userdata);
int dispd_encoder_spawn(struct dispd_encoder **out, struct dispd_session *s);
struct dispd_encoder * dispd_encoder_ref(struct dispd_encoder *e);
void dispd_encoder_unref(struct dispd_encoder *e);
void dispd_encoder_unrefp(struct dispd_encoder **e);
int dispd_encoder_configure(struct dispd_encoder *e, struct dispd_session *s);
int dispd_encoder_start(struct dispd_encoder *e);
int dispd_encoder_pause(struct dispd_encoder *e);
int dispd_encoder_stop(struct dispd_encoder *e);
void dispd_encoder_set_handler(struct dispd_encoder *e,
dispd_encoder_state_change_handler handler,
void *userdata);
dispd_encoder_state_change_handler dispd_encoder_get_handler(struct dispd_encoder *e);
enum dispd_encoder_state dispd_encoder_get_state(struct dispd_encoder *e);
#endif /* DISPD_ENCODER_H */

1004
src/disp/dispd-out-session.c Normal file

File diff suppressed because it is too large Load diff

1091
src/disp/dispd-session.c Normal file

File diff suppressed because it is too large Load diff

146
src/disp/dispd-session.h Normal file
View file

@ -0,0 +1,146 @@
/*
* 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 <unistd.h>
#include "dispd.h"
#include "dispd-arg.h"
#ifndef DISPD_SESSION_H
#define DISPD_SESSION_H
#define dispd_out_session(s) (assert(dispd_is_out_session(s)), (struct dispd_out_session *) (s))
#define dispd_in_session(s) (assert(dispd_is_in_session(s)), (struct dispd_in_session *) (s))
struct dispd_session;
struct dispd_sink;
struct rtsp;
struct rtsp_message;
enum rtsp_message_id
{
RTSP_M_UNKNOWN,
RTSP_M1_REQUEST_SINK_OPTIONS,
RTSP_M2_REQUEST_SRC_OPTIONS,
RTSP_M3_GET_PARAMETER,
RTSP_M4_SET_PARAMETER,
RTSP_M5_TRIGGER,
RTSP_M6_SETUP,
RTSP_M7_PLAY,
RTSP_M8_TEARDOWN,
RTSP_M9_PAUSE,
RTSP_M10_SET_ROUTE,
RTSP_M11_SET_CONNECTOR_TYPE,
RTSP_M12_SET_STANDBY,
RTSP_M13_REQUEST_IDR,
RTSP_M14_ESTABLISH_UIBC,
RTSP_M15_ENABLE_UIBC,
RTSP_M16_KEEPALIVE,
};
enum dispd_stream_id
{
DISPD_STREAM_ID_PRIMARY,
DISPD_STREAM_ID_SECONDARY,
};
enum dispd_session_arg_id
{
DISPD_SESSION_ARG_NEXT_REQUEST,
DISPD_SESSION_ARG_NEW_STATE,
DISPD_SESSION_ARG_REQUEST_ARGS,
};
struct rtsp_dispatch_entry
{
union {
int (*request)(struct dispd_session *s,
struct rtsp *bus,
const struct dispd_arg_list *args,
struct rtsp_message **out);
int (*handle_request)(struct dispd_session *s,
struct rtsp_message *req,
struct rtsp_message **out_rep);
};
int (*handle_reply)(struct dispd_session *s,
struct rtsp_message *m);
struct dispd_arg_list rule;
};
struct dispd_session_vtable
{
int (*initiate_io)(struct dispd_session *s, int *out_fd, uint32_t *out_mask);
int (*handle_io)(struct dispd_session *s, int error, int *out_fd);
int (*initiate_request)(struct dispd_session *s);
int (*resume)(struct dispd_session *);
int (*pause)(struct dispd_session *);
int (*teardown)(struct dispd_session *);
void (*destroy)(struct dispd_session *s);
};
struct dispd_session
{
int ref;
enum dispd_session_dir dir;
enum dispd_session_state state;
enum rtsp_message_id last_request;
const struct rtsp_dispatch_entry *rtsp_disp_tbl;
unsigned int id;
struct rtsp *rtsp;
uint64_t req_cookie;
uint16_t rtp_ports[2];
struct wfd_video_formats *vformats;
struct wfd_audio_codecs *acodecs;
struct {
enum dispd_stream_id id;
char *url;
uint16_t rtp_port;
uint16_t rtcp_port;
} stream;
enum dispd_display_server_type disp_type;
char *disp_name;
char *disp_params;
char *disp_auth;
struct dispd_rectangle disp_dimen;
enum dispd_audio_server_type audio_type;
char *audio_dev_name;
uid_t client_uid;
gid_t client_gid;
gid_t client_pid;
char *runtime_path;
};
int dispd_session_init(struct dispd_session *s,
unsigned int id,
enum dispd_session_dir dir,
const struct rtsp_dispatch_entry *disp_tbl);
int dispd_session_gen_stream_url(struct dispd_session *s,
const char *local_addr,
enum dispd_stream_id id);
int dispd_session_request(struct dispd_session *s,
enum rtsp_message_id id,
const struct dispd_arg_list *args);
void dispd_session_end(struct dispd_session *s);
struct dispd_sink * dispd_out_session_get_sink(struct dispd_session *s);
void dispd_session_set_state(struct dispd_session *s,
enum dispd_session_state state);
#endif /* DISPD_SESSION_H */

173
src/disp/dispd-sink.c Normal file
View file

@ -0,0 +1,173 @@
/*
* 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 <time.h>
#include <systemd/sd-event.h>
#include "ctl.h"
#include "dispd.h"
#include "dispd-dbus.h"
static int dispd_sink_set_session(struct dispd_sink *sink,
struct dispd_session *session)
{
int r;
assert_ret(sink);
if(sink->session == session) {
return 0;
}
if(session) {
r = dispd_add_session(dispd_get(), session);
if(0 > r) {
return log_ERR(r);
}
}
if(sink->session) {
dispd_fn_sink_detach(sink);
dispd_remove_session_by_id(dispd_get(),
dispd_session_get_id(sink->session),
NULL);
dispd_session_unref(sink->session);
}
sink->session = session ? dispd_session_ref(session) : NULL;
dispd_fn_sink_properties_changed(sink, "Session");
return 0;
}
int dispd_sink_new(struct dispd_sink **out,
struct ctl_peer *peer,
union wfd_sube *sube)
{
struct dispd_sink *sink;
assert_ret(out);
assert_ret(peer);
assert_ret(sube);
assert_ret(wfd_sube_device_is_sink(sube));
sink = calloc(1, sizeof(struct dispd_sink));
if(!sink) {
return -ENOMEM;
}
sink->label = strdup(peer->label);
if(!sink->label) {
dispd_sink_free(sink);
return -ENOMEM;
}
sink->peer = peer;
sink->dev_info = *sube;
*out = sink;
return 0;
}
void dispd_sink_free(struct dispd_sink *sink)
{
if(!sink) {
return;
}
dispd_sink_set_session(sink, NULL);
if(sink->label) {
free(sink->label);
}
free(sink);
}
const char * dispd_sink_get_label(struct dispd_sink *sink)
{
assert_retv(sink, NULL);
return sink->label;
}
const union wfd_sube * dispd_sink_get_dev_info(struct dispd_sink *sink)
{
assert_retv(sink, NULL);
return &sink->dev_info;
}
struct ctl_peer * dispd_sink_get_peer(struct dispd_sink *sink)
{
assert_retv(sink, NULL);
return sink->peer;
}
int dispd_sink_create_session(struct dispd_sink *sink, struct dispd_session **out)
{
int r;
_dispd_session_unref_ struct dispd_session *sess = NULL;
assert_ret(sink);
assert_ret(out);
if(dispd_sink_is_session_started(sink)) {
return -EALREADY;
}
r = dispd_out_session_new(&sess,
dispd_alloc_session_id(dispd_get()),
sink);
if(0 > r) {
return r;
}
r = dispd_sink_set_session(sink, sess);
if(0 > r) {
return r;
}
*out = dispd_session_ref(sess);
dispd_fn_sink_properties_changed(sink, "Session");
return 0;
}
int dispd_fn_out_session_ended(struct dispd_session *s)
{
struct dispd_sink *sink;
assert_ret(dispd_is_out_session(s));
sink = dispd_out_session_get_sink(s);
if(sink) {
dispd_sink_set_session(sink, NULL);
}
return 0;
}
bool dispd_sink_is_session_started(struct dispd_sink *sink)
{
assert_retv(sink, false);
return NULL != sink->session;
}

522
src/disp/dispd.c Normal file
View file

@ -0,0 +1,522 @@
/*
* 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/>.
*/
#define LOG_SUBSYSTEM "dispd"
#include <locale.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <systemd/sd-event.h>
#include <systemd/sd-daemon.h>
#include "ctl.h"
#include "dispd.h"
#include "wfd.h"
#include "dispd-dbus.h"
#include "config.h"
static int dispd_init(struct dispd *dispd, sd_bus *bus);
static void dispd_free(struct dispd *dispd);
static struct dispd_dbus *dispd_dbus = NULL;
struct dispd_dbus * dispd_dbus_get()
{
return dispd_dbus;
}
static struct dispd *dispd = NULL;
struct dispd * dispd_get()
{
return dispd;
}
int dispd_new(struct dispd **out, sd_event *loop, sd_bus *bus)
{
int r;
struct dispd *dispd = calloc(1, sizeof(struct dispd));
if(!dispd) {
r = -ENOMEM;
goto error;
}
shl_htable_init_str(&dispd->sinks);
shl_htable_init_uint(&dispd->sessions);
dispd->loop = sd_event_ref(loop);
r = dispd_init(dispd, bus);
if(0 > r) {
goto error;
}
*out = dispd;
return 0;
error:
dispd_free(dispd);
return log_ERR(r);
}
static void dispd_free(struct dispd *dispd)
{
if(!dispd) {
return;
}
ctl_wifi_free(dispd->wifi);
dispd->wifi = NULL;
shl_htable_clear_str(&dispd->sinks, NULL, NULL);
shl_htable_clear_uint(&dispd->sessions, NULL, NULL);
if(dispd->loop) {
sd_event_unref(dispd->loop);
}
free(dispd);
}
static int dispd_handle_shutdown(sd_event_source *s,
uint64_t usec,
void *userdata)
{
struct dispd *dispd = userdata;
sd_event_exit(dispd->loop, 0);
return 0;
}
void dispd_shutdown(struct dispd *dispd)
{
uint64_t now;
int r = sd_event_now(dispd_get_loop(), CLOCK_MONOTONIC, &now);
if(0 > r) {
goto error;
}
r = sd_event_add_time(dispd_get_loop(),
NULL,
CLOCK_MONOTONIC,
now + 100 * 1000,
0,
dispd_handle_shutdown,
dispd);
if(0 <= r) {
return;
}
error:
sd_event_exit(dispd->loop, 0);
}
int dispd_add_sink(struct dispd *dispd,
struct ctl_peer *p,
union wfd_sube *sube,
struct dispd_sink **out)
{
_dispd_sink_free_ struct dispd_sink *s = NULL;
int r = shl_htable_lookup_str(&dispd->sinks,
p->label,
NULL,
NULL);
if(r) {
return -EEXIST;
}
r = dispd_sink_new(&s, p, sube);
if(0 > r) {
return log_ERR(r);
}
r = shl_htable_insert_str(&dispd->sinks,
dispd_sink_to_htable(s),
NULL);
if(0 > r) {
return log_ERR(r);
}
++dispd->n_sinks;
*out = s;
s = NULL;
return 0;
}
int dispd_find_sink_by_label(struct dispd *dispd,
const char *label,
struct dispd_sink **out)
{
char **entry;
int r = shl_htable_lookup_str(&dispd->sinks, label, NULL, &entry);
if(r && out) {
*out = dispd_sink_from_htable(entry);
}
return r;
}
static int dispd_remove_sink_by_label(struct dispd *dispd,
const char *label,
struct dispd_sink **out)
{
char **entry;
int r = shl_htable_remove_str(&dispd->sinks, label, NULL, &entry);
if(!r) {
goto end;
}
--dispd->n_sinks;
if(out) {
*out = dispd_sink_from_htable(entry);
}
end:
return r;
}
unsigned int dispd_alloc_session_id(struct dispd *dispd)
{
return ++dispd->id_pool;
}
int dispd_add_session(struct dispd *dispd, struct dispd_session *s)
{
int r;
assert(dispd);
assert(s && dispd_session_get_id(s));
assert(!dispd_find_session_by_id(dispd, dispd_session_get_id(s), NULL));
r = shl_htable_insert_uint(&dispd->sessions, dispd_session_to_htable(s));
if(0 > r) {
return log_ERR(r);
}
++dispd->n_sessions;
dispd_fn_session_new(s);
return 0;
}
int dispd_find_session_by_id(struct dispd *dispd,
unsigned int id,
struct dispd_session **out)
{
unsigned int *entry;
int r = shl_htable_lookup_uint(&dispd->sessions, id, &entry);
if(r && out) {
*out = dispd_session_from_htable(entry);
}
return r;
}
int dispd_remove_session_by_id(struct dispd *dispd,
unsigned int id,
struct dispd_session **out)
{
unsigned int *entry;
struct dispd_session *s;
int r = shl_htable_remove_uint(&dispd->sessions, id, &entry);
if(!r) {
return 0;
}
--dispd->n_sessions;
s = dispd_session_from_htable(entry);
dispd_fn_session_free(s);
if(out) {
*out = s;
}
return 1;
}
static int dispd_fetch_info(sd_event_source *s, void *userdata)
{
struct dispd *dispd = userdata;
int r;
sd_event_source_unref(s);
r = ctl_wifi_fetch(dispd->wifi);
if(0 > r) {
log_warning("failed to fetch information about links and peers: %s",
strerror(errno));
sd_event_exit(dispd->loop, r);
}
return r;
}
static int dispd_handle_signal(sd_event_source *s,
const struct signalfd_siginfo *ssi,
void *userdata)
{
int r;
siginfo_t siginfo;
struct dispd *dispd = userdata;
if(ssi->ssi_signo == SIGCHLD) {
r = waitid(P_PID, ssi->ssi_pid, &siginfo, WNOHANG | WEXITED);
if(0 > r) {
log_warning("failed to reaping child %d", ssi->ssi_pid);
}
else {
log_info("child %d exit: %d",
ssi->ssi_pid,
siginfo.si_code);
}
return 0;
}
return sd_event_exit(dispd->loop, 0);
}
static int dispd_init(struct dispd *dispd, sd_bus *bus)
{
int i, r;
const int signals[] = {
SIGINT, SIGHUP, SIGQUIT, SIGTERM, SIGCHLD,
};
struct ctl_wifi *wifi;
for(i = 0; i < SHL_ARRAY_LENGTH(signals); i ++) {
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, signals[i]);
r = sigprocmask(SIG_BLOCK, &mask, NULL);
if(0 > r) {
break;
}
r = sd_event_add_signal(dispd->loop,
NULL,
signals[i],
dispd_handle_signal,
dispd);
if(0 > r) {
break;
}
}
r = ctl_wifi_new(&wifi, bus);
if(0 > r) {
log_vENOMEM();
goto end;
}
r = sd_event_add_defer(dispd->loop, NULL, dispd_fetch_info, dispd);
if(0 > r) {
log_vERRNO();
ctl_wifi_free(wifi);
goto end;
}
dispd->wifi = wifi;
end:
return r;
}
void ctl_fn_peer_new(struct ctl_peer *p)
{
struct dispd_sink *s;
union wfd_sube sube;
int r;
log_debug("new peer %s (%s) shows up, wfd_subelems: '%s'",
p->label,
p->friendly_name,
p->wfd_subelements);
if(!p->wfd_subelements || !*p->wfd_subelements) {
log_info("peer %s has no wfd_subelems, ignore it", p->label);
return;
}
r = wfd_sube_parse(p->wfd_subelements, &sube);
if(0 > r) {
log_debug("peer %s has no valid subelement, ignore it", p->label);
return;
}
if(wfd_sube_device_is_sink(&sube)) {
r = dispd_add_sink(dispd_get(), p, &sube, &s);
if(0 > r) {
log_warning("failed to add sink (%s, '%s'): %s",
p->friendly_name,
p->p2p_mac,
strerror(errno));
return log_vERRNO();
}
r = dispd_fn_sink_new(s);
if(0 > r) {
log_warning("failed to publish newly added sink (%s): %s",
dispd_sink_get_label(s),
strerror(errno));
return log_vERRNO();
}
log_info("sink %s added", s->label);
}
if(wfd_sube_device_is_source(&sube)) {
log_info("source %s ignired", p->label);
}
}
void ctl_fn_peer_free(struct ctl_peer *p)
{
struct dispd_sink *s;
int r;
r = dispd_remove_sink_by_label(dispd, p->label, &s);
if(r) {
dispd_fn_sink_free(s);
log_info("sink %s removed", s->label);
dispd_sink_free(s);
}
log_info("peer %s down", p->label);
}
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
const char *prov,
const char *pin)
{
}
void ctl_fn_peer_go_neg_request(struct ctl_peer *p,
const char *prov,
const char *pin)
{
}
void ctl_fn_peer_formation_failure(struct ctl_peer *p, const char *reason)
{
}
void ctl_fn_peer_connected(struct ctl_peer *p)
{
}
void ctl_fn_peer_disconnected(struct ctl_peer *p)
{
}
void ctl_fn_link_new(struct ctl_link *l)
{
}
void ctl_fn_link_free(struct ctl_link *l)
{
}
void cli_fn_help()
{
}
int main(int argc, char **argv)
{
int r;
sd_event *event;
sd_bus *bus;
setlocale(LC_ALL, "");
setlocale(LC_TIME, "en_US.UTF-8");
if(getenv("LOG_LEVEL")) {
log_max_sev = log_parse_arg(getenv("LOG_LEVEL"));
}
r = sd_event_default(&event);
if(0 > r) {
log_warning("can't create default event loop");
goto end;
}
r = sd_event_set_watchdog(event, true);
if (0 > r) {
log_warning("unable to start automatic watchdog support: %s", strerror(errno));
goto unref_event;
}
r = sd_bus_default_system(&bus);
if(0 > r) {
log_warning("unable to connect to system DBus: %s", strerror(errno));
goto disable_watchdog;
}
r = sd_bus_attach_event(bus, event, 0);
if(0 > r) {
goto unref_bus;
}
r = dispd_dbus_new(&dispd_dbus, event, bus);
if(0 > r) {
goto bus_detach_event;
}
r = dispd_new(&dispd, event, bus);
if(0 > r) {
goto free_dispd_dbus;
}
r = dispd_dbus_expose(dispd_dbus);
if(0 > r) {
log_warning("unable to publish WFD service: %s", strerror(errno));
goto free_dispd;
}
r = sd_notify(false, "READY=1\n"
"STATUS=Running..");
if (0 > r) {
log_warning("unable to notify systemd that we are ready: %s", strerror(errno));
goto free_dispd_dbus;
}
sd_event_loop(event);
sd_notify(false, "STATUS=Exiting..");
free_dispd:
dispd_free(dispd);
free_dispd_dbus:
dispd_dbus_free(dispd_dbus);
bus_detach_event:
sd_bus_detach_event(bus);
unref_bus:
sd_bus_flush_close_unref(bus);
disable_watchdog:
sd_event_set_watchdog(event, false);
unref_event:
sd_event_unref(event);
end:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}

223
src/disp/dispd.h Normal file
View file

@ -0,0 +1,223 @@
/*
* 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 <systemd/sd-event.h>
#include "shl_htable.h"
#include "ctl.h"
#include "wfd.h"
#ifndef DISP_DISP_H
#define DISP_DISP_H
/* dispd session */
#define dispd_session(s) ((struct dispd_session *) (s))
#define dispd_is_session(s) ( \
(s) && \
(DISPD_SESSION_DIR_OUT == dispd_session(s)->dir || \
DISPD_SESSION_DIR_IN == dispd_session(s)->dir) \
)
#define dispd_session_has_id(s) (0 < dispd_session_get_id(s))
#define dispd_is_out_session(s) (DISPD_SESSION_DIR_OUT == dispd_session_get_dir(s))
#define dispd_is_in_session(s) (DISPD_SESSION_DIR_IN == dispd_session_get_dir(s))
#define _dispd_session_unref_ _shl_cleanup_(dispd_session_unrefp)
struct dispd_sink;
struct dispd_session;
struct rtsp_dispatch_entry;
enum dispd_session_dir
{
DISPD_SESSION_DIR_OUT,
DISPD_SESSION_DIR_IN,
};
enum dispd_session_state
{
DISPD_SESSION_STATE_NULL,
DISPD_SESSION_STATE_CONNECTING,
DISPD_SESSION_STATE_CAPS_EXCHANGING,
DISPD_SESSION_STATE_ESTABLISHED,
DISPD_SESSION_STATE_SETTING_UP,
DISPD_SESSION_STATE_PAUSED,
DISPD_SESSION_STATE_PLAYING,
DISPD_SESSION_STATE_TEARING_DOWN,
DISPD_SESSION_STATE_DESTROYED,
};
struct dispd_rectangle
{
int x;
int y;
int width;
int height;
};
enum dispd_display_server_type
{
DISPD_DISPLAY_SERVER_TYPE_UNKNOWN = 0,
DISPD_DISPLAY_SERVER_TYPE_X,
};
enum dispd_audio_server_type
{
DISPD_AUDIO_SERVER_TYPE_UNKNOWN = 0,
DISPD_AUDIO_SERVER_TYPE_PULSE_AUDIO,
};
int dispd_out_session_new(struct dispd_session **out,
unsigned int id,
struct dispd_sink *sink);
struct dispd_session * _dispd_session_ref(struct dispd_session *s);
#define dispd_session_ref(s) ( \
log_debug("dispd_session_ref(%p): %d => %d", (s), *(int *) s, 1 + *(int *) s), \
_dispd_session_ref(s) \
)
void _dispd_session_unref(struct dispd_session *s);
#define dispd_session_unref(s) { \
log_debug("dispd_session_unref(%p): %d => %d", (s), *(int *) s, *(int *) s - 1); \
_dispd_session_unref(s); \
}
void dispd_session_unrefp(struct dispd_session **s);
unsigned int * dispd_session_to_htable(struct dispd_session *s);
struct dispd_session * dispd_session_from_htable(unsigned int *e);
int dispd_session_start(struct dispd_session *s);
int dispd_session_resume(struct dispd_session *s);
int dispd_session_pause(struct dispd_session *s);
int dispd_session_teardown(struct dispd_session *s);
int dispd_session_destroy(struct dispd_session *s);
bool dispd_session_is_established(struct dispd_session *s);
unsigned int dispd_session_get_id(struct dispd_session *s);
const char * dispd_session_get_stream_url(struct dispd_session *s);
bool dispd_session_is_state(struct dispd_session *s, enum dispd_session_state state);
enum dispd_session_state dispd_session_get_state(struct dispd_session *s);
enum dispd_session_dir dispd_session_get_dir(struct dispd_session *s);
struct dispd_sink * dispd_out_session_get_sink(struct dispd_session *s);
enum dispd_display_server_type dispd_session_get_disp_type(struct dispd_session *s);
bool dispd_session_has_disp(struct dispd_session *s);
int dispd_session_set_disp_type(struct dispd_session *s, enum dispd_display_server_type);
const char * dispd_session_get_disp_name(struct dispd_session *s);
int dispd_session_set_disp_name(struct dispd_session *s, const char *disp_name);
const char * dispd_session_get_disp_params(struct dispd_session *s);
int dispd_session_set_disp_params(struct dispd_session *s, const char *disp_params);
const char * dispd_session_get_disp_auth(struct dispd_session *s);
int dispd_session_set_disp_auth(struct dispd_session *s, const char *disp_auth);
const struct dispd_rectangle * dispd_session_get_disp_dimension(struct dispd_session *s);
int dispd_session_set_disp_dimension(struct dispd_session *s, const struct dispd_rectangle *rect);
enum dispd_audio_server_type dispd_session_get_audio_type(struct dispd_session *s);
bool dispd_session_has_audio(struct dispd_session *s);
int dispd_session_set_audio_type(struct dispd_session *s, enum dispd_audio_server_type audio_type);
const char * dispd_session_get_audio_dev_name(struct dispd_session *s);
int dispd_session_set_audio_dev_name(struct dispd_session *s, const char *audio_dev_name);
const char * dispd_session_get_runtime_path(struct dispd_session *s);
int dispd_session_set_runtime_path(struct dispd_session *s,
const char *runtime_path);
uid_t dispd_session_get_client_uid(struct dispd_session *s);
int dispd_session_set_client_uid(struct dispd_session *s, uid_t uid);
uid_t dispd_session_get_client_gid(struct dispd_session *s);
int dispd_session_set_client_gid(struct dispd_session *s, uid_t gid);
pid_t dispd_session_get_client_pid(struct dispd_session *s);
int dispd_session_set_client_pid(struct dispd_session *s, pid_t pid);
/* dispd sink */
#define _dispd_sink_free_ _shl_cleanup_(dispd_sink_freep)
#define dispd_sink_to_htable(s) (&(s)->label)
#define dispd_sink_from_htable(s) shl_htable_entry(s, struct dispd_sink, label)
struct dispd_sink
{
struct ctl_peer *peer;
union wfd_sube dev_info;
char *label;
struct dispd_session *session;
sd_event_source *session_cleanup_source;
};
int dispd_sink_new(struct dispd_sink **out,
struct ctl_peer *peer,
union wfd_sube *sube);
void dispd_sink_free(struct dispd_sink *sink);
static inline void dispd_sink_freep(struct dispd_sink **s)
{
dispd_sink_free(*s);
*s = NULL;
}
int dispd_sink_create_session(struct dispd_sink *sink, struct dispd_session **out);
const char * dispd_sink_get_label(struct dispd_sink *sink);
const union wfd_sube * dispd_sink_get_dev_info(struct dispd_sink *sink);
bool dispd_sink_is_session_started(struct dispd_sink *sink);
void dispd_sink_handle_session_ended(struct dispd_sink *sink);
/* wfd handling */
#define dispd_foreach_sink(_i, _w) \
SHL_HTABLE_FOREACH_MACRO(_i, \
&(_w)->sinks, \
dispd_sink_from_htable)
#define dispd_foreach_session(_i, _w) \
SHL_HTABLE_FOREACH_MACRO(_i, \
&(_w)->sessions, \
dispd_session_from_htable)
struct dispd
{
sd_event *loop;
struct ctl_wifi *wifi;
struct shl_htable sinks;
size_t n_sinks;
struct shl_htable sessions;
size_t n_sessions;
unsigned int id_pool;
};
struct dispd * dispd_get();
void dispd_shutdown(struct dispd *wfd);
static inline struct sd_event * dispd_get_loop()
{
return dispd_get()->loop;
}
int dispd_find_sink_by_label(struct dispd *wfd,
const char *label,
struct dispd_sink **out);
int dispd_add_session(struct dispd *wfd, struct dispd_session *s);
int dispd_find_session_by_id(struct dispd *wfd,
unsigned int id,
struct dispd_session **out);
int dispd_remove_session_by_id(struct dispd *wfd,
unsigned int id,
struct dispd_session **out);
unsigned int dispd_alloc_session_id(struct dispd *wfd);
int dispd_fn_session_new(struct dispd_session *s);
int dispd_fn_session_free(struct dispd_session *s);
int dispd_fn_out_session_ended(struct dispd_session *s);
int dispd_fn_sink_new(struct dispd_sink *s);
int dispd_fn_sink_free(struct dispd_sink *s);
int dispd_fn_sink_detach(struct dispd_sink *s);
#endif /* DISP_DISP_H */

25
src/disp/meson.build Normal file
View file

@ -0,0 +1,25 @@
inc = include_directories('../..', '../ctl',)
gst1 = dependency('gstreamer-1.0')
gst1_base = dependency('gstreamer-base-1.0')
deps = [libsystemd, libmiracle_shared_dep, gst1, gst1_base]
if readline.found()
deps += [readline]
endif
miracle_dispd_src = ['../ctl/ctl-cli.c',
'../ctl/ctl-wifi.c',
'../ctl/wfd.c',
'dispd.c',
'dispd-sink.c',
'dispd-dbus.c',
'dispd-session.c',
'dispd-out-session.c',
'dispd-arg.c',
'dispd-encoder.c'
]
executable('miracle-dispd',
miracle_dispd_src,
install: true,
include_directories: inc,
dependencies: deps
)

View file

@ -1,8 +1,11 @@
inc_shared = include_directories('shared')
subdir('shared')
subdir('wifi')
subdir('dhcp')
subdir('ctl')
subdir('uibc')
subdir('disp')
executable('miracled', 'miracled.c',
dependencies: libmiracle_shared_dep,

View file

@ -221,6 +221,11 @@ extern const char *LOG_SUBSYSTEM;
#define log_vEPIPE() \
((void)log_EPIPE())
#define log_EPROTO() \
(log_error("protocol error"), -EPROTO)
#define log_vEPROTO() \
((void)log_EPROTO())
#define log_ERRNO() \
(log_error("syscall failed (%d): %m", errno), -errno)
#define log_vERRNO() \
@ -236,4 +241,13 @@ extern const char *LOG_SUBSYSTEM;
#define log_vEUNMANAGED() \
((void)log_EUNMANAGED())
#define assert_ret(c) \
if(!(c)) return (log_error("assertion " #c " failed"), -EINVAL)
#define assert_retv(c, v) \
if(!(c)) return (log_error("assertion " #c " failed"), (v))
#define assert_vret(c) \
if(!(c)) { log_error("assertion " #c " failed"); return; }
#endif /* SHL_LOG_H */

View file

@ -57,6 +57,11 @@ static inline void cleanup_sd_bus_message(sd_bus_message **ptr)
sd_bus_message_unref(*ptr);
}
static inline void cleanup_sd_bus_creds(sd_bus_creds **ptr)
{
sd_bus_creds_unref(*ptr);
}
static inline void cleanup_udev_device(struct udev_device **ptr)
{
udev_device_unref(*ptr);
@ -71,6 +76,7 @@ static inline void cleanup_udev_enumerate(struct udev_enumerate **ptr)
#define _sd_bus_error_free_ _shl_cleanup_(sd_bus_error_free)
#define _cleanup_sd_bus_message_ _shl_cleanup_(cleanup_sd_bus_message)
#define _sd_bus_message_unref_ _shl_cleanup_(cleanup_sd_bus_message)
#define _sd_bus_creds_unref_ _shl_cleanup_(cleanup_sd_bus_creds)
#define _cleanup_udev_device_ _shl_cleanup_(cleanup_udev_device)
#define _cleanup_udev_enumerate_ _shl_cleanup_(cleanup_udev_enumerate)

View file

@ -261,12 +261,12 @@ static const sd_bus_vtable peer_dbus_vtable[] = {
"ss",
NULL,
peer_dbus_connect,
0),
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Disconnect",
NULL,
NULL,
peer_dbus_disconnect,
0),
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("Link",
"o",
peer_dbus_get_link,
@ -513,6 +513,24 @@ static int link_dbus_get_interface_index(sd_bus *bus,
return 1;
}
static int link_dbus_get_mac_addr(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *data,
sd_bus_error *err)
{
struct link *l = data;
int r;
r = sd_bus_message_append_basic(reply, 's', l->mac_addr);
if (r < 0)
return r;
return 1;
}
static int link_dbus_get_interface_name(sd_bus *bus,
const char *path,
const char *interface,
@ -589,22 +607,46 @@ static int link_dbus_get_managed(sd_bus *bus,
return 1;
}
static int link_dbus_set_managed(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *value,
static int link_dbus_manage(sd_bus_message *msg,
void *data,
sd_bus_error *err)
{
struct link *l = data;
int val, r;
int r = link_manage(l, true);
if(r < 0)
return r;
r = sd_bus_message_read(value, "b", &val);
return sd_bus_reply_method_return(msg, NULL);
}
static int link_dbus_unmanage(sd_bus_message *msg,
void *data,
sd_bus_error *err)
{
struct link *l = data;
int r = link_manage(l, false);
if(r < 0)
return r;
return sd_bus_reply_method_return(msg, NULL);
}
static int link_dbus_get_p2p_state(sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *data,
sd_bus_error *err)
{
struct link *l = data;
int r;
r = sd_bus_message_append(reply, "i", link_get_p2p_state(l));
if (r < 0)
return r;
return link_set_managed(l, val);
return 1;
}
static int link_dbus_get_p2p_scanning(sd_bus *bus,
@ -682,11 +724,26 @@ static int link_dbus_set_wfd_subelements(sd_bus *bus,
static const sd_bus_vtable link_dbus_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("Manage",
NULL,
NULL,
link_dbus_manage,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Unmanage",
NULL,
NULL,
link_dbus_unmanage,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("InterfaceIndex",
"u",
link_dbus_get_interface_index,
0,
SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MACAddress",
"s",
link_dbus_get_mac_addr,
0,
SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("InterfaceName",
"s",
link_dbus_get_interface_name,
@ -697,11 +754,15 @@ static const sd_bus_vtable link_dbus_vtable[] = {
link_dbus_get_friendly_name,
link_dbus_set_friendly_name,
0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("Managed",
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("Managed",
"b",
link_dbus_get_managed,
link_dbus_set_managed,
0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("P2PState",
"i",
link_dbus_get_p2p_state,
0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("P2PScanning",
@ -709,13 +770,13 @@ static const sd_bus_vtable link_dbus_vtable[] = {
link_dbus_get_p2p_scanning,
link_dbus_set_p2p_scanning,
0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_WRITABLE_PROPERTY("WfdSubelements",
"s",
link_dbus_get_wfd_subelements,
link_dbus_set_wfd_subelements,
0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
@ -829,14 +890,17 @@ static int manager_dbus_enumerate(sd_bus *bus,
struct peer *p;
size_t i, peer_cnt;
char **nodes, *node;
int r;
if(strcmp("/org/freedesktop/miracle/wifi", path)) {
return 0;
}
peer_cnt = 0;
MANAGER_FOREACH_LINK(l, m)
if (l->public)
peer_cnt += l->peer_cnt;
nodes = malloc(sizeof(*nodes) * (m->link_cnt + peer_cnt + 2));
nodes = malloc(sizeof(*nodes) * (m->link_cnt + peer_cnt + 1));
if (!nodes)
return log_ENOMEM();
@ -875,13 +939,6 @@ static int manager_dbus_enumerate(sd_bus *bus,
}
}
node = strdup("/org/freedesktop/miracle/wifi");
if (!node) {
r = log_ENOMEM();
goto error;
}
nodes[i++] = node;
nodes[i] = NULL;
*out = nodes;
@ -891,7 +948,7 @@ error:
while (i--)
free(nodes[i]);
free(nodes);
return r;
return log_ENOMEM();
}
int manager_dbus_connect(struct manager *m)

View file

@ -57,6 +57,7 @@ struct peer *link_find_peer_by_label(struct link *l, const char *label)
int link_new(struct manager *m,
unsigned int ifindex,
const char *ifname,
const char *mac_addr,
struct link **out)
{
struct link *l;
@ -84,6 +85,12 @@ int link_new(struct manager *m,
goto error;
}
l->mac_addr = strdup(mac_addr);
if (!l->mac_addr) {
r = log_ENOMEM();
goto error;
}
r = supplicant_new(l, &l->s);
if (r < 0)
goto error;
@ -116,7 +123,10 @@ void link_free(struct link *l)
log_debug("free link: %s (%u)", l->ifname, l->ifindex);
link_set_managed(l, false);
link_manage(l, false);
link_dbus_removed(l);
l->public = false;
link_dbus_removed(l);
l->public = false;
@ -128,9 +138,10 @@ void link_free(struct link *l)
supplicant_free(l->s);
/* link_set_managed(l, false) already removed all peers */
/* link_manage(l, false) already removed all peers */
shl_htable_clear_str(&l->peers, NULL, NULL);
free(l->mac_addr);
free(l->wfd_subelements);
free(l->friendly_name);
free(l->ifname);
@ -148,20 +159,9 @@ bool link_is_using_dev(struct link *l)
return l->use_dev;
}
int link_set_config_methods(struct link *l, char *config_methods)
int link_get_p2p_state(struct link *l)
{
char *cm;
if (!config_methods)
return log_EINVAL();
cm = strdup(config_methods);
if (!cm)
return log_ENOMEM();
free(l->config_methods);
l->config_methods = config_methods;
return 0;
return l->p2p_state;
}
bool link_get_managed(struct link *l)
@ -169,7 +169,7 @@ bool link_get_managed(struct link *l)
return l->managed;
}
int link_set_managed(struct link *l, bool set)
int link_manage(struct link *l, bool set)
{
int r;
@ -179,22 +179,33 @@ int link_set_managed(struct link *l, bool set)
return 0;
if (set) {
log_info("manage link %s", l->ifname);
r = supplicant_start(l->s);
if (r < 0) {
log_error("cannot start supplicant on %s", l->ifname);
return -EFAULT;
}
log_info("acquiring link ownership %s", l->ifname);
} else {
log_info("link %s no longer managed", l->ifname);
log_info("droping link ownership %s", l->ifname);
supplicant_stop(l->s);
}
l->managed = set;
return 0;
}
void link_supplicant_p2p_state_known(struct link *l, int state)
{
if (!l)
return log_vEINVAL();
if (l->p2p_state == state)
return;
if(-1 > state || 1 < state)
return log_vEINVAL();
l->p2p_state = state;
link_dbus_properties_changed(l, "P2PState", NULL);
}
int link_renamed(struct link *l, const char *ifname)
{
char *t;
@ -325,22 +336,39 @@ bool link_get_p2p_scanning(struct link *l)
return supplicant_p2p_scanning(l->s);
}
const char *link_get_mac_addr(struct link *l)
{
if (!l)
return NULL;
return l->mac_addr;
}
void link_supplicant_started(struct link *l)
{
if(l && !l->managed) {
l->managed = true;
link_dbus_properties_changed(l, "Managed", NULL);
}
if (!l || l->public)
return;
if (l->m->friendly_name && l->managed)
link_set_friendly_name(l, l->m->friendly_name);
log_info("link %s managed", l->ifname);
log_info("link is %s managed", l->ifname);
}
void link_supplicant_stopped(struct link *l)
{
if(l && l->managed) {
l->managed = false;
link_dbus_properties_changed(l, "Managed", NULL);
}
if (!l || !l->public)
return;
log_info("link %s unmanaged", l->ifname);
log_info("link is %s unmanaged", l->ifname);
}
void link_supplicant_p2p_scan_changed(struct link *l, bool new_value)

View file

@ -213,8 +213,10 @@ static void supplicant_group_free(struct supplicant_group *g)
}
LINK_FOREACH_PEER(p, g->s->l)
if (p->sp->g == g)
if (p->sp->g == g) {
supplicant_peer_drop_group(p->sp);
return;
}
shl_dlist_unlink(&g->list);
@ -880,7 +882,13 @@ static void supplicant_parse_peer(struct supplicant *s,
* parse it we _definitely_ have to provide proper data. */
r = wpas_message_dict_read(m, "wfd_dev_info", 's', &val);
if (r >= 0) {
t = strdup(val);
/* remove "0x" from the start of wfd_dev_info and prepend
* subelement ID and lenght to it to make it compliant
* with the format of wfd_subelems */
char buf[19] = "000006";
t = strdup(strncmp("0x", val, 2)
? val
: strncat(buf, val + 2, sizeof(buf) - 7));
if (!t) {
log_vENOMEM();
} else {
@ -1518,10 +1526,12 @@ static void supplicant_try_ready(struct supplicant *s)
s->has_wfd = false;
s->running = true;
link_supplicant_started(s->l);
LINK_FOREACH_PEER(p, s->l)
peer_supplicant_started(p);
link_supplicant_started(s->l);
link_supplicant_p2p_state_known(s->l, s->has_p2p ? 1 : -1);
}
static int supplicant_p2p_set_disallow_freq_fn(struct wpas *w,
@ -1696,8 +1706,10 @@ static int supplicant_status_fn(struct wpas *w,
if (!p2p_state) {
log_warning("wpa_supplicant or driver does not support P2P");
link_supplicant_p2p_state_known(s->l, -1);
} else if (!strcmp(p2p_state, "DISABLED")) {
log_warning("P2P support disabled on given interface");
link_supplicant_p2p_state_known(s->l, -1);
} else {
s->has_p2p = true;
@ -1723,6 +1735,8 @@ static int supplicant_status_fn(struct wpas *w,
NULL,
0,
NULL);
wpas_message_unref(m);
m = NULL;
if (r < 0) {
log_vERR(r);
goto error;
@ -1751,6 +1765,8 @@ static int supplicant_status_fn(struct wpas *w,
s,
0,
NULL);
wpas_message_unref(m);
m = NULL;
if (r < 0) {
log_vERR(r);
goto error;
@ -1779,6 +1795,8 @@ static int supplicant_status_fn(struct wpas *w,
s,
0,
NULL);
wpas_message_unref(m);
m = NULL;
if (r < 0) {
log_vERR(r);
goto error;
@ -1895,6 +1913,7 @@ static void supplicant_stopped(struct supplicant *s)
if (s->running) {
s->running = false;
link_supplicant_p2p_state_known(s->l, 0);
link_supplicant_stopped(s->l);
}
}

View file

@ -41,7 +41,6 @@
#include "config.h"
const char *interface_name = NULL;
const char *config_methods = NULL;
unsigned int arg_wpa_loglevel = LOG_NOTICE;
bool use_dev = false;
bool lazy_managed = false;
@ -81,6 +80,8 @@ static void manager_add_udev_link(struct manager *m,
struct link *l;
unsigned int ifindex;
const char *ifname;
const char *mac_addr;
char buf[18];
int r;
ifindex = ifindex_from_udev_device(d);
@ -99,25 +100,23 @@ static void manager_add_udev_link(struct manager *m,
if (shl_startswith(ifname, "p2p-"))
return;
r = link_new(m, ifindex, ifname, &l);
mac_addr = udev_device_get_property_value(d, "ID_NET_NAME_MAC");
mac_addr = mac_addr + strlen(mac_addr) - 12;
snprintf(buf, sizeof(buf), "%.2s:%.2s:%.2s:%.2s:%.2s:%.2s", mac_addr, mac_addr + 2, mac_addr + 4, mac_addr + 6, mac_addr + 8, mac_addr + 10);
r = link_new(m, ifindex, ifname, buf, &l);
if (r < 0)
return;
if (m->friendly_name && l->managed)
link_set_friendly_name(l, m->friendly_name);
if (m->config_methods)
link_set_config_methods(l, m->config_methods);
if(use_dev)
link_use_dev(l);
#ifdef RELY_UDEV
bool managed = udev_device_has_tag(d, "miracle") && !lazy_managed;
if (udev_device_has_tag(d, "miracle") && !lazy_managed) {
#else
bool managed = (!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed;
if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) {
#endif
if (managed) {
link_set_managed(l, true);
link_manage(l, true);
} else {
log_debug("ignored device: %s", ifname);
}
@ -157,12 +156,12 @@ static int manager_udev_fn(sd_event_source *source,
#ifdef RELY_UDEV
if (udev_device_has_tag(d, "miracle") && !lazy_managed)
link_set_managed(l, true);
link_manage(l, true);
else
link_set_managed(l, false);
link_manage(l, false);
#else
if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) {
link_set_managed(l, true);
link_manage(l, true);
} else {
log_debug("ignored device: %s", ifname);
}
@ -181,8 +180,9 @@ static int manager_signal_fn(sd_event_source *source,
struct manager *m = data;
if (ssi->ssi_signo == SIGCHLD) {
siginfo_t info;
log_debug("caught SIGCHLD for %ld, reaping child", (long)ssi->ssi_pid);
waitid(P_PID, ssi->ssi_pid, NULL, WNOHANG|WEXITED);
waitid(P_PID, ssi->ssi_pid, &info, WNOHANG|WEXITED);
return 0;
} else if (ssi->ssi_signo == SIGPIPE) {
/* ignore SIGPIPE */
@ -221,7 +221,6 @@ static void manager_free(struct manager *m)
sd_event_unref(m->event);
free(m->friendly_name);
free(m->config_methods);
free(m);
}
@ -234,7 +233,6 @@ static int manager_new(struct manager **out)
unsigned int i;
sigset_t mask;
int r;
char *cm;
m = calloc(1, sizeof(*m));
if (!m)
@ -242,16 +240,6 @@ static int manager_new(struct manager **out)
shl_htable_init_uint(&m->links);
if (config_methods) {
cm = strdup(config_methods);
if (!cm)
return log_ENOMEM();
free(m->config_methods);
m->config_methods = cm;
}
r = sd_event_default(&m->event);
if (r < 0) {
log_vERR(r);
@ -477,7 +465,6 @@ static int help(void)
" --log-time Prefix log-messages with timestamp\n"
"\n"
" -i --interface Choose the interface to use\n"
" --config-methods Define config methods for pairing, default 'pbc'\n"
"\n"
" --wpa-loglevel <lvl wpa_supplicant log-level\n"
" --use-dev enable workaround for 'no ifname' issue\n"
@ -497,9 +484,10 @@ static int parse_argv(int argc, char *argv[])
ARG_VERSION = 0x100,
ARG_LOG_LEVEL,
ARG_LOG_TIME,
ARG_WPA_LOGLEVEL,
ARG_USE_DEV,
ARG_CONFIG_METHODS,
ARG_LAZY_MANAGED,
};
static const struct option options[] = {
@ -511,7 +499,6 @@ static int parse_argv(int argc, char *argv[])
{ "wpa-loglevel", required_argument, NULL, ARG_WPA_LOGLEVEL },
{ "interface", required_argument, NULL, 'i' },
{ "use-dev", no_argument, NULL, ARG_USE_DEV },
{ "config-methods", required_argument, NULL, ARG_CONFIG_METHODS },
{ "lazy-managed", no_argument, NULL, ARG_LAZY_MANAGED },
{}
};
@ -536,8 +523,6 @@ static int parse_argv(int argc, char *argv[])
case ARG_USE_DEV:
use_dev = true;
break;
case ARG_CONFIG_METHODS:
config_methods = optarg;
case ARG_LAZY_MANAGED:
lazy_managed = true;
break;

View file

@ -130,6 +130,8 @@ struct link {
char *friendly_name;
char *wfd_subelements;
char *config_methods;
char *mac_addr;
int p2p_state; /* 0: unknown, 1: supported, -1: unsupproted */
size_t peer_cnt;
struct shl_htable peers;
@ -152,6 +154,7 @@ struct peer *link_find_peer_by_label(struct link *l, const char *label);
int link_new(struct manager *m,
unsigned int ifindex,
const char *ifname,
const char *mac_addr,
struct link **out);
void link_free(struct link *l);
@ -159,7 +162,7 @@ void link_free(struct link *l);
void link_use_dev(struct link *l);
bool link_is_using_dev(struct link *l);
int link_set_managed(struct link *l, bool set);
int link_manage(struct link *l, bool set);
bool link_get_managed(struct link *l);
int link_renamed(struct link *l, const char *ifname);
@ -170,10 +173,14 @@ int link_set_wfd_subelements(struct link *l, const char *val);
const char *link_get_wfd_subelements(struct link *l);
int link_set_p2p_scanning(struct link *l, bool set);
bool link_get_p2p_scanning(struct link *l);
int link_get_p2p_state(struct link *l);
const char *link_get_mac_addr(struct link *l);
void link_supplicant_started(struct link *l);
void link_supplicant_stopped(struct link *l);
void link_supplicant_p2p_scan_changed(struct link *l, bool new_value);
/* 0: unknown, -1: unsupported, 1: supported */
void link_supplicant_p2p_state_known(struct link *l, int state);
_shl_sentinel_
void link_dbus_properties_changed(struct link *l, const char *prop, ...);