# -*- encoding: UTF-8 -*-
require 'csd/application/minisip/phonebook_example'
require 'csd/application/minisip/component/core_packaging'
module CSD
module Application
module Minisip
# This namespace is reserved for sub-components of this application. This is done for better readability and modularity
# (i.e. less risky to fail in production).
#
module Component
# This MiniSIP component is the very minisip source code itself.
#
module Core
# This is an +Array+ containing the names of the internal MiniSIP libraries. Note that they
# are sorted according to the sequence in which they need to be compiled (because they depend on each other).
#
LIBRARIES = %w{ libmutil libmnetutil libmcrypto libmikey libmsip libmstun libminisip minisip mloggingutil }
class << self
include Packaging
# This method processes the MiniSIP Core component and does everything needed for compiling it. Note that
# it is not responsible for checking depenencies here. It will just focus on compiling the internal MiniSIP libraries.
#
def compile
UI.debug "#{self}.compile was called"
UI.debug "The current Options are: #{::CSD.options.inspect_for_debug}"
remove_ffmpeg
if Path.repository.directory? and !Options.reveal
UI.warn "The MiniSIP source code will not be downloaded, because the directory #{Path.repository.enquote} already exists."
else
checkout
modify_source_code
end
modify_dirlist
compile_libraries # We would like to re-compile MiniSIP no matter what options were given as command-line arguments.
link_libraries
create_address_book
ensure_ati_vsync
update_decklink_firmware
end
# The method removes FFmpeg, if FFmpeg is already installed in the system before MiniSIP compilation.
# This is because MiniSIP use HDViper as its H.264 encoder, while HDviper use modified FFmpeg library within it.
# This may cause conflict with original FFmpeg library. The solution to that is to remove FFmpeg before MiniSIP
# compilation and install it again after completion of MiniSIP compilation process.
# However,if the user set the option "--force-ffmpeg", AI will skip the process, even though FFmpeg may have been
# installed in the system.
# Currently, FFmpeg must have been installed via apt-get or via the AI in order for this to work,
# because manual compilations of FFmpeg cannot be removed automatically.
#
def remove_ffmpeg
ffmpeg_available = Cmd.run('ffmpeg -h', :internal => true, :die_on_failure => false).success?
return if Options.ffmpeg_first or !libraries.include?('libminisip') or !ffmpeg_available
UI.debug "MILESTONE_removing_ffmpeg"
if Gem::Platform.local.debian?
# Note that FFmpeg must have been installed via apt-get or via the AI in order for this to work,
# because manual compilations of FFmpeg cannot be removed automatically
UI.info "Removing FFmpeg before re-compiling MiniSIP".green.bold
UI.info "You can skip this step by giving the option --force-ffmpeg".yellow
Cmd.run "sudo apt-get remove ffmpeg --yes", :announce_pwd => false
else
# On other linux distributions we don't know how to remove ffmpeg
UI.debug "MILESTONE_cannot_remove_ffmpeg"
raise Error::Minisip::Core::FFmpegInstalled, "Please remove ffmpeg from your system first, or run the #{CSD.executable} with --no-configure" unless Options.testmode
end
end
# This method provides upfront information to the user about how the MiniSIP Core component will be processed.
#
def introduction
UI.info " MiniSIP".green.bold
# If the repository directory already exists, we indicate that here
download_text = Path.repository.directory? ? " - located at: " : " - downloading to: "
UI.info download_text.green + Path.repository.to_s.yellow
# Now let's present which libraries will be compiled with which commands
UI.info " - libraries to process: ".green + libraries.join(', ').yellow
UI.info " - with these commands: ".green + [('bootstrap' if Options.bootstrap), ('configure' if Options.configure), ('make' if Options.make), ('make install' if Options.make_install)].compact.join(', ').yellow
end
# Determines which libraries of MiniSIP should be processed, because the --only parameter might be set
# by the user, requesting for only a subset of the libraries.
#
def libraries
Options.only ? LIBRARIES.map { |lib| lib if Options.only.to_a.include?(lib) }.compact : LIBRARIES
end
# Determines which libraries of MiniSIP should be configured using --enable-debug
#
def debug_libraries
Options.enable_debug_on ? LIBRARIES.map { |lib| lib if Options.enable_debug_on.to_a.include?(lib) }.compact : LIBRARIES
end
# This method downloads the minisip source code in the right version. If the Options.branch
# parameter is set to a branchname of the source code repository, that branch will be downloaded. Currently
# this function uses the intermediary Github repository to make sure that
# * the downloaded version is not a risky cutting-edge trunk
# * the download works even if the vendor's repository is down (again)
# That means that the Github repository (or any other intermediary repository) should be manually updated
# by an TTA AI developer, after having made sure that that source code version is working properly.
#
def checkout
# The untested version from the vendor was requested
return checkout_from_vendor if Options.vendor
Cmd.git_clone 'MiniSIP repository', 'http://github.com/csd/minisip.git', Path.repository
# Note that the command above will checkout the master branch.
# In that case we are not allowed to checkout the master branch again.
if Options.branch and Options.branch != 'master'
Cmd.cd Path.repository, :internal => true
Cmd.run "git checkout -b #{Options.branch} origin/#{Options.branch}"
end
UI.info "Initializing any optional git submodules".green.bold
Cmd.cd Path.repository, :internal => true
Cmd.run 'git submodule init'
Cmd.run 'git submodule update'
end
# The method downloads MiniSIP source code from official SVN repository. If the Options.vendor
# parameter is set, AI will not use the default git repository and use SVN to download the source code
# of MiniSIP.
#
def checkout_from_vendor
Cmd.cd Path.work, :internal => true
Cmd.run "svn checkout svn://svn.minisip.org/minisip/trunk minisip"
end
# Some places in the MiniSIP source code have to be modified before MiniSIP can be compiled.
# * An absolute path must be replaced with the current absolute prefix path in
# libminisip/source/subsystem_media/video/display/OpenGLDisplay.cxx
# * The .minisip.conf configuration file generated by MiniSIP has a SIP proxy server by default.
# The configuration _key_ is $proxy_addr$ and the _value_ is $sip.domain.example$. We modify
# the source code file responsible for generating the default configuration file, so that it will
# not fill in a SIP proxy server by default. The reason is that during compilation there is no
# .minisip.conf file that we could edit! So we go for the source.
# * Modifications of some constants will be done, because this is more compatible
# with the most recent FFmpeg version. In fact, MiniSIP won't compile if FFmpeg is present
# and this has not been modified. See http://www.howgeek.com/2010/03/01/ffmpeg-php-error-‘pix_fmt_rgba32’-undeclared-first-use-in-this-function
# and http://ffmpeg.org/doxygen/0.5/pixfmt_8h.html#33d341c4f443d24492a95fb7641d0986 for more information
# about the FFmpeg pixel format constants.
#
def modify_source_code
UI.info "Fixing MiniSIP OpenGL GUI source code".green.bold
# Replacing the hardcoded path in the OpenGLDisplay.cxx
Cmd.replace Path.repository_open_gl_display, /\tstring path = "(.+)"\+/, %{\tstring path = "#{Path.build}"+}
UI.info "Modifying default MiniSIP configuration parameters".green.bold
# Removing the default SIP proxy server from the Configuration generator
Cmd.replace Path.repository_sip_conf, 'sip.domain.example', ''
# We would like decklink to be the default video device
Cmd.replace Path.repository_sip_conf, 'be->commit();', %{
be->save("video_device", "decklink:0/720p50@25");
be->save("display_frame_size", "hd720");
be->save("display_frame_rate", "24");
be->commit();
}
# Switching logging to ON as default, as opposed to OFF
Cmd.replace Path.repository_sip_conf, 'be->saveBool("logging",false)', 'be->saveBool("logging",true)'
if Options.ffmpeg_first
UI.info "Fixing MiniSIP Audio/Video en/decoder source code".green.bold
Cmd.replace Path.repository_avcoder_cxx, 'PIX_FMT_RGBA32', 'PIX_FMT_RGB32'
Cmd.replace Path.repository_avdecoder_cxx, 'PIX_FMT_RGBA32', 'PIX_FMT_RGB32'
end
# Changing MiniSIP HELP Version text
repository_name = Options.vendor ? 'SVN repository' : "Github (#{Options.branch})"
Cmd.replace Path.repository_main_window, '(VERSION)', %{("#{repository_name} via AI #{CSD::Version.gsub("\n", '')}")}
modify_libminisip_rules
end
# The method fixes libminisip Debian rules file.
#
def modify_libminisip_rules
if Path.repository_libminisip_rules_backup.file?
UI.warn "The libminisip rules seem to be fixed already, I won't touch them now. Delete #{Path.repository_libminisip_rules_backup.enquote} to enforce it."
else
Cmd.copy Path.repository_libminisip_rules, Path.repository_libminisip_rules_backup
Cmd.replace Path.repository_libminisip_rules, 'AUTOMATED_INSTALLER_PLACEHOLDER=""', [libminisip_cpp_flags, libminisip_ld_flags].join(' ')
end
end
# Usually, Ubuntu ignores /usr/local/share/aclocal. So we need to create a file called
# +dirlist+ in /usr/share/aclocal which contains the path to the other directory.
#
def modify_dirlist
Path.dirlist = Pathname.new File.join('/', 'usr', 'share', 'aclocal', 'dirlist')
if !Path.dirlist.file? and Gem::Platform.local.debian? or Options.reveal
UI.info "Fixing broken Debian aclocal path".green.bold
Path.new_dirlist = Pathname.new File.join(Path.work, 'dirlist')
Cmd.touch_and_replace_content Path.new_dirlist, '/usr/local/share/aclocal'
Cmd.run "sudo mv #{Path.new_dirlist} #{Path.dirlist}", :announce_pwd => false
end
end
# The method links shared MiniSIP libraries by +ldconfig+ command. +ldconfig+ determines run-time
# linkbindings between ld.so and shared libraries. It scans a running system and sets up the
# symbolic links that are used to load shared libraries properly.
#
def link_libraries
return if Options.this_user
UI.info "Linking shared MiniSIP libraries".green.bold
Cmd.run "sudo ldconfig #{Path.build_lib_libminisip_so}", :announce_pwd => false
end
# The method holds the CPPFLAGS value, where MiniSIP +make+ process is needed. CPPFLAGS is for compiler to find
# the libraries and their header files. When force_ffmpeg option is set, the value of CPPFLAGS will also includes
# the path to related FFmpeg libraries.
#
def libminisip_cpp_flags
if Options.ffmpeg_first
%{CPPFLAGS="-I#{Path.hdviper_x264} -I#{Path.hdviper_x264_test_x264api} -I#{Path.ffmpeg_libavutil} -I#{Path.ffmpeg_libavcodec} -I#{Path.ffmpeg_libswscale} -I#{Path.repository_grabber} -I#{Path.repository_decklinksdk}"}
else
%{CPPFLAGS="-I#{Path.hdviper_x264} -I#{Path.hdviper_x264_test_x264api} -I#{Path.repository_grabber} -I#{Path.repository_decklinksdk}"}
end
end
# The method holds the LDFLAGS value, where MiniSIP +make+ process is needed. CPPFLAGS is for linker to find
# the libraries and their header files.
#
def libminisip_ld_flags
%{LDFLAGS="#{Path.hdviper_libx264api} #{Path.hdviper_libtidx264} -lpthread -lrt"}
end
# Iteratively processes the internal MiniSIP libraries (+bootstrap+, +configure+, +make+, +make install+).
#
def compile_libraries
create_build_dir
UI.debug "MILESTONE_processing_libraries"
libraries.each do |library|
directory = Pathname.new(File.join(Path.repository, library))
# The mloggingutil is more or less optional because it might not be checked in as of today yet
next if !directory.directory? and library == 'mloggingutil'
if Cmd.cd(directory, :internal => true).success? or Options.reveal
UI.debug "MILESTONE_processing_#{library}"
UI.info "Processing MiniSIP -> #{library}".green.bold
bootstrap
configure library
make
make_install
else
UI.warn "Skipping MiniSIP library #{library} because it could not be found in #{directory.enquote}"
end
end
end
# Creates all build directories such as +lib+, +share+, +bin+, etc.
#
def create_build_dir
# In sudo mode, we don't need to create these. They already exist in the OS.
return unless Options.this_user
UI.info "Creating target build directories".green.bold
[Path.build, Path.build_include, Path.build_lib, Path.build_share, Path.build_share_aclocal].each { |target| Cmd.mkdir target }
end
# This method runs the `bootstrap´ command in the current directory unless --no-bootstrap was given.
# It is only used for the internal MiniSIP libraries.
#
def bootstrap
bootstrap! if Options.bootstrap
end
# This method forces running the `bootstrap´ command in the current directory.
# It is only used for the internal MiniSIP libraries.
#
def bootstrap!
if Options.this_user
Cmd.run(%Q{./bootstrap -I "#{Path.build_share_aclocal}"})
else
Cmd.run("./bootstrap")
end
end
# This method runs the `configure´ command in the current directory unless --no-configure was given.
# It is only used for the internal MiniSIP libraries.
#
def configure(name='')
configure! name if Options.configure
end
# This method forces running the `configure´ command in the current directory.
# It is only used for the internal MiniSIP libraries.
# If enable_debug option is set, the configuration option will include "--enable-debug", except for the default options.
# If blank_minisip_configuration option is set, the configuration option will be blank. For example, this option will be
# used during the i2conf server setting-up procedure.
#
def configure!(name='')
individual_options = case name
when 'libminisip'
%Q{--enable-video --enable-opengl --disable-mil --enable-decklink --disable-sdl #{libminisip_cpp_flags} #{libminisip_ld_flags}}
when 'minisip'
%Q{--enable-video --enable-opengl --enable-textui}
else
''
end
# The --enable-debug option should only be there if specifically requested
debug_options = '--enable-debug' if Options.enable_debug or debug_libraries.include?(name)
# These options are used by all libraries
common_options = %Q{--prefix="#{Path.build}" PKG_CONFIG_PATH="#{Path.build_lib_pkg_config}" ACLOCAL_FLAGS="#{Path.build_share_aclocal}" LD_LIBRARY_PATH="#{Path.build_lib}"} if Options.this_user
# I2conf needs to compile MiniSIP without any options
individual_options = nil if Options.blank_minisip_configuration
# Putting it all together
Cmd.run ['./configure', common_options, debug_options, individual_options].compact.join(' ')
end
# This method runs the `make´ command in the current directory unless --no-make was given.
# It is only used for the internal MiniSIP libraries.
#
def make
make! if Options.make
end
# This method forces running the `make´ command in the current directory.
# It is only used for the internal MiniSIP libraries.
# AI uses "-j 15" option of +make+ command to speed up the make process.
#
def make!
Cmd.run "make -j #{Options.threads}"
end
# This method runs the `make install´ command in the current directory unless --no-make-install was given.
# It is only used for the internal MiniSIP libraries.
#
def make_install
make_install! if Options.make_install
end
# This method forces running the `make install´ command in the current directory.
# It is only used for the internal MiniSIP libraries.
#
def make_install!
if Options.this_user
Cmd.run("make install")
else
Cmd.run("sudo make install")
end
end
# Execute the MiniSIP GTK GUI.
#
def run_gtkgui
UI.info "Executing MiniSIP".green.bold
if Options.this_user
Cmd.run Path.build_gtkgui, :die_on_failure => false, :announce_pwd => false
else
Cmd.run 'minisip_gtkgui', :die_on_failure => false, :announce_pwd => false
end
end
# The method create default MiniSIP phonebook for MiniSIP. The default phonebook is derived from carenet-se scenario,
# users can modify it in MiniSIP GUI after MiniSIP successfully installed.
#
def create_address_book
return unless !Path.phonebook.file? or ::CSD::Application::Minisip::OUTDATED_PHONEBOOKS.include?(File.read(Path.phonebook).hashed)
UI.info "Creating default MiniSIP phonebook".green.bold
Cmd.mkdir Path.phonebook_dir
Cmd.touch_and_replace_content Path.phonebook, ::CSD::Application::Minisip::PHONEBOOK_EXAMPLE, :internal => true
UI.info " Phonebook successfully saved in #{Path.phonebook}".yellow
end
# This method is agreeably a little bit out of place. Because this technically should happen whenever
# the graphic card drivers are installed and not when MiniSIP is installed. However, the AI is not present
# when the graphic card drivers are installed (because then X11 is booted down and the AI process is
# replaced by the proprietary ATI or nVidia installation process). So we better make sure at this point,
# that the vertical synching for ATI graphic cards is switched on.
#
# The reason why we want this is because otherwise we will have vertical lines in the video. It is commonly
# referred to as "teared images" and is caused by the monitor having a Hertz rate of 60Hz, while OpenGL has
# a rate of about 400Hz. So the screen picture might be refreshed by OpenGL *while* the monitor is doing his
# own refresh. This will result in a horizontal line on the screen. When they are synchronized, OpenGL will
# be reduced to about 59Hz and the video will be clear. This information was obtained by Erik, the vendor
# of MiniSIP.
#
def ensure_ati_vsync
# Return if no ATI drivers are installed
return unless Path.catalyst_config.file?
UI.info "Ensuring AMD vertical synchronization between OpenGL and Monitor".green.bold
Cmd.run "sudo aticonfig --set-pcs-u32=BUSID-2:0:0-0/OpenGL,VSyncControl,2", :announce_pwd => false
end
# The method updates Decklink Firmware (if needed).
#
def update_decklink_firmware
UI.info "Updating Decklink Firmware (if needed)".green.bold
Cmd.run "BlackmagicFirmwareUpdater update", :announce_pwd => false, :die_on_failure => false
end
end
end
end
end
end
end