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

View file

@ -1,15 +1,12 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 3.0.2)
project(Miraclecast) project(Miraclecast)
SET(PACKAGE_NAME miraclecast) SET(PACKAGE_NAME miraclecast)
SET(PACKAGE_VERSION 1) SET(PACKAGE_VERSION 1)
SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") SET(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
${CMAKE_MODULE_PATH})
set(CMAKE_C_FLAGS "-std=gnu11 ${CMAKE_C_FLAGS}")
add_definitions(-D_GNU_SOURCE)
OPTION(ENABLE_SYSTEMD "Enable Systemd" ON) 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 (GLIB2 REQUIRED glib-2.0)
pkg_check_modules (UDEV REQUIRED libudev) pkg_check_modules (UDEV REQUIRED libudev)
pkg_check_modules (SYSTEMD REQUIRED libsystemd) 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) 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 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. 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. - **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..) *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) - **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') if get_option('build-tests')
subdir('test') subdir('test')
endif endif

View file

@ -1,14 +1,130 @@
INSTALL( include(ValaPrecompile)
PROGRAMS miracle-gst gstplayer uibc-viewer pkg_check_modules(GIO2 REQUIRED gio-2.0)
DESTINATION bin pkg_check_modules(GDK3 REQUIRED gdk-3.0)
) pkg_check_modules(GST1 REQUIRED gstreamer-1.0)
INSTALL( include_directories(
FILES org.freedesktop.miracle.conf ${GST1_INCLUDE_DIRS}
DESTINATION ${SYSCONFDIR}/dbus-1/system.d ${GDK3_INCLUDE_DIRS}
) ${GIO_INCLUDE_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}
)
INSTALL( set(CMAKE_C_FLAGS "-Wno-deprecated-declarations ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-Wno-unused-but-set-variable ${CMAKE_C_FLAGS}")
set(CMAKE_C_FLAGS "-Wno-missing-braces ${CMAKE_C_FLAGS}")
if(VALA_VERSION VERSION_GREATER 0.34.0 AND NOT GDK_VERSION_NEWER_THEN_3_22)
list(APPEND VALA_EXTRA_OPTIONS -D GDK3_HAS_MONITOR_CLASS)
endif()
vala_precompile(
GSTENCODER_SRC gstencoder
gstencoder.vala
OPTIONS
--target-glib=2.50
PACKAGES
gstreamer-1.0
gio-2.0
posix
)
add_executable(gstencoder ${GSTENCODER_SRC})
target_link_libraries(
gstencoder
${GST1_LIBRARIES}
${GDK3_LIBRARIES}
${GIO2_LIBRARIES}
)
vala_precompile(
DISPCTL_SRC dispctl
dispctl.vala
networkmanager.vala
miracle-wifi.vala
miracle-wfd.vala
GENERATE_HEADER
dispctl.h
CUSTOM_VAPIS
sigint.vapi
OPTIONS
--target-glib=2.50
${VALA_EXTRA_OPTIONS}
PACKAGES
gio-2.0
gdk-3.0
)
add_executable(miracle-dispctl ${DISPCTL_SRC} sigint.c)
target_link_libraries(miracle-dispctl ${GIO2_LIBRARIES} ${GDK3_LIBRARIES})
########### install files ###############
install(TARGETS gstencoder DESTINATION bin)
install(TARGETS miracle-dispctl DESTINATION bin)
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=system_bus_services_dir dbus-1
OUTPUT_VARIABLE DBUS_SYSTEM_SERVICES_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=systemdsystemunitdir systemd
OUTPUT_VARIABLE SYSTEMD_SYSTEM_UNIT_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
configure_file(
miracle-wifid.service.cmake
miracle-wifid.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/miracle-wifid.service
DESTINATION ${SYSTEMD_SYSTEM_UNIT_DIR}/
)
install(
CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink miracle-wifid.service \$ENV{DESTDIR}${SYSTEMD_SYSTEM_UNIT_DIR}/dbus-org.freedesktop.miracle.wifi.service)"
)
configure_file(
miracle-dispd.service.cmake
miracle-dispd.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/miracle-dispd.service
DESTINATION ${SYSTEMD_SYSTEM_UNIT_DIR}/
)
install(
CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink miracle-dispd.service \$ENV{DESTDIR}${SYSTEMD_SYSTEM_UNIT_DIR}/dbus-org.freedesktop.miracle.wfd.service)"
)
configure_file(
org.freedesktop.miracle.wifi.service.cmake
org.freedesktop.miracle.wifi.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.miracle.wifi.service
DESTINATION $ENV{DESTDIR}${DBUS_SYSTEM_SERVICES_DIR}/
)
configure_file(
org.freedesktop.miracle.wfd.service.cmake
org.freedesktop.miracle.wfd.service
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.miracle.wfd.service
DESTINATION $ENV{DESTDIR}${DBUS_SYSTEM_SERVICES_DIR}/
)
install(
PROGRAMS miracle-gst gstplayer uibc-viewer
DESTINATION bin
)
install(
FILES org.freedesktop.miracle.conf
DESTINATION ${SYSCONFDIR}/dbus-1/system.d
)
install(
FILES miracle-wifid miracle-sinkctl miracle-wifictl FILES miracle-wifid miracle-sinkctl miracle-wifictl
DESTINATION ${DATADIR}/bash-completion/completions 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: if title:
self.window.set_title(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.window.set_default_size(self.width, self.height)
self.drawingarea = Gtk.DrawingArea() self.drawingarea = Gtk.DrawingArea()
@ -96,10 +98,10 @@ class Player(object):
if scale: if scale:
gstcommand += "videoscale method=1 ! video/x-raw,width="+str(self.width)+",height="+str(self.height)+" ! " gstcommand += "videoscale method=1 ! video/x-raw,width="+str(self.width)+",height="+str(self.height)+" ! "
gstcommand += "autovideosink " gstcommand += "autovideosink sync=false "
if audio: 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) 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("--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("-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("-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("-s", "--scale", metavar="WxH", help="Scale to resolution")
parser.add_argument("-d", "--debug", help="Debug") parser.add_argument("-d", "--debug", help="Debug")
parser.add_argument("--uibc", help="Enable UIBC") parser.add_argument("--uibc", help="Enable UIBC")
@ -216,7 +219,8 @@ if __name__ == '__main__':
# " default VESA %08X\n" # " default VESA %08X\n"
# " default HH %08X\n" # " default HH %08X\n"
parser.add_argument("-r", "--resolution", help="Resolution") 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() args = parser.parse_args()
p = Player(**vars(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( install_data(
'org.freedesktop.miracle.conf', 'org.freedesktop.miracle.conf',
install_dir: join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d') install_dir: join_paths(get_option('sysconfdir'), 'dbus-1', 'system.d')
@ -11,3 +47,33 @@ install_data(
'miracle-wifid', 'miracle-sinkctl', 'miracle-wifictl', 'miracle-wifid', 'miracle-sinkctl', 'miracle-wifictl',
install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions') install_dir: join_paths(get_option('datadir'), 'bash-completion', 'completions')
) )
gio2 = dependency('gio-2.0')
gdk3 = dependency('gdk-3.0')
gst1 = dependency('gstreamer-1.0')
gst1_base = dependency('gstreamer-base-1.0')
add_languages('vala')
valac = meson.get_compiler('vala')
valac_extra_args = []
if valac.version().version_compare('>=0.34') and gdk3.version().version_compare('>=3.22')
valac_extra_args += ['-D', 'GDK3_HAS_MONITOR_CLASS']
endif
executable('gstencoder', 'gstencoder.vala',
dependencies: [gst1, gst1_base, gio2],
install: true,
vala_args: ['--pkg=posix'])
miracle_dispctl_src = ['dispctl.vala',
'sigint.vapi',
'sigint.c',
'networkmanager.vala',
'miracle-wifi.vala',
'miracle-wfd.vala'
]
executable('miracle-dispctl', miracle_dispctl_src,
dependencies: [gio2, gdk3],
vala_args: valac_extra_args,
install: true
)

View file

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

View file

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

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

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

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

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

View file

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

View file

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

114
res/networkmanager.vala Normal file
View file

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

View file

@ -1,6 +1,6 @@
<?xml version="1.0"?> <!--*-nxml-*--> <?xml version="1.0"?> <!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!-- <!--
This file is part of MiracleCast. This file is part of MiracleCast.
@ -8,50 +8,26 @@
<busconfig> <busconfig>
<policy user="root"> <policy user="root">
<allow own="org.freedesktop.miracle"/> <allow own="org.freedesktop.miracle"/>
<allow own="org.freedesktop.miracle.wifi"/> <allow own="org.freedesktop.miracle.wifi"/>
<allow send_destination="org.freedesktop.miracle"/> <allow own="org.freedesktop.miracle.wfd"/>
<allow send_destination="org.freedesktop.miracle.wifi"/> <allow send_destination="org.freedesktop.miracle"/>
<allow receive_sender="org.freedesktop.miracle"/> <allow send_destination="org.freedesktop.miracle.wifi"/>
<allow receive_sender="org.freedesktop.miracle.wifi"/> <allow send_destination="org.freedesktop.miracle.wfd"/>
</policy> <allow receive_sender="org.freedesktop.miracle"/>
<allow receive_sender="org.freedesktop.miracle.wifi"/>
<allow receive_sender="org.freedesktop.miracle.wfd"/>
</policy>
<policy context="default"> <policy context="default">
<deny send_destination="org.freedesktop.miracle"/> <allow send_destination="org.freedesktop.miracle"/>
<deny send_destination="org.freedesktop.miracle.wifi"/> <allow send_destination="org.freedesktop.miracle.wifi"/>
<allow send_destination="org.freedesktop.miracle.wfd"/>
<allow send_destination="org.freedesktop.miracle" <allow receive_sender="org.freedesktop.miracle"/>
send_interface="org.freedesktop.DBus.Introspectable"/> <allow receive_sender="org.freedesktop.miracle.wifi"/>
<allow send_destination="org.freedesktop.miracle.wifi" <allow receive_sender="org.freedesktop.miracle.wfd"/>
send_interface="org.freedesktop.DBus.Introspectable"/> </policy>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.Peer"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.Peer"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.ObjectManager"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.Properties"
send_member="Get"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.Properties"
send_member="Get"/>
<allow send_destination="org.freedesktop.miracle"
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
<allow send_destination="org.freedesktop.miracle.wifi"
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
<allow receive_sender="org.freedesktop.miracle"/>
<allow receive_sender="org.freedesktop.miracle.wifi"/>
</policy>
</busconfig> </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(dhcp)
add_subdirectory(ctl) add_subdirectory(ctl)
add_subdirectory(uibc) add_subdirectory(uibc)
add_subdirectory(disp)
set(miracled_SRCS miracled.h miracled.c) set(miracled_SRCS miracled.h miracled.c)
add_executable(miracled ${miracled_SRCS}) add_executable(miracled ${miracled_SRCS})

View file

@ -50,4 +50,5 @@ endif(READLINE_FOUND)
target_link_libraries(miracle-sinkctl miracle-shared) 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) if (r < 0)
return cli_vERR(r); return cli_vERR(r);
r = rtsp_message_append(rep, "<s>", r = rtsp_message_append(rep, "<&>",
"Public", "Public",
"org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER"); "org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER");
if (r < 0) 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) \ #define check_and_response_option(option, response) \
if (check_rtsp_option(m, option)) { \ if (check_rtsp_option(m, option)) { \
char option_response[512]; \ gchar option_response[512]; \
sprintf(option_response, "%s: %s", option, response); \ sprintf(option_response, "%s: %s", option, response); \
r = rtsp_message_append(rep, "{&}", option_response); \ r = rtsp_message_append(rep, "{&}", option_response); \
if (r < 0) {\ 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_message_unref_ sd_bus_message *m = NULL;
_sd_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL; _sd_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
_shl_free_ char *node = NULL; _shl_free_ char *node = NULL;
const char *method = val ? "Manage" : "Unmanage";
int r; int r;
if (!l) if (!l)
@ -654,26 +655,8 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
&m, &m,
"org.freedesktop.miracle.wifi", "org.freedesktop.miracle.wifi",
node, node,
"org.freedesktop.DBus.Properties", "org.freedesktop.miracle.wifi.Link",
"Set"); method);
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);
if (r < 0) if (r < 0)
return cli_log_create(r); 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); l = ctl_wifi_find_link_by_peer(w, label);
if (!l) if (!l)
return 0; return -EINVAL;
r = ctl_peer_new(&p, l, label); r = ctl_peer_new(&p, l, label);
if (r < 0) 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, r = sd_bus_path_decode(t,
"/org/freedesktop/miracle/wifi/peer", "/org/freedesktop/miracle/wifi/peer",
&label); &label);
@ -1068,6 +1054,38 @@ static int ctl_wifi_peer_fn(sd_bus_message *m,
return 0; 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) static int ctl_wifi_init(struct ctl_wifi *w)
{ {
int r; int r;
@ -1099,6 +1117,18 @@ static int ctl_wifi_init(struct ctl_wifi *w)
if (r < 0) if (r < 0)
return r; 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; 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) void ctl_wifi_free(struct ctl_wifi *w)
{ {
struct ctl_link *l;
if (!w) if (!w)
return; return;
while (!shl_dlist_empty(&w->links)) { ctl_wifi_unlink_all_links(w);
l = shl_dlist_last_entry(&w->links, struct ctl_link, list);
ctl_link_free(l);
}
ctl_wifi_destroy(w); ctl_wifi_destroy(w);
sd_bus_unref(w->bus); sd_bus_unref(w->bus);
free(w); 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) int ctl_wifi_fetch(struct ctl_wifi *w)
{ {
_sd_bus_message_unref_ sd_bus_message *m = NULL; _sd_bus_message_unref_ sd_bus_message *m = NULL;
@ -1173,29 +1234,20 @@ int ctl_wifi_fetch(struct ctl_wifi *w)
return r; return r;
} }
r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}"); r = ctl_wifi_parse_objects(w, m, true);
if (r < 0) if(0 <= r || -EAGAIN != r) {
return cli_log_parser(r); goto end;
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);
} }
if (r < 0)
return cli_log_parser(r);
r = sd_bus_message_exit_container(m); r = sd_bus_message_rewind(m, true);
if (r < 0) if(0 > r) {
return cli_log_parser(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, struct ctl_link *ctl_wifi_find_link(struct ctl_wifi *w,

View file

@ -30,7 +30,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <systemd/sd-bus.h> #include <systemd/sd-bus.h>
#include "shl_dlist.h" #include "shl_dlist.h"
#include "shl_htable.h"
#include "shl_log.h" #include "shl_log.h"
#include "wfd.h"
/* *sigh* readline doesn't include all their deps, so put them last */ /* *sigh* readline doesn't include all their deps, so put them last */
#include <readline/history.h> #include <readline/history.h>
@ -262,3 +264,4 @@ void ctl_fn_sink_resolution_set(struct ctl_sink *s);
void cli_fn_help(void); void cli_fn_help(void);
#endif /* CTL_CTL_H */ #endif /* CTL_CTL_H */

View file

@ -279,10 +279,8 @@ static int cmd_run(char **args, unsigned int n)
return 0; return 0;
} }
if (!l->managed) { if (!l->managed)
cli_printf("link %s not managed\n", l->label); return log_EUNMANAGED();
return 0;
}
run_on(l); run_on(l);
@ -314,10 +312,8 @@ static int cmd_bind(char **args, unsigned int n)
if (!l) if (!l)
return 0; return 0;
if (!l->managed) { if (!l->managed)
cli_printf("link %s not managed\n", l->label); return log_EUNMANAGED();
return 0;
}
run_on(l); run_on(l);
@ -859,9 +855,8 @@ static int ctl_main(int argc, char *argv[])
if (r < 0) if (r < 0)
return r; return r;
left = argc - optind; left = argc - optind;
left = left <= 0 ? 0 : left; r = ctl_interactive(argv + optind, left <= 0 ? 0 : left);
r = ctl_interactive(argv + optind, left);
/* stop all scans */ /* stop all scans */
shl_dlist_for_each(i, &wifi->links) { shl_dlist_for_each(i, &wifi->links) {
@ -912,19 +907,19 @@ static int parse_argv(int argc, char *argv[])
uibc_option = false; uibc_option = false;
uibc_enabled = false; uibc_enabled = false;
external_player = false; external_player = false;
rstp_port = DEFAULT_RSTP_PORT; rstp_port = DEFAULT_RSTP_PORT;
while ((c = getopt_long(argc, argv, "he:p:", options, NULL)) >= 0) { while ((c = getopt_long(argc, argv, "he:p:", options, NULL)) >= 0) {
switch (c) { switch (c) {
case 'h': case 'h':
cli_fn_help(); cli_fn_help();
return 0; return 0;
case ARG_HELP_COMMANDS: case ARG_HELP_COMMANDS:
return cli_help(cli_cmds, 20); return cli_help(cli_cmds, 20);
case ARG_HELP_RES: case ARG_HELP_RES:
wfd_print_resolutions(""); wfd_print_resolutions("");
return 0; return 0;
case ARG_VERSION: case ARG_VERSION:
puts(PACKAGE_STRING); puts(PACKAGE_STRING);
return 0; return 0;
@ -976,74 +971,74 @@ static int parse_argv(int argc, char *argv[])
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int r; int r;
bool free_argv = false; bool free_argv = false;
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
GKeyFile* gkf = load_ini_file(); GKeyFile* gkf = load_ini_file();
gchar** autocmds_free = NULL; gchar** autocmds_free = NULL;
if (gkf) { if (gkf) {
player = g_key_file_get_string (gkf, "sinkctl", "external-player", NULL); player = g_key_file_get_string (gkf, "sinkctl", "external-player", NULL);
if (player) { if (player) {
external_player = true; external_player = true;
} }
gchar* log_level; gchar* log_level;
log_level = g_key_file_get_string (gkf, "sinkctl", "log-journal-level", NULL); log_level = g_key_file_get_string (gkf, "sinkctl", "log-journal-level", NULL);
if (log_level) { if (log_level) {
log_max_sev = log_parse_arg(log_level); log_max_sev = log_parse_arg(log_level);
g_free(log_level); g_free(log_level);
} }
log_level = g_key_file_get_string (gkf, "sinkctl", "log-level", NULL); log_level = g_key_file_get_string (gkf, "sinkctl", "log-level", NULL);
if (log_level) { if (log_level) {
cli_max_sev = log_parse_arg(log_level); cli_max_sev = log_parse_arg(log_level);
g_free(log_level); g_free(log_level);
} }
gchar* rstp_port_str = g_key_file_get_string (gkf, "sinkctl", "rstp-port", NULL); gchar* rstp_port_str = g_key_file_get_string (gkf, "sinkctl", "rstp-port", NULL);
if (rstp_port_str) { if (rstp_port_str) {
rstp_port = atoi(rstp_port_str); rstp_port = atoi(rstp_port_str);
g_free(rstp_port_str); g_free(rstp_port_str);
} }
gchar* autocmd; gchar* autocmd;
autocmd = g_key_file_get_string (gkf, "sinkctl", "autocmd", NULL); autocmd = g_key_file_get_string (gkf, "sinkctl", "autocmd", NULL);
if (autocmd) { if (autocmd) {
gchar** autocmds = g_strsplit(autocmd, " ", -1); gchar** autocmds = g_strsplit(autocmd, " ", -1);
autocmds_free = autocmds; autocmds_free = autocmds;
while (*autocmds) { while (*autocmds) {
if (strcmp(*autocmds, "") != 0) { if (strcmp(*autocmds, "") != 0) {
gchar **newv = malloc((argc + 2) * sizeof(gchar*)); gchar **newv = malloc((argc + 2) * sizeof(gchar*));
memmove(newv, argv, sizeof(gchar*) * argc); memmove(newv, argv, sizeof(gchar*) * argc);
newv[argc] = *autocmds; newv[argc] = *autocmds;
newv[argc+1] = NULL; newv[argc+1] = NULL;
argc++; argc++;
if (free_argv) { if (free_argv) {
free(argv); free(argv);
} }
argv = newv; argv = newv;
free_argv = true; free_argv = true;
} }
autocmds++; autocmds++;
} }
g_free(autocmd); g_free(autocmd);
} }
gchar** sinkctl_keys; gchar** sinkctl_keys;
gsize len = 0; gsize len = 0;
protocol_extensions = g_hash_table_new(g_str_hash, g_str_equal); protocol_extensions = g_hash_table_new(g_str_hash, g_str_equal);
sinkctl_keys = g_key_file_get_keys (gkf, sinkctl_keys = g_key_file_get_keys (gkf,
"sinkctl", "sinkctl",
&len, &len,
NULL); NULL);
for (int i = 0; i < (int)len; i++) { for (int i = 0; i < (int)len; i++) {
if (g_str_has_prefix(sinkctl_keys[i], "extends.")) { if (g_str_has_prefix(sinkctl_keys[i], "extends.")) {
gchar* orig_key = sinkctl_keys[i]; gchar* orig_key = sinkctl_keys[i];
gchar* key = orig_key+8; gchar* key = orig_key+8;
gchar* value = g_key_file_get_string (gkf, "sinkctl", orig_key, NULL); gchar* value = g_key_file_get_string (gkf, "sinkctl", orig_key, NULL);
g_hash_table_insert(protocol_extensions, key, value); g_hash_table_insert(protocol_extensions, key, value);
} }
} }
g_key_file_free(gkf); g_key_file_free(gkf);
} }
r = parse_argv(argc, argv); r = parse_argv(argc, argv);
if (r < 0) if (r < 0)
@ -1058,10 +1053,10 @@ int main(int argc, char **argv)
} }
r = ctl_main(argc, argv); r = ctl_main(argc, argv);
g_strfreev(autocmds_free); g_strfreev(autocmds_free);
if (free_argv) { if (free_argv) {
free(argv); free(argv);
} }
sd_bus_unref(bus); sd_bus_unref(bus);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;

View file

@ -20,109 +20,152 @@
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include "ctl.h" #include "ctl.h"
#include "wfd.h"
#include "util.h"
struct resolution_bitmap { typedef int (*wfd_sube_parse_func)(const char *in, union wfd_sube *out);
int index;
int hres; struct wfd_sube_info
int vres; {
int fps; 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 * CEA resolutions and refrash rate bitmap/index table
* also used in native resolution field * also used in native resolution field
*/ */
struct resolution_bitmap resolutions_cea[] = { static const struct wfd_resolution resolutions_cea[] = {
{0, 640, 480, 60}, /* p60 */ {0, 640, 480, 60, 1}, /* p60 */
{1, 720, 480, 60}, /* p60 */ {1, 720, 480, 60, 1}, /* p60 */
{2, 720, 480, 60}, /* i60 */ {2, 720, 480, 60, 0}, /* i60 */
{3, 720, 576, 50}, /* p50 */ {3, 720, 576, 50, 1}, /* p50 */
{4, 720, 576, 50}, /* i50 */ {4, 720, 576, 50, 0}, /* i50 */
{5, 1280, 720, 30}, /* p30 */ {5, 1280, 720, 30, 1}, /* p30 */
{6, 1280, 720, 60}, /* p60 */ {6, 1280, 720, 60, 1}, /* p60 */
{7, 1920, 1080, 30}, /* p30 */ {7, 1920, 1080, 30, 1}, /* p30 */
{8, 1920, 1080, 60}, /* p60 */ {8, 1920, 1080, 60, 1}, /* p60 */
{9, 1920, 1080, 60}, /* i60 */ {9, 1920, 1080, 60, 0}, /* i60 */
{10, 1280, 720, 25}, /* p25 */ {10, 1280, 720, 25, 1}, /* p25 */
{11, 1280, 720, 50}, /* p50 */ {11, 1280, 720, 50, 1}, /* p50 */
{12, 1920, 1080, 25}, /* p25 */ {12, 1920, 1080, 25, 1}, /* p25 */
{13, 1920, 1080, 50}, /* p50 */ {13, 1920, 1080, 50, 1}, /* p50 */
{14, 1920, 1080, 50}, /* i50 */ {14, 1920, 1080, 50, 0}, /* i50 */
{15, 1280, 720, 24}, /* p24 */ {15, 1280, 720, 24, 1}, /* p24 */
{16, 1920, 1080, 24}, /* p24 */ {16, 1920, 1080, 24, 1}, /* p24 */
{0, 0, 0, 0},
}; };
struct resolution_bitmap resolutions_vesa[] = { static const struct wfd_resolution resolutions_vesa[] = {
{0, 800, 600, 30}, /* p30 */ {0, 800, 600, 30, 1}, /* p30 */
{1, 800, 600, 60}, /* p60 */ {1, 800, 600, 60, 1}, /* p60 */
{2, 1024, 768, 30}, /* p30 */ {2, 1024, 768, 30, 1}, /* p30 */
{3, 1024, 768, 60}, /* p60 */ {3, 1024, 768, 60, 1}, /* p60 */
{4, 1152, 854, 30}, /* p30 */ {4, 1152, 854, 30, 1}, /* p30 */
{5, 1152, 854, 60}, /* p60 */ {5, 1152, 854, 60, 1}, /* p60 */
{6, 1280, 768, 30}, /* p30 */ {6, 1280, 768, 30, 1}, /* p30 */
{7, 1280, 768, 60}, /* p60 */ {7, 1280, 768, 60, 1}, /* p60 */
{8, 1280, 800, 30}, /* p30 */ {8, 1280, 800, 30, 1}, /* p30 */
{9, 1280, 800, 60}, /* p60 */ {9, 1280, 800, 60, 1}, /* p60 */
{10, 1360, 768, 30}, /* p30 */ {10, 1360, 768, 30, 1}, /* p30 */
{11, 1360, 768, 60}, /* p60 */ {11, 1360, 768, 60, 1}, /* p60 */
{12, 1366, 768, 30}, /* p30 */ {12, 1366, 768, 30, 1}, /* p30 */
{13, 1366, 768, 60}, /* p60 */ {13, 1366, 768, 60, 1}, /* p60 */
{14, 1280, 1024, 30}, /* p30 */ {14, 1280, 1024, 30, 1}, /* p30 */
{15, 1280, 1024, 60}, /* p60 */ {15, 1280, 1024, 60, 1}, /* p60 */
{16, 1440, 1050, 30}, /* p30 */ {16, 1440, 1050, 30, 1}, /* p30 */
{17, 1440, 1050, 60}, /* p60 */ {17, 1440, 1050, 60, 1}, /* p60 */
{18, 1440, 900, 30}, /* p30 */ {18, 1440, 900, 30, 1}, /* p30 */
{19, 1440, 900, 60}, /* p60 */ {19, 1440, 900, 60, 1}, /* p60 */
{20, 1600, 900, 30}, /* p30 */ {20, 1600, 900, 30, 1}, /* p30 */
{21, 1600, 900, 60}, /* p60 */ {21, 1600, 900, 60, 1}, /* p60 */
{22, 1600, 1200, 30}, /* p30 */ {22, 1600, 1200, 30, 1}, /* p30 */
{23, 1600, 1200, 60}, /* p60 */ {23, 1600, 1200, 60, 1}, /* p60 */
{24, 1680, 1024, 30}, /* p30 */ {24, 1680, 1024, 30, 1}, /* p30 */
{25, 1680, 1024, 60}, /* p60 */ {25, 1680, 1024, 60, 1}, /* p60 */
{26, 1680, 1050, 30}, /* p30 */ {26, 1680, 1050, 30, 1}, /* p30 */
{27, 1680, 1050, 60}, /* p60 */ {27, 1680, 1050, 60, 1}, /* p60 */
{28, 1920, 1200, 30}, /* p30 */ {28, 1920, 1200, 30, 1}, /* p30 */
{0, 0, 0, 0},
}; };
struct resolution_bitmap resolutions_hh[] = { static const struct wfd_resolution resolutions_hh[] = {
{0, 800, 480, 30}, /* p30 */ {0, 800, 480, 30, 1}, /* p30 */
{1, 800, 480, 60}, /* p60 */ {1, 800, 480, 60, 1}, /* p60 */
{2, 854, 480, 30}, /* p30 */ {2, 854, 480, 30, 1}, /* p30 */
{3, 854, 480, 60}, /* p60 */ {3, 854, 480, 60, 1}, /* p60 */
{4, 864, 480, 30}, /* p30 */ {4, 864, 480, 30, 1}, /* p30 */
{5, 864, 480, 60}, /* p60 */ {5, 864, 480, 60, 1}, /* p60 */
{6, 640, 360, 30}, /* p30 */ {6, 640, 360, 30, 1}, /* p30 */
{7, 640, 360, 60}, /* p60 */ {7, 640, 360, 60, 1}, /* p60 */
{8, 960, 540, 30}, /* p30 */ {8, 960, 540, 30, 1}, /* p30 */
{9, 960, 540, 60}, /* p60 */ {9, 960, 540, 60, 1}, /* p60 */
{10, 848, 480, 30}, /* p30 */ {10, 848, 480, 30, 1}, /* p30 */
{11, 848, 480, 60}, /* p60 */ {11, 848, 480, 60, 1}, /* p60 */
{0, 0, 0, 0},
}; };
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) void wfd_print_resolutions(char * prefix)
{ {
int i; int i;
printf("%sCEA resolutions:\n", prefix); 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, printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
resolutions_cea[i].index, 1 << resolutions_cea[i].index, resolutions_cea[i].index, 1 << resolutions_cea[i].index,
resolutions_cea[i].hres, resolutions_cea[i].vres, resolutions_cea[i].hres, resolutions_cea[i].vres,
resolutions_cea[i].fps); resolutions_cea[i].fps);
} }
printf("%sVESA resolutions:\n", prefix); 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, printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index, resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
resolutions_vesa[i].hres, resolutions_vesa[i].vres, resolutions_vesa[i].hres, resolutions_vesa[i].vres,
resolutions_vesa[i].fps); resolutions_vesa[i].fps);
} }
printf("%sHH resolutions:\n", prefix); 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, printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
resolutions_hh[i].index, 1 << resolutions_hh[i].index, resolutions_hh[i].index, 1 << resolutions_hh[i].index,
resolutions_hh[i].hres, resolutions_hh[i].vres, 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) { if (cea_mask) {
cli_debug("CEA resolutions:"); 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) if ((1 << resolutions_cea[i].index) & cea_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n", cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_cea[i].index, 1 << resolutions_cea[i].index, 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) { if (vesa_mask) {
cli_debug("VESA resolutions:"); 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) if ((1 << resolutions_vesa[i].index) & vesa_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n", cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index, 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) { if (hh_mask) {
cli_debug("HH resolutions:"); 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) if ((1 << resolutions_hh[i].index) & hh_mask)
cli_debug("\t%2d %08x %4dx%4d@%d\n", cli_debug("\t%2d %08x %4dx%4d@%d\n",
resolutions_hh[i].index, 1 << resolutions_hh[i].index, 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) if (!mask)
return -EINVAL; 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) { if ((1 << resolutions_cea[i].index) & mask) {
*vres = resolutions_cea[i].vres; *vres = resolutions_cea[i].vres;
*hres = resolutions_cea[i].hres; *hres = resolutions_cea[i].hres;
@ -192,7 +235,7 @@ int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres)
if (!mask) if (!mask)
return -EINVAL; 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) { if ((1 << resolutions_vesa[i].index) & mask) {
*vres = resolutions_vesa[i].vres; *vres = resolutions_vesa[i].vres;
*hres = resolutions_vesa[i].hres; *hres = resolutions_vesa[i].hres;
@ -209,7 +252,7 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
if (!mask) if (!mask)
return -EINVAL; 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) { if ((1 << resolutions_hh[i].index) & mask) {
*vres = resolutions_hh[i].vres; *vres = resolutions_hh[i].vres;
*hres = resolutions_hh[i].hres; *hres = resolutions_hh[i].hres;
@ -218,3 +261,436 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
} }
return -EINVAL; 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 #ifndef WFD_H
#define 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); 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_cea_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_vesa_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_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 */ #endif /* WFD_H */

View file

@ -240,6 +240,9 @@ static int cmd_set_friendly_name(char **args, unsigned int n)
return 0; return 0;
} }
if (!l->managed)
return log_EUNMANAGED();
return ctl_link_set_friendly_name(l, name); return ctl_link_set_friendly_name(l, name);
} }
@ -310,10 +313,8 @@ static int cmd_p2p_scan(char **args, unsigned int n)
return 0; return 0;
} }
if (!l->managed) { if (!l->managed)
cli_printf("link %s not managed\n", l->label); return log_EUNMANAGED();
return 0;
}
return ctl_link_set_p2p_scanning(l, !stop); return ctl_link_set_p2p_scanning(l, !stop);
} }
@ -362,10 +363,8 @@ static int cmd_connect(char **args, unsigned int n)
pin = ""; pin = "";
} }
if (!p->l->managed) { if (!p->l->managed)
cli_printf("link %s not managed\n", p->l->label); return log_EUNMANAGED();
return 0;
}
return ctl_peer_connect(p, prov, pin); return ctl_peer_connect(p, prov, pin);
} }
@ -389,10 +388,8 @@ static int cmd_disconnect(char **args, unsigned int n)
return 0; return 0;
} }
if (!p->l->managed) { if (!p->l->managed)
cli_printf("link %s not managed\n", p->l->label); return log_EUNMANAGED();
return 0;
}
return ctl_peer_disconnect(p); return ctl_peer_disconnect(p);
} }
@ -605,7 +602,7 @@ static int parse_argv(int argc, char *argv[])
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
switch (c) { switch (c) {
case 'h': case 'h':
cli_fn_help(); cli_fn_help();
return 0; return 0;
case ARG_HELP_COMMANDS: case ARG_HELP_COMMANDS:
return cli_help(cli_cmds, 20); return cli_help(cli_cmds, 20);
@ -638,22 +635,22 @@ int main(int argc, char **argv)
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
GKeyFile* gkf = load_ini_file(); GKeyFile* gkf = load_ini_file();
if (gkf) { if (gkf) {
gchar* log_level; gchar* log_level;
log_level = g_key_file_get_string (gkf, "wifictl", "log-journal-level", NULL); log_level = g_key_file_get_string (gkf, "wifictl", "log-journal-level", NULL);
if (log_level) { if (log_level) {
log_max_sev = log_parse_arg(log_level); log_max_sev = log_parse_arg(log_level);
g_free(log_level); g_free(log_level);
} }
log_level = g_key_file_get_string (gkf, "wifictl", "log-level", NULL); log_level = g_key_file_get_string (gkf, "wifictl", "log-level", NULL);
if (log_level) { if (log_level) {
cli_max_sev = log_parse_arg(log_level); cli_max_sev = log_parse_arg(log_level);
g_free(log_level); g_free(log_level);
} }
g_key_file_free(gkf); g_key_file_free(gkf);
} }
r = parse_argv(argc, argv); r = parse_argv(argc, argv);
if (r < 0) if (r < 0)

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->gateway_nip = client_packet->gateway_nip;
packet->ciaddr = client_packet->ciaddr; packet->ciaddr = client_packet->ciaddr;
dhcp_add_option_uint32(packet, DHCP_SERVER_ID, 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) 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); server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
if (server_id_option) { 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) if (server_nid != dhcp_server->server_nip)
return TRUE; 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('shared')
subdir('wifi') subdir('wifi')
subdir('dhcp') subdir('dhcp')
subdir('ctl') subdir('ctl')
subdir('uibc') subdir('uibc')
subdir('disp')
executable('miracled', 'miracled.c', executable('miracled', 'miracled.c',
dependencies: [libmiracle_shared_dep, m], dependencies: [libmiracle_shared_dep, m],

View file

@ -231,6 +231,11 @@ extern const char *LOG_SUBSYSTEM;
#define log_vEPIPE() \ #define log_vEPIPE() \
((void)log_EPIPE()) ((void)log_EPIPE())
#define log_EPROTO() \
(log_error("protocol error"), -EPROTO)
#define log_vEPROTO() \
((void)log_EPROTO())
#define log_ERRNO() \ #define log_ERRNO() \
(log_error("syscall failed (%d): %m", errno), -errno) (log_error("syscall failed (%d): %m", errno), -errno)
#define log_vERRNO() \ #define log_vERRNO() \
@ -246,4 +251,13 @@ extern const char *LOG_SUBSYSTEM;
#define log_vEUNMANAGED() \ #define log_vEUNMANAGED() \
((void)log_EUNMANAGED()) ((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 */ #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); 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) static inline void cleanup_udev_device(struct udev_device **ptr)
{ {
udev_device_unref(*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 _sd_bus_error_free_ _shl_cleanup_(sd_bus_error_free)
#define _cleanup_sd_bus_message_ _shl_cleanup_(cleanup_sd_bus_message) #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_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_device_ _shl_cleanup_(cleanup_udev_device)
#define _cleanup_udev_enumerate_ _shl_cleanup_(cleanup_udev_enumerate) #define _cleanup_udev_enumerate_ _shl_cleanup_(cleanup_udev_enumerate)

View file

@ -261,12 +261,12 @@ static const sd_bus_vtable peer_dbus_vtable[] = {
"ss", "ss",
NULL, NULL,
peer_dbus_connect, peer_dbus_connect,
0), SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Disconnect", SD_BUS_METHOD("Disconnect",
NULL, NULL,
NULL, NULL,
peer_dbus_disconnect, peer_dbus_disconnect,
0), SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("Link", SD_BUS_PROPERTY("Link",
"o", "o",
peer_dbus_get_link, peer_dbus_get_link,
@ -513,6 +513,24 @@ static int link_dbus_get_interface_index(sd_bus *bus,
return 1; 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, static int link_dbus_get_interface_name(sd_bus *bus,
const char *path, const char *path,
const char *interface, const char *interface,
@ -589,22 +607,46 @@ static int link_dbus_get_managed(sd_bus *bus,
return 1; return 1;
} }
static int link_dbus_set_managed(sd_bus *bus, static int link_dbus_manage(sd_bus_message *msg,
void *data,
sd_bus_error *err)
{
struct link *l = data;
int r = link_manage(l, true);
if(r < 0)
return r;
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 *path,
const char *interface, const char *interface,
const char *property, const char *property,
sd_bus_message *value, sd_bus_message *reply,
void *data, void *data,
sd_bus_error *err) sd_bus_error *err)
{ {
struct link *l = data; struct link *l = data;
int val, r; int r;
r = sd_bus_message_read(value, "b", &val); r = sd_bus_message_append(reply, "i", link_get_p2p_state(l));
if (r < 0) if (r < 0)
return r; return r;
return link_set_managed(l, val); return 1;
} }
static int link_dbus_get_p2p_scanning(sd_bus *bus, 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[] = { static const sd_bus_vtable link_dbus_vtable[] = {
SD_BUS_VTABLE_START(0), 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", SD_BUS_PROPERTY("InterfaceIndex",
"u", "u",
link_dbus_get_interface_index, link_dbus_get_interface_index,
0, 0,
SD_BUS_VTABLE_PROPERTY_CONST), 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", SD_BUS_PROPERTY("InterfaceName",
"s", "s",
link_dbus_get_interface_name, link_dbus_get_interface_name,
@ -697,11 +754,15 @@ static const sd_bus_vtable link_dbus_vtable[] = {
link_dbus_get_friendly_name, link_dbus_get_friendly_name,
link_dbus_set_friendly_name, link_dbus_set_friendly_name,
0, 0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_WRITABLE_PROPERTY("Managed", SD_BUS_PROPERTY("Managed",
"b", "b",
link_dbus_get_managed, 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, 0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_WRITABLE_PROPERTY("P2PScanning", SD_BUS_WRITABLE_PROPERTY("P2PScanning",
@ -709,13 +770,13 @@ static const sd_bus_vtable link_dbus_vtable[] = {
link_dbus_get_p2p_scanning, link_dbus_get_p2p_scanning,
link_dbus_set_p2p_scanning, link_dbus_set_p2p_scanning,
0, 0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_WRITABLE_PROPERTY("WfdSubelements", SD_BUS_WRITABLE_PROPERTY("WfdSubelements",
"s", "s",
link_dbus_get_wfd_subelements, link_dbus_get_wfd_subelements,
link_dbus_set_wfd_subelements, link_dbus_set_wfd_subelements,
0, 0,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END SD_BUS_VTABLE_END
}; };
@ -829,14 +890,17 @@ static int manager_dbus_enumerate(sd_bus *bus,
struct peer *p; struct peer *p;
size_t i, peer_cnt; size_t i, peer_cnt;
char **nodes, *node; char **nodes, *node;
int r;
if(strcmp("/org/freedesktop/miracle/wifi", path)) {
return 0;
}
peer_cnt = 0; peer_cnt = 0;
MANAGER_FOREACH_LINK(l, m) MANAGER_FOREACH_LINK(l, m)
if (l->public) if (l->public)
peer_cnt += l->peer_cnt; 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) if (!nodes)
return log_ENOMEM(); 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; nodes[i] = NULL;
*out = nodes; *out = nodes;
@ -891,7 +948,7 @@ error:
while (i--) while (i--)
free(nodes[i]); free(nodes[i]);
free(nodes); free(nodes);
return r; return log_ENOMEM();
} }
int manager_dbus_connect(struct manager *m) 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, int link_new(struct manager *m,
unsigned int ifindex, unsigned int ifindex,
const char *ifname, const char *ifname,
const char *mac_addr,
struct link **out) struct link **out)
{ {
struct link *l; struct link *l;
@ -84,6 +85,12 @@ int link_new(struct manager *m,
goto error; goto error;
} }
l->mac_addr = strdup(mac_addr);
if (!l->mac_addr) {
r = log_ENOMEM();
goto error;
}
r = supplicant_new(l, &l->s); r = supplicant_new(l, &l->s);
if (r < 0) if (r < 0)
goto error; goto error;
@ -116,7 +123,10 @@ void link_free(struct link *l)
log_debug("free link: %s (%u)", l->ifname, l->ifindex); 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); link_dbus_removed(l);
l->public = false; l->public = false;
@ -128,9 +138,10 @@ void link_free(struct link *l)
supplicant_free(l->s); 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); shl_htable_clear_str(&l->peers, NULL, NULL);
free(l->mac_addr);
free(l->wfd_subelements); free(l->wfd_subelements);
free(l->friendly_name); free(l->friendly_name);
free(l->ifname); free(l->ifname);
@ -181,12 +192,18 @@ int link_set_ip_binary(struct link *l, const char *ip_binary)
return 0; return 0;
} }
int link_get_p2p_state(struct link *l)
{
return l->p2p_state;
}
bool link_get_managed(struct link *l) bool link_get_managed(struct link *l)
{ {
return l->managed; return l->managed;
} }
int link_set_managed(struct link *l, bool set) int link_manage(struct link *l, bool set)
{ {
int r; int r;
@ -196,22 +213,33 @@ int link_set_managed(struct link *l, bool set)
return 0; return 0;
if (set) { if (set) {
log_info("manage link %s", l->ifname);
r = supplicant_start(l->s); r = supplicant_start(l->s);
if (r < 0) { if (r < 0) {
log_error("cannot start supplicant on %s", l->ifname); log_error("cannot start supplicant on %s", l->ifname);
return -EFAULT; return -EFAULT;
} }
log_info("acquiring link ownership %s", l->ifname);
} else { } else {
log_info("link %s no longer managed", l->ifname); log_info("droping link ownership %s", l->ifname);
supplicant_stop(l->s); supplicant_stop(l->s);
} }
l->managed = set;
return 0; 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) int link_renamed(struct link *l, const char *ifname)
{ {
char *t; char *t;
@ -244,6 +272,9 @@ int link_set_friendly_name(struct link *l, const char *name)
if (!l || !name || !*name) if (!l || !name || !*name)
return log_EINVAL(); return log_EINVAL();
if (!l->managed)
return log_EUNMANAGED();
t = strdup(name); t = strdup(name);
if (!t) if (!t)
return log_ENOMEM(); return log_ENOMEM();
@ -339,18 +370,36 @@ bool link_get_p2p_scanning(struct link *l)
return supplicant_p2p_scanning(l->s); 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) 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) if (!l || l->public)
return; return;
if (l->m->friendly_name && l->managed) if (l->m->friendly_name && l->managed)
link_set_friendly_name(l, l->m->friendly_name); link_set_friendly_name(l, l->m->friendly_name);
log_info("link %s managed", l->ifname); log_info("link %s managed", l->ifname);
} }
void link_supplicant_stopped(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) if (!l || !l->public)
return; return;

View file

@ -218,8 +218,10 @@ static void supplicant_group_free(struct supplicant_group *g)
} }
LINK_FOREACH_PEER(p, g->s->l) LINK_FOREACH_PEER(p, g->s->l)
if (p->sp->g == g) if (p->sp->g == g) {
supplicant_peer_drop_group(p->sp); supplicant_peer_drop_group(p->sp);
return;
}
shl_dlist_unlink(&g->list); 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. */ * parse it we _definitely_ have to provide proper data. */
r = wpas_message_dict_read(m, "wfd_dev_info", 's', &val); r = wpas_message_dict_read(m, "wfd_dev_info", 's', &val);
if (r >= 0) { 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) { if (!t) {
log_vENOMEM(); log_vENOMEM();
} else { } else {
@ -1538,10 +1546,12 @@ static void supplicant_try_ready(struct supplicant *s)
s->has_wfd = false; s->has_wfd = false;
s->running = true; s->running = true;
link_supplicant_started(s->l);
LINK_FOREACH_PEER(p, s->l) LINK_FOREACH_PEER(p, s->l)
peer_supplicant_started(p); 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, 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) { if (!p2p_state) {
log_warning("wpa_supplicant or driver does not support P2P"); log_warning("wpa_supplicant or driver does not support P2P");
link_supplicant_p2p_state_known(s->l, -1);
} else if (!strcmp(p2p_state, "DISABLED")) { } else if (!strcmp(p2p_state, "DISABLED")) {
log_warning("P2P support disabled on given interface"); log_warning("P2P support disabled on given interface");
link_supplicant_p2p_state_known(s->l, -1);
} else { } else {
s->has_p2p = true; s->has_p2p = true;
@ -1743,6 +1755,8 @@ static int supplicant_status_fn(struct wpas *w,
NULL, NULL,
0, 0,
NULL); NULL);
wpas_message_unref(m);
m = NULL;
if (r < 0) { if (r < 0) {
log_vERR(r); log_vERR(r);
goto error; goto error;
@ -1771,6 +1785,8 @@ static int supplicant_status_fn(struct wpas *w,
s, s,
0, 0,
NULL); NULL);
wpas_message_unref(m);
m = NULL;
if (r < 0) { if (r < 0) {
log_vERR(r); log_vERR(r);
goto error; goto error;
@ -1799,6 +1815,8 @@ static int supplicant_status_fn(struct wpas *w,
s, s,
0, 0,
NULL); NULL);
wpas_message_unref(m);
m = NULL;
if (r < 0) { if (r < 0) {
log_vERR(r); log_vERR(r);
goto error; goto error;
@ -1915,6 +1933,7 @@ static void supplicant_stopped(struct supplicant *s)
if (s->running) { if (s->running) {
s->running = false; s->running = false;
link_supplicant_p2p_state_known(s->l, 0);
link_supplicant_stopped(s->l); link_supplicant_stopped(s->l);
} }
} }
@ -2452,45 +2471,45 @@ static void supplicant_run(struct supplicant *s, const char *binary)
static int supplicant_find(char **binary) static int supplicant_find(char **binary)
{ {
_shl_free_ char *path = getenv("PATH"); _shl_free_ char *path = getenv("PATH");
if(!path) { if(!path) {
return -EINVAL; return -EINVAL;
} }
path = strdup(path); path = strdup(path);
if(!path) { if(!path) {
return log_ENOMEM(); return log_ENOMEM();
} }
struct stat bin_stat; struct stat bin_stat;
char *curr = path, *next; char *curr = path, *next;
while(1) { while(1) {
curr = strtok_r(curr, ":", &next); curr = strtok_r(curr, ":", &next);
if(!curr) { if(!curr) {
break; break;
} }
_shl_free_ char *bin = shl_strcat(curr, "/wpa_supplicant"); _shl_free_ char *bin = shl_strcat(curr, "/wpa_supplicant");
if (!bin) if (!bin)
return log_ENOMEM(); return log_ENOMEM();
if(stat(bin, &bin_stat) < 0) { if(stat(bin, &bin_stat) < 0) {
if(ENOENT == errno || ENOTDIR == errno) { if(ENOENT == errno || ENOTDIR == errno) {
goto end; goto end;
} }
return log_ERRNO(); return log_ERRNO();
} }
if (!access(bin, X_OK)) { if (!access(bin, X_OK)) {
*binary = strdup(bin); *binary = strdup(bin);
return 0; return 0;
} }
end: end:
curr = NULL; curr = NULL;
} }
return -EINVAL; return -EINVAL;
} }
static int supplicant_spawn(struct supplicant *s) static int supplicant_spawn(struct supplicant *s)
@ -2506,16 +2525,16 @@ static int supplicant_spawn(struct supplicant *s)
log_debug("spawn supplicant of %s", s->l->ifname); log_debug("spawn supplicant of %s", s->l->ifname);
if (supplicant_find(&binary) < 0) { if (supplicant_find(&binary) < 0) {
if (binary != NULL) { if (binary != NULL) {
log_error("execution of wpas (%s) not possible: %m", binary); log_error("execution of wpas (%s) not possible: %m", binary);
} else { } else {
log_error("execution of wpas not possible: %m"); log_error("execution of wpas not possible: %m");
}
return -EINVAL;
} }
return -EINVAL;
}
log_info("wpa_supplicant found: %s", binary); log_info("wpa_supplicant found: %s", binary);
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {

View file

@ -86,6 +86,8 @@ static void manager_add_udev_link(struct manager *m,
struct link *l; struct link *l;
unsigned int ifindex; unsigned int ifindex;
const char *ifname; const char *ifname;
const char *mac_addr;
char buf[18];
int r; int r;
ifindex = ifindex_from_udev_device(d); ifindex = ifindex_from_udev_device(d);
@ -104,7 +106,11 @@ static void manager_add_udev_link(struct manager *m,
if (shl_startswith(ifname, "p2p-")) if (shl_startswith(ifname, "p2p-"))
return; 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) if (r < 0)
return; return;
@ -124,7 +130,7 @@ static void manager_add_udev_link(struct manager *m,
bool managed = (!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed; bool managed = (!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed;
#endif #endif
if (managed) { if (managed) {
link_set_managed(l, true); link_manage(l, true);
} else { } else {
log_debug("ignored device: %s", ifname); log_debug("ignored device: %s", ifname);
} }
@ -164,12 +170,12 @@ static int manager_udev_fn(sd_event_source *source,
#ifdef RELY_UDEV #ifdef RELY_UDEV
if (udev_device_has_tag(d, "miracle") && !lazy_managed) if (udev_device_has_tag(d, "miracle") && !lazy_managed)
link_set_managed(l, true); link_manage(l, true);
else else
link_set_managed(l, false); link_manage(l, false);
#else #else
if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) { if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) {
link_set_managed(l, true); link_manage(l, true);
} else { } else {
log_debug("ignored device: %s", ifname); log_debug("ignored device: %s", ifname);
} }
@ -188,8 +194,9 @@ static int manager_signal_fn(sd_event_source *source,
struct manager *m = data; struct manager *m = data;
if (ssi->ssi_signo == SIGCHLD) { if (ssi->ssi_signo == SIGCHLD) {
siginfo_t info;
log_debug("caught SIGCHLD for %ld, reaping child", (long)ssi->ssi_pid); 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; return 0;
} else if (ssi->ssi_signo == SIGPIPE) { } else if (ssi->ssi_signo == SIGPIPE) {
/* ignore SIGPIPE */ /* ignore SIGPIPE */
@ -595,17 +602,17 @@ int main(int argc, char **argv)
srand(time(NULL)); srand(time(NULL));
GKeyFile* gkf = load_ini_file(); GKeyFile* gkf = load_ini_file();
if (gkf) { if (gkf) {
gchar* log_level; gchar* log_level;
log_level = g_key_file_get_string (gkf, "wifid", "log-level", NULL); log_level = g_key_file_get_string (gkf, "wifid", "log-level", NULL);
if (log_level) { if (log_level) {
log_max_sev = log_parse_arg(log_level); log_max_sev = log_parse_arg(log_level);
g_free(log_level); g_free(log_level);
} }
g_key_file_free(gkf); g_key_file_free(gkf);
} }
r = parse_argv(argc, argv); r = parse_argv(argc, argv);
if (r < 0) if (r < 0)
@ -614,9 +621,9 @@ int main(int argc, char **argv)
return EXIT_SUCCESS; return EXIT_SUCCESS;
if (getuid() != 0) { if (getuid() != 0) {
r = EACCES; r = EACCES;
log_notice("Must run as root"); log_notice("Must run as root");
goto finish; goto finish;
} }
r = manager_new(&m); r = manager_new(&m);

View file

@ -129,6 +129,8 @@ struct link {
char *ifname; char *ifname;
char *friendly_name; char *friendly_name;
char *wfd_subelements; char *wfd_subelements;
char *mac_addr;
int p2p_state; /* 0: unknown, 1: supported, -1: unsupproted */
char *config_methods; char *config_methods;
char *ip_binary; 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, int link_new(struct manager *m,
unsigned int ifindex, unsigned int ifindex,
const char *ifname, const char *ifname,
const char *mac_addr,
struct link **out); struct link **out);
void link_free(struct link *l); 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_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); bool link_get_managed(struct link *l);
int link_renamed(struct link *l, const char *ifname); 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); const char *link_get_wfd_subelements(struct link *l);
int link_set_p2p_scanning(struct link *l, bool set); int link_set_p2p_scanning(struct link *l, bool set);
bool link_get_p2p_scanning(struct link *l); 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_started(struct link *l);
void link_supplicant_stopped(struct link *l); void link_supplicant_stopped(struct link *l);
void link_supplicant_p2p_scan_changed(struct link *l, bool new_value); 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_ _shl_sentinel_
void link_dbus_properties_changed(struct link *l, const char *prop, ...); void link_dbus_properties_changed(struct link *l, const char *prop, ...);