diff --git a/.github/scripts/Linux/arm/bootstrap.sh b/.github/scripts/Linux/arm/bootstrap.sh index 41773b7ba..4fc6c0161 100755 --- a/.github/scripts/Linux/arm/bootstrap.sh +++ b/.github/scripts/Linux/arm/bootstrap.sh @@ -5,21 +5,21 @@ ARCH=$1 BUILD_DIR=$2 -sudo chroot $BUILD_DIR /bin/sh -c 'if grep -q Raspbian /etc/os-release; then sed -i s-http://deb.debian.org/debian-http://mirrordirector.raspbian.org/raspbian/- /etc/apt/sources.list && apt-get -y update; fi' # https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/1670905 workaround -sudo chroot $BUILD_DIR /bin/sh -c 'apt-get -y install build-essential git pkg-config autoconf automake libtool' -sudo chroot $BUILD_DIR /bin/sh -c 'apt-get -y install portaudio19-dev libsdl2-dev libglib2.0-dev libglew-dev libcurl4-openssl-dev freeglut3-dev libssl-dev libjack-dev libasound2-dev' +sudo chroot $BUILD_DIR /bin/sh -ec 'if grep -q Raspbian /etc/os-release; then sed -i s-http://deb.debian.org/debian-http://mirrordirector.raspbian.org/raspbian/- /etc/apt/sources.list; apt-get -y update; fi' # https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/1670905 workaround +sudo chroot $BUILD_DIR /bin/sh -ec 'apt-get -y install build-essential git pkg-config autoconf automake libtool' +sudo chroot $BUILD_DIR /bin/sh -ec 'apt-get -y install portaudio19-dev libsdl2-dev libglib2.0-dev libglew-dev libcurl4-openssl-dev freeglut3-dev libssl-dev libjack-dev libasound2-dev' if [ $ARCH = armhf ]; then # Raspbian - build own FFmpeg with OMX camera patch - sudo chroot $BUILD_DIR /bin/sh -c "git clone --depth 1 https://github.com/raspberrypi/firmware.git firmware && mv firmware/* / && echo /opt/vc/lib > /etc/ld.so.conf.d/00-vmcs.conf && ldconfig"\ + sudo chroot $BUILD_DIR /bin/sh -ec "git clone --depth 1 https://github.com/raspberrypi/firmware.git firmware && mv firmware/* / && echo /opt/vc/lib > /etc/ld.so.conf.d/00-vmcs.conf && ldconfig"\ "&& sed -i '/^deb /p;s/deb/deb-src/' /etc/apt/sources.list "\ "&& apt-get -y update && apt-get -y build-dep ffmpeg"\ "&& apt-get -y remove libavcodec58 && apt-get -y autoremove"\ "&& git clone --depth 1 https://github.com/FFmpeg/FFmpeg.git && cd FFmpeg"\ "&& git fetch --depth 2 https://github.com/Serveurperso/FFmpeg.git && git cherry-pick FETCH_HEAD"\ "&& ./configure --enable-gpl --disable-stripping --enable-libaom --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libspeex --enable-libvpx --enable-libwebp --enable-libx265 --enable-omx --enable-neon --enable-libx264 --enable-mmal --enable-omx-rpi --cpu=arm1176jzf-s --enable-shared --disable-static && make -j 3 && make install"\ -"&& cd .. && rm -rf FFmpeg" +"&& cd .. && rm -rf FFmpeg || exit 1" else - sudo chroot $BUILD_DIR /bin/sh -c 'apt-get -y install libavcodec-dev libavformat-dev libswscale-dev' + sudo chroot $BUILD_DIR /bin/sh -ec 'apt-get -y install libavcodec-dev libavformat-dev libswscale-dev' fi -sudo chroot $BUILD_DIR /bin/sh -c 'apt-get -y install desktop-file-utils git-core libfuse-dev libcairo2-dev cmake wget zsync' # to build appimagetool -sudo chroot $BUILD_DIR /bin/sh -c 'git clone https://github.com/AppImage/AppImageKit.git && cd AppImageKit && ./build.sh && cd build && cmake -DAUXILIARY_FILES_DESTINATION= .. && make install' -sudo chroot $BUILD_DIR /bin/sh -c 'rm -rf AppImageKit; apt-get -y clean' +sudo chroot $BUILD_DIR /bin/sh -ec 'apt-get -y install desktop-file-utils git-core libfuse-dev libcairo2-dev cmake wget zsync' # to build appimagetool +sudo chroot $BUILD_DIR /bin/sh -ec 'git clone https://github.com/AppImage/AppImageKit.git && cd AppImageKit && ./build.sh && cd build && cmake -DAUXILIARY_FILES_DESTINATION= .. && make install || exit 1' +sudo chroot $BUILD_DIR /bin/sh -ec 'rm -rf AppImageKit; apt-get -y clean' diff --git a/.github/scripts/Linux/arm/build.sh b/.github/scripts/Linux/arm/build.sh index 43b53ed6c..64db4c0b8 100755 --- a/.github/scripts/Linux/arm/build.sh +++ b/.github/scripts/Linux/arm/build.sh @@ -1,7 +1,7 @@ #!/bin/sh -eux export CPATH=/usr/local/include${CPATH:+":$CPATH"} -export EXTRA_LIB_PATH=/usr/local/cuda/lib64:/usr/local/lib +EXTRA_LIB_PATH=/usr/local/cuda/lib64:/usr/local/lib export LIBRARY_PATH=$EXTRA_LIB_PATH${LIBRARY_PATH:+":$LIBRARY_PATH"} export LD_LIBRARY_PATH=$EXTRA_LIB_PATH${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"} export PATH=/usr/local/bin:$PATH diff --git a/.github/scripts/Linux/download_build_ffmpeg.sh b/.github/scripts/Linux/download_build_ffmpeg.sh index 19de88bba..d17f42df2 100755 --- a/.github/scripts/Linux/download_build_ffmpeg.sh +++ b/.github/scripts/Linux/download_build_ffmpeg.sh @@ -1,18 +1,19 @@ #!/bin/bash -eux install_svt() { - ( git clone --depth 1 https://github.com/OpenVisualCloud/SVT-HEVC && cd SVT-HEVC/Build/linux && ./build.sh release && cd Release && make && sudo make install ) - ( git clone --depth 1 -b v0.8.4 https://github.com/OpenVisualCloud/SVT-AV1 && cd SVT-AV1 && cd Build && cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release && make -j $(nproc) && sudo make install ) + ( git clone --depth 1 https://github.com/OpenVisualCloud/SVT-HEVC && cd SVT-HEVC/Build/linux && ./build.sh release && cd Release && make && sudo make install || exit 1 ) + ( git clone --depth 1 -b v0.8.4 https://github.com/OpenVisualCloud/SVT-AV1 && cd SVT-AV1 && cd Build && cmake .. -G"Unix Makefiles" -DCMAKE_BUILD_TYPE=Release && make -j $(nproc) && sudo make install || exit 1 ) git apply SVT-HEVC/ffmpeg_plugin/0001*.patch git apply SVT-AV1/ffmpeg_plugin/0001-Add-ability-for-ffmpeg-to-run-svt-av1.patch } git clone -b n4.3 --depth 1 https://git.ffmpeg.org/ffmpeg.git /var/tmp/ffmpeg # n4.3 is needed for SVT HEVC patch cd /var/tmp/ffmpeg -( git clone --depth 1 -b nasm-2.13.xx https://github.com/sezero/nasm.git && cd nasm && ./autogen.sh && ./configure && make nasm.1 && make ndisasm.1 && make && sudo make install ) -( git clone --depth 1 http://git.videolan.org/git/x264.git && cd x264 && ./configure --disable-static --enable-shared && make && sudo make install ) -( git clone -b sdk/8.1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git && cd nv-codec-headers && make && sudo make install ) -( git clone --depth 1 https://aomedia.googlesource.com/aom && mkdir -p aom/build && cd aom/build && cmake -DBUILD_SHARED_LIBS=1 .. && make && sudo make install ) +sed -i '1,/sliceModeData = 1;/s/sliceModeData = 1;/sliceModeData = 8;/' libavcodec/nvenc.c # only first occurence - for H.264, not HEVC +( git clone --depth 1 -b nasm-2.13.xx https://github.com/sezero/nasm.git && cd nasm && ./autogen.sh && ./configure && make nasm.1 && make ndisasm.1 && make && sudo make install || exit 1 ) +( git clone --depth 1 http://git.videolan.org/git/x264.git && cd x264 && ./configure --disable-static --enable-shared && make && sudo make install || exit 1 ) +( git clone -b sdk/8.1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git && cd nv-codec-headers && make && sudo make install || exit 1 ) +( git clone --depth 1 https://aomedia.googlesource.com/aom && mkdir -p aom/build && cd aom/build && cmake -DBUILD_SHARED_LIBS=1 .. && make && sudo make install || exit 1 ) install_svt ./configure --disable-static --enable-shared --enable-gpl --enable-libx264 --enable-libx265 --enable-libopus --enable-nonfree --enable-nvenc --enable-libaom --enable-libvpx --enable-libspeex --enable-libmp3lame --enable-libsvthevc --enable-libsvtav1 make diff --git a/.github/scripts/Linux/prepare.sh b/.github/scripts/Linux/prepare.sh index 1f92077c6..843d499dc 100755 --- a/.github/scripts/Linux/prepare.sh +++ b/.github/scripts/Linux/prepare.sh @@ -1,17 +1,17 @@ #!/bin/bash -eux -echo "::set-env name=AJA_DIRECTORY::/var/tmp/ntv2sdk" -echo "::set-env name=CPATH::/usr/local/qt/include" -echo "::set-env name=LIBRARY_PATH::/usr/local/qt/lib" -echo "::set-env name=PKG_CONFIG_PATH::/usr/local/qt/lib/pkgconfig" -echo "::add-path::/usr/local/qt/bin" +echo "AJA_DIRECTORY=/var/tmp/ntv2sdk" >> $GITHUB_ENV +echo "CPATH=/usr/local/qt/include" >> $GITHUB_ENV +echo "LIBRARY_PATH=/usr/local/qt/lib" >> $GITHUB_ENV +echo "PKG_CONFIG_PATH=/usr/local/qt/lib/pkgconfig" >> $GITHUB_ENV +echo "/usr/local/qt/bin" >> $GITHUB_PATH if command -v gcc-5; then CUDA_HOST_COMPILER=gcc-5 else CUDA_HOST_COMPILER=gcc-6 fi -echo "::set-env name=CUDA_HOST_COMPILER::$CUDA_HOST_COMPILER" +echo "CUDA_HOST_COMPILER=$CUDA_HOST_COMPILER" >> $GITHUB_ENV sudo sed -n 'p; /^deb /s/^deb /deb-src /p' -i /etc/apt/sources.list # for build-dep ffmpeg sudo apt update @@ -24,13 +24,13 @@ sudo apt install portaudio19-dev libjack-jackd2-dev libasound-dev libv4l-dev # for FFmpeg sudo apt build-dep ffmpeg -sudo apt-get remove 'libx264*' nasm +sudo apt-get -y remove 'libavcodec*' 'libavutil*' 'libswscale*' 'libx264*' nasm sudo apt --no-install-recommends install asciidoc xmlto sudo apt install libopencv-dev sudo apt install libglib2.0-dev libcurl4-nss-dev -( mkdir gpujpeg/build && cd gpujpeg/build && CC=$CUDA_HOST_COMPILER cmake .. && make && sudo make install && sudo ldconfig ) -( sudo apt install uuid-dev && cd cineform-sdk/ && cmake -DBUILD_TOOLS=OFF . && make CFHDCodecStatic ) +( mkdir gpujpeg/build && cd gpujpeg/build && CC=$CUDA_HOST_COMPILER cmake .. && make && sudo make install && sudo ldconfig || exit 1 ) +( sudo apt install uuid-dev && cd cineform-sdk/ && cmake -DBUILD_TOOLS=OFF . && make CFHDCodecStatic || exit 1 ) sudo apt install qtbase5-dev sudo chmod 777 /usr/local diff --git a/.github/scripts/Windows/find_msvc.ps1 b/.github/scripts/Windows/find_msvc.ps1 index 44e727243..f34d56874 100644 --- a/.github/scripts/Windows/find_msvc.ps1 +++ b/.github/scripts/Windows/find_msvc.ps1 @@ -16,7 +16,7 @@ if (-Not $version) { throw "Cannot get MSVS version" } $version = $version.Trim() -echo "::add-path::$installDir\VC\Tools\MSVC\$version\bin\HostX64\x64" # cl -echo "::add-path::$installDir\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin" -echo "::add-path::$installDir\MSBuild\Current\Bin" +echo "$installDir\VC\Tools\MSVC\$version\bin\HostX64\x64" >> ${env:GITHUB_PATH} # cl +echo "$installDir\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin" >> ${env:GITHUB_PATH} +echo "$installDir\MSBuild\Current\Bin" >> ${env:GITHUB_PATH} diff --git a/.github/scripts/Windows/prepare.ps1 b/.github/scripts/Windows/prepare.ps1 index 7c3a723b0..33059e834 100644 --- a/.github/scripts/Windows/prepare.ps1 +++ b/.github/scripts/Windows/prepare.ps1 @@ -9,7 +9,7 @@ if (!${env:no_cuda}) { Invoke-WebRequest https://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda_10.2.89_441.22_win10.exe -OutFile cuda_inst.exe Start-Process -FilePath "cuda_inst.exe" -ArgumentList "-s nvcc_10.2" -Wait -NoNewWindow Remove-Item cuda_inst.exe - echo "::add-path::C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\bin" + echo "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\bin" >> ${env:GITHUB_PATH} } # Install XIMEA @@ -28,7 +28,7 @@ if (${env:SDK_URL} -and ${env:GITHUB_REF} -eq "refs/heads/ndi-build") { Start-Process -FilePath "C:\ndi.exe" -ArgumentList "/VERYSILENT" Sleep 10 $sdk=(dir "C:\Program Files\NewTek" -Filter *SDK -Name) - echo "::add-path::C:\Program Files\NewTek\$sdk\Bin\x64" + echo "C:\Program Files\NewTek\$sdk\Bin\x64" >> ${env:GITHUB_PATH} #Remove-Item C:\ndi.exe } diff --git a/.github/scripts/Windows/prepare_msys.sh b/.github/scripts/Windows/prepare_msys.sh index b5f637fac..8272833d0 100644 --- a/.github/scripts/Windows/prepare_msys.sh +++ b/.github/scripts/Windows/prepare_msys.sh @@ -71,8 +71,8 @@ data/scripts/build_spout64.sh src/SpoutSDK/VS2012/x64/Release wget --no-verbose https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z && 7z x ffmpeg-release-full-shared.7z && cp -r ffmpeg-*build-shared/{bin,lib,include} /usr/local && rm -rf ffmpeg-* || exit 1 # Install GPUJPEG -( wget --no-verbose https://github.com/CESNET/GPUJPEG/releases/download/continuous/GPUJPEG.zip && unzip GPUJPEG.zip && cp -r GPUJPEG/* /usr/local ) +( wget --no-verbose https://github.com/CESNET/GPUJPEG/releases/download/continuous/GPUJPEG.zip && unzip GPUJPEG.zip && cp -r GPUJPEG/* /usr/local || exit 1 ) # Build CineForm -( git submodule update --init cineform-sdk && cd cineform-sdk && cmake -DBUILD_STATIC=false -DBUILD_TOOLS=false -A x64 && MSBuild.exe CineFormSDK.sln -property:Configuration=Release && cp Release/CFHDCodec.dll /usr/local/bin && cp Release/CFHDCodec.lib /usr/local/lib && cp Common/* /usr/local/include && cp libcineformsdk.pc /usr/local/lib/pkgconfig ) +( git submodule update --init cineform-sdk && cd cineform-sdk && cmake -DBUILD_STATIC=false -DBUILD_TOOLS=false -A x64 && MSBuild.exe CineFormSDK.sln -property:Configuration=Release && cp Release/CFHDCodec.dll /usr/local/bin && cp Release/CFHDCodec.lib /usr/local/lib && cp Common/* /usr/local/include && cp libcineformsdk.pc /usr/local/lib/pkgconfig || exit 1 ) diff --git a/.github/scripts/environment.sh b/.github/scripts/environment.sh index 774d3edff..717fee60a 100755 --- a/.github/scripts/environment.sh +++ b/.github/scripts/environment.sh @@ -13,5 +13,5 @@ fi export VERSION TAG -echo "::set-env name=VERSION::$VERSION" -echo "::set-env name=TAG::$TAG" +echo "VERSION=$VERSION" >> $GITHUB_ENV +echo "TAG=$TAG" >> $GITHUB_ENV diff --git a/.github/scripts/macOS/prepare.sh b/.github/scripts/macOS/prepare.sh index f8cb87717..e91876d73 100755 --- a/.github/scripts/macOS/prepare.sh +++ b/.github/scripts/macOS/prepare.sh @@ -5,19 +5,19 @@ TEMP_INST=/tmp/install CPATH=/usr/local/include:/usr/local/opt/qt/include LIBRARY_PATH=/usr/local/lib:/usr/local/opt/qt/lib -echo "::set-env name=AJA_DIRECTORY::$AJA_INST" -echo "::set-env name=UG_SKIP_NET_TESTS::1" -echo "::set-env name=CPATH::$CPATH" -echo "::set-env name=LIBRARY_PATH::$LIBRARY_PATH" +echo "AJA_DIRECTORY=$AJA_INST" >> $GITHUB_ENV +echo "UG_SKIP_NET_TESTS=1" >> $GITHUB_ENV +echo "CPATH=$CPATH" >> $GITHUB_ENV +echo "LIBRARY_PATH=$LIBRARY_PATH" >> $GITHUB_ENV # libcrypto.pc (and other libcrypto files) is not linked to /usr/local/{lib,include} because conflicting with system libcrypto -echo "::set-env name=PKG_CONFIG_PATH::/usr/local/lib/pkgconfig:/usr/local/opt/qt/lib/pkgconfig:/usr/local/opt/openssl/lib/pkgconfig" -echo "::add-path::/usr/local/opt/qt/bin" +echo "PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/opt/qt/lib/pkgconfig:/usr/local/opt/openssl/lib/pkgconfig" >> $GITHUB_ENV +echo "/usr/local/opt/qt/bin" >> $GITHUB_PATH brew install autoconf automake cppunit libtool pkg-config brew install ffmpeg portaudio sdl2 brew install imagemagick jack opencv openssl brew install ossp-uuid # for cineform -( cd cineform-sdk/ && cmake -DBUILD_TOOLS=OFF . && make CFHDCodecStatic ) +( cd cineform-sdk/ && cmake -DBUILD_TOOLS=OFF . && make CFHDCodecStatic || exit 1 ) brew install qt .github/scripts/macOS/install_dylibbundler_v2.sh @@ -57,10 +57,10 @@ if [ -f /var/tmp/sdks/NDISDK_Apple.pkg ]; then export DYLIBBUNDLER_FLAGS="${DYLIBBUNDLER_FLAGS:+$DYLIBBUNDLER_FLAGS }-s /Library/NDI/lib/x64" export LIBRARY_PATH=${LIBRARY_PATH:+"$LIBRARY_PATH:"}/Library/NDI/lib/x64 export MY_DYLD_LIBRARY_PATH="${MY_DYLD_LIBRARY_PATH:+$MY_DYLD_LIBRARY_PATH:}/Library/NDI/lib/x64" - echo "::set-env name=CPATH::$CPATH" - echo "::set-env name=DYLIBBUNDLER_FLAGS::$DYLIBBUNDLER_FLAGS" - echo "::set-env name=LIBRARY_PATH::$LIBRARY_PATH" - echo "::set-env name=MY_DYLD_LIBRARY_PATH::$MY_DYLD_LIBRARY_PATH" + echo "CPATH=$CPATH" >> $GITHUB_ENV + echo "DYLIBBUNDLER_FLAGS=$DYLIBBUNDLER_FLAGS" >> $GITHUB_ENV + echo "LIBRARY_PATH=$LIBRARY_PATH" >> $GITHUB_ENV + echo "MY_DYLD_LIBRARY_PATH=$MY_DYLD_LIBRARY_PATH" >> $GITHUB_ENV cd $TEMP_INST fi diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 257831e94..ad9112bf7 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -81,19 +81,14 @@ jobs: uses: actions/cache@v1 with: path: '/var/tmp/ffmpeg' - key: cache-ffmpeg-8 + key: cache-ffmpeg-9 - name: Build FFmpeg if: steps.cache-ffmpeg.outputs.cache-hit != 'true' run: .github/scripts/Linux/download_build_ffmpeg.sh - name: Install FFmpeg run: .github/scripts/Linux/install_ffmpeg.sh - name: configure - env: - CC: clang-9 - CXX: clang++-9 - CPP: clang-cpp-9 - LDFLAGS: -static-libstdc++ - run: "[ ${{ github.ref }} != refs/heads/ndi-build ] && NDI=-disable-ndi; ./autogen.sh --enable-qt --with-cuda-host-compiler=$CUDA_HOST_COMPILER --enable-plugins --enable-jack-transport=force --with-live555=/usr/local $NDI" + run: "[ ${{ github.ref }} != refs/heads/ndi-build ] && NDI=-disable-ndi; ./autogen.sh --enable-qt --with-cuda-host-compiler=$CUDA_HOST_COMPILER --enable-plugins --with-live555=/usr/local $NDI" - name: make run: make -j4 - name: make check @@ -245,7 +240,7 @@ jobs: - name: bootsrap MSYS2 run: C:\msys64\usr\bin\bash -cel '$GITHUB_WORKSPACE/.github/scripts/Windows/prepare_msys.sh' - name: configure - run: C:\msys64\usr\bin\bash -cel '[ ${{ github.ref }} != refs/heads/ndi-build ] && NDI=-disable-ndi; ./autogen.sh --enable-qt $NDI --with-live555=/usr/local' + run: C:\msys64\usr\bin\bash -cel '[ ${{ github.ref }} != refs/heads/ndi-build ] && NDI=-disable-ndi; ./autogen.sh --prefix=/ --bindir=/ --docdir=/doc --enable-qt $NDI --with-live555=/usr/local' - name: make run: C:\msys64\usr\bin\bash -cel "make -j4" - name: make check @@ -255,11 +250,11 @@ jobs: C:\msys64\usr\bin\bash -cel ' cp gui/QT/uv-qt.exe bin rm bin/run_tests.exe - IFS=\"|\"; for exe in bin/*exe; do for n in `data/scripts/get_dll_depends.sh \"$exe\" | tr \"\n\" \"|\"`; do cp \"$n\" bin; done; done - windeployqt bin/uv-qt.exe - cp data/update.ps1 bin - mkdir build - mv bin build/UltraGrid-$VERSION-win64' + export DESTDIR=build/UltraGrid-$VERSION-win64 + make install + IFS=\"|\"; for exe in $DESTDIR/*exe; do for n in `data/scripts/get_dll_depends.sh \"$exe\" | tr \"\n\" \"|\"`; do cp \"$n\" $DESTDIR; done; done + windeployqt $DESTDIR/uv-qt.exe + cp data/update.ps1 $DESTDIR' - name: make dist-check run: C:\msys64\usr\bin\bash -cel 'PATH= /usr/bin/make distcheck TARGET=build/UltraGrid-$VERSION-win64/uv.exe GUI_EXE=build/UltraGrid-$VERSION-win64/uv-qt.exe' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 56054e7d3..2979d8d53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ way or extend this document. ## Resources -- [Adding modules HOWTO](ADDING-MODULES.md) +- [Adding modules HOWTO](doc/ADDING-MODULES.md) - [Doxygen](https://frakira.fi.muni.cz/~xpulec/ultragrid-doxygen/html/) - [GitHub devel page](https://github.com/CESNET/UltraGrid/wiki/Developer-Documentation) - [Trello board](https://trello.com/b/PjZW4sas/ultragrid-development) tracking the development @@ -27,7 +27,7 @@ way or extend this document. You can either fill an issue at GitHub or contact our development team directly with the e-mail or use a chat. If you suspect that the issue may not be always replicable, you can use a script `ultragrid-bugreport-collect.sh` to collect -data about a computer and attach its result. See also [here](REPORTING-BUGS.md). +data about a computer and attach its result. See also [here](doc/REPORTING-BUGS.md). Especially for various questions about UltraGrid usage you may also use our Matrix chat [![@ultragrid:matrix.org](https://img.shields.io/badge/Matrix-chat-black)](https://matrix.to/#/!IrTYOLJOmZIoTBiITI:matrix.org?via=matrix.org). diff --git a/MODS b/MODS deleted file mode 100644 index 6c4fd518a..000000000 --- a/MODS +++ /dev/null @@ -1,74 +0,0 @@ -MODIFICATIONS FILE ------------------- - -$Revision: 1.1 $ -$Date: 2007/11/08 09:48:58 $ - -Copyright (c) 2001-2004 University of Southern California -Copyright (c) 2003-2004 University of Glasgow -All rights reserved. - -This software is distributed under license, see the file COPYRIGHT for full -terms and conditions. - -v0.0.1 - Initial version, HDTV receiver - * 14 August 2002 - -v0.0.2 - Fix builds with --enable-debug - - Update display code - - Update capture code - * 26 August 2002 - -v0.0.3 - Add "-d " option to select display device - - Add "-t " option to select capture device, and enable transmit - - Reduce RTCP housekeeping frequency - - Disable UI for now - * 27 August 2002 - -v0.1.0 - Add "-m " option to select transmit MTU - - Add TFRC code - - Display in a window using Xvideo (not cleanly implemented) - - Initial test suite (incomplete) - * 8 October 2002 - -v0.1.1 - Performance optimizations for Xvideo display device - * 28 November 2002 (demonstrated at SuperComputing 2002) - -v0.2.0 - Source code reorganization; add audio code (unused) - * 18 August 2003 - -v0.2.1 - Cleanup configure script, removing duplicate tests - - Update configure script to explicitly test for sched_setscheduler() - since some platforms (e.g. MacOS X) don't have it. - - Update configure script and video display routines to partially - support the case where X11 is not present. - - Fix -v option - - Rewrite playout buffer code - - Rewrite video display probing - - Removing scatter-read from the RTP code - - Framerate now tunable using "-f " - * 2 May 2004 - -v0.2.2 - Check reported loss fraction, and abort if excessive - - Playout buffer now uses a fixed 32ms playout delay, equivalent - to 2 frames at 60fps, instead of decoding frames immediately. - * 10 May 2004 - -v0.3.0 - Update documentation - - Add initial FireWire/DV support - - Add initial video codec API - - Add initial MacOS X audio driver (contributed to rat by Juraj Sucik) - - Add initial AccessGrid service plugins - - Add initial participant database framework, to eventually allow - multiple participants - * 13 August 2004 - -v0.3.1 - Fix crash in RTP code if getpwuid() fails (patch contributed to - rat by , and adapted for UltraGrid) - - Update AES code code to rijndael-fst-3.0.zip, taken from: - http://www.esat.kuleuven.ac.be/~rijmen/rijndael/ - This now passes the official NIST AES test suite (included). - - Add initial TFRC code to RTP library - - Update ALSA code with Steve Smith's ALSA 0.9+/final audio driver - * 26 October 2004 - diff --git a/Makefile.in b/Makefile.in index c66e9f2fd..99c4a5132 100644 --- a/Makefile.in +++ b/Makefile.in @@ -13,10 +13,9 @@ CXXFLAGS = @CXXFLAGS@ $(COMMON_FLAGS) -D_GNU_SOURCE MKDIR_P = mkdir -p CUDA_FLAGS = @CUDA_FLAGS@ @CUDA_COMPUTE_ARGS@ LDFLAGS = @LDFLAGS@ -LIBS += @LIBS@ @JACK_TRANS_LIB@ @MATHLIBS@ -lm -pthread +LIBS += @LIBS@ @MATHLIBS@ -lm -pthread INC = -Isrc -I$(srcdir) -I$(srcdir)/src -I$(srcdir)/test -Idxt_compress \ - @JACK_TRANS_INC@ @SPEEX_INC@ \ - @CUDA_INC@ @INC@ + @SPEEX_INC@ @CUDA_INC@ @INC@ DECKLINK_PATH = @DECKLINK_PATH@ DYLIBBUNDLER = @DYLIBBUNDLER@ DYLIBBUNDLER_FLAGS += @DYLIBBUNDLER_FLAGS@ @@ -28,13 +27,13 @@ GUI_BUNDLE = gui/QT/uv-qt.app DXT_GLSL_CFLAGS = @DXT_GLSL_CFLAGS@ CUDA_COMPILER = @CUDA_COMPILER@ SYSTEM = @system@ -APPEXT = @APPEXT@ GUI_EXE = @GUI_EXE@ GUI_TARGET = @GUI_TARGET@ REFLECTOR_TARGET = bin/hd-rum-transcode$(EXEEXT) TEST_TARGET = bin/run_tests$(EXEEXT) +PACKAGE_TARNAME ?= @PACKAGE_TARNAME@ PREFIX = @prefix@ prefix = $(PREFIX) exec_prefix = @exec_prefix@ @@ -43,14 +42,13 @@ bindir = @bindir@ libdir = @libdir@ datadir = @datadir@ datarootdir = @datarootdir@ -docdir = @docdir@/ultragrid -uv_datadir = @datadir@/ultragrid +docdir = @docdir@ mandir = @mandir@ man1dir = @mandir@/man1 srcdir = @srcdir@ VPATH = @srcdir@ -DOCS = $(srcdir)/CONTRIBUTING.md $(srcdir)/README.md $(srcdir)/REPORTING-BUGS.md +DOCS = $(srcdir)/CONTRIBUTING.md $(srcdir)/README.md $(wildcard $(srcdir)/doc/*) # autogenerated headers GENERATED_HEADERS = @GENERATED_HEADERS@ @@ -464,7 +462,7 @@ dxt_compress/dxt_glsl.h:dxt_compress/compress_vp.glsl \ #cat dxt_compress/rgba_to_yuv422_vp.glsl | sed 's/\(.*\)/ \"\1\\n\"/' >> $@ #echo ";" >> $@ -gui/QT/uv-qt$(APPEXT): $(wildcard $(srcdir)/gui/QT/*.cpp $(srcdir)/gui/QT/*.hpp) $(srcdir)/src/shared_mem_frame.cpp $(srcdir)/tools/astat.h +$(GUI_TARGET): $(wildcard $(srcdir)/gui/QT/*.cpp $(srcdir)/gui/QT/*.hpp) $(srcdir)/src/shared_mem_frame.cpp $(srcdir)/tools/astat.h $(MKDIR_P) $(dir $@) if test -z "$(QMAKE)"; then echo "Reconfigure with '--enable-qt'"; exit 1; fi cd gui/QT && $(QMAKE) "DESTDIR+=./" $(srcdir)/../../gui/QT && make -j 4 @@ -667,12 +665,11 @@ install: all $(INSTALL) -d -m 755 $(DESTDIR)$(libdir)/ultragrid;\ $(INSTALL) -m 755 @MODULES@ $(DESTDIR)$(libdir)/ultragrid;\ fi - $(INSTALL) -d -m 755 $(DESTDIR)$(uv_datadir) - $(INSTALL) -m 755 $(srcdir)/data/ultragrid-bugreport-collect.sh $(DESTDIR)$(uv_datadir) $(INSTALL) -d -m 755 $(DESTDIR)$(docdir) $(INSTALL) -m 644 $(DOCS) $(DESTDIR)$(docdir) - $(INSTALL) -m 644 $(srcdir)/COPYRIGHT $(DESTDIR)$(docdir) + $(INSTALL) -m 644 $(srcdir)/CONTRIBUTING.md $(srcdir)/COPYRIGHT $(srcdir)/INSTALL $(srcdir)/NEWS $(srcdir)/README.md $(DESTDIR)$(docdir) $(CP) $(srcdir)/speex-1.2rc1/COPYING $(DESTDIR)$(docdir)/COPYING.speex + $(INSTALL) -m 755 $(srcdir)/data/ultragrid-bugreport-collect.sh $(DESTDIR)$(docdir) $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) $(INSTALL) -m 644 $(srcdir)/data/uv.1 $(srcdir)/data/hd-rum-transcode.1 $(DESTDIR)$(man1dir) if [ -n '@DLL_LIBS@' ]; then $(INSTALL) -m 644 @DLL_LIBS@ $(DESTDIR)$(bindir); fi @@ -681,11 +678,10 @@ uninstall: $(RM) $(DESTDIR)$(bindir)/uv $(RM) $(DESTDIR)$(bindir)/hd-rum-transcode if [ -n "@MODULES@" ]; then for n in @MODULES@; do $(RM) $(DESTDIR)$(libdir)/ultragrid/`basename $$n`; done; fi - $(RM) $(DESTDIR)$(uv_datadir)/ultragrid-bugreport-collect.sh for n in $(DOCS); do $(RM) $(DESTDIR)$(docdir)$$n; done; - $(RM) $(DESTDIR)$(docdir)COPYRIGHT - $(RM) $(DESTDIR)$(docdir)COPYING.speex - $(RM) $(DESTDIR)$(uv_datadir)ultragrid-bugreport-collect.sh + $(RM) $(DESTDIR)$(docdir)/CONTRIBUTING.md $(DESTDIR)$(docdir)/COPYRIGHT $(DESTDIR)$(docdir)/INSTALL $(DESTDIR)$(docdir)/NEWS $(DESTDIR)$(docdir)/README.md + $(RM) $(DESTDIR)$(docdir)/COPYING.speex + $(RM) $(DESTDIR)$(docdir)/ultragrid-bugreport-collect.sh if [ -f "$(GUI_TARGET)" ]; then \ $(RM) $(DESTDIR)$(bindir)/`basename $(GUI_TARGET)`;\ $(RM) $(DESTDIR)$(datadir)/applications/uv-qt.desktop;\ diff --git a/NEWS b/NEWS index b8175ef7b..7f75a6dda 100644 --- a/NEWS +++ b/NEWS @@ -146,3 +146,85 @@ next target system has installed CUDA Toolkit. * format detection for DVS cards * DVS support for Mac OS X + +0.3.1 +===== +* Fix crash in RTP code if getpwuid() fails (patch contributed to + rat by , and adapted for UltraGrid) +* Update AES code code to rijndael-fst-3.0.zip, taken from: + http://www.esat.kuleuven.ac.be/~rijmen/rijndael/ + This now passes the official NIST AES test suite (included). +* Add initial TFRC code to RTP library +* Update ALSA code with Steve Smith's ALSA 0.9+/final audio driver +* 26 October 2004 + +0.3.0 +===== +* Update documentation +* Add initial FireWire/DV support +* Add initial video codec API +* Add initial MacOS X audio driver (contributed to rat by Juraj Sucik) +* Add initial AccessGrid service plugins +* Add initial participant database framework, to eventually allow + multiple participants +* 13 August 2004 + +0.2.2 +===== +* Check reported loss fraction, and abort if excessive +* Playout buffer now uses a fixed 32ms playout delay, equivalent + to 2 frames at 60fps, instead of decoding frames immediately. +* 10 May 2004 + +v0.2.1 +====== +* Cleanup configure script, removing duplicate tests +* Update configure script to explicitly test for sched_setscheduler() + since some platforms (e.g. MacOS X) don't have it. +* Update configure script and video display routines to partially + support the case where X11 is not present. +* Fix -v option +* Rewrite playout buffer code +* Rewrite video display probing +* Removing scatter-read from the RTP code +* Framerate now tunable using "-f " +* 2 May 2004 + +0.2.0 +===== +* Source code reorganization; add audio code (unused) +* 18 August 2003 + +0.1.1 +===== +* Performance optimizations for Xvideo display device +* 28 November 2002 (demonstrated at SuperComputing 2002) + +0.1.0 +===== +* Add "-m " option to select transmit MTU +* Add TFRC code +* Display in a window using Xvideo (not cleanly implemented) +* Initial test suite (incomplete) +* 8 October 2002 + +0.0.3 +===== +* Add "-d " option to select display device +* Add "-t " option to select capture device, and enable transmit +* Reduce RTCP housekeeping frequency +* Disable UI for now +* 27 August 2002 + +0.0.2 +===== +* Fix builds with --enable-debug +* Update display code +* Update capture code +* 26 August 2002 + +0.0.1 +===== +* Initial version, HDTV receiver +* 14 August 2002 + diff --git a/README.md b/README.md index 490f53b71..8218f4ced 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,15 @@ UltraGrid - A High Definition Collaboratory [COPYRIGHT](COPYRIGHT) for full terms and conditions. +Contents +-------- + + - [About UltraGrid](#about-ultragrid) + - [Hardware and Software Requirements](#hardware-and-software-requirements) + * [Required Software Preliminaries](#required-software-preliminaries) + - [Using the UltraGrid System](#using-the-ultragrid-system) + - [Documentation](#documentation) + About UltraGrid --------------- @@ -63,8 +72,8 @@ About UltraGrid INSTALL Installation instructions NEWS Change log and modification history README.md This file - REPORTING-BUGS.md Recommendations for reporting bugs bin/ Compiled binaries + doc/ Documentation src/ Source code for the UltraGrid system test/ Source code and binaries for test routines Makefile.in Build script @@ -150,109 +159,23 @@ Using the UltraGrid System Using different supported compression schemes, the needed network capacity can be as low as 10 Megabits per second for a high definition video. +Documentation +------------- + Documentation can be found either _offline_ (apart from this document) and + _online_. The online documentation is more comprehensive and up-to-date, + offline is rather complementary. -Performance Tuning: Network ---------------------------- + The **online** documentation is available in our GitHub + [wiki](https://github.com/CESNET/UltraGrid/wiki). - If transmitting *uncompressed video* stream to achieve optimal performance - with high definition video, it may be necessary to tune your system's - network parameters to more aggressive values than used by default. + UltraGrid _built-in_ documentation can be found in [doc](doc) subdirectory, + these documents are available: - A key factor affecting performance is the path MTU. It is unlikely that - the system will sustain gigabit rates with the 1500 octet Ethernet MTU. - If using a gigabit Ethernet you may be able to improve performance by - setting an 8192 octet MTU on the interface, provided all intermediate - hops on the path from sender to receiver support the large MTU. - - UltraGrid attempts to increase the UDP receive socket buffer from the - default value (typically 64 kilobytes) to 4/6 megabytes. If successful, - this will make the system more robust to scheduling variations and - better able to accept bursty packet arrivals. UltraGrid will notify - you if it cannot increase buffers. You should follow those instructions - and set your system according to it. - - Interrupt processing load on the receiver host may be significant when - running at high rates. Depending on your network interface hardware it - may be possible to coalesce interrupts to reduce this load, although - the settings to do this are highly driver dependent. On FreeBSD, the - use of network device polling may also help performance: see the man - page for "polling" in section 4 of the manual. - - In many cases, the performance of your network interface card may be - limited by host bus performance (this is particularly an issue at high - rates, for example when using HD format video). + - [Adding modules](doc/ADDING-MODULES.md) (**developers only**) - information + how to add new UltraGrid modules + - [Performance tuning](doc/PERFORMANCE-TUNING.md) - various tweaks to improve + UltraGrid performance + - [Reporting bugs](doc/REPORTING_BUGS.md) - recommended steps for reporting + bugs -Performance Tuning: Display devices ------------------------------------ - - If using a HW grabbing card (eg. DVS) as a display device, the - key factor limiting performance is PCI bus contention. Ensure that - the grabbing card is on a separate PCI bus to the network card -- - this typically requires a server class motherboard. On Linux, the - PCI bus topology can be displayed using "lspci -tv", for example: - - [root@ormal root]# lspci -tv - -+-[03]---06.0 Xilinx, Inc.: Unknown device d150 - +-[01]-+-02.0-[02]--+-04.0 Adaptec 7899P - | | \-04.1 Adaptec 7899P - | \-0e.0 3Com Corporation 3c985 1000BaseSX - \-[00]-+-00.0 ServerWorks CNB20HE - +-00.1 ServerWorks CNB20HE - +-00.2 ServerWorks: Unknown device 0006 - +-00.3 ServerWorks: Unknown device 0006 - +-04.0 Intel Corporation 82557 [Ethernet Pro 100] - +-0e.0 ATI Technologies Inc Rage XL - +-0f.0 ServerWorks OSB4 - \-0f.1 ServerWorks: Unknown device 0211 - [root@ormal root]# - - showing an DVS card on PCI bus [03] (the card shows as a Xilinx - device) and a gigabit Ethernet card on PCI bus [02] (the 3Com entry). - - For software display, you can use SDL or OpenGL display. Both are - accelerated (Mac and Linux) if you have properly configured video - drivers. On Linux, basic operability can be checked with following - commands. If configured properly, both should display driver - properties: - [root@ormal root]# glxinfo - <-- output omitted --> - and for SDL (accelerated through XVideo: - [root@ormal root]# xvinfo - <-- output omitted --> - - If you intend to use some of DXT compressions, recommended driver - is OpenGL, which can display it natively. When using other display - drivers, decompression is still done throught OpenGL and then displayed - with requested video driver. - - -Performance Tuning: Other Factors ---------------------------------- - - The UltraGrid system will attempt to enable POSIX real-time scheduling - to improve performance. This behaviour is disabled by default now, because - it can occupy the whole system when enabled, but it can be stil enabled by - '--enable-rt' configure option. If you see the message: - - WARNING: Unable to set real-time scheduling - - when starting the application, this means that the operating system did - not permit it to enable real-time scheduling. The application will run, - but with reduced performance. The most likely reason for failure to set - realtime scheduling is that the application has insufficient privilege: - it should either be run by root, or be made setuid root. A similar - message: - - WARNING: System does not support real-time scheduling - - indicates that your operating system does not support POSIX real-time - scheduling. The application will still run, but performance may be less - than desired. - - - You can find more operating system tweaks at this page: - https://github.com/CESNET/UltraGrid/wiki/OS-Setup-UltraGrid - - - * - - diff --git a/configure.ac b/configure.ac index e1760aa1e..9edacec5e 100644 --- a/configure.ac +++ b/configure.ac @@ -132,9 +132,9 @@ if expr "$host_os" : ".*darwin.*" > /dev/null; then system=MacOSX AC_DEFINE([HAVE_MACOSX], [1], [This is Mac X OS]) APPEXT=.app - AC_SUBST(APPEXT) elif expr "$host_os" : ".*mingw32.*" > /dev/null || expr "$host_os" : ".*msys.*" > /dev/null; then system=Windows + APPEXT=.exe AC_DEFINE([WIN32], [1], [This is an Windows OS]) else system=Linux @@ -2290,10 +2290,10 @@ fi # ------------------------------------------------------------------------------------------------- gpujpeg_to_dxt=no AC_ARG_ENABLE(gpujpeg_to_dxt, -[ --disable-jpeg-to-dxt disable GPUJPEG DXT transcoder (default is auto)] +[ --disable-jpeg-to-dxt disable GPUJPEG DXT transcoder (default is disable)] [ Requires: CUDA libgpujpeg], [gpujpeg_to_dxt_req=$enableval], - [gpujpeg_to_dxt_req=$build_default]) + [gpujpeg_to_dxt_req=no]) if test $gpujpeg_to_dxt_req != no -a $FOUND_CUDA = yes -a \ "$found_gpujpeg" = yes @@ -2302,7 +2302,7 @@ then DEFINE_CUDA_DXT gpujpeg_to_dxt=yes GPUJPEG_TO_DXT_INC=" $CUDA_INC" - GPUJPEG_TO_DXT_LIB="$CUDA_DXT_COMMON_LIB $CUDA_COMMON_LIB $CUDA_LIB $JPEG_LIB" + GPUJPEG_TO_DXT_LIB="$CUDA_DXT_COMMON_LIB $CUDA_COMMON_LIB $CUDA_LIB $GPUJPEG_LIB" GPUJPEG_TO_DXT_OBJ="src/video_decompress/gpujpeg_to_dxt.o $CUDA_COMMON_OBJ $CUDA_DXT_COMMON_OBJ" ADD_MODULE("vdecompress_gpujpeg_to_dxt", $GPUJPEG_TO_DXT_OBJ, "$GPUJPEG_TO_DXT_LIB") AC_DEFINE([HAVE_GPUJPEG_TO_DXT], [1], [Build with GPUJPEG DXT transcode support]) @@ -2444,35 +2444,21 @@ jack_trans=no jack=no AC_ARG_ENABLE(jack-transport, -[ --enable-jack-transport[=force] enable JACK transport (default is auto; disabled in modular build, can be forced)] +[ --enable-jack-transport[=force] enable JACK transport (default is auto)] [ Requires: jack], [jack_trans_req=$enableval], [jack_trans_req=$build_default]) -if test $jack_trans_req = yes -a $build_libraries = yes -then - AC_MSG_ERROR([JACK transport is currently incompatible with modular build]); -fi AC_CHECK_HEADER(jack/jack.h, FOUND_JACK_H=yes, FOUND_JACK_H=no) -AC_CHECK_LIB(jack, jack_client_new, [FOUND_JACK_L=yes] - [JACK_LIB=-ljack], FOUND_JACK_L=no) -if test $FOUND_JACK_L = no; then - # Windows - AC_CHECK_LIB(libjack64, jack_client_new, [FOUND_JACK_L=yes] - [JACK_LIB=-llibjack64], FOUND_JACK_L=no) -fi -if test $jack_trans_req != no -a $FOUND_JACK_H = yes -a $FOUND_JACK_L = yes -a \( $build_libraries = no -o $jack_trans_req = force \) +if test $jack_trans_req != no -a $FOUND_JACK_H = yes then JACK_TRANS_OBJ="src/audio/jack.o" - JACK_TRANS_LIB="$JACK_LIB" AC_DEFINE([HAVE_JACK_TRANS], [1], [Build with JACK transport support]) jack_trans=yes fi AC_SUBST(JACK_TRANS_OBJ) -AC_SUBST(JACK_TRANS_LIB) -AC_SUBST(JACK_TRANS_INC) # sound system AC_ARG_ENABLE(jack, @@ -2481,14 +2467,14 @@ AC_ARG_ENABLE(jack, [jack_req=$enableval], [jack_req=$build_default]) -if test $jack_req != no -a $FOUND_JACK_H = yes -a $FOUND_JACK_L = yes +if test $jack_req != no -a $FOUND_JACK_H = yes then JACK_CAP_OBJ="src/audio/capture/jack.o" JACK_PLAY_OBJ="src/audio/playback/jack.o" AC_DEFINE([HAVE_JACK], [1], [Build with JACK support]) jack=yes - ADD_MODULE("acap_jack", "$JACK_CAP_OBJ", "$JACK_LIB") - ADD_MODULE("aplay_jack", "$JACK_PLAY_OBJ", "$JACK_LIB") + ADD_MODULE("acap_jack", "$JACK_CAP_OBJ", "") + ADD_MODULE("aplay_jack", "$JACK_PLAY_OBJ", "") fi if test $jack = yes -a $speex = no diff --git a/data/scripts/Linux-AppImage/create-appimage.sh b/data/scripts/Linux-AppImage/create-appimage.sh index fd2c3c87a..4c4587fdd 100755 --- a/data/scripts/Linux-AppImage/create-appimage.sh +++ b/data/scripts/Linux-AppImage/create-appimage.sh @@ -44,9 +44,6 @@ while read -r x; do continue fi NAME=$(echo "$x" | awk '{ print $1 }') - if [ "$NAME" = libjack.so.0 ]; then # JACK is currently handled in AppRun - continue - fi EXCLUDE_LIST="$EXCLUDE_LIST $NAME" done < excludelist for n in $EXCLUDE_LIST; do diff --git a/data/scripts/Linux-AppImage/uv-wrapper.sh b/data/scripts/Linux-AppImage/uv-wrapper.sh index 33f0b4989..00b65e090 100755 --- a/data/scripts/Linux-AppImage/uv-wrapper.sh +++ b/data/scripts/Linux-AppImage/uv-wrapper.sh @@ -2,40 +2,10 @@ set -u -get_loader() { - LOADERS='/lib64/ld-linux-*so* /lib/ld-linux-*so* /lib*/ld-linux-*so*' - for n in $LOADERS; do - for m in `ls $n`; do - if [ -x $m ]; then - echo $m - return - fi - done - done -} - -set_ld_preload() { - if [ ! -f $DIR/lib/ultragrid/ultragrid_aplay_jack.so ]; then - return - fi - local LOADER=$(get_loader) - if [ ! -x "$LOADER" ]; then - return - fi - S_LD_LIBRARY_PATH=$LD_LIBRARY_PATH - LD_LIBRARY_PATH= - JACK_LIB=$(LD_TRACE_LOADED_OBJECTS=1 $LOADER $DIR/lib/ultragrid/ultragrid_aplay_jack.so | grep libjack | grep -v 'not found' | awk '{print $3}') - LD_LIBRARY_PATH=$S_LD_LIBRARY_PATH - if [ -n "$JACK_LIB" ]; then - export LD_PRELOAD=$JACK_LIB${LD_PRELOAD:+" $LD_PRELOAD"} - fi -} - DIR=`dirname $0` export LD_LIBRARY_PATH=$DIR/lib${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"} # there is an issue with running_from_path() which evaluates this executable # as being system-installed #export PATH=$DIR/bin:$PATH -set_ld_preload exec $DIR/bin/uv "$@" diff --git a/ADDING-MODULES.md b/doc/ADDING-MODULES.md similarity index 100% rename from ADDING-MODULES.md rename to doc/ADDING-MODULES.md diff --git a/doc/PERFORMANCE-TUNING.md b/doc/PERFORMANCE-TUNING.md new file mode 100644 index 000000000..0b8a035ac --- /dev/null +++ b/doc/PERFORMANCE-TUNING.md @@ -0,0 +1,111 @@ +Performance Tuning +================== + +Network +------- + + If transmitting *uncompressed video* stream to achieve optimal performance + with high definition video, it may be necessary to tune your system's + network parameters to more aggressive values than used by default. + + A key factor affecting performance is the path MTU. It is unlikely that + the system will sustain gigabit rates with the 1500 octet Ethernet MTU. + If using a gigabit Ethernet you may be able to improve performance by + setting an 8192 octet MTU on the interface, provided all intermediate + hops on the path from sender to receiver support the large MTU. + + UltraGrid attempts to increase the UDP receive socket buffer from the + default value (typically 64 kilobytes) to 4/6 megabytes. If successful, + this will make the system more robust to scheduling variations and + better able to accept bursty packet arrivals. UltraGrid will notify + you if it cannot increase buffers. You should follow those instructions + and set your system according to it. + + Interrupt processing load on the receiver host may be significant when + running at high rates. Depending on your network interface hardware it + may be possible to coalesce interrupts to reduce this load, although + the settings to do this are highly driver dependent. On FreeBSD, the + use of network device polling may also help performance: see the man + page for "polling" in section 4 of the manual. + + In many cases, the performance of your network interface card may be + limited by host bus performance (this is particularly an issue at high + rates, for example when using HD format video). + + +Display devices +--------------- + + If using a HW grabbing card (eg. DVS) as a display device, the + key factor limiting performance is PCI bus contention. Ensure that + the grabbing card is on a separate PCI bus to the network card -- + this typically requires a server class motherboard. On Linux, the + PCI bus topology can be displayed using "lspci -tv", for example: + + [root@ormal root]# lspci -tv + -+-[03]---06.0 Xilinx, Inc.: Unknown device d150 + +-[01]-+-02.0-[02]--+-04.0 Adaptec 7899P + | | \-04.1 Adaptec 7899P + | \-0e.0 3Com Corporation 3c985 1000BaseSX + \-[00]-+-00.0 ServerWorks CNB20HE + +-00.1 ServerWorks CNB20HE + +-00.2 ServerWorks: Unknown device 0006 + +-00.3 ServerWorks: Unknown device 0006 + +-04.0 Intel Corporation 82557 [Ethernet Pro 100] + +-0e.0 ATI Technologies Inc Rage XL + +-0f.0 ServerWorks OSB4 + \-0f.1 ServerWorks: Unknown device 0211 + [root@ormal root]# + + showing an DVS card on PCI bus [03] (the card shows as a Xilinx + device) and a gigabit Ethernet card on PCI bus [02] (the 3Com entry). + + For software display, you can use SDL or OpenGL display. Both are + accelerated (Mac and Linux) if you have properly configured video + drivers. On Linux, basic operability can be checked with following + commands. If configured properly, both should display driver + properties: + [root@ormal root]# glxinfo + <-- output omitted --> + and for SDL (accelerated through XVideo: + [root@ormal root]# xvinfo + <-- output omitted --> + + If you intend to use some of DXT compressions, recommended driver + is OpenGL, which can display it natively. When using other display + drivers, decompression is still done throught OpenGL and then displayed + with requested video driver. + + +Other Factors +------------- + + **Note:** This is left only as a legacy behavior - currently UltraGrid + is intended to run without _real-time_ priority (although still being + able to compiled with it). This may not be recommended in a general case, + however. + + The UltraGrid system will attempt to enable POSIX real-time scheduling + to improve performance. This behaviour is disabled by default now, because + it can occupy the whole system when enabled, but it can be stil enabled by + '--enable-rt' configure option. If you see the message: + + WARNING: Unable to set real-time scheduling + + when starting the application, this means that the operating system did + not permit it to enable real-time scheduling. The application will run, + but with reduced performance. The most likely reason for failure to set + realtime scheduling is that the application has insufficient privilege: + it should either be run by root, or be made setuid root. A similar + message: + + WARNING: System does not support real-time scheduling + + indicates that your operating system does not support POSIX real-time + scheduling. The application will still run, but performance may be less + than desired. + + + You can find more operating system tweaks at this page: + https://github.com/CESNET/UltraGrid/wiki/OS-Setup-UltraGrid + diff --git a/REPORTING-BUGS.md b/doc/REPORTING-BUGS.md similarity index 100% rename from REPORTING-BUGS.md rename to doc/REPORTING-BUGS.md diff --git a/src/audio/capture/jack.c b/src/audio/capture/jack.c index c2d2c5d7e..afa160aa7 100644 --- a/src/audio/capture/jack.c +++ b/src/audio/capture/jack.c @@ -1,35 +1,26 @@ +/** + * @file audio/capture/jack.c + * @author Martin Pulec + */ /* - * FILE: audio/capture/jack.c - * AUTHORS: Martin Benes - * Lukas Hejtmanek - * Petr Holub - * Milos Liska - * Jiri Matela - * Dalibor Matura <255899@mail.muni.cz> - * Ian Wesley-Smith - * - * Copyright (c) 2005-2010 CESNET z.s.p.o. + * Copyright (c) 2012-2020 CESNET z.s.p.o. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, is 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. - * - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * - * This product includes software developed by CESNET z.s.p.o. - * - * 4. Neither the name of CESNET nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY @@ -42,8 +33,6 @@ * 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. - * - * */ #ifdef HAVE_CONFIG_H @@ -51,6 +40,10 @@ #include "config_unix.h" #include "config_win32.h" #endif + +#define MAX_PORTS 64 +#define MOD_NAME "[JACK capture] " + #include "debug.h" #include "host.h" @@ -65,13 +58,12 @@ #include #include -#define MAX_PORTS 64 -#define MOD_NAME "[JACK capture] " - static int jack_samplerate_changed_callback(jack_nframes_t nframes, void *arg); static int jack_process_callback(jack_nframes_t nframes, void *arg); struct state_jack_capture { + struct libjack_connection *libjack; + struct audio_frame frame; jack_client_t *client; jack_port_t *input_ports[MAX_PORTS]; @@ -103,7 +95,7 @@ static int jack_process_callback(jack_nframes_t nframes, void *arg) } for (i = 0; i < s->frame.ch_count; ++i) { - jack_default_audio_sample_t *in = jack_port_get_buffer(s->input_ports[i], nframes); + jack_default_audio_sample_t *in = s->libjack->port_get_buffer(s->input_ports[i], nframes); mux_channel(s->tmp, (char *) in, sizeof(int32_t), channel_size, s->frame.ch_count, i, 1.0); } @@ -160,6 +152,11 @@ static void * audio_cap_jack_init(const char *cfg) log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to allocate memory.\n"); return NULL; } + s->libjack = open_libjack(); + if (s->libjack == NULL) { + free(s); + return NULL; + } char *dup = strdup(cfg); assert(dup != NULL); @@ -191,13 +188,13 @@ static void * audio_cap_jack_init(const char *cfg) free(dup); dup = NULL; - s->client = jack_client_open(client_name, JackNullOption, &status); + s->client = s->libjack->client_open(client_name, JackNullOption, &status); if(status & JackFailure) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Opening JACK client failed.\n"); goto error; } - ports = jack_get_ports(s->client, source_name, NULL, JackPortIsOutput); + ports = s->libjack->get_ports(s->client, source_name, NULL, JackPortIsOutput); if(ports == NULL) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to output ports matching \"%s\".\n", source_name); goto release_client; @@ -218,7 +215,7 @@ static void * audio_cap_jack_init(const char *cfg) if (audio_capture_sample_rate) { log_msg(LOG_LEVEL_WARNING, "[JACK capture] Ignoring user specified sample rate!\n"); } - s->frame.sample_rate = jack_get_sample_rate (s->client); + s->frame.sample_rate = s->libjack->get_sample_rate (s->client); s->frame.max_size = s->frame.ch_count * s->frame.bps * s->frame.sample_rate; s->frame.data = malloc(s->frame.max_size); @@ -226,17 +223,17 @@ static void * audio_cap_jack_init(const char *cfg) s->data = ring_buffer_init(s->frame.max_size); - if(jack_set_sample_rate_callback(s->client, jack_samplerate_changed_callback, (void *) s)) { + if (s->libjack->set_sample_rate_callback(s->client, jack_samplerate_changed_callback, (void *) s)) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Registring callback problem.\n"); goto release_client; } - if(jack_set_process_callback(s->client, jack_process_callback, (void *) s) != 0) { + if (s->libjack->set_process_callback(s->client, jack_process_callback, (void *) s) != 0) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Process callback registration problem.\n"); goto release_client; } - if(jack_activate(s->client)) { + if (s->libjack->activate(s->client)) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot activate client.\n"); goto release_client; } @@ -247,9 +244,9 @@ static void * audio_cap_jack_init(const char *cfg) for(port = 0; port < s->frame.ch_count; port++) { snprintf(name, 32, "capture_%02u", port); - s->input_ports[port] = jack_port_register(s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + s->input_ports[port] = s->libjack->port_register(s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); /* attach ports */ - if(jack_connect(s->client, ports[port], jack_port_name(s->input_ports[port]))) { + if (s->libjack->connect(s->client, ports[port], s->libjack->port_name(s->input_ports[port]))) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot connect input ports.\n"); } } @@ -262,8 +259,9 @@ static void * audio_cap_jack_init(const char *cfg) return s; release_client: - jack_client_close(s->client); + s->libjack->client_close(s->client); error: + close_libjack(s->libjack); free(dup); free(s); return NULL; @@ -286,10 +284,11 @@ static void audio_cap_jack_done(void *state) { struct state_jack_capture *s = (struct state_jack_capture *) state; - jack_client_close(s->client); + s->libjack->client_close(s->client); free(s->tmp); ring_buffer_destroy(s->data); free(s->frame.data); + close_libjack(s->libjack); free(s); } diff --git a/src/audio/jack.c b/src/audio/jack.c index c7765f09e..21d3cbeb4 100644 --- a/src/audio/jack.c +++ b/src/audio/jack.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2011-2019 CESNET, z. s. p. o. + * Copyright (c) 2011-2020 CESNET, z. s. p. o. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,6 +34,11 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/** + * @file + * @todo + * It looks like there is no jack_stop()? + */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -49,6 +54,7 @@ #include "audio/audio.h" #include "audio/jack.h" +#include "jack_common.h" #include "pthread.h" #include "rtp/rtp.h" #include "rtp/pbuf.h" @@ -57,8 +63,11 @@ #define BUFF_ELEM (1<<16) #define BUFF_SIZE (BUFF_ELEM * sizeof(float)) #define MAX_PORTS 8 +#define MOD_NAME "[JACK trans.] " struct state_jack { + struct libjack_connection *libjack; + unsigned int sender:1, receiver:1; @@ -102,7 +111,7 @@ int jack_process_callback(jack_nframes_t nframes, void *arg) { int to_end = BUFF_SIZE - s->play_buffer_start; jack_default_audio_sample_t *out = - jack_port_get_buffer (s->output_port[i], nframes); + s->libjack->port_get_buffer (s->output_port[i], nframes); if(to_end > send_b) { memcpy (out, s->play_buffer[i] + s->play_buffer_start, send_b); @@ -119,7 +128,7 @@ int jack_process_callback(jack_nframes_t nframes, void *arg) { for(i = 0; i < s->record.ch_count; ++i) { int j; jack_default_audio_sample_t *in = - jack_port_get_buffer (s->input_port[i], nframes); + s->libjack->port_get_buffer (s->input_port[i], nframes); for(j = 0; j < (int) nframes; ++j) { *(int *)(void *)(s->rec_buffer + ((s->rec_buffer_end + (j * s->record.ch_count + i) * sizeof(int32_t)) % BUFF_SIZE)) = in[j] * INT_MAX; @@ -146,13 +155,13 @@ void reconfigure_send_ch_count(struct state_jack *s, int ch_count) s->out_channel_count = s->out_channel_count_req = ch_count; - if ((ports = jack_get_ports (s->client, s->out_port_pattern, NULL, JackPortIsInput)) == NULL) { - fprintf(stderr, "Cannot find any ports matching pattern '%s'\n", s->out_port_pattern); + if ((ports = s->libjack->get_ports (s->client, s->out_port_pattern, NULL, JackPortIsInput)) == NULL) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot find any ports matching pattern '%s'\n", s->out_port_pattern); s->out_channel_count = 0; return; } for (i = 0; i < s->record.ch_count; ++i) { - jack_disconnect(s->client, jack_port_name (s->output_port[i]), ports[i]); + s->libjack->disconnect(s->client, s->libjack->port_name (s->output_port[i]), ports[i]); free(s->play_buffer[i]); } @@ -160,21 +169,20 @@ void reconfigure_send_ch_count(struct state_jack *s, int ch_count) while (ports[i]) ++i; if(i < s->out_channel_count) { - fprintf(stderr, "Not enought output ports found matching pattern '%s': " + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Not enought output ports found matching pattern '%s': " "%d requested, %d found\n", s->out_port_pattern, s->record.ch_count, i); - fprintf(stderr, "Reducing port count to %d\n", i); + log_msg(LOG_LEVEL_WARNING, MOD_NAME "Reducing port count to %d\n", i); s->out_channel_count = i; } for(i = 0; i < s->out_channel_count; ++i) { - fprintf(stderr, "%s\n\n\n", ports[i]); - if (jack_connect (s->client, jack_port_name (s->output_port[i]), ports[i])) { - fprintf (stderr, "cannot connect output ports\n"); + if (s->libjack->connect (s->client, s->libjack->port_name (s->output_port[i]), ports[i])) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot connect output ports: %s\n", ports[i]); } s->play_buffer[i] = malloc(BUFF_SIZE); } - fprintf(stderr, "[JACK] Sending %d output audio streams (ports).\n", s->out_channel_count); + log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Sending %d output audio streams (ports).\n", s->out_channel_count); free (ports); } @@ -234,21 +242,21 @@ static int attach_input_ports(struct state_jack *s) { int i = 0; const char **ports; - if ((ports = jack_get_ports (s->client, s->in_port_pattern, NULL, JackPortIsOutput)) == NULL) { - fprintf(stderr, "Cannot find any ports matching pattern '%s'\n", s->in_port_pattern); + if ((ports = s->libjack->get_ports (s->client, s->in_port_pattern, NULL, JackPortIsOutput)) == NULL) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot find any ports matching pattern '%s'\n", s->in_port_pattern); return FALSE; } while (ports[i]) ++i; if(i < s->record.ch_count) { - fprintf(stderr, "Not enought input ports found matching pattern '%s': " - "%d requested, %d found\n", s->in_port_pattern, s->record.ch_count, i); - fprintf(stderr, "Reducing port count to %d\n", i); - s->record.ch_count = i; + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Not enought input ports found matching pattern '%s': " + "%d requested, %d found\n", s->in_port_pattern, s->record.ch_count, i); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Reducing port count to %d\n", i); + s->record.ch_count = i; } for(i = 0; i < s->in_ch_count; ++i) { - if (jack_connect (s->client, ports[i], jack_port_name (s->input_port[i]))) { + if (s->libjack->connect (s->client, ports[i], s->libjack->port_name (s->input_port[i]))) { fprintf (stderr, "cannot connect input ports\n"); } } @@ -262,6 +270,12 @@ void * jack_start(const char *cfg) struct state_jack *s; s = (struct state_jack *) malloc(sizeof(struct state_jack)); + assert (s != NULL); + s->libjack = open_libjack(); + if (s->libjack == NULL) { + free(s); + return NULL; + } s->in_port_pattern = NULL; s->out_port_pattern = NULL; @@ -276,25 +290,23 @@ void * jack_start(const char *cfg) int ret = settings_init(s, cfg_copy); free(cfg_copy); if (ret != 0) { - fprintf(stderr, "Setting JACK failed. Check configuration ('-j' option).\n"); - free(s); - return NULL; - } - - if(!s->sender && !s->receiver) { - free(s); - return NULL; - } - - s->client = jack_client_open(CLIENT_NAME, JackNullOption, NULL); - if(jack_set_process_callback(s->client, jack_process_callback, (void *) s) != 0) { - fprintf(stderr, "[jack] Callback initialization problem.\n"); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Setting JACK failed. Check configuration ('-j' option).\n"); goto error; } - if(jack_set_sample_rate_callback(s->client, + if(!s->sender && !s->receiver) { + goto error; + } + + s->client = s->libjack->client_open(CLIENT_NAME, JackNullOption, NULL); + if(s->libjack->set_process_callback(s->client, jack_process_callback, (void *) s) != 0) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Callback initialization problem.\n"); + goto error; + } + + if(s->libjack->set_sample_rate_callback(s->client, jack_samplerate_changed_callback, (void *) s)) { - fprintf(stderr, "[jack] Callback initialization problem.\n"); + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Sample rate callback initialization problem.\n"); goto error; } @@ -306,7 +318,7 @@ void * jack_start(const char *cfg) for(i = 0; i < MAX_PORTS; ++i) { snprintf(name, 30, "out_%02u", i); - s->output_port[i] = jack_port_register (s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + s->output_port[i] = s->libjack->port_register (s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } s->out_channel_count = s->out_channel_count_req = 0; @@ -318,16 +330,16 @@ void * jack_start(const char *cfg) for(i = 0; i < s->in_ch_count; ++i) { snprintf(name, 30, "in_%02u", i); - s->input_port[i] = jack_port_register (s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + s->input_port[i] = s->libjack->port_register (s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); } - s->record.sample_rate = jack_get_sample_rate (s->client); + s->record.sample_rate = s->libjack->get_sample_rate (s->client); s->record.bps = sizeof(int32_t); s->record.ch_count = s->in_ch_count; s->rec_buffer = s->record.data = (void *) malloc(BUFF_SIZE); } - if (jack_activate (s->client)) { + if (s->libjack->activate (s->client)) { fprintf (stderr, "cannot activate client"); goto error; } @@ -338,7 +350,9 @@ void * jack_start(const char *cfg) } return s; -error: +error: + close_libjack(s->libjack); + free(s); return NULL; } diff --git a/src/audio/playback/jack.c b/src/audio/playback/jack.c index 5e2ba3791..5a5c8730e 100644 --- a/src/audio/playback/jack.c +++ b/src/audio/playback/jack.c @@ -3,7 +3,7 @@ * @author Martin Pulec */ /* - * Copyright (c) 2012-2019 CESNET z.s.p.o. + * Copyright (c) 2012-2020 CESNET z.s.p.o. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,6 +41,11 @@ #include "config_win32.h" #endif +#define DEFAULT_AUDIO_BUF_LEN_MS 50 +#define MAX_LEN_MS 1000 +#define MAX_PORTS 64 +#define MOD_NAME "[JACK playback] " + #include "audio/audio.h" #include "audio/audio_playback.h" #include "audio/utils.h" @@ -56,13 +61,9 @@ #include #include -#define DEFAULT_AUDIO_BUF_LEN_MS 50 -#define MAX_LEN_MS 1000 -#define MAX_PORTS 64 -#define MOD_NAME "[JACK playback] " - - struct state_jack_playback { + struct libjack_connection *libjack; + char *jack_ports_pattern; int jack_sample_rate; jack_client_t *client; @@ -116,7 +117,7 @@ static int jack_process_callback(jack_nframes_t nframes, void *arg) for (int i = 0; i < s->desc.ch_count; ++i) { jack_default_audio_sample_t *out = - jack_port_get_buffer (s->output_port[i], nframes_available); + s->libjack->port_get_buffer (s->output_port[i], nframes_available); assert(out != NULL); demux_channel((char *) out, s->tmp, sizeof(float), len, s->desc.ch_count, i); } @@ -172,6 +173,12 @@ static void * audio_play_jack_init(const char *cfg) return NULL; } + s->libjack = open_libjack(); + if (s->libjack == NULL) { + free(s); + return NULL; + } + char *dup = strdup(cfg); assert(dup != NULL); char *tmp = dup, *item, *save_ptr; @@ -203,28 +210,28 @@ static void * audio_play_jack_init(const char *cfg) s->jack_ports_pattern = strdup(source_name); - s->client = jack_client_open(client_name, JackNullOption, &status); + s->client = s->libjack->client_open(client_name, JackNullOption, &status); if(status & JackFailure) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Opening JACK client failed.\n"); goto error; } - if(jack_set_sample_rate_callback(s->client, jack_samplerate_changed_callback, (void *) s)) { + if (s->libjack->set_sample_rate_callback(s->client, jack_samplerate_changed_callback, (void *) s)) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Registering callback problem.\n"); goto release_client; } - if(jack_set_process_callback(s->client, jack_process_callback, (void *) s) != 0) { + if (s->libjack->set_process_callback(s->client, jack_process_callback, (void *) s) != 0) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Process callback registration problem.\n"); goto release_client; } - s->jack_sample_rate = jack_get_sample_rate (s->client); + s->jack_sample_rate = s->libjack->get_sample_rate (s->client); log_msg(LOG_LEVEL_INFO, "JACK sample rate: %d\n", s->jack_sample_rate); - ports = jack_get_ports(s->client, s->jack_ports_pattern, NULL, JackPortIsInput); + ports = s->libjack->get_ports(s->client, s->jack_ports_pattern, NULL, JackPortIsInput); if(ports == NULL) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to input ports matching %s.\n", s->jack_ports_pattern); goto release_client; @@ -240,7 +247,7 @@ static void * audio_play_jack_init(const char *cfg) for(i = 0; i < MAX_PORTS; ++i) { snprintf(name, 30, "playback_%02u", i); - s->output_port[i] = jack_port_register (s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + s->output_port[i] = s->libjack->port_register (s->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } } @@ -248,8 +255,9 @@ static void * audio_play_jack_init(const char *cfg) return s; release_client: - jack_client_close(s->client); + s->libjack->client_close(s->client); error: + close_libjack(s->libjack); free(dup); free(s); return NULL; @@ -296,9 +304,9 @@ static int audio_play_jack_reconfigure(void *state, struct audio_desc desc) assert(desc.bps == 4 && desc.sample_rate == s->jack_sample_rate && desc.codec == AC_PCM); - jack_deactivate(s->client); + s->libjack->deactivate(s->client); - ports = jack_get_ports(s->client, s->jack_ports_pattern, NULL, JackPortIsInput); + ports = s->libjack->get_ports(s->client, s->jack_ports_pattern, NULL, JackPortIsInput); if(ports == NULL) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to input ports matching %s.\n", s->jack_ports_pattern); return FALSE; @@ -335,7 +343,7 @@ static int audio_play_jack_reconfigure(void *state, struct audio_desc desc) /* for all channels previously connected */ for(i = 0; i < desc.ch_count; ++i) { - jack_disconnect(s->client, jack_port_name (s->output_port[i]), ports[i]); + s->libjack->disconnect(s->client, s->libjack->port_name (s->output_port[i]), ports[i]); log_msg(LOG_LEVEL_INFO, MOD_NAME "Port %d: %s\n", i, ports[i]); } free(s->tmp); @@ -348,13 +356,13 @@ static int audio_play_jack_reconfigure(void *state, struct audio_desc desc) s->tmp = malloc(s->max_channel_len); s->converted = malloc(desc.ch_count * s->max_channel_len); - if(jack_activate(s->client)) { + if (s->libjack->activate(s->client)) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot activate client.\n"); return FALSE; } for(i = 0; i < desc.ch_count; ++i) { - if (jack_connect (s->client, jack_port_name (s->output_port[i]), ports[i])) { + if (s->libjack->connect (s->client, s->libjack->port_name (s->output_port[i]), ports[i])) { log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot connect output port: %d.\n", i); return FALSE; } @@ -383,7 +391,7 @@ static void audio_play_jack_done(void *state) { struct state_jack_playback *s = (struct state_jack_playback *) state; - jack_client_close(s->client); + s->libjack->client_close(s->client); free(s->tmp); free(s->converted); free(s->jack_ports_pattern); @@ -391,6 +399,8 @@ static void audio_play_jack_done(void *state) s->buffer_fns->destroy(s->data); } + close_libjack(s->libjack); + free(s); } diff --git a/src/jack_common.h b/src/jack_common.h index 055584647..1f7c6ff1f 100644 --- a/src/jack_common.h +++ b/src/jack_common.h @@ -39,11 +39,117 @@ #ifndef JACK_COMMON_H #define JACK_COMMON_H -#include +#ifndef _WIN32 +#include +#endif + #include +#include +#include + #include "debug.h" +#include "lib_common.h" + #include "types.h" +typedef int (*jack_activate_t)(jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; +typedef int (*jack_client_close_t)(jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; +typedef jack_client_t *(*jack_client_open_t)(const char *client_name, + jack_options_t options, + jack_status_t *status, ...) JACK_OPTIONAL_WEAK_EXPORT; +typedef int (*jack_connect_t)(jack_client_t *client, + const char *source_port, + const char *destination_port) JACK_OPTIONAL_WEAK_EXPORT; +typedef int (*jack_deactivate_t)(jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; +typedef int (*jack_disconnect_t)(jack_client_t *client, + const char *source_port, + const char *destination_port) JACK_OPTIONAL_WEAK_EXPORT; +typedef void (*jack_free_t)(void* ptr) JACK_OPTIONAL_WEAK_EXPORT; +typedef const char **(*jack_get_ports_t)(jack_client_t *client, + const char *port_name_pattern, + const char *type_name_pattern, + unsigned long flags) JACK_OPTIONAL_WEAK_EXPORT; +typedef jack_nframes_t (*jack_get_sample_rate_t)(jack_client_t *) JACK_OPTIONAL_WEAK_EXPORT; +typedef void * (*jack_port_get_buffer_t)(jack_port_t *port, jack_nframes_t) JACK_OPTIONAL_WEAK_EXPORT; +typedef const char * (*jack_port_name_t)(const jack_port_t *port) JACK_OPTIONAL_WEAK_EXPORT; +typedef jack_port_t * (*jack_port_register_t)(jack_client_t *client, + const char *port_name, + const char *port_type, + unsigned long flags, + unsigned long buffer_size) JACK_OPTIONAL_WEAK_EXPORT; +typedef int (*jack_set_process_callback_t)(jack_client_t *client, + JackProcessCallback process_callback, + void *arg) JACK_OPTIONAL_WEAK_EXPORT; +typedef int (*jack_set_sample_rate_callback_t)(jack_client_t *client, + JackSampleRateCallback srate_callback, + void *arg) JACK_OPTIONAL_WEAK_EXPORT; + +struct libjack_connection { + LIB_HANDLE libjack; ///< lib connection + + jack_activate_t activate; + jack_client_close_t client_close; + jack_client_open_t client_open; + jack_connect_t connect; + jack_deactivate_t deactivate; + jack_disconnect_t disconnect; + jack_free_t free; + jack_get_ports_t get_ports; + jack_get_sample_rate_t get_sample_rate; + jack_port_get_buffer_t port_get_buffer; + jack_port_name_t port_name; + jack_port_register_t port_register; + jack_set_process_callback_t set_process_callback; + jack_set_sample_rate_callback_t set_sample_rate_callback; +}; + +static void close_libjack(struct libjack_connection *s) +{ + if (s == NULL) { + return; + } + dlclose(s->libjack); + free(s); +} + +#define JACK_DLSYM(sym) s->sym = (void *) dlsym(s->libjack, "jack_" #sym); if (s->sym == NULL) { log_msg(LOG_LEVEL_ERROR, "JACK symbol %s not found: %s\n", "jack_" #sym, dlerror()); close_libjack(s); return NULL; } + +static struct libjack_connection *open_libjack(void) +{ + struct libjack_connection *s = calloc(1, sizeof(struct libjack_connection)); + const char *shlib = +#ifdef _WIN32 + "C:/Windows/libjack64.dll"; +#elif defined (__APPLE__) + "libjack.dylib"; +#elif defined (__linux__) + "libjack.so"; +#else + ""; +#endif + s->libjack = dlopen(shlib, RTLD_NOW); + if (s->libjack == NULL) { + log_msg(LOG_LEVEL_ERROR, "JACK library \"%s\" opening failed: %s\n", shlib, dlerror()); + free(s); + return NULL; + } + JACK_DLSYM(activate); + JACK_DLSYM(client_close) + JACK_DLSYM(client_open) + JACK_DLSYM(connect); + JACK_DLSYM(deactivate); + JACK_DLSYM(disconnect); + JACK_DLSYM(free) + JACK_DLSYM(get_ports) + JACK_DLSYM(get_sample_rate); + JACK_DLSYM(set_sample_rate_callback); + JACK_DLSYM(port_get_buffer); + JACK_DLSYM(port_name); + JACK_DLSYM(port_register); + JACK_DLSYM(set_process_callback); + return s; +} + static inline struct device_info *audio_jack_probe(const char *client_name, unsigned long port_flags, int *count) @@ -52,29 +158,33 @@ static inline struct device_info *audio_jack_probe(const char *client_name, jack_status_t status; char *last_name = NULL; int i; - int channel_count; const char **ports; int port_count = 0; - struct device_info *available_devices = NULL; - - *count = 0; - client = jack_client_open(client_name, JackNullOption, &status); - if(status & JackFailure) { - fprintf(stderr, "[JACK playback] Opening JACK client failed.\n"); + struct libjack_connection *libjack = open_libjack(); + if (!libjack) { return NULL; } - ports = jack_get_ports(client, NULL, NULL, port_flags); + *count = 0; + client = libjack->client_open(client_name, JackNullOption, &status); + if(status & JackFailure) { + log_msg(LOG_LEVEL_ERROR, "Opening JACK client failed.\n"); + close_libjack(libjack); + return NULL; + } + + ports = libjack->get_ports(client, NULL, NULL, port_flags); if(ports == NULL) { - fprintf(stderr, "[JACK playback] Unable to enumerate ports.\n"); + log_msg(LOG_LEVEL_ERROR, "Unable to enumerate JACK ports.\n"); + close_libjack(libjack); return NULL; } for(port_count = 0; ports[port_count] != NULL; port_count++); - available_devices = calloc(port_count, sizeof(struct device_info)); + struct device_info *available_devices = port_count > 0 ? calloc(port_count, sizeof(struct device_info)) : NULL; - channel_count = 0; + int channel_count = 0; for(i = 0; ports[i] != NULL; i++) { char *item = strdup(ports[i]); assert(item != NULL); @@ -104,8 +214,9 @@ static inline struct device_info *audio_jack_probe(const char *client_name, (*count)++; } free(last_name); - jack_free(ports); - jack_client_close(client); + libjack->free(ports); + libjack->client_close(client); + close_libjack(libjack); return available_devices; } diff --git a/src/lib_common.h b/src/lib_common.h index 672078ac4..e8209be80 100644 --- a/src/lib_common.h +++ b/src/lib_common.h @@ -38,8 +38,36 @@ #ifndef LIB_COMMON_H #define LIB_COMMON_H +#include "config_unix.h" +#include "config_win32.h" + #include "host.h" // UNIQUE_NAME +#ifdef _WIN32 +#define LIB_HANDLE HMODULE +#define dlopen(name, flags) LoadLibraryA(name) +#define dlsym GetProcAddress +#define dlclose FreeLibrary +#if !defined __cplusplus && !defined thread_local +#define thread_local _Thread_local +#endif +static char *dlerror(void) ATTRIBUTE(unused); + +static char *dlerror(void) { + thread_local static char buf[1024] = "(unknown)"; + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + GetLastError(), // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + buf, // output buffer + sizeof buf, // size of msgbuf, bytes + NULL); // va_list of arguments + return buf; +} +#else // ! defined _WIN32 +#define LIB_HANDLE void * +#endif // defined _WIN32 + /** @brief This macro causes that this module will be statically linked with UltraGrid. */ #define MK_STATIC(A) A, NULL #define MK_STATIC_REF(A) &A, NULL diff --git a/src/libavcodec_common.c b/src/libavcodec_common.c index 1b43e71b9..a4977711e 100644 --- a/src/libavcodec_common.c +++ b/src/libavcodec_common.c @@ -1624,6 +1624,29 @@ static void yuv444p_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict i } } +static void yuv444p16le_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in_frame, + int width, int height, int pitch, int * __restrict rgb_shift) +{ + UNUSED(rgb_shift); + for(int y = 0; y < height; ++y) { + unsigned char *src_y = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y + 1; + unsigned char *src_cb = (unsigned char *) in_frame->data[1] + in_frame->linesize[1] * y + 1; + unsigned char *src_cr = (unsigned char *) in_frame->data[2] + in_frame->linesize[2] * y + 1; + unsigned char *dst = (unsigned char *) dst_buffer + pitch * y; + + OPTIMIZED_FOR (int x = 0; x < width / 2; ++x) { + *dst++ = (*src_cb + *(src_cb + 2)) / 2; + src_cb += 4; + *dst++ = *src_y; + src_y += 2; + *dst++ = (*src_cr + *(src_cr + 2)) / 2; + src_cr += 4; + *dst++ = *src_y; + src_y += 2; + } + } +} + static void yuv444p_to_v210(char * __restrict dst_buffer, AVFrame * __restrict in_frame, int width, int height, int pitch, int * __restrict rgb_shift) { @@ -1667,7 +1690,6 @@ static void yuv444p_to_v210(char * __restrict dst_buffer, AVFrame * __restrict i } } - /** * Changes pixel format from planar YUV 422 to packed RGB/A. * Color space is assumed ITU-T Rec. 609. YUV is expected to be full scale (aka in JPEG). @@ -2314,19 +2336,19 @@ static void p010le_to_uyvy(char * __restrict dst_buffer, AVFrame * __restrict in OPTIMIZED_FOR (int x = 0; x < width / 2; ++x) { uint8_t tmp; // U - tmp = *src_cbcr++ >> 2; + tmp = *src_cbcr++ >> 8; *dst1++ = tmp; *dst2++ = tmp; // Y - *dst1++ = *src_y1++ >> 2; - *dst2++ = *src_y2++ >> 2; + *dst1++ = *src_y1++ >> 8; + *dst2++ = *src_y2++ >> 8; // V - tmp = *src_cbcr++ >> 2; + tmp = *src_cbcr++ >> 8; *dst1++ = tmp; *dst2++ = tmp; // Y - *dst1++ = *src_y1++ >> 2; - *dst2++ = *src_y2++ >> 2; + *dst1++ = *src_y1++ >> 8; + *dst2++ = *src_y2++ >> 8; } } } @@ -2376,8 +2398,8 @@ const struct uv_to_av_conversion *get_uv_to_av_conversions() { { R10k, AV_PIX_FMT_YUV444P10LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r10k_to_yuv444p10le }, { R10k, AV_PIX_FMT_YUV444P12LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r10k_to_yuv444p12le }, { R10k, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r10k_to_yuv444p16le }, - { R12L, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p10le }, - { R12L, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p12le }, + { R12L, AV_PIX_FMT_YUV444P10LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p10le }, + { R12L, AV_PIX_FMT_YUV444P12LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p12le }, { R12L, AV_PIX_FMT_YUV444P16LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, r12l_to_yuv444p16le }, #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 15, 100) // FFMPEG commit c2869b4640f { v210, AV_PIX_FMT_P010LE, AVCOL_SPC_BT709, AVCOL_RANGE_MPEG, v210_to_p010le }, @@ -2443,12 +2465,11 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { {AV_PIX_FMT_YUV422P10LE, RGB, yuv422p10le_to_rgb24, false}, {AV_PIX_FMT_YUV422P10LE, RGBA, yuv422p10le_to_rgb32, false}, {AV_PIX_FMT_YUV444P10LE, v210, yuv444p10le_to_v210, true}, - {AV_PIX_FMT_YUV444P16LE, v210, yuv444p16le_to_v210, true}, {AV_PIX_FMT_YUV444P10LE, UYVY, yuv444p10le_to_uyvy, false}, {AV_PIX_FMT_YUV444P10LE, R10k, yuv444p10le_to_r10k, false}, {AV_PIX_FMT_YUV444P10LE, RGB, yuv444p10le_to_rgb24, false}, {AV_PIX_FMT_YUV444P10LE, RGBA, yuv444p10le_to_rgb32, false}, - {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p10le_to_r12l, false}, + {AV_PIX_FMT_YUV444P10LE, R12L, yuv444p10le_to_r12l, false}, #if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(55, 15, 100) // FFMPEG commit c2869b4640f {AV_PIX_FMT_P010LE, v210, p010le_to_v210, true}, {AV_PIX_FMT_P010LE, UYVY, p010le_to_uyvy, true}, @@ -2484,13 +2505,15 @@ const struct av_to_uv_conversion *get_av_to_uv_conversions() { // 8-bit YUV (NV12) {AV_PIX_FMT_NV12, UYVY, nv12_to_uyvy, true}, {AV_PIX_FMT_NV12, RGB, nv12_to_rgb24, false}, - {AV_PIX_FMT_NV12, RGB, nv12_to_rgb32, false}, + {AV_PIX_FMT_NV12, RGBA, nv12_to_rgb32, false}, // 12-bit YUV {AV_PIX_FMT_YUV444P12LE, R10k, yuv444p12le_to_r10k, false}, - {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p12le_to_r12l, false}, + {AV_PIX_FMT_YUV444P12LE, R12L, yuv444p12le_to_r12l, false}, // 16-bit YUV {AV_PIX_FMT_YUV444P16LE, R10k, yuv444p16le_to_r10k, false}, {AV_PIX_FMT_YUV444P16LE, R12L, yuv444p16le_to_r12l, false}, + {AV_PIX_FMT_YUV444P16LE, UYVY, yuv444p16le_to_uyvy, false}, + {AV_PIX_FMT_YUV444P16LE, v210, yuv444p16le_to_v210, false}, // RGB {AV_PIX_FMT_GBRP, RGB, gbrp_to_rgb, true}, {AV_PIX_FMT_GBRP, RGBA, gbrp_to_rgba, true}, @@ -2539,14 +2562,17 @@ av_to_uv_convert_p get_av_to_uv_conversion(int av_codec, codec_t uv_codec) { */ struct SwsContext *getSwsContext(unsigned int SrcW, unsigned int SrcH, enum AVPixelFormat SrcFormat, unsigned int DstW, unsigned int DstH, enum AVPixelFormat DstFormat, int64_t Flags) { struct SwsContext *Context = sws_alloc_context(); - // 0 = limited range, 1 = full range - int SrcRange = 1; - int DstRange = 1; - if (!Context) { return 0; } + const struct AVPixFmtDescriptor *SrcFormatDesc = av_pix_fmt_desc_get(SrcFormat); + const struct AVPixFmtDescriptor *DstFormatDesc = av_pix_fmt_desc_get(DstFormat); + + // 0 = limited range, 1 = full range + int SrcRange = SrcFormatDesc != NULL && (SrcFormatDesc->flags & AV_PIX_FMT_FLAG_RGB) != 0 ? 1 : 0; + int DstRange = DstFormatDesc != NULL && (DstFormatDesc->flags & AV_PIX_FMT_FLAG_RGB) != 0 ? 1 : 0; + av_opt_set_int(Context, "sws_flags", Flags, 0); av_opt_set_int(Context, "srcw", SrcW, 0); av_opt_set_int(Context, "srch", SrcH, 0); diff --git a/src/main.cpp b/src/main.cpp index 41d617029..2f20829ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -292,7 +292,7 @@ static void crash_signal_handler(int sig) *ptr++ = PACKAGE_BUGREPORT[i]; } *ptr++ = '.'; *ptr++ = '\n'; - const char message3[] = "You may find some tips how to report bugs in file REPORTING-BUGS distributed with "; + const char message3[] = "You may find some tips how to report bugs in file doc/reporting_bugs.md distributed with "; for (size_t i = 0; i < sizeof message3 - 1; ++i) { *ptr++ = message3[i]; } diff --git a/src/rtp/pbuf.cpp b/src/rtp/pbuf.cpp index cd483cb18..f7725001d 100644 --- a/src/rtp/pbuf.cpp +++ b/src/rtp/pbuf.cpp @@ -526,6 +526,7 @@ int pbuf_decode(struct pbuf *playout_buf, std::chrono::high_resolution_clock::time_point const & curr_time, decode_frame_t decode_func, void *data) { + using namespace std::chrono_literals; /* Find the first complete frame that has reached it's playout */ /* time, and decode it into the framebuffer. Mark the frame as */ /* decoded, but otherwise leave it in the playout buffer. */ @@ -545,6 +546,9 @@ pbuf_decode(struct pbuf *playout_buf, std::chrono::high_resolution_clock::time_p curr->decoded = 1; return ret; } else { + if (curr_time > curr->playout_time + 1s) { + curr->completed = true; + } debug_msg ("Unable to decode frame due to missing data (RTP TS=%u)\n", curr->rtp_timestamp); diff --git a/src/video_capture/ximea.c b/src/video_capture/ximea.c index a5a84c48a..061e9ee25 100644 --- a/src/video_capture/ximea.c +++ b/src/video_capture/ximea.c @@ -64,24 +64,15 @@ #define MOD_NAME "[XIMEA] " #define MICROSEC_IN_SEC 1000000.0 -#ifdef WIN32 -#define LIB_HANDLE HINSTANCE -#define dlopen(name, flags) LoadLibraryA(name) -#define dlsym GetProcAddress -#define dlclose FreeLibrary -#else -#define LIB_HANDLE void * -#endif - struct ximea_functions { - XIAPI XI_RETURN __cdecl (*xiGetNumberDevices)(OUT PDWORD pNumberDevices); - XIAPI XI_RETURN __cdecl (*xiGetDeviceInfoString)(IN DWORD DevId, const char* prm, char* value, DWORD value_size); - XIAPI XI_RETURN __cdecl (*xiOpenDevice)(IN DWORD DevId, OUT PHANDLE hDevice); - XIAPI XI_RETURN __cdecl (*xiSetParamInt)(IN HANDLE hDevice, const char* prm, const int val); - XIAPI XI_RETURN __cdecl (*xiStartAcquisition)(IN HANDLE hDevice); - XIAPI XI_RETURN __cdecl (*xiGetImage)(IN HANDLE hDevice, IN DWORD timeout, OUT LPXI_IMG img); - XIAPI XI_RETURN __cdecl (*xiStopAcquisition)(IN HANDLE hDevice); - XIAPI XI_RETURN __cdecl (*xiCloseDevice)(IN HANDLE hDevice); + XI_RETURN __cdecl (*xiGetNumberDevices)(OUT PDWORD pNumberDevices); + XI_RETURN __cdecl (*xiGetDeviceInfoString)(IN DWORD DevId, const char* prm, char* value, DWORD value_size); + XI_RETURN __cdecl (*xiOpenDevice)(IN DWORD DevId, OUT PHANDLE hDevice); + XI_RETURN __cdecl (*xiSetParamInt)(IN HANDLE hDevice, const char* prm, const int val); + XI_RETURN __cdecl (*xiStartAcquisition)(IN HANDLE hDevice); + XI_RETURN __cdecl (*xiGetImage)(IN HANDLE hDevice, IN DWORD timeout, OUT LPXI_IMG img); + XI_RETURN __cdecl (*xiStopAcquisition)(IN HANDLE hDevice); + XI_RETURN __cdecl (*xiCloseDevice)(IN HANDLE hDevice); LIB_HANDLE handle; }; @@ -95,9 +86,9 @@ struct state_vidcap_ximea { }; #define GET_SYMBOL(sym) do {\ - f->sym = dlsym(f->handle, #sym);\ + f->sym = (void *) dlsym(f->handle, #sym);\ if (f->sym == NULL) {\ - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to find symbol %s\n", #sym);\ + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to find symbol %s: %s\n", #sym, dlerror());\ return false;\ }\ } while(0) diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index 47985b804..3d210b305 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -143,6 +143,9 @@ static void deconfigure(struct state_libavcodec_decompress *s) #ifdef HAVE_SWSCALE s->sws.ctx = NULL; sws_freeContext(s->sws.ctx); + if (s->sws.frame) { + av_freep(s->sws.frame->data); + } av_frame_free(&s->sws.frame); #endif // defined HAVE_SWSCALE } @@ -423,6 +426,13 @@ static int libavcodec_decompress_reconfigure(void *state, struct video_desc desc } static bool has_conversion(enum AVPixelFormat pix_fmt, codec_t *ug_pix_fmt) { + { + codec_t mapped_pix_fmt = get_av_to_ug_pixfmt(pix_fmt); + if (mapped_pix_fmt != VIDEO_CODEC_NONE) { + *ug_pix_fmt = mapped_pix_fmt; + return true; + } + } for (const struct av_to_uv_conversion *c = get_av_to_uv_conversions(); c->uv_codec != VIDEO_CODEC_NONE; c++) { if (c->av_codec != pix_fmt) { // this conversion is not valid @@ -497,8 +507,21 @@ static enum AVPixelFormat get_format_callback(struct AVCodecContext *s __attribu } #endif + // directly mapped UG codecs + for (const enum AVPixelFormat *fmt_it = fmt; *fmt_it != AV_PIX_FMT_NONE; fmt_it++) { + codec_t mapped_pix_fmt = get_av_to_ug_pixfmt(*fmt_it); + if (mapped_pix_fmt != VIDEO_CODEC_NONE) { + if (state->out_codec == VIDEO_CODEC_NONE) { // just probing internal format + state->internal_codec = mapped_pix_fmt; + return AV_PIX_FMT_NONE; + } + if (state->out_codec == mapped_pix_fmt) { + state->internal_codec = mapped_pix_fmt; + return *fmt_it; + } + } + } bool use_native[] = { true, false }; // try native first - for (const bool *use_native_it = use_native; use_native_it != use_native + sizeof use_native / sizeof use_native[0]; ++use_native_it) { for (const enum AVPixelFormat *fmt_it = fmt; *fmt_it != AV_PIX_FMT_NONE; fmt_it++) { @@ -542,40 +565,56 @@ static enum AVPixelFormat get_format_callback(struct AVCodecContext *s __attribu } #ifdef HAVE_SWSCALE +static bool lavd_sws_convert_reconfigure(struct state_libavcodec_decompress_sws *sws, enum AVPixelFormat sws_in_codec, + enum AVPixelFormat sws_out_codec, int width, int height) +{ + if (sws->width == width && sws->height == height && sws->in_codec == sws_in_codec && sws->ctx != NULL) { + return true; + } + log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Using swscale to convert from %s to %s.\n", + av_get_pix_fmt_name(sws_in_codec), av_get_pix_fmt_name(sws_out_codec)); + sws_freeContext(sws->ctx); + if (sws->frame) { + av_freep(sws->frame->data); + } + av_frame_free(&sws->frame); + sws->ctx = getSwsContext(width, height, sws_in_codec, + width, height, sws_out_codec, + SWS_POINT); + if(!sws->ctx){ + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to init sws context.\n"); + return false; + } + sws->frame = av_frame_alloc(); + if (!sws->frame) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not allocate sws frame\n"); + return false; + } + sws->frame->width = width; + sws->frame->height = height; + sws->frame->format = sws_out_codec; + int ret = av_image_alloc(sws->frame->data, sws->frame->linesize, + sws->frame->width, sws->frame->height, + sws_out_codec, 32); + if (ret < 0) { + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not allocate raw picture buffer for sws\n"); + return false; + } + sws->width = width; + sws->height = height; + sws->in_codec = sws_in_codec; + sws->out_codec = sws_out_codec; + + return true; +} + static bool lavd_sws_convert(struct state_libavcodec_decompress_sws *sws, enum AVPixelFormat sws_in_codec, enum AVPixelFormat sws_out_codec, int width, int height, AVFrame *in_frame) { - if (sws->width != width || sws->height != height|| sws->in_codec != sws_in_codec || sws->ctx == NULL) { - log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Attempting to use swscale to convert.\n"); - sws_freeContext(sws->ctx); - av_frame_free(&sws->frame); - sws->ctx = getSwsContext(width, height, sws_in_codec, - width, height, sws_out_codec, - SWS_POINT); - if(!sws->ctx){ - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unable to init sws context.\n"); - return false; - } - sws->frame = av_frame_alloc(); - if (!sws->frame) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not allocate sws frame\n"); - return false; - } - sws->frame->width = width; - sws->frame->height = height; - sws->frame->format = sws_out_codec; - int ret = av_image_alloc(sws->frame->data, sws->frame->linesize, - sws->frame->width, sws->frame->height, - sws_out_codec, 32); - if (ret < 0) { - log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not allocate raw picture buffer for sws\n"); - return false; - } - sws->width = width; - sws->height = height; - sws->in_codec = sws_in_codec; - sws->out_codec = sws_out_codec; + if (!lavd_sws_convert_reconfigure(sws, sws_in_codec, sws_out_codec, width, height)) { + return false; } + sws_scale(sws->ctx, (const uint8_t * const *) in_frame->data, in_frame->linesize, @@ -584,6 +623,36 @@ static bool lavd_sws_convert(struct state_libavcodec_decompress_sws *sws, enum A sws->frame->data, sws->frame->linesize); return true; +} + +/// @brief Converts directly to out_buffer (instead to sws->frame). This is used for directly mapped +/// UltraGrid pixel formats that can be decoded directly to framebuffer. +static bool lavd_sws_convert_to_buffer(struct state_libavcodec_decompress_sws *sws, enum AVPixelFormat sws_in_codec, + enum AVPixelFormat sws_out_codec, int width, int height, AVFrame *in_frame, char *out_buffer) +{ + if (!lavd_sws_convert_reconfigure(sws, sws_in_codec, sws_out_codec, width, height)) { + return false; + } + + struct AVFrame *out = av_frame_alloc(); + codec_t ug_out_pixfmt = get_av_to_ug_pixfmt(sws_out_codec); + if (codec_is_planar(ug_out_pixfmt)) { + buf_get_planes(width, height, ug_out_pixfmt, out_buffer, (char **) out->data); + buf_get_linesizes(width, ug_out_pixfmt, out->linesize); + } else { + out->data[0] = (unsigned char *) out_buffer; + out->linesize[0] = vc_get_linesize(width, ug_out_pixfmt); + } + + sws_scale(sws->ctx, + (const uint8_t * const *) in_frame->data, + in_frame->linesize, + 0, + in_frame->height, + out->data, + out->linesize); + av_frame_free(&out); + return true; } #endif @@ -608,6 +677,15 @@ static int change_pixfmt(AVFrame *frame, unsigned char *dst, int av_codec, codec int pitch, int rgb_shift[static restrict 3], struct state_libavcodec_decompress_sws *sws) { av_to_uv_convert_p convert = NULL; + if (get_av_to_ug_pixfmt(av_codec) == out_codec) { + if (!codec_is_planar(out_codec)) { + memcpy(dst, frame->data[0], vc_get_datalen(width, height, out_codec)); + return TRUE; + } + log_msg(LOG_LEVEL_ERROR, MOD_NAME "Planar pixfmts not support here, please report a bug!\n"); + return FALSE; + } + for (const struct av_to_uv_conversion *c = get_av_to_uv_conversions(); c->uv_codec != VIDEO_CODEC_NONE; c++) { if (c->av_codec == av_codec && c->uv_codec == out_codec) { convert = c->convert; @@ -620,8 +698,14 @@ static int change_pixfmt(AVFrame *frame, unsigned char *dst, int av_codec, codec } #ifdef HAVE_SWSCALE + if (get_ug_to_av_pixfmt(out_codec) != AV_PIX_FMT_NONE) { + lavd_sws_convert_to_buffer(sws, av_codec, get_ug_to_av_pixfmt(out_codec), width, height, frame, (char *) dst); + return TRUE; + } + // else try to find swscale enum AVPixelFormat sws_out_codec = 0; + bool native[2] = { true, false }; for (int n = 0; n < 2; n++) { for (const struct av_to_uv_conversion *c = get_av_to_uv_conversions(); c->uv_codec != VIDEO_CODEC_NONE; c++) { diff --git a/src/video_display/ndi.c b/src/video_display/ndi.c index 7a5fddecc..2485589a4 100644 --- a/src/video_display/ndi.c +++ b/src/video_display/ndi.c @@ -207,7 +207,7 @@ static int display_ndi_putf(void *state, struct video_frame *frame, int flag) NDI_video_frame.p_data = (uint8_t *) frame->tiles[0].data; NDI_video_frame.frame_rate_N = get_framerate_n(frame->fps); NDI_video_frame.frame_rate_D = get_framerate_d(frame->fps); - NDI_video_frame.frame_format_type = frame->interlacing = PROGRESSIVE ? NDIlib_frame_format_type_progressive : NDIlib_frame_format_type_interleaved; + NDI_video_frame.frame_format_type = frame->interlacing == PROGRESSIVE ? NDIlib_frame_format_type_progressive : NDIlib_frame_format_type_interleaved; NDI_video_frame.timecode = NDIlib_send_timecode_synthesize; NDIlib_send_send_video_v2(s->pNDI_send, &NDI_video_frame); diff --git a/src/video_display/sdl2.cpp b/src/video_display/sdl2.cpp index 8fe7443d5..53afd522d 100644 --- a/src/video_display/sdl2.cpp +++ b/src/video_display/sdl2.cpp @@ -68,8 +68,8 @@ #include -#include #include +#include #include #include #include @@ -377,7 +377,8 @@ static int display_sdl2_reconfigure(void *state, struct video_desc desc) return 1; } -static const unordered_map> pf_mapping = { +struct ug_to_sdl_pf { codec_t first; uint32_t second; }; +static const ug_to_sdl_pf pf_mapping[] = { { I420, SDL_PIXELFORMAT_IYUV }, { UYVY, SDL_PIXELFORMAT_UYVY }, { YUYV, SDL_PIXELFORMAT_YUY2 }, @@ -390,19 +391,42 @@ static const unordered_map> pf_mapping = { #endif }; -static bool create_texture(struct state_sdl2 *s, struct video_desc desc) { - uint32_t format; - auto it = pf_mapping.find(desc.color_spec); - if (it == pf_mapping.end()) { - abort(); +static uint32_t get_ug_to_sdl_format(codec_t ug_codec) { + if (ug_codec == R10k) { + return SDL_PIXELFORMAT_ARGB2101010; } - format = it->second; + const auto *it = find_if(begin(pf_mapping), end(pf_mapping), [ug_codec](const ug_to_sdl_pf &u) { return u.first == ug_codec; }); + if (it == end(pf_mapping)) { + LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Wrong codec: " << get_codec_name(ug_codec) << "\n"; + return SDL_PIXELFORMAT_UNKNOWN; + } + return it->second; +} + +ADD_TO_PARAM("sdl2-r10k", + "* sdl2-r10k\n" + " Enable 10-bit RGB support for SDL2 (EXPERIMENTAL)\n"); + +static auto get_supported_pfs() { + vector codecs; + codecs.reserve(sizeof pf_mapping / sizeof pf_mapping[0]); + + for (auto item : pf_mapping) { + codecs.push_back(item.first); + } + if (get_commandline_param("sdl2-r10k") != nullptr) { + codecs.push_back(R10k); + } + return codecs; +} + +static bool create_texture(struct state_sdl2 *s, struct video_desc desc) { if (s->texture) { SDL_DestroyTexture(s->texture); } - s->texture = SDL_CreateTexture(s->renderer, format, SDL_TEXTUREACCESS_STREAMING, desc.width, desc.height); + s->texture = SDL_CreateTexture(s->renderer, get_ug_to_sdl_format(desc.color_spec), SDL_TEXTUREACCESS_STREAMING, desc.width, desc.height); if (!s->texture) { log_msg(LOG_LEVEL_ERROR, "[SDL] Unable to create texture: %s\n", SDL_GetError()); return false; @@ -693,18 +717,14 @@ static int display_sdl2_putf(void *state, struct video_frame *frame, int nonbloc static int display_sdl2_get_property(void *state, int property, void *val, size_t *len) { UNUSED(state); - codec_t codecs[pf_mapping.size()]; - - int i = 0; - for (auto item : pf_mapping) { - codecs[i++] = item.first; - } + auto codecs = get_supported_pfs(); + size_t codecs_len = codecs.size() * sizeof(codec_t); switch (property) { case DISPLAY_PROPERTY_CODECS: - if (sizeof(codecs) <= *len) { - memcpy(val, codecs, sizeof(codecs)); - *len = sizeof(codecs); + if (codecs_len <= *len) { + memcpy(val, codecs.data(), codecs_len); + *len = codecs_len; } else { return FALSE; } diff --git a/src/video_frame.c b/src/video_frame.c index 106011900..b601ab26c 100644 --- a/src/video_frame.c +++ b/src/video_frame.c @@ -456,3 +456,15 @@ void buf_get_planes(int width, int height, codec_t color_spec, char *data, char } } +void buf_get_linesizes(int width, codec_t color_spec, int *linesize) +{ + int sub[8]; + codec_get_planes_subsampling(color_spec, sub); + for (int i = 0; i < 4; ++i) { + if (sub[2 * i] == 0) { + break; + } + linesize[i] = (width + sub[2 * i] - 1) / sub[2 * i]; + } +} + diff --git a/src/video_frame.h b/src/video_frame.h index 2762b0ce9..80ceb2f1f 100644 --- a/src/video_frame.h +++ b/src/video_frame.h @@ -206,6 +206,7 @@ unsigned int vf_get_data_len(struct video_frame *f); * Works with planar pixel formats only. */ void buf_get_planes(int width, int height, codec_t color_spec, char *data, char **planes); +void buf_get_linesizes(int width, codec_t color_spec, int *linesize); /** @name Video Flags * @deprecated use rather video_frame or video_desc members diff --git a/test/ff_codec_conversions_test.cpp b/test/ff_codec_conversions_test.cpp index 289bc4e11..6ea04275f 100644 --- a/test/ff_codec_conversions_test.cpp +++ b/test/ff_codec_conversions_test.cpp @@ -51,22 +51,22 @@ ff_codec_conversions_test::tearDown() #define TIMER(t) struct timeval t{}; gettimeofday(&(t), nullptr) void -ff_codec_conversions_test::test_yuv444p16le_from_to_r10k() +ff_codec_conversions_test::test_yuv444pXXle_from_to_r10k() { using namespace std::string_literals; - constexpr int width = 1920; - constexpr int height = 1080; + constexpr int width = 320; + constexpr int height = 240; vector rgba_buf(width * height * 4); /// @todo Use 10-bit natively - auto test_pattern = [&] { + auto test_pattern = [&](AVPixelFormat avfmt) { vector r10k_buf(width * height * 4); copy(rgba_buf.begin(), rgba_buf.end(), r10k_buf.begin()); toR10k(r10k_buf.data(), width, height); AVFrame frame; - frame.format = AV_PIX_FMT_YUV444P16LE; + frame.format = avfmt; frame.width = width; frame.height = height; @@ -112,36 +112,38 @@ ff_codec_conversions_test::test_yuv444p16le_from_to_r10k() CPPUNIT_ASSERT_MESSAGE("Maximal allowed difference 1, found "s + to_string(max_diff), max_diff <= 1); }; - int i = 0; - for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = (i++ / 4) % 0x100; }); - test_pattern(); + for (auto f : { AV_PIX_FMT_YUV444P10LE, AV_PIX_FMT_YUV444P12LE, AV_PIX_FMT_YUV444P16LE }) { + int i = 0; + for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = (i++ / 4) % 0x100; }); + test_pattern(f); - array pattern{ 0xFFU, 0, 0, 0xFFU }; - for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = pattern[i++ % 4]; }); - test_pattern(); + array pattern{ 0xFFU, 0, 0, 0xFFU }; + for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = pattern[i++ % 4]; }); + test_pattern(f); - default_random_engine rand_gen; - for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = rand_gen() % 0x100; }); - test_pattern(); + default_random_engine rand_gen; + for_each(rgba_buf.begin(), rgba_buf.end(), [&](unsigned char & c) { c = rand_gen() % 0x100; }); + test_pattern(f); + } } void -ff_codec_conversions_test::test_yuv444p16le_from_to_r12l() +ff_codec_conversions_test::test_yuv444pXXle_from_to_r12l() { using namespace std::string_literals; - constexpr int width = 1920; - constexpr int height = 1080; + constexpr int width = 320; + constexpr int height = 240; vector rgb_buf(width * height * 3); /// @todo Use 12-bit natively - auto test_pattern = [&] { + auto test_pattern = [&](AVPixelFormat avfmt) { vector r12l_buf(vc_get_datalen(width, height, R12L)); decoder_t rgb_to_r12l = get_decoder_from_to(RGB, R12L, true); rgb_to_r12l(r12l_buf.data(), rgb_buf.data(), vc_get_datalen(width, height, R12L), 0, 8, 16); AVFrame frame; - frame.format = AV_PIX_FMT_YUV444P16LE; + frame.format = avfmt; frame.width = width; frame.height = height; @@ -186,17 +188,19 @@ ff_codec_conversions_test::test_yuv444p16le_from_to_r12l() CPPUNIT_ASSERT_MESSAGE("Maximal allowed difference 1, found "s + to_string(max_diff), max_diff <= 1); }; - int i = 0; - array pattern{ 0xFFU, 0, 0 }; - for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = pattern[i++ % 3]; }); - test_pattern(); + for (auto f : { AV_PIX_FMT_YUV444P10LE, AV_PIX_FMT_YUV444P12LE, AV_PIX_FMT_YUV444P16LE }) { + int i = 0; + array pattern{ 0xFFU, 0, 0 }; + for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = pattern[i++ % 3]; }); + test_pattern(f); - for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = (i++ / 3) % 0x100; }); - test_pattern(); + for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = (i++ / 3) % 0x100; }); + test_pattern(f); - default_random_engine rand_gen; - for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = rand_gen() % 0x100; }); - test_pattern(); + default_random_engine rand_gen; + for_each(rgb_buf.begin(), rgb_buf.end(), [&](unsigned char & c) { c = rand_gen() % 0x100; }); + test_pattern(f); + } } #endif // defined HAVE_CPPUNIT && HAVE_LAVC diff --git a/test/ff_codec_conversions_test.h b/test/ff_codec_conversions_test.h index 9af83b004..76ec94a59 100644 --- a/test/ff_codec_conversions_test.h +++ b/test/ff_codec_conversions_test.h @@ -10,8 +10,8 @@ class ff_codec_conversions_test : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE( ff_codec_conversions_test ); - CPPUNIT_TEST( test_yuv444p16le_from_to_r10k ); - CPPUNIT_TEST( test_yuv444p16le_from_to_r12l ); + CPPUNIT_TEST( test_yuv444pXXle_from_to_r10k ); + CPPUNIT_TEST( test_yuv444pXXle_from_to_r12l ); CPPUNIT_TEST_SUITE_END(); public: @@ -20,8 +20,8 @@ public: void setUp(); void tearDown(); - void test_yuv444p16le_from_to_r10k(); - void test_yuv444p16le_from_to_r12l(); + void test_yuv444pXXle_from_to_r10k(); + void test_yuv444pXXle_from_to_r12l(); }; #endif // defined HAVE_LAVC