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

run as source

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

2
.gitignore vendored
View file

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

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)
OPTION(ENABLE_SYSTEMD "Enable Systemd" ON)
@ -41,6 +38,19 @@ set(DATADIR "${CMAKE_INSTALL_PREFIX}/share" CACHE STRING "shared data dir")
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

@ -7,7 +7,7 @@
The MiracleCast project provides software to connect external monitors to your system via Wi-Fi. It is compatible to the Wifi-Display specification also known as Miracast. MiracleCast implements the Display-Source as well as Display-Sink side.
The Display-Source side allows you to connect external displays to your system and stream local content to the device. A lot of effort is put into making this as easy as connecting external displays via HDMI. *Note: This is not implemented yet. Please see [#4](../../issues/4).*
The Display-Source side allows you to connect external displays to your system and stream local content to the device. A lot of effort is put into making this as easy as connecting external displays via HDMI.
On the other hand, the Display-Sink side allows you to create wifi-capable external displays yourself. You can use it on your embedded devices or even on full desktops to allow other systems to use your device as external display.
@ -21,9 +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..)
- **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)
- readline**: A library which is used to provide command line interface to control wifid, sink, etc..
- **wpa_supplicant**: MiracleCast spawns wpa_supplicant with a custom config.
- **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 plugins: here are the gstreamer 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

@ -52,4 +52,3 @@ subdir('res')
if get_option('build-tests')
subdir('test')
endif

View file

@ -1,14 +1,130 @@
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(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(
install(
FILES org.freedesktop.miracle.conf
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
)
INSTALL(
install(
FILES miracle-wifid miracle-sinkctl miracle-wifictl
DESTINATION ${DATADIR}/bash-completion/completions
)

6
res/create-symlinks Executable file
View file

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

824
res/dispctl.vala Normal file
View file

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

426
res/gstencoder.vala Normal file
View file

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

View file

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

8
res/install-scripts Executable file
View file

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

View file

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

View file

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

View file

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

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

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

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

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

View file

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

View file

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

114
res/networkmanager.vala Normal file
View file

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

View file

@ -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

@ -50,4 +50,5 @@ endif(READLINE_FOUND)
target_link_libraries(miracle-sinkctl miracle-shared)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/shared)

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

@ -81,7 +81,7 @@ bool check_rtsp_option(struct rtsp_message *m, char *option);
#define check_and_response_option(option, response) \
if (check_rtsp_option(m, option)) { \
char option_response[512]; \
gchar option_response[512]; \
sprintf(option_response, "%s: %s", option, response); \
r = rtsp_message_append(rep, "{&}", option_response); \
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)
@ -654,26 +655,8 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
&m,
"org.freedesktop.miracle.wifi",
node,
"org.freedesktop.DBus.Properties",
"Set");
if (r < 0)
return cli_log_create(r);
r = sd_bus_message_append(m, "ss",
"org.freedesktop.miracle.wifi.Link",
"Managed");
if (r < 0)
return cli_log_create(r);
r = sd_bus_message_open_container(m, 'v', "b");
if (r < 0)
return cli_log_create(r);
r = sd_bus_message_append(m, "b", val);
if (r < 0)
return cli_log_create(r);
r = sd_bus_message_close_container(m);
method);
if (r < 0)
return cli_log_create(r);
@ -823,7 +806,7 @@ static int ctl_wifi_parse_peer(struct ctl_wifi *w,
l = ctl_wifi_find_link_by_peer(w, label);
if (!l)
return 0;
return -EINVAL;
r = ctl_peer_new(&p, l, label);
if (r < 0)
@ -901,6 +884,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 +1054,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 +1117,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 +1165,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 +1234,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

@ -30,7 +30,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"
/* *sigh* readline doesn't include all their deps, so put them last */
#include <readline/history.h>
@ -262,3 +264,4 @@ void ctl_fn_sink_resolution_set(struct ctl_sink *s);
void cli_fn_help(void);
#endif /* CTL_CTL_H */

View file

@ -279,10 +279,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);
@ -314,10 +312,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);
@ -860,8 +856,7 @@ static int ctl_main(int argc, char *argv[])
return r;
left = argc - optind;
left = left <= 0 ? 0 : left;
r = ctl_interactive(argv + optind, left);
r = ctl_interactive(argv + optind, left <= 0 ? 0 : left);
/* stop all scans */
shl_dlist_for_each(i, &wifi->links) {

View file

@ -20,109 +20,152 @@
#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 */
};
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(char * prefix)
{
int i;
printf("%sCEA resolutions:\n", prefix);
for (i = 0; resolutions_cea[i].hres != 0; i++) {
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_cea); i++) {
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
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++) {
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_vesa); i++) {
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
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++) {
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_hh); i++) {
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
resolutions_hh[i].hres, resolutions_hh[i].vres,
@ -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,199 @@
#ifndef WFD_H
#define WFD_H
#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(char * prefix);
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

@ -240,6 +240,9 @@ static int cmd_set_friendly_name(char **args, unsigned int n)
return 0;
}
if (!l->managed)
return log_EUNMANAGED();
return ctl_link_set_friendly_name(l, name);
}
@ -310,10 +313,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);
}
@ -362,10 +363,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);
}
@ -389,10 +388,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;

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

@ -0,0 +1,43 @@
find_package(PkgConfig)
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
link_directories( ${GLIB2_LIBRARY_DIRS})
include_directories( ${GLIB2_INCLUDE_DIRS})
find_package(Readline REQUIRED)
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
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)
target_link_libraries(miracle-dispd m)
target_link_libraries(miracle-dispd ${GLIB2_LIBRARIES})

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;
}

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

@ -0,0 +1,545 @@
/*
* 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"
#define CLI_PROMPT "\001" CLI_BLUE "\002" "[dispctl] # " "\001" CLI_DEFAULT "\002"
#define HISTORY_FILENAME ".miracle-disp.history"
static int dispd_init(struct dispd *dispd, sd_bus *bus);
static void dispd_free(struct dispd *dispd);
static struct dispd_dbus *dispd_dbus = NULL;
char* get_cli_prompt()
{
return CLI_PROMPT;
}
/*
* get history filename
*/
char* get_history_filename()
{
return HISTORY_FILENAME;
}
struct dispd_dbus * dispd_dbus_get()
{
return dispd_dbus;
}
static struct dispd *dispd = NULL;
struct ctl_wifi *get_wifi()
{
return dispd->wifi;
}
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, m],

View file

@ -231,6 +231,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() \
@ -246,4 +251,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);
@ -181,12 +192,18 @@ int link_set_ip_binary(struct link *l, const char *ip_binary)
return 0;
}
int link_get_p2p_state(struct link *l)
{
return l->p2p_state;
}
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;
@ -196,22 +213,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;
@ -244,6 +272,9 @@ int link_set_friendly_name(struct link *l, const char *name)
if (!l || !name || !*name)
return log_EINVAL();
if (!l->managed)
return log_EUNMANAGED();
t = strdup(name);
if (!t)
return log_ENOMEM();
@ -339,8 +370,21 @@ 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;
@ -351,6 +395,11 @@ void link_supplicant_started(struct link *l)
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;

View file

@ -218,8 +218,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);
@ -901,7 +903,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 {
@ -1538,10 +1546,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,
@ -1716,8 +1726,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;
@ -1743,6 +1755,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;
@ -1771,6 +1785,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;
@ -1799,6 +1815,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;
@ -1915,6 +1933,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

@ -86,6 +86,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);
@ -104,7 +106,11 @@ 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;
@ -124,7 +130,7 @@ static void manager_add_udev_link(struct manager *m,
bool managed = (!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);
}
@ -164,12 +170,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);
}
@ -188,8 +194,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 */

View file

@ -129,6 +129,8 @@ struct link {
char *ifname;
char *friendly_name;
char *wfd_subelements;
char *mac_addr;
int p2p_state; /* 0: unknown, 1: supported, -1: unsupproted */
char *config_methods;
char *ip_binary;
@ -153,6 +155,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);
@ -162,7 +165,7 @@ bool link_is_using_dev(struct link *l);
int link_set_ip_binary(struct link *l, const char *ip_binary);
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);
@ -173,10 +176,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, ...);