├── .circleci └── config.yml ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── astyle └── astylerc └── toxcam ├── avatar.png ├── loop_services.sh ├── loop_tor_services.sh ├── scripts ├── get_cpu_temp.sh ├── get_gpu_temp.sh ├── linux │ ├── get_cpu_temp.sh │ └── get_gpu_temp.sh ├── raspi │ ├── get_cpu_temp.sh │ └── get_gpu_temp.sh ├── set_os_dir.sh └── single_shot.sh ├── toxcam.c └── update_from_ci.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: /home/ubuntu 5 | docker: 6 | - image: ubuntu:16.04 7 | environment: 8 | - MAKEFLAGS: "" 9 | - CIRCLE_ARTIFACTS: "/tmp/artifacts" 10 | - WRKDIR: "/home/ubuntu" 11 | ## ----------- RASPI cross compile ---------------- 12 | - RASPI_PATH: "/home/ubuntu/cc/tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin:$PATH" 13 | - RASPI_SYSROOT_: "/home/ubuntu/cc/tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot" 14 | - RASPI_TOOL_PREFIX: arm-linux-gnueabihf 15 | - RASPI_INSTALL_DEST: "/home/ubuntu/installdest/" 16 | - RASPI_TARGET_: arm-linux-gnueabi 17 | - RASPI_HOST_: arm-linux-gnueabi 18 | - RASPI_CXX: $RASPI_TOOL_PREFIX-g++ 19 | - RASPI_AR: $RASPI_TOOL_PREFIX-ar 20 | - RASPI_RANLIB: $RASPI_TOOL_PREFIX-ranlib 21 | - RASPI_CC: $RASPI_TOOL_PREFIX-gcc 22 | - RASPI_LD: $RASPI_TOOL_PREFIX-ld 23 | - RASPI_PKG_CONFIG_PATH: "/home/ubuntu/cc/tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/usr/lib/pkgconfig" 24 | - RASPI_s_: "/home/ubuntu/src/" 25 | - RASPI_PKGSDIR: "/home/ubuntu/pkgs/" 26 | - CF2: " -O3 -g -fPIC -marm -march=armv8-a+crc -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard -ftree-vectorize " 27 | - CF3: " -funsafe-math-optimizations " 28 | # c-toxcore version used 29 | - CTOXCORE_VERSION_HASH: "zoff99/toxcore_v1.0.10__toxav_h264_001" 30 | # c-toxcore repo used 31 | # CTOXCORE_URL: "https://github.com/TokTok/c-toxcore" 32 | - CTOXCORE_URL: "https://github.com/zoff99/c-toxcore_team" 33 | - LIBSODIUM_VERSION: "tags/1.0.16" 34 | - LIBSODIUM_BRANCH: "1.0.16" 35 | - RASPBERRRY_TOOLS_HASH: d820ab9c21969013b4e56c7e9cba25518afcdd44 36 | 37 | steps: 38 | # ubuntu removed "sudo" from minimal docker image starting with 16.0.4 :-( 39 | - run: apt-get update && apt-get install -y sudo lsb-release && rm -rf /var/lib/apt/lists/* 40 | - run: sudo apt update 41 | # to make circleCI tools work properly --- 42 | - run: sudo apt -qq install -y git ssh tar gzip ca-certificates 43 | # to make circleCI tools work properly --- 44 | - checkout 45 | - run: uname -a;pwd;df -h;id -a;lsb_release --all 46 | - run: 47 | command: | 48 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y cmake 49 | sudo DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" -qq install -y libtool autotools-dev automake checkinstall check git yasm libv4lconvert0 libv4l-dev 50 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y libopus-dev libvpx-dev pkg-config 51 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y libasound2-dev 52 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y linux-generic 53 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y libjpeg-dev 54 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y libpulse-dev 55 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y libconfig-dev 56 | sudo DEBIAN_FRONTEND=noninteractive apt-get -qq install -y astyle gawk sed bc 57 | sudo bash -c "echo '::1 localhost ipv6-localhost ipv6-loopback' >> /etc/hosts" 58 | gcc --version 59 | astyle --version 60 | cmake --version 61 | 62 | - run: 63 | command: | 64 | mkdir -p "$CIRCLE_ARTIFACTS" 65 | echo aaa > "$CIRCLE_ARTIFACTS"/blub.txt 66 | ls -al "$CIRCLE_ARTIFACTS"/blub.txt 67 | 68 | - run: 69 | command: | 70 | ### ------- compile and install libsodium ------- 71 | cd "$WRKDIR";rm -Rf libsodium 72 | cd "$WRKDIR";git clone --depth=1 --branch="$LIBSODIUM_BRANCH" https://github.com/jedisct1/libsodium.git ./libsodium 73 | cd "$WRKDIR";cd libsodium/ ; ./autogen.sh 74 | cd "$WRKDIR";cd libsodium/ ; ./configure && make check 75 | cd "$WRKDIR";cd libsodium/ ; sudo bash -c "printf 'y\naa\n\n' | checkinstall --install --pkgname libsodium --pkgversion 1.0.0 --nodoc --deldesc=no --pkglicense='GPL2'" 76 | cd "$WRKDIR";cd libsodium/ ; sudo ldconfig 77 | cd "$WRKDIR";cd libsodium ; sudo ldconfig -v 2>/dev/null | grep sodium 78 | ## --- now again to save the artefact --- 79 | cd "$WRKDIR";cd libsodium ; export INSTALL_DEST=/home/ubuntu/installdest_linux/ ; rm -Rf "$INSTALL_DEST" 80 | cd "$WRKDIR";cd libsodium ; export INSTALL_DEST=/home/ubuntu/installdest_linux/ ; mkdir -p "$INSTALL_DEST"/usr ; ./configure --prefix="$INSTALL_DEST"/usr 81 | cd "$WRKDIR";cd libsodium ; export INSTALL_DEST=/home/ubuntu/installdest_linux/ ; make install ; ls -alR "$INSTALL_DEST"/usr 82 | cd "$WRKDIR";mkdir -p $CIRCLE_ARTIFACTS/ubuntu_14_04_binaries 83 | cd "$WRKDIR";export INSTALL_DEST=/home/ubuntu/installdest_linux/ ; cd "$INSTALL_DEST" ; tar -czvf $CIRCLE_ARTIFACTS/ubuntu_14_04_binaries/pkg_libsodium.tar.gz * 84 | ## --- now again to save the artefact --- 85 | ### ------- compile and install libsodium ------- 86 | 87 | - run: 88 | command: | 89 | ### ------------ compile and install nasm ------------ 90 | cd "$WRKDIR";rm -Rf nasm 91 | cd "$WRKDIR";git clone http://repo.or.cz/nasm.git 92 | cd "$WRKDIR";cd nasm;git checkout nasm-2.13.03 93 | cd "$WRKDIR";cd nasm;./autogen.sh 94 | cd "$WRKDIR";cd nasm;./configure --prefix=/home/ubuntu/installdest_linux/usr 95 | cd "$WRKDIR";cd nasm;make clean 96 | cd "$WRKDIR";cd nasm;make -j4 97 | cd "$WRKDIR";cd nasm;touch nasm.1 98 | cd "$WRKDIR";cd nasm;touch ndisasm.1 99 | cd "$WRKDIR";cd nasm;make install 100 | ### ------------ compile and install nasm ------------ 101 | 102 | 103 | - run: 104 | command: | 105 | ### ------------ compile and install x264 ------------ 106 | cd "$WRKDIR";git clone git://git.videolan.org/x264.git 107 | cd "$WRKDIR";cd x264 108 | cd "$WRKDIR";cd x264;git checkout stable 109 | export PATH=/home/ubuntu/installdest_linux/usr/bin:/home/ubuntu/installdest_linux/bin:$PATH 110 | cd "$WRKDIR";cd x264;./configure --prefix=/home/ubuntu/installdest_linux/usr --disable-opencl --enable-shared --enable-static --disable-avs --disable-cli 111 | cd "$WRKDIR";cd x264;make clean 112 | cd "$WRKDIR";cd x264;make -j4 113 | cd "$WRKDIR";cd x264;make install 114 | ### ------------ compile and install x264 ------------ 115 | 116 | - run: 117 | command: | 118 | ### ------------ compile and install libav ------------ 119 | cd "$WRKDIR";git clone https://github.com/libav/libav 120 | cd "$WRKDIR";cd libav 121 | cd "$WRKDIR";cd libav;git checkout v12.3 122 | cd "$WRKDIR";cd libav;./configure --prefix=/home/ubuntu/installdest_linux/usr --disable-devices --disable-programs \ 123 | --disable-doc --disable-avdevice --disable-avformat \ 124 | --disable-swscale \ 125 | --disable-avfilter --disable-network --disable-everything \ 126 | --disable-bzlib \ 127 | --disable-libxcb-shm \ 128 | --disable-libxcb-xfixes \ 129 | --enable-parser=h264 \ 130 | --enable-runtime-cpudetect \ 131 | --enable-decoder=h264_vdpau \ 132 | --enable-vdpau \ 133 | --enable-gpl --enable-decoder=h264 134 | cd "$WRKDIR";cd libav;make clean 135 | cd "$WRKDIR";cd libav;make -j4 136 | cd "$WRKDIR";cd libav;make install 137 | ### ------------ compile and install libav ------------ 138 | 139 | 140 | - run: 141 | command: | 142 | ### ------------ compile and install c-toxcore ------------ 143 | cd "$WRKDIR";rm -Rf c-toxcore 144 | cd "$WRKDIR";git clone https://github.com/zoff99/c-toxcore_team ./c-toxcore 145 | export PKG_CONFIG_PATH=/home/ubuntu/installdest_linux/usr/lib/pkgconfig 146 | cd "$WRKDIR";cd c-toxcore ; git checkout zoff99/toxcore_v1.0.10__toxav_h264_001 147 | cd "$WRKDIR";cd c-toxcore ; export CFLAGS=" -D_GNU_SOURCE -I/home/ubuntu/installdest_linux/usr/include/ -O3 -g -fstack-protector-all " 148 | cd "$WRKDIR";cd c-toxcore ; export LDFLAGS=-L/home/ubuntu/installdest_linux/usr/lib 149 | cd "$WRKDIR";cd c-toxcore ; ./autogen.sh 150 | cd "$WRKDIR";cd c-toxcore ; ./configure --prefix=/home/ubuntu/installdest_linux/usr/ --disable-soname-versions --disable-testing --disable-shared || cat config.log 151 | cd "$WRKDIR";cd c-toxcore ; make -j 4 152 | cd "$WRKDIR";cd c-toxcore ; make install 153 | ### ------------ compile and install c-toxcore ------------ 154 | 155 | - run: 156 | command: | 157 | cd /home/ubuntu/installdest_linux/ ; find . 158 | 159 | - run: 160 | command: | 161 | set -x 162 | cd "$WRKDIR";cd toxcam ; rm -fv toxcam toxcam_static 163 | cd "$WRKDIR";cd toxcam ; gcc -O3 -fPIC -I/home/ubuntu/installdest_linux/usr/include/ -L/home/ubuntu/installdest_linux/usr/lib -o toxcam toxcam.c -std=gnu99 -lsodium -ltoxcore -ltoxav -lpthread -lvpx -lv4lconvert -lx264 -lavcodec -lavutil -lavresample -lm -lrt 164 | cd "$WRKDIR";cd toxcam ; ldd toxcam 165 | cd "$WRKDIR";cd toxcam ; gcc -g -O3 -Wall -Wextra -Wpedantic -L/home/ubuntu/installdest_linux/usr/lib -I/home/ubuntu/installdest_linux/usr/include/ -o toxcam_static toxcam.c -static -std=gnu99 -lsodium -ltoxcore -ltoxav -ltoxgroup -ltoxmessenger -ltoxfriends -ltoxnetcrypto -ltoxdht -ltoxnetwork -ltoxcrypto -lsodium -lpthread -static-libgcc -static-libstdc++ -lopus -lvpx -lm -lpthread -lv4lconvert -lx264 -lavcodec -lavutil -lavresample -ljpeg -lm -lrt 166 | cd "$WRKDIR";cd toxcam ; ls -al toxcam toxcam_static 167 | cd "$WRKDIR";mkdir -p $CIRCLE_ARTIFACTS/ubuntu_14_04_binaries/ 168 | cd "$WRKDIR";cp -av toxcam/toxcam $CIRCLE_ARTIFACTS/ubuntu_14_04_binaries/ 169 | cd "$WRKDIR";cp -av toxcam/toxcam_static $CIRCLE_ARTIFACTS/ubuntu_14_04_binaries/ 170 | 171 | - run: 172 | command: | 173 | cd toxcam ; ./toxcam_static 174 | background: true 175 | 176 | - run: 177 | command: | 178 | cd "$WRKDIR";sleep 10 179 | cd "$WRKDIR";cd toxcam ; cat ./toxcam.log 180 | cd "$WRKDIR";cd toxcam ; cat ./toxcam.log | grep '\-\-MyToxID\-\-:' | cut -d':' -f 3 181 | cd "$WRKDIR";sleep 10 182 | #- sleep 240 183 | cd "$WRKDIR";cd toxcam ; cat ./toxcam.log 184 | 185 | # Save artifacts 186 | - store_artifacts: 187 | path: /tmp/artifacts 188 | 189 | workflows: 190 | version: 2 191 | build-deploy: 192 | jobs: 193 | - build 194 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "c-toxcore"] 2 | path = c-toxcore 3 | url = https://github.com/TokTok/c-toxcore 4 | [submodule "libsodium"] 5 | path = libsodium 6 | url = https://github.com/jedisct1/libsodium 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | cache: false 3 | sudo: true 4 | dist: trusty 5 | 6 | addons: 7 | apt: 8 | sources: 9 | - avsm 10 | packages: 11 | - check 12 | - libcv-dev # For av_test. 13 | - libhighgui-dev # For av_test. 14 | - libopencv-contrib-dev # For av_test. 15 | - libsndfile1-dev # For av_test. 16 | - libvpx-dev 17 | - opam # For apidsl and Frama-C. 18 | - portaudio19-dev # For av_test. 19 | - texinfo # For libconfig. 20 | 21 | script: 22 | - sudo apt-get update 23 | - sudo apt-get install cmake 24 | - aptitude search 4l|grep -i convert ; echo exit 0 25 | - aptitude search 4l|grep -i v4l ; echo exit 0 26 | - sudo apt-get install build-essential libtool autotools-dev automake checkinstall check git yasm 27 | - sudo apt-get install libv4lconvert0 28 | - sudo apt-get install libv4l-dev 29 | - sudo apt-get install libjpeg-dev 30 | - sudo apt-get install libvpx-dev 31 | - sudo apt-get install pkg-config 32 | - sudo apt-get install linux-generic 33 | - sudo bash -c "echo '::1 localhost ipv6-localhost ipv6-loopback' >> /etc/hosts" # ipv6 localhost entry 34 | 35 | - gcc --version ; echo exit 0 36 | - clang --version ; echo exit 0 37 | 38 | ### submodules ---------------- 39 | - git submodule add --force https://github.com/TokTok/c-toxcore c-toxcore ; echo ok 40 | - git submodule add --force https://github.com/jedisct1/libsodium libsodium ; echo ok 41 | - git submodule init ; git submodule update ; echo ok 42 | - cd c-toxcore/ ; git checkout a096c71db867ac83fc3e01e0fbe98573d20f9286 43 | - cd .. 44 | - cd libsodium/ ; git checkout tags/1.0.11 45 | - cd .. 46 | ### submodules ---------------- 47 | 48 | ### ------- compile and install libsodium ------- 49 | - cd libsodium/ ; ./autogen.sh 50 | - ./configure && make check 51 | - sudo bash -c "printf 'y\naa\n\n' | checkinstall --install --pkgname libsodium --pkgversion 1.0.11 --nodoc --deldesc=no --pkglicense='GPL2'" 52 | - sudo ldconfig 53 | - sudo ldconfig -v 2>/dev/null | grep sodium 54 | - cd .. 55 | ### ------- compile and install libsodium ------- 56 | 57 | ### ------- compile and install libopus ------- 58 | - wget http://downloads.xiph.org/releases/opus/opus-1.1.3.tar.gz -O opus.tgz 59 | - tar -xzvf opus.tgz 60 | - rm opus.tgz 61 | - ls -al 62 | - mv -v opus* libopus 63 | - cd libopus/ # autogen ? 64 | - ./configure && make check 65 | - sudo bash -c "printf 'y\naa\n\n' | checkinstall --install --pkgname libopus --pkgversion 1.1.3 --nodoc --deldesc=no --pkglicense='GPL2'" 66 | - sudo ldconfig 67 | - sudo ldconfig -v 2>/dev/null | grep opus 68 | - cd .. 69 | ### ------- compile and install libopus ------- 70 | 71 | ### ------------ compile and install c-toxcore ------------ 72 | - cd c-toxcore ; cmake -DWARNINGS=OFF . 73 | - make 74 | #- sudo make install 75 | - sudo bash -c "printf 'y\naa\n\n' | checkinstall --install --pkgname toxcore --pkgversion 0.1.2 --nodoc --deldesc=no --pkglicense='GPL2'" 76 | - sudo ldconfig -v 2>/dev/null | grep toxcore 77 | ### ------------ run tests 78 | #- make test ARGS="-V" ; ex1=$? ; if [ $ex1 -ne 0 ]; then sleep 60; make test ARGS="-V" ; exit $? ; fi 79 | - cd .. 80 | ### ------------ compile and install c-toxcore ------------ 81 | 82 | - cd toxcam ; gcc -O2 -fPIC -Wall -Wextra -o toxcam toxcam.c -std=gnu99 -lsodium -I/usr/local/include/ -ltoxcore -ltoxav -lpthread -lvpx -lv4lconvert 83 | - ldd toxcam ; echo exit 0 84 | - find / -name '*libjpeg*' 2> /dev/null ; echo exit 0 85 | - gcc -O2 -Wall -Wextra -o toxcam_static toxcam.c -static -std=gnu99 -L/usr/local/lib -I/usr/local/include/ -lsodium -ltoxcore -ltoxav -ltoxgroup -ltoxmessenger -ltoxfriends -ltoxnetcrypto -ltoxdht -ltoxnetwork -ltoxcrypto -lsodium -lpthread -static-libgcc -static-libstdc++ -lopus -lvpx -lm -lpthread -lv4lconvert -ljpeg -lm -lrt 86 | - ls -al toxcam toxcam_static 87 | 88 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ToxCam 2 | 3 | You can take a PC or RaspberryPi with a camera on it and watch the 4 | camera stream from anywhere without a central server 5 | 6 | Build Status 7 | = 8 | **CircleCI:** [![CircleCI](https://circleci.com/gh/zoff99/ToxCam/tree/master.png?style=badge)](https://circleci.com/gh/zoff99/ToxCam) 9 | **Travis:** [![Build Status](https://travis-ci.org/zoff99/ToxCam.png?branch=master)](https://travis-ci.org/zoff99/ToxCam) 10 | 11 | 12 | Development Snapshot Version (Linux) 13 | = 14 | the latest Development Snapshot can be downloaded from CircleCI, [here](https://circleci.com/api/v1/project/zoff99/ToxCam/latest/artifacts/0/$CIRCLE_ARTIFACTS/ubuntu_14_04_binaries/toxcam_static?filter=successful&branch=master) 15 | 16 | Development Snapshot Version (Raspberry PI) 17 | = 18 | the latest Development Snapshot can be downloaded from CircleCI, [here](https://circleci.com/api/v1/project/zoff99/ToxCam/latest/artifacts/0/$CIRCLE_ARTIFACTS/RASPI/toxcam_static?filter=successful&branch=master) 19 | 20 | install on PI 21 | = 22 | 23 | ``` 24 | # as user pi: 25 | git clone https://github.com/zoff99/ToxCam 26 | cd ToxCam/toxcam 27 | chmod u+rwx loop_services.sh loop_tor_services.sh update_from_ci.sh scripts/*.sh 28 | ./update_from_ci.sh 29 | ``` 30 | ``` 31 | sudo apt-get install luvcview 32 | sudo sed -i -e "s#exit 0#su - pi bash -c '/home/pi/ToxCam/toxcam/loop_services.sh' > /dev/null 2>/dev/null \&\nexit 0#" /etc/rc.local 33 | ``` 34 | 35 | then reboot 36 | now ToxCam should be active already. get the ToxID 37 | 38 | ``` 39 | cat /home/pi/ToxCam/toxcam/toxcam.log|grep MyToxID|cut -d: -f3 40 | ``` 41 | 42 | note down the ToxID of your ToxCam and add it as friend from another ToxClient. 43 | 44 | 45 | install on PI with Tor 46 | = 47 | 48 | ``` 49 | # as user pi: 50 | git clone https://github.com/zoff99/ToxCam 51 | cd ToxCam/toxcam 52 | chmod u+rwx loop_services.sh loop_tor_services.sh update_from_ci.sh scripts/*.sh 53 | ./update_from_ci.sh 54 | ``` 55 | ``` 56 | sudo apt-get install luvcview 57 | sudo apt-get install tor tor-arm 58 | sudo sed -i -e "s#exit 0#su - pi bash -c '/home/pi/ToxCam/toxcam/loop_tor_services.sh' > /dev/null 2>/dev/null \&\nexit 0#" /etc/rc.local 59 | ``` 60 | 61 | then reboot 62 | now ToxCam should be active already. get the ToxID 63 | 64 | ``` 65 | cat /home/pi/ToxCam/toxcam/toxcam.log|grep MyToxID|cut -d: -f3 66 | ``` 67 | 68 | note down the ToxID of your ToxCam and add it as friend from another ToxClient. 69 | -------------------------------------------------------------------------------- /astyle/astylerc: -------------------------------------------------------------------------------- 1 | # Bracket Style Options 2 | --style=allman 3 | 4 | # Tab Options 5 | --indent=spaces=4 6 | 7 | # Indentation Options 8 | --indent-switches 9 | #--indent-preproc-block 10 | 11 | # Padding Options 12 | --pad-header 13 | --break-blocks 14 | --pad-oper 15 | --delete-empty-lines 16 | --unpad-paren 17 | --align-pointer=name 18 | --align-reference=name 19 | 20 | # Formatting Options 21 | --add-brackets 22 | --convert-tabs 23 | --max-code-length=120 24 | 25 | # Other Options 26 | --preserve-date 27 | --formatted 28 | --lineend=linux 29 | -------------------------------------------------------------------------------- /toxcam/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zoff99/ToxCam/a605bd5aa4ae7aa84178950b600822070fd93230/toxcam/avatar.png -------------------------------------------------------------------------------- /toxcam/loop_services.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | cd $(dirname "$0") 4 | 5 | while [ 1 == 1 ]; do 6 | ##################################################### 7 | # pick first available video device 8 | # change for your needs here! 9 | video_device=$(ls -1 /dev/video*|tail -1) 10 | # 11 | ##################################################### 12 | 13 | v4l2-ctl -d "$video_device" -v width=1280,height=720,pixelformat=YV12 14 | # v4l2-ctl -d "$video_device" -v width=640,height=480,pixelformat=YV12 15 | v4l2-ctl -d "$video_device" -p 15 16 | 17 | prog="./toxcam" 18 | if [ -e ./toxcam_static ]; then 19 | prog="./toxcam_static" 20 | fi 21 | chmod u+x "$prog" 22 | 23 | "$prog" -f -2 -b 200 -q 60 -d "$video_device" > /dev/null 2> /dev/null 24 | sleep 10 25 | done 26 | 27 | -------------------------------------------------------------------------------- /toxcam/loop_tor_services.sh: -------------------------------------------------------------------------------- 1 | 2 | #! /bin/bash 3 | 4 | ##################################################### 5 | # pick first available video device 6 | # change for your needs here! 7 | video_device=$(ls -1 /dev/video*|tail -1) 8 | # 9 | ##################################################### 10 | 11 | cd $(dirname "$0") 12 | 13 | while [ 1 == 1 ]; do 14 | ./toxcam_static -2 -T -d "$video_device" # > /dev/null 2> /dev/null 15 | sleep 10 16 | done 17 | -------------------------------------------------------------------------------- /toxcam/scripts/get_cpu_temp.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | _dir=$(dirname "${0}") 4 | _scr=$(basename "$0") 5 | . "$_dir"/set_os_dir.sh 6 | . "$_dir"/"$_OS_DIR_"/"$_scr" 7 | -------------------------------------------------------------------------------- /toxcam/scripts/get_gpu_temp.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | _dir=$(dirname "${0}") 4 | _scr=$(basename "$0") 5 | . "$_dir"/set_os_dir.sh 6 | . "$_dir"/"$_OS_DIR_"/"$_scr" 7 | -------------------------------------------------------------------------------- /toxcam/scripts/linux/get_cpu_temp.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | -------------------------------------------------------------------------------- /toxcam/scripts/linux/get_gpu_temp.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | -------------------------------------------------------------------------------- /toxcam/scripts/raspi/get_cpu_temp.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | cpu1=$( 4 | * in 2017 5 | * 6 | * dirty hack (echobot and toxic were used as blueprint) 7 | * 8 | * 9 | * compile on linux (dynamic): 10 | * gcc -O2 -fPIC -Wall -Wextra -Wpedantic -o toxdoorspy toxdoorspy.c -std=gnu99 -lsodium -I/usr/local/include/ -ltoxcore -ltoxav -lpthread -lvpx -lv4lconvert 11 | * compile for debugging (dynamic): 12 | * gcc -O0 -g -fPIC -Wall -Wextra -Wpedantic -o toxdoorspy toxdoorspy.c -std=gnu99 -lsodium -I/usr/local/include/ -ltoxcore -ltoxav -lpthread -lvpx -lv4lconvert 13 | * 14 | * compile on linux (static): 15 | * gcc -O2 -Wall -Wextra -Wpedantic -o toxdoorspy_static toxdoorspy.c -static -std=gnu99 -L/usr/local/lib -I/usr/local/include/ \ 16 | -lsodium -ltoxcore -ltoxav -ltoxgroup -ltoxmessenger -ltoxfriends -ltoxnetcrypto \ 17 | -ltoxdht -ltoxnetwork -ltoxcrypto -lsodium -lpthread -static-libgcc -static-libstdc++ \ 18 | -lopus -lvpx -lm -lpthread -lv4lconvert 19 | * 20 | * 21 | * 22 | */ 23 | 24 | #define _GNU_SOURCE 25 | 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #ifdef TOX_HAVE_TOXUTIL 50 | #include 51 | #endif 52 | #include 53 | 54 | 55 | 56 | #include 57 | #include 58 | 59 | 60 | /* 61 | * ------------------------------------------------------------ 62 | * TOXCORE compatibility layer -------------------------------- 63 | * ------------------------------------------------------------ 64 | */ 65 | #ifndef TOXCOMPAT_H_ 66 | #define TOXCOMPAT_H_ 67 | 68 | #endif 69 | /* 70 | * ------------------------------------------------------------ 71 | * TOXCORE compatibility layer -------------------------------- 72 | * ------------------------------------------------------------ 73 | */ 74 | 75 | 76 | // --------- change nospam --------- 77 | // #define CHANGE_NOSPAM_REGULARLY 1 78 | // --------- change nospam --------- 79 | 80 | 81 | 82 | 83 | #include 84 | #include 85 | #include 86 | 87 | #define V4LCONVERT 1 88 | // #define HAVE_SOUND 1 89 | 90 | #ifdef HAVE_SOUND 91 | #include 92 | #endif 93 | 94 | 95 | 96 | #ifdef V4LCONVERT 97 | #include 98 | #endif 99 | 100 | #ifdef V4LCONVERT 101 | static struct v4lconvert_data *v4lconvert_data; 102 | #endif 103 | 104 | 105 | 106 | // ----------- version ----------- 107 | // ----------- version ----------- 108 | #define VERSION_MAJOR 0 109 | #define VERSION_MINOR 99 110 | #define VERSION_PATCH 19 111 | static const char global_version_string[] = "0.99.19"; 112 | // ----------- version ----------- 113 | // ----------- version ----------- 114 | 115 | typedef struct DHT_node 116 | { 117 | const char *ip; 118 | uint16_t port; 119 | const char key_hex[TOX_PUBLIC_KEY_SIZE * 2 + 1]; 120 | unsigned char key_bin[TOX_PUBLIC_KEY_SIZE]; 121 | } DHT_node; 122 | 123 | 124 | 125 | #define MAX_AVATAR_FILE_SIZE 65536 126 | #define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */ 127 | #define TIME_STR_SIZE 32 128 | #define MAX_STR_SIZE 200 129 | 130 | #define CURRENT_LOG_LEVEL 9 // 0 -> error, 1 -> warn, 2 -> info, 9 -> debug 131 | 132 | #define KiB 1024 133 | #define MiB 1048576 /* 1024^2 */ 134 | #define GiB 1073741824 /* 1024^3 */ 135 | 136 | #define seconds_since_last_mod 1 // how long to wait before we process image files in seconds 137 | #define MAX_FILES 6 // how many filetransfers to/from 1 friend at the same time? 138 | #define MAX_RESEND_FILE_BEFORE_ASK 6 139 | #define AUTO_RESEND_SECONDS 60*5 // resend for this much seconds before asking again [5 min] 140 | #define VIDEO_BUFFER_COUNT 4 141 | uint32_t DEFAULT_GLOBAL_VID_BITRATE = 900; // kbit/sec 142 | int32_t RC_MAX_QUANTIZER = 56; // valid values between 10 - 56 143 | #define DEFAULT_GLOBAL_AUD_BITRATE 6 // kbit/sec 144 | #define DEFAULT_GLOBAL_MIN_VID_BITRATE 90 // kbit/sec 145 | #define DEFAULT_GLOBAL_MIN_AUD_BITRATE 6 // kbit/sec 146 | // 250=4fps, 500=2fps, 160=6fps, 90=11fps // default video fps (sleep in msecs.) 147 | int DEFAULT_FPS_SLEEP_MS = 10; 148 | #define PROXY_PORT_TOR_DEFAULT 9050 149 | #define RECONNECT_AFTER_OFFLINE_SECONDS 90 // 90s offline and we try to reconnect 150 | 151 | #define CLEAR(x) memset(&(x), 0, sizeof(x)) 152 | #define c_sleep(x) usleep(1000*x) 153 | 154 | typedef enum FILE_TRANSFER_STATE 155 | { 156 | FILE_TRANSFER_INACTIVE, // == 0 , this is important 157 | FILE_TRANSFER_PAUSED, 158 | FILE_TRANSFER_PENDING, 159 | FILE_TRANSFER_STARTED, 160 | } FILE_TRANSFER_STATE; 161 | 162 | typedef enum FILE_TRANSFER_DIRECTION 163 | { 164 | FILE_TRANSFER_SEND, 165 | FILE_TRANSFER_RECV 166 | } FILE_TRANSFER_DIRECTION; 167 | 168 | struct FileTransfer 169 | { 170 | FILE *file; 171 | FILE_TRANSFER_STATE state; 172 | FILE_TRANSFER_DIRECTION direction; 173 | uint8_t file_type; 174 | char file_name[TOX_MAX_FILENAME_LENGTH + 1]; 175 | char file_path[PATH_MAX + 1]; /* Not used by senders */ 176 | double bps; 177 | uint32_t filenum; 178 | uint32_t friendnum; 179 | size_t index; 180 | uint64_t file_size; 181 | uint64_t position; 182 | time_t last_keep_alive; /* The last time we sent or received data */ 183 | uint32_t line_id; 184 | uint8_t file_id[TOX_FILE_ID_LENGTH]; 185 | }; 186 | 187 | 188 | struct LastOnline 189 | { 190 | uint64_t last_on; 191 | struct tm tm; 192 | char hour_min_str[TIME_STR_SIZE]; /* holds 24-hour time string e.g. "15:43:24" */ 193 | }; 194 | 195 | struct GroupChatInvite 196 | { 197 | char *key; 198 | uint16_t length; 199 | uint8_t type; 200 | bool pending; 201 | }; 202 | 203 | typedef struct 204 | { 205 | char name[TOXIC_MAX_NAME_LENGTH + 1]; 206 | int namelength; 207 | char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; 208 | size_t statusmsg_len; 209 | char pub_key[TOX_PUBLIC_KEY_SIZE]; 210 | char pubkey_string[(TOX_ADDRESS_SIZE * 2 + 1)]; 211 | char worksubdir[MAX_STR_SIZE]; 212 | uint32_t num; 213 | bool active; 214 | TOX_CONNECTION connection_status; 215 | bool is_typing; 216 | uint8_t status; 217 | struct LastOnline last_online; 218 | int have_resumed_fts; // wait with new FTs until all old FTs have been started (to resume) including avatars! 219 | struct FileTransfer file_receiver[MAX_FILES]; 220 | struct FileTransfer file_sender[MAX_FILES]; 221 | char last_answer[100]; 222 | int waiting_for_answer; // 0 -> no, 1 -> waiting for answer, 2 -> got answer 223 | time_t auto_resend_start_time; 224 | // mz_zip_archive zip_archive; 225 | } ToxicFriend; 226 | 227 | typedef struct 228 | { 229 | char name[TOXIC_MAX_NAME_LENGTH + 1]; 230 | int namelength; 231 | char pub_key[TOX_PUBLIC_KEY_SIZE]; 232 | uint32_t num; 233 | bool active; 234 | uint64_t last_on; 235 | } BlockedFriend; 236 | 237 | typedef struct 238 | { 239 | int num_selected; 240 | size_t num_friends; 241 | size_t num_online; 242 | size_t max_idx; /* 1 + the index of the last friend in list */ 243 | uint32_t *index; 244 | ToxicFriend *list; 245 | } FriendsList; 246 | 247 | 248 | static struct Avatar 249 | { 250 | char name[TOX_MAX_FILENAME_LENGTH + 1]; 251 | size_t name_len; 252 | char path[PATH_MAX + 1]; 253 | size_t path_len; 254 | off_t size; 255 | } Avatar; 256 | 257 | typedef struct 258 | { 259 | bool incoming; 260 | uint32_t state; 261 | uint32_t audio_bit_rate; 262 | uint32_t video_bit_rate; 263 | pthread_mutex_t arb_mutex[1]; 264 | } CallControl; 265 | 266 | 267 | struct buffer 268 | { 269 | void *start; 270 | size_t length; 271 | }; 272 | 273 | typedef struct TOXCAM_AV_VIDEO_FRAME 274 | { 275 | uint16_t w, h; 276 | uint8_t *y, *u, *v; 277 | // uint8_t bit_depth; 278 | } toxcam_av_video_frame; 279 | 280 | 281 | 282 | void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length); 283 | int avatar_send(Tox *m, uint32_t friendnum); 284 | struct FileTransfer *new_file_transfer(uint32_t friendnum, uint32_t filenum, FILE_TRANSFER_DIRECTION direction, 285 | uint8_t type); 286 | void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum); 287 | int has_reached_max_file_transfer_for_friend(uint32_t num); 288 | static int find_friend_in_friendlist(uint32_t friendnum); 289 | int is_friend_online(Tox *tox, uint32_t num); 290 | void av_local_disconnect(ToxAV *av, uint32_t num); 291 | void run_cmd_return_output(const char *command, char *output, int lastline); 292 | void save_resumable_fts(Tox *m, uint32_t friendnum); 293 | void resume_resumable_fts(Tox *m, uint32_t friendnum); 294 | void left_top_bar_into_yuv_frame(int bar_start_x_pix, int bar_start_y_pix, int bar_w_pix, int bar_h_pix, uint8_t r, 295 | uint8_t g, uint8_t b); 296 | void print_font_char(int start_x_pix, int start_y_pix, int font_char_num, uint8_t col_value); 297 | void text_on_yuf_frame_xy(int start_x_pix, int start_y_pix, const char *text); 298 | void blinking_dot_on_frame_xy(int start_x_pix, int start_y_pix, int *state); 299 | void black_yuf_frame_xy(); 300 | void rbg_to_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t *y, uint8_t *u, uint8_t *v); 301 | void set_color_in_yuv_frame_xy(uint8_t *yuv_frame, int px_x, int px_y, int frame_w, int frame_h, uint8_t r, uint8_t g, 302 | uint8_t b); 303 | 304 | 305 | const char *savedata_filename = "savedata.tox"; 306 | const char *savedata_tmp_filename = "savedata.tox.tmp"; 307 | const char *log_filename = "toxcam.log"; 308 | const char *my_avatar_filename = "avatar.png"; 309 | 310 | char *v4l2_device; // video device filename 311 | 312 | const char *shell_cmd__single_shot = "./scripts/single_shot.sh 2> /dev/null"; 313 | const char *shell_cmd__get_cpu_temp = "./scripts/get_cpu_temp.sh 2> /dev/null"; 314 | const char *shell_cmd__get_gpu_temp = "./scripts/get_gpu_temp.sh 2> /dev/null"; 315 | const char *shell_cmd__get_my_number_of_open_files = "cat /proc/sys/fs/file-nr 2> /dev/null"; 316 | int global_want_restart = 0; 317 | const char *global_timestamp_format = "%H:%M:%S"; 318 | const char *global_long_timestamp_format = "%Y-%m-%d %H:%M:%S"; 319 | const char *global_overlay_timestamp_format = "%Y-%m-%d %H:%M:%S"; 320 | uint64_t global_start_time; 321 | int global_cam_device_fd = 0; 322 | uint32_t n_buffers; 323 | struct buffer *buffers = NULL; 324 | uint16_t video_width = 0; 325 | uint16_t video_height = 0; 326 | struct v4l2_format format; 327 | struct v4l2_format dest_format; 328 | toxcam_av_video_frame av_video_frame; 329 | vpx_image_t input; 330 | int global_video_active = 0; 331 | int global_send_first_frame = 0; 332 | int switch_nodelist_2 = 0; 333 | int video_high = 0; 334 | int switch_tcponly = 0; 335 | int use_tor = 0; 336 | time_t my_last_offline_timestamp = -1; 337 | time_t my_last_online_timestamp = -1; 338 | 339 | 340 | uint32_t global_audio_bit_rate; 341 | uint32_t global_video_bit_rate; 342 | ToxAV *mytox_av = NULL; 343 | int tox_loop_running = 1; 344 | int global_blink_state = 0; 345 | 346 | int toxav_video_thread_stop = 0; 347 | int toxav_iterate_thread_stop = 0; 348 | 349 | // -- hardcoded -- 350 | // -- hardcoded -- 351 | // -- hardcoded -- 352 | uint32_t friend_to_send_video_to = -1; 353 | // -- hardcoded -- 354 | // -- hardcoded -- 355 | // -- hardcoded -- 356 | 357 | int video_call_enabled = 1; 358 | uint32_t global_last_change_nospam_ts = 0; 359 | #define CHANGE_NOSPAM_REGULAR_INTERVAL_SECS (3600) // 1h 360 | 361 | 362 | TOX_CONNECTION my_connection_status = TOX_CONNECTION_NONE; 363 | FILE *logfile = NULL; 364 | FriendsList Friends; 365 | 366 | void dbg(int level, const char *fmt, ...) 367 | { 368 | char *level_and_format = NULL; 369 | char *fmt_copy = NULL; 370 | 371 | if (fmt == NULL) 372 | { 373 | return; 374 | } 375 | 376 | if (strlen(fmt) < 1) 377 | { 378 | return; 379 | } 380 | 381 | if (!logfile) 382 | { 383 | return; 384 | } 385 | 386 | if ((level < 0) || (level > 9)) 387 | { 388 | level = 0; 389 | } 390 | 391 | level_and_format = malloc(strlen(fmt) + 3 + 1); 392 | 393 | if (!level_and_format) 394 | { 395 | // dbg(9, stderr, "free:000a\n"); 396 | return; 397 | } 398 | 399 | fmt_copy = level_and_format + 2; 400 | strcpy(fmt_copy, fmt); 401 | level_and_format[1] = ':'; 402 | 403 | if (level == 0) 404 | { 405 | level_and_format[0] = 'E'; 406 | } 407 | else if (level == 1) 408 | { 409 | level_and_format[0] = 'W'; 410 | } 411 | else if (level == 2) 412 | { 413 | level_and_format[0] = 'I'; 414 | } 415 | else 416 | { 417 | level_and_format[0] = 'D'; 418 | } 419 | 420 | level_and_format[(strlen(fmt) + 2)] = '\0'; // '\0' or '\n' 421 | level_and_format[(strlen(fmt) + 3)] = '\0'; 422 | time_t t3 = time(NULL); 423 | struct tm tm3 = *localtime(&t3); 424 | char *level_and_format_2 = malloc(strlen(level_and_format) + 5 + 3 + 3 + 1 + 3 + 3 + 3 + 1); 425 | level_and_format_2[0] = '\0'; 426 | snprintf(level_and_format_2, (strlen(level_and_format) + 5 + 3 + 3 + 1 + 3 + 3 + 3 + 1), 427 | "%04d-%02d-%02d %02d:%02d:%02d:%s", 428 | tm3.tm_year + 1900, tm3.tm_mon + 1, tm3.tm_mday, 429 | tm3.tm_hour, tm3.tm_min, tm3.tm_sec, level_and_format); 430 | 431 | if (level <= CURRENT_LOG_LEVEL) 432 | { 433 | va_list ap; 434 | va_start(ap, fmt); 435 | vfprintf(logfile, level_and_format_2, ap); 436 | va_end(ap); 437 | } 438 | 439 | // dbg(9, "free:001\n"); 440 | if (level_and_format) 441 | { 442 | // dbg(9, "free:001.a\n"); 443 | free(level_and_format); 444 | } 445 | 446 | if (level_and_format_2) 447 | { 448 | free(level_and_format_2); 449 | } 450 | 451 | // dbg(9, "free:002\n"); 452 | } 453 | 454 | 455 | uint32_t generate_random_uint32() 456 | { 457 | // HINT: this is not perfectly randon, FIX ME! 458 | uint32_t r; 459 | struct timeval time; 460 | gettimeofday(&time, NULL); 461 | srand((time.tv_sec * 1000) + (time.tv_usec / 1000)); 462 | rand(); 463 | rand(); 464 | r = rand(); 465 | return r; 466 | } 467 | 468 | 469 | 470 | 471 | 472 | 473 | // linked list ---------- 474 | typedef struct ll_node 475 | { 476 | void *val; 477 | struct ll_node *next; 478 | } ll_node_t; 479 | 480 | 481 | void ll_fill_val(void **val, size_t data_size, void *data) 482 | { 483 | if (*val != NULL) 484 | { 485 | free(*val); 486 | *val = NULL; 487 | } 488 | 489 | *val = malloc(data_size); 490 | memcpy(*val, data, data_size); 491 | } 492 | 493 | 494 | // add to the beginning of the list 495 | void ll_push(ll_node_t **head, size_t data_size, void *data) 496 | { 497 | ll_node_t *new_node; 498 | new_node = calloc(1, sizeof(ll_node_t)); 499 | ll_fill_val(&(new_node->val), data_size, data); 500 | new_node->next = *head; 501 | *head = new_node; 502 | } 503 | 504 | void *ll_pop(ll_node_t **head) 505 | { 506 | void *retval = NULL; 507 | ll_node_t *next_node = NULL; 508 | 509 | if (*head == NULL) 510 | { 511 | return NULL; 512 | } 513 | 514 | next_node = (*head)->next; 515 | retval = (*head)->val; 516 | free(*head); 517 | *head = next_node; 518 | return retval; 519 | } 520 | 521 | void ll_free_val(void *val) 522 | { 523 | if (val != NULL) 524 | { 525 | free(val); 526 | val = NULL; 527 | } 528 | } 529 | 530 | void *ll_remove_by_index(ll_node_t **head, int n) 531 | { 532 | int i = 0; 533 | void *retval = NULL; 534 | ll_node_t *current = *head; 535 | ll_node_t *temp_node = NULL; 536 | 537 | if (n == 0) 538 | { 539 | return ll_pop(head); 540 | } 541 | 542 | for (i = 0; i < n - 1; i++) 543 | { 544 | if (current->next == NULL) 545 | { 546 | return NULL; 547 | } 548 | 549 | current = current->next; 550 | } 551 | 552 | temp_node = current->next; 553 | 554 | if (temp_node != NULL) 555 | { 556 | retval = temp_node->val; 557 | current->next = temp_node->next; 558 | free(temp_node); 559 | } 560 | 561 | return retval; 562 | } 563 | 564 | #if 0 565 | void ll_print_list(ll_node_t *head) 566 | { 567 | ll_node_t *current = head; 568 | int i = 0; 569 | 570 | while (current != NULL) 571 | { 572 | dbg(9, "element #%d=%p\n", i, current->val); 573 | i++; 574 | current = current->next; 575 | } 576 | } 577 | #endif 578 | 579 | ll_node_t *resumable_filetransfers = NULL; 580 | 581 | // linked list ---------- 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | time_t get_unix_time(void) 592 | { 593 | return time(NULL); 594 | } 595 | 596 | void yieldcpu(uint32_t ms) 597 | { 598 | usleep(1000 * ms); 599 | } 600 | 601 | 602 | int get_number_in_string(const char *str, int default_value) 603 | { 604 | int number; 605 | 606 | while (!(*str >= '0' && *str <= '9') && (*str != '-') && (*str != '+')) 607 | { 608 | str++; 609 | } 610 | 611 | if (sscanf(str, "%d", &number) == 1) 612 | { 613 | return number; 614 | } 615 | 616 | // no int found, return default value 617 | return default_value; 618 | } 619 | 620 | 621 | void tox_log_cb__custom(Tox *tox, TOX_LOG_LEVEL level, const char *file, uint32_t line, const char *func, 622 | const char *message, void *user_data) 623 | { 624 | dbg(9, "%d:%s:%d:%s:%s\n", (int)level, file, (int)line, func, message); 625 | } 626 | 627 | 628 | Tox *create_tox() 629 | { 630 | Tox *tox; 631 | struct Tox_Options options; 632 | /* 633 | TOX_ERR_OPTIONS_NEW err_options; 634 | struct Tox_Options options = tox_options_new(&err_options); 635 | if (err_options != TOX_ERR_OPTIONS_NEW_OK) 636 | { 637 | dbg(0, "tox_options_new error\n"); 638 | } 639 | */ 640 | tox_options_default(&options); 641 | // ---------------------------------------------- 642 | // uint16_t tcp_port = 33445; // act as TCP relay 643 | uint16_t tcp_port = 0; // DON'T act as TCP relay 644 | // ---------------------------------------------- 645 | 646 | // ---------------------------------------------- 647 | if (switch_tcponly == 0) 648 | { 649 | options.udp_enabled = true; // UDP mode 650 | dbg(0, "setting UDP mode\n"); 651 | } 652 | else 653 | { 654 | options.udp_enabled = false; // TCP mode 655 | dbg(0, "setting TCP mode\n"); 656 | } 657 | 658 | // ---------------------------------------------- 659 | options.ipv6_enabled = false; 660 | options.local_discovery_enabled = true; 661 | options.hole_punching_enabled = false; 662 | options.tcp_port = tcp_port; 663 | 664 | if (use_tor == 1) 665 | { 666 | dbg(0, "setting Tor Relay mode\n"); 667 | options.udp_enabled = false; // TCP mode 668 | dbg(0, "setting TCP mode\n"); 669 | const char *proxy_host = "127.0.0.1"; 670 | dbg(0, "setting proxy_host %s\n", proxy_host); 671 | uint16_t proxy_port = PROXY_PORT_TOR_DEFAULT; 672 | dbg(0, "setting proxy_port %d\n", (int)proxy_port); 673 | options.proxy_type = TOX_PROXY_TYPE_SOCKS5; 674 | options.proxy_host = proxy_host; 675 | options.proxy_port = proxy_port; 676 | } 677 | else 678 | { 679 | options.proxy_type = TOX_PROXY_TYPE_NONE; 680 | } 681 | 682 | // ------------------------------------------------------------ 683 | // set our own handler for c-toxcore logging messages!! 684 | options.log_callback = tox_log_cb__custom; 685 | // ------------------------------------------------------------ 686 | FILE *f = fopen(savedata_filename, "rb"); 687 | 688 | if (f) 689 | { 690 | fseek(f, 0, SEEK_END); 691 | long fsize = ftell(f); 692 | fseek(f, 0, SEEK_SET); 693 | uint8_t *savedata = malloc(fsize); 694 | size_t dummy = fread(savedata, fsize, 1, f); 695 | 696 | if (dummy < 1) 697 | { 698 | dbg(0, "reading savedata failed\n"); 699 | } 700 | 701 | fclose(f); 702 | options.savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE; 703 | options.savedata_data = savedata; 704 | options.savedata_length = fsize; 705 | #ifdef TOX_HAVE_TOXUTIL 706 | tox = tox_utils_new(&options, NULL); 707 | #else 708 | tox = tox_new(&options, NULL); 709 | #endif 710 | free((void *)savedata); 711 | } 712 | else 713 | { 714 | #ifdef TOX_HAVE_TOXUTIL 715 | tox = tox_utils_new(&options, NULL); 716 | #else 717 | tox = tox_new(&options, NULL); 718 | #endif 719 | } 720 | 721 | bool local_discovery_enabled = tox_options_get_local_discovery_enabled(&options); 722 | dbg(9, "local discovery enabled = %d\n", (int)local_discovery_enabled); 723 | return tox; 724 | } 725 | 726 | void replace_bad_char_from_string(char *str, const char replace_with) 727 | { 728 | // win32: '\ / : * ? " < > |' 729 | char bad_chars[] = "/:*?<>|\""; 730 | int i; 731 | int j; 732 | 733 | if ((str) && (strlen(str) > 0)) 734 | { 735 | for (i = 0; (int)i < (int)strlen(str) ; i++) 736 | { 737 | for (j = 0; (int)j < (int)strlen(bad_chars); j++) 738 | if (str[i] == bad_chars[j]) 739 | { 740 | str[i] = replace_with; 741 | } 742 | } 743 | } 744 | } 745 | 746 | 747 | void update_savedata_file(const Tox *tox) 748 | { 749 | size_t size = tox_get_savedata_size(tox); 750 | char *savedata = malloc(size); 751 | tox_get_savedata(tox, (uint8_t *)savedata); 752 | FILE *f = fopen(savedata_tmp_filename, "wb"); 753 | fwrite(savedata, size, 1, f); 754 | fclose(f); 755 | rename(savedata_tmp_filename, savedata_filename); 756 | free(savedata); 757 | } 758 | 759 | off_t file_size(const char *path) 760 | { 761 | struct stat st; 762 | 763 | if (stat(path, &st) == -1) 764 | { 765 | return 0; 766 | } 767 | 768 | return st.st_size; 769 | } 770 | 771 | int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size) 772 | { 773 | if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) 774 | { 775 | return -1; 776 | } 777 | 778 | size_t i; 779 | 780 | for (i = 0; i < TOX_ADDRESS_SIZE; ++i) 781 | { 782 | snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); 783 | } 784 | 785 | return 0; 786 | } 787 | 788 | void random_char(char *output, int len) 789 | { 790 | int i; 791 | srandom(time(NULL)); 792 | 793 | for (i = 0; i < len - 1; i++) 794 | { 795 | output[i] = (unsigned char)(rand() % 255 + 1); 796 | } 797 | 798 | output[len - 1] = '\0'; 799 | } 800 | 801 | void bin_id_to_string_all(const char *bin_id, size_t bin_id_size, char *output, size_t output_size) 802 | { 803 | if (bin_id) 804 | { 805 | size_t i; 806 | 807 | for (i = 0; i < bin_id_size; ++i) 808 | { 809 | snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); 810 | } 811 | } 812 | } 813 | 814 | 815 | size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname) 816 | { 817 | int len = strlen(pathname) - 1; 818 | char *path = strdup(pathname); 819 | 820 | if (path == NULL) 821 | { 822 | // TODO 823 | } 824 | 825 | while (len >= 0 && pathname[len] == '/') 826 | { 827 | path[len--] = '\0'; 828 | } 829 | 830 | char *finalname = strdup(path); 831 | 832 | if (finalname == NULL) 833 | { 834 | // TODO 835 | } 836 | 837 | const char *basenm = strrchr(path, '/'); 838 | 839 | if (basenm != NULL) 840 | { 841 | if (basenm[1]) 842 | { 843 | strcpy(finalname, &basenm[1]); 844 | } 845 | } 846 | 847 | snprintf(namebuf, bufsize, "%s", finalname); 848 | free(finalname); 849 | free(path); 850 | return strlen(namebuf); 851 | } 852 | 853 | void shuffle(int *array, size_t n) 854 | { 855 | struct timeval tv; 856 | gettimeofday(&tv, NULL); 857 | int usec = tv.tv_usec; 858 | srand48(usec); 859 | 860 | if (n > 1) 861 | { 862 | size_t i; 863 | 864 | for (i = n - 1; i > 0; i--) 865 | { 866 | size_t j = (unsigned int)(drand48() * (i + 1)); 867 | int t = array[j]; 868 | array[j] = array[i]; 869 | array[i] = t; 870 | } 871 | } 872 | } 873 | 874 | 875 | void bootstap_nodes(Tox *tox, DHT_node nodes[], int number_of_nodes, int add_as_tcp_relay) 876 | { 877 | bool res = 0; 878 | size_t i = 0; 879 | int random_order_nodenums[number_of_nodes]; 880 | 881 | for (size_t j = 0; (int)j < (int)number_of_nodes; j++) 882 | { 883 | random_order_nodenums[j] = (int)j; 884 | } 885 | 886 | shuffle(random_order_nodenums, number_of_nodes); 887 | 888 | for (size_t j = 0; (int)j < (int)number_of_nodes; j++) 889 | { 890 | i = (size_t)random_order_nodenums[j]; 891 | res = sodium_hex2bin(nodes[i].key_bin, sizeof(nodes[i].key_bin), 892 | nodes[i].key_hex, sizeof(nodes[i].key_hex) - 1, NULL, NULL, NULL); 893 | // dbg(9, "sodium_hex2bin:res=%d\n", res); 894 | TOX_ERR_BOOTSTRAP error; 895 | res = tox_bootstrap(tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, &error); 896 | 897 | if (res != true) 898 | { 899 | if (error == TOX_ERR_BOOTSTRAP_OK) 900 | { 901 | // dbg(9, "bootstrap:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_OK\n", nodes[i].ip, nodes[i].port); 902 | } 903 | else if (error == TOX_ERR_BOOTSTRAP_NULL) 904 | { 905 | // dbg(9, "bootstrap:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_NULL\n", nodes[i].ip, nodes[i].port); 906 | } 907 | else if (error == TOX_ERR_BOOTSTRAP_BAD_HOST) 908 | { 909 | // dbg(9, "bootstrap:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_BAD_HOST\n", nodes[i].ip, nodes[i].port); 910 | } 911 | else if (error == TOX_ERR_BOOTSTRAP_BAD_PORT) 912 | { 913 | // dbg(9, "bootstrap:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_BAD_PORT\n", nodes[i].ip, nodes[i].port); 914 | } 915 | } 916 | else 917 | { 918 | // dbg(9, "bootstrap:%s %d [TRUE]res=%d\n", nodes[i].ip, nodes[i].port, res); 919 | } 920 | 921 | if ((add_as_tcp_relay == 1) || (switch_tcponly == 1)) 922 | { 923 | res = tox_add_tcp_relay(tox, nodes[i].ip, nodes[i].port, nodes[i].key_bin, &error); // use also as TCP relay 924 | 925 | if (res != true) 926 | { 927 | if (error == TOX_ERR_BOOTSTRAP_OK) 928 | { 929 | // dbg(9, "add_tcp_relay:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_OK\n", nodes[i].ip, nodes[i].port); 930 | } 931 | else if (error == TOX_ERR_BOOTSTRAP_NULL) 932 | { 933 | // dbg(9, "add_tcp_relay:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_NULL\n", nodes[i].ip, nodes[i].port); 934 | } 935 | else if (error == TOX_ERR_BOOTSTRAP_BAD_HOST) 936 | { 937 | // dbg(9, "add_tcp_relay:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_BAD_HOST\n", nodes[i].ip, nodes[i].port); 938 | } 939 | else if (error == TOX_ERR_BOOTSTRAP_BAD_PORT) 940 | { 941 | // dbg(9, "add_tcp_relay:%s %d [FALSE]res=TOX_ERR_BOOTSTRAP_BAD_PORT\n", nodes[i].ip, nodes[i].port); 942 | } 943 | } 944 | else 945 | { 946 | // dbg(9, "add_tcp_relay:%s %d [TRUE]res=%d\n", nodes[i].ip, nodes[i].port, res); 947 | } 948 | } 949 | else 950 | { 951 | dbg(2, "Not adding any TCP relays\n"); 952 | } 953 | } 954 | } 955 | 956 | 957 | void bootstrap(Tox *tox) 958 | { 959 | // these nodes seem to be faster!! 960 | DHT_node nodes1[] = 961 | { 962 | {"178.62.250.138", 33445, "788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B", {0}}, 963 | {"51.15.37.145", 33445, "6FC41E2BD381D37E9748FC0E0328CE086AF9598BECC8FEB7DDF2E440475F300E", {0}}, 964 | {"130.133.110.14", 33445, "461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F", {0}}, 965 | {"23.226.230.47", 33445, "A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074", {0}}, 966 | {"163.172.136.118", 33445, "2C289F9F37C20D09DA83565588BF496FAB3764853FA38141817A72E3F18ACA0B", {0}}, 967 | {"217.182.143.254", 443, "7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147", {0}}, 968 | {"185.14.30.213", 443, "2555763C8C460495B14157D234DD56B86300A2395554BCAE4621AC345B8C1B1B", {0}}, 969 | {"136.243.141.187", 443, "6EE1FADE9F55CC7938234CC07C864081FC606D8FE7B751EDA217F268F1078A39", {0}}, 970 | {"128.199.199.197", 33445, "B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09", {0}}, 971 | {"198.46.138.44", 33445, "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67", {0}} 972 | }; 973 | // more nodes here, but maybe some issues 974 | DHT_node nodes2[] = 975 | { 976 | {"178.62.250.138", 33445, "788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B", {0}}, 977 | {"136.243.141.187", 443, "6EE1FADE9F55CC7938234CC07C864081FC606D8FE7B751EDA217F268F1078A39", {0}}, 978 | {"185.14.30.213", 443, "2555763C8C460495B14157D234DD56B86300A2395554BCAE4621AC345B8C1B1B", {0}}, 979 | {"198.46.138.44", 33445, "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67", {0}}, 980 | {"51.15.37.145", 33445, "6FC41E2BD381D37E9748FC0E0328CE086AF9598BECC8FEB7DDF2E440475F300E", {0}}, 981 | {"130.133.110.14", 33445, "461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F", {0}}, 982 | {"205.185.116.116", 33445, "A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702", {0}}, 983 | {"198.98.51.198", 33445, "1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F", {0}}, 984 | {"108.61.165.198", 33445, "8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832", {0}}, 985 | {"194.249.212.109", 33445, "3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B", {0}}, 986 | {"185.25.116.107", 33445, "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43", {0}}, 987 | {"5.189.176.217", 5190, "2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F", {0}}, 988 | {"217.182.143.254", 2306, "7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147", {0}}, 989 | {"104.223.122.15", 33445, "0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A", {0}}, 990 | {"tox.verdict.gg", 33445, "1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976", {0}}, 991 | {"d4rk4.ru", 1813, "53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039", {0}}, 992 | {"104.233.104.126", 33445, "EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414", {0}}, 993 | {"51.254.84.212", 33445, "AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D", {0}}, 994 | {"88.99.133.52", 33445, "2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211", {0}}, 995 | {"185.58.206.164", 33445, "24156472041E5F220D1FA11D9DF32F7AD697D59845701CDD7BE7D1785EB9DB39", {0}}, 996 | {"92.54.84.70", 33445, "5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802", {0}}, 997 | {"195.93.190.6", 33445, "FB4CE0DDEFEED45F26917053E5D24BDDA0FA0A3D83A672A9DA2375928B37023D", {0}}, 998 | {"tox.uplinklabs.net", 33445, "1A56EA3EDF5DF4C0AEABBF3C2E4E603890F87E983CAC8A0D532A335F2C6E3E1F", {0}}, 999 | {"toxnode.nek0.net", 33445, "20965721D32CE50C3E837DD75B33908B33037E6225110BFF209277AEAF3F9639", {0}}, 1000 | {"95.215.44.78", 33445, "672DBE27B4ADB9D5FB105A6BB648B2F8FDB89B3323486A7A21968316E012023C", {0}}, 1001 | {"163.172.136.118", 33445, "2C289F9F37C20D09DA83565588BF496FAB3764853FA38141817A72E3F18ACA0B", {0}}, 1002 | {"sorunome.de", 33445, "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46", {0}}, 1003 | {"37.97.185.116", 33445, "E59A0E71ADA20D35BD1B0957059D7EF7E7792B3D680AE25C6F4DBBA09114D165", {0}}, 1004 | {"193.124.186.205", 5228, "9906D65F2A4751068A59D30505C5FC8AE1A95E0843AE9372EAFA3BAB6AC16C2C", {0}}, 1005 | {"80.87.193.193", 33445, "B38255EE4B054924F6D79A5E6E5889EC94B6ADF6FE9906F97A3D01E3D083223A", {0}}, 1006 | {"initramfs.io", 33445, "3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25", {0}}, 1007 | {"hibiki.eve.moe", 33445, "D3EB45181B343C2C222A5BCF72B760638E15ED87904625AAD351C594EEFAE03E", {0}}, 1008 | {"tox.deadteam.org", 33445, "C7D284129E83877D63591F14B3F658D77FF9BA9BA7293AEB2BDFBFE1A803AF47", {0}}, 1009 | {"46.229.52.198", 33445, "813C8F4187833EF0655B10F7752141A352248462A567529A38B6BBF73E979307", {0}}, 1010 | {"node.tox.ngc.network", 33445, "A856243058D1DE633379508ADCAFCF944E40E1672FF402750EF712E30C42012A", {0}}, 1011 | {"144.217.86.39", 33445, "7E5668E0EE09E19F320AD47902419331FFEE147BB3606769CFBE921A2A2FD34C", {0}}, 1012 | {"185.14.30.213", 443, "2555763C8C460495B14157D234DD56B86300A2395554BCAE4621AC345B8C1B1B", {0}}, 1013 | {"77.37.160.178", 33440, "CE678DEAFA29182EFD1B0C5B9BC6999E5A20B50A1A6EC18B91C8EBB591712416", {0}}, 1014 | {"85.21.144.224", 33445, "8F738BBC8FA9394670BCAB146C67A507B9907C8E564E28C2B59BEBB2FF68711B", {0}}, 1015 | {"tox.natalenko.name", 33445, "1CB6EBFD9D85448FA70D3CAE1220B76BF6FCE911B46ACDCF88054C190589650B", {0}}, 1016 | {"37.187.122.30", 33445, "BEB71F97ED9C99C04B8489BB75579EB4DC6AB6F441B603D63533122F1858B51D", {0}}, 1017 | {"completelyunoriginal.moe", 33445, "FBC7DED0B0B662D81094D91CC312D6CDF12A7B16C7FFB93817143116B510C13E", {0}}, 1018 | {"136.243.141.187", 443, "6EE1FADE9F55CC7938234CC07C864081FC606D8FE7B751EDA217F268F1078A39", {0}}, 1019 | {"tox.abilinski.com", 33445, "0E9D7FEE2AA4B42A4C18FE81C038E32FFD8D907AAA7896F05AA76C8D31A20065", {0}}, 1020 | {"95.215.46.114", 33445, "5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23", {0}}, 1021 | {"51.15.54.207", 33445, "1E64DBA45EC810C0BF3A96327DC8A9D441AB262C14E57FCE11ECBCE355305239", {0}} 1022 | }; 1023 | // only nodes.tox.chat 1024 | DHT_node nodes3[] = 1025 | { 1026 | {"51.15.37.145", 33445, "6FC41E2BD381D37E9748FC0E0328CE086AF9598BECC8FEB7DDF2E440475F300E", {0}} 1027 | }; 1028 | 1029 | if (switch_nodelist_2 == 0) 1030 | { 1031 | dbg(9, "nodeslist:1\n"); 1032 | bootstap_nodes(tox, nodes1, (int)(sizeof(nodes1) / sizeof(DHT_node)), 1); 1033 | } 1034 | else if (switch_nodelist_2 == 2) 1035 | { 1036 | dbg(9, "nodeslist:3\n"); 1037 | bootstap_nodes(tox, nodes3, (int)(sizeof(nodes3) / sizeof(DHT_node)), 0); 1038 | } 1039 | else // (switch_nodelist_2 == 1) 1040 | { 1041 | dbg(9, "nodeslist:2\n"); 1042 | bootstap_nodes(tox, nodes2, (int)(sizeof(nodes2) / sizeof(DHT_node)), 1); 1043 | } 1044 | } 1045 | 1046 | 1047 | void reconnect(Tox *tox) 1048 | { 1049 | bootstrap(tox); 1050 | // -------- try to go online -------- 1051 | long long unsigned int cur_time = time(NULL); 1052 | uint8_t off = 1; 1053 | long long loop_counter = 0; 1054 | long long overall_loop_counter = 0; 1055 | 1056 | while (1) 1057 | { 1058 | tox_iterate(tox, NULL); 1059 | usleep(tox_iteration_interval(tox) * 1000); 1060 | 1061 | if (tox_self_get_connection_status(tox) && off) 1062 | { 1063 | dbg(2, "Reconnect: Tox online, took %llu seconds\n", time(NULL) - cur_time); 1064 | off = 0; 1065 | break; 1066 | } 1067 | 1068 | c_sleep(20); 1069 | loop_counter++; 1070 | overall_loop_counter++; 1071 | 1072 | if (overall_loop_counter > (100 * 20)) // 40 secs 1073 | { 1074 | dbg(2, "Reconnect: Giving up after %llu seconds\n", time(NULL) - cur_time); 1075 | break; 1076 | } 1077 | 1078 | if (loop_counter > (50 * 20)) 1079 | { 1080 | loop_counter = 0; 1081 | // if not yet online, bootstrap every 20 seconds 1082 | dbg(2, "Reconnect: Tox NOT online yet, bootstrapping again\n"); 1083 | bootstrap(tox); 1084 | } 1085 | } 1086 | 1087 | // -------- try to go online -------- 1088 | } 1089 | 1090 | 1091 | void check_online_status(Tox *tox) 1092 | { 1093 | if (my_connection_status == TOX_CONNECTION_NONE) 1094 | { 1095 | if ((get_unix_time() - my_last_offline_timestamp) > RECONNECT_AFTER_OFFLINE_SECONDS) 1096 | { 1097 | // we are offline for too long, try to reconnect 1098 | reconnect(tox); 1099 | } 1100 | } 1101 | } 1102 | 1103 | 1104 | // fill string with toxid in upper case hex. 1105 | // size of toxid_str needs to be: [TOX_ADDRESS_SIZE*2 + 1] !! 1106 | void get_my_toxid(Tox *tox, char *toxid_str) 1107 | { 1108 | uint8_t tox_id_bin[TOX_ADDRESS_SIZE]; 1109 | tox_self_get_address(tox, tox_id_bin); 1110 | char tox_id_hex_local[TOX_ADDRESS_SIZE * 2 + 1]; 1111 | sodium_bin2hex(tox_id_hex_local, sizeof(tox_id_hex_local), tox_id_bin, sizeof(tox_id_bin)); 1112 | 1113 | for (size_t i = 0; i < sizeof(tox_id_hex_local) - 1; i ++) 1114 | { 1115 | tox_id_hex_local[i] = toupper(tox_id_hex_local[i]); 1116 | } 1117 | 1118 | snprintf(toxid_str, (size_t)(TOX_ADDRESS_SIZE * 2 + 1), "%s", (const char *)tox_id_hex_local); 1119 | } 1120 | 1121 | void print_tox_id(Tox *tox) 1122 | { 1123 | char tox_id_hex[TOX_ADDRESS_SIZE * 2 + 1]; 1124 | get_my_toxid(tox, tox_id_hex); 1125 | 1126 | if (logfile) 1127 | { 1128 | dbg(2, "--MyToxID--:%s\n", tox_id_hex); 1129 | int fd = fileno(logfile); 1130 | fsync(fd); 1131 | } 1132 | } 1133 | 1134 | int is_friend_online(Tox *tox, uint32_t num) 1135 | { 1136 | int j = find_friend_in_friendlist(num); 1137 | 1138 | switch (Friends.list[j].connection_status) 1139 | { 1140 | case TOX_CONNECTION_NONE: 1141 | return 0; 1142 | break; 1143 | 1144 | case TOX_CONNECTION_TCP: 1145 | return 1; 1146 | break; 1147 | 1148 | case TOX_CONNECTION_UDP: 1149 | return 1; 1150 | break; 1151 | 1152 | default: 1153 | return 0; 1154 | break; 1155 | } 1156 | } 1157 | 1158 | static int find_friend_in_friendlist(uint32_t friendnum) 1159 | { 1160 | int i; 1161 | 1162 | for (i = 0; i <= Friends.max_idx; ++i) 1163 | { 1164 | if (Friends.list[i].num == friendnum) 1165 | { 1166 | return i; 1167 | } 1168 | } 1169 | 1170 | return -1; 1171 | } 1172 | 1173 | static void update_friend_last_online(uint32_t num, time_t timestamp) 1174 | { 1175 | int friendlistnum = find_friend_in_friendlist(num); 1176 | Friends.list[friendlistnum].last_online.last_on = timestamp; 1177 | Friends.list[friendlistnum].last_online.tm = *localtime((const time_t *)×tamp); 1178 | /* if the format changes make sure TIME_STR_SIZE is the correct size !! */ 1179 | strftime(Friends.list[friendlistnum].last_online.hour_min_str, TIME_STR_SIZE, global_timestamp_format, 1180 | &Friends.list[friendlistnum].last_online.tm); 1181 | } 1182 | 1183 | void send_file_to_friend_real(Tox *m, uint32_t num, const char *filename, int resume, uint8_t *fileid_resume) 1184 | { 1185 | // ------- hack to send file -------- 1186 | // ------- hack to send file -------- 1187 | const char *errmsg = NULL; 1188 | char path[MAX_STR_SIZE]; 1189 | snprintf(path, sizeof(path), "%s", filename); 1190 | dbg(2, "send_file_to_friend_real:path=%s\n", path); 1191 | FILE *file_to_send = fopen(path, "r"); 1192 | 1193 | if (file_to_send == NULL) 1194 | { 1195 | dbg(0, "send_file_to_friend_real:error opening file\n"); 1196 | return; 1197 | } 1198 | 1199 | off_t filesize = file_size(path); 1200 | 1201 | if (filesize == 0) 1202 | { 1203 | dbg(0, "send_file_to_friend_real:filesize 0\n"); 1204 | fclose(file_to_send); 1205 | return; 1206 | } 1207 | 1208 | char file_name[TOX_MAX_FILENAME_LENGTH]; 1209 | size_t namelen = get_file_name(file_name, sizeof(file_name), path); 1210 | TOX_ERR_FILE_SEND err; 1211 | char *o = calloc(1, (size_t)TOX_FILE_ID_LENGTH); 1212 | uint32_t filenum = -1; 1213 | 1214 | if (resume == 0) 1215 | { 1216 | dbg(9, "resume == 0\n"); 1217 | random_char(o, (int)TOX_FILE_ID_LENGTH); 1218 | filenum = tox_file_send(m, num, TOX_FILE_KIND_DATA, (uint64_t)filesize, (uint8_t *)o, 1219 | (uint8_t *)file_name, namelen, &err); 1220 | } 1221 | else 1222 | { 1223 | dbg(9, "resume == 1\n"); 1224 | filenum = tox_file_send(m, num, TOX_FILE_KIND_DATA, (uint64_t)filesize, fileid_resume, 1225 | (uint8_t *)file_name, namelen, &err); 1226 | } 1227 | 1228 | dbg(2, "send_file_to_friend:tox_file_send=%s filenum=%d\n", file_name, (int)filenum); 1229 | 1230 | if (err != TOX_ERR_FILE_SEND_OK) 1231 | { 1232 | dbg(0, "send_file_to_friend_real: ! TOX_ERR_FILE_SEND_OK\n"); 1233 | goto on_send_error; 1234 | } 1235 | 1236 | dbg(2, "send_file_to_friend_real(1):tox_file_send=%s filenum=%d\n", file_name, (int)filenum); 1237 | struct FileTransfer *ft = new_file_transfer(num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); 1238 | dbg(2, "send_file_to_friend_real(2):tox_file_send=%s filenum=%d\n", file_name, (int)filenum); 1239 | 1240 | if (!ft) 1241 | { 1242 | dbg(0, "send_file_to_friend_real:ft=NULL\n"); 1243 | err = TOX_ERR_FILE_SEND_TOO_MANY; 1244 | goto on_send_error; 1245 | } 1246 | 1247 | memcpy(ft->file_name, file_name, namelen + 1); 1248 | ft->file = file_to_send; 1249 | ft->file_size = filesize; 1250 | 1251 | if (resume == 0) 1252 | { 1253 | dbg(9, "resume == 0\n"); 1254 | memcpy(ft->file_id, o, (size_t)TOX_FILE_ID_LENGTH); 1255 | } 1256 | else 1257 | { 1258 | dbg(9, "resume == 1\n"); 1259 | memcpy(ft->file_id, fileid_resume, (size_t)TOX_FILE_ID_LENGTH); 1260 | } 1261 | 1262 | dbg(0, "send_file_to_friend_real:tox_file_get_file_id num=%d filenum=%d\n", (int)num, (int)filenum); 1263 | dbg(0, "send_file_to_friend_real:file_id_resume=%d ft->file_id=%d\n", (int)fileid_resume, (int)ft->file_id); 1264 | dbg(0, "send_file_to_friend_real:o=%d ft->file_id=%d\n", (int)o, (int)ft->file_id); 1265 | char file_id_str[TOX_FILE_ID_LENGTH * 2 + 1]; 1266 | bin_id_to_string_all((char *)ft->file_id, (size_t)TOX_FILE_ID_LENGTH, file_id_str, 1267 | (size_t)(TOX_FILE_ID_LENGTH * 2 + 1)); 1268 | dbg(2, "send_file_to_friend_real:file_id=%s\n", file_id_str); 1269 | bin_id_to_string_all((char *)fileid_resume, (size_t)TOX_FILE_ID_LENGTH, file_id_str, 1270 | (size_t)(TOX_FILE_ID_LENGTH * 2 + 1)); 1271 | dbg(2, "send_file_to_friend_real:fileid_resume=%s\n", file_id_str); 1272 | bin_id_to_string_all((char *)o, (size_t)TOX_FILE_ID_LENGTH, file_id_str, (size_t)(TOX_FILE_ID_LENGTH * 2 + 1)); 1273 | dbg(2, "send_file_to_friend_real:o=%s\n", file_id_str); 1274 | free(o); 1275 | o = NULL; 1276 | return; 1277 | on_send_error: 1278 | free(o); 1279 | o = NULL; 1280 | 1281 | switch (err) 1282 | { 1283 | case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: 1284 | errmsg = "File transfer failed: Invalid friend."; 1285 | break; 1286 | 1287 | case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED: 1288 | errmsg = "File transfer failed: Friend is offline."; 1289 | break; 1290 | 1291 | case TOX_ERR_FILE_SEND_NAME_TOO_LONG: 1292 | errmsg = "File transfer failed: Filename is too long."; 1293 | break; 1294 | 1295 | case TOX_ERR_FILE_SEND_TOO_MANY: 1296 | errmsg = "File transfer failed: Too many concurrent file transfers."; 1297 | break; 1298 | 1299 | default: 1300 | errmsg = "File transfer failed."; 1301 | break; 1302 | } 1303 | 1304 | dbg(0, "send_file_to_friend_real:ft error=%s\n", errmsg); 1305 | tox_file_control(m, num, filenum, TOX_FILE_CONTROL_CANCEL, NULL); 1306 | fclose(file_to_send); 1307 | // ------- hack to send file -------- 1308 | // ------- hack to send file -------- 1309 | } 1310 | 1311 | void resume_file_to_friend(Tox *m, uint32_t num, struct FileTransfer *ft) 1312 | { 1313 | char *file_name_incl_full_path = NULL; 1314 | int j = find_friend_in_friendlist(ft->friendnum); 1315 | 1316 | if (j > -1) 1317 | { 1318 | file_name_incl_full_path = malloc(300); 1319 | snprintf(file_name_incl_full_path, 299, "%s/%s", (const char *)Friends.list[j].worksubdir, ft->file_name); 1320 | dbg(2, "resume_file_to_friend:full path=%s\n", file_name_incl_full_path); 1321 | char file_id_str[TOX_FILE_ID_LENGTH * 2 + 1]; 1322 | bin_id_to_string_all((char *)ft->file_id, (size_t)TOX_FILE_ID_LENGTH, file_id_str, 1323 | (size_t)(TOX_FILE_ID_LENGTH * 2 + 1)); 1324 | dbg(2, "resume_file_to_friend:file_id=%d file_id_bin=%d\n", (int)file_id_str, (int)ft->file_id); 1325 | dbg(2, "resume_file_to_friend:file_id=%s\n", file_id_str); 1326 | dbg(2, "resume_file_to_friend:path=%s friendnum=%d filenum=%d\n", file_name_incl_full_path, (int)ft->friendnum, 1327 | (int)ft->filenum); 1328 | send_file_to_friend_real(m, ft->friendnum, file_name_incl_full_path, 1, ft->file_id); 1329 | } 1330 | else 1331 | { 1332 | dbg(0, "resume_file_to_friend:friend %d not found in friendlist\n", (int)ft->friendnum); 1333 | } 1334 | } 1335 | 1336 | void send_file_to_friend(Tox *m, uint32_t num, const char *filename) 1337 | { 1338 | send_file_to_friend_real(m, num, filename, 0, NULL); 1339 | } 1340 | 1341 | 1342 | int copy_file(const char *from, const char *to) 1343 | { 1344 | int fd_to, fd_from; 1345 | char buf[4096]; 1346 | ssize_t nread; 1347 | int saved_errno; 1348 | fd_from = open(from, O_RDONLY); 1349 | 1350 | if (fd_from < 0) 1351 | { 1352 | dbg(0, "copy_file:002\n"); 1353 | return -1; 1354 | } 1355 | 1356 | fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666); 1357 | 1358 | if (fd_to < 0) 1359 | { 1360 | dbg(0, "copy_file:003\n"); 1361 | goto out_error; 1362 | } 1363 | 1364 | while (nread = read(fd_from, buf, sizeof buf), nread > 0) 1365 | { 1366 | char *out_ptr = buf; 1367 | ssize_t nwritten; 1368 | 1369 | do 1370 | { 1371 | nwritten = write(fd_to, out_ptr, nread); 1372 | 1373 | if (nwritten >= 0) 1374 | { 1375 | nread -= nwritten; 1376 | out_ptr += nwritten; 1377 | } 1378 | else if (errno != EINTR) 1379 | { 1380 | dbg(0, "copy_file:004\n"); 1381 | goto out_error; 1382 | } 1383 | } 1384 | while (nread > 0); 1385 | } 1386 | 1387 | if (nread == 0) 1388 | { 1389 | if (close(fd_to) < 0) 1390 | { 1391 | fd_to = -1; 1392 | dbg(0, "copy_file:005\n"); 1393 | goto out_error; 1394 | } 1395 | 1396 | close(fd_from); 1397 | /* Success! */ 1398 | return 0; 1399 | } 1400 | 1401 | out_error: 1402 | saved_errno = errno; 1403 | close(fd_from); 1404 | 1405 | if (fd_to >= 0) 1406 | { 1407 | close(fd_to); 1408 | } 1409 | 1410 | dbg(0, "copy_file:009\n"); 1411 | errno = saved_errno; 1412 | return -1; 1413 | } 1414 | 1415 | 1416 | 1417 | char *copy_file_to_friend_subdir(int friendlistnum, const char *file_with_path, const char *filename) 1418 | { 1419 | } 1420 | 1421 | int have_resumed_fts_friend(uint32_t friendnum) 1422 | { 1423 | int j = find_friend_in_friendlist(friendnum); 1424 | 1425 | if (Friends.list[j].have_resumed_fts == 1) 1426 | { 1427 | return 1; 1428 | } 1429 | else 1430 | { 1431 | return 0; 1432 | } 1433 | } 1434 | 1435 | void send_file_to_all_friends(Tox *m, const char *file_with_path, const char *filename) 1436 | { 1437 | } 1438 | 1439 | void on_tox_friend_status(Tox *tox, uint32_t friend_number, TOX_USER_STATUS status, void *user_data) 1440 | { 1441 | dbg(2, "on_tox_friend_status:friendnum=%d status=%d\n", (int)friend_number, (int)status); 1442 | } 1443 | 1444 | void friendlist_onConnectionChange(Tox *m, uint32_t num, TOX_CONNECTION connection_status, void *user_data) 1445 | { 1446 | int friendlistnum = find_friend_in_friendlist(num); 1447 | dbg(2, "friendlist_onConnectionChange:*ENTER*:friendnum=%d %d\n", (int)num, (int)connection_status); 1448 | Friends.list[friendlistnum].connection_status = connection_status; 1449 | update_friend_last_online(num, get_unix_time()); 1450 | 1451 | if (is_friend_online(m, num) == 1) 1452 | { 1453 | dbg(0, "friend %d just got online\n", num); 1454 | resume_resumable_fts(m, num); 1455 | 1456 | if (avatar_send(m, num) == -1) 1457 | { 1458 | dbg(0, "avatar_send failed for friend %d\n", num); 1459 | } 1460 | } 1461 | else 1462 | { 1463 | dbg(0, "friend %d went *OFFLINE*\n", num); 1464 | // save all resumeable FTs 1465 | save_resumable_fts(m, num); 1466 | // friend went offline -> cancel all filetransfers 1467 | kill_all_file_transfers_friend(m, num); 1468 | 1469 | // friend went offline -> hang up on all calls 1470 | if (friend_to_send_video_to == num) 1471 | { 1472 | av_local_disconnect(mytox_av, num); 1473 | } 1474 | } 1475 | 1476 | dbg(2, "friendlist_onConnectionChange:*READY*:friendnum=%d %d\n", (int)num, (int)connection_status); 1477 | } 1478 | 1479 | void friendlist_onFriendAdded(Tox *m, uint32_t num, bool sort) 1480 | { 1481 | // dbg(9, "friendlist_onFriendAdded:001\n"); 1482 | if (Friends.max_idx == 0) 1483 | { 1484 | // dbg(9, "friendlist_onFriendAdded:001.a malloc 1 friend struct, max_id=%d, num=%d\n", (int)Friends.max_idx, (int)num); 1485 | Friends.list = malloc(sizeof(ToxicFriend)); 1486 | } 1487 | else 1488 | { 1489 | // dbg(9, "friendlist_onFriendAdded:001.b realloc %d friend struct, max_id=%d, num=%d\n", (int)(Friends.max_idx + 1), (int)Friends.max_idx, (int)num); 1490 | Friends.list = realloc(Friends.list, ((Friends.max_idx + 1) * sizeof(ToxicFriend))); 1491 | } 1492 | 1493 | // dbg(9, "friendlist_onFriendAdded:001.c set friend to all 0 values\n"); 1494 | memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend)); // fill friend with "0" bytes 1495 | // dbg(2, "friendlist_onFriendAdded:003:%d\n", (int)Friends.max_idx); 1496 | Friends.list[Friends.max_idx].num = num; 1497 | Friends.list[Friends.max_idx].active = true; 1498 | Friends.list[Friends.max_idx].connection_status = TOX_CONNECTION_NONE; 1499 | Friends.list[Friends.max_idx].status = TOX_USER_STATUS_NONE; 1500 | Friends.list[Friends.max_idx].waiting_for_answer = 0; 1501 | Friends.list[Friends.max_idx].auto_resend_start_time = 0; 1502 | Friends.list[Friends.max_idx].have_resumed_fts = 0; 1503 | TOX_ERR_FRIEND_GET_PUBLIC_KEY pkerr; 1504 | tox_friend_get_public_key(m, num, (uint8_t *) Friends.list[Friends.max_idx].pub_key, &pkerr); 1505 | 1506 | if (pkerr != TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK) 1507 | { 1508 | dbg(0, "tox_friend_get_public_key failed (error %d)\n", pkerr); 1509 | } 1510 | 1511 | bin_id_to_string(Friends.list[Friends.max_idx].pub_key, (size_t) TOX_ADDRESS_SIZE, 1512 | Friends.list[Friends.max_idx].pubkey_string, (size_t)(TOX_ADDRESS_SIZE * 2 + 1)); 1513 | // dbg(2, "friend pubkey=%s\n", Friends.list[Friends.max_idx].pubkey_string); 1514 | TOX_ERR_FRIEND_GET_LAST_ONLINE loerr; 1515 | time_t t = tox_friend_get_last_online(m, num, &loerr); 1516 | 1517 | if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK) 1518 | { 1519 | t = 0; 1520 | } 1521 | 1522 | update_friend_last_online(num, t); 1523 | Friends.max_idx++; 1524 | } 1525 | 1526 | // after you are finished call free_friendlist_nums ! 1527 | uint32_t *load_friendlist_nums(Tox *m) 1528 | { 1529 | size_t numfriends = tox_self_get_friend_list_size(m); 1530 | uint32_t *friend_list = malloc(numfriends * sizeof(uint32_t)); 1531 | tox_self_get_friend_list(m, friend_list); 1532 | return friend_list; 1533 | } 1534 | 1535 | void free_friendlist_nums(void *friend_list) 1536 | { 1537 | if (friend_list) 1538 | { 1539 | free(friend_list); 1540 | friend_list = NULL; 1541 | } 1542 | } 1543 | 1544 | static void load_friendlist(Tox *m) 1545 | { 1546 | size_t i; 1547 | // TODO 1548 | size_t numfriends = tox_self_get_friend_list_size(m); 1549 | uint32_t *friend_lookup_list = load_friendlist_nums(m); 1550 | 1551 | for (i = 0; i < numfriends; ++i) 1552 | { 1553 | friendlist_onFriendAdded(m, friend_lookup_list[i], false); 1554 | dbg(2, "loading friend num:%d pubkey=%s\n", (int)friend_lookup_list[i], 1555 | Friends.list[Friends.max_idx - 1].pubkey_string); 1556 | } 1557 | 1558 | free_friendlist_nums((void *) friend_lookup_list); 1559 | } 1560 | 1561 | 1562 | 1563 | 1564 | void close_file_transfer(Tox *m, struct FileTransfer *ft, int CTRL) 1565 | { 1566 | dbg(9, "close_file_transfer:001\n"); 1567 | 1568 | if (!ft) 1569 | { 1570 | return; 1571 | } 1572 | 1573 | if (ft->state == FILE_TRANSFER_INACTIVE) 1574 | { 1575 | return; 1576 | } 1577 | 1578 | if (ft->file) 1579 | { 1580 | fclose(ft->file); 1581 | } 1582 | 1583 | if (CTRL >= 0) 1584 | { 1585 | tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL); 1586 | } 1587 | 1588 | memset(ft, 0, sizeof(struct FileTransfer)); 1589 | ft->state = FILE_TRANSFER_INACTIVE; // == 0 1590 | } 1591 | 1592 | int has_reached_max_file_transfer_for_friend(uint32_t num) 1593 | { 1594 | int active_ft = 0; 1595 | int friendlistnum = find_friend_in_friendlist(num); 1596 | int i; 1597 | 1598 | for (i = 0; i < MAX_FILES; ++i) 1599 | { 1600 | struct FileTransfer *ft_send = &Friends.list[friendlistnum].file_sender[i]; 1601 | 1602 | if (ft_send->state != FILE_TRANSFER_INACTIVE) 1603 | { 1604 | if (ft_send->file_name != NULL) 1605 | { 1606 | active_ft++; 1607 | } 1608 | } 1609 | } 1610 | 1611 | if (active_ft < MAX_FILES) 1612 | { 1613 | return 0; 1614 | } 1615 | else 1616 | { 1617 | // have reached max filetransfers already 1618 | return 1; 1619 | } 1620 | } 1621 | 1622 | struct FileTransfer *get_file_transfer_from_filename_struct(int friendlistnum, const char *filename) 1623 | { 1624 | size_t i; 1625 | 1626 | for (i = 0; i < MAX_FILES; ++i) 1627 | { 1628 | struct FileTransfer *ft_send = &Friends.list[friendlistnum].file_sender[i]; 1629 | 1630 | if (ft_send->state != FILE_TRANSFER_INACTIVE) 1631 | { 1632 | if (ft_send->file_name != NULL) 1633 | { 1634 | if ((strlen(ft_send->file_name) > 0) && (filename != NULL) && (strlen(filename) > 0)) 1635 | { 1636 | if (strncmp((char *)ft_send->file_name, filename, strlen(ft_send->file_name)) == 0) 1637 | { 1638 | // dbg(9, "found ft by filename:%s\n", ft_send->file_name); 1639 | return ft_send; 1640 | } 1641 | } 1642 | } 1643 | } 1644 | } 1645 | 1646 | return NULL; 1647 | } 1648 | 1649 | 1650 | struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum) 1651 | { 1652 | size_t i; 1653 | int friendlistnum = find_friend_in_friendlist(friendnum); 1654 | 1655 | for (i = 0; i < MAX_FILES; ++i) 1656 | { 1657 | struct FileTransfer *ft_send = &Friends.list[friendlistnum].file_sender[i]; 1658 | 1659 | if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) 1660 | { 1661 | return ft_send; 1662 | } 1663 | 1664 | struct FileTransfer *ft_recv = &Friends.list[friendlistnum].file_receiver[i]; 1665 | 1666 | if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) 1667 | { 1668 | return ft_recv; 1669 | } 1670 | } 1671 | 1672 | return NULL; 1673 | } 1674 | 1675 | // 1676 | // cut message at 999 chars length !! 1677 | // 1678 | void send_text_message_to_friend(Tox *tox, uint32_t friend_number, const char *fmt, ...) 1679 | { 1680 | char msg2[1000]; 1681 | size_t length = 0; 1682 | 1683 | if (fmt == NULL) 1684 | { 1685 | dbg(9, "send_text_message_to_friend:no message to send\n"); 1686 | return; 1687 | } 1688 | 1689 | va_list ap; 1690 | va_start(ap, fmt); 1691 | vsnprintf(msg2, 999, fmt, ap); 1692 | va_end(ap); 1693 | length = (size_t)strlen(msg2); 1694 | tox_friend_send_message(tox, friend_number, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *)msg2, length, NULL); 1695 | } 1696 | 1697 | void change_nospam_to_new_random_value(Tox *tox) 1698 | { 1699 | dbg(9, "change_nospam_to_new_random_value\n"); 1700 | global_last_change_nospam_ts = (uint32_t)get_unix_time(); 1701 | tox_self_set_nospam(tox, generate_random_uint32()); 1702 | update_savedata_file(tox); 1703 | print_tox_id(tox); 1704 | } 1705 | 1706 | 1707 | void friend_request_cb(Tox *tox, const uint8_t *public_key, const uint8_t *message, size_t length, 1708 | void *user_data) 1709 | { 1710 | uint32_t friendnum = tox_friend_add_norequest(tox, public_key, NULL); 1711 | dbg(2, "add friend:002:friendnum=%d max_id=%d\n", friendnum, (int)Friends.max_idx); 1712 | friendlist_onFriendAdded(tox, friendnum, 0); 1713 | update_savedata_file(tox); 1714 | } 1715 | 1716 | /* ssssshhh I stole this from ToxBot, don't tell anyone.. */ 1717 | /* ssssshhh and I stole this from EchoBot, don't tell anyone.. */ 1718 | static void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) 1719 | { 1720 | long unsigned int minutes = (secs % 3600) / 60; 1721 | long unsigned int hours = (secs / 3600) % 24; 1722 | long unsigned int days = (secs / 3600) / 24; 1723 | snprintf(buf, bufsize, "%lud %luh %lum", days, hours, minutes); 1724 | } 1725 | 1726 | // 1727 | // lastline param ignored for now!! 1728 | // 1729 | void run_cmd_return_output(const char *command, char *output, int lastline) 1730 | { 1731 | FILE *fp = NULL; 1732 | char path[1035]; 1733 | char *pos = NULL; 1734 | 1735 | if (!output) 1736 | { 1737 | return; 1738 | } 1739 | 1740 | /* Open the command for reading. */ 1741 | fp = popen(command, "r"); 1742 | 1743 | if (fp == NULL) 1744 | { 1745 | dbg(0, "Failed to run command: %s errno=%d error=%s\n", command, errno, strerror(errno)); 1746 | output[0] = '\0'; 1747 | return; 1748 | } 1749 | 1750 | /* Read the output a line at a time - output it. */ 1751 | while (fgets(path, sizeof(path) - 1, fp) != NULL) 1752 | { 1753 | snprintf(output, 299, "%s", (const char *)path); 1754 | } 1755 | 1756 | if (strlen(output) > 1) 1757 | { 1758 | if ((pos = strchr(output, '\n')) != NULL) 1759 | { 1760 | *pos = '\0'; 1761 | } 1762 | } 1763 | 1764 | /* close */ 1765 | pclose(fp); 1766 | } 1767 | 1768 | void remove_friend(Tox *tox, uint32_t friend_number) 1769 | { 1770 | TOX_ERR_FRIEND_DELETE error; 1771 | #ifdef TOX_HAVE_TOXUTIL 1772 | tox_utils_friend_delete(tox, friend_number, &error); 1773 | #else 1774 | tox_friend_delete(tox, friend_number, &error); 1775 | #endif 1776 | } 1777 | 1778 | void cmd_delfriend(Tox *tox, uint32_t friend_number, const char *message) 1779 | { 1780 | uint32_t del_friend_number = -1; 1781 | 1782 | if (friend_number != del_friend_number) 1783 | { 1784 | // remove_friend(tox, del_friend_number); 1785 | } 1786 | } 1787 | 1788 | void cmd_stats(Tox *tox, uint32_t friend_number) 1789 | { 1790 | switch (my_connection_status) 1791 | { 1792 | case TOX_CONNECTION_NONE: 1793 | send_text_message_to_friend(tox, friend_number, "toxcam status:offline"); 1794 | break; 1795 | 1796 | case TOX_CONNECTION_TCP: 1797 | send_text_message_to_friend(tox, friend_number, "toxcam status:Online, using TCP"); 1798 | break; 1799 | 1800 | case TOX_CONNECTION_UDP: 1801 | send_text_message_to_friend(tox, friend_number, "toxcam status:Online, using UDP"); 1802 | break; 1803 | 1804 | default: 1805 | send_text_message_to_friend(tox, friend_number, "toxcam status:*unknown*"); 1806 | break; 1807 | } 1808 | 1809 | // ----- uptime ----- 1810 | char time_str[200]; 1811 | uint64_t cur_time = time(NULL); 1812 | get_elapsed_time_str(time_str, sizeof(time_str), cur_time - global_start_time); 1813 | send_text_message_to_friend(tox, friend_number, "Uptime: %s", time_str); 1814 | // ----- uptime ----- 1815 | char output_str[1000]; 1816 | run_cmd_return_output(shell_cmd__get_my_number_of_open_files, output_str, 1); 1817 | 1818 | if (strlen(output_str) > 0) 1819 | { 1820 | send_text_message_to_friend(tox, friend_number, "toxcam open files:%s", output_str); 1821 | } 1822 | else 1823 | { 1824 | send_text_message_to_friend(tox, friend_number, "ERROR getting open files"); 1825 | } 1826 | 1827 | // --- temp --- 1828 | run_cmd_return_output(shell_cmd__get_cpu_temp, output_str, 1); 1829 | 1830 | if (strlen(output_str) > 0) 1831 | { 1832 | send_text_message_to_friend(tox, friend_number, "toxcam Cpu temp:%s\xC2\xB0%s", output_str, "C"); 1833 | } 1834 | else 1835 | { 1836 | send_text_message_to_friend(tox, friend_number, "ERROR getting Cpu temp"); 1837 | } 1838 | 1839 | run_cmd_return_output(shell_cmd__get_gpu_temp, output_str, 1); 1840 | 1841 | if (strlen(output_str) > 0) 1842 | { 1843 | send_text_message_to_friend(tox, friend_number, "toxcam GPU temp:%s\xC2\xB0%s", output_str, "C"); 1844 | } 1845 | else 1846 | { 1847 | send_text_message_to_friend(tox, friend_number, "ERROR getting GPU temp"); 1848 | } 1849 | 1850 | // --- temp --- 1851 | char tox_id_hex[TOX_ADDRESS_SIZE * 2 + 1]; 1852 | get_my_toxid(tox, tox_id_hex); 1853 | send_text_message_to_friend(tox, friend_number, "tox:%s", tox_id_hex); 1854 | } 1855 | 1856 | void cmd_kamft(Tox *tox, uint32_t friend_number) 1857 | { 1858 | send_text_message_to_friend(tox, friend_number, "killing all filetransfers to you ..."); 1859 | kill_all_file_transfers_friend(tox, friend_number); 1860 | } 1861 | 1862 | void cmd_snap(Tox *tox, uint32_t friend_number) 1863 | { 1864 | send_text_message_to_friend(tox, friend_number, "*Feature DISABLED*"); 1865 | 1866 | if (1 == 1 + 1) 1867 | { 1868 | send_text_message_to_friend(tox, friend_number, "capture single shot, and send to all friends ..."); 1869 | char output_str[1000]; 1870 | run_cmd_return_output(shell_cmd__single_shot, output_str, 1); 1871 | #if 0 1872 | 1873 | if (strlen(output_str) > 0) 1874 | { 1875 | // send_text_message_to_friend(tox, friend_number, "toxcam:%s", output_str); 1876 | } 1877 | else 1878 | { 1879 | send_text_message_to_friend(tox, friend_number, "ERROR running snap command"); 1880 | } 1881 | 1882 | #endif 1883 | send_text_message_to_friend(tox, friend_number, "... capture single shot, ready!"); 1884 | } 1885 | } 1886 | 1887 | void cmd_friends(Tox *tox, uint32_t friend_number) 1888 | { 1889 | size_t i; 1890 | // TODO 1891 | size_t numfriends = tox_self_get_friend_list_size(tox); 1892 | int j = -1; 1893 | 1894 | for (i = 0; i < numfriends; ++i) 1895 | { 1896 | j = find_friend_in_friendlist((uint32_t) i); 1897 | 1898 | if (j > -1) 1899 | { 1900 | send_text_message_to_friend(tox, friend_number, "%d:friend", j); 1901 | send_text_message_to_friend(tox, friend_number, "%d tox:%s", j, (const char *)Friends.list[j].pubkey_string); 1902 | send_text_message_to_friend(tox, friend_number, "%d:last online (in client local time):%s", j, 1903 | (const char *)Friends.list[j].last_online.hour_min_str); 1904 | 1905 | switch (Friends.list[j].connection_status) 1906 | { 1907 | case TOX_CONNECTION_NONE: 1908 | send_text_message_to_friend(tox, friend_number, "%d:%s", j, "status:offline"); 1909 | break; 1910 | 1911 | case TOX_CONNECTION_TCP: 1912 | send_text_message_to_friend(tox, friend_number, "%d:%s", j, "status:Online, using TCP"); 1913 | break; 1914 | 1915 | case TOX_CONNECTION_UDP: 1916 | send_text_message_to_friend(tox, friend_number, "%d:%s", j, "status:Online, using UDP"); 1917 | break; 1918 | 1919 | default: 1920 | send_text_message_to_friend(tox, friend_number, "%d:%s", j, "status:*unknown*"); 1921 | break; 1922 | } 1923 | } 1924 | } 1925 | } 1926 | 1927 | void cmd_restart(Tox *tox, uint32_t friend_number) 1928 | { 1929 | send_text_message_to_friend(tox, friend_number, "toxcam services will restart ..."); 1930 | global_want_restart = 1; 1931 | } 1932 | 1933 | 1934 | void cmd_vcm(Tox *tox, uint32_t friend_number) 1935 | { 1936 | // send_text_message_to_friend(tox, friend_number, "video-call-me not yet implemented!"); 1937 | dbg(9, "cmd_vcm:001\n"); 1938 | 1939 | if (global_video_active == 1) 1940 | { 1941 | send_text_message_to_friend(tox, friend_number, "there is already a video session active"); 1942 | dbg(9, "fnum=%d msg=there is already a video session active\n", (int)friend_number); 1943 | } 1944 | else 1945 | { 1946 | send_text_message_to_friend(tox, friend_number, "i am trying to send my video ..."); 1947 | dbg(9, "fnum=%d msg=i am trying to send my video ...\n", (int)friend_number); 1948 | 1949 | if (mytox_av != NULL) 1950 | { 1951 | dbg(9, "cmd_vcm:003\n"); 1952 | global_video_bit_rate = DEFAULT_GLOBAL_VID_BITRATE; 1953 | friend_to_send_video_to = friend_number; 1954 | dbg(9, "cmd_vcm:004\n"); 1955 | TOXAV_ERR_CALL error = 0; 1956 | toxav_call(mytox_av, friend_number, global_audio_bit_rate, global_video_bit_rate, &error); 1957 | // toxav_call(mytox_av, friend_number, 0, 40, &error); 1958 | dbg(9, "cmd_vcm:005\n"); 1959 | 1960 | if (error != TOXAV_ERR_CALL_OK) 1961 | { 1962 | switch (error) 1963 | { 1964 | case TOXAV_ERR_CALL_MALLOC: 1965 | dbg(0, "toxav_call (1):TOXAV_ERR_CALL_MALLOC\n"); 1966 | break; 1967 | 1968 | case TOXAV_ERR_CALL_SYNC: 1969 | dbg(0, "toxav_call (1):TOXAV_ERR_CALL_SYNC\n"); 1970 | break; 1971 | 1972 | case TOXAV_ERR_CALL_FRIEND_NOT_FOUND: 1973 | dbg(0, "toxav_call (1):TOXAV_ERR_CALL_FRIEND_NOT_FOUND\n"); 1974 | break; 1975 | 1976 | case TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED: 1977 | dbg(0, "toxav_call (1):TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED\n"); 1978 | break; 1979 | 1980 | case TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL: 1981 | dbg(0, "toxav_call (1):TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL\n"); 1982 | break; 1983 | 1984 | case TOXAV_ERR_CALL_INVALID_BIT_RATE: 1985 | dbg(0, "toxav_call (1):TOXAV_ERR_CALL_INVALID_BIT_RATE\n"); 1986 | break; 1987 | 1988 | default: 1989 | dbg(0, "toxav_call (1):*unknown error*\n"); 1990 | break; 1991 | } 1992 | } 1993 | } 1994 | else 1995 | { 1996 | dbg(9, "cmd_vcm:006\n"); 1997 | send_text_message_to_friend(tox, friend_number, "sending video failed:toxav==NULL"); 1998 | } 1999 | } 2000 | 2001 | dbg(9, "cmd_vcm:099\n"); 2002 | } 2003 | 2004 | void send_help_to_friend(Tox *tox, uint32_t friend_number) 2005 | { 2006 | send_text_message_to_friend(tox, friend_number, 2007 | "=========================\nToxCam version:%s\n=========================", global_version_string); 2008 | // send_text_message_to_friend(tox, friend_number, " commands are:"); 2009 | // send_text_message_to_friend(tox, friend_number, " .help --> show this help"); 2010 | send_text_message_to_friend(tox, friend_number, " .stats --> show ToxCam status"); 2011 | send_text_message_to_friend(tox, friend_number, " .friends --> show ToxCam Friends"); 2012 | send_text_message_to_friend(tox, friend_number, " .snap --> snap a single still image"); 2013 | send_text_message_to_friend(tox, friend_number, " .restart --> restart ToxCam system"); 2014 | send_text_message_to_friend(tox, friend_number, " .vcm --> videocall me"); 2015 | send_text_message_to_friend(tox, friend_number, " .fps --> set delay in ms between sent frames"); 2016 | } 2017 | 2018 | //void start_zipfile(mz_zip_archive *pZip, size_t size_pZip, const char* zip_file_full_path) 2019 | //{ 2020 | //} 2021 | //void add_file_to_zipfile(mz_zip_archive *pZip, const char* file_to_add_full_path, const char* filename_in_zipfile) 2022 | //{ 2023 | //} 2024 | //void finish_zipfile(mz_zip_archive *pZip) 2025 | //{ 2026 | //} 2027 | 2028 | void friend_message_cb(Tox *tox, uint32_t friend_number, TOX_MESSAGE_TYPE type, const uint8_t *message, 2029 | size_t length, void *user_data) 2030 | { 2031 | int j; 2032 | int send_back = 0; 2033 | 2034 | if (type == TOX_MESSAGE_TYPE_NORMAL) 2035 | { 2036 | if (message != NULL) 2037 | { 2038 | j = find_friend_in_friendlist(friend_number); 2039 | dbg(2, "message from friend:%d msg:%s\n", (int)friend_number, (char *)message); 2040 | 2041 | if (strncmp((char *)message, ".help", strlen((char *)".help")) == 0) 2042 | { 2043 | send_help_to_friend(tox, friend_number); 2044 | } 2045 | else if (strncmp((char *)message, ".stats", strlen((char *)".stats")) == 0) 2046 | { 2047 | cmd_stats(tox, friend_number); 2048 | } 2049 | else if (strncmp((char *)message, ".friends", strlen((char *)".friends")) == 0) 2050 | { 2051 | cmd_friends(tox, friend_number); 2052 | } 2053 | else if (strncmp((char *)message, ".snap", strlen((char *)".snap")) == 0) 2054 | { 2055 | cmd_snap(tox, friend_number); 2056 | } 2057 | else if (strncmp((char *)message, ".restart", strlen((char *)".restart")) == 0) // restart toxcam processes (no reboot) 2058 | { 2059 | cmd_restart(tox, friend_number); 2060 | } 2061 | else if (strncmp((char *)message, ".vcm", strlen((char *)".vcm")) == 0) // video call me! 2062 | { 2063 | cmd_vcm(tox, friend_number); 2064 | } 2065 | else if (strncmp((char *)message, ".fps ", strlen((char *)".fps ")) == 0) // set 1000/fps 2066 | { 2067 | if (strlen(message) > 5) 2068 | { 2069 | int num_new = get_number_in_string(message, (int)DEFAULT_FPS_SLEEP_MS); 2070 | 2071 | if ((num_new >= 1) && (num_new <= 1000)) 2072 | { 2073 | DEFAULT_FPS_SLEEP_MS = num_new; 2074 | dbg(9, "setting wait in ms: %d\n", (int)DEFAULT_FPS_SLEEP_MS); 2075 | } 2076 | } 2077 | } 2078 | else 2079 | { 2080 | if (Friends.list[j].waiting_for_answer == 1) 2081 | { 2082 | // we want to get user feedback 2083 | snprintf(Friends.list[j].last_answer, 99, (char *)message); 2084 | Friends.list[j].waiting_for_answer = 2; 2085 | 2086 | if (Friends.list[j].last_answer) 2087 | { 2088 | dbg(2, "got answer from friend:%d answer:%s\n", (int)friend_number, Friends.list[j].last_answer); 2089 | } 2090 | else 2091 | { 2092 | dbg(2, "got answer from friend:%d answer:NULL\n", (int)friend_number); 2093 | } 2094 | } 2095 | else 2096 | { 2097 | // send_back = 1; 2098 | // unknown command, just send "help / usage" 2099 | send_help_to_friend(tox, friend_number); 2100 | } 2101 | } 2102 | } 2103 | else 2104 | { 2105 | dbg(2, "message from friend:%d msg:NULL\n", (int)friend_number); 2106 | } 2107 | } 2108 | else 2109 | { 2110 | dbg(2, "message from friend:%d\n", (int)friend_number); 2111 | } 2112 | 2113 | if (send_back == 1) 2114 | { 2115 | tox_friend_send_message(tox, friend_number, type, message, length, NULL); 2116 | } 2117 | } 2118 | 2119 | 2120 | 2121 | void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, 2122 | const uint8_t *data, size_t length, void *user_data) 2123 | { 2124 | struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); 2125 | 2126 | if (!ft) 2127 | { 2128 | return; 2129 | } 2130 | } 2131 | 2132 | 2133 | void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size, 2134 | const uint8_t *filename, size_t filename_length, void *userdata) 2135 | { 2136 | /* We don't care about receiving avatars */ 2137 | if (kind != TOX_FILE_KIND_DATA) 2138 | { 2139 | tox_file_control(m, friendnumber, filenumber, TOX_FILE_CONTROL_CANCEL, NULL); 2140 | dbg(9, "on_file_recv:002:cancel incoming avatar\n"); 2141 | return; 2142 | } 2143 | else 2144 | { 2145 | // cancel all filetransfers. we don't want to receive files 2146 | tox_file_control(m, friendnumber, filenumber, TOX_FILE_CONTROL_CANCEL, NULL); 2147 | dbg(9, "on_file_recv:003:cancel incoming file\n"); 2148 | return; 2149 | } 2150 | } 2151 | 2152 | void save_resumable_fts(Tox *m, uint32_t friendnum) 2153 | { 2154 | size_t i; 2155 | int friendlistnum = find_friend_in_friendlist(friendnum); 2156 | 2157 | for (i = 0; i < MAX_FILES; ++i) 2158 | { 2159 | // for now save only sending FTs 2160 | struct FileTransfer *ft = &Friends.list[friendlistnum].file_sender[i]; 2161 | 2162 | if (ft->state != FILE_TRANSFER_INACTIVE) 2163 | { 2164 | dbg(9, "save_resumable_fts:saving sender FT i=%d ftnum=%d for friendnum:#%d pos=%d filesize=%d\n", i, (int)ft->filenum, 2165 | (int)friendnum, (int)ft->position, (int)ft->file_size); 2166 | ll_push(&resumable_filetransfers, sizeof(struct FileTransfer), ft); 2167 | dbg(9, "save_resumable_fts:pushed struct=%p\n", resumable_filetransfers->val); 2168 | } 2169 | } 2170 | 2171 | Friends.list[friendlistnum].have_resumed_fts = 0; 2172 | } 2173 | 2174 | 2175 | 2176 | void resume_resumable_fts(Tox *m, uint32_t friendnum) 2177 | { 2178 | dbg(9, "resume_resumable_fts:001\n"); 2179 | ll_node_t *saved_ft_list = resumable_filetransfers; 2180 | int i = 0; 2181 | 2182 | while (saved_ft_list != NULL) 2183 | { 2184 | dbg(9, "resume_resumable_fts:element #%d=%p\n", i, saved_ft_list->val); 2185 | 2186 | if (saved_ft_list->val != NULL) 2187 | { 2188 | struct FileTransfer *ft = (struct FileTransfer *)saved_ft_list->val; 2189 | 2190 | if (ft->friendnum == friendnum) 2191 | { 2192 | dbg(9, "resume_resumable_fts:**found element #%d=%p\n", i, saved_ft_list->val); 2193 | resume_file_to_friend(m, ft->filenum, ft); 2194 | // now remove element, and start loop again 2195 | ll_remove_by_index(&resumable_filetransfers, i); 2196 | saved_ft_list = resumable_filetransfers; 2197 | i = 0; 2198 | continue; 2199 | } 2200 | } 2201 | 2202 | i++; 2203 | saved_ft_list = saved_ft_list->next; 2204 | } 2205 | 2206 | int j = find_friend_in_friendlist(friendnum); 2207 | 2208 | if (j > -1) 2209 | { 2210 | Friends.list[j].have_resumed_fts = 1; 2211 | } 2212 | } 2213 | 2214 | 2215 | 2216 | void on_file_chunk_request(Tox *tox, uint32_t friendnumber, uint32_t filenumber, uint64_t position, 2217 | size_t length, void *userdata) 2218 | { 2219 | // dbg(9, "on_file_chunk_request:001:friendnum=%d filenum=%d position=%ld len=%d\n", (int)friendnumber, (int)filenumber, (long)position, (int)length); 2220 | struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); 2221 | 2222 | if (!ft) 2223 | { 2224 | dbg(0, "on_file_chunk_request:003 ft=NULL\n"); 2225 | return; 2226 | } 2227 | 2228 | if (ft->file_type == TOX_FILE_KIND_AVATAR) 2229 | { 2230 | on_avatar_chunk_request(tox, ft, position, length); 2231 | return; 2232 | } 2233 | 2234 | if (ft->state != FILE_TRANSFER_STARTED) 2235 | { 2236 | dbg(0, "on_file_chunk_request:005 !FILE_TRANSFER_STARTED\n"); 2237 | return; 2238 | } 2239 | 2240 | if (length == 0) 2241 | { 2242 | dbg(2, "File '%s' successfully sent, ft->state=%d\n", ft->file_name, (int)ft->state); 2243 | char origname[300]; 2244 | snprintf(origname, 299, "%s", (const char *)ft->file_name); 2245 | close_file_transfer(tox, ft, -1); 2246 | // also remove the file from disk 2247 | int friendlist_num = find_friend_in_friendlist(friendnumber); 2248 | char longname[300]; 2249 | snprintf(longname, 299, "%s/%s", (const char *)Friends.list[friendlist_num].worksubdir, origname); 2250 | dbg(2, "delete file %s\n", longname); 2251 | unlink(longname); 2252 | return; 2253 | } 2254 | 2255 | if (ft->file == NULL) 2256 | { 2257 | dbg(0, "File transfer for '%s' failed: Null file pointer\n", ft->file_name); 2258 | close_file_transfer(tox, ft, TOX_FILE_CONTROL_CANCEL); 2259 | return; 2260 | } 2261 | 2262 | if (ft->position != position) 2263 | { 2264 | if (fseek(ft->file, position, SEEK_SET) == -1) 2265 | { 2266 | dbg(0, "File transfer for '%s' failed: Seek fail\n", ft->file_name); 2267 | close_file_transfer(tox, ft, TOX_FILE_CONTROL_CANCEL); 2268 | return; 2269 | } 2270 | 2271 | ft->position = position; 2272 | } 2273 | 2274 | uint8_t send_data[length]; 2275 | size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file); 2276 | 2277 | if (send_length != length) 2278 | { 2279 | dbg(0, "File transfer for '%s' failed: Read fail\n", ft->file_name); 2280 | close_file_transfer(tox, ft, TOX_FILE_CONTROL_CANCEL); 2281 | return; 2282 | } 2283 | 2284 | TOX_ERR_FILE_SEND_CHUNK err; 2285 | tox_file_send_chunk(tox, friendnumber, filenumber, position, send_data, send_length, &err); 2286 | 2287 | if (err != TOX_ERR_FILE_SEND_CHUNK_OK) 2288 | { 2289 | dbg(0, "tox_file_send_chunk failed in chat callback (error %d)\n", err); 2290 | } 2291 | 2292 | ft->position += send_length; 2293 | ft->bps += send_length; 2294 | ft->last_keep_alive = get_unix_time(); 2295 | } 2296 | 2297 | 2298 | void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control) 2299 | { 2300 | switch (control) 2301 | { 2302 | case TOX_FILE_CONTROL_RESUME: 2303 | if (ft->state == FILE_TRANSFER_PENDING) 2304 | { 2305 | ft->state = FILE_TRANSFER_STARTED; 2306 | } 2307 | else if (ft->state == FILE_TRANSFER_PAUSED) 2308 | { 2309 | ft->state = FILE_TRANSFER_STARTED; 2310 | } 2311 | 2312 | break; 2313 | 2314 | case TOX_FILE_CONTROL_PAUSE: 2315 | ft->state = FILE_TRANSFER_PAUSED; 2316 | break; 2317 | 2318 | case TOX_FILE_CONTROL_CANCEL: 2319 | close_file_transfer(m, ft, -1); 2320 | break; 2321 | } 2322 | } 2323 | 2324 | 2325 | void on_file_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, 2326 | void *userdata) 2327 | { 2328 | struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); 2329 | 2330 | if (!ft) 2331 | { 2332 | return; 2333 | } 2334 | 2335 | if (ft->file_type == TOX_FILE_KIND_AVATAR) 2336 | { 2337 | on_avatar_file_control(m, ft, control); 2338 | return; 2339 | } 2340 | 2341 | dbg(9, "on_file_control:002:file in/out\n"); 2342 | 2343 | switch (control) 2344 | { 2345 | case TOX_FILE_CONTROL_RESUME: 2346 | { 2347 | dbg(9, "on_file_control:003:TOX_FILE_CONTROL_RESUME\n"); 2348 | ft->last_keep_alive = get_unix_time(); 2349 | 2350 | /* transfer is accepted */ 2351 | if (ft->state == FILE_TRANSFER_PENDING) 2352 | { 2353 | ft->state = FILE_TRANSFER_STARTED; 2354 | dbg(9, "on_file_control:004:pending -> started\n"); 2355 | } 2356 | else if (ft->state == FILE_TRANSFER_PAUSED) 2357 | { 2358 | /* transfer is resumed */ 2359 | ft->state = FILE_TRANSFER_STARTED; 2360 | dbg(9, "on_file_control:005:paused -> started\n"); 2361 | } 2362 | 2363 | break; 2364 | } 2365 | 2366 | case TOX_FILE_CONTROL_PAUSE: 2367 | { 2368 | dbg(9, "on_file_control:006:TOX_FILE_CONTROL_PAUSE\n"); 2369 | ft->state = FILE_TRANSFER_PAUSED; 2370 | break; 2371 | } 2372 | 2373 | case TOX_FILE_CONTROL_CANCEL: 2374 | { 2375 | dbg(1, "File transfer for '%s' was aborted\n", ft->file_name); 2376 | close_file_transfer(m, ft, -1); 2377 | break; 2378 | } 2379 | } 2380 | } 2381 | 2382 | 2383 | 2384 | void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length) 2385 | { 2386 | dbg(9, "on_avatar_chunk_request:001\n"); 2387 | 2388 | if (ft->state != FILE_TRANSFER_STARTED) 2389 | { 2390 | dbg(0, "on_avatar_chunk_request:001a:!FILE_TRANSFER_STARTED\n"); 2391 | return; 2392 | } 2393 | 2394 | if (length == 0) 2395 | { 2396 | close_file_transfer(m, ft, -1); 2397 | return; 2398 | } 2399 | 2400 | if (ft->file == NULL) 2401 | { 2402 | close_file_transfer(m, ft, TOX_FILE_CONTROL_CANCEL); 2403 | return; 2404 | } 2405 | 2406 | if (ft->position != position) 2407 | { 2408 | if (fseek(ft->file, position, SEEK_SET) == -1) 2409 | { 2410 | close_file_transfer(m, ft, TOX_FILE_CONTROL_CANCEL); 2411 | return; 2412 | } 2413 | 2414 | ft->position = position; 2415 | } 2416 | 2417 | uint8_t send_data[length]; 2418 | size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file); 2419 | 2420 | if (send_length != length) 2421 | { 2422 | close_file_transfer(m, ft, TOX_FILE_CONTROL_CANCEL); 2423 | return; 2424 | } 2425 | 2426 | TOX_ERR_FILE_SEND_CHUNK err; 2427 | tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err); 2428 | 2429 | if (err != TOX_ERR_FILE_SEND_CHUNK_OK) 2430 | { 2431 | dbg(0, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); 2432 | } 2433 | 2434 | ft->position += send_length; 2435 | ft->last_keep_alive = get_unix_time(); 2436 | } 2437 | 2438 | 2439 | void self_connection_status_cb(Tox *tox, TOX_CONNECTION connection_status, void *user_data) 2440 | { 2441 | switch (connection_status) 2442 | { 2443 | case TOX_CONNECTION_NONE: 2444 | dbg(2, "Offline\n"); 2445 | my_connection_status = TOX_CONNECTION_NONE; 2446 | my_last_offline_timestamp = get_unix_time(); 2447 | break; 2448 | 2449 | case TOX_CONNECTION_TCP: 2450 | dbg(2, "Online, using TCP\n"); 2451 | my_connection_status = TOX_CONNECTION_TCP; 2452 | my_last_online_timestamp = get_unix_time(); 2453 | break; 2454 | 2455 | case TOX_CONNECTION_UDP: 2456 | dbg(2, "Online, using UDP\n"); 2457 | my_connection_status = TOX_CONNECTION_UDP; 2458 | my_last_online_timestamp = get_unix_time(); 2459 | break; 2460 | } 2461 | } 2462 | 2463 | 2464 | static struct FileTransfer *new_file_sender(uint32_t friendnum, uint32_t filenum, uint8_t type) 2465 | { 2466 | size_t i; 2467 | dbg(9, "new_file_sender:001 friendnum=%d filenum=%d type=%d\n", (int)friendnum, (int) filenum, (int) type); 2468 | int friendlistnum = find_friend_in_friendlist(friendnum); 2469 | 2470 | for (i = 0; i < MAX_FILES; ++i) 2471 | { 2472 | struct FileTransfer *ft = &Friends.list[friendlistnum].file_sender[i]; 2473 | dbg(9, "new_file_sender:002 i=%d\n", (int)i); 2474 | 2475 | if (ft->state == FILE_TRANSFER_INACTIVE) 2476 | { 2477 | dbg(9, "new_file_sender:003:reusing sender i=%d\n", (int)i); 2478 | memset(ft, 0, sizeof(struct FileTransfer)); 2479 | // ft->state = FILE_TRANSFER_INACTIVE; // == 0 2480 | ft->index = i; 2481 | ft->friendnum = friendnum; 2482 | ft->filenum = filenum; 2483 | ft->file_type = type; 2484 | ft->last_keep_alive = get_unix_time(); 2485 | ft->state = FILE_TRANSFER_PENDING; 2486 | ft->direction = FILE_TRANSFER_SEND; 2487 | dbg(9, "new_file_sender:003 i=%d\n", (int)i); 2488 | return ft; 2489 | } 2490 | } 2491 | 2492 | return NULL; 2493 | } 2494 | 2495 | 2496 | 2497 | static struct FileTransfer *new_file_receiver(uint32_t friendnum, uint32_t filenum, uint8_t type) 2498 | { 2499 | size_t i; 2500 | int friendlistnum = find_friend_in_friendlist(friendnum); 2501 | 2502 | for (i = 0; i < MAX_FILES; ++i) 2503 | { 2504 | struct FileTransfer *ft = &Friends.list[friendlistnum].file_receiver[i]; 2505 | 2506 | if (ft->state == FILE_TRANSFER_INACTIVE) 2507 | { 2508 | memset(ft, 0, sizeof(struct FileTransfer)); 2509 | // ft->state = FILE_TRANSFER_INACTIVE; // == 0 2510 | ft->index = i; 2511 | ft->friendnum = friendnum; 2512 | ft->filenum = filenum; 2513 | ft->file_type = type; 2514 | ft->last_keep_alive = get_unix_time(); 2515 | ft->state = FILE_TRANSFER_PENDING; 2516 | ft->direction = FILE_TRANSFER_RECV; 2517 | return ft; 2518 | } 2519 | } 2520 | 2521 | return NULL; 2522 | } 2523 | 2524 | 2525 | struct FileTransfer *new_file_transfer(uint32_t friendnum, uint32_t filenum, 2526 | FILE_TRANSFER_DIRECTION direction, uint8_t type) 2527 | { 2528 | if (direction == FILE_TRANSFER_RECV) 2529 | { 2530 | return new_file_receiver(friendnum, filenum, type); 2531 | } 2532 | 2533 | if (direction == FILE_TRANSFER_SEND) 2534 | { 2535 | return new_file_sender(friendnum, filenum, type); 2536 | } 2537 | 2538 | return NULL; 2539 | } 2540 | 2541 | 2542 | int avatar_send(Tox *m, uint32_t friendnum) 2543 | { 2544 | dbg(2, "avatar_send:001 friendnum=%d\n", (int)friendnum); 2545 | dbg(2, "avatar_send:002 %d %s %d\n", (int)Avatar.size, Avatar.name, (int)Avatar.name_len); 2546 | TOX_ERR_FILE_SEND err; 2547 | uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, 2548 | NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); 2549 | dbg(2, "avatar_send:tox_file_send=%s filenum=%d\n", (const char *)Avatar.name, (int)filenum); 2550 | 2551 | if (Avatar.size == 0) 2552 | { 2553 | return 0; 2554 | } 2555 | 2556 | if (err != TOX_ERR_FILE_SEND_OK) 2557 | { 2558 | dbg(0, "avatar_send:tox_file_send failed for _friendnumber %d (error %d)\n", friendnum, err); 2559 | return -1; 2560 | } 2561 | 2562 | dbg(2, "avatar_send(1):tox_file_send=%s filenum=%d\n", (const char *)Avatar.name, (int)filenum); 2563 | struct FileTransfer *ft = new_file_transfer(friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); 2564 | dbg(2, "avatar_send(2):tox_file_send=%s filenum=%d\n", (const char *)Avatar.name, (int)filenum); 2565 | 2566 | if (!ft) 2567 | { 2568 | dbg(0, "avatar_send:003:ft=NULL\n"); 2569 | return -1; 2570 | } 2571 | 2572 | ft->file = fopen(Avatar.path, "r"); 2573 | 2574 | if (ft->file == NULL) 2575 | { 2576 | dbg(0, "avatar_send:004:ft->file=NULL\n"); 2577 | return -1; 2578 | } 2579 | 2580 | snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name); 2581 | ft->file_size = Avatar.size; 2582 | return 0; 2583 | } 2584 | 2585 | 2586 | int check_file_signature(const char *signature, size_t size, FILE *fp) 2587 | { 2588 | char buf[size]; 2589 | 2590 | if (fread(buf, size, 1, fp) != 1) 2591 | { 2592 | return -1; 2593 | } 2594 | 2595 | int ret = memcmp(signature, buf, size); 2596 | 2597 | if (fseek(fp, 0L, SEEK_SET) == -1) 2598 | { 2599 | return -1; 2600 | } 2601 | 2602 | return ret == 0 ? 0 : 1; 2603 | } 2604 | 2605 | 2606 | void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum) 2607 | { 2608 | } 2609 | void kill_all_file_transfers(Tox *m) 2610 | { 2611 | } 2612 | 2613 | 2614 | int avatar_set(Tox *m, const char *path, size_t path_len) 2615 | { 2616 | dbg(2, "avatar_set:001\n"); 2617 | 2618 | if (path_len == 0 || path_len >= sizeof(Avatar.path)) 2619 | { 2620 | return -1; 2621 | } 2622 | 2623 | dbg(9, "avatar_set:002\n"); 2624 | FILE *fp = fopen(path, "rb"); 2625 | 2626 | if (fp == NULL) 2627 | { 2628 | return -1; 2629 | } 2630 | 2631 | char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; 2632 | 2633 | if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) 2634 | { 2635 | fclose(fp); 2636 | return -1; 2637 | } 2638 | 2639 | fclose(fp); 2640 | dbg(9, "avatar_set:003\n"); 2641 | off_t size = file_size(path); 2642 | 2643 | if (size == 0 || size > MAX_AVATAR_FILE_SIZE) 2644 | { 2645 | return -1; 2646 | } 2647 | 2648 | dbg(9, "avatar_set:004\n"); 2649 | get_file_name(Avatar.name, sizeof(Avatar.name), path); 2650 | Avatar.name_len = strlen(Avatar.name); 2651 | snprintf(Avatar.path, sizeof(Avatar.path), "%s", path); 2652 | Avatar.path_len = path_len; 2653 | Avatar.size = size; 2654 | dbg(9, "avatar_set:099\n"); 2655 | return 0; 2656 | } 2657 | 2658 | static void avatar_clear(void) 2659 | { 2660 | memset(&Avatar, 0, sizeof(struct Avatar)); 2661 | } 2662 | 2663 | void avatar_unset(Tox *m) 2664 | { 2665 | avatar_clear(); 2666 | } 2667 | 2668 | int check_number_of_files_to_resend_to_friend(Tox *m, uint32_t friendnum, int friendlistnum) 2669 | { 2670 | } 2671 | void resend_zip_files_and_send(Tox *m, uint32_t friendnum, int friendlistnum) 2672 | { 2673 | } 2674 | void process_friends_dir(Tox *m, uint32_t friendnum, int friendlistnum) 2675 | { 2676 | } 2677 | void check_friends_dir(Tox *m) 2678 | { 2679 | } 2680 | void check_dir(Tox *m) 2681 | { 2682 | } 2683 | 2684 | 2685 | char *get_current_time_date_formatted() 2686 | { 2687 | time_t t; 2688 | struct tm *tm = NULL; 2689 | const int max_size_datetime_str = 100; 2690 | char *str_date_time = malloc(max_size_datetime_str); 2691 | memset(str_date_time, 0, 100); 2692 | t = time(NULL); 2693 | tm = localtime(&t); 2694 | strftime(str_date_time, max_size_datetime_str, global_overlay_timestamp_format, tm); 2695 | // dbg(9, "str_date_time=%s\n", str_date_time); 2696 | return str_date_time; 2697 | } 2698 | 2699 | 2700 | // ------------------- V4L2 stuff --------------------- 2701 | // ------------------- V4L2 stuff --------------------- 2702 | // ------------------- V4L2 stuff --------------------- 2703 | 2704 | 2705 | static int xioctl(int fh, unsigned long request, void *arg) 2706 | { 2707 | int r; 2708 | 2709 | do 2710 | { 2711 | r = ioctl(fh, request, arg); 2712 | } 2713 | while (-1 == r && EINTR == errno); 2714 | 2715 | return r; 2716 | } 2717 | 2718 | 2719 | 2720 | int init_cam() 2721 | { 2722 | int video_dev_open_error = 0; 2723 | int fd; 2724 | 2725 | if ((fd = open(v4l2_device, O_RDWR)) < 0) 2726 | { 2727 | dbg(0, "error opening video device[1]\n"); 2728 | video_dev_open_error = 1; 2729 | } 2730 | 2731 | if (video_dev_open_error == 1) 2732 | { 2733 | sleep(20); // sleep 20 seconds 2734 | 2735 | if ((fd = open(v4l2_device, O_RDWR)) < 0) 2736 | { 2737 | dbg(0, "error opening video device[2]\n"); 2738 | video_dev_open_error = 1; 2739 | } 2740 | else 2741 | { 2742 | video_dev_open_error = 0; 2743 | } 2744 | } 2745 | 2746 | struct v4l2_capability cap; 2747 | 2748 | struct v4l2_cropcap cropcap; 2749 | 2750 | // struct v4l2_crop crop; 2751 | if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) 2752 | { 2753 | dbg(0, "VIDIOC_QUERYCAP\n"); 2754 | } 2755 | 2756 | if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) 2757 | { 2758 | dbg(0, "The device does not handle single-planar video capture.\n"); 2759 | } 2760 | 2761 | if (!(cap.capabilities & V4L2_CAP_STREAMING)) 2762 | { 2763 | dbg(0, "The device does not support streaming i/o.\n"); 2764 | } 2765 | 2766 | /* Select video input, video standard and tune here. */ 2767 | CLEAR(cropcap); 2768 | cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2769 | 2770 | if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) 2771 | { 2772 | #if 0 2773 | crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2774 | crop.c = cropcap.defrect; /* reset to full area */ 2775 | /* Scale the width and height to 50 % of their original size and center the output. */ 2776 | crop.c.width = crop.c.width / 2; 2777 | crop.c.height = crop.c.height / 2; 2778 | crop.c.left = crop.c.left + crop.c.width / 2; 2779 | crop.c.top = crop.c.top + crop.c.height / 2; 2780 | 2781 | if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) 2782 | { 2783 | switch (errno) 2784 | { 2785 | case EINVAL: 2786 | dbg(0, "Cropping not supported (1)\n"); 2787 | break; 2788 | 2789 | default: 2790 | dbg(0, "some error on croping setup\n"); 2791 | break; 2792 | } 2793 | } 2794 | 2795 | #endif 2796 | } 2797 | else 2798 | { 2799 | dbg(0, "Cropping not supported (2)\n"); 2800 | } 2801 | 2802 | #ifdef V4LCONVERT 2803 | v4lconvert_data = v4lconvert_create(fd); 2804 | #endif 2805 | CLEAR(format); 2806 | CLEAR(dest_format); 2807 | format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2808 | format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; 2809 | format.fmt.pix.width = 1920; 2810 | format.fmt.pix.height = 1080; 2811 | dest_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2812 | dest_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; 2813 | dest_format.fmt.pix.width = format.fmt.pix.width; 2814 | dest_format.fmt.pix.height = format.fmt.pix.height; 2815 | 2816 | if (format.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) 2817 | { 2818 | dbg(2, "Video format(wanted): V4L2_PIX_FMT_YUV420\n"); 2819 | } 2820 | else if (format.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) 2821 | { 2822 | dbg(2, "Video format(wanted): V4L2_PIX_FMT_MJPEG\n"); 2823 | } 2824 | else 2825 | { 2826 | dbg(2, "Video format(wanted): %u\n", format.fmt.pix.pixelformat); 2827 | } 2828 | 2829 | // Get <-> Set ?? 2830 | if (-1 == xioctl(fd, VIDIOC_G_FMT, &format)) 2831 | { 2832 | dbg(0, "VIDIOC_G_FMT\n"); 2833 | } 2834 | 2835 | if (format.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) 2836 | { 2837 | dbg(2, "Video format(got): V4L2_PIX_FMT_YUV420\n"); 2838 | } 2839 | else if (format.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) 2840 | { 2841 | dbg(2, "Video format(got): V4L2_PIX_FMT_MJPEG\n"); 2842 | } 2843 | else 2844 | { 2845 | dbg(2, "Video format(got): %u\n", format.fmt.pix.pixelformat); 2846 | } 2847 | 2848 | if (video_high == 1) 2849 | { 2850 | format.fmt.pix.width = 1280; 2851 | format.fmt.pix.height = 720; 2852 | } 2853 | else 2854 | { 2855 | format.fmt.pix.width = 640; 2856 | format.fmt.pix.height = 480; 2857 | } 2858 | 2859 | video_width = format.fmt.pix.width; 2860 | video_height = format.fmt.pix.height; 2861 | dbg(2, "Video size(wanted): %u %u\n", video_width, video_height); 2862 | 2863 | if (-1 == xioctl(fd, VIDIOC_S_FMT, &format)) 2864 | { 2865 | dbg(0, "VIDIOC_S_FMT\n"); 2866 | } 2867 | 2868 | if (-1 == xioctl(fd, VIDIOC_G_FMT, &format)) 2869 | { 2870 | dbg(0, "VIDIOC_G_FMT\n"); 2871 | } 2872 | 2873 | video_width = format.fmt.pix.width; 2874 | video_height = format.fmt.pix.height; 2875 | dbg(2, "Video size(got): %u %u\n", video_width, video_height); 2876 | dest_format.fmt.pix.width = format.fmt.pix.width; 2877 | dest_format.fmt.pix.height = format.fmt.pix.height; 2878 | /* Buggy driver paranoia. */ 2879 | /* 2880 | min = fmt.fmt.pix.width * 2; 2881 | if (fmt.fmt.pix.bytesperline < min) 2882 | fmt.fmt.pix.bytesperline = min; 2883 | min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; 2884 | if (fmt.fmt.pix.sizeimage < min) 2885 | fmt.fmt.pix.sizeimage = min; 2886 | */ 2887 | // HINT: set camera device fps ----------------------- 2888 | struct v4l2_streamparm *setfps; 2889 | setfps = (struct v4l2_streamparm *)calloc(1, sizeof(struct v4l2_streamparm)); 2890 | 2891 | if (setfps) 2892 | { 2893 | memset(setfps, 0, sizeof(struct v4l2_streamparm)); 2894 | setfps->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2895 | setfps->parm.capture.timeperframe.numerator = 1; 2896 | setfps->parm.capture.timeperframe.denominator = 30; 2897 | 2898 | if (-1 == xioctl(fd, VIDIOC_S_PARM, setfps)) 2899 | { 2900 | dbg(0, "VIDIOC_S_PARM:30fps -> Error\n"); 2901 | setfps->parm.capture.timeperframe.denominator = 15; 2902 | 2903 | if (-1 == xioctl(fd, VIDIOC_S_PARM, setfps)) 2904 | { 2905 | dbg(0, "VIDIOC_S_PARM:15fps -> Error\n"); 2906 | setfps->parm.capture.timeperframe.denominator = 10; 2907 | 2908 | if (-1 == xioctl(fd, VIDIOC_S_PARM, setfps)) 2909 | { 2910 | dbg(0, "VIDIOC_S_PARM:10fps -> Error\n"); 2911 | } 2912 | } 2913 | } 2914 | 2915 | free(setfps); 2916 | setfps = NULL; 2917 | } 2918 | 2919 | // HINT: set camera device fps ----------------------- 2920 | struct v4l2_requestbuffers bufrequest; 2921 | CLEAR(bufrequest); 2922 | bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2923 | bufrequest.memory = V4L2_MEMORY_MMAP; 2924 | bufrequest.count = VIDEO_BUFFER_COUNT; 2925 | dbg(0, "VIDIOC_REQBUFS want type=%d\n", (int)bufrequest.type); 2926 | 2927 | if (-1 == xioctl(fd, VIDIOC_REQBUFS, &bufrequest)) 2928 | { 2929 | if (EINVAL == errno) 2930 | { 2931 | dbg(0, "%s does not support x i/o\n", v4l2_device); 2932 | } 2933 | else 2934 | { 2935 | // dbg(0, "VIDIOC_REQBUFS error %d, %s\n", errno, strerror(errno)); 2936 | // try again ... 2937 | if (-1 == xioctl(fd, VIDIOC_REQBUFS, &bufrequest)) 2938 | { 2939 | if (EINVAL == errno) 2940 | { 2941 | dbg(0, "[2nd] %s does not support x i/o\n", v4l2_device); 2942 | } 2943 | else 2944 | { 2945 | dbg(0, "[2nd] VIDIOC_REQBUFS error %d, %s\n", errno, strerror(errno)); 2946 | } 2947 | } 2948 | } 2949 | } 2950 | 2951 | dbg(0, "VIDIOC_REQBUFS got type=%d\n", (int)bufrequest.type); 2952 | 2953 | if (bufrequest.count < 2) 2954 | { 2955 | dbg(0, "Insufficient buffer memory on %s\n", v4l2_device); 2956 | } 2957 | 2958 | buffers = calloc(bufrequest.count, sizeof(*buffers)); 2959 | 2960 | for (n_buffers = 0; n_buffers < bufrequest.count; ++n_buffers) 2961 | { 2962 | struct v4l2_buffer bufferinfo; 2963 | CLEAR(bufferinfo); 2964 | bufferinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2965 | bufferinfo.memory = V4L2_MEMORY_MMAP; 2966 | bufferinfo.index = n_buffers; 2967 | 2968 | if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &bufferinfo)) 2969 | { 2970 | dbg(9, "VIDIOC_QUERYBUF (2) error %d, %s\n", errno, strerror(errno)); 2971 | } 2972 | 2973 | /* 2974 | if (ioctl(fd, VIDIOC_QUERYBUF, &bufferinfo) < 0) 2975 | { 2976 | dbg(0, "VIDIOC_QUERYBUF %d %s\n", errno, strerror(errno)); 2977 | } 2978 | */ 2979 | buffers[n_buffers].length = bufferinfo.length; 2980 | buffers[n_buffers].start = mmap(NULL /* start anywhere */, bufferinfo.length, PROT_READ | PROT_WRITE /* required */, 2981 | MAP_SHARED /* recommended */, fd, bufferinfo.m.offset); 2982 | 2983 | if (MAP_FAILED == buffers[n_buffers].start) 2984 | { 2985 | dbg(0, "mmap error %d, %s\n", errno, strerror(errno)); 2986 | } 2987 | } 2988 | 2989 | return fd; 2990 | } 2991 | 2992 | 2993 | int v4l_startread() 2994 | { 2995 | dbg(9, "start cam\n"); 2996 | size_t i; 2997 | enum v4l2_buf_type type; 2998 | 2999 | for (i = 0; i < n_buffers; ++i) 3000 | { 3001 | struct v4l2_buffer buf; 3002 | dbg(9, "buffer (1) %d of %d\n", i, n_buffers); 3003 | CLEAR(buf); 3004 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3005 | buf.memory = V4L2_MEMORY_MMAP; 3006 | buf.index = i; 3007 | 3008 | if (-1 == xioctl(global_cam_device_fd, VIDIOC_QBUF, &buf)) 3009 | { 3010 | dbg(9, "VIDIOC_QBUF (3) error %d, %s\n", errno, strerror(errno)); 3011 | return 0; 3012 | } 3013 | } 3014 | 3015 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3016 | 3017 | if (-1 == xioctl(global_cam_device_fd, VIDIOC_STREAMON, &type)) 3018 | { 3019 | dbg(9, "VIDIOC_STREAMON error %d, %s\n", errno, strerror(errno)); 3020 | return 0; 3021 | } 3022 | 3023 | return 1; 3024 | } 3025 | 3026 | 3027 | int v4l_endread() 3028 | { 3029 | dbg(9, "stop webcam\n"); 3030 | enum v4l2_buf_type type; 3031 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3032 | 3033 | if (-1 == xioctl(global_cam_device_fd, VIDIOC_STREAMOFF, &type)) 3034 | { 3035 | dbg(9, "VIDIOC_STREAMOFF error %d, %s\n", errno, strerror(errno)); 3036 | return 0; 3037 | } 3038 | 3039 | return 1; 3040 | } 3041 | 3042 | 3043 | void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *input, uint16_t width, uint16_t height) 3044 | { 3045 | uint8_t *end = input + width * height * 2; 3046 | 3047 | while (input != end) 3048 | { 3049 | uint8_t *line_end = input + width * 2; 3050 | 3051 | while (input != line_end) 3052 | { 3053 | *plane_y++ = *input++; 3054 | *plane_v++ = *input++; 3055 | *plane_y++ = *input++; 3056 | *plane_u++ = *input++; 3057 | } 3058 | 3059 | line_end = input + width * 2; 3060 | 3061 | while (input != line_end) 3062 | { 3063 | *plane_y++ = *input++; 3064 | input++; // u 3065 | *plane_y++ = *input++; 3066 | input++; // v 3067 | } 3068 | } 3069 | } 3070 | 3071 | 3072 | int v4l_getframe(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t width, uint16_t height) 3073 | { 3074 | if (width != video_width || height != video_height) 3075 | { 3076 | dbg(9, "V4L:\twidth/height mismatch %u %u != %u %u\n", width, height, video_width, video_height); 3077 | return 0; 3078 | } 3079 | 3080 | struct v4l2_buffer buf; 3081 | 3082 | CLEAR(buf); 3083 | 3084 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3085 | 3086 | buf.memory = V4L2_MEMORY_MMAP; // V4L2_MEMORY_USERPTR; 3087 | 3088 | if (-1 == ioctl(global_cam_device_fd, VIDIOC_DQBUF, &buf)) 3089 | { 3090 | switch (errno) 3091 | { 3092 | case EINTR: 3093 | case EAGAIN: 3094 | return 0; 3095 | 3096 | case EIO: 3097 | 3098 | /* Could ignore EIO, see spec. */ 3099 | 3100 | /* fall through */ 3101 | default: 3102 | dbg(9, "VIDIOC_DQBUF error %d, %s\n", errno, strerror(errno)); 3103 | return -1; 3104 | } 3105 | } 3106 | 3107 | /*for (i = 0; i < n_buffers; ++i) 3108 | if (buf.m.userptr == (unsigned long)buffers[i].start 3109 | && buf.length == buffers[i].length) 3110 | break; 3111 | 3112 | if(i >= n_buffers) { 3113 | dbg(9, "fatal error\n"); 3114 | return 0; 3115 | }*/ 3116 | // dbg(9, "buf.index=%d\n", (int)buf.index); 3117 | void *data = (void *)buffers[buf.index].start; // length = buf.bytesused //(void*)buf.m.userptr 3118 | /* assumes planes are continuous memory */ 3119 | #ifdef V4LCONVERT 3120 | // dbg(9, "V4LCONVERT\n"); 3121 | int result = v4lconvert_convert(v4lconvert_data, &format, &dest_format, data, buf.bytesused, y, 3122 | (video_width * video_height * 3) / 2); 3123 | 3124 | if (result == -1) 3125 | { 3126 | dbg(0, "v4lconvert_convert error %s\n", v4lconvert_get_error_message(v4lconvert_data)); 3127 | } 3128 | 3129 | #else 3130 | dbg(9, "convert2\n"); 3131 | 3132 | if (format.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) 3133 | { 3134 | dbg(9, "yuv422to420\n"); 3135 | yuv422to420(y, u, v, data, video_width, video_height); 3136 | } 3137 | else 3138 | { 3139 | } 3140 | 3141 | #endif 3142 | 3143 | if (-1 == xioctl(global_cam_device_fd, VIDIOC_QBUF, &buf)) 3144 | { 3145 | dbg(9, "VIDIOC_QBUF (1) error %d, %s\n", errno, strerror(errno)); 3146 | } 3147 | 3148 | #ifdef V4LCONVERT 3149 | return (result == -1 ? 0 : 1); 3150 | #else 3151 | return 1; 3152 | #endif 3153 | } 3154 | 3155 | 3156 | void close_cam() 3157 | { 3158 | #ifdef V4LCONVERT 3159 | v4lconvert_destroy(v4lconvert_data); 3160 | #endif 3161 | size_t i; 3162 | 3163 | for (i = 0; i < n_buffers; ++i) 3164 | { 3165 | if (-1 == munmap(buffers[i].start, buffers[i].length)) 3166 | { 3167 | dbg(9, "munmap error\n"); 3168 | } 3169 | } 3170 | 3171 | close(global_cam_device_fd); 3172 | } 3173 | 3174 | // ------------------- V4L2 stuff --------------------- 3175 | // ------------------- V4L2 stuff --------------------- 3176 | // ------------------- V4L2 stuff --------------------- 3177 | 3178 | 3179 | 3180 | // ------------------ Tox AV stuff -------------------- 3181 | // ------------------ Tox AV stuff -------------------- 3182 | // ------------------ Tox AV stuff -------------------- 3183 | 3184 | static void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data) 3185 | { 3186 | if (global_video_active == 1) 3187 | { 3188 | dbg(9, "Call already active\n"); 3189 | } 3190 | else 3191 | { 3192 | dbg(9, "Handling CALL callback friendnum=%d audio_enabled=%d video_enabled=%d\n", (int)friend_number, 3193 | (int)audio_enabled, (int)video_enabled); 3194 | ((CallControl *)user_data)->incoming = true; 3195 | TOXAV_ERR_ANSWER err; 3196 | global_video_bit_rate = DEFAULT_GLOBAL_VID_BITRATE; 3197 | int audio_bitrate = DEFAULT_GLOBAL_AUD_BITRATE; 3198 | int video_bitrate = global_video_bit_rate; 3199 | friend_to_send_video_to = friend_number; 3200 | global_video_active = 1; 3201 | global_send_first_frame = 2; 3202 | dbg(9, "Handling CALL callback friendnum=%d audio_bitrate=%d video_bitrate=%d\n", (int)friend_number, 3203 | (int)audio_bitrate, (int)video_bitrate); 3204 | toxav_answer(av, friend_number, audio_bitrate, video_bitrate, &err); 3205 | #ifdef HAVE_TOXAV_OPTION_SET 3206 | TOXAV_ERR_OPTION_SET error2; 3207 | // toxav_option_set(av, friend_number, TOXAV_ENCODER_VP8_QUALITY, (int32_t)TOXAV_ENCODER_VP8_QUALITY_NORMAL, &error2); 3208 | toxav_option_set(av, friend_number, TOXAV_ENCODER_RC_MAX_QUANTIZER, (int32_t)RC_MAX_QUANTIZER, &error2); 3209 | dbg(9, "Call with friend state:(1)set TOXAV_ENCODER_RC_MAX_QUANTIZER to %d res=%d\n", 3210 | (int)RC_MAX_QUANTIZER, 3211 | (int)error2); 3212 | #endif 3213 | } 3214 | } 3215 | 3216 | static void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) 3217 | { 3218 | dbg(9, "Handling CALL STATE callback: %d friend_number=%d\n", state, (int)friend_number); 3219 | ((CallControl *)user_data)->state = state; 3220 | 3221 | if (state & TOXAV_FRIEND_CALL_STATE_FINISHED) 3222 | { 3223 | dbg(9, "Call with friend %d finished\n", friend_number); 3224 | global_video_active = 0; 3225 | return; 3226 | } 3227 | else if (state & TOXAV_FRIEND_CALL_STATE_ERROR) 3228 | { 3229 | dbg(9, "Call with friend %d errored\n", friend_number); 3230 | global_video_active = 0; 3231 | return; 3232 | } 3233 | else if (state & TOXAV_FRIEND_CALL_STATE_SENDING_A) 3234 | { 3235 | dbg(9, "Call with friend state:TOXAV_FRIEND_CALL_STATE_SENDING_A\n"); 3236 | } 3237 | else if (state & TOXAV_FRIEND_CALL_STATE_SENDING_V) 3238 | { 3239 | dbg(9, "Call with friend state:TOXAV_FRIEND_CALL_STATE_SENDING_V\n"); 3240 | } 3241 | else if (state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A) 3242 | { 3243 | dbg(9, "Call with friend state:TOXAV_FRIEND_CALL_STATE_ACCEPTING_A\n"); 3244 | } 3245 | else if (state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V) 3246 | { 3247 | dbg(9, "Call with friend state:TOXAV_FRIEND_CALL_STATE_ACCEPTING_V\n"); 3248 | } 3249 | 3250 | #ifdef HAVE_TOXAV_OPTION_SET 3251 | TOXAV_ERR_OPTION_SET error2; 3252 | // toxav_option_set(av, friend_number, TOXAV_ENCODER_VP8_QUALITY, (int32_t)TOXAV_ENCODER_VP8_QUALITY_NORMAL, &error2); 3253 | toxav_option_set(av, friend_number, TOXAV_ENCODER_RC_MAX_QUANTIZER, (int32_t)RC_MAX_QUANTIZER, &error2); 3254 | dbg(9, "Call with friend state:(0)set TOXAV_ENCODER_RC_MAX_QUANTIZER to %d res=%d\n", 3255 | (int)RC_MAX_QUANTIZER, 3256 | (int)error2); 3257 | #endif 3258 | dbg(9, "t_toxav_call_state_cb:002\n"); 3259 | int send_audio = (state & TOXAV_FRIEND_CALL_STATE_SENDING_A) && (state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_A); 3260 | int send_video = state & TOXAV_FRIEND_CALL_STATE_SENDING_V && (state & TOXAV_FRIEND_CALL_STATE_ACCEPTING_V); 3261 | dbg(9, "t_toxav_call_state_cb:002a send_audio=%d send_video=%d global_video_bit_rate=%d\n", send_audio, send_video, 3262 | (int)global_video_bit_rate); 3263 | TOXAV_ERR_BIT_RATE_SET bitrate_err = 0; 3264 | // ** // toxav_bit_rate_set(av, friend_number, 0, send_video ? global_video_bit_rate : 0, &bitrate_err); 3265 | dbg(9, "t_toxav_call_state_cb:004\n"); 3266 | 3267 | if (bitrate_err) 3268 | { 3269 | dbg(9, "ToxAV:Error setting/changing video bitrate\n"); 3270 | } 3271 | 3272 | if (send_video == 1) 3273 | { 3274 | dbg(9, "t_toxav_call_state_cb:004\n"); 3275 | global_video_active = 1; 3276 | global_send_first_frame = 2; 3277 | } 3278 | else 3279 | { 3280 | dbg(9, "t_toxav_call_state_cb:005\n"); 3281 | global_video_active = 0; 3282 | global_send_first_frame = 0; 3283 | } 3284 | 3285 | dbg(9, "Call state for friend %d changed to %d, audio=%d, video=%d\n", friend_number, state, send_audio, send_video); 3286 | } 3287 | 3288 | static void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, 3289 | uint32_t audio_bit_rate, uint32_t video_bit_rate, 3290 | void *user_data) 3291 | { 3292 | dbg(0, "t_toxav_bit_rate_status_cb:001 video_bit_rate=%d\n", (int)video_bit_rate); 3293 | dbg(0, "t_toxav_bit_rate_status_cb:001 audio_bit_rate=%d\n", (int)audio_bit_rate); 3294 | TOXAV_ERR_BIT_RATE_SET error = 0; 3295 | uint32_t video_bit_rate_ = video_bit_rate; 3296 | 3297 | if (video_bit_rate < DEFAULT_GLOBAL_MIN_VID_BITRATE) 3298 | { 3299 | video_bit_rate_ = DEFAULT_GLOBAL_MIN_VID_BITRATE; 3300 | } 3301 | 3302 | // toxav_bit_rate_set(av, friend_number, audio_bit_rate, video_bit_rate_, &error); 3303 | 3304 | if (error != 0) 3305 | { 3306 | dbg(0, "ToxAV:Setting new Video bitrate has failed with error #%u\n", error); 3307 | } 3308 | else 3309 | { 3310 | global_video_bit_rate = video_bit_rate_; 3311 | } 3312 | 3313 | dbg(2, "suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate); 3314 | dbg(2, "actual bit rates: audio: %d video: %d\n", global_audio_bit_rate, global_video_bit_rate); 3315 | } 3316 | 3317 | 3318 | static void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, 3319 | int16_t const *pcm, 3320 | size_t sample_count, 3321 | uint8_t channels, 3322 | uint32_t sampling_rate, 3323 | void *user_data) 3324 | { 3325 | if (global_video_active == 1) 3326 | { 3327 | if (friend_to_send_video_to == friend_number) 3328 | { 3329 | } 3330 | else 3331 | { 3332 | // wrong friend 3333 | } 3334 | } 3335 | else 3336 | { 3337 | } 3338 | 3339 | // CallControl *cc = (CallControl *)user_data; 3340 | // frame *f = (frame *)malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels); 3341 | // memcpy(f->data, pcm, sample_count * sizeof(int16_t) * channels); 3342 | // f->size = sample_count; 3343 | // pthread_mutex_lock(cc->arb_mutex); 3344 | // free(rb_write(cc->arb, f)); 3345 | // pthread_mutex_unlock(cc->arb_mutex); 3346 | } 3347 | 3348 | 3349 | static void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, 3350 | uint16_t width, uint16_t height, 3351 | uint8_t const *y, uint8_t const *u, uint8_t const *v, 3352 | int32_t ystride, int32_t ustride, int32_t vstride, 3353 | void *user_data) 3354 | { 3355 | if (global_video_active == 1) 3356 | { 3357 | if (friend_to_send_video_to == friend_number) 3358 | { 3359 | } 3360 | else 3361 | { 3362 | // wrong friend 3363 | } 3364 | } 3365 | else 3366 | { 3367 | } 3368 | 3369 | // ystride = abs(ystride); 3370 | // ustride = abs(ustride); 3371 | // vstride = abs(vstride); 3372 | // uint16_t *img_data = (uint16_t *)malloc(height * width * 6); 3373 | // unsigned long int i, j; 3374 | // for (i = 0; i < height; ++i) 3375 | // { 3376 | // for (j = 0; j < width; ++j) 3377 | // { 3378 | // uint8_t *point = (uint8_t *) img_data + 3 * ((i * width) + j); 3379 | // int yx = y[(i * ystride) + j]; 3380 | // int ux = u[((i / 2) * ustride) + (j / 2)]; 3381 | // int vx = v[((i / 2) * vstride) + (j / 2)]; 3382 | // point[0] = YUV2R(yx, ux, vx); 3383 | // point[1] = YUV2G(yx, ux, vx); 3384 | // point[2] = YUV2B(yx, ux, vx); 3385 | // } 3386 | // } 3387 | // CvMat mat = cvMat(height, width, CV_8UC3, img_data); 3388 | // CvSize sz; 3389 | // sz.height = height; 3390 | // sz.width = width; 3391 | // IplImage *header = cvCreateImageHeader(sz, 1, 3); 3392 | // IplImage *img = cvGetImage(&mat, header); 3393 | // cvShowImage(vdout, img); 3394 | // free(img_data); 3395 | } 3396 | 3397 | void set_av_video_frame() 3398 | { 3399 | vpx_img_alloc(&input, VPX_IMG_FMT_I420, video_width, video_height, 1); 3400 | av_video_frame.y = input.planes[0]; /**< Y (Luminance) plane and VPX_PLANE_PACKED */ 3401 | av_video_frame.u = input.planes[1]; /**< U (Chroma) plane */ 3402 | av_video_frame.v = input.planes[2]; /**< V (Chroma) plane */ 3403 | av_video_frame.w = input.d_w; 3404 | av_video_frame.h = input.d_h; 3405 | //av_video_frame.bit_depth = input.bit_depth; 3406 | dbg(2, "ToxVideo:av_video_frame set\n"); 3407 | } 3408 | 3409 | void *thread_av(void *data) 3410 | { 3411 | ToxAV *av = (ToxAV *) data; 3412 | pthread_t id = pthread_self(); 3413 | pthread_mutex_t av_thread_lock; 3414 | 3415 | if (pthread_mutex_init(&av_thread_lock, NULL) != 0) 3416 | { 3417 | dbg(0, "Error creating av_thread_lock\n"); 3418 | } 3419 | else 3420 | { 3421 | dbg(2, "av_thread_lock created successfully\n"); 3422 | } 3423 | 3424 | dbg(2, "AV Thread #%d: starting\n", (int) id); 3425 | 3426 | if (video_call_enabled == 1) 3427 | { 3428 | global_cam_device_fd = init_cam(); 3429 | dbg(2, "AV Thread #%d: init cam\n", (int) id); 3430 | set_av_video_frame(); 3431 | // start streaming 3432 | v4l_startread(); 3433 | } 3434 | 3435 | while (toxav_iterate_thread_stop != 1) 3436 | { 3437 | if (global_video_active == 1) 3438 | { 3439 | pthread_mutex_lock(&av_thread_lock); 3440 | // dbg(9, "AV Thread #%d:get frame\n", (int) id); 3441 | // capturing is enabled, capture frames 3442 | int r = v4l_getframe(av_video_frame.y, av_video_frame.u, av_video_frame.v, 3443 | av_video_frame.w, av_video_frame.h); 3444 | 3445 | if (r == 1) 3446 | { 3447 | if (global_send_first_frame > 0) 3448 | { 3449 | black_yuf_frame_xy(); 3450 | global_send_first_frame--; 3451 | } 3452 | 3453 | // "0" -> [48] 3454 | // "9" -> [57] 3455 | // ":" -> [58] 3456 | char *date_time_str = get_current_time_date_formatted(); 3457 | 3458 | if (date_time_str) 3459 | { 3460 | text_on_yuf_frame_xy(10, 10, date_time_str); 3461 | free(date_time_str); 3462 | } 3463 | 3464 | blinking_dot_on_frame_xy(20, 30, &global_blink_state); 3465 | 3466 | if (friend_to_send_video_to != -1) 3467 | { 3468 | // dbg(9, "AV Thread #%d:send frame to friend num=%d\n", (int) id, (int)friend_to_send_video_to); 3469 | TOXAV_ERR_SEND_FRAME error = 0; 3470 | toxav_video_send_frame(av, friend_to_send_video_to, av_video_frame.w, av_video_frame.h, 3471 | av_video_frame.y, av_video_frame.u, av_video_frame.v, &error); 3472 | 3473 | if (error) 3474 | { 3475 | if (error == TOXAV_ERR_SEND_FRAME_SYNC) 3476 | { 3477 | //debug_notice("uToxVideo:\tVid Frame sync error: w=%u h=%u\n", av_video_frame.w, 3478 | // av_video_frame.h); 3479 | dbg(0, "TOXAV_ERR_SEND_FRAME_SYNC\n"); 3480 | } 3481 | else if (error == TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED) 3482 | { 3483 | //debug_error("uToxVideo:\tToxAV disagrees with our AV state for friend %lu, self %u, friend %u\n", 3484 | // i, friend[i].call_state_self, friend[i].call_state_friend); 3485 | dbg(0, "TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED\n"); 3486 | } 3487 | else 3488 | { 3489 | //debug_error("uToxVideo:\ttoxav_send_video error friend: %i error: %u\n", 3490 | // friend[i].number, error); 3491 | dbg(0, "ToxVideo:toxav_send_video error %u\n", error); 3492 | // *TODO* if these keep piling up --> just disconnect the call!! 3493 | // *TODO* if these keep piling up --> just disconnect the call!! 3494 | // *TODO* if these keep piling up --> just disconnect the call!! 3495 | } 3496 | } 3497 | } 3498 | } 3499 | else if (r == -1) 3500 | { 3501 | // debug_error("uToxVideo:\tErr... something really bad happened trying to get this frame, I'm just going " 3502 | // "to plots now!\n"); 3503 | //video_device_stop(); 3504 | //close_video_device(video_device); 3505 | dbg(0, "ToxVideo:something really bad happened trying to get this frame\n"); 3506 | } 3507 | 3508 | pthread_mutex_unlock(&av_thread_lock); 3509 | // yieldcpu(1000); // 1 frame every 1 seconds!! 3510 | yieldcpu(DEFAULT_FPS_SLEEP_MS); /* ~4 frames per second */ 3511 | // yieldcpu(80); /* ~12 frames per second */ 3512 | // yieldcpu(40); /* 60fps = 16.666ms || 25 fps = 40ms || the data quality is SO much better at 25... */ 3513 | } 3514 | else 3515 | { 3516 | yieldcpu(100); 3517 | } 3518 | } 3519 | 3520 | if (video_call_enabled == 1) 3521 | { 3522 | // end streaming 3523 | v4l_endread(); 3524 | } 3525 | 3526 | dbg(2, "ToxVideo:Clean thread exit!\n"); 3527 | } 3528 | 3529 | 3530 | void *thread_video_av(void *data) 3531 | { 3532 | ToxAV *av = (ToxAV *) data; 3533 | pthread_t id = pthread_self(); 3534 | pthread_mutex_t av_thread_lock; 3535 | 3536 | if (pthread_mutex_init(&av_thread_lock, NULL) != 0) 3537 | { 3538 | dbg(0, "Error creating video av_thread_lock\n"); 3539 | } 3540 | else 3541 | { 3542 | dbg(2, "av_thread_lock video created successfully\n"); 3543 | } 3544 | 3545 | dbg(2, "AV video Thread #%d: starting\n", (int) id); 3546 | 3547 | while (toxav_video_thread_stop != 1) 3548 | { 3549 | pthread_mutex_lock(&av_thread_lock); 3550 | toxav_iterate(av); 3551 | // dbg(9, "AV video Thread #%d running ...", (int) id); 3552 | pthread_mutex_unlock(&av_thread_lock); 3553 | // usleep(toxav_iteration_interval(av) * 1000); 3554 | usleep(4 * 1000); 3555 | } 3556 | 3557 | dbg(2, "ToxVideo:Clean video thread exit!\n"); 3558 | } 3559 | 3560 | 3561 | void av_local_disconnect(ToxAV *av, uint32_t num) 3562 | { 3563 | dbg(9, "av_local_disconnect\n"); 3564 | TOXAV_ERR_CALL_CONTROL error = 0; 3565 | toxav_call_control(av, num, TOXAV_CALL_CONTROL_CANCEL, &error); 3566 | global_video_active = 0; 3567 | global_send_first_frame = 0; 3568 | friend_to_send_video_to = -1; 3569 | } 3570 | 3571 | 3572 | // ------------------ Tox AV stuff -------------------- 3573 | // ------------------ Tox AV stuff -------------------- 3574 | // ------------------ Tox AV stuff -------------------- 3575 | 3576 | 3577 | 3578 | // ------------------ YUV420 overlay hack ------------- 3579 | // ------------------ YUV420 overlay hack ------------- 3580 | // ------------------ YUV420 overlay hack ------------- 3581 | 3582 | 3583 | 3584 | 3585 | /** 3586 | * 8x8 monochrome bitmap fonts for rendering 3587 | * Author: Daniel Hepper 3588 | * 3589 | * License: Public Domain 3590 | * 3591 | * Based on: 3592 | * // Summary: font8x8.h 3593 | * // 8x8 monochrome bitmap fonts for rendering 3594 | * // 3595 | * // Author: 3596 | * // Marcel Sondaar 3597 | * // International Business Machines (public domain VGA fonts) 3598 | * // 3599 | * // License: 3600 | * // Public Domain 3601 | * 3602 | * Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm 3603 | **/ 3604 | 3605 | // Constant: font8x8_basic 3606 | // Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) 3607 | char font8x8_basic[128][8] = 3608 | { 3609 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul) 3610 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001 3611 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002 3612 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003 3613 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004 3614 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005 3615 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006 3616 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007 3617 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008 3618 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009 3619 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A 3620 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B 3621 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C 3622 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D 3623 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E 3624 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F 3625 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010 3626 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011 3627 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012 3628 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013 3629 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014 3630 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015 3631 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016 3632 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017 3633 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018 3634 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019 3635 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A 3636 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B 3637 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C 3638 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D 3639 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E 3640 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F 3641 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) 3642 | { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!) 3643 | { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (") 3644 | { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#) 3645 | { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($) 3646 | { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%) 3647 | { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&) 3648 | { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (') 3649 | { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (() 3650 | { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ()) 3651 | { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*) 3652 | { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+) 3653 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,) 3654 | { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) 3655 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) 3656 | { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) 3657 | { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) 3658 | { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) 3659 | { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) 3660 | { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) 3661 | { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) 3662 | { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) 3663 | { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) 3664 | { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) 3665 | { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) 3666 | { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) 3667 | { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:) 3668 | { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (//) 3669 | { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<) 3670 | { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=) 3671 | { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>) 3672 | { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?) 3673 | { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@) 3674 | { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) 3675 | { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) 3676 | { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) 3677 | { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) 3678 | { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) 3679 | { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) 3680 | { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) 3681 | { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) 3682 | { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) 3683 | { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) 3684 | { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) 3685 | { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) 3686 | { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) 3687 | { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) 3688 | { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) 3689 | { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) 3690 | { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) 3691 | { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) 3692 | { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) 3693 | { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) 3694 | { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) 3695 | { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) 3696 | { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) 3697 | { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) 3698 | { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) 3699 | { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) 3700 | { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([) 3701 | { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\) 3702 | { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (]) 3703 | { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^) 3704 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_) 3705 | { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`) 3706 | { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) 3707 | { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) 3708 | { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) 3709 | { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) 3710 | { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) 3711 | { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) 3712 | { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) 3713 | { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) 3714 | { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) 3715 | { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) 3716 | { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) 3717 | { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) 3718 | { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) 3719 | { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) 3720 | { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) 3721 | { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) 3722 | { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) 3723 | { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) 3724 | { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) 3725 | { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) 3726 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) 3727 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) 3728 | { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) 3729 | { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) 3730 | { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) 3731 | { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) 3732 | { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({) 3733 | { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|) 3734 | { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (}) 3735 | { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~) 3736 | { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F 3737 | }; 3738 | 3739 | 3740 | 3741 | // "0" -> [48] 3742 | // "9" -> [57] 3743 | // ":" -> [58] 3744 | 3745 | 3746 | void print_font_char(int start_x_pix, int start_y_pix, int font_char_num, uint8_t col_value) 3747 | { 3748 | int font_w = 8; 3749 | int font_h = 8; 3750 | uint8_t *y_plane = av_video_frame.y; 3751 | // uint8_t col_value = 0; // black 3752 | char *bitmap = font8x8_basic[font_char_num]; 3753 | int k; 3754 | int j; 3755 | int offset = 0; 3756 | int set = 0; 3757 | 3758 | for (k = 0; k < font_h; k++) 3759 | { 3760 | y_plane = av_video_frame.y + ((start_y_pix + k) * av_video_frame.w); 3761 | y_plane = y_plane + start_x_pix; 3762 | 3763 | for (j = 0; j < font_w; j++) 3764 | { 3765 | set = bitmap[k] & 1 << j; 3766 | 3767 | if (set) 3768 | { 3769 | *y_plane = col_value; // set luma value 3770 | } 3771 | 3772 | y_plane = y_plane + 1; 3773 | } 3774 | } 3775 | } 3776 | 3777 | void black_yuf_frame_xy() 3778 | { 3779 | const uint8_t r = 0; 3780 | const uint8_t g = 0; 3781 | const uint8_t b = 0; 3782 | left_top_bar_into_yuv_frame(0, 0, av_video_frame.w, av_video_frame.h, r, g, b); 3783 | } 3784 | 3785 | void blinking_dot_on_frame_xy(int start_x_pix, int start_y_pix, int *state) 3786 | { 3787 | uint8_t r; 3788 | uint8_t g; 3789 | uint8_t b; 3790 | 3791 | if (*state == 0) 3792 | { 3793 | *state = 1; 3794 | r = 255; 3795 | g = 0; 3796 | b = 0; 3797 | left_top_bar_into_yuv_frame(start_x_pix, start_y_pix, 30, 30, r, g, b); 3798 | } 3799 | else if (*state == 1) 3800 | { 3801 | r = 255; 3802 | g = 255; 3803 | b = 0; 3804 | *state = 2; 3805 | left_top_bar_into_yuv_frame(start_x_pix, start_y_pix, 30, 30, r, g, b); 3806 | } 3807 | else 3808 | { 3809 | r = 0; 3810 | g = 255; 3811 | b = 0; 3812 | *state = 0; 3813 | left_top_bar_into_yuv_frame(start_x_pix, start_y_pix, 30, 30, r, g, b); 3814 | } 3815 | } 3816 | 3817 | 3818 | void set_color_in_yuv_frame_xy(uint8_t *yuv_frame, int px_x, int px_y, int frame_w, int frame_h, uint8_t r, uint8_t g, 3819 | uint8_t b) 3820 | { 3821 | int size_total = frame_w * frame_h; 3822 | uint8_t y; 3823 | uint8_t u; 3824 | uint8_t v; 3825 | rbg_to_yuv(r, g, b, &y, &u, &v); 3826 | yuv_frame[px_y * frame_w + px_x] = y; 3827 | yuv_frame[(px_y / 2) * (frame_w / 2) + (px_x / 2) + size_total] = u; 3828 | yuv_frame[(px_y / 2) * (frame_w / 2) + (px_x / 2) + size_total + (size_total / 4)] = v; 3829 | } 3830 | 3831 | 3832 | 3833 | #define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X) 3834 | 3835 | // RGB -> YUV 3836 | #define RGB2Y(R, G, B) CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) 3837 | #define RGB2U(R, G, B) CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) 3838 | #define RGB2V(R, G, B) CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) 3839 | 3840 | // YUV -> RGB 3841 | #define C(Y) ( (Y) - 16 ) 3842 | #define D(U) ( (U) - 128 ) 3843 | #define E(V) ( (V) - 128 ) 3844 | 3845 | #define YUV2R(Y, U, V) CLIP(( 298 * C(Y) + 409 * E(V) + 128) >> 8) 3846 | #define YUV2G(Y, U, V) CLIP(( 298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8) 3847 | #define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8) 3848 | 3849 | void rbg_to_yuv(uint8_t r, uint8_t g, uint8_t b, uint8_t *y, uint8_t *u, uint8_t *v) 3850 | { 3851 | *y = RGB2Y(r, g, b); 3852 | *u = RGB2U(r, g, b); 3853 | *v = RGB2V(r, g, b); 3854 | } 3855 | 3856 | void text_on_yuf_frame_xy(int start_x_pix, int start_y_pix, const char *text) 3857 | { 3858 | int carriage = 0; 3859 | const int letter_width = 8; 3860 | const int letter_spacing = 1; 3861 | int block_needed_width = 2 + 2 + (strlen(text) * (letter_width + letter_spacing)); 3862 | left_top_bar_into_yuv_frame(start_x_pix, start_y_pix, block_needed_width, 12, 255, 255, 255); 3863 | int looper; 3864 | 3865 | for (looper = 0; (int)looper < (int)strlen(text); looper++) 3866 | { 3867 | uint8_t c = text[looper]; 3868 | 3869 | if ((c > 0) && (c < 127)) 3870 | { 3871 | print_font_char((12 + ((letter_width + letter_spacing) * carriage)), 12, c, 0); 3872 | } 3873 | else 3874 | { 3875 | // leave a blank 3876 | } 3877 | 3878 | carriage++; 3879 | } 3880 | } 3881 | 3882 | void left_top_bar_into_yuv_frame(int bar_start_x_pix, int bar_start_y_pix, int bar_w_pix, int bar_h_pix, uint8_t r, 3883 | uint8_t g, uint8_t b) 3884 | { 3885 | // int bar_width = bar_w_pix; // 150; // should be mulitple of 2 !! 3886 | // int bar_height = bar_h_pix; // 20; // should be mulitple of 2 !! 3887 | // int bar_start_x = bar_start_x_pix; // 10; // should be mulitple of 2 !! (zero is also ok) 3888 | // int bar_start_y = bar_start_y_pix; // 10; // should be mulitple of 2 !! (zero is also ok) 3889 | // uint8_t *y_plane = av_video_frame.y; 3890 | int k; 3891 | int j; 3892 | // int offset = 0; 3893 | 3894 | for (k = 0; k < bar_h_pix; k++) 3895 | { 3896 | // y_plane = av_video_frame.y + ((bar_start_y + k) * av_video_frame.w); 3897 | // y_plane = y_plane + bar_start_x; 3898 | for (j = 0; j < bar_w_pix; j++) 3899 | { 3900 | // ******** // *y_plane = col_value; // luma value to 255 (white) 3901 | set_color_in_yuv_frame_xy(av_video_frame.y, (bar_start_x_pix + j), (bar_start_y_pix + k), 3902 | av_video_frame.w, av_video_frame.h, r, g, b); 3903 | // y_plane = y_plane + 1; 3904 | } 3905 | } 3906 | } 3907 | 3908 | // ------------------ YUV420 overlay hack ------------- 3909 | // ------------------ YUV420 overlay hack ------------- 3910 | // ------------------ YUV420 overlay hack ------------- 3911 | 3912 | 3913 | 3914 | // ------------------ alsa recording ------------------ 3915 | // ------------------ alsa recording ------------------ 3916 | // ------------------ alsa recording ------------------ 3917 | 3918 | #ifdef HAVE_SOUND 3919 | 3920 | 3921 | short audio_buf[128]; 3922 | snd_pcm_t *audio_capture_handle; 3923 | // const char *audio_device = "plughw:0,0"; 3924 | // const char *audio_device = "hw:CARD=U0x46d0x991,DEV=0"; 3925 | const char *audio_device = "default"; 3926 | // sysdefault:CARD 3927 | 3928 | void record_from_sound_device() 3929 | { 3930 | int i; 3931 | int err; 3932 | 3933 | for (i = 0; i < 10; ++i) 3934 | { 3935 | if ((err = snd_pcm_readi(audio_capture_handle, audio_buf, 128)) != 128) 3936 | { 3937 | dbg(9, "read from audio interface failed (%s)\n", snd_strerror(err)); 3938 | } 3939 | } 3940 | } 3941 | 3942 | void close_sound_device() 3943 | { 3944 | snd_pcm_close(audio_capture_handle); 3945 | } 3946 | 3947 | void init_sound_device() 3948 | { 3949 | int i; 3950 | int err; 3951 | snd_pcm_hw_params_t *hw_params; 3952 | 3953 | if ((err = snd_pcm_open(&audio_capture_handle, audio_device, SND_PCM_STREAM_CAPTURE, 0)) < 0) 3954 | { 3955 | dbg(9, "cannot open audio device %s (%s)\n", 3956 | audio_device, 3957 | snd_strerror(err)); 3958 | //exit (1); 3959 | } 3960 | 3961 | if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 3962 | { 3963 | dbg(9, "cannot allocate hardware parameter structure (%s)\n", 3964 | snd_strerror(err)); 3965 | //exit (1); 3966 | } 3967 | 3968 | if ((err = snd_pcm_hw_params_any(audio_capture_handle, hw_params)) < 0) 3969 | { 3970 | dbg(9, "cannot initialize hardware parameter structure (%s)\n", 3971 | snd_strerror(err)); 3972 | //exit (1); 3973 | } 3974 | 3975 | if ((err = snd_pcm_hw_params_set_access(audio_capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 3976 | { 3977 | dbg(9, "cannot set access type (%s)\n", 3978 | snd_strerror(err)); 3979 | //exit (1); 3980 | } 3981 | 3982 | if ((err = snd_pcm_hw_params_set_format(audio_capture_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) 3983 | { 3984 | dbg(9, "cannot set sample format (%s)\n", 3985 | snd_strerror(err)); 3986 | //exit (1); 3987 | } 3988 | 3989 | unsigned int actualRate = 44100; 3990 | dbg(9, "sound: wanted audio rate:%d\n", actualRate); 3991 | 3992 | if ((err = snd_pcm_hw_params_set_rate_near(audio_capture_handle, hw_params, &actualRate, 0)) < 0) 3993 | { 3994 | dbg(9, "cannot set sample rate (%s)\n", 3995 | snd_strerror(err)); 3996 | //exit (1); 3997 | } 3998 | 3999 | dbg(9, "sound: got audio rate:%d\n", actualRate); 4000 | 4001 | // 1 -> mono, 2 -> stereo 4002 | if ((err = snd_pcm_hw_params_set_channels(audio_capture_handle, hw_params, 1)) < 0) 4003 | { 4004 | dbg(9, "cannot set channel count (%s)\n", 4005 | snd_strerror(err)); 4006 | //exit (1); 4007 | } 4008 | 4009 | if ((err = snd_pcm_hw_params(audio_capture_handle, hw_params)) < 0) 4010 | { 4011 | dbg(9, "cannot set parameters (%s)\n", 4012 | snd_strerror(err)); 4013 | //exit (1); 4014 | } 4015 | 4016 | snd_pcm_hw_params_free(hw_params); 4017 | 4018 | if ((err = snd_pcm_prepare(audio_capture_handle)) < 0) 4019 | { 4020 | dbg(9, "cannot prepare audio interface for use (%s)\n", 4021 | snd_strerror(err)); 4022 | //exit (1); 4023 | } 4024 | } 4025 | 4026 | #endif 4027 | 4028 | // ------------------ alsa recording ------------------ 4029 | // ------------------ alsa recording ------------------ 4030 | // ------------------ alsa recording ------------------ 4031 | 4032 | 4033 | void sigint_handler(int signo) 4034 | { 4035 | if (signo == SIGINT) 4036 | { 4037 | printf("received SIGINT, pid=%d\n", getpid()); 4038 | tox_loop_running = 0; 4039 | } 4040 | } 4041 | 4042 | int main(int argc, char *argv[]) 4043 | { 4044 | global_want_restart = 0; 4045 | global_video_active = 0; 4046 | global_send_first_frame = 0; 4047 | my_last_offline_timestamp = -1; 4048 | my_last_online_timestamp = -1; 4049 | // valid audio bitrates: [ bit_rate < 6 || bit_rate > 510 ] 4050 | global_audio_bit_rate = DEFAULT_GLOBAL_AUD_BITRATE; 4051 | global_video_bit_rate = DEFAULT_GLOBAL_VID_BITRATE; 4052 | video_high = 0; 4053 | logfile = fopen(log_filename, "wb"); 4054 | setvbuf(logfile, NULL, _IONBF, 0); 4055 | v4l2_device = malloc(400); 4056 | memset(v4l2_device, 0, 400); 4057 | snprintf(v4l2_device, 399, "%s", "/dev/video0"); 4058 | int aflag = 0; 4059 | char *cvalue = NULL; 4060 | int index; 4061 | int opt; 4062 | const char *short_opt = "hvd:tT23b:fq:"; 4063 | struct option long_opt[] = 4064 | { 4065 | {"help", no_argument, NULL, 'h'}, 4066 | {"version", no_argument, NULL, 'v'}, 4067 | {"videodevice", required_argument, NULL, 'd'}, 4068 | {NULL, 0, NULL, 0 } 4069 | }; 4070 | 4071 | while ((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) 4072 | { 4073 | switch (opt) 4074 | { 4075 | case -1: /* no more arguments */ 4076 | case 0: /* long options toggles */ 4077 | break; 4078 | 4079 | case 'a': 4080 | aflag = 1; 4081 | break; 4082 | 4083 | case '2': 4084 | switch_nodelist_2 = 1; 4085 | break; 4086 | 4087 | case '3': 4088 | switch_nodelist_2 = 2; 4089 | break; 4090 | 4091 | case 't': 4092 | switch_tcponly = 1; 4093 | break; 4094 | 4095 | case 'T': 4096 | use_tor = 1; 4097 | break; 4098 | 4099 | case 'f': 4100 | video_high = 1; 4101 | break; 4102 | 4103 | case 'q': 4104 | RC_MAX_QUANTIZER = (int32_t)atoi(optarg); 4105 | dbg(3, "Using max quantizer: %d\n", (int)RC_MAX_QUANTIZER); 4106 | break; 4107 | 4108 | case 'd': 4109 | snprintf(v4l2_device, 399, "%s", optarg); 4110 | // printf("Using Videodevice: %s\n", v4l2_device); 4111 | dbg(3, "Using Videodevice: %s\n", v4l2_device); 4112 | break; 4113 | 4114 | case 'b': 4115 | DEFAULT_GLOBAL_VID_BITRATE = (uint32_t)atoi(optarg); 4116 | dbg(3, "Using Videobitrate: %d\n", (int)DEFAULT_GLOBAL_VID_BITRATE); 4117 | global_video_bit_rate = DEFAULT_GLOBAL_VID_BITRATE; 4118 | break; 4119 | 4120 | case 'v': 4121 | printf("ToxCam version: %s\n", global_version_string); 4122 | 4123 | if (logfile) 4124 | { 4125 | fclose(logfile); 4126 | logfile = NULL; 4127 | } 4128 | 4129 | return (0); 4130 | break; 4131 | 4132 | case 'h': 4133 | printf("Usage: %s [OPTIONS]\n", argv[0]); 4134 | printf(" -d, --videodevice devicefile file\n"); 4135 | printf(" -b bitrate video bitrate in kbit/s\n"); 4136 | printf(" -q quality 20 - 65 (20 is super high)\n"); 4137 | printf(" -f use 720p video mode\n"); 4138 | printf(" -t, tcp only mode\n"); 4139 | printf(" -T, use TOR as Relay\n"); 4140 | printf(" -2, use alternate bootnode list\n"); 4141 | printf(" -3, use only nodes.tox.chat as bootnode\n"); 4142 | printf(" -v, --version show version\n"); 4143 | printf(" -h, --help print this help and exit\n"); 4144 | printf("\n"); 4145 | 4146 | if (logfile) 4147 | { 4148 | fclose(logfile); 4149 | logfile = NULL; 4150 | } 4151 | 4152 | return (0); 4153 | 4154 | case ':': 4155 | case '?': 4156 | fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); 4157 | 4158 | if (logfile) 4159 | { 4160 | fclose(logfile); 4161 | logfile = NULL; 4162 | } 4163 | 4164 | return (-2); 4165 | 4166 | default: 4167 | fprintf(stderr, "%s: invalid option -- %c\n", argv[0], opt); 4168 | fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); 4169 | 4170 | if (logfile) 4171 | { 4172 | fclose(logfile); 4173 | logfile = NULL; 4174 | } 4175 | 4176 | return (-2); 4177 | } 4178 | } 4179 | 4180 | Tox *tox = create_tox(); 4181 | global_start_time = time(NULL); 4182 | const char *name = "ToxCam"; 4183 | tox_self_set_name(tox, (uint8_t *)name, strlen(name), NULL); 4184 | const char *status_message = "This is your ToxCam"; 4185 | tox_self_set_status_message(tox, (uint8_t *)status_message, strlen(status_message), NULL); 4186 | Friends.max_idx = 0; 4187 | bootstrap(tox); 4188 | print_tox_id(tox); 4189 | // init callbacks ---------------------------------- 4190 | tox_callback_friend_request(tox, friend_request_cb); 4191 | tox_callback_friend_message(tox, friend_message_cb); 4192 | tox_callback_friend_status(tox, on_tox_friend_status); 4193 | tox_callback_self_connection_status(tox, self_connection_status_cb); 4194 | tox_callback_file_chunk_request(tox, on_file_chunk_request); 4195 | tox_callback_file_recv_control(tox, on_file_control); 4196 | tox_callback_file_recv(tox, on_file_recv); 4197 | tox_callback_file_recv_chunk(tox, on_file_recv_chunk); 4198 | // init callbacks ---------------------------------- 4199 | // init toxutil callbacks ---------------------------------- 4200 | #ifdef TOX_HAVE_TOXUTIL 4201 | tox_utils_callback_friend_connection_status(tox, friendlist_onConnectionChange); 4202 | tox_callback_friend_connection_status(tox, tox_utils_friend_connection_status_cb); 4203 | tox_callback_friend_lossless_packet(tox, tox_utils_friend_lossless_packet_cb); 4204 | #else 4205 | tox_callback_friend_connection_status(tox, friendlist_onConnectionChange); 4206 | #endif 4207 | // init toxutil callbacks ---------------------------------- 4208 | update_savedata_file(tox); 4209 | load_friendlist(tox); 4210 | char path[300]; 4211 | snprintf(path, sizeof(path), "%s", my_avatar_filename); 4212 | int len = strlen(path) - 1; 4213 | avatar_set(tox, path, len); 4214 | // -------- try to go online -------- 4215 | long long unsigned int cur_time = time(NULL); 4216 | uint8_t off = 1; 4217 | long long loop_counter = 0; 4218 | 4219 | while (1) 4220 | { 4221 | tox_iterate(tox, NULL); 4222 | usleep(tox_iteration_interval(tox) * 1000); 4223 | 4224 | if (tox_self_get_connection_status(tox) && off) 4225 | { 4226 | dbg(2, "Tox online, took %llu seconds\n", time(NULL) - cur_time); 4227 | off = 0; 4228 | break; 4229 | } 4230 | 4231 | c_sleep(20); 4232 | loop_counter++; 4233 | 4234 | if (loop_counter > (50 * 20)) 4235 | { 4236 | loop_counter = 0; 4237 | // if not yet online, bootstrap every 20 seconds 4238 | dbg(2, "Tox NOT online yet, bootstrapping again\n"); 4239 | bootstrap(tox); 4240 | } 4241 | } 4242 | 4243 | // -------- try to go online -------- 4244 | TOXAV_ERR_NEW rc; 4245 | dbg(2, "new Tox AV\n"); 4246 | mytox_av = toxav_new(tox, &rc); 4247 | 4248 | if (rc != TOXAV_ERR_NEW_OK) 4249 | { 4250 | dbg(0, "Error at toxav_new: %d\n", rc); 4251 | } 4252 | 4253 | CallControl mytox_CC; 4254 | memset(&mytox_CC, 0, sizeof(CallControl)); 4255 | // init AV callbacks ------------------------------- 4256 | toxav_callback_call(mytox_av, t_toxav_call_cb, &mytox_CC); 4257 | toxav_callback_call_state(mytox_av, t_toxav_call_state_cb, &mytox_CC); 4258 | //toxav_callback_bit_rate_status(mytox_av, t_toxav_bit_rate_status_cb, &mytox_CC); 4259 | toxav_callback_video_receive_frame(mytox_av, t_toxav_receive_video_frame_cb, &mytox_CC); 4260 | toxav_callback_audio_receive_frame(mytox_av, t_toxav_receive_audio_frame_cb, &mytox_CC); 4261 | // init AV callbacks ------------------------------- 4262 | // start toxav thread ------------------------------ 4263 | pthread_t tid[2]; // 0 -> toxav_iterate thread, 1 -> video send thread 4264 | // start toxav thread ------------------------------ 4265 | toxav_iterate_thread_stop = 0; 4266 | 4267 | if (pthread_create(&(tid[0]), NULL, thread_av, (void *)mytox_av) != 0) 4268 | { 4269 | dbg(0, "AV iterate Thread create failed"); 4270 | } 4271 | else 4272 | { 4273 | pthread_setname_np(tid[0], "t_av"); 4274 | dbg(2, "AV iterate Thread successfully created"); 4275 | } 4276 | 4277 | toxav_video_thread_stop = 0; 4278 | 4279 | if (pthread_create(&(tid[1]), NULL, thread_video_av, (void *)mytox_av) != 0) 4280 | { 4281 | dbg(0, "AV video Thread create failed"); 4282 | } 4283 | else 4284 | { 4285 | dbg(2, "AV video Thread successfully created"); 4286 | pthread_setname_np(tid[1], "t_video_av"); 4287 | } 4288 | 4289 | // start toxav thread ------------------------------ 4290 | // start audio recoding stuff ---------------------- 4291 | #ifdef HAVE_SOUND 4292 | init_sound_device(); 4293 | record_from_sound_device(); 4294 | close_sound_device(); 4295 | #endif 4296 | // start audio recoding stuff ---------------------- 4297 | tox_loop_running = 1; 4298 | signal(SIGINT, sigint_handler); 4299 | pthread_setname_np(pthread_self(), "t_main"); 4300 | 4301 | while (tox_loop_running) 4302 | { 4303 | tox_iterate(tox, NULL); 4304 | // usleep(tox_iteration_interval(tox) * 1000); 4305 | usleep(5 * 1000); // hardcode to 5ms 4306 | #ifdef CHANGE_NOSPAM_REGULARLY 4307 | 4308 | if ((uint32_t)(global_last_change_nospam_ts + CHANGE_NOSPAM_REGULAR_INTERVAL_SECS) < (uint32_t)get_unix_time()) 4309 | { 4310 | change_nospam_to_new_random_value(tox); 4311 | } 4312 | 4313 | #endif 4314 | 4315 | if (global_want_restart == 1) 4316 | { 4317 | // need to restart me! 4318 | break; 4319 | } 4320 | else 4321 | { 4322 | check_online_status(tox); 4323 | check_dir(tox); 4324 | check_friends_dir(tox); 4325 | } 4326 | } 4327 | 4328 | kill_all_file_transfers(tox); 4329 | close_cam(); 4330 | toxav_kill(mytox_av); 4331 | #ifdef TOX_HAVE_TOXUTIL 4332 | tox_utils_kill(tox); 4333 | #else 4334 | tox_kill(tox); 4335 | #endif 4336 | 4337 | if (logfile) 4338 | { 4339 | fclose(logfile); 4340 | logfile = NULL; 4341 | } 4342 | 4343 | return 0; 4344 | } 4345 | 4346 | -------------------------------------------------------------------------------- /toxcam/update_from_ci.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | ##################################################### 4 | # update toxcam binary from Circle CI (master branch) 5 | ##################################################### 6 | 7 | cd $(dirname "$0") 8 | 9 | pkill toxcam_static # will stop toxcam 10 | cp -av toxcam_static toxcam_static__BACKUP 11 | wget -O toxcam_static 'https://circleci.com/api/v1/project/zoff99/ToxCam/latest/artifacts/0/$CIRCLE_ARTIFACTS/RASPI/toxcam_static?filter=successful&branch=master' 12 | chmod u+rwx toxcam_static 13 | 14 | --------------------------------------------------------------------------------