├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── 3rdparty ├── memlogger.cpp └── memlogger.h ├── CMakeLists.txt ├── QUOTES.md ├── README.md ├── TODO ├── collect.logs ├── linux64-mshd_use_after_free.txt ├── logweave.rb ├── session1 │ ├── local │ ├── regserver │ ├── remote │ └── weaved └── session2 │ ├── local │ ├── remote │ └── weaved ├── demos ├── CMakeLists.txt └── opus-streaming.cpp ├── docs ├── IMdesign │ ├── 01_HRbox_UI_Details_Tab.png │ ├── 02_HRbox_UI_Documents_Tab.png │ ├── 03_HRbox_UI_Chat_Tab.png │ ├── 04_HRbox_UI_Add_People_Dropdown.png │ ├── 05_HRbox_UI_Add_Box_Dropdown.png │ ├── 9131ec81ea760ffbbbc286f4c64ef25d-d4or9n3.jpg │ ├── Dream_IM_main_window_by_chanq.png │ ├── Windows_Live_Concepts_3_by_Interfacor.jpg │ └── chat___messenger_concept_by_tavi004-d5qa2bv.jpg ├── callwindow.ep ├── layers.txt └── uvvy.png ├── filesyncbox ├── CMakeLists.txt ├── include │ └── filesyncbox │ │ ├── abstract_chunk_reader.h │ │ ├── chunk.h │ │ ├── chunk_protocol.h │ │ ├── chunk_reader.h │ │ ├── chunk_share.h │ │ ├── filesync_service.h │ │ ├── index.h │ │ └── internal │ │ └── chunk_peer.h ├── lib │ ├── CMakeLists.txt │ ├── abstract_chunk_reader.cpp │ ├── chunk_peer.cpp │ ├── chunk_share.cpp │ └── filesync_service.cpp └── tests │ └── CMakeLists.txt ├── mettanode.sublime-project ├── naming ├── CMakeLists.txt ├── README.md ├── TODO └── include │ └── naming │ └── peer.h ├── playground ├── CMakeLists.txt ├── data │ ├── packet-to-playback-local.dat │ ├── packet-to-playback-remote.dat │ ├── packet-to-receive-local.dat │ ├── packet-to-receive-log-local.dat │ ├── packet-to-receive-log-remote.dat │ ├── packet-to-receive-remote.dat │ ├── plot-delay-log.png │ ├── plot-delay-play.png │ ├── plot-delay-play.svg │ ├── plot-delay.png │ ├── plot-delay.svg │ └── timing.plt ├── kextest │ ├── CMakeLists.txt │ ├── cryptest.cpp │ └── kextest.cpp ├── main.cpp ├── packetsizes.plt ├── screencaster.cpp ├── sstreams │ ├── CMakeLists.txt │ └── main.cpp ├── udpsend.cpp ├── udpsend.h ├── unlogger.cpp └── vpxenc.c ├── scripts ├── asan_symbolicate.rb ├── asioviz.pl ├── rpath.sh └── travis_build.sh ├── ui ├── CMakeLists.txt ├── README.md ├── client_utils.cpp ├── client_utils.h ├── doc │ ├── Makefile │ ├── chat_and_messaging.md │ ├── lib.conf │ ├── livemedia.dot │ └── mainpage.dox ├── gui │ ├── chat.cc │ ├── chat.h │ ├── chathistory.cpp │ ├── chathistory.h │ ├── gui.rc │ ├── logarea.cc │ ├── logarea.h │ ├── logwindow.cpp │ ├── logwindow.h │ ├── main.cc │ ├── main.h │ ├── save.cc │ ├── save.h │ ├── search.cc │ ├── search.h │ ├── settings.cc │ ├── settings.h │ ├── sounds │ │ └── bnb.s16 │ ├── view.cc │ └── view.h ├── lib │ ├── CMakeLists.txt │ ├── action.cc │ ├── action.h │ ├── adler32.cc │ ├── adler32.h │ ├── arch.cc │ ├── arch.h │ ├── audio.cc │ ├── audio.h │ ├── chunk.cc │ ├── chunk.h │ ├── crypt.cc │ ├── crypt.h │ ├── env.h │ ├── file.cc │ ├── file.h │ ├── index.cc │ ├── index.h │ ├── opaque.cc │ ├── opaque.h │ ├── peer.cc │ ├── peer.h │ ├── rcsum.cc │ ├── rcsum.h │ ├── scan.cc │ ├── scan.h │ ├── share.cc │ ├── share.h │ ├── store.cc │ ├── store.h │ ├── update.cc │ ├── update.h │ ├── voice.cc │ └── voice.h ├── qt5 │ ├── CMakeLists.txt │ ├── CallService.cpp │ ├── CallService.h │ ├── CallWindow.cpp │ ├── CallWindow.h │ ├── ChatHistory.cpp │ ├── ChatHistory.h │ ├── ChatModel.h │ ├── ChatWindow.cpp │ ├── ChatWindow.h │ ├── ContactModel.cpp │ ├── ContactModel.h │ ├── HostState.h │ ├── MainWindow.cpp │ ├── MainWindow.h │ ├── MainWindow.ui │ ├── Meter.cpp │ ├── Meter.h │ ├── OSXInfo.plist │ ├── ProfileEditor.cpp │ ├── ProfileEditor.h │ ├── ProfileEditor.ui │ ├── QmlBasedWindow.cpp │ ├── QmlBasedWindow.h │ ├── SingleCallWidget.ui │ ├── XcpApplication.cpp │ ├── XcpApplication.h │ ├── img │ │ ├── attribution.txt │ │ ├── mettanode.icns │ │ ├── mettanode.png │ │ ├── status-offline-crossed.png │ │ ├── status-offline.png │ │ ├── status-online-33%.png │ │ ├── status-online-66%.png │ │ └── status-online.png │ ├── macosx.mm │ ├── macsupport.h │ ├── main.cpp │ ├── quick │ │ ├── CallWindow.qml │ │ ├── ChatMessage.qml │ │ ├── ChatModel.qml │ │ ├── ChatWindow.qml │ │ ├── ContactList.qml │ │ ├── ContactModel.qml │ │ ├── MainWindow.qml │ │ └── UserCard.qml │ └── ui.qrc ├── traverse_nat.cpp └── traverse_nat.h └── voicebox ├── CMakeLists.txt ├── doc └── classes.txt ├── include └── voicebox │ ├── audio_hardware.h │ ├── audio_service.h │ ├── audio_sink.h │ ├── audio_source.h │ ├── audio_stream.h │ ├── file_read_sink.h │ ├── jitterbuffer.h │ ├── opus_decode_sink.h │ ├── opus_encode_sink.h │ ├── packet_sink.h │ ├── packet_source.h │ ├── packetizer.h │ ├── plotfile.h │ ├── rtaudio_sink.h │ └── rtaudio_source.h ├── lib ├── CMakeLists.txt ├── audio_hardware.cpp ├── audio_service.cpp ├── audio_sink.cpp ├── audio_source.cpp ├── audio_stream.cpp ├── file_read_sink.cpp ├── jitterbuffer.cpp ├── opus_decode_sink.cpp ├── opus_encode_sink.cpp ├── packet_sink.cpp ├── packet_source.cpp ├── packetizer.cpp ├── rtaudio_sink.cpp └── rtaudio_source.cpp └── tests ├── CMakeLists.txt ├── test_file_playback.cpp ├── test_file_send.cpp ├── test_jitterbuffer.cpp └── test_sim_transmit.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # We'll base on defaults from the WebKit style. 3 | BasedOnStyle: WebKit 4 | IndentWidth: 4 5 | TabWidth: 4 6 | UseTab: Never 7 | ColumnLimit: 100 8 | --- 9 | Language: Cpp 10 | Standard: Cpp11 11 | # Force pointers to the type for C++. 12 | DerivePointerAlignment: false 13 | PointerAlignment: Left 14 | AllowShortBlocksOnASingleLine: false 15 | AlignAfterOpenBracket: true 16 | AlignConsecutiveAssignments: true 17 | AllowAllParametersOfDeclarationOnNextLine: false 18 | AllowShortFunctionsOnASingleLine: Inline 19 | AllowShortCaseLabelsOnASingleLine: true 20 | AlwaysBreakAfterDefinitionReturnType: TopLevel 21 | AlwaysBreakBeforeMultilineStrings: true 22 | AlwaysBreakTemplateDeclarations: true 23 | AlignOperands: true 24 | AlignTrailingComments: true 25 | BinPackArguments: false 26 | BinPackParameters: false 27 | BreakBeforeBinaryOperators: NonAssignment 28 | BreakBeforeBraces: Mozilla 29 | BreakBeforeTernaryOperators: false 30 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 31 | Cpp11BracedListStyle: true 32 | IndentCaseLabels: true 33 | MaxEmptyLinesToKeep: 1 34 | NamespaceIndentation: None 35 | SpaceAfterCStyleCast: false 36 | SpacesInCStyleCastParentheses: false 37 | SpaceBeforeParens: ControlStatements 38 | SpaceInEmptyParentheses: false 39 | SpacesInAngles: false 40 | SpacesInContainerLiterals: true 41 | SpacesInParentheses: false 42 | SpacesInSquareBrackets: false 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lai 11 | *.la 12 | *.a 13 | 14 | # Build directory 15 | _build_ 16 | .DS_Store 17 | 18 | ui/doc/html 19 | sst/doc/html 20 | mettanode.sublime-workspace 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/endian"] 2 | path = 3rdparty/endian 3 | url = git@github.com:berkus/endian.git 4 | [submodule "sss"] 5 | path = sss 6 | url = git@github.com:berkus/libsss.git 7 | branch = master 8 | [submodule "cmake"] 9 | path = cmake 10 | url = git@github.com:berkus/cmake-setup.git 11 | branch = master 12 | [submodule "travis-ci"] 13 | path = travis-ci 14 | url = git@github.com:berkus/travis-scripts.git 15 | branch = master 16 | [submodule "routing"] 17 | path = routing 18 | url = git@github.com:berkus/librouting.git 19 | branch = master 20 | [submodule "krypto"] 21 | path = krypto 22 | url = git@github.com:berkus/libkrypto.git 23 | [submodule "nat"] 24 | path = nat 25 | url = git@github.com:berkus/libnat.git 26 | branch = master 27 | [submodule "arsenal"] 28 | path = arsenal 29 | url = git@github.com:berkus/libarsenal.git 30 | branch = master 31 | [submodule "3rdparty/dir_monitor"] 32 | path = 3rdparty/dir_monitor 33 | url = git@github.com:berkus/dir_monitor.git 34 | branch = master 35 | [submodule "3rdparty/metakit"] 36 | path = 3rdparty/metakit 37 | url = git@github.com:berkus/metakit.git 38 | branch = master 39 | [submodule "3rdparty/opus"] 40 | path = 3rdparty/opus 41 | url = git@github.com:berkus/opus.git 42 | branch = master 43 | [submodule "3rdparty/rtaudio"] 44 | path = 3rdparty/rtaudio 45 | url = git@github.com:berkus/rtaudio.git 46 | branch = master 47 | [submodule "3rdparty/sodiumpp"] 48 | path = 3rdparty/sodiumpp 49 | url=git@github.com:berkus/sodiumpp.git 50 | branch = master 51 | [submodule "uia"] 52 | path = uia 53 | url = git@github.com:berkus/libuia.git 54 | branch = master 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: clang 4 | 5 | os: 6 | - linux 7 | - osx 8 | 9 | env: 10 | - BUILD_TYPE=RelWithDebInfo 11 | - BUILD_TYPE=Release 12 | - BUILD_TYPE=Debug 13 | 14 | cache: apt 15 | 16 | before_install: 17 | - sh ./travis-ci/travis_install_packages.sh 18 | - sh ./travis-ci/travis_build_ninja.sh 19 | - sh ./travis-ci/travis_build_libcxx.sh 20 | - sh ./travis-ci/travis_build_boost.sh 21 | 22 | install: sh ./scripts/travis_build.sh 23 | script: sh ./scripts/travis_tests.sh 24 | 25 | branches: 26 | only: 27 | - master 28 | 29 | notifications: 30 | recipients: 31 | - berkus@atta-metta.net 32 | email: 33 | on_success: change 34 | on_failure: always 35 | -------------------------------------------------------------------------------- /3rdparty/memlogger.cpp: -------------------------------------------------------------------------------- 1 | #include "memlogger.h" 2 | 3 | namespace CircularLogger 4 | { 5 | Event g_events[BUFFER_SIZE]; 6 | LONG g_pos = -1; 7 | } 8 | -------------------------------------------------------------------------------- /3rdparty/memlogger.h: -------------------------------------------------------------------------------- 1 | // from http://preshing.com/20120522/lightweight-in-memory-logging 2 | #pragma once 3 | 4 | namespace CircularLogger 5 | { 6 | struct Event 7 | { 8 | DWORD tid; // Thread ID 9 | const char* msg; // Message string 10 | DWORD param; // A parameter which can mean anything you want 11 | }; 12 | 13 | static const int BUFFER_SIZE = 65536; // Must be a power of 2 14 | extern Event g_events[BUFFER_SIZE]; 15 | extern LONG g_pos; 16 | 17 | inline void Log(const char* msg, DWORD param) 18 | { 19 | // Get next event index 20 | LONG index = _InterlockedIncrement(&g_pos); 21 | // Write an event at this index 22 | Event* e = g_events + (index & (BUFFER_SIZE - 1)); // Wrap to buffer size 23 | e->tid = ((DWORD*) __readfsdword(24))[9]; // Get thread ID 24 | e->msg = msg; 25 | e->param = param; 26 | } 27 | } 28 | 29 | #define TRACE(m, p) CircularLogger::Log(m, p) 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(mettanode) 4 | include(CTest) 5 | 6 | include(cmake/setup.cmake) 7 | 8 | # Export compile commands for ST3 clang plugin. 9 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 10 | 11 | # Set flag for submodules build 12 | set(BUILD_IN_UVVY ON) 13 | 14 | if (APPLE) 15 | set(GUI_TYPE MACOSX_BUNDLE) 16 | endif (APPLE) 17 | 18 | include_directories(3rdparty/endian) # Additional unofficial boost libraries 19 | include_directories(3rdparty) # sodiumpp 20 | 21 | include_directories(arsenal/include) # For routing access to sss host and link_receiver 22 | include_directories(krypto/include) # For cryptographic primitives 23 | include_directories(uia/include) 24 | include_directories(sss/include) # For routing access to sss host and link_receiver 25 | include_directories(routing/include) # For sss access to routing client 26 | include_directories(voicebox/include) 27 | 28 | add_subdirectory(3rdparty/sodiumpp) 29 | add_subdirectory(arsenal) 30 | #add_subdirectory(krypto) 31 | #add_subdirectory(voicebox) 32 | #add_subdirectory(filesyncbox) 33 | 34 | add_subdirectory(uia) 35 | add_subdirectory(sss) 36 | add_subdirectory(routing) 37 | #add_subdirectory(naming) 38 | #add_subdirectory(nat) 39 | 40 | #add_subdirectory(3rdparty/opus) 41 | #add_subdirectory(3rdparty/rtaudio) 42 | #add_subdirectory(3rdparty/metakit) 43 | #add_subdirectory(ui) 44 | 45 | #add_subdirectory(demos) 46 | #add_subdirectory(playground) 47 | -------------------------------------------------------------------------------- /QUOTES.md: -------------------------------------------------------------------------------- 1 | # Some interesting quotations from other projects 2 | 3 | AB[C](#c)DEF[G](#g)HIJKLM[N](#n)OPQRSTUVWXYZ 4 | 5 | ## C 6 | 7 | ### CCNx 8 | 9 | > http://www.ccnx.org/ 10 | 11 | ### Cryptosphere 12 | 13 | > [notes on security and crypto](https://github.com/cryptosphere/cryptosphere/wiki/Protocol) - CurveCP and DTLS 14 | 15 | > [CS trello taskboard](https://trello.com/b/WMKsvLOW/cryptosphere) 16 | 17 | ## G 18 | 19 | ### [GNUnet](https://gnunet.org) 20 | 21 | > Anonymity is provided by making messages originating from a peer indistinguishable from messages that the peer is routing. All peers act as routers and use link-encrypted connections with stable bandwidth utilization to communicate with each other. 22 | 23 | * The purpose of this service is to manage private keys that 24 | * represent the various egos/pseudonyms/identities of a GNUnet user. 25 | 26 | > Pseudonyms in GNUnet are essentially public-private (RSA) key pairs that allow a GNUnet user to maintain an identity (which may or may not be detached from his real-life identity). GNUnet's pseudonyms are not file-sharing specific --- and they will likely be used by many GNUnet applications where a user identity is required. 27 | 28 | > Note that a pseudonym is NOT bound to a GNUnet peer. There can be multiple pseudonyms for a single user, and users could (theoretically) share the private pseudonym keys (currently only out-of-band by knowing which files to copy around). 29 | 30 | > *MN note*: UI representation: similar to Urbit, but combine all sections together, e.g. have a subsection 31 | for lord/lady, a subsection for punk, subsection for anon, subsection for org and so on. 32 | 33 | * GNUnet itself never interprets the contents of shared files, except when using GNU libextractor to obtain keywords. 34 | 35 | > Depending on the peer's configuration, GNUnet peers migrate content between peers. Content in this sense are individual blocks of a file, not necessarily entire files. When peers run out of space (due to local publishing operations or due to migration of content from other peers), blocks sometimes need to be discarded. GNUnet first always discards expired blocks (typically, blocks are published with an expiration of about two years in the future; this is another option). If there is still not enough space, GNUnet discards the blocks with the lowest priority. The priority of a block is decided by its popularity (in terms of requests from peers we trust) and, in case of blocks published locally, the base-priority that was specified by the user when the block was published initially. 36 | 37 | > When peers migrate content to other systems, the replication level of a block is used to decide which blocks need to be migrated most urgently. GNUnet will always push the block with the highest replication level into the network, and then decrement the replication level by one. If all blocks reach replication level zero, the selection is simply random. 38 | 39 | > https://gnunet.org/gns 40 | 41 | > You can usually get the hash of your public key using blablabla. Alternatively, you can obtain a QR code with your zone key AND your pseudonym from gnunet-setup. The QR code is displayed in the GNS tab and can be stored to disk using the Save as button next to the image. 42 | 43 | ## N 44 | 45 | ### NaCl 46 | 47 | > NaCl provides a simple crypto_box function that does everything in one step. The function takes the sender's secret key, the recipient's public key, and a message, and produces an authenticated ciphertext. All objects are represented in wire format, as sequences of bytes suitable for transmission; the crypto_box function automatically handles all necessary conversions, initializations, etc. 48 | 49 | > All of the NaCl software is in the public domain. 50 | 51 | Not CC0, but probably bearable. 52 | 53 | [cmakeified version](https://github.com/cjdelisle/cnacl) 54 | 55 | cryptobox functions are handy (although throwing exceptions is a bit excess), also this may remove 56 | dependency on whole openssl. nacl should be much more manageable. 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MettaNode grabber prototype 2 | =========================== 3 | 4 | [![Join the chat at https://gitter.im/metta-systems/uvvy](https://badges.gitter.im/metta-systems/uvvy.svg)](https://gitter.im/metta-systems/uvvy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | MettaNode is a tool for fully decentralized communications - grab data you like and store it forever, share it with your friends, start chats, voice and video calls, form groups by interest, transparently keep all your notes between all of your devices; all based on a simple ideas of [UIA](http://pdos.csail.mit.edu/uia/). It is still young and only base transport protocol is done, work now continues on overlay routing network. 7 | 8 | Final target is to have a bunch of clients for desktop and mobile platforms (Win, Mac, Linux, Android, iOS) as well as own operating system implementation ([Metta](https://github.com/berkus/metta/wiki)) running together. 9 | 10 | Progress updates in [TODO](TODO). 11 | 12 | Dependencies 13 | ============ 14 | 15 | * C++14 (right now, only Clang and libc++) 16 | * [Qt5](http://qt-project.org/) (QtCore, QtNetwork; QtXml for UPnP; QtGui for demo apps) 17 | * [cmake](http://cmake.org/) 18 | * [boost](http://boost.org/) 19 | 20 | aptly 21 | ``` 22 | $ apt-get install git cmake clang libboost-test1.50-dev libqt4-dev libssl-dev libasound2-dev 23 | ``` 24 | 25 | Included in this repository: 26 | 27 | * [opus](http://opus-codec.org/) 28 | * [rtaudio](http://www.music.mcgill.ca/~gary/rtaudio/) 29 | * [miniupnpc](https://github.com/miniupnp/miniupnp/tree/master/miniupnpc) 30 | * [libnatpmp](http://thebends.googlecode.com/svn/trunk/nat/pmp) 31 | 32 | Typical config command 33 | ====================== 34 | ``` 35 | cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -G Ninja \ 36 | -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug 37 | ``` 38 | 39 | CI status 40 | ========= 41 | [![Build Status](https://secure.travis-ci.org/berkus/mettanode.png)](http://travis-ci.org/berkus/mettanode) 42 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/berkus/mettanode/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 43 | 44 | Authors 45 | ======= 46 | Design and development: 47 | [Stanislav Karchebny](http://exocortex.madfire.net) 48 | 49 | Code contributions: 50 | Bogdan Lytvynovsky 51 | 52 | Original SST and UIA development: 53 | [Bryan Ford](http://www.brynosaurus.com) 54 | 55 | -------------------------------------------------------------------------------- /collect.logs/logweave.rb: -------------------------------------------------------------------------------- 1 | require "time" 2 | 3 | leftLog = File.new(ARGV[0], "r") 4 | rightLog = File.new(ARGV[1], "r") 5 | 6 | readLeft = true 7 | readRight = true 8 | 9 | while readLeft or readRight 10 | leftLine = leftLog.gets if readLeft 11 | rightLine = rightLog.gets if readRight 12 | 13 | leftTimestamp = Time.iso8601(leftLine.split(" ")[0]).to_f if leftLine 14 | rightTimestamp = Time.iso8601(rightLine.split(" ")[0]).to_f if rightLine 15 | 16 | if leftLine and leftTimestamp <= rightTimestamp 17 | puts "L<< #{leftLine}" if leftLine 18 | readLeft = true 19 | readRight = false 20 | elsif rightLine 21 | puts ">>R #{rightLine}" if rightLine 22 | readLeft = false 23 | readRight = true 24 | else 25 | readLeft = false 26 | readRight = false 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /collect.logs/session1/regserver: -------------------------------------------------------------------------------- 1 | 2013-03-03T13:52:47 D: RegServer bound on port 8662 2 | 2013-03-03T13:54:09 D: Received 68 byte message from "192.168.1.64:8661" 3 | 2013-03-03T13:54:09 D: SST::RegServer(0x7fff5c813980) Insert1 4 | 2013-03-03T13:54:09 D: SST::RegServer(0x7fff5c813980) replyInsert1 challenge "CfOOQXAdzXYtqQ4t/FKwyAgtVBMtJYCGqxosfnx/VTw=" 5 | 2013-03-03T13:54:09 D: SST::RegServer(0x7fff5c813980) replyInsert1 sent to "192.168.1.64:8661" 6 | 2013-03-03T13:54:09 D: Received 584 byte message from "192.168.1.64:8661" 7 | 2013-03-03T13:54:09 D: SST::RegServer(0x7fff5c813980) Insert2 8 | 2013-03-03T13:54:09 D: Registering record for "FSMBOWOWWU6LK7TCNLQORDZKRC3NH5DG" at "192.168.1.64:8661" 9 | 2013-03-03T13:54:09 D: Inserted record for "FSMBOWOWWU6LK7TCNLQORDZKRC3NH5DG" at "192.168.1.64:8661" 10 | 2013-03-03T13:54:09 D: Received 96 byte message from "192.168.1.64:8661" 11 | 2013-03-03T13:54:09 D: Lookup with notify 12 | 2013-03-03T13:54:09 D: SST::RegServer(0x7fff5c813980) replyLookup 514 13 | 2013-03-03T13:54:10 D: Received 68 byte message from "192.168.1.80:8661" 14 | 2013-03-03T13:54:10 D: SST::RegServer(0x7fff5c813980) Insert1 15 | 2013-03-03T13:54:10 D: SST::RegServer(0x7fff5c813980) replyInsert1 challenge "Nk/Tn79oGpPZvxY2i85QlQYDZiuy50tJvm9q8/bsDcA=" 16 | 2013-03-03T13:54:10 D: SST::RegServer(0x7fff5c813980) replyInsert1 sent to "192.168.1.80:8661" 17 | 2013-03-03T13:54:10 D: Received 600 byte message from "192.168.1.80:8661" 18 | 2013-03-03T13:54:10 D: SST::RegServer(0x7fff5c813980) Insert2 19 | 2013-03-03T13:54:10 D: Registering record for "FQERZYRYJOUAYTLVKH3PBRBGUBT4IJZN" at "192.168.1.80:8661" 20 | 2013-03-03T13:54:10 D: Inserted record for "FQERZYRYJOUAYTLVKH3PBRBGUBT4IJZN" at "192.168.1.80:8661" 21 | 2013-03-03T13:54:10 D: Received 96 byte message from "192.168.1.80:8661" 22 | 2013-03-03T13:54:10 D: Lookup with notify 23 | 2013-03-03T13:54:10 D: SST::RegServer(0x7fff5c813980) replyLookup 514 24 | 2013-03-03T13:54:10 D: SST::RegServer(0x7fff5c813980) replyLookup 770 25 | 2013-03-03T13:54:15 D: Received 96 byte message from "192.168.1.64:8661" 26 | 2013-03-03T13:54:15 D: Lookup with notify 27 | 2013-03-03T13:54:15 D: SST::RegServer(0x7fff5c813980) replyLookup 514 28 | 2013-03-03T13:54:15 D: SST::RegServer(0x7fff5c813980) replyLookup 770 29 | 2013-03-03T13:54:15 D: Received 96 byte message from "192.168.1.64:8661" 30 | 2013-03-03T13:54:15 D: Lookup with notify 31 | 2013-03-03T13:54:15 D: SST::RegServer(0x7fff5c813980) replyLookup 514 32 | 2013-03-03T13:54:15 D: SST::RegServer(0x7fff5c813980) replyLookup 770 33 | 2013-03-03T13:54:18 D: Received 96 byte message from "192.168.1.64:8661" 34 | 2013-03-03T13:54:18 D: Lookup with notify 35 | 2013-03-03T13:54:18 D: SST::RegServer(0x7fff5c813980) replyLookup 514 36 | 2013-03-03T13:54:18 D: SST::RegServer(0x7fff5c813980) replyLookup 770 37 | 2013-03-03T13:56:14 D: Received 68 byte message from "192.168.1.80:8661" 38 | 2013-03-03T13:56:14 D: Received delete request 39 | 2013-03-03T13:56:14 D: ~RegRecord: deleting record for "FQERZYRYJOUAYTLVKH3PBRBGUBT4IJZN" 40 | 2013-03-03T13:56:20 D: Received 68 byte message from "192.168.1.64:8661" 41 | 2013-03-03T13:56:20 D: Received delete request 42 | 2013-03-03T13:56:20 D: ~RegRecord: deleting record for "FSMBOWOWWU6LK7TCNLQORDZKRC3NH5DG" 43 | -------------------------------------------------------------------------------- /demos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../nat/include ../ui) # For traverse_nat.h 2 | 3 | add_executable(opus-streaming opus-streaming.cpp) 4 | target_link_libraries(opus-streaming sss arsenal ${VOICEBOX_LIBS} opus rclient routing 5 | natclient nat upnpc krypto ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES}) 6 | install(TARGETS opus-streaming 7 | RUNTIME DESTINATION tools) 8 | -------------------------------------------------------------------------------- /docs/IMdesign/01_HRbox_UI_Details_Tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/01_HRbox_UI_Details_Tab.png -------------------------------------------------------------------------------- /docs/IMdesign/02_HRbox_UI_Documents_Tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/02_HRbox_UI_Documents_Tab.png -------------------------------------------------------------------------------- /docs/IMdesign/03_HRbox_UI_Chat_Tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/03_HRbox_UI_Chat_Tab.png -------------------------------------------------------------------------------- /docs/IMdesign/04_HRbox_UI_Add_People_Dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/04_HRbox_UI_Add_People_Dropdown.png -------------------------------------------------------------------------------- /docs/IMdesign/05_HRbox_UI_Add_Box_Dropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/05_HRbox_UI_Add_Box_Dropdown.png -------------------------------------------------------------------------------- /docs/IMdesign/9131ec81ea760ffbbbc286f4c64ef25d-d4or9n3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/9131ec81ea760ffbbbc286f4c64ef25d-d4or9n3.jpg -------------------------------------------------------------------------------- /docs/IMdesign/Dream_IM_main_window_by_chanq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/Dream_IM_main_window_by_chanq.png -------------------------------------------------------------------------------- /docs/IMdesign/Windows_Live_Concepts_3_by_Interfacor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/Windows_Live_Concepts_3_by_Interfacor.jpg -------------------------------------------------------------------------------- /docs/IMdesign/chat___messenger_concept_by_tavi004-d5qa2bv.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/IMdesign/chat___messenger_concept_by_tavi004-d5qa2bv.jpg -------------------------------------------------------------------------------- /docs/layers.txt: -------------------------------------------------------------------------------- 1 | Layers 2 | ====== 3 | 4 | 5 | Vertical - dependency on all below layers 6 | Horizontal - no dependency 7 | 8 | ``` 9 | 10 | sss 11 | 12 | routing naming 13 | 14 | uia nat 15 | 16 | sodiumpp arsenal 17 | 18 | ``` 19 | 20 | 21 | library breakdown 22 | 23 | uia contains 24 | - base socket operations 25 | - channel setup (key exchange/channel negotiation, channel-packet send and receive) 26 | 27 | nat contains 28 | - punching nat holes 29 | - support for maintaining a TURN/STUN server component 30 | 31 | routing contains 32 | - DHT network searches and updates 33 | 34 | sss adds 35 | - framing on top of channels 36 | - streams on top of framing 37 | 38 | -------------------------------------------------------------------------------- /docs/uvvy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/docs/uvvy.png -------------------------------------------------------------------------------- /filesyncbox/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(include) 2 | add_subdirectory(lib) 3 | 4 | if (BUILD_TESTING) 5 | add_subdirectory(tests) 6 | endif (BUILD_TESTING) 7 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/abstract_chunk_reader.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include "arsenal/byte_array.h" 13 | 14 | /** 15 | * Base interface for chunk data provider. 16 | * It supports only one operation: request chunk of data by its outer hash. 17 | * It can respond in one of two ways: by returning the data via on_got_data signal 18 | * or by giving up via on_no_data signal. You could also override virtual got_data() and 19 | * no_data() in your subclass to provide additional handling. 20 | */ 21 | class abstract_chunk_reader 22 | { 23 | protected: 24 | inline abstract_chunk_reader() {} 25 | 26 | /** 27 | * Submit a request to read a chunk. 28 | * For each such chunk request this class will send either 29 | * a got_data() or a no_data() signal for the requested chunk 30 | * (unless the chunk_reader gets destroyed first). 31 | * The caller can use a single chunk_reader object 32 | * to read multiple chunks simultaneously. 33 | */ 34 | void read_chunk(byte_array const& ohash); 35 | 36 | boost::signals2::signal on_got_data; 37 | boost::signals2::signal on_no_data; 38 | 39 | /** 40 | * The chunk reader calls these methods when a chunk arrives, 41 | * or when it gives up on finding a chunk. 42 | */ 43 | virtual void got_data(byte_array const& outer_hash, byte_array const& data) { 44 | on_got_data(outer_hash, data); 45 | } 46 | virtual void no_data(byte_array const& outer_hash) { 47 | on_no_data(outer_hash); 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/chunk.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | namespace filesyncbox { 12 | 13 | /** 14 | * A chunk is a piece of data identified by a combination of hashes. 15 | * Typical chunk sizes are 256k, 512k, 1M, 2M, 4M. 16 | * 17 | * A chunk is "positively matched" when all the hashes match search criteria. 18 | * A chunk is "partially matched" when at least one hash matches. 19 | * 20 | * Hashes list from gentoo SHA256 SHA512 WHIRLPOOL size ;-) 21 | */ 22 | class chunk 23 | { 24 | size_t size_factor_; // log2, e.g. 18 is 256k, 19 is 512k, 20 is 1M 25 | 26 | public: 27 | size_t size() const { 28 | return 1 << size_factor_; 29 | } 30 | 31 | // outer_hash() const; 32 | // inner_hash() const; 33 | size_t small_hash() const; 34 | }; 35 | 36 | } // filesyncbox namespace 37 | 38 | // Hash specialization for chunk 39 | namespace std { 40 | 41 | template<> 42 | struct hash : public std::unary_function 43 | { 44 | inline size_t operator()(filesyncbox::chunk const& a) const noexcept 45 | { 46 | return a.small_hash(); 47 | } 48 | }; 49 | 50 | } // namespace std 51 | 52 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/chunk_protocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "arsenal/byte_array.h" 12 | 13 | namespace sss { 14 | 15 | /** 16 | * Base pseudo-class to provide symbols shared by several chunk classes. 17 | */ 18 | class chunk_protocol 19 | { 20 | public: 21 | /// Chunk message codes 22 | enum msg_code : uint32_t 23 | { 24 | chunk_status_request = 0x101, 25 | chunk_request = 0x102, 26 | 27 | chunk_status_reply = 0x201, 28 | chunk_reply = 0x202, 29 | }; 30 | 31 | // Chunk status codes 32 | enum chunk_status : uint32_t 33 | { 34 | invalid_status = 0, 35 | online_chunk, // Have it immediately available 36 | offline_chunk, // Might be available with user effort 37 | unknown_chunk, // Don't know anything about it 38 | lost_chunk, // Used to have it, but lost 39 | }; 40 | 41 | struct request 42 | { 43 | byte_array const outer_hash; // Chunk identity 44 | 45 | set readers; // who wants it 46 | 47 | // Potential peers that might have this chunk. 48 | // A peer is included if we've sent it a status request, 49 | // or if it has positively reported having the chunk. 50 | set potentials; 51 | 52 | // Who knows what about this chunk. 53 | // We use the 'InvalidStatus' code here to mean 54 | // we've requested status from this peer but not received it. 55 | QHash status; 56 | 57 | 58 | inline request(byte_array const& ohash) : outer_hash(ohash) {} 59 | }; 60 | }; 61 | 62 | } // sss namespace 63 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/chunk_reader.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "filesyncbox/abstract_chunk_reader.h" 12 | 13 | class chunk_reader : public abstract_chunk_reader 14 | { 15 | inline chunk_reader() : abstract_chunk_reader() {} 16 | 17 | // signals: 18 | void got_data(byte_array const& outer_hash, byte_array const& data) override; 19 | void no_data(byte_array const& outer_hash) override; 20 | }; 21 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/chunk_share.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | /** 12 | * Single-instance class that handles chunk sharing in the social network. 13 | * 14 | * Updating a file tree is handled by simply sending a NewRoot message with the outerhash 15 | * of new tree root block, which in turn would contain a merkle tree of outerhashes 16 | * for sub-trees. These sub-trees are recursively requested and are either satisfied 17 | * from local storage or pulled from another node. 18 | */ 19 | class chunk_share : public peer_service, private chunk_protocol 20 | { 21 | /** 22 | * Outstanding chunk requests 23 | */ 24 | static std::map requests; 25 | 26 | /** 27 | * Maintain a persistent chunk query stream for each of our peers. 28 | */ 29 | static std::map peers; 30 | 31 | private: 32 | chunk_share(); 33 | 34 | public: 35 | static chunk_share *instance(); 36 | 37 | static inline void init() { (void)instance(); } 38 | 39 | static void request_chunk(abstract_chunk_reader *reader, byte_array const& ohash); 40 | 41 | private: 42 | static chunk_peer *peer(peer_id const& hostid, bool create); 43 | 44 | /** 45 | * Check all peers for chunks they might be able to download. 46 | */ 47 | static void check_peers(); 48 | 49 | /** 50 | * Check a particular request to see if it can still be satisfied; 51 | * otherwise delete it and send no_data() to all requestors. 52 | */ 53 | static void check_request(request *req); 54 | 55 | private: 56 | void got_out_stream_connected(stream *stream); 57 | void got_out_stream_disconnected(stream *stream); 58 | void got_in_stream_connected(stream *stream); 59 | }; 60 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/filesync_service.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "sss/host.h" 6 | 7 | namespace filesyncbox { 8 | 9 | /** 10 | * File sync service monitors given directories and keeps these files synchronised between 11 | * given instances. There are several sync policies applicable for different level of trust 12 | * between peer nodes. 13 | */ 14 | class filesync_service 15 | { 16 | class private_impl; 17 | std::shared_ptr pimpl_; 18 | 19 | public: 20 | filesync_service(std::shared_ptr host); 21 | ~filesync_service(); 22 | bool is_active() const; 23 | 24 | void add_directory_sync(boost::filesystem::path dir, std::vector const& to_peers); 25 | 26 | // File sync service signals 27 | using completion_signal = boost::signals2::signal; 28 | completion_signal sync_completed; 29 | }; 30 | 31 | } // filesyncbox namespace 32 | 33 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/index.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Index keeps all chunks in a multimap by all used hashes. 5 | * It can quickly find a chunk given any hash. 6 | * 7 | * Index also allows to look up hash information for a given local file, under any directory. 8 | * It will calculate the information upon first request and cache it. 9 | 10 | multimap chunk*> 11 | 12 | */ 13 | -------------------------------------------------------------------------------- /filesyncbox/include/filesyncbox/internal/chunk_peer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "filesyncbox/chunk_protocol.h" 12 | 13 | namespace sss { 14 | namespace internal { 15 | 16 | /** 17 | * Private helper class for chunk_share: 18 | * implements our interaction with a given peer for chunk sharing. 19 | */ 20 | class chunk_peer : public chunk_protocol 21 | { 22 | /** 23 | * Minimum delay between successive connection attempts - 1 minute 24 | */ 25 | static const int recon_delay = 60*1000; 26 | 27 | peer_id const eid_; ///< Host ID of peer 28 | std::set wishlist_; ///< What we want from this host 29 | byte_array current_; ///< Ohash of chunk being gotten 30 | 31 | public: 32 | chunk_peer(peer_id const& hostid); 33 | ~chunk_peer(); 34 | 35 | inline peer_id peer_host_id() { return hostid; } 36 | string peer_name(); 37 | 38 | void check_work(); 39 | 40 | void send_status_request(request *req); 41 | 42 | void got_status_request(stream *strm, byte_array_iwrap& is); 43 | void got_chunk_request(stream *strm, byte_array_iwrap& is); 44 | 45 | void got_status_reply(stream *strm, byte_array_iwrap& is); 46 | void got_chunk_reply(stream *strm, byte_array_iwrap& is); 47 | 48 | void remove_from_requests(); 49 | 50 | void peer_read_message(stream* s); 51 | }; 52 | 53 | } // internal namespace 54 | } // sss namespace 55 | -------------------------------------------------------------------------------- /filesyncbox/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(filesyncbox STATIC 3 | filesync_service.cpp 4 | abstract_chunk_reader.cpp 5 | chunk_peer.cpp 6 | chunk_share.cpp) 7 | -------------------------------------------------------------------------------- /filesyncbox/lib/abstract_chunk_reader.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "filesyncbox/abstract_chunk_reader.h" 10 | 11 | void abstract_chunk_reader::read_chunk(byte_array const& ohash) 12 | { 13 | byte_array data = store::read_stores(ohash); 14 | if (!data.is_empty()) 15 | return got_data(ohash, data); 16 | 17 | // Request asynchronously from other nodes 18 | chunk_share::request_chunk(this, ohash); 19 | } 20 | 21 | -------------------------------------------------------------------------------- /filesyncbox/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #create_test(test1) 2 | -------------------------------------------------------------------------------- /mettanode.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": "/Users/berkus/Hobby/mettanode" 6 | } 7 | ], 8 | "settings": 9 | { 10 | "sublimeclang_enabled": false, 11 | "sublimeclang_options": [ 12 | "-I${folder${project_path:.gitignore}}/3rdparty/**", 13 | "-I${folder${project_path:.gitignore}}/naming/**", 14 | "-I${folder${project_path:.gitignore}}/nat/**", 15 | "-I${folder${project_path:.gitignore}}/playground/**", 16 | "-I${folder${project_path:.gitignore}}/routing/**", 17 | "-I${folder${project_path:.gitignore}}/sst/**", 18 | "-I${folder${project_path:.gitignore}}/ui/**" 19 | ], 20 | "sublimeclang_automatic_completion_popup": true, 21 | "sublimeclang_recompile_delay": 0, 22 | "sublimeclang_hide_output_when_empty": false, 23 | "sublimeclang_show_output_panel": true, 24 | "sublimeclang_update_output_panel": true, 25 | "sublimeclang_show_status": true, 26 | "sublimeclang_show_visual_error_marks": true, 27 | "sublimeclang_reparse_on_activated": true, 28 | "sublimeclang_reparse_on_save": true, 29 | "sublimeclang_parse_status_messages": true, 30 | "sublimeclang_time_completions": true, 31 | "sublimeclang_inhibit_sublime_completions": true, 32 | // "sublimeclang_options_script": "python ${home}/Hobby/cmake_options_script.py ${project_path:_build_}/compile_commands.json", 33 | "sublimeclang_debug_options": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /naming/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(include) 2 | 3 | -------------------------------------------------------------------------------- /naming/README.md: -------------------------------------------------------------------------------- 1 | Personal naming service 2 | ======================= 3 | 4 | Allows naming and name lookups for federations of private personal groups, 5 | completely decentralized whilst authoritative. 6 | 7 | -------------------------------------------------------------------------------- /naming/TODO: -------------------------------------------------------------------------------- 1 | Principal (me) 2 | +--Identities 3 | +--Primary 4 | +--Corporate 5 | +--Pseudonymous1 6 | +--Pseudonymous2 7 | +--Devices 8 | +--My Personal devices 9 | +--My mac 10 | +--My iPhone 11 | +--My TV 12 | +--My Nest controller 13 | +--Third party devices 14 | +--Wife's phone 15 | +--Friend's TV 16 | +--Colleague's Tablet 17 | 18 | You have full control over devices in My Personal devices, and only partial access control over Third party devices. 19 | Principal is your master identity, it is never exposed or shared to anyone, you contact with peers using one of 20 | the identities you create. 21 | Master identity allows you to control other identities and devices, so it has to be well protected. 22 | 23 | 24 | 25 | Peering: 26 | ☐ "Peer" is a remote contact. 27 | Peer can have a profile, a set of EIDs, various access rights into our cluster(s). 28 | EIDs correspond to peers' devices and/or identities. Not all peers see the same set of EIDs - some may know more 29 | than the other. Peer profile serves as a public peer's principal ID, it is not associated with Master Identity 30 | of this peer in any way. This profile can be exposed or traded by third parties, reducing anonymity. 31 | 32 | 33 | 34 | Peers are concepts in uia::naming namespace: 35 | 36 | Peer ::- list of EIDs, profile, list of peers (almost recursively - we don't know their lists of peers) 37 | ::- with EIDs also list of last known endpoints for those EIDs 38 | 39 | These EIDs and endpoints information is also used by the uia::routing layer for DHT. 40 | 41 | 42 | 43 | Urbit: 44 | Profiles are interesting, you can specify a lot of information about yourself. 45 | 46 | %lord male-identified individual 47 | %lady female-identified individual 48 | %punk opaque handle 49 | %anon totally anonymous 50 | %home family 51 | %crew corporation 52 | %band creative collective 53 | %fair nonprofit 54 | %dept government agency 55 | %holy religious institution 56 | 57 | Also ask for political alignments, profile pic, given name(s), nicknames, family names, CEO, leaders etc. 58 | Probably look at a Facebook profile? What's relevant there - education, past working places etc. 59 | You can add this in your profile yourself, but it can also be harvested and filled in by other peers in their instances 60 | of your profile. The notes they keep and whatever you update never overwrite each other, so it's possible to 61 | collect information you see relevant. 62 | 63 | Config storage representation: 64 | vector peers; => "peers" 65 | peer..[fields] 66 | -------------------------------------------------------------------------------- /naming/include/naming/peer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace uia { 4 | namespace naming { 5 | 6 | class peer 7 | { 8 | // eids 9 | // profile 10 | // friends (aka peers) 11 | }; 12 | 13 | } // naming namespace 14 | } // uia namespace 15 | -------------------------------------------------------------------------------- /playground/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(../nat/include ../sss/include ../arsenal/include) 2 | include_directories(../3rdparty/opus/include) # For unlogger 3 | include_directories(../3rdparty) # For cryptest 4 | 5 | add_executable(unlogger unlogger.cpp) 6 | target_link_libraries(unlogger arsenal opus ${Boost_LIBRARIES}) 7 | install(TARGETS unlogger 8 | RUNTIME DESTINATION tools) 9 | 10 | #if (APPLE) 11 | # add_executable(screencaster screencaster.cpp) #macosx.mm 12 | # target_link_libraries(screencaster ${QT_LIBRARIES} objc) 13 | # install(TARGETS screencaster 14 | # RUNTIME DESTINATION tools) 15 | #endif (APPLE) 16 | 17 | #add_subdirectory(sstreams) 18 | 19 | add_subdirectory(kextest) 20 | -------------------------------------------------------------------------------- /playground/data/plot-delay-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/playground/data/plot-delay-log.png -------------------------------------------------------------------------------- /playground/data/plot-delay-play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/playground/data/plot-delay-play.png -------------------------------------------------------------------------------- /playground/data/plot-delay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/playground/data/plot-delay.png -------------------------------------------------------------------------------- /playground/data/timing.plt: -------------------------------------------------------------------------------- 1 | set term png font "Arial" 2 | 3 | set title "Delay variation" 4 | set ylabel "Packet delay, ms" 5 | set xlabel "Time" 6 | set output "plot-delay.png" 7 | plot "packet-to-receive-remote.dat" using 1:3 title "RCV remote" with lines, "packet-to-receive-local.dat" using 1:3 title "RCV local" with lines 8 | 9 | set output "plot-delay-play.png" 10 | plot "packet-to-playback-remote.dat" using 1:3 title "PLAY remote" with lines, "packet-to-playback-local.dat" using 1:3 title "PLAY local" with lines 11 | 12 | set output "plot-delay-log.png" 13 | plot "packet-to-receive-log-remote.dat" using 1:3 title "RCV remote w/ log" with lines, "packet-to-receive-log-local.dat" using 1:3 title "RCV local w/ log" with lines 14 | -------------------------------------------------------------------------------- /playground/kextest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #add_executable(cryptest cryptest.cpp) 2 | #target_link_libraries(cryptest sodiumpp arsenal) 3 | 4 | add_definitions("-Wno-deprecated-register") # Damnit, boost! 5 | 6 | add_executable(kextest kextest.cpp) 7 | target_link_libraries(kextest sodiumpp arsenal ${Boost_LIBRARIES}) 8 | -------------------------------------------------------------------------------- /playground/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include 10 | #include "udpsend.h" 11 | #include "upnp/upnpmcastsocket.h" 12 | #include "upnp/router.h" 13 | 14 | // 15 | // Main application entrypoint 16 | // 17 | int main(int argc, char **argv) 18 | { 19 | QCoreApplication app(argc, argv); 20 | 21 | bt::UPnPMCastSocket* upnp = new bt::UPnPMCastSocket(true); 22 | UdpTestSender sock(upnp); 23 | QObject::connect(upnp, SIGNAL(discovered(UPnPRouter*)), 24 | &sock, SLOT(routerFound(UPnPRouter*))); 25 | 26 | // upnp->loadRouters("routers.txt"); 27 | upnp->discover(); 28 | 29 | return app.exec(); 30 | } 31 | -------------------------------------------------------------------------------- /playground/packetsizes.plt: -------------------------------------------------------------------------------- 1 | set term png font "Arial" 2 | 3 | set title "Packet sizes" 4 | set ylabel "Packet size, bytes" 5 | set xlabel "Sequence number" 6 | set output "pktsizes.png" 7 | plot "local_packetsizes.plot" using 1:2 title "Orig local size" with dots, "remote_packetsizes.plot" using 1:2 title "Orig remote size" with dots 8 | -------------------------------------------------------------------------------- /playground/screencaster.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include 10 | 11 | // Save pixmap to a byte array: 12 | // QPixmap pixmap(); 13 | // QByteArray bytes; 14 | // QBuffer buffer(&bytes); 15 | // buffer.open(QIODevice::WriteOnly); 16 | // pixmap.save(&buffer, "JPEG", 0); 17 | 18 | float dpiScaleFactor(QWidget* widget); 19 | 20 | int main(int argc, char** argv) 21 | { 22 | QApplication app(argc, argv); 23 | QDesktopWidget *desktop = QApplication::desktop(); 24 | float scale = dpiScaleFactor(desktop); 25 | qDebug() << "Scale factor" << scale; 26 | QPixmap screenshot = QPixmap::grabWindow(desktop->winId(), 27 | 0, 0, desktop->width()*scale, desktop->height()*scale); 28 | QFile file("screenshot.png"); 29 | file.open(QIODevice::WriteOnly); 30 | screenshot.save(&file, "JPEG", 100);//(-1..0-100) 0 smallest lowest quality 31 | return app.exec(); 32 | } 33 | -------------------------------------------------------------------------------- /playground/sstreams/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(QT_DONT_USE_QTGUI TRUE) 2 | set(QT_USE_QTNETWORK TRUE) 3 | set(QT_USE_QTXML FALSE) 4 | include(${QT_USE_FILE}) 5 | 6 | include_directories(../../sst/lib ../../routing/lib ../../ui/lib) 7 | 8 | set(sstreams_SOURCES main.cpp) 9 | #set(udpsender_HEADERS udpsend.h) 10 | qt4_wrap_cpp(udpsender_HEADERS_MOC ${udpsender_HEADERS}) 11 | 12 | add_executable(sstreams ${sstreams_SOURCES} ${sstreams_HEADERS_MOC}) 13 | target_link_libraries(sstreams sst routing netsteria ${QT_LIBRARIES} ${OPENSSL_LIBRARIES}) 14 | install(TARGETS sstreams 15 | RUNTIME DESTINATION tools) 16 | -------------------------------------------------------------------------------- /playground/udpsend.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "udpsend.h" 10 | #include "upnp/router.h" 11 | #include "upnp/upnpmcastsocket.h" 12 | #include "protocol.h" 13 | #include 14 | 15 | UdpTestSender::UdpTestSender(bt::UPnPMCastSocket* up) 16 | : QUdpSocket() 17 | , router(0) 18 | , upnp(up) 19 | { 20 | QObject::connect(this,SIGNAL(readyRead()),this,SLOT(onReadyRead())); 21 | QObject::connect(this,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error(QAbstractSocket::SocketError))); 22 | 23 | if (!bind(stream_protocol::default_port, QUdpSocket::ShareAddress)) 24 | qWarning() << "Cannot bind to UDP port" << stream_protocol::default_port; 25 | 26 | ping(QHostAddress("127.0.0.1"), stream_protocol::default_port); 27 | } 28 | 29 | UdpTestSender::~UdpTestSender() 30 | { 31 | 32 | } 33 | 34 | void UdpTestSender::ping(QHostAddress remote, uint16_t port) 35 | { 36 | QByteArray b("hello world!"); 37 | qDebug() << "Sending:" << QString(b) << "to" << remote << port; 38 | writeDatagram(b, remote, port); 39 | } 40 | 41 | void UdpTestSender::onReadyRead() 42 | { 43 | QHostAddress origin; 44 | uint16_t port; 45 | QByteArray data(pendingDatagramSize(),0); 46 | if (readDatagram(data.data(),pendingDatagramSize(),&origin,&port) == -1) 47 | return; 48 | 49 | qDebug() << "Received:" << QString(data); 50 | 51 | ping(origin, port); 52 | } 53 | 54 | void UdpTestSender::error(QAbstractSocket::SocketError err) 55 | { 56 | qWarning() << "Socket error" << err; 57 | } 58 | 59 | void UdpTestSender::routerFound(UPnPRouter* r) 60 | { 61 | qDebug() << "Router detected, punching a hole."; 62 | upnp->saveRouters("routers.txt"); 63 | router = r; 64 | connect(router, SIGNAL(stateChanged()), 65 | this, SLOT(routerStateChanged())); 66 | connect(router, SIGNAL(portForwarded(bool)), 67 | this, SLOT(portForwarded(bool))); 68 | 69 | Port p2(stream_protocol::default_port, Port::UDP); 70 | router->forward(p2, /*leaseDuration:*/ 3600, /*extPort:*/ 0); 71 | } 72 | 73 | void UdpTestSender::portForwarded(bool success) 74 | { 75 | if (success) 76 | { 77 | qDebug() << "Port forwarding succeeded, sending UDP."; 78 | ping(QHostAddress("212.7.7.70"), stream_protocol::default_port); 79 | } 80 | } 81 | 82 | void UdpTestSender::routerStateChanged() 83 | { 84 | QString err = router->getError(); 85 | if (err != QString::null) 86 | { 87 | qWarning() << "Routing setup error" << err; 88 | qWarning() << "Giving up"; 89 | // exit(1); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /playground/udpsend.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | class UPnPRouter; 15 | namespace bt { class UPnPMCastSocket; } 16 | 17 | class UdpTestSender : public QUdpSocket 18 | { 19 | Q_OBJECT 20 | UPnPRouter* router; 21 | bt::UPnPMCastSocket* upnp; 22 | 23 | public: 24 | UdpTestSender(bt::UPnPMCastSocket* upnp); 25 | ~UdpTestSender(); 26 | 27 | /** 28 | * Construct a packet with return address and send it to @c remote. 29 | */ 30 | void ping(QHostAddress remote, uint16_t port); 31 | 32 | public slots: 33 | void routerFound(UPnPRouter*); 34 | void portForwarded(bool); 35 | void routerStateChanged(); 36 | 37 | private slots: 38 | void onReadyRead(); 39 | void error(QAbstractSocket::SocketError err); 40 | }; 41 | -------------------------------------------------------------------------------- /scripts/asan_symbolicate.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Use OSX atos tool to symbolicate LLVM asan crashdump 3 | 4 | ARGF.each { |line| 5 | match = / #(\d+) (0x[0-9A-Fa-f]+) \((.+?)\+(0x[0-9A-Fa-f]+)\)/.match(line) 6 | frame, memadddr, exe, fileaddr = match.captures if match 7 | puts " \##{frame} #{memadddr} #{`atos -arch x86_64 -o #{exe} #{fileaddr}`}" if match 8 | puts line unless match 9 | } 10 | -------------------------------------------------------------------------------- /scripts/rpath.sh: -------------------------------------------------------------------------------- 1 | install_name_tool -change /usr/local/opt/openssl/lib/libssl.1.0.0.dylib ./libssl.1.0.0.dylib opus-streaming 2 | install_name_tool -change /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib ./libcrypto.1.0.0.dylib opus-streaming 3 | install_name_tool -change /Users/berkus/Hobby/mettanode/_build_/3rdparty/rtaudio/librtaudio.dylib ./librtaudio.dylib opus-streaming 4 | install_name_tool -change /usr/local/Cellar/openssl/1.0.1h/lib/libcrypto.1.0.0.dylib ./libcrypto.1.0.0.dylib libssl.1.0.0.dylib 5 | -------------------------------------------------------------------------------- /scripts/travis_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # BUILD_TYPE comes from Travis environment. 3 | 4 | set -x 5 | 6 | mkdir -p _build_ 7 | cd _build_ 8 | CC=/usr/bin/clang CXX=/usr/bin/clang++ cmake -DTRAVIS_CI=YES -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=YES -G "Ninja" -DCMAKE_INSTALL_PREFIX=`pwd`/dist/$BUILD_TYPE/mettanode -DBOOST_LIBRARYDIR=/usr/lib64 .. || exit 1 9 | ninja -j1 install || exit 1 10 | 11 | # byte_array, flurry and crypto unit tests suffer from 12 | # ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) 13 | # This is seemingly a bug in libc++ 14 | # Disable asan checks for alloc-dealloc-mismatch until libc++ is sorted out. 15 | ASAN_OPTIONS=alloc_dealloc_mismatch=0 ctest || exit 1 16 | -------------------------------------------------------------------------------- /ui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(. ../nat/include) 2 | add_library(natclient STATIC traverse_nat.cpp) 3 | add_library(rclient STATIC client_utils.cpp) 4 | 5 | #add_subdirectory(lib) 6 | add_subdirectory(qt5) 7 | 8 | -------------------------------------------------------------------------------- /ui/README.md: -------------------------------------------------------------------------------- 1 | MettaNode 2 | ========= 3 | 4 | Peer-to-peer text and voice chat client with file sharing capabilities. 5 | 6 | A practical test suite for sss, routing, naming and NAT libraries. 7 | -------------------------------------------------------------------------------- /ui/client_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include 10 | #include "arsenal/settings_provider.h" 11 | #include "sss/host.h" 12 | #include "routing/client_profile.h" 13 | #include "client_utils.h" 14 | #include "routing/private/regserver_client.h" 15 | 16 | using namespace std; 17 | 18 | void regclient_set_profile(settings_provider* settings, 19 | uia::routing::internal::regserver_client& regclient, 20 | sss::host* host) 21 | { 22 | // Pull client profile from settings. 23 | byte_array s_client = settings->get_byte_array("profile"); 24 | uia::routing::client_profile client(s_client); 25 | client.set_endpoints(set_to_vector(host->active_local_endpoints())); 26 | for (auto kw : client.keywords()) { 27 | logger::debug() << "Keyword: " << kw; 28 | } 29 | regclient.set_profile(client); 30 | } 31 | 32 | void regclient_connect_regservers(settings_provider* settings, 33 | uia::routing::internal::regserver_client& regclient) 34 | { 35 | byte_array s_rs = settings->get_byte_array("regservers"); 36 | if (!s_rs.is_empty()) 37 | { 38 | byte_array rs_ba(s_rs); 39 | byte_array_iwrap read(rs_ba); 40 | vector regservers; 41 | read.archive() >> regservers; 42 | for (auto server : regservers) 43 | { 44 | regclient.register_at(server); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ui/client_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | class settings_provider; 12 | namespace uia { namespace routing { namespace internal { class regserver_client; }}} 13 | namespace sss { class host; } 14 | 15 | /** 16 | * Load from settings provider and set user profile for regclient. 17 | */ 18 | void regclient_set_profile(settings_provider* settings, 19 | uia::routing::internal::regserver_client& regclient, 20 | sss::host* host); 21 | /** 22 | * Load from settings provider and connect regclient to a list of regservers. 23 | */ 24 | void regclient_connect_regservers(settings_provider* settings, 25 | uia::routing::internal::regserver_client& regclient); 26 | -------------------------------------------------------------------------------- /ui/doc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | doxygen lib.conf 4 | -------------------------------------------------------------------------------- /ui/doc/livemedia.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | CALL -> RINGING -> -> EARLY_MEDIA -> LIVE; 3 | VIDEO_CALL -> RINGING -> EARLY_MEDIA -> LIVE; 4 | LIVE -> HANGUP -> FINISHED; 5 | 6 | SHARE_SCREEN -> -> LIVE -> STOP_SHARE; 7 | } 8 | -------------------------------------------------------------------------------- /ui/doc/mainpage.dox: -------------------------------------------------------------------------------- 1 | /* doxygen index page */ 2 | /** @mainpage Netsteria library 3 | 4 | Netsteria library provides chat, voice, file synchronization capabilities for MettaNodes. 5 | 6 | Work in progress! 7 | */ 8 | -------------------------------------------------------------------------------- /ui/gui/chat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "stream.h" 9 | #include "scan.h" 10 | #include "peerid.h" 11 | 12 | class QLabel; 13 | class QGridLayout; 14 | class QLineEdit; 15 | class QPushButton; 16 | class LogArea; 17 | class ChatServer; 18 | class FileInfo; 19 | class ChatHistory; 20 | 21 | struct ChatProtocol 22 | { 23 | // Message type codes 24 | enum MsgCode { 25 | Invalid = 0, 26 | Text, // Plain text message (UTF-8) 27 | Files, // File shares (encoded FileInfo) 28 | }; 29 | }; 30 | 31 | class ChatDialog : public QDialog, public ChatProtocol 32 | { 33 | friend class ChatServer; 34 | Q_OBJECT 35 | 36 | ChatDialog(const SST::PeerId& id, const QString &name, SST::Stream *strm = NULL); 37 | ~ChatDialog(); 38 | 39 | const SST::PeerId otherid; // Who we're chatting with 40 | const QString othername; 41 | 42 | SST::Stream *stream; // Connection we chat over 43 | 44 | ChatHistory* history; // History storage for this chat 45 | 46 | QList labels; // List of log entry sender labels 47 | QList entries; // List of log entry display widgets 48 | 49 | QWidget *logwidget; // Virtual widget displaying the log 50 | QGridLayout *loglayout; 51 | 52 | LogArea *logview; // Scrolling view onto the logwidget 53 | QLineEdit *textentry; // Text entry line 54 | QPushButton *button; // Send button 55 | 56 | // Global hash table of open chat dialogs by ID 57 | //static QHash chathash; 58 | 59 | public: 60 | 61 | static ChatDialog *open(const SST::PeerId &id, const QString &name); 62 | 63 | void sendFiles(const QList &files); 64 | 65 | 66 | private: 67 | inline bool active() { return !textentry->isReadOnly(); } 68 | 69 | void closeEvent(QCloseEvent *event); 70 | void dragEnterEvent(QDragEnterEvent *event); 71 | void dropEvent(QDropEvent *event); 72 | 73 | void addText(const QString &source, const QString &text); 74 | void addFiles(const QString &source, const QList &files); 75 | void addEntry(QString source, QWidget *widget); 76 | 77 | private slots: 78 | void connected(); 79 | void readyReadMessage(); 80 | void streamError(const QString &err); 81 | void sendTextLine(); 82 | /** 83 | * Load available chat history. Currently tries to load everything, for simplicity. 84 | */ 85 | void loadHistory(); 86 | }; 87 | 88 | 89 | // Helper class for receiving incoming chat requests 90 | class ChatServer : public SST::StreamServer 91 | { 92 | Q_OBJECT 93 | 94 | public: 95 | ChatServer(QObject *parent = NULL); 96 | 97 | private slots: 98 | void incoming(); 99 | }; 100 | 101 | 102 | // Helper class to scan and send a file or directory tree 103 | class ChatScanner : public QProgressDialog 104 | { 105 | Q_OBJECT 106 | 107 | private: 108 | ChatDialog *chat; 109 | QList scans; 110 | 111 | public: 112 | ChatScanner(ChatDialog *parent, const QStringList &files); 113 | 114 | private slots: 115 | void scanProgress(); 116 | void scanCanceled(); 117 | }; 118 | 119 | -------------------------------------------------------------------------------- /ui/gui/chathistory.cpp: -------------------------------------------------------------------------------- 1 | #include "chathistory.h" 2 | #include "env.h" 3 | #include 4 | 5 | ChatHistory::ChatHistory(const SST::PeerId& id, QObject* parent) 6 | : QObject(parent) 7 | { 8 | appdir.mkdir("ChatHistory"); 9 | // Use base32 for naming the files, it's more filesystem-compatible. 10 | // QString filename = QString(Base32(id)); 11 | QString filename = id.toString(); 12 | QString name = appdir.path() + "/ChatHistory/history." + filename; 13 | storage = new c4_Storage(name.toLocal8Bit().constData(), /*read-write-flag:*/ 1); 14 | view = new c4_View(storage->GetAs("chathistory[unread:B,originator:S,originator_eid:S,timestamp:T,target:S,msg_hash:S,msg:S]")); 15 | } 16 | 17 | ChatHistory::~ChatHistory() 18 | { 19 | storage->Commit(); 20 | 21 | delete view; 22 | delete storage; 23 | } 24 | 25 | void ChatHistory::insertHistoryLine(const QString& originator, const QDateTime timestamp, const QString& message) 26 | { 27 | c4_Row row; 28 | c4_StringProp pOriginator("originator"); 29 | c4_StringProp pMsg("msg"); 30 | 31 | pOriginator(row) = originator.toUtf8().constData(); 32 | pMsg(row) = message.toUtf8().constData(); 33 | 34 | view->Add(row); 35 | } 36 | 37 | void ChatHistory::newHistorySynced() 38 | { 39 | } 40 | -------------------------------------------------------------------------------- /ui/gui/chathistory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // @todo: Should be a Store? 5 | // 6 | #include 7 | #include 8 | #include 9 | // #include "store.h" 10 | #include "mk4.h" 11 | #include "peerid.h" 12 | 13 | // For now, store a file per chat ID. 14 | class ChatHistory : public QObject 15 | { 16 | Q_OBJECT 17 | 18 | c4_Storage* storage; 19 | c4_View* view; 20 | // Store* historyStore; 21 | 22 | public: 23 | /** 24 | * @param id binary chat identifier. 25 | */ 26 | ChatHistory(const SST::PeerId& id, QObject* parent = 0); 27 | ~ChatHistory(); 28 | 29 | public slots: 30 | void insertHistoryLine(const QString& originator, const QDateTime timestamp, const QString& message); 31 | 32 | private slots: 33 | void newHistorySynced(); 34 | }; 35 | -------------------------------------------------------------------------------- /ui/gui/gui.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "img/netsteria.ico" 2 | -------------------------------------------------------------------------------- /ui/gui/logarea.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "logarea.h" 5 | 6 | 7 | LogArea::LogArea(QWidget *parent) 8 | : QScrollArea(parent) 9 | { 10 | QScrollBar *scrollbar = verticalScrollBar(); 11 | 12 | connect(scrollbar, SIGNAL(valueChanged(int)), 13 | this, SLOT(scrollValueChanged(int))); 14 | connect(scrollbar, SIGNAL(rangeChanged(int, int)), 15 | this, SLOT(scrollRangeChanged(int, int))); 16 | 17 | scrollValue = scrollbar->value(); 18 | scrollMax = scrollbar->maximum(); 19 | } 20 | 21 | void LogArea::scrollValueChanged(int value) 22 | { 23 | // Just track the current value 24 | scrollValue = value; 25 | } 26 | 27 | void LogArea::scrollRangeChanged(int, int max) 28 | { 29 | // If the scroll bar was at the bottom before the new chat entry, 30 | // keep it at the bottom of the logview afterwards as well. 31 | if (scrollValue == scrollMax) 32 | verticalScrollBar()->setSliderPosition(max); 33 | 34 | scrollMax = max; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /ui/gui/logarea.h: -------------------------------------------------------------------------------- 1 | // 2 | // LogArea: a simple variant of a QScrollArea for log views, 3 | // which by default keeps the vertical scrollbar pegged to the bottom 4 | // of the internal area as the internal widget's virtual size changes, 5 | // but still allows the user to scroll back in the log when desired. 6 | // 7 | #ifndef LOGAREA_H 8 | #define LOGAREA_H 9 | 10 | #include 11 | 12 | class LogArea : public QScrollArea 13 | { 14 | Q_OBJECT 15 | 16 | private: 17 | // These variables track the vertical scrollbar's 18 | // current and maximum value in order to keep it pegged. 19 | int scrollValue, scrollMax; 20 | 21 | public: 22 | LogArea(QWidget *parent = NULL); 23 | 24 | private slots: 25 | void scrollValueChanged(int value); 26 | void scrollRangeChanged(int min, int max); 27 | }; 28 | 29 | #endif // LOGAREA_H 30 | -------------------------------------------------------------------------------- /ui/gui/logwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "logwindow.h" 14 | #include "logarea.h" 15 | #include "env.h" 16 | 17 | LogWindow::LogWindow() 18 | : QDialog(NULL) 19 | { 20 | setWindowTitle(tr("Log")); 21 | 22 | settings->beginGroup("LogWindow"); 23 | move(settings->value("pos", QPoint(100, 100)).toPoint()); 24 | resize(settings->value("size", QSize(400, 500)).toSize()); 25 | settings->endGroup(); 26 | 27 | logwidget = new QWidget(this); 28 | loglayout = new QGridLayout(); 29 | logwidget->setLayout(loglayout); 30 | 31 | logview = new LogArea(this); 32 | logview->setWidget(logwidget); 33 | logview->setWidgetResizable(true); 34 | 35 | QVBoxLayout *layout = new QVBoxLayout; 36 | layout->setContentsMargins(2,2,2,2); 37 | layout->setSpacing(1); 38 | layout->addWidget(logview); 39 | setLayout(layout); 40 | } 41 | 42 | LogWindow::~LogWindow() 43 | { 44 | qDebug() << "~LogWindow"; 45 | } 46 | 47 | void LogWindow::closeEvent(QCloseEvent *event) 48 | { 49 | settings->beginGroup("LogWindow"); 50 | settings->setValue("pos", pos()); 51 | settings->setValue("size", size()); 52 | settings->endGroup(); 53 | 54 | // Close our window as usual 55 | QDialog::closeEvent(event); 56 | } 57 | 58 | void LogWindow::writeLog(const QString &text) 59 | { 60 | QLabel *disp = new QLabel(text, logwidget); 61 | disp->setWordWrap(true); 62 | 63 | // Add the label and entry widget to the log. 64 | int itemno = entries.size(); 65 | entries.append(disp); 66 | 67 | // Add them to the log's layout. 68 | loglayout->addWidget(disp, itemno, 1); 69 | loglayout->setRowStretch(itemno+1, 1); 70 | } 71 | 72 | LogWindow& LogWindow::get() 73 | { 74 | static LogWindow* window = 0; 75 | if (!window) 76 | window = new LogWindow; 77 | return *window; 78 | } 79 | -------------------------------------------------------------------------------- /ui/gui/logwindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class QLabel; 10 | class QGridLayout; 11 | class QLineEdit; 12 | class QPushButton; 13 | class LogArea; 14 | class ChatServer; 15 | class FileInfo; 16 | class ChatHistory; 17 | 18 | class LogWindow : public QDialog 19 | { 20 | Q_OBJECT 21 | 22 | LogWindow(); 23 | ~LogWindow(); 24 | 25 | QWidget *logwidget; // Virtual widget displaying the log 26 | QGridLayout *loglayout; 27 | LogArea *logview; // Scrolling view onto the logwidget 28 | QList entries; // List of log entry display widgets 29 | 30 | public: 31 | static LogWindow& get(); 32 | 33 | void writeLog(const QString &text); 34 | 35 | private: 36 | void closeEvent(QCloseEvent *event); 37 | }; 38 | 39 | inline LogWindow& operator <<(LogWindow& lw, const QString& s) 40 | { 41 | lw.writeLog(s); 42 | return lw; 43 | } 44 | -------------------------------------------------------------------------------- /ui/gui/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | #define MAIN_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "regcli.h" 11 | 12 | class QSettings; 13 | class QDir; 14 | class QTableView; 15 | class QModelIndex; 16 | class QAction; 17 | 18 | class SearchDialog; 19 | class ChatDialog; 20 | class PeerTable; 21 | 22 | class MainWindow : public QMainWindow 23 | { 24 | Q_OBJECT 25 | 26 | SearchDialog *searcher; 27 | QTableView *friendslist; 28 | 29 | // Menu/toolbar actions requiring dynamic enable/disable control 30 | QAction *maMessage, *maTalk, *maRename, *maDelete; 31 | QAction *taMessage, *taTalk, *taRename, *taDelete; 32 | 33 | 34 | public: 35 | MainWindow(); 36 | ~MainWindow(); 37 | 38 | // Add identity 'id' as a friend with suggested name 'name'. 39 | // If 'edit' is true, start editing the name after adding it. 40 | void addPeer(const QByteArray &id, QString name, bool edit = false); 41 | 42 | protected: 43 | // Re-implement to watch for window activate/deactivate events. 44 | virtual bool event(QEvent *event); 45 | 46 | virtual void closeEvent(QCloseEvent *event); 47 | 48 | private: 49 | int selectedFriend(); 50 | 51 | private slots: 52 | void friendsClicked(const QModelIndex &index); 53 | void trayActivate(QSystemTrayIcon::ActivationReason); 54 | void openFriends(); 55 | void openSearch(); 56 | void openChat(); 57 | void openDownload(); 58 | void openSettings(); 59 | void openLogWindow(); 60 | void openProfile(); 61 | void gotoFiles(); 62 | void openHelp(); 63 | void openWeb(); 64 | void openAbout(); 65 | void startTalk(); 66 | void renameFriend(); 67 | void deleteFriend(); 68 | void updateMenus(); 69 | void exitApp(); 70 | }; 71 | 72 | //============================================================================= 73 | // TEST ZONE 74 | //============================================================================= 75 | class UPnPRouter; 76 | 77 | class Puncher : public QObject 78 | { 79 | Q_OBJECT 80 | 81 | int port; 82 | public: 83 | Puncher(int port); 84 | 85 | public slots: 86 | void routerFound(UPnPRouter*); 87 | void portForwarded(bool); 88 | }; 89 | //============================================================================= 90 | 91 | 92 | extern MainWindow *mainwin; 93 | extern PeerTable *friends; 94 | 95 | extern QList regclients; 96 | extern SST::RegInfo myreginfo; 97 | 98 | extern QSettings *settings; 99 | extern QDir appdir; 100 | 101 | 102 | 103 | #endif // MAIN_H 104 | -------------------------------------------------------------------------------- /ui/gui/save.h: -------------------------------------------------------------------------------- 1 | #ifndef SAVE_H 2 | #define SAVE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "file.h" 8 | 9 | class QWidget; 10 | class QLabel; 11 | class QProgressBar; 12 | class QPushButton; 13 | class QGridLayout; 14 | class LogArea; 15 | class SaveItem; 16 | class Update; 17 | class Action; // lawsuit 18 | 19 | 20 | // Firefox-esque dialog showing progress of current and recent downloads 21 | class SaveDialog : public QDialog 22 | { 23 | friend class SaveItem; 24 | Q_OBJECT 25 | 26 | private: 27 | // Download list format: 28 | // status label, progress bar, cancel button 29 | static const int columns = 3; 30 | 31 | QList items; // List of active items 32 | 33 | QWidget *listwidget; // Virtual widget displaying the items 34 | QGridLayout *listlayout; 35 | LogArea *listview; // Scrolling view onto the logwidget 36 | 37 | 38 | public: 39 | static void init(); 40 | static void save(const FileInfo &info, const QString &localName); 41 | static void present(); 42 | 43 | static int numActive(); 44 | 45 | private: 46 | SaveDialog(); 47 | void writestate(); 48 | void relayout(); 49 | 50 | private slots: 51 | void cleanup(); 52 | }; 53 | 54 | // Private helper class for SaveDialog, representing one download item 55 | class SaveItem : public QObject 56 | { 57 | friend class SaveDialog; 58 | Q_OBJECT 59 | 60 | private: 61 | static const int progressMax = 1000000; 62 | 63 | const FileInfo info; 64 | const QString localpath; 65 | QString finalmsg; 66 | Action *act; 67 | QLabel *status; 68 | QProgressBar *progress; 69 | //QPushButton *pause; 70 | QPushButton *cancel; 71 | 72 | 73 | SaveItem(SaveDialog *dlg, const FileInfo &info, 74 | const QString &localpath, const QString &finalmsg = QString()); 75 | ~SaveItem(); 76 | 77 | bool isDone(); 78 | 79 | private slots: 80 | void pausePressed(); 81 | void cancelPressed(); 82 | void updateStatus(); 83 | void updateProgress(float ratio); 84 | }; 85 | 86 | // A modeless file dialog for picking a download location 87 | class SaveAsDialog : public QFileDialog 88 | { 89 | Q_OBJECT 90 | 91 | private: 92 | const FileInfo info; // Keys to file to be saved 93 | bool done; 94 | 95 | public: 96 | 97 | // Interactively start a Save As... operation for a specified file, 98 | // requesting a local filename from the user and starting a download. 99 | static void saveAs(const FileInfo &info); 100 | 101 | private: 102 | SaveAsDialog(const FileInfo &info); 103 | 104 | private slots: 105 | void gotFinished(int result); 106 | }; 107 | 108 | #endif // SAVE_H 109 | -------------------------------------------------------------------------------- /ui/gui/search.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "regcli.h" 11 | 12 | class QLineEdit; 13 | class QTableView; 14 | class QTableWidgetItem; 15 | namespace SST { 16 | class RegInfo; 17 | class Endpoint; 18 | class PeerId; 19 | } 20 | 21 | class SearchDialog : public QDialog 22 | { 23 | Q_OBJECT 24 | 25 | private: 26 | typedef SST::Endpoint Endpoint; 27 | typedef SST::RegInfo RegInfo; 28 | 29 | QLineEdit *textline; 30 | QTableWidget *results; 31 | 32 | QString searchtext; 33 | QHash reqids; 34 | QList resultids; 35 | 36 | public: 37 | SearchDialog(QWidget *parent); 38 | 39 | void present(); 40 | 41 | 42 | private: 43 | void closeEvent(QCloseEvent *event); 44 | QTableWidgetItem *item(const QString &text); 45 | 46 | private slots: 47 | void startSearch(); 48 | void searchDone(const QString &text, const QList ids, bool complete); 49 | void lookupDone(const SST::PeerId &id, const Endpoint &loc, const RegInfo &info); 50 | void addPeer(); 51 | }; 52 | -------------------------------------------------------------------------------- /ui/gui/settings.h: -------------------------------------------------------------------------------- 1 | #ifndef SETTINGS_H 2 | #define SETTINGS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class QDateTime; 9 | class QTabWidget; 10 | class QListView; 11 | class QCheckBox; 12 | class QLabel; 13 | class QModelIndex; 14 | class Meter; 15 | class AudioLoop; 16 | 17 | 18 | class Profile : public QWidget 19 | { 20 | Q_OBJECT 21 | 22 | private: 23 | QLineEdit owneredit, hostedit; 24 | QLineEdit cityedit, regionedit, countryedit; 25 | QDateEdit birthedit; 26 | 27 | public: 28 | Profile(QWidget *parent = NULL); 29 | 30 | inline QString ownerName() { return owneredit.text(); } 31 | inline QString hostName() { return hostedit.text(); } 32 | inline QString city() { return cityedit.text(); } 33 | inline QString region() { return regionedit.text(); } 34 | inline QString country() { return countryedit.text(); } 35 | inline QDate birthDate() { return birthedit.date(); } 36 | 37 | signals: 38 | void profileChanged(); 39 | 40 | private slots: 41 | void writeProfile(); 42 | }; 43 | 44 | class AudioControl : public QWidget 45 | { 46 | Q_OBJECT 47 | 48 | private: 49 | AudioLoop *aloop; 50 | 51 | QCheckBox *indefault, *outdefault; 52 | QListView *inview, *outview; 53 | Meter *inmeter, *outmeter; 54 | QCheckBox *loopbox; 55 | QLabel *looplabel; 56 | 57 | protected: 58 | void showEvent(QShowEvent *event); 59 | void hideEvent(QHideEvent *event); 60 | 61 | public: 62 | AudioControl(QWidget *parent = NULL); 63 | 64 | private: 65 | void loopEnable(); 66 | void loopDisable(); 67 | 68 | private slots: 69 | void inChanged(const QModelIndex ¤t); 70 | void outChanged(const QModelIndex ¤t); 71 | void loopChanged(); 72 | void loopPlayback(); 73 | }; 74 | 75 | class SettingsDialog : public QDialog 76 | { 77 | Q_OBJECT 78 | 79 | private: 80 | QTabWidget *tabwidget; 81 | 82 | public: 83 | SettingsDialog(); 84 | 85 | static void init(); 86 | static void openSettings(); 87 | static void openProfile(); 88 | }; 89 | 90 | extern Profile *profile; 91 | extern SettingsDialog *settingsdlg; 92 | 93 | #endif // SETTINGS_H 94 | -------------------------------------------------------------------------------- /ui/gui/sounds/bnb.s16: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/gui/sounds/bnb.s16 -------------------------------------------------------------------------------- /ui/gui/view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "file.h" 6 | 7 | class QLabel; 8 | class QVBoxLayout; 9 | class QGridLayout; 10 | 11 | 12 | class Viewer : public QWidget 13 | { 14 | FileInfo info; 15 | QLabel *typelabel; 16 | QWidget *content; 17 | QGridLayout *layout; 18 | QPoint pressPos; 19 | 20 | public: 21 | Viewer(QWidget *parent, const FileInfo &info); 22 | 23 | private: 24 | void mousePressEvent(QMouseEvent *event); 25 | void mouseReleaseEvent(QMouseEvent *event); 26 | 27 | void showMenu(const QPoint &pos); 28 | void saveAs(); 29 | }; 30 | 31 | 32 | class DirView : public QWidget, public AbstractDirReader 33 | { 34 | QVBoxLayout *layout; 35 | 36 | public: 37 | DirView(QWidget *parent, const FileInfo &dirInfo); 38 | 39 | private: 40 | void gotEntries(int pos, qint64 recno, 41 | const QList &ents); 42 | void noEntries(int pos, qint64 recno, int nents); 43 | }; 44 | -------------------------------------------------------------------------------- /ui/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(QT_USE_QTNETWORK TRUE) 2 | include(${QT_USE_FILE}) 3 | include_directories(../3rdparty/opus-1.0.1/include ../3rdparty/rtaudio ../../sst/lib ../../routing/lib) 4 | 5 | set(netsteria_SOURCES store.cc share.cc index.cc arch.cc chunk.cc file.cc filesync.cpp opaque.cc 6 | action.cc scan.cc update.cc crypt.cc adler32.cc rcsum.cc peer.cc audio.cc voice.cc) 7 | set(netsteria_HEADERS action.h audio.h chunk.h filesync.h opaque.h peer.h scan.h share.h store.h voice.h update.h) 8 | 9 | qt4_wrap_cpp(netsteria_HEADERS_MOC ${netsteria_HEADERS}) 10 | 11 | add_library(netsteria STATIC ${netsteria_SOURCES} ${netsteria_HEADERS_MOC}) 12 | -------------------------------------------------------------------------------- /ui/lib/action.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "action.h" 4 | 5 | 6 | Action::Action(QObject *parent, const QString &description) 7 | : QObject(parent), desc(description), 8 | st(Fresh), progr(-1.0), 9 | retrytimer(NULL) 10 | { 11 | } 12 | 13 | void Action::setStatus(Status newstatus, const QString &statustext) 14 | { 15 | // Not allowed to change status after we're in a Done state. 16 | Q_ASSERT(!isDone()); 17 | 18 | if (st == newstatus && note == statustext) 19 | return; // Nothing actually changed 20 | 21 | st = newstatus; 22 | note = statustext; 23 | 24 | // Notify anyone interested 25 | statusChanged(); 26 | } 27 | 28 | void Action::setPauseRequested(bool pausereq) 29 | { 30 | this->pausereq = pausereq; 31 | } 32 | 33 | void Action::setError(const QString &errortext, int retryDelay) 34 | { 35 | startRetryTimer(retryDelay); 36 | setStatus(Error, errortext); 37 | } 38 | 39 | void Action::startRetryTimer(int delay) 40 | { 41 | if (!retrytimer) { 42 | retrytimer = new QTimer(this); 43 | retrytimer->setSingleShot(true); 44 | connect(retrytimer, SIGNAL(timeout()), 45 | this, SLOT(retryTimeout())); 46 | } 47 | 48 | // XX jitter the retry timer? 49 | retrytimer->start(delay); 50 | } 51 | 52 | void Action::stopRetryTimer() 53 | { 54 | if (!retrytimer) 55 | return; 56 | 57 | retrytimer->stop(); 58 | } 59 | 60 | void Action::retryTimeout() 61 | { 62 | if (!isRunning()) 63 | start(); 64 | } 65 | 66 | void Action::insertPart(int idx, Action *act, bool signal) 67 | { 68 | Q_ASSERT(idx >= 0 && idx <= numParts()); 69 | Q_ASSERT(act); 70 | Q_ASSERT(!parts.contains(act)); 71 | 72 | if (signal) 73 | beforeInsertParts(idx, idx); 74 | 75 | act->setParent(this); 76 | parts.insert(idx, act); 77 | 78 | if (signal) 79 | afterInsertParts(idx, idx); 80 | } 81 | 82 | void Action::removePart(int idx, bool signal) 83 | { 84 | Q_ASSERT(idx >= 0 && idx < numParts()); 85 | 86 | if (signal) 87 | beforeRemoveParts(idx, idx); 88 | 89 | parts.removeAt(idx); 90 | 91 | if (signal) 92 | afterRemoveParts(idx, idx); 93 | } 94 | 95 | void Action::deletePart(int idx) 96 | { 97 | Q_ASSERT(idx >= 0 && idx < numParts()); 98 | 99 | part(idx)->deleteLater(); 100 | removePart(idx); 101 | } 102 | 103 | void Action::deleteAllParts() 104 | { 105 | int nparts = numParts(); 106 | if (nparts == 0) 107 | return; 108 | 109 | beforeRemoveParts(0, nparts-1); 110 | 111 | for (int i = 0; i < nparts; i++) 112 | part(i)->deleteLater(); 113 | parts.clear(); 114 | 115 | afterRemoveParts(0, nparts-1); 116 | } 117 | 118 | -------------------------------------------------------------------------------- /ui/lib/action.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class QTimer; 8 | 9 | 10 | class Action : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | enum Status { 16 | Fresh, // Brand-new, not yet started 17 | Running, // Apparently making good progress 18 | Success, // Action successfully completed 19 | Stalled, // Temporarily not making progress 20 | Paused, // Paused at user's request 21 | Error, // Blocked by potentially fixable error 22 | FatalError, // Blocked by permanent fatal error 23 | }; 24 | 25 | static const int defaultRetryDelay = 60*1000; // 1 minute 26 | 27 | private: 28 | const QString desc; 29 | Status st; 30 | bool pausereq; 31 | QString note; 32 | float progr; 33 | QList parts; 34 | QTimer *retrytimer; 35 | 36 | public: 37 | inline Status status() { return st; } 38 | inline bool isFresh() { return st == Fresh; } 39 | inline bool isRunning() { return st == Running; } 40 | inline bool isSuccess() { return st == Success; } 41 | inline bool isStalled() { return st == Stalled; } 42 | inline bool isPaused() { return st == Paused; } 43 | inline bool isError() { return st == Error; } 44 | inline bool isFatalError() { return st == FatalError; } 45 | inline bool isBlocked() { return st == Error || st == FatalError; } 46 | inline bool isDone() { return st == Success || st == FatalError; } 47 | 48 | inline QString description() { return desc; } 49 | inline QString statusString() { return note; } 50 | inline float progress() { return progr; } 51 | 52 | // Actions can be composed of multiple sub-actions, 53 | // which may run in series or parallel in any fashion. 54 | inline int numParts() { return parts.size(); } 55 | inline Action *part(int idx) { return parts.value(idx); } 56 | 57 | // Abstract methods that the subclass must implement 58 | virtual void start() = 0; // Start or restart 59 | 60 | // Client-controllable pause request flag 61 | // XX eventually want to add client-controllable prioritization, etc. 62 | inline bool pauseRequested() { return pausereq; } 63 | virtual void setPauseRequested(bool pausereq); 64 | inline void pause() { setPauseRequested(true); } 65 | inline void resume() { setPauseRequested(false); } 66 | 67 | signals: 68 | void statusChanged(); 69 | void progressChanged(float percent); 70 | 71 | void beforeInsertParts(int first, int last); 72 | void afterInsertParts(int first, int last); 73 | void beforeRemoveParts(int first, int last); 74 | void afterRemoveParts(int first, int last); 75 | 76 | 77 | protected: 78 | Action(QObject *parent, const QString &description); 79 | 80 | // Set the current status. Must not change from a 'done' state. 81 | void setStatus(Status newstatus, const QString &statustext); 82 | 83 | inline void setProgress(float newratio) 84 | { progr = newratio; progressChanged(newratio); } 85 | 86 | // Set the current status to indicate a non-fatal error, 87 | // and start a one-shot timer to call start() automatically 88 | // after a specified or default retryy delay. 89 | void setError(const QString &errortext, 90 | int retryDelay = defaultRetryDelay); 91 | 92 | // Lower-level methods for controlling the retry timer explicitly. 93 | void startRetryTimer(int delay = defaultRetryDelay); 94 | void stopRetryTimer(); 95 | 96 | // Manage the list of parts comprising this action. 97 | void insertPart(int idx, Action *act, bool signal = true); 98 | inline void appendPart(Action *act, bool signal = true) 99 | { return insertPart(numParts(), act, signal); } 100 | void removePart(int idx, bool signal = true); 101 | void deletePart(int idx); 102 | void deleteAllParts(); 103 | 104 | 105 | private slots: 106 | void retryTimeout(); 107 | }; 108 | -------------------------------------------------------------------------------- /ui/lib/adler32.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "adler32.h" 6 | 7 | 8 | // Compute an Adler-32 checksum over a given block of data. 9 | uint32_t adler32(const void *data, int len, uint32_t init) 10 | { 11 | const uint8_t *d = (const uint8_t*)data; 12 | unsigned a = init & 0xffff; 13 | unsigned b = init >> 16; 14 | for (int j = 0; j < len; ) { 15 | 16 | // We can sum up to 5552 bytes at a time (RFC 1950) 17 | // before our unsigned 32-bit accumulators overflow. 18 | for (int k = std::min(j+5550, len); j < k; j++) { 19 | a += *d++; 20 | b += a; 21 | } 22 | a %= 65521; 23 | b %= 65521; 24 | } 25 | return (b << 16) | a; 26 | } 27 | 28 | 29 | // Compose the Adler-32 checksums for two data blocks 30 | // into the checksum for the concatenation of the two blocks. 31 | // To do this we only need the length of the second block. 32 | uint32_t adler32cat(uint32_t sum1, uint32_t sum2, uint64_t len2) 33 | { 34 | // Modulo the length before multiplying, to avoid overflow. 35 | unsigned len2mod = len2 % 65521; 36 | 37 | unsigned a1 = sum1 & 0xffff; unsigned a2 = sum2 & 0xffff; 38 | unsigned b1 = sum1 >> 16; unsigned b2 = sum2 >> 16; 39 | 40 | unsigned a = (a1 + a2 - 1) % 65521; 41 | unsigned b = ((a1 * len2mod) + b1 + b2 - len2mod) % 65521; 42 | 43 | return (b << 16) | a; 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /ui/lib/adler32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /// Standard Adler32 checksum initialization value: b = 0, a = 1 6 | #define ADLER32_INIT 0x00000001 7 | 8 | /// Compute an Adler-32 checksum over a given block of data. 9 | uint32_t adler32(const void *data, int len, uint32_t init = ADLER32_INIT); 10 | 11 | /// Compose the Adler-32 checksums for two data blocks 12 | /// into the checksum for the concatenation of the two blocks. 13 | /// To do this we only need the length of the second block. 14 | uint32_t adler32cat(uint32_t sum1, uint32_t sum2, uint64_t len2); 15 | -------------------------------------------------------------------------------- /ui/lib/arch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "store.h" 9 | #include "chunk.h" 10 | 11 | class QDir; 12 | 13 | /** 14 | * Type for chunk index numbers within an archive 15 | */ 16 | typedef qint32 ArchiveIndex; 17 | 18 | /** 19 | * A variant of Store kept in an indexed archive file. 20 | */ 21 | class Archive : public Store 22 | { 23 | static const int hdrsize = 16; // Size of chunk header 24 | static const int chksize = 8; // Size of check field 25 | 26 | QFile file; 27 | qint64 readpos; 28 | ArchiveIndex readidx; 29 | bool verify; 30 | 31 | 32 | /** 33 | * Index of chunk header positions in the archive file. 34 | */ 35 | QVector chunks; 36 | 37 | /** 38 | * Hash table to lookup chunks by outer hash or check. 39 | * Collisions possible since OuterCheck is only 64 bits, 40 | * so we use QMultiHash and allow multiple values per key. 41 | */ 42 | QMultiHash chash; 43 | 44 | 45 | Archive(const QString &filename); 46 | 47 | /** 48 | * Read a chunk header at a given archive file position. 49 | * @param pos [description] 50 | * @param check [description] 51 | * @param size [description] 52 | * @return [description] 53 | */ 54 | bool readHeader(quint64 pos, quint64 &check, qint32 &size); 55 | 56 | /** 57 | * Read a chunk at a given archive file position. 58 | * @param pos [description] 59 | * @param expcheck [description] 60 | * @param expsize [description] 61 | * @return [description] 62 | */ 63 | QByteArray readAt(quint64 pos, quint64 expcheck = 0, int expsize = -1); 64 | 65 | /** 66 | * Read a chunk at a given archive index. 67 | * If size is >= 0, only read the chunk if actual size matches. 68 | * @param idx [description] 69 | * @param expcheck [description] 70 | * @param expsize [description] 71 | * @return [description] 72 | */ 73 | QByteArray readChunk(ArchiveIndex idx, quint64 expcheck = 0, 74 | int expsize = -1); 75 | 76 | /** 77 | * Scan for new chunks written to the underlying file. 78 | * @return [description] 79 | */ 80 | bool scan(); 81 | 82 | /** 83 | * Store method implementations 84 | * @param ohash [description] 85 | * @return [description] 86 | */ 87 | virtual QByteArray readStore(const QByteArray &ohash); 88 | 89 | 90 | public: 91 | /** 92 | * Read and verify the archived chunk, if present, 93 | * matching a specified full 512-bit outer hash. 94 | * May be slightly more efficient if size is provided. 95 | * @param ohash [description] 96 | * @param size [description] 97 | * @return [description] 98 | */ 99 | QByteArray readChunk(const QByteArray &ohash, int size = -1); 100 | 101 | /** 102 | * Write a chunk to the archive, and return its SHA-512 outer hash. 103 | * Returns an empty hash if the write fails (e.g., disk full). 104 | * @param ch [description] 105 | * @return [description] 106 | */ 107 | QByteArray writeChunk(const QByteArray &ch); 108 | 109 | /** 110 | * Test the integrity of the entire archive. 111 | * 112 | * XXX may take a long time - supply progress reports. 113 | */ 114 | bool test(); 115 | 116 | public: 117 | static Archive *primary; 118 | 119 | static void init(const QDir &appdir); 120 | }; 121 | -------------------------------------------------------------------------------- /ui/lib/audio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Audio I/O implementation using a portable RtAudio library. 3 | */ 4 | class Audio : public QObject 5 | { 6 | friend class AbstractAudioInput; 7 | friend class AbstractAudioOutput; 8 | friend class AudioInputMonitor; 9 | Q_OBJECT 10 | 11 | private: 12 | static int ndevs; 13 | static int indev, outdev; 14 | static QString indevname, outdevname; 15 | static QString errmsg; 16 | 17 | static QList instreams; 18 | static QList outstreams; 19 | 20 | 21 | Audio(); 22 | ~Audio(); 23 | 24 | public: 25 | static Audio *instance(); 26 | 27 | // Initialize the audio system and scan for available devices. 28 | // Returns the number of devices available (may be 0), or -1 on error. 29 | // The error string is set appropriately on either 0 or -1 return. 30 | static int scan(); 31 | 32 | // Re-scan to detect changes in the list of audio devices available. 33 | static int rescan(); 34 | 35 | // Return the number of audio devices available and the name of each. 36 | // (Some may be input-only or output-only.) 37 | static inline int numDevices() { return ndevs; } 38 | static QString deviceName(int dev); 39 | static QStringList deviceNames(); 40 | 41 | // Return models for the input and output device list 42 | static QAbstractItemModel *inputDeviceListModel(); 43 | static QAbstractItemModel *outputDeviceListModel(); 44 | 45 | // Find a device number by name, returning -1 if no such device exists. 46 | static int findDevice(const QString &name); 47 | 48 | // Get or set the currently selected input and output devices. 49 | static inline int inputDevice() { return indev; } 50 | static inline int outputDevice() { return outdev; } 51 | static void setInputDevice(int dev); 52 | static void setOutputDevice(int dev); 53 | static void setInputDevice(const QString &devname); 54 | static void setOutputDevice(const QString &devname); 55 | 56 | // Get the system-selected default input and output devices, 57 | // or -1 if no such device is available. 58 | static int defaultInputDevice(); 59 | static int defaultOutputDevice(); 60 | 61 | // Return number of input and output channels a given device supports 62 | static int inChannels(int dev); 63 | static int outChannels(int dev); 64 | 65 | // Return the minimum and maximum sample rates supported by a device 66 | static double minSampleRate(int dev); 67 | static double maxSampleRate(int dev); 68 | 69 | // Return the specific list of sample rates supported, 70 | // or the empty list if a continuous range is allowed. 71 | // This is for information only - the Audio class will (XXX should) 72 | // automatically re-sample to/from any desired rate. 73 | static QList sampleRates(int dev); 74 | 75 | static inline QString errorString() { return instance()->errmsg; } 76 | 77 | signals: 78 | // Signals for monitoring input/output level, measured in percent 79 | void inputLevelChanged(int level); 80 | void outputLevelChanged(int level); 81 | 82 | protected: 83 | void connectNotify(const char *signal); 84 | void disconnectNotify(const char *signal); 85 | 86 | private: 87 | static void open(); 88 | static void reopen(); 89 | static void close(); 90 | 91 | static int rtcallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData ); 92 | static void sendin(const float *inbuf); 93 | static void mixout(float *outbuf); 94 | 95 | static int computeLevel(const float *buf); 96 | static void setInputLevel(int lev); 97 | static void setOutputLevel(int lev); 98 | }; 99 | 100 | /** 101 | * Private helper class for input level monitoring. 102 | */ 103 | class AudioInputMonitor : public AudioInput 104 | { 105 | friend class Audio; 106 | 107 | private: 108 | AudioInputMonitor(); 109 | 110 | virtual void acceptInput(const float *buf); 111 | }; 112 | -------------------------------------------------------------------------------- /ui/lib/chunk.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/lib/chunk.cc -------------------------------------------------------------------------------- /ui/lib/chunk.h: -------------------------------------------------------------------------------- 1 | // Basic format defs - should be moved into a C-only header 2 | #define VXA_ARCHSIG 0x56584168 // 'VXAh' 3 | #define VXA_CHUNKSIG 0x56584163 // 'VXAc' 4 | 5 | #define VXA_IHASH_SIZE 32 // Size of inner hash (encrypt key) 6 | #define VXA_OHASH_SIZE 64 // Size of outer hash 7 | #define VXA_OCHECK_SIZE 8 // Size of outer hash check 8 | #define VXA_RECOG_SIZE 32 // Size of recognizer preamble 9 | 10 | 11 | /** 12 | * A 64-bit outer hash check is simply the first 8 bytes of a chunk's 512-bit outer hash. 13 | */ 14 | typedef quint64 OuterCheck; 15 | 16 | 17 | /** 18 | * Extract the 64-bit check field from a full outer hash. 19 | * @param ohash full 512-bit outer hash 20 | * @return 64-bit prefix 21 | */ 22 | OuterCheck hashCheck(const QByteArray &ohash); 23 | -------------------------------------------------------------------------------- /ui/lib/crypt.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include "crypt.h" 9 | #include "crypto/sha2.h" 10 | 11 | using namespace SST; 12 | 13 | 14 | static quint8 ctrinit[AES_BLOCK_SIZE] = "VXAcount"; 15 | 16 | void convDecrypt(const void *in, void *out, int size, const QByteArray &key) 17 | { 18 | // Set up the AES key schedule 19 | Q_ASSERT(key.size() == 32); 20 | AES_KEY akey; 21 | AES_set_encrypt_key((const unsigned char*)key.data(), 32*8, &akey); 22 | 23 | // Encrypt the block in CTR mode 24 | unsigned char ivec[AES_BLOCK_SIZE]; 25 | unsigned char ctr[AES_BLOCK_SIZE]; 26 | unsigned num = 0; 27 | memcpy(ivec, ctrinit, AES_BLOCK_SIZE); 28 | AES_ctr128_encrypt((const quint8*)in, (quint8*)out, size, 29 | &akey, ivec, ctr, &num); 30 | } 31 | 32 | QByteArray convEncrypt(const void *in, void *out, int size) 33 | { 34 | // First calculate the encryption key 35 | QByteArray key = Sha256::hash(in, size); 36 | 37 | // Then CTR-encrypt the block (same as decryption due to XOR) 38 | convDecrypt(in, out, size, key); 39 | 40 | return key; 41 | } 42 | 43 | 44 | QByteArray passEncrypt(const QByteArray &data, const QByteArray &key) 45 | { 46 | Q_ASSERT(false); 47 | return QByteArray(); 48 | } 49 | 50 | QByteArray passDecrypt(const QByteArray &data, const QByteArray &key) 51 | { 52 | Q_ASSERT(false); 53 | return QByteArray(); 54 | } 55 | 56 | QByteArray passCheck(const QByteArray &data, const QByteArray &key) 57 | { 58 | Q_ASSERT(false); 59 | return QByteArray(); 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /ui/lib/crypt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class QByteArray; 4 | 5 | /// Convergent encryption/decryption 6 | QByteArray convEncrypt(QByteArray &data); 7 | void convDecrypt(QByteArray &data, const QByteArray &key); 8 | 9 | QByteArray convEncrypt(const void *in, void *out, int size); 10 | void convDecrypt(const void *in, void *out, int size, 11 | const QByteArray &key); 12 | 13 | /// Passkey-based encryption/decryption 14 | QByteArray passEncrypt(const QByteArray &data, const QByteArray &key); 15 | QByteArray passDecrypt(const QByteArray &data, const QByteArray &key); 16 | QByteArray passCheck(const QByteArray &data, const QByteArray &key); 17 | -------------------------------------------------------------------------------- /ui/lib/env.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace SST { class Host; }; 4 | 5 | extern class SST::Host *ssthost; 6 | 7 | extern class QDir appdir; 8 | extern class QDir shareDir; /**< Directory for sharing files. */ 9 | extern class QSettings *settings; 10 | -------------------------------------------------------------------------------- /ui/lib/index.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "store.h" 4 | 5 | class FileInfo; 6 | 7 | /** 8 | * Local file index, providing: 9 | * - lookup by chunk hash of both file data chunks and metadata chunks 10 | * - lookup of the FileInfo representing a local file, by local pathname 11 | * 12 | * This module is thread-safe: can be called by different threads at any time. 13 | * It is a purely passive data repository, however: 14 | * higher layers are responsible for tracking file system changes. 15 | */ 16 | class Index : public Store 17 | { 18 | /** 19 | * Private constructor, to enforce only one instance 20 | */ 21 | Index(); 22 | 23 | virtual QByteArray readStore(const QByteArray &ohash); 24 | 25 | public: 26 | /** 27 | * Create if necessary and return the Index object instance. 28 | */ 29 | static Index *store(); 30 | static inline void init() { (void)store(); } 31 | 32 | /** 33 | * Register and retrieve metadata about scanned files 34 | * @param path [description] 35 | * @param info [description] 36 | * @return [description] 37 | */ 38 | static bool addFileInfo(const QString &path, const FileInfo &info); 39 | static FileInfo getFileInfo(const QString &path); 40 | 41 | /** 42 | * Register a data chunk existing in a local file. 43 | * @param ohash [description] 44 | * @param path [description] 45 | * @param ofs [description] 46 | * @param len [description] 47 | * @return [description] 48 | */ 49 | static bool addFileChunk(const QByteArray &ohash, const QString &path, 50 | qint64 ofs, int len); 51 | 52 | /** 53 | * Insert a metadata chunk's raw cyphertext into the chunk store. 54 | * @param ohash [description] 55 | * @param cyph [description] 56 | * @return [description] 57 | */ 58 | static bool addMetaChunk(const QByteArray &ohash, 59 | const QByteArray &cyph); 60 | 61 | /** 62 | * Update the file index to reflect a file rename. 63 | * @param oldpath [description] 64 | * @param newpath [description] 65 | */ 66 | static void fileMoved(const QString &oldpath, const QString &newpath); 67 | 68 | static QString errorString(); 69 | 70 | private: 71 | static QByteArray readFileChunk(const QByteArray &ohash); 72 | static QByteArray readMetaChunk(const QByteArray &ohash); 73 | 74 | private slots: 75 | void flushTimeout(); 76 | }; 77 | -------------------------------------------------------------------------------- /ui/lib/peer.h: -------------------------------------------------------------------------------- 1 | class PeerService : public QObject 2 | { 3 | QTimer recontimer; // To reconnect failed streams 4 | bool exclusive; 5 | 6 | // This stuff is not really needed for peerservice, only for peertable updater 7 | QPointer peers; // Peer table to track 8 | 9 | // For online status reporting 10 | int statcol; // PeerTable status column 11 | QVariant onlineval; // Value to set when online 12 | QVariant offlineval; // Value to set when offline 13 | 14 | 15 | public: 16 | /** 17 | * Set this PeerService to track the peers in a given PeerTable, 18 | * automatically maintaining an outgoing stream to each listed pear. 19 | * @param peers [description] 20 | */ 21 | void setPeerTable(PeerTable *peers); 22 | inline PeerTable *peerTable() { return peers; } 23 | 24 | /** 25 | * Set us up to keep a particular column of our PeerTable updated 26 | * with reports of the on-line status of this service for each peer. 27 | * Caller can optionally specify the particular display values to use 28 | * for "online" and "offline" status. 29 | * @param column [description] 30 | * @param onlineValue [description] 31 | * @param offlineValue [description] 32 | */ 33 | void setStatusColumn(int column, 34 | const QVariant &onlineValue = QVariant(), 35 | const QVariant &offlineValue = QVariant()); 36 | inline void clearStatusColumn() { setStatusColumn(-1); } 37 | 38 | /** 39 | * Return the name of a given peer, or 'defaultName' if unknown 40 | * @param hostId peer identifier. 41 | * @param defaultName default name to return in case no known name is found. 42 | * @return peer name. 43 | */ 44 | virtual QString peerName(const SST::PeerId &hostId, 45 | const QString &defaultName = tr("unknown peer")) const; 46 | 47 | /** 48 | * Return the name of a given peer, or its Base64 host ID if unknown. 49 | * @param hostId [description] 50 | * @return [description] 51 | */ 52 | inline QString peerNameOrId(const SST::PeerId &hostId) 53 | { return peerName(hostId, hostId.toString()); } 54 | 55 | protected: 56 | /** 57 | * Decide whether to allow an incoming stream from a particular host. 58 | * The default implementation returns true if 'exclusive' is not set; 59 | * otherwise it only returns true if the host is listed in 'peers'. 60 | * @param hostId [description] 61 | * @return [description] 62 | */ 63 | virtual bool allowConnection(const SST::PeerId &hostId); 64 | 65 | /** 66 | * Update the status indicators in our PeerTable for a given peer. 67 | * @param id [description] 68 | */ 69 | virtual void updateStatus(const SST::PeerId &id); 70 | 71 | /** 72 | * Update the status indicators for all peers in our PeerTable. 73 | */ 74 | void updateStatusAll(); 75 | 76 | private: 77 | void deletePrimary(); 78 | 79 | private slots: 80 | void peerInsert(const SST::PeerId &id); 81 | void peerRemove(const SST::PeerId &id); 82 | void outConnected(); 83 | void outDisconnected(); 84 | void inConnection(); 85 | void inDisconnected(); 86 | void reconTimeout(); 87 | }; 88 | -------------------------------------------------------------------------------- /ui/lib/rcsum.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "rcsum.h" 5 | 6 | 7 | // Initialize the rolling checksum over the first data slab. 8 | void RollingChecksum::init(const void *buffer, int slabstart, int slabend) 9 | { 10 | assert(slabstart < slabend); 11 | 12 | buf = (const uint8_t*)buffer; 13 | a = 0; 14 | b = 0; 15 | i = slabstart; 16 | for (j = i; j < slabend; j++) { 17 | a += buf[j]; 18 | b += a; 19 | } 20 | } 21 | 22 | int RollingChecksum::scanChunk(const void *data, int minsize, int maxsize) 23 | { 24 | assert(maxsize >= minsize); 25 | 26 | // Initialize the checksum over the first minsize bytes. 27 | RollingChecksum rc(data, minsize); 28 | 29 | // Now compute the rolling checksum for successive minsize-blocks, 30 | // recording the position resulting in the lowest checksum. 31 | uint32_t selsum = rc.sum(); 32 | int selsize = minsize; 33 | while (rc.slabEnd() < maxsize) { 34 | 35 | // Advance and adjust the checksum 36 | rc.advance(); 37 | uint32_t newsum = rc.sum(); 38 | 39 | // Pick the end of the minsize-block with lowest checksum 40 | // to be the end of the selected chunk. 41 | // Break checksum ties in favor of larger chunks. 42 | if (newsum <= selsum) { 43 | selsum = newsum; 44 | selsize = rc.slabEnd(); 45 | } 46 | 47 | // This assert is very bad for performance; 48 | // only uncomment it for sanity-checking purposes. 49 | //assert(rc.sum() == sumSlab(rc.buf+rc.slabStart(), minsize)); 50 | } 51 | 52 | return selsize; 53 | } 54 | 55 | uint32_t RollingChecksum::sumSlab(const void *data, int slabsize) 56 | { 57 | RollingChecksum rc(data, slabsize); 58 | return rc.sum(); 59 | } 60 | 61 | -------------------------------------------------------------------------------- /ui/lib/rcsum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | /** 7 | Simple 32-bit rolling checksum algorithm inspired by Adler-32, 8 | which can be easily rolled through a buffer at byte granularity. 9 | Not exactly Adler-32 because we use mod-65536 instead of mod-65521, 10 | for simplicity and efficiency of the checksum calculations. 11 | (The nice delayed modulo optimization in Adler-32 doesn't work 12 | when it's used as a rolling checksum, unfortunately.) 13 | */ 14 | class RollingChecksum 15 | { 16 | const uint8_t *buf; 17 | int i, j; 18 | unsigned a, b; 19 | 20 | 21 | public: 22 | // Setup the rolling checksum to use the specified buffer, 23 | // and initialize it with the specified slab start and end points. 24 | void init(const void *buf, int slabstart, int slabend); 25 | 26 | // Setup the rolling checksum to use the specified buffer, 27 | // and initialize it with the first 'slabsize' bytes of data. 28 | inline void init(const void *buf, int slabsize) 29 | { init(buf, 0, slabsize); } 30 | 31 | inline RollingChecksum() { buf = NULL; } 32 | inline RollingChecksum(const void *buf, int slabstart, int slabend) 33 | { init(buf, slabstart, slabend); } 34 | inline RollingChecksum(const void *buf, int slabsize) 35 | { init(buf, 0, slabsize); } 36 | 37 | // Return current slab position or size 38 | inline int slabStart() { return i; } 39 | inline int slabEnd() { return j; } 40 | inline int slabSize() { return j - i; } 41 | 42 | // Advance the slab by one byte and adjust the checksum 43 | inline void advance() { 44 | a = a - buf[i] + buf[j]; 45 | b = b - slabSize()*buf[i] + a; 46 | i++, j++; 47 | } 48 | 49 | // Advance to a specified start/end position 50 | inline void advanceStart(int newStart) { 51 | while (i < newStart) { advance(); } } 52 | inline void advanceEnd(int newEnd) { 53 | while (j < newEnd) { advance(); } } 54 | 55 | // Return the computed checksum value for the current slab 56 | inline uint32_t sum() { return (b << 16) | (a & 0xffff); } 57 | 58 | 59 | ///// Static methods ///// 60 | 61 | // Use a rolling checksum to choose an appropriate chunk size 62 | // for data starting at 'buf' and containing at least 'maxsize' bytes. 63 | // The returned chunk size will be between 'minsize' and 'maxsize', 64 | // and will correspond to the end of the 'minsize'-byte slab 65 | // having the minimum rolling checksum over all possible positions, 66 | // ensuring that chosen split points converge on similar content. 67 | // The 'minsize' should ideally be much more than 256 to ensure that 68 | // the rolling checksum has a decent distribution - see RFC 3309. 69 | static int scanChunk(const void *data, int minsize, int maxsize); 70 | 71 | // Compute a one-off checksum over a given data slab - 72 | // typically only used for sanity-checking. 73 | static uint32_t sumSlab(const void *data, int slabsize); 74 | }; 75 | -------------------------------------------------------------------------------- /ui/lib/scan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "action.h" 9 | #include "opaque.h" 10 | #include "file.h" 11 | 12 | class ScanItem; 13 | class Scanner; 14 | 15 | /** 16 | * Action: Asynchronous directory and file scanning. 17 | */ 18 | class Scan : public Action 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | enum Option { 24 | NoOptions = 0x00, 25 | ForceRescan = 0x01, // Rescan even if apparently unmodified 26 | }; 27 | Q_DECLARE_FLAGS(Options, Option) 28 | 29 | private: 30 | QString path; 31 | Options opts; 32 | QFileInfo qfi; 33 | FileInfo info; 34 | int nfiles, nerrors; 35 | qint64 gotbytes, totbytes; 36 | ScanItem *item; 37 | 38 | public: 39 | Scan(QObject *parent, const QString &path, Options opts = NoOptions); 40 | ~Scan(); 41 | virtual void start(); 42 | 43 | inline QString filePath() { return path; } 44 | inline FileInfo fileInfo() { return info; } 45 | 46 | inline int numFiles() { return nfiles; } 47 | inline int numErrors() { return nerrors; } 48 | inline qint64 numBytes() { return gotbytes; } 49 | inline qint64 totalBytes() { return totbytes; } 50 | 51 | private: 52 | void scanFile(const QFileInfo &qfi); 53 | void scanDir(const QFileInfo &qfi); 54 | void report(); 55 | void reportDone(); 56 | 57 | private slots: 58 | void fileProgress(); 59 | void dirProgress(); 60 | }; 61 | 62 | Q_DECLARE_OPERATORS_FOR_FLAGS(Scan::Options) 63 | 64 | 65 | /** 66 | * Private helper class representing a work item for the scanner thread 67 | */ 68 | class ScanItem : public QObject 69 | { 70 | friend class Scan; 71 | friend class Scanner; 72 | friend class ScanCoder; 73 | Q_OBJECT 74 | 75 | private: 76 | const QString path; 77 | OpaqueInfo datainfo; 78 | QString errmsg; 79 | qint64 gotbytes; 80 | bool done; 81 | bool cancel; 82 | 83 | inline ScanItem(Scan *scan); 84 | 85 | signals: 86 | void progress(); 87 | }; 88 | 89 | 90 | /** 91 | * Private helper class representing the asynchronous scanner thread. 92 | */ 93 | class Scanner : public QThread 94 | { 95 | friend class Scan; 96 | friend class ScanCoder; 97 | 98 | QMutex mutex; 99 | QWaitCondition cond; 100 | 101 | 102 | Scanner(); 103 | virtual void run(); 104 | 105 | ScanItem *getwork(); 106 | void scan(Scan::Options opts); 107 | void scanFile(ScanItem *item); 108 | }; 109 | 110 | 111 | /** 112 | * OpaqueCoder subclass that chunkifies and encodes opaque data 113 | * and adds the appropriate chunks to the ShareStore. 114 | */ 115 | class ScanCoder : public OpaqueCoder 116 | { 117 | ScanItem *item; 118 | 119 | virtual bool dataChunk(const QByteArray &chunk, 120 | const OpaqueKey &key, 121 | qint64 ofs, qint32 size); 122 | virtual bool keyChunk(const QByteArray &chunk, 123 | const OpaqueKey &key, 124 | const QList &subkeys); 125 | 126 | public: 127 | inline ScanCoder(QObject *parent, ScanItem *item) 128 | : OpaqueCoder(parent), item(item) { } 129 | }; 130 | -------------------------------------------------------------------------------- /ui/lib/share.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "share.h" 5 | #include "index.h" 6 | 7 | 8 | static QHash filehash; 9 | 10 | 11 | ////////// Share ////////// 12 | 13 | Share::Share(QObject *parent, const QString &root_) 14 | : QObject(parent) 15 | , root(root_) 16 | { 17 | insert(ShareFile::file(root, true)); 18 | } 19 | 20 | Share::~Share() 21 | { 22 | foreach (ShareFile *sf, files) 23 | { 24 | Q_ASSERT(sf->shares.contains(this)); 25 | sf->shares.remove(this); 26 | } 27 | files.clear(); 28 | } 29 | 30 | void Share::insert(ShareFile *sf, bool recursive) 31 | { 32 | files.insert(sf); 33 | sf->shares.insert(this); 34 | 35 | if (recursive) { 36 | foreach (ShareFile *subsf, sf->subs.values()) { 37 | if (!exclude(subsf->path)) 38 | insert(subsf); 39 | } 40 | } 41 | } 42 | 43 | void Share::remove(ShareFile *sf, bool recursive) 44 | { 45 | if (!files.contains(sf)) { 46 | Q_ASSERT(!sf->shares.contains(this)); 47 | return; 48 | } 49 | 50 | Q_ASSERT(sf->shares.contains(this)); 51 | files.remove(sf); 52 | sf->shares.remove(this); 53 | 54 | if (recursive) { 55 | foreach (ShareFile *subsf, sf->subs.values()) 56 | remove(subsf, true); 57 | } 58 | } 59 | 60 | bool Share::exclude(const QString &) 61 | { 62 | return false; 63 | } 64 | 65 | 66 | ////////// ShareFile ////////// 67 | 68 | ShareFile::ShareFile(const QString &path) 69 | : path(path) 70 | { 71 | // Insert us into the global file table. 72 | Q_ASSERT(!filehash.contains(path)); 73 | filehash.insert(path, this); 74 | 75 | // If we already have a parent, notify its Shares of a new child. 76 | checkparent(); 77 | } 78 | 79 | ShareFile::~ShareFile() 80 | { 81 | // Remove us from any Shares we are in 82 | foreach (Share *sh, shares) 83 | sh->remove(this); 84 | Q_ASSERT(shares.isEmpty()); 85 | 86 | // Remove us from the global file table. 87 | Q_ASSERT(filehash.value(path) == this); 88 | filehash.remove(path); 89 | } 90 | 91 | QString ShareFile::fileName() 92 | { 93 | int slashpos = path.lastIndexOf('/'); 94 | if (slashpos < 0) 95 | return path; 96 | 97 | return path.mid(slashpos+1); 98 | } 99 | 100 | ShareFile *ShareFile::parent() 101 | { 102 | int slashpos = path.lastIndexOf('/'); 103 | if (slashpos < 0) 104 | return NULL; 105 | 106 | return file(path.mid(0, slashpos), false); 107 | } 108 | 109 | void ShareFile::update(const FileInfo &newInfo, const QDateTime newModTime) 110 | { 111 | info = newInfo; 112 | modtime = newModTime; 113 | 114 | checkparent(); 115 | emit updated(); 116 | } 117 | 118 | void ShareFile::checkparent() 119 | { 120 | ShareFile *par = parent(); 121 | if (!par) 122 | return; 123 | 124 | QString name = fileName(); 125 | ShareFile *child = par->subs.value(name); 126 | if (!child) { 127 | par->subs.insert(name, this); 128 | emit par->newChild(path); 129 | } else { 130 | Q_ASSERT(child == this); 131 | } 132 | } 133 | 134 | ShareFile *ShareFile::file(const QString &canonPath, bool create) 135 | { 136 | ShareFile *sf = filehash.value(canonPath); 137 | if (sf || !create) 138 | return sf; 139 | 140 | sf = new ShareFile(canonPath); 141 | filehash.insert(canonPath, sf); 142 | return sf; 143 | } 144 | 145 | 146 | -------------------------------------------------------------------------------- /ui/lib/share.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "file.h" 11 | 12 | class Share; 13 | class ShareFile; 14 | 15 | /** 16 | Sharing of files and directory trees on the local file system. 17 | This is the basis for filesync/backup. 18 | */ 19 | class Share : public QObject 20 | { 21 | friend class ShareFile; 22 | Q_OBJECT 23 | 24 | const QString &root; 25 | QSet files; 26 | 27 | public: 28 | Share(QObject *parent, const QString &root); 29 | ~Share(); 30 | 31 | // Overridable method to determine whether to exclude a given subtree. 32 | // The default implementation always returns false. 33 | virtual bool exclude(const QString &path); 34 | 35 | private: 36 | void insert(ShareFile *sf, bool recursive = true); 37 | void remove(ShareFile *sf, bool recursive = true); 38 | }; 39 | 40 | 41 | /** 42 | * The Share class represents a shareable object on a local file system. 43 | */ 44 | class ShareFile : public QObject 45 | { 46 | friend class Share; 47 | Q_OBJECT 48 | 49 | const QString path; 50 | QSet shares; // Shares interested in this file 51 | QHash subs; 52 | FileInfo info; 53 | QDateTime modtime; 54 | 55 | 56 | public: 57 | 58 | // Called when a possible change has been detected to the file, 59 | // meaning it probably needs to be re-scanned. 60 | //void markRescan() { } // XXX 61 | 62 | // Returns the FileInfo metadata describing this file. 63 | inline FileInfo fileInfo() { return info; } 64 | inline QString filePath() { return path; } 65 | QString fileName(); // Last component only 66 | inline QDateTime lastModified() { return modtime; } 67 | 68 | // Update the tree summary information for this file. 69 | void update(const FileInfo &newInfo, const QDateTime newModTime); 70 | 71 | // Find the parent directory containing this ShareFile, if any. 72 | // Returns NULL if none or not yet created. 73 | ShareFile *parent(); 74 | 75 | // Find or create if necessary the ShareFile for a given canonical path 76 | static ShareFile *file(const QString &canonPath, bool create); 77 | 78 | signals: 79 | void updated(); 80 | void newChild(const QString &path); 81 | 82 | private: 83 | ShareFile(const QString &path); 84 | ~ShareFile(); 85 | 86 | void checkparent(); 87 | }; 88 | -------------------------------------------------------------------------------- /ui/lib/store.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "store.h" 5 | 6 | 7 | ////////// Store ////////// 8 | 9 | QList Store::stores; 10 | 11 | // Cache size: enough for 10 max-size chunks 12 | static QCache chunkcache(10*1024*1024); 13 | 14 | Store::Store() 15 | { 16 | Q_ASSERT(!stores.contains(this)); 17 | stores.append(this); 18 | } 19 | 20 | Store::~Store() 21 | { 22 | int i = stores.indexOf(this); 23 | Q_ASSERT(i >= 0); 24 | stores.removeAt(i); 25 | } 26 | 27 | ChunkSearch *Store::searchStore(const QByteArray &, QObject *, const char *) 28 | { 29 | return NULL; 30 | } 31 | 32 | QByteArray Store::readStores(const QByteArray &ohash) 33 | { 34 | // Return a copy of the chunk from our cache, if available 35 | QByteArray *dp = chunkcache.object(ohash); 36 | if (dp != NULL) 37 | return *dp; 38 | 39 | // XX On success, bring succeeding store to the front? 40 | foreach (Store *st, stores) { 41 | QByteArray ba = st->readStore(ohash); 42 | if (!ba.isEmpty()) { 43 | chunkcache.insert(ohash, new QByteArray(ba), ba.size()); 44 | return ba; 45 | } 46 | } 47 | return QByteArray(); 48 | } 49 | 50 | ChunkSearch *Store::searchStores(const QByteArray &ohash, 51 | QObject *parent, const char *slot) 52 | { 53 | StoresSearch *srch = new StoresSearch(parent); 54 | connect(srch, SIGNAL(relayFound(const QByteArray&)), parent, slot); 55 | 56 | foreach (Store *st, stores) { 57 | ChunkSearch *cs = st->searchStore(ohash, srch, 58 | SLOT(storeFound(const QByteArray&))); 59 | if (cs) 60 | srch->subs.append(cs); 61 | } 62 | 63 | return srch; 64 | } 65 | 66 | 67 | ////////// StoresSearch ////////// 68 | 69 | bool StoresSearch::done() 70 | { 71 | foreach (ChunkSearch *cs, subs) 72 | if (cs and !cs->done()) 73 | return false; 74 | return true; 75 | } 76 | 77 | void StoresSearch::storeDone(const QByteArray &data) 78 | { 79 | if (data.isEmpty()) { 80 | if (!done()) 81 | return; // Ignore all failures but last 82 | } else { 83 | if (done()) 84 | return; // Already returned data 85 | } 86 | 87 | // Schedule this search object and its sub-searches for deletion 88 | cancel(); 89 | 90 | // Relay the results 91 | relayDone(data); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /ui/lib/store.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class QByteArray; 8 | 9 | /** 10 | * Represents an in-progress asynchronous search for a chunk. 11 | */ 12 | struct ChunkSearch : public QObject 13 | { 14 | inline ChunkSearch(QObject *parent) : QObject(parent) { } 15 | 16 | inline void cancel() { deleteLater(); } 17 | 18 | virtual bool done() = 0; 19 | }; 20 | 21 | /** 22 | * Abstract base class for objects providing chunk storage and lookup. 23 | * A Store is an abstract repository from which chunks can be looked up. 24 | * This module keeps a systemwide list of all instantiated Store objects, 25 | * and provides static methods to search all of them for a desired chunk. 26 | */ 27 | class Store : public QObject 28 | { 29 | static QList stores; 30 | 31 | public: 32 | Store(); 33 | virtual ~Store(); 34 | 35 | // Read the contents of a given chunk, if it's locally available. 36 | // Returns an empty QByteArray if not immediately available. 37 | virtual QByteArray readStore(const QByteArray &ohash) = 0; 38 | 39 | // Initiate a request to find a given chunk, remotely if necessary. 40 | // Signals the specified slot of the specified parent object 41 | // with the chunk contents (QByteArray) are found or the search fails. 42 | // The resulting ChunkSearch also becomes of a child of 'parent', 43 | // thus canceling the search if 'parent' is deleted. 44 | // The default implementation returns NULL, 45 | // indicating this store doesn't support asynchronous lookup. 46 | virtual ChunkSearch *searchStore(const QByteArray &ohash, 47 | QObject *parent, const char *slot); 48 | 49 | // Write a chunk into this store. 50 | //virtual bool writeChunk(const QByteArray &data) = 0; 51 | 52 | 53 | ///// Static methods ///// 54 | 55 | // Search through all stores for a locally-available chunk. 56 | static QByteArray readStores(const QByteArray &ohash); 57 | 58 | // Initiate an asynchronous request for a chunk, 59 | // searching all available stores in parallel. 60 | static ChunkSearch *searchStores(const QByteArray &ohash, 61 | QObject *receiver, const char *slot); 62 | }; 63 | 64 | #if 0 65 | // A UnionStore represents the logical union of one or more sub-Stores. 66 | class UnionStore : public Store 67 | { 68 | virtual QByteArray readStore(const QByteArray &ohash); 69 | 70 | virtual ChunkSearch *searchStore(const QByteArray &ohash, 71 | QObject *parent, const char *slot); 72 | 73 | 74 | public: 75 | }; 76 | #endif 77 | 78 | /** 79 | * Private helper class for Store 80 | */ 81 | class StoresSearch : public ChunkSearch 82 | { 83 | friend class Store; 84 | Q_OBJECT 85 | 86 | QList > subs; 87 | 88 | 89 | inline StoresSearch(QObject *parent) : ChunkSearch(parent) { } 90 | 91 | virtual bool done(); 92 | 93 | private slots: 94 | void storeDone(const QByteArray &data); 95 | 96 | signals: 97 | void relayDone(const QByteArray &data); 98 | }; 99 | -------------------------------------------------------------------------------- /ui/lib/update.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "action.h" 6 | #include "opaque.h" 7 | #include "file.h" 8 | 9 | /** 10 | * Action: update a local file or directory tree from the network 11 | */ 12 | class Update : public Action 13 | { 14 | Q_OBJECT 15 | 16 | class FileReader : public AbstractOpaqueReader 17 | { 18 | Update *up; 19 | QTemporaryFile tmp; 20 | 21 | virtual void gotData2(const QByteArray &ohash, 22 | qint64 ofs, qint64 recno, 23 | const QByteArray &data, int nrecs); 24 | void gotMetaData(const QByteArray &ohash, 25 | const QByteArray &chunkdata, 26 | qint64 ofs, qint64 recno, 27 | qint64 size, int nrecs); 28 | virtual void noData2(const QByteArray &ohash, 29 | qint64 ofs, qint64 recno, 30 | qint64 size, int nrecs); 31 | void readDone(); 32 | 33 | void error(const QString &msg); 34 | 35 | public: 36 | FileReader(Update *update); 37 | ~FileReader(); 38 | 39 | void start(); 40 | }; 41 | 42 | class DirReader : public AbstractDirReader 43 | { 44 | Update *up; 45 | bool dirdone; 46 | 47 | virtual void gotData(const QByteArray &ohash, const QByteArray &data); 48 | void gotEntries(int pos, qint64 recno, const QList &ents); 49 | void noEntries(int pos, qint64 recno, int nents); 50 | void readDone(); 51 | 52 | public: 53 | DirReader(Update *update); 54 | 55 | void start(); 56 | void check(); 57 | }; 58 | 59 | const FileInfo info; 60 | const QString path; 61 | qint64 totbytes, gotbytes; 62 | int nerrors; 63 | FileReader *fr; 64 | DirReader *dr; 65 | 66 | public: 67 | Update(QObject *parent, const FileInfo &info, const QString &path); 68 | 69 | virtual void start(); 70 | 71 | private: 72 | void report(); 73 | 74 | private slots: 75 | void subChanged(); 76 | }; 77 | -------------------------------------------------------------------------------- /ui/qt5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Qt5 - http://qt-project.org/doc/qt-5.0/qtdoc/cmake-manual.html 2 | set(CMAKE_PREFIX_PATH /usr/local/opt/qt5/) 3 | 4 | # Tell CMake to run moc when necessary: 5 | set(CMAKE_AUTOMOC ON) 6 | # As moc files are generated in the binary dir, tell CMake 7 | # to always look for includes there: 8 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 9 | 10 | # Widgets finds its own dependencies. 11 | find_package(Qt5Widgets REQUIRED) 12 | 13 | set(mettanode_SOURCES 14 | main.cpp 15 | ProfileEditor.cpp 16 | ContactModel.cpp 17 | CallService.cpp 18 | QmlBasedWindow.cpp 19 | MainWindow.cpp 20 | CallWindow.cpp 21 | XcpApplication.cpp 22 | Meter.cpp) 23 | set(mettanode_FORMS 24 | ProfileEditor.ui 25 | SingleCallWidget.ui) 26 | 27 | set(mettanode_RESOURCES ui.qrc) 28 | 29 | if (APPLE) 30 | list(APPEND mettanode_SOURCES macosx.mm) 31 | qt5_wrap_cpp(mettanode_HEADERS_MOC macsupport.h) 32 | set(MACOSX_BUNDLE_ICON_FILE "mettanode.icns") 33 | set_source_files_properties("img/${MACOSX_BUNDLE_ICON_FILE}" PROPERTIES 34 | MACOSX_PACKAGE_LOCATION Resources) 35 | set(APPLE_BUNDLE_SRC "img/${MACOSX_BUNDLE_ICON_FILE}") 36 | endif (APPLE) 37 | 38 | set(mettanode_RESOURCES ui.qrc) 39 | qt5_add_resources(mettanode_RESOURCES_RCC ${mettanode_RESOURCES}) 40 | 41 | add_executable(MettaNode ${GUI_TYPE} 42 | ${mettanode_SOURCES} 43 | ${mettanode_HEADERS_MOC} 44 | ${mettanode_RESOURCES} 45 | ${mettanode_RESOURCES_RCC} 46 | ${mettanode_FORMS} 47 | ${APPLE_BUNDLE_SRC}) 48 | target_link_libraries(MettaNode krypto rclient routing sss arsenal natclient nat upnpc 49 | ${VOICEBOX_LIBS} opus 50 | ${Boost_LIBRARIES} ${QT_LIBRARIES} ${OPENSSL_LIBRARIES}) 51 | 52 | qt5_use_modules(MettaNode Widgets Quick Network) 53 | qt5_wrap_ui(mettanode_FORMS_HEADERS ${mettanode_FORMS}) 54 | 55 | if (WIN32) 56 | target_link_libraries(MettaNode ws2_32 gdi32 winmm) 57 | # RC_FILE = gui.rc 58 | endif (WIN32) 59 | 60 | if (APPLE) 61 | find_library(COREFOUNDATION_LIB CoreFoundation) 62 | find_library(COREAUDIO_LIB CoreAudio) 63 | find_library(AUDIOTOOLBOX_LIB AudioToolbox) 64 | find_library(COCOA_LIB Cocoa) 65 | target_link_libraries(MettaNode 66 | ${COREFOUNDATION_LIB} ${COREAUDIO_LIB} ${AUDIOTOOLBOX_LIB} ${COCOA_LIB}) 67 | set_target_properties(MettaNode PROPERTIES 68 | MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/OSXInfo.plist) 69 | endif (APPLE) 70 | 71 | install(TARGETS MettaNode 72 | RUNTIME DESTINATION . 73 | BUNDLE DESTINATION . 74 | LIBRARY DESTINATION lib 75 | ARCHIVE DESTINATION lib) 76 | 77 | -------------------------------------------------------------------------------- /ui/qt5/CallService.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "CallService.h" 10 | 11 | using namespace std; 12 | using namespace sss; 13 | 14 | CallService::CallService(HostState& s, QObject *parent) 15 | : QObject(parent) 16 | , audioclient_(s.host()) 17 | { 18 | audioclient_.on_session_started.connect([this] { 19 | // Less detailed logging while in real-time 20 | logger::set_verbosity(logger::verbosity::warnings); 21 | // Todo: post this to GUI thread? Signals work fine across threads boundaries. 22 | emit callStarted(); 23 | }); 24 | 25 | audioclient_.on_session_finished.connect([this] { 26 | // More detailed logging while not in real-time 27 | logger::set_verbosity(logger::verbosity::debug); 28 | // Todo: post this to GUI thread? Signals work fine across threads boundaries. 29 | emit callFinished(); 30 | }); 31 | 32 | audioclient_.listen_incoming_session(); 33 | 34 | // connect(actionCall, SIGNAL(triggered()), this, SLOT(call())); 35 | } 36 | 37 | void CallService::makeCall(QString callee_eid) 38 | { 39 | if (isCallActive()) 40 | return; 41 | 42 | audioclient_.establish_outgoing_session( 43 | string(callee_eid.toUtf8().constData()), vector()); 44 | } 45 | 46 | void CallService::hangUp() 47 | { 48 | if (isCallActive()) { 49 | audioclient_.end_session(); 50 | } 51 | } 52 | 53 | bool CallService::isCallActive() const 54 | { 55 | return audioclient_.is_active(); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /ui/qt5/CallService.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "voicebox/audio_service.h" 4 | #include "HostState.h" 5 | #include 6 | 7 | class CallService : public QObject 8 | { 9 | Q_OBJECT 10 | 11 | voicebox::audio_service audioclient_; 12 | 13 | public: 14 | CallService(HostState& s, QObject *parent = nullptr); 15 | 16 | bool isCallActive() const; 17 | 18 | public slots: 19 | void makeCall(QString callee_eid); 20 | void hangUp(); 21 | 22 | signals: 23 | void callStarted(); 24 | void callFinished(); 25 | }; 26 | -------------------------------------------------------------------------------- /ui/qt5/CallWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "CallWindow.h" 2 | 3 | CallWindow::CallWindow() 4 | : QmlBasedWindow("qrc:/quick/CallWindow.qml") 5 | {} 6 | -------------------------------------------------------------------------------- /ui/qt5/CallWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QmlBasedWindow.h" 4 | 5 | class CallWindow : public QObject, public QmlBasedWindow 6 | { 7 | Q_OBJECT 8 | 9 | public: 10 | CallWindow(); 11 | }; 12 | -------------------------------------------------------------------------------- /ui/qt5/ChatHistory.cpp: -------------------------------------------------------------------------------- 1 | #include "chathistory.h" 2 | #include "mk4.h" 3 | #include 4 | 5 | //probably add database encryption directly to Metakit if not yet supported? 6 | class ChatHistory::Private 7 | { 8 | public: 9 | std::unique_ptr storage; 10 | std::unique_ptr view; 11 | 12 | Private(sss::peer_identity const& id) 13 | { 14 | appdir.mkdir("ChatHistory"); 15 | // Use base32 for naming the files, it's more filesystem-compatible. 16 | // this here uses proquint currently. 17 | QString filename = QString::fromUtf8(id.to_string().c_str()); 18 | QString name = appdir.path() + "/ChatHistory/history." + filename; 19 | 20 | storage = make_unique(name.toLocal8Bit().constData(), /*read-write-flag:*/ 1); 21 | view = make_unique( 22 | storage->GetAs( 23 | "chathistory[" 24 | "unread:B," 25 | "originator:S," 26 | "originator_eid:S," 27 | "timestamp:T," 28 | "target:S," 29 | "msg_hash:S," 30 | "msg:S]")); 31 | } 32 | }; 33 | 34 | ChatHistory::ChatHistory(sss::peer_identity const& id, QObject* parent) 35 | : QObject(parent) 36 | , m_pimpl(std::make_shared(id)) 37 | {} 38 | 39 | ChatHistory::~ChatHistory() 40 | { 41 | m_pimpl->storage->Commit(); 42 | } 43 | 44 | void ChatHistory::insertHistoryLine(const QString& originator, const QDateTime timestamp, const QString& message) 45 | { 46 | c4_Row row; 47 | c4_StringProp pOriginator("originator"); 48 | c4_StringProp pMsg("msg"); 49 | 50 | pOriginator(row) = originator.toUtf8().constData(); 51 | pMsg(row) = message.toUtf8().constData(); 52 | 53 | m_pimpl->view->Add(row); 54 | } 55 | 56 | void ChatHistory::newHistorySynced() 57 | { 58 | } 59 | -------------------------------------------------------------------------------- /ui/qt5/ChatHistory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "peer_identity.h" 7 | 8 | // For now, store a file per chat ID. 9 | class ChatHistory : public QObject 10 | { 11 | Q_OBJECT 12 | 13 | class Private; 14 | std::shared_ptr m_pimpl; 15 | 16 | public: 17 | /** 18 | * @param id binary chat identifier. 19 | */ 20 | ChatHistory(sss::peer_identity const& id, QObject* parent = 0); 21 | ~ChatHistory(); 22 | 23 | public slots: 24 | void insertHistoryLine(const QString& originator, const QDateTime timestamp, const QString& message); 25 | 26 | private slots: 27 | void newHistorySynced(); 28 | }; 29 | -------------------------------------------------------------------------------- /ui/qt5/ChatModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | // #include 13 | 14 | class settings_provider; 15 | namespace sss { 16 | class host; 17 | class peer_identity; 18 | } 19 | 20 | /** 21 | * Qt item model describing a list of chat messages. 22 | * There are roles bindings for QML: 23 | * 24 | * - avatarUrl 25 | * 26 | * An arbitrary number of additional columns can hold dynamic content: 27 | * e.g., online status information about each peer. 28 | */ 29 | class ChatModel : public QAbstractItemModel 30 | { 31 | Q_OBJECT 32 | class Private; 33 | std::shared_ptr m_pimpl; 34 | 35 | public: 36 | ChatModel(std::shared_ptr h, QObject *parent = 0); 37 | 38 | // QAbstractItemModel methods for QML: 39 | QHash roleNames() const override; 40 | 41 | QModelIndex parent(const QModelIndex &child) const override; 42 | QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; 43 | 44 | // AbstractTableModel methods; 45 | int rowCount(const QModelIndex &parent) const override; 46 | int columnCount(const QModelIndex &parent) const override; 47 | QVariant data(const QModelIndex &index, int role) const override; 48 | Qt::ItemFlags flags(const QModelIndex &index) const override; 49 | bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override; 50 | bool insertRows(int position, int rows, const QModelIndex &index=QModelIndex()) override; 51 | bool removeRows(int position, int rows, const QModelIndex &index=QModelIndex()) override; 52 | 53 | int count() const; 54 | bool setFlags(const QModelIndex &index, Qt::ItemFlags flags); 55 | 56 | // private: 57 | // Internal use only. 58 | void updateData(int row); 59 | private: 60 | void insertAt(int row, sss::peer_identity const& eid, QString const& name); 61 | 62 | signals: 63 | // These signals provide slightly simpler alternatives to 64 | // beginInsertRow, endInsertRow, beginInsertColumn, endInsertColumn. 65 | void peerInserted(sss::peer_identity const& eid); 66 | void peerRemoved(sss::peer_identity const& eid); 67 | }; 68 | -------------------------------------------------------------------------------- /ui/qt5/ChatWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "ChatWindow.h" 2 | #include "ChatModel.h" 3 | #include 4 | #include 5 | 6 | ChatWindow::ChatWindow(ChatModel* model) 7 | : QmlBasedWindow("qrc:/quick/ChatWindow.qml") 8 | { 9 | context_->setContextProperty("chatModel", model); 10 | QObject::connect(window_, SIGNAL(sendMessage(QString)), 11 | this, SLOT(sendMessage(QString))); 12 | } 13 | 14 | void ChatWindow::sendMessage(QString const& text) 15 | { 16 | qDebug() << "Sending chat message " << text; 17 | } 18 | -------------------------------------------------------------------------------- /ui/qt5/ChatWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QmlBasedWindow.h" 4 | 5 | class ChatModel; 6 | 7 | class ChatWindow : public QObject, public QmlBasedWindow 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | ChatWindow(ChatModel* model); 13 | 14 | public slots: 15 | void sendMessage(QString const& msg); 16 | }; 17 | -------------------------------------------------------------------------------- /ui/qt5/HostState.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "sss/host.h" 5 | #include "arsenal/settings_provider.h" 6 | #include "traverse_nat.h" 7 | 8 | /** 9 | * HostState embeds settings, NAT traversal and io_service runner for the SSS host. 10 | */ 11 | class HostState 12 | { 13 | std::shared_ptr settings_; 14 | std::shared_ptr host_; 15 | std::shared_ptr nat_; 16 | std::thread runner_; // Thread to run io_service (@todo Could be a thread pool) 17 | 18 | public: 19 | HostState() 20 | : settings_(settings_provider::instance()) 21 | , host_(sss::host::create(settings_)) 22 | , runner_([this] { host_->run_io_service(); }) 23 | { 24 | nat_ = traverse_nat(host_); 25 | } 26 | 27 | ~HostState() 28 | { 29 | host_->get_io_service().stop(); 30 | runner_.join(); 31 | } 32 | 33 | inline std::shared_ptr host() const { return host_; } 34 | inline std::shared_ptr settings() const { return settings_; } 35 | }; 36 | 37 | -------------------------------------------------------------------------------- /ui/qt5/MainWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "QmlBasedWindow.h" 4 | 5 | class ContactModel; 6 | 7 | class MainWindow : public QObject, public QmlBasedWindow 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | MainWindow(ContactModel* model); 13 | 14 | public slots: 15 | void startCall(QString const& eid); 16 | }; 17 | -------------------------------------------------------------------------------- /ui/qt5/Meter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Meter.h" 3 | 4 | Meter::Meter(QWidget *parent) 5 | : QWidget(parent) 6 | , min_(0) 7 | , max_(0) 8 | , value_(0) 9 | { 10 | setMinimumSize(50, 10); 11 | setMaximumSize(QWIDGETSIZE_MAX, 10); 12 | } 13 | 14 | void Meter::setValue(int val) 15 | { 16 | value_ = val; 17 | update(); 18 | } 19 | 20 | void Meter::setRange(int min, int max) 21 | { 22 | min_ = min; 23 | max_ = max; 24 | update(); 25 | } 26 | 27 | void Meter::paintEvent(QPaintEvent *) 28 | { 29 | if (min_ >= max_) { 30 | return; 31 | } 32 | float frac = (float)(value_ - min_) / (float)(max_ - min_); 33 | 34 | QPainter p(this); 35 | 36 | int nticks = width() / 10; 37 | int nfill = (int)(nticks * frac); 38 | for (int i = 0; i < nticks; i++) 39 | { 40 | p.setBrush(i < nfill ? Qt::cyan : Qt::darkBlue); 41 | p.setPen(Qt::darkCyan); 42 | p.drawRect(10*i, 0, 6, height()-1); 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /ui/qt5/Meter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Meter : public QWidget 6 | { 7 | Q_OBJECT 8 | 9 | private: 10 | int min_, max_, value_; 11 | 12 | public: 13 | Meter(QWidget *parent = NULL); 14 | 15 | inline int minimum() { return min_; } 16 | inline int maximum() { return max_; } 17 | inline int value() { return value_; } 18 | 19 | public slots: 20 | void setRange(int min, int max); 21 | void setValue(int val); 22 | 23 | protected: 24 | virtual void paintEvent(QPaintEvent *event); 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /ui/qt5/OSXInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleInfoDictionaryVersion 6 | 6.0 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundlePackageType 10 | APPL 11 | CFBundleExecutable 12 | ${MACOSX_BUNDLE_EXECUTABLE_NAME} 13 | CFBundleIdentifier 14 | ${MACOSX_BUNDLE_GUI_IDENTIFIER} 15 | CFBundleName 16 | ${MACOSX_BUNDLE_BUNDLE_NAME} 17 | CFBundleIconFile 18 | ${MACOSX_BUNDLE_ICON_FILE} 19 | NSPrincipalClass 20 | NSApplication 21 | NSHighResolutionCapable 22 | True 23 | 24 | -------------------------------------------------------------------------------- /ui/qt5/ProfileEditor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "ui_ProfileEditor.h" 5 | 6 | /** 7 | * Simple client_profile editor. 8 | * Open settings_provider with default settings (or a specified settings file), 9 | * display current data and allow editing profile fields using Qt4 gui. 10 | * Save profile back. 11 | */ 12 | class ProfileEditor : public QWidget, private Ui::ProfileEditor 13 | { 14 | Q_OBJECT 15 | class Private; 16 | std::shared_ptr m_pimpl; 17 | 18 | public: 19 | ProfileEditor(QWidget *parent = 0); 20 | 21 | signals: 22 | void profileChanged(); 23 | 24 | public slots: 25 | void load(); 26 | void save(); 27 | void generateNewEid(); 28 | void setStatus(QString const& text); 29 | void clearStatus(); 30 | }; 31 | -------------------------------------------------------------------------------- /ui/qt5/QmlBasedWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "QmlBasedWindow.h" 2 | #include 3 | #include 4 | 5 | QmlBasedWindow::QmlBasedWindow(QString qmlFile) 6 | : engine_() 7 | , component_(&engine_) 8 | { 9 | context_ = new QQmlContext(engine_.rootContext()); 10 | component_.loadUrl(QUrl(qmlFile)); 11 | 12 | if (!component_.isReady() ) { 13 | qFatal("%s", component_.errorString().toUtf8().constData()); 14 | } 15 | 16 | QObject *topLevel = component_.create(context_); 17 | window_ = qobject_cast(topLevel); 18 | 19 | QSurfaceFormat surfaceFormat = window_->requestedFormat(); 20 | window_->setFormat(surfaceFormat); 21 | } 22 | -------------------------------------------------------------------------------- /ui/qt5/QmlBasedWindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class QmlBasedWindow 8 | { 9 | protected: 10 | QQmlEngine engine_; 11 | QQmlComponent component_; 12 | QQuickWindow* window_{nullptr}; 13 | QQmlContext *context_{nullptr}; 14 | 15 | public: 16 | QmlBasedWindow(QString qmlFile); 17 | inline void show() { window_->show(); } 18 | }; 19 | -------------------------------------------------------------------------------- /ui/qt5/SingleCallWidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | SingleCallWidget 4 | 5 | 6 | 7 | 0 8 | 0 9 | 354 10 | 106 11 | 12 | 13 | 14 | Call with 15 | 16 | 17 | 18 | 2 19 | 20 | 21 | 2 22 | 23 | 24 | 2 25 | 26 | 27 | 2 28 | 29 | 30 | 2 31 | 32 | 33 | 34 | 35 | 36 | 96 37 | 96 38 | 39 | 40 | 41 | TextLabel 42 | 43 | 44 | Qt::AlignCenter 45 | 46 | 47 | Qt::NoTextInteraction 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | nickname 57 | 58 | 59 | 60 | 61 | 62 | 63 | Call in progress... 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 250 72 | 0 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Qt::Horizontal 83 | 84 | 85 | 86 | 158 87 | 20 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | Hang up 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Meter 108 | QWidget 109 |
Meter.h
110 | 0 111 | 112 | setRange(int,int) 113 | setValue(int) 114 | 115 |
116 |
117 | 118 | 119 |
120 | -------------------------------------------------------------------------------- /ui/qt5/XcpApplication.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "XcpApplication.h" 3 | 4 | bool XcpApplication::notify(QObject *receiver_, QEvent *event_) 5 | { 6 | try { 7 | return QGuiApplication::notify(receiver_, event_); 8 | } 9 | catch (std::exception& ex) { 10 | std::cerr << "Exception: " << ex.what() << std::endl; 11 | qFatal("Unhandled application exception."); 12 | } 13 | return false; 14 | } 15 | -------------------------------------------------------------------------------- /ui/qt5/XcpApplication.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class XcpApplication : public QGuiApplication 6 | { 7 | public: 8 | XcpApplication(int &argc, char *argv[]) : QGuiApplication(argc, argv) {} 9 | bool notify(QObject *receiver_, QEvent *event_) override; 10 | }; 11 | -------------------------------------------------------------------------------- /ui/qt5/img/attribution.txt: -------------------------------------------------------------------------------- 1 | status-online-icon.png and status-offline-icon.png are by 2 | 3 | Artist: Fatcow Web Hosting 4 | Iconset: Farm Fresh Icons (2000 icons) 5 | License: CC Attribution 3.0 6 | Commercial usage: Allowed (Backlink to http://www.fatcow.com/free-icons required) 7 | 8 | More icons listed here http://www.fatcow.com/free-icons (about 3000 icons) 9 | -------------------------------------------------------------------------------- /ui/qt5/img/mettanode.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/qt5/img/mettanode.icns -------------------------------------------------------------------------------- /ui/qt5/img/mettanode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/qt5/img/mettanode.png -------------------------------------------------------------------------------- /ui/qt5/img/status-offline-crossed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/qt5/img/status-offline-crossed.png -------------------------------------------------------------------------------- /ui/qt5/img/status-offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/qt5/img/status-offline.png -------------------------------------------------------------------------------- /ui/qt5/img/status-online-33%.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/qt5/img/status-online-33%.png -------------------------------------------------------------------------------- /ui/qt5/img/status-online-66%.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/qt5/img/status-online-66%.png -------------------------------------------------------------------------------- /ui/qt5/img/status-online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uvvy/uvvy-cpp/829678d191fa2ee818846097fbeb83debce7327a/ui/qt5/img/status-online.png -------------------------------------------------------------------------------- /ui/qt5/macosx.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "macsupport.h" 10 | 11 | #import 12 | #import 13 | #include 14 | 15 | float dpiScaleFactor(QWidget* widget) 16 | { 17 | NSView* view = reinterpret_cast(widget->winId()); 18 | CGFloat scaleFactor = 1.0; 19 | if ([[view window] respondsToSelector: @selector(backingScaleFactor)]) 20 | scaleFactor = [[view window] backingScaleFactor]; 21 | 22 | return scaleFactor; 23 | } 24 | 25 | //================================================================================================= 26 | // MacSupport 27 | //================================================================================================= 28 | 29 | static MacSupport *ms_instance = 0; 30 | 31 | NSString * nsStringFromQString(const QString & s) 32 | { 33 | const char * utf8String = s.toUtf8().constData(); 34 | return [[NSString alloc] initWithUTF8String: utf8String]; 35 | } 36 | 37 | void dockClickHandler(id self, SEL _cmd) 38 | { 39 | Q_UNUSED(self); 40 | Q_UNUSED(_cmd); 41 | ms_instance->emitDockClick(); 42 | } 43 | 44 | MacSupport::MacSupport() { 45 | Class cls = [[[NSApplication sharedApplication] delegate] class]; 46 | 47 | if (!class_addMethod(cls, @selector(applicationShouldHandleReopen:hasVisibleWindows:), (IMP) dockClickHandler, "v@:")) 48 | NSLog(@"MyPrivate::MyPrivate() : class_addMethod failed!"); 49 | 50 | ms_instance = this; 51 | } 52 | 53 | MacSupport::~MacSupport() { 54 | } 55 | 56 | void MacSupport::emitDockClick() { 57 | emit dockClicked(); 58 | } 59 | 60 | void MacSupport::setDockBadge(const QString & badgeText) 61 | { 62 | NSString * badgeString = nsStringFromQString(badgeText); 63 | [[NSApp dockTile] setBadgeLabel: badgeString]; 64 | [badgeString release]; 65 | } 66 | 67 | void MacSupport::requestAttention() 68 | { 69 | [NSApp requestUserAttention: NSInformationalRequest]; 70 | } 71 | -------------------------------------------------------------------------------- /ui/qt5/macsupport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //================================================================================================= 6 | // MacSupport 7 | // Handles Dock interaction and requesting App attention. 8 | //================================================================================================= 9 | 10 | class MacSupport : public QObject 11 | { 12 | Q_OBJECT 13 | public: 14 | MacSupport(); 15 | virtual ~MacSupport(); 16 | void emitDockClick(); 17 | 18 | public slots: 19 | void setDockBadge(const QString & badgeText); 20 | void requestAttention(); 21 | 22 | signals: 23 | void dockClicked(); 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /ui/qt5/main.cpp: -------------------------------------------------------------------------------- 1 | #include "ContactModel.h" 2 | #include "MainWindow.h" 3 | #include "XcpApplication.h" 4 | #include "HostState.h" 5 | #include 6 | #include 7 | 8 | // 9 | // Main application entrypoint 10 | // 11 | int main(int argc, char* argv[]) 12 | { 13 | // bool verbose_debug{true}; 14 | // if (!verbose_debug) { 15 | // logger::set_verbosity(logger::verbosity::info); 16 | // } 17 | HostState state; 18 | ContactModel* model = new ContactModel(state.host(), state.settings()); 19 | 20 | XcpApplication app(argc, argv); 21 | app.setQuitOnLastWindowClosed(false); 22 | 23 | MainWindow win(model); 24 | win.show(); 25 | 26 | return app.exec(); 27 | } 28 | -------------------------------------------------------------------------------- /ui/qt5/quick/CallWindow.qml: -------------------------------------------------------------------------------- 1 | /* Call Window */ 2 | import QtQuick 2.1 3 | import QtQuick.Controls 1.0 4 | import QtQuick.Window 2.1 5 | 6 | Window { 7 | property string avatarUrl: "http://gravatar.com/avatar/29191c38b8d1e4d6a3a25846c5e673c9?s=96" 8 | property string userName: "berkus" 9 | property string callStatus: "RINGING..." 10 | property alias hangupButton: hangup 11 | 12 | title: "Call" 13 | 14 | width: { r.width < 200 ? 200 : r.width } 15 | height: { r.height < 100 ? 100 : r.height } 16 | Row { 17 | id: r 18 | spacing: 7 19 | // inside 20 | // user avatar on the left, 96x96 fixed 21 | Image { 22 | width: 96 23 | height: 96 24 | source: avatarUrl 25 | } 26 | Column { 27 | spacing: 5 28 | Text { 29 | text: userName 30 | } 31 | Text { 32 | text: callStatus 33 | } 34 | // signal meter 35 | //Component { 36 | // id: meter 37 | //} 38 | } 39 | } 40 | Button { 41 | id: hangup 42 | text: "Hang up" 43 | anchors.bottom: parent.bottom 44 | anchors.right: parent.right 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ui/qt5/quick/ChatMessage.qml: -------------------------------------------------------------------------------- 1 | /* Chat message delegate */ 2 | import QtQuick 2.1 3 | import QtQuick.Controls 1.0 4 | 5 | Component { 6 | Item { 7 | width: parent.width 8 | height: message.height 9 | 10 | Rectangle { 11 | id: line 12 | anchors { left: parent.left; right: parent.right } 13 | height: 1 14 | color: "lightgray" 15 | } 16 | 17 | Row { 18 | id: message 19 | spacing: 8 20 | Image { 21 | id: avatar 22 | source: avatarUrl 23 | width: 48 24 | height: 48 25 | smooth: true 26 | } 27 | Column { 28 | spacing: 3 29 | Text { 30 | text: author 31 | font.pixelSize: 10 32 | } 33 | Text { 34 | text: date 35 | font.pixelSize: 8 36 | } 37 | Text { 38 | text: messageText 39 | font.pixelSize: 17 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ui/qt5/quick/ChatModel.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.1 2 | 3 | ListModel { 4 | ListElement{ 5 | author: "berkus" 6 | messageText: "Hello world!" 7 | date: "2014-02-11 12:38" 8 | avatarUrl: "http://gravatar.com/avatar/29191c38b8d1e4d6a3a25846c5e673c9?s=96" 9 | } 10 | ListElement{ 11 | author: "shaman🚶sir" 12 | messageText: "Hello the chat!" 13 | date: "2014-02-11 12:40" 14 | avatarUrl: "" 15 | } 16 | ListElement{ 17 | author: "berkus" 18 | messageText: "Wassup!" 19 | date: "2014-02-11 12:41" 20 | avatarUrl: "http://gravatar.com/avatar/29191c38b8d1e4d6a3a25846c5e673c9?s=96" 21 | } 22 | ListElement{ 23 | author: "shaman🚶sir" 24 | messageText: "The chat!" 25 | date: "2014-02-11 12:41" 26 | avatarUrl: "" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ui/qt5/quick/ChatWindow.qml: -------------------------------------------------------------------------------- 1 | /* Main Window */ 2 | import QtQuick 2.1 3 | import QtQuick.Controls 1.0 4 | import QtQuick.Layouts 1.0 5 | import QtQuick.Window 2.1 6 | 7 | Window 8 | { 9 | property alias textEntry: sendText 10 | //signal sendMessage(string) 11 | 12 | title: "Chat" 13 | width: 600 14 | height: 400 15 | 16 | ColumnLayout { 17 | anchors.fill: parent 18 | ListView { 19 | id: list 20 | anchors.top: parent.top 21 | anchors.left: parent.left 22 | anchors.right: parent.right 23 | anchors.bottom: input.top 24 | 25 | model: ChatModel {} 26 | delegate: ChatMessage {} 27 | focus: true 28 | clip: true 29 | } 30 | RowLayout { 31 | id: input 32 | anchors.left: parent.left 33 | anchors.right: parent.right 34 | anchors.bottom: parent.bottom 35 | 36 | Rectangle { 37 | id: rect 38 | border.width: 2 39 | border.color: "gray" 40 | radius: 3 41 | color: "transparent" 42 | anchors.left: parent.left 43 | anchors.right: sendButton.left 44 | height: input.height 45 | } 46 | TextInput { 47 | id: sendText 48 | anchors.fill: rect 49 | anchors.margins: 2 50 | 51 | focus: true 52 | inputMethodHints: Qt.ImhSensitiveData 53 | wrapMode: TextInput.Wrap 54 | onAccepted: { sendButton.clicked() } 55 | } 56 | Button { 57 | id: sendButton 58 | anchors.right: parent.right 59 | 60 | text: "Send" 61 | enabled: { sendText.text.length > 0 } 62 | onClicked: { sendMessage(sendText.text); sendText.text = "" } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ui/qt5/quick/ContactList.qml: -------------------------------------------------------------------------------- 1 | /* Contact List */ 2 | import QtQuick 2.1 3 | import QtQuick.Controls 1.0 4 | import QtQuick.Layouts 1.0 5 | import QtQuick.Window 2.1 6 | 7 | Window { 8 | title: "Contacts" 9 | width: 600 10 | height: 400 11 | 12 | ColumnLayout { 13 | anchors.fill: parent 14 | 15 | ListView { 16 | anchors.fill: parent 17 | delegate: UserCard {} 18 | model: ContactModel {} 19 | focus: true 20 | clip: true 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ui/qt5/quick/ContactModel.qml: -------------------------------------------------------------------------------- 1 | // 2 | // Temporary testing model 3 | // Replaced by ContactModel.cpp in real client 4 | // 5 | import QtQuick 2.1 6 | 7 | ListModel { 8 | ListElement{ 9 | firstName: "Stanislav" 10 | lastName: "Karchebny" 11 | nickName: "berkus" 12 | displayName: "berkus" 13 | mood: "no mood" 14 | eid: "swab|ba-gab|ba-bob|us-dock|us" 15 | host: "host" 16 | city: "Tallinn" 17 | email: "berkus@gmail.com" 18 | avatarUrl: "http://gravatar.com/avatar/29191c38b8d1e4d6a3a25846c5e673c9?s=96" 19 | } 20 | ListElement{ 21 | firstName: "Anton" 22 | lastName: "Kotenko" 23 | nickName: "shaman🚶sir" 24 | displayName: "shaman" 25 | eid: "dog|e-wear|ba-werw-ewaety" 26 | host: "" 27 | city: "Munchen" 28 | avatarUrl: "" 29 | } 30 | ListElement{ 31 | firstName: "Stanislav" 32 | lastName: "Kljuhhin" 33 | nickName: "crz" 34 | displayName: "stanley" 35 | eid: "swabba-gabba-bobus-dockus" 36 | host: "" 37 | city: "Den Bos" 38 | avatarUrl: "" 39 | } 40 | ListElement{ 41 | firstName: "Nikolay" 42 | lastName: "Yaremko" 43 | nickName: "mendokusee" 44 | displayName: "kuso" 45 | eid: "posipdofis-sdfsrw-sdfjcvbcf-ret" 46 | host: "" 47 | city: "Yekaterinburg" 48 | avatarUrl: "" 49 | } 50 | ListElement{ 51 | firstName: "Matti" 52 | lastName: "Salminen" 53 | nickName: "msalminen" 54 | displayName: "matti" 55 | eid: "poie-w-qwet-sdfwer-ebcvvbnbn" 56 | host: "" 57 | city: "San Pedro" 58 | avatarUrl: "" 59 | } 60 | ListElement{ 61 | firstName: "Руслан" 62 | lastName: "Гроховецкий" 63 | nickName: "ruguevara" 64 | displayName: "ruslick" 65 | eid: "posdgsdg-bewrbwer-wercawer-qwemfx" 66 | host: "" 67 | city: "Yekaterinburg" 68 | avatarUrl: "" 69 | } 70 | ListElement{ 71 | firstName: "Vladimiras" 72 | lastName: "Lekecinskas" 73 | nickName: "xekc" 74 | displayName: "xekc" 75 | eid: "asdf-gerster-dxfh-eqwerv" 76 | host: "" 77 | city: "London" 78 | avatarUrl: "" 79 | } 80 | ListElement{ 81 | firstName: "Алексей" 82 | lastName: "Дубовцев" 83 | nickName: "daoorbita" 84 | displayName: "daoorbita" 85 | eid: "oasdfba-werw-xbkdfg-zosdf" 86 | host: "" 87 | city: "Moscow" 88 | avatarUrl: "" 89 | } 90 | ListElement{ 91 | firstName: "Timofey" 92 | lastName: "" 93 | nickName: "(╯°□°)╯︵ nɹopnu" 94 | displayName: "Nudoru" 95 | host: "" 96 | city: "Moscow" 97 | eid: "" 98 | avatarUrl: "" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ui/qt5/quick/MainWindow.qml: -------------------------------------------------------------------------------- 1 | /* Main Window */ 2 | import QtQuick 2.1 3 | import QtQuick.Controls 1.0 4 | import QtQuick.Layouts 1.0 5 | 6 | ApplicationWindow 7 | { 8 | signal startCall(string eid) 9 | signal startChat(string eid) 10 | 11 | title: "MettaNode" 12 | id: mainWindow 13 | width: 600 14 | height: 400 15 | 16 | MenuBar { 17 | Menu { 18 | title: "Friends" 19 | MenuItem { text: "Message"; shortcut: "Ctrl+M" } 20 | MenuItem { text: "Talk"; shortcut: "Ctrl+T" } 21 | MenuSeparator {} 22 | MenuItem { text: "Find Friends"; shortcut: "Ctrl+F" } 23 | MenuItem { text: "Rename"; shortcut: "Ctrl+R" } 24 | MenuItem { text: "Delete" } 25 | } 26 | Menu { 27 | title: "Window" 28 | MenuItem { text: "Friends" } 29 | MenuItem { text: "Search" } 30 | MenuItem { text: "Downloads" } 31 | MenuItem { text: "Settings" } 32 | MenuItem { text: "My Profile"; shortcut: "Ctrl+P" } 33 | MenuSeparator {} 34 | MenuItem { text: "Log Window" } 35 | MenuSeparator {} 36 | MenuItem { text: "Open synced files" } 37 | } 38 | } 39 | /*StatusBar { 40 | anchors.bottom: parent.bottom 41 | }*/ 42 | 43 | ColumnLayout { 44 | anchors.fill: parent 45 | ListView { 46 | anchors.fill: parent 47 | model: contactModel 48 | delegate: UserCard {} 49 | highlight: Rectangle { color: "lightsteelblue"; radius: 5 } 50 | focus: true 51 | clip: true 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ui/qt5/quick/UserCard.qml: -------------------------------------------------------------------------------- 1 | /* User card */ 2 | import QtQuick 2.1 3 | import QtQuick.Controls 1.0 4 | 5 | Component { 6 | Item { 7 | width: parent.width 8 | height: cardInfo.height 9 | Row { 10 | id: cardInfo 11 | spacing: 8 12 | Image { 13 | id: avatar 14 | source: avatarUrl 15 | width: 96 16 | height: 96 17 | smooth: true 18 | } 19 | Column { 20 | spacing: 3 21 | Text { 22 | text: displayName 23 | font.pixelSize: 24 24 | } 25 | Text { 26 | text: nickName 27 | font.pixelSize: 20 28 | } 29 | Text { 30 | text: firstName 31 | font.pixelSize: 18 32 | } 33 | Text { 34 | text: lastName 35 | font.pixelSize: 14 36 | } 37 | Text { 38 | text: host 39 | font.pixelSize: 10 40 | } 41 | Text { 42 | text: city 43 | font.pixelSize: 10 44 | } 45 | Text { 46 | text: eid 47 | font.pixelSize: 8 48 | } 49 | } 50 | Column { 51 | Button { 52 | text: "Call" 53 | onClicked: { mainWindow.startCall(eid) } 54 | } 55 | Button { 56 | text: "Chat" 57 | onClicked: { mainWindow.startChat(eid) } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ui/qt5/ui.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | img/mettanode.png 4 | img/status-online.png 5 | img/status-offline.png 6 | quick/MainWindow.qml 7 | quick/ContactList.qml 8 | quick/UserCard.qml 9 | quick/CallWindow.qml 10 | quick/ChatWindow.qml 11 | quick/ChatMessage.qml 12 | 13 | quick/ContactModel.qml 14 | quick/ChatModel.qml 15 | 16 | 17 | -------------------------------------------------------------------------------- /ui/traverse_nat.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include "nat/upnpclient.h" 13 | 14 | namespace sss { class host; } 15 | 16 | std::shared_ptr traverse_nat(std::shared_ptr host); 17 | -------------------------------------------------------------------------------- /voicebox/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(VOICEBOX_LIBS voicebox rtaudio) 2 | 3 | if (APPLE) 4 | find_library(COREAUDIO_LIB CoreAudio) 5 | find_library(COREFOUNDATION_LIB CoreFoundation) 6 | list(APPEND VOICEBOX_LIBS ${COREAUDIO_LIB} ${COREFOUNDATION_LIB}) 7 | endif (APPLE) 8 | 9 | if (UNIX AND NOT APPLE) 10 | find_library(PULSE_LIB pulse) 11 | find_library(PULSESIMPLE_LIB pulse-simple) 12 | list(APPEND VOICEBOX_LIBS ${PULSE_LIB} ${PULSESIMPLE_LIB}) 13 | endif (UNIX AND NOT APPLE) 14 | 15 | include_directories(include) 16 | include_directories(../3rdparty/opus/include ../3rdparty/rtaudio) 17 | 18 | add_subdirectory(lib) 19 | 20 | if (BUILD_TESTING) 21 | add_subdirectory(tests) 22 | endif (BUILD_TESTING) 23 | 24 | # TODO: This breaks modularity... 25 | set(VOICEBOX_LIBS ${VOICEBOX_LIBS} PARENT_SCOPE) 26 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/audio_hardware.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include "sss/server.h" 14 | #include "sss/stream.h" 15 | 16 | class RtAudio; 17 | 18 | namespace voicebox { 19 | 20 | class audio_sink; 21 | class audio_source; 22 | 23 | /** 24 | * Controls the audio layer and starts playback and capture. 25 | */ 26 | class audio_hardware 27 | { 28 | RtAudio* audio_inst{0}; 29 | 30 | audio_hardware(); 31 | ~audio_hardware(); 32 | 33 | public: 34 | static audio_hardware* instance(); 35 | 36 | static bool add_instream(voicebox::audio_source* in); 37 | static bool remove_instream(voicebox::audio_source* in); 38 | static bool add_outstream(voicebox::audio_sink* out); 39 | static bool remove_outstream(voicebox::audio_sink* out); 40 | 41 | static void reopen(); 42 | static int get_sample_rate(); 43 | static int get_frame_size(); 44 | 45 | /** 46 | * Detect available devices. 47 | * @return Number of available audio devices. 48 | */ 49 | int scan(); 50 | 51 | void open_audio(); 52 | void close_audio(); 53 | 54 | void start_audio(); 55 | void stop_audio(); 56 | bool is_running() const; 57 | 58 | void set_input_level(int level); 59 | void set_output_level(int level); 60 | 61 | // Hardware I/O handlers called from rtcallback 62 | void capture(void* buffer, unsigned int nFrames); 63 | void playback(void* buffer, unsigned int nFrames); 64 | }; 65 | 66 | } // voicebox namespace 67 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/audio_service.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include "sss/host.h" 13 | 14 | // Set to 1 if you want to console-log in realtime thread. 15 | #define REALTIME_CRIME 0 16 | 17 | #define TRACE_RARE 199 18 | #define TRACE_ENTRY 250 19 | #define TRACE_DETAIL 251 20 | 21 | namespace voicebox { 22 | 23 | /** 24 | * Clock reference base. 25 | */ 26 | static const boost::posix_time::ptime epoch{boost::gregorian::date(2010, boost::gregorian::Jan, 1)}; 27 | 28 | /** 29 | * Audio service creates several streams for sending and receiving packetized audio data 30 | * as well as a special high-priority stream for controlling the session establishment 31 | * and sending audio session control commands. 32 | */ 33 | class audio_service 34 | { 35 | class private_impl; 36 | std::shared_ptr pimpl_; 37 | 38 | public: 39 | audio_service(std::shared_ptr host); 40 | ~audio_service(); 41 | 42 | bool is_active() const; 43 | void establish_outgoing_session(sss::peer_identity const& eid, std::vector ep_hints); 44 | void listen_incoming_session(); 45 | /** 46 | * Terminate an active session, causing all audio I/O to stop. 47 | * Notifies the far end to also terminate session. 48 | */ 49 | void end_session(); 50 | 51 | // Voice service media signals 52 | using session_signal = boost::signals2::signal; 53 | session_signal on_session_started; 54 | session_signal on_session_finished; 55 | }; 56 | 57 | } // voicebox namespace 58 | 59 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/audio_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "voicebox/audio_stream.h" 12 | #include "arsenal/byte_array.h" 13 | 14 | namespace voicebox { 15 | 16 | /** 17 | * This base class represents a sink for audio output 18 | * to the currently selected output device, at a controllable bitrate. 19 | * 20 | * Sinks pull data from a producer. 21 | */ 22 | class audio_sink : public audio_stream 23 | { 24 | using super = audio_stream; 25 | 26 | audio_sink* producer_{nullptr}; 27 | 28 | protected: 29 | inline audio_sink* producer() const { return producer_; } 30 | 31 | public: 32 | audio_sink(audio_sink* producer = nullptr) 33 | { 34 | if (producer) { 35 | set_producer(producer); 36 | } 37 | } 38 | audio_sink(int framesize, double samplerate, int channels = 1); 39 | 40 | inline void set_producer(audio_sink* as) { 41 | producer_ = as; 42 | } 43 | 44 | /** 45 | * Produce a frame of audio data to be sent to the sound hardware. 46 | * This method is typically called from a dedicated audio thread, 47 | * so the subclass must handle multithread synchronization! 48 | */ 49 | virtual void produce_output(byte_array& buffer); 50 | 51 | // Overrides to pass the request up the producer chain. 52 | 53 | void set_enabled(bool enable) override; 54 | void set_frame_size(unsigned int frame_size) override; 55 | void set_sample_rate(double rate) override; 56 | void set_num_channels(unsigned int num_channels) override; 57 | }; 58 | 59 | } // voicebox namespace 60 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/audio_source.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "voicebox/audio_stream.h" 12 | #include "arsenal/byte_array.h" 13 | 14 | namespace voicebox { 15 | 16 | /** 17 | * Sources push data to an acceptor. 18 | * 19 | * A source is an active object in the sense that the first source usually initiates send of data. 20 | * A source at the end of the chain is usually a mix of source and sink, providing a connection 21 | * and synchronization between sources and sinks. 22 | */ 23 | class audio_source : public audio_stream 24 | { 25 | using super = audio_stream; 26 | 27 | audio_source* acceptor_{nullptr}; 28 | 29 | protected: 30 | inline audio_source* acceptor() const { return acceptor_; } 31 | 32 | public: 33 | audio_source() = default; 34 | audio_source(int framesize, double samplerate, int channels = 1); 35 | 36 | inline void set_acceptor(audio_source* as) { 37 | acceptor_ = as; 38 | } 39 | 40 | /** 41 | * Accept input wrapped into a byte_array. 42 | * The parameters of data inside the array come from audio_stream's settings for 43 | * frame_size(), num_channels() and sample_rate(). 44 | */ 45 | virtual void accept_input(byte_array data); 46 | 47 | // Overrides to pass the request down the consumer chain. 48 | 49 | void set_enabled(bool enable) override; 50 | void set_frame_size(unsigned int frame_size) override; 51 | void set_sample_rate(double rate) override; 52 | void set_num_channels(unsigned int num_channels) override; 53 | }; 54 | 55 | } // voicebox namespace 56 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/audio_stream.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | /** 12 | * Represents information about typed audio stream. 13 | * Keeps track of frame size, number of channels and sample rate. 14 | * The sample format is 32 bits float for convenience. 15 | * Frame size is number of samples in one frame. It does not take into account 16 | * number of channels. 17 | * Total size in bytes of one frame is frame_size() * num_channels() * sizeof(float). 18 | * 19 | * Common base class for audio_source and audio_sink. 20 | */ 21 | class audio_stream 22 | { 23 | bool enabled_{false}; 24 | unsigned int frame_size_{480}; 25 | unsigned int num_channels_{1}; 26 | double rate_{48000.0}; 27 | 28 | public: 29 | audio_stream() = default; 30 | audio_stream(int framesize, double samplerate, int channels = 1); 31 | virtual ~audio_stream(); 32 | 33 | inline bool is_enabled() const { return enabled_; } 34 | /// Enable or disable this stream. 35 | virtual void set_enabled(bool enable); 36 | inline void enable() { set_enabled(true); } 37 | inline void disable() { set_enabled(false); } 38 | 39 | inline int frame_bytes() const { return frame_size_ * num_channels_ * sizeof(float); } 40 | 41 | /// Get or set the frame size of this stream. 42 | /// Frame size may only be changed while stream is disabled. 43 | inline unsigned int frame_size() const { return frame_size_; } 44 | virtual void set_frame_size(unsigned int frame_size); 45 | 46 | /// Get or set the sample rate for this stream. 47 | /// Sampling rate may only be changed while stream is disabled. 48 | inline double sample_rate() const { return rate_; } 49 | virtual void set_sample_rate(double rate); 50 | 51 | /// Get or set the number of channels for this stream. 52 | /// Number of channels may only be changed while stream is disabled. 53 | inline unsigned int num_channels() const { return num_channels_; } 54 | virtual void set_num_channels(unsigned int num_channels); 55 | }; 56 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/file_read_sink.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "voicebox/audio_sink.h" 5 | 6 | namespace voicebox { 7 | 8 | /** 9 | * This class provides data from pseudo-input by reading a specified file in a loop. 10 | * Current implementation reads 16 bit raw audio file in an endless loop. Set sample rate 11 | * and number of channels through a regular audio_stream interface. 12 | */ 13 | class file_read_sink : public audio_sink 14 | { 15 | using super = audio_sink; 16 | 17 | std::string filename_; 18 | std::ifstream file_; 19 | size_t offset_{0}; 20 | 21 | public: 22 | file_read_sink(std::string const& filename); 23 | 24 | void set_enabled(bool enabling) override; 25 | 26 | void produce_output(byte_array& buffer) override; 27 | }; 28 | 29 | } // voicebox namespace 30 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/jitterbuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include "voicebox/audio_source.h" 15 | #include "voicebox/audio_sink.h" 16 | 17 | namespace voicebox { 18 | 19 | /** 20 | * It doesn't have, neither use, acceptor or producer, since it sits at the intersection 21 | * of push and pull threads, all other components call into jitterbuffer. 22 | * It passes the data on, using synchronization. 23 | * 24 | * Jitterbuffer forwards queue signals for use by other layers. 25 | */ 26 | class jitterbuffer : public audio_source, public audio_sink 27 | { 28 | protected: 29 | std::mutex mutex_; 30 | std::deque queue_; 31 | uint32_t sequence_number_{0}; 32 | int64_t time_skew_{0}; 33 | 34 | void reset(); 35 | void enqueue(byte_array a); 36 | 37 | public: 38 | inline jitterbuffer(audio_source* from = nullptr) 39 | { 40 | if (from) { 41 | from->set_acceptor(this); 42 | } 43 | } 44 | 45 | void produce_output(byte_array& buffer) override; // from sink 46 | void accept_input(byte_array data) override; // from source 47 | 48 | using state_signal = boost::signals2::signal; 49 | state_signal on_ready_read; 50 | state_signal on_queue_empty; 51 | }; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/opus_decode_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "voicebox/audio_sink.h" 12 | #include "opus.h" 13 | 14 | namespace voicebox { 15 | 16 | /** 17 | * This class represents a high-level sink for audio output by decoding OPUS stream. 18 | */ 19 | class opus_decode_sink : public audio_sink 20 | { 21 | using super = audio_sink; 22 | 23 | /** 24 | * Decoder state. 25 | * Owned exclusively by the audio thread while enabled. 26 | */ 27 | OpusDecoder* decode_state_{nullptr}; 28 | 29 | public: 30 | opus_decode_sink() = default; 31 | 32 | void set_enabled(bool enabling) override; 33 | 34 | private: 35 | /** 36 | * Our implementation of AbstractAudioOutput::produceOutput(). 37 | * @param buf [description] 38 | */ 39 | void produce_output(byte_array& buffer) override; 40 | }; 41 | 42 | } // voicebox namespace 43 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/opus_encode_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "voicebox/audio_sink.h" 12 | #include "opus.h" 13 | 14 | namespace voicebox { 15 | 16 | /** 17 | * This class represents an OPUS-encoded sink for audio. 18 | */ 19 | class opus_encode_sink : public audio_sink 20 | { 21 | using super = audio_sink; 22 | 23 | /** 24 | * Encoder state. 25 | * Owned exclusively by the audio thread while enabled. 26 | */ 27 | OpusEncoder *encode_state_{nullptr}; 28 | 29 | public: 30 | opus_encode_sink() = default; 31 | 32 | void set_enabled(bool enabling) override; 33 | 34 | private: 35 | void produce_output(byte_array& buffer) override; 36 | }; 37 | 38 | } // voicebox namespace 39 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/packet_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "sss/stream.h" 12 | #include "voicebox/audio_sink.h" 13 | 14 | namespace voicebox { 15 | 16 | /** 17 | * Packet sink sends data to the network using an sss::stream. 18 | */ 19 | class packet_sink : public audio_sink 20 | { 21 | using super = audio_sink; 22 | 23 | std::shared_ptr stream_; 24 | uint32_t sequence_number_{0}; 25 | 26 | public: 27 | packet_sink(std::shared_ptr stream, audio_sink* from = nullptr) 28 | : audio_sink(from) 29 | , stream_(stream) 30 | {} 31 | 32 | void produce_output(byte_array& buffer) override; 33 | 34 | /** 35 | * Run produce loop until it returns an empty buffer. 36 | */ 37 | void send_packets(); 38 | }; 39 | 40 | } // voicebox namespace 41 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/packet_source.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "sss/stream.h" 12 | #include "voicebox/audio_source.h" 13 | 14 | namespace voicebox { 15 | 16 | /** 17 | * Packet source receives data from the network using an sss::stream. 18 | */ 19 | class packet_source : public audio_source 20 | { 21 | using super = audio_source; 22 | 23 | std::shared_ptr stream_; 24 | boost::signals2::connection ready_read_conn; 25 | 26 | public: 27 | packet_source() = default; 28 | packet_source(std::shared_ptr stream); 29 | ~packet_source(); 30 | 31 | void set_enabled(bool enable) override; 32 | 33 | void set_source(std::shared_ptr stream); 34 | 35 | private: 36 | void on_packet_received(); 37 | }; 38 | 39 | } // voicebox namespace 40 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/packetizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include "voicebox/audio_source.h" 15 | #include "voicebox/audio_sink.h" 16 | 17 | namespace voicebox { 18 | 19 | /** 20 | * It doesn't have, neither use, acceptor or producer, since it sits at the intersection 21 | * of push and pull threads, all other components call into packetizer. 22 | * It passes the data on, using synchronization. 23 | * The actual packetization happens in the producer for this node, this class only 24 | * accepts fixed-size packets. 25 | * 26 | * Packetizer needs a side channel to record and transport timestamp information 27 | * about every packet. It is currently embedded directly into the produced buffer. 28 | * Encoder sinks would embed these timestamps into the final buffer. 29 | * Jitterbuffer would use this timestamp info on the receiving side. 30 | * 31 | * accept_input packet format: 32 | * [frame_bytes()] uncompressed audio data frame (framed by an audio_source usually) 33 | * 34 | * produce_output packet format: 35 | * [8 bytes] microseconds since epoch (Jan 1, 2000 0:00:00 UTC) 36 | * [frame_bytes()] uncompressed audio data frame 37 | * 38 | * Packetizer's on_ready_read() signal is often connected to the network sink send driver, 39 | * since presence of packets in the output queue is a good enough reason to send, most of the time. 40 | */ 41 | class packetizer : public audio_source, public audio_sink 42 | { 43 | protected: 44 | std::mutex mutex_; 45 | std::deque queue_; 46 | 47 | public: 48 | inline packetizer(audio_source* from = nullptr) 49 | { 50 | if (from) { 51 | from->set_acceptor(this); 52 | } 53 | } 54 | 55 | ~packetizer(); 56 | 57 | void produce_output(byte_array& buffer) override; // from sink 58 | void accept_input(byte_array data) override; // from source 59 | 60 | using state_signal = boost::signals2::signal; 61 | state_signal on_ready_read; 62 | state_signal on_queue_empty; 63 | }; 64 | 65 | } 66 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/plotfile.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include 12 | #include 13 | 14 | // Set to 1 if you want to generate gnuplot file of delays. 15 | #define DELAY_PLOT 0 16 | 17 | /** 18 | * Record timing information into a gnuplot-compatible file. 19 | */ 20 | class plotfile 21 | { 22 | #if DELAY_PLOT 23 | std::mutex m; 24 | std::ofstream out_; 25 | public: 26 | plotfile() 27 | : out_("timeplot.dat", std::ios::out|std::ios::trunc|std::ios::binary) 28 | { 29 | out_ << "# gnuplot data for packet timing\r\n" 30 | << "# ts\tlocal_ts\tdifference\r\n"; 31 | } 32 | 33 | ~plotfile() 34 | { 35 | out_ << "\r\n\r\n"; // gnuplot end marker 36 | out_.close(); 37 | } 38 | 39 | void dump(int64_t ts, int64_t local_ts) 40 | { 41 | std::lock_guard guard(m); 42 | out_ << ts << '\t' << local_ts << '\t' << fabs(local_ts - ts) << "\r\n"; 43 | } 44 | #endif 45 | }; 46 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/rtaudio_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "voicebox/audio_sink.h" 12 | 13 | namespace voicebox { 14 | 15 | /** 16 | * RtAudio sink uses audio_hardware to playback received data. 17 | */ 18 | class rtaudio_sink : public audio_sink 19 | { 20 | using super = audio_sink; 21 | 22 | public: 23 | rtaudio_sink(audio_sink* from = nullptr) : audio_sink(from) {} 24 | 25 | void set_enabled(bool enable) override; 26 | }; 27 | 28 | } // voicebox namespace 29 | -------------------------------------------------------------------------------- /voicebox/include/voicebox/rtaudio_source.h: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #pragma once 10 | 11 | #include "voicebox/audio_source.h" 12 | 13 | namespace voicebox { 14 | 15 | /** 16 | * RtAudio source uses audio_hardware to receive captured input stream. 17 | * 18 | * @todo Set a fixed-size ringbuffer to store audio data before packetizer 19 | * gets it chunked. 20 | */ 21 | class rtaudio_source : public audio_source 22 | { 23 | using super = audio_source; 24 | 25 | public: 26 | rtaudio_source() = default; 27 | 28 | void set_enabled(bool enable) override; 29 | 30 | void accept_input(byte_array data) override; 31 | }; 32 | 33 | } // voicebox namespace 34 | -------------------------------------------------------------------------------- /voicebox/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(voicebox STATIC 3 | audio_hardware.cpp 4 | audio_stream.cpp 5 | audio_service.cpp 6 | jitterbuffer.cpp 7 | packetizer.cpp 8 | audio_sink.cpp 9 | audio_source.cpp 10 | rtaudio_sink.cpp 11 | rtaudio_source.cpp 12 | opus_encode_sink.cpp 13 | opus_decode_sink.cpp 14 | file_read_sink.cpp 15 | packet_sink.cpp 16 | packet_source.cpp) 17 | -------------------------------------------------------------------------------- /voicebox/lib/audio_sink.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "arsenal/logging.h" 10 | #include "voicebox/audio_sink.h" 11 | 12 | namespace voicebox { 13 | 14 | void audio_sink::produce_output(byte_array& buffer) 15 | { 16 | if (producer_) { 17 | producer_->produce_output(buffer); 18 | } 19 | } 20 | 21 | void audio_sink::set_enabled(bool enabling) 22 | { 23 | logger::debug() << __PRETTY_FUNCTION__ << " " << enabling; 24 | super::set_enabled(enabling); 25 | if (producer_) { 26 | producer_->set_enabled(enabling); 27 | } 28 | } 29 | 30 | void audio_sink::set_frame_size(unsigned int frame_size) 31 | { 32 | super::set_frame_size(frame_size); 33 | if (producer_) { 34 | producer_->set_frame_size(frame_size); 35 | } 36 | } 37 | 38 | void audio_sink::set_sample_rate(double rate) 39 | { 40 | super::set_sample_rate(rate); 41 | if (producer_) { 42 | producer_->set_sample_rate(rate); 43 | } 44 | } 45 | 46 | void audio_sink::set_num_channels(unsigned int num_channels) 47 | { 48 | super::set_num_channels(num_channels); 49 | if (producer_) { 50 | producer_->set_num_channels(num_channels); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /voicebox/lib/audio_source.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "arsenal/logging.h" 10 | #include "voicebox/audio_source.h" 11 | 12 | namespace voicebox { 13 | 14 | void audio_source::accept_input(byte_array buffer) 15 | { 16 | if (acceptor_) { 17 | acceptor_->accept_input(buffer); 18 | } 19 | } 20 | 21 | void audio_source::set_enabled(bool enabling) 22 | { 23 | logger::debug() << __PRETTY_FUNCTION__ << " " << enabling; 24 | super::set_enabled(enabling); 25 | if (acceptor_) { 26 | acceptor_->set_enabled(enabling); 27 | } 28 | } 29 | 30 | void audio_source::set_frame_size(unsigned int frame_size) 31 | { 32 | super::set_frame_size(frame_size); 33 | if (acceptor_) { 34 | acceptor_->set_frame_size(frame_size); 35 | } 36 | } 37 | 38 | void audio_source::set_sample_rate(double rate) 39 | { 40 | super::set_sample_rate(rate); 41 | if (acceptor_) { 42 | acceptor_->set_sample_rate(rate); 43 | } 44 | } 45 | 46 | void audio_source::set_num_channels(unsigned int num_channels) 47 | { 48 | super::set_num_channels(num_channels); 49 | if (acceptor_) { 50 | acceptor_->set_num_channels(num_channels); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /voicebox/lib/audio_stream.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include 10 | #include "arsenal/logging.h" 11 | #include "voicebox/audio_stream.h" 12 | 13 | audio_stream::audio_stream(int framesize, double samplerate, int channels) 14 | { 15 | set_frame_size(framesize); 16 | set_sample_rate(samplerate); 17 | set_num_channels(channels); 18 | } 19 | 20 | audio_stream::~audio_stream() 21 | { 22 | disable(); 23 | } 24 | 25 | void audio_stream::set_enabled(bool enabling) 26 | { 27 | logger::debug() << __PRETTY_FUNCTION__ << " " << enabling; 28 | enabled_ = enabling; 29 | } 30 | 31 | void audio_stream::set_frame_size(unsigned int frame_size) 32 | { 33 | assert(!is_enabled()); 34 | frame_size_ = frame_size; 35 | } 36 | 37 | void audio_stream::set_sample_rate(double rate) 38 | { 39 | assert(!is_enabled()); 40 | rate_ = rate; 41 | } 42 | 43 | void audio_stream::set_num_channels(unsigned int num_channels) 44 | { 45 | assert(!is_enabled()); 46 | num_channels_ = num_channels; 47 | } 48 | -------------------------------------------------------------------------------- /voicebox/lib/file_read_sink.cpp: -------------------------------------------------------------------------------- 1 | #include "arsenal/logging.h" 2 | #include "voicebox/file_read_sink.h" 3 | 4 | using namespace std; 5 | 6 | namespace voicebox { 7 | 8 | file_read_sink::file_read_sink(std::string const& filename) 9 | : filename_(filename) 10 | {} 11 | 12 | void file_read_sink::set_enabled(bool enabling) 13 | { 14 | logger::debug() << __PRETTY_FUNCTION__ << " " << enabling; 15 | if (enabling and !is_enabled()) 16 | { 17 | assert(!file_.is_open()); 18 | file_.open(filename_, ios::in|ios::binary); 19 | offset_ = 0; 20 | 21 | logger::debug() << "File read sink enabled: rate " << sample_rate() 22 | << ", channels " << num_channels() << ", frame size " << frame_size(); 23 | 24 | super::set_enabled(true); 25 | } 26 | else if (!enabling and is_enabled()) 27 | { 28 | super::set_enabled(false); 29 | file_.close(); 30 | logger::debug() << "File read sink disabled"; 31 | } 32 | } 33 | 34 | void file_read_sink::produce_output(byte_array& buffer) 35 | { 36 | if (!file_.is_open()) 37 | return; 38 | 39 | size_t n_frames = frame_size() * num_channels(); 40 | 41 | short samples[n_frames]; 42 | off_t off = 0; 43 | size_t nbytesToRead = n_frames * sizeof(short); 44 | 45 | fill_n(samples, n_frames, 0); 46 | 47 | file_.seekg(offset_); 48 | 49 | while ((nbytesToRead > 0) and file_) 50 | { 51 | file_.read((char*)&samples[off], nbytesToRead); 52 | size_t nread = file_.gcount(); 53 | assert(!(nread % 2)); 54 | offset_ += nread; 55 | off += nread/2; 56 | nbytesToRead -= nread; 57 | assert(nbytesToRead >= 0); 58 | 59 | if (nread <= nbytesToRead or file_.eof()) 60 | { 61 | // Loop the file 62 | offset_ = 0; 63 | file_.clear(); 64 | file_.seekg(offset_); 65 | } 66 | } 67 | 68 | buffer.resize(frame_bytes()); 69 | for (unsigned int i = 0; i < n_frames; ++i) { 70 | buffer.as()[i] = float(samples[i]) / 32768.0; 71 | } 72 | } 73 | 74 | } // voicebox namespace 75 | -------------------------------------------------------------------------------- /voicebox/lib/opus_decode_sink.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "arsenal/logging.h" 10 | #include "voicebox/opus_decode_sink.h" 11 | #include "voicebox/audio_service.h" 12 | 13 | using namespace std; 14 | 15 | namespace voicebox { 16 | 17 | void opus_decode_sink::set_enabled(bool enabling) 18 | { 19 | logger::debug(TRACE_ENTRY) << __PRETTY_FUNCTION__ << " " << enabling; 20 | if (enabling and !is_enabled()) 21 | { 22 | assert(!decode_state_); 23 | int error = 0; 24 | decode_state_ = opus_decoder_create(sample_rate(), num_channels(), &error); 25 | assert(decode_state_); 26 | assert(!error); 27 | 28 | int framesize, rate; 29 | opus_decoder_ctl(decode_state_, OPUS_GET_SAMPLE_RATE(&rate)); 30 | framesize = rate / 100; // 10ms 31 | set_frame_size(framesize); 32 | set_sample_rate(rate); 33 | logger::debug(TRACE_DETAIL) << "opus_decode_sink: frame size " 34 | << dec << framesize << " sample rate " << rate; 35 | 36 | super::set_enabled(true); 37 | } 38 | else if (!enabling and is_enabled()) 39 | { 40 | super::set_enabled(false); 41 | 42 | assert(decode_state_); 43 | opus_decoder_destroy(decode_state_); 44 | decode_state_ = nullptr; 45 | } 46 | } 47 | 48 | // Packets format: 49 | // [8 bytes] microseconds since epoch (Jan 1, 2010) 50 | // [4 bytes] sequence number 51 | // [variable] payload 52 | const int buffer_offset = 12; 53 | 54 | void opus_decode_sink::produce_output(byte_array& samplebuf) 55 | { 56 | // Grab the next buffer from the queue 57 | byte_array bytebuf; 58 | if (producer()) { 59 | producer()->produce_output(bytebuf); 60 | } 61 | 62 | samplebuf.resize(frame_bytes()); 63 | 64 | // Decode the frame 65 | if (bytebuf.size() > buffer_offset) 66 | { 67 | #if REALTIME_CRIME 68 | logger::debug(TRACE_DETAIL) << "Decode frame size: " << bytebuf.size() - buffer_offset; 69 | #endif 70 | int len = opus_decode_float(decode_state_, bytebuf.as() + buffer_offset, 71 | bytebuf.size() - buffer_offset, samplebuf.as(), frame_size(), /*decodeFEC:*/0); 72 | 73 | if (len < 0) { 74 | // perform recovery - fill buffer with 0 for example... 75 | // fill_n(samplebuf.as(), frame_bytes(), 0); 76 | // len = frame_size(); 77 | } 78 | assert(len == (int)frame_size()); 79 | } 80 | else 81 | { 82 | // "decode" a missing frame 83 | int len = opus_decode_float(decode_state_, NULL, 0, samplebuf.as(), 84 | frame_size(), /*decodeFEC:*/0); 85 | assert(len > 0); 86 | } 87 | } 88 | 89 | } // voicebox namespace 90 | -------------------------------------------------------------------------------- /voicebox/lib/opus_encode_sink.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "arsenal/logging.h" 10 | #include "voicebox/opus_encode_sink.h" 11 | #include "voicebox/audio_service.h" 12 | 13 | using namespace std; 14 | 15 | namespace voicebox { 16 | 17 | void opus_encode_sink::set_enabled(bool enabling) 18 | { 19 | logger::debug(TRACE_ENTRY) << __PRETTY_FUNCTION__ << " " << enabling; 20 | if (enabling and !is_enabled()) { 21 | assert(!encode_state_); 22 | int error = 0; 23 | encode_state_ = opus_encoder_create(sample_rate(), num_channels(), 24 | OPUS_APPLICATION_VOIP, &error); 25 | assert(encode_state_); 26 | assert(!error); 27 | 28 | int framesize, rate; 29 | opus_encoder_ctl(encode_state_, OPUS_GET_SAMPLE_RATE(&rate)); 30 | framesize = rate / 100; // 10ms 31 | set_frame_size(framesize); 32 | set_sample_rate(rate); 33 | logger::debug(TRACE_DETAIL) << "opus_encode_sink: frame size " << dec << framesize 34 | << " sample rate " << rate; 35 | 36 | opus_encoder_ctl(encode_state_, OPUS_SET_VBR(1)); 37 | opus_encoder_ctl(encode_state_, OPUS_SET_BITRATE(OPUS_AUTO)); 38 | opus_encoder_ctl(encode_state_, OPUS_SET_DTX(1)); 39 | 40 | super::set_enabled(true); 41 | 42 | } else if (!enabling and is_enabled()) { 43 | 44 | super::set_enabled(false); 45 | 46 | assert(encode_state_); 47 | opus_encoder_destroy(encode_state_); 48 | encode_state_ = nullptr; 49 | } 50 | } 51 | 52 | const int ts_size = 8; 53 | const int ts_floats = ts_size / sizeof(float); // Timestamp size in floats, due to cast. 54 | const int buffer_offset = 12; 55 | 56 | void opus_encode_sink::produce_output(byte_array& buffer) 57 | { 58 | // Get data from our producer, if any. 59 | byte_array samplebuf; 60 | if (producer()) { 61 | producer()->produce_output(samplebuf); 62 | } 63 | 64 | if (!samplebuf.is_empty()) 65 | { 66 | #if REALTIME_CRIME 67 | logger::debug(TRACE_DETAIL) << "Encoding source frame with size: " 68 | << dec << samplebuf.size() - ts_size; 69 | #endif 70 | // Encode the frame and write it into a buffer 71 | int maxbytes = 1024;//meh, any opus option to get this? 72 | buffer.resize(maxbytes); 73 | int nbytes = opus_encode_float(encode_state_, samplebuf.as() + ts_floats, 74 | frame_size(), 75 | buffer.as() + buffer_offset, buffer.size() - buffer_offset); 76 | assert(nbytes <= maxbytes); 77 | buffer.resize(nbytes + buffer_offset); 78 | buffer.as()[0] = samplebuf.as()[0]; // Copy timestamp 79 | #if REALTIME_CRIME 80 | logger::debug(TRACE_DETAIL) << "Encoded frame size: " << dec << nbytes; 81 | logger::file_dump(buffer, "encoded opus packet"); 82 | #endif 83 | } 84 | else // packetizer may return empty buffer if queue is empty. 85 | { 86 | buffer.resize(0); 87 | } 88 | } 89 | 90 | } // voicebox namespace 91 | -------------------------------------------------------------------------------- /voicebox/lib/packet_sink.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "arsenal/logging.h" 10 | #include "voicebox/packet_sink.h" 11 | 12 | using namespace std; 13 | using namespace sss; 14 | 15 | namespace voicebox { 16 | 17 | /** 18 | * Somebody needs to pull this method to send the packets, 19 | * some kind of signal on_ready_send() or something is necessary. 20 | */ 21 | void packet_sink::produce_output(byte_array& buffer) 22 | { 23 | // Get data from our producer, if any. 24 | if (producer()) { 25 | producer()->produce_output(buffer); 26 | } 27 | 28 | if (!buffer.is_empty()) { 29 | buffer.as()[2] = sequence_number_++; 30 | stream_->write_datagram(buffer, stream::datagram_type::non_reliable); 31 | } 32 | } 33 | 34 | void packet_sink::send_packets() 35 | { 36 | if (!stream_->is_connected()) { 37 | logger::warning() << "Packet sink - stream is not connected, cannot send"; 38 | return; 39 | } 40 | 41 | byte_array buffer; 42 | do { 43 | produce_output(buffer); 44 | } while(!buffer.is_empty()); 45 | } 46 | 47 | } // voicebox namespace 48 | -------------------------------------------------------------------------------- /voicebox/lib/packet_source.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "arsenal/logging.h" 10 | #include "voicebox/packet_source.h" 11 | #include "voicebox/audio_service.h" 12 | 13 | using namespace std; 14 | using namespace sss; 15 | namespace pt = boost::posix_time; 16 | 17 | namespace voicebox { 18 | 19 | packet_source::packet_source(std::shared_ptr stream) 20 | : audio_source() 21 | { 22 | set_source(stream); 23 | } 24 | 25 | packet_source::~packet_source() 26 | { 27 | ready_read_conn.disconnect(); 28 | if (stream_) { 29 | stream_->shutdown(stream::shutdown_mode::read); // @todo Maybe do this in set_enabled(false)? 30 | } 31 | } 32 | 33 | void packet_source::set_source(std::shared_ptr stream) 34 | { 35 | stream_ = stream; 36 | ready_read_conn = stream_->on_ready_read_datagram.connect([this]{ on_packet_received(); }); 37 | } 38 | 39 | void packet_source::set_enabled(bool enabling) 40 | { 41 | logger::debug(TRACE_ENTRY) << __PRETTY_FUNCTION__ << " " << enabling; 42 | super::set_enabled(enabling); 43 | } 44 | 45 | /* Put received packet into receive queue */ 46 | 47 | // NB: 48 | // Should this class handle the received packets or should it already receive the appearing packets 49 | // in its accept_input() method and so essentially do nothing? 50 | // Probably on_packet_received() should be here, but should also check is_enabled() before calling 51 | // accept_input(), so disabling packet_source would just ignore ingress of packets. 52 | 53 | void packet_source::on_packet_received() 54 | { 55 | if (is_enabled()) { 56 | byte_array msg = stream_->read_datagram(); 57 | #if REALTIME_CRIME 58 | logger::debug(TRACE_ENTRY) << "Received packet of size " << msg.size(); 59 | #endif 60 | if (msg.size() > 12) { // @todo Magic number - amount of header data 61 | accept_input(msg); 62 | } 63 | } 64 | } 65 | 66 | // void packet_source::log_packet_delay(byte_array const& pkt) 67 | // { 68 | // #if DELAY_PLOT 69 | // int64_t ts = pkt.as()[0]; 70 | // int64_t local_ts = (pt::microsec_clock::universal_time() - epoch).total_milliseconds(); 71 | // // logger::info() << "Packet ts " << ts << ", local ts " << local_ts << ", play difference " 72 | // // << fabs(local_ts - ts); 73 | 74 | // plot_.dump(ts, local_ts); 75 | // #endif 76 | // } 77 | 78 | } // voicebox namespace 79 | -------------------------------------------------------------------------------- /voicebox/lib/packetizer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "arsenal/logging.h" 10 | #include "voicebox/packetizer.h" 11 | #include "voicebox/audio_service.h" 12 | 13 | namespace voicebox { 14 | 15 | packetizer::~packetizer() 16 | { 17 | audio_source::disable(); 18 | // audio_sink::disable(); 19 | } 20 | 21 | void packetizer::produce_output(byte_array& buffer) 22 | { 23 | #if REALTIME_CRIME 24 | logger::debug(TRACE_ENTRY) << "packetizer::produce_output"; 25 | #endif 26 | std::unique_lock guard(mutex_); 27 | if (queue_.empty()) { 28 | buffer.resize(0); 29 | return; 30 | } 31 | 32 | buffer = queue_.front(); 33 | queue_.pop_front(); 34 | bool emptied = queue_.empty(); 35 | guard.unlock(); 36 | 37 | if (emptied) { 38 | on_queue_empty(); 39 | } 40 | } 41 | 42 | void packetizer::accept_input(byte_array data) 43 | { 44 | #if REALTIME_CRIME 45 | logger::debug(TRACE_ENTRY) << "packetizer::accept_input of " 46 | << std::dec << data.size() << " bytes"; 47 | #endif 48 | std::unique_lock guard(mutex_); 49 | bool wasempty = queue_.empty(); 50 | queue_.emplace_back(data); 51 | guard.unlock(); 52 | 53 | // Notify reader if appropriate 54 | if (wasempty) { 55 | on_ready_read(); 56 | } 57 | } 58 | 59 | } // voicebox namespace 60 | -------------------------------------------------------------------------------- /voicebox/lib/rtaudio_sink.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "voicebox/rtaudio_sink.h" 10 | #include "voicebox/audio_hardware.h" 11 | #include "voicebox/audio_service.h" 12 | 13 | namespace voicebox { 14 | 15 | void rtaudio_sink::set_enabled(bool enabling) 16 | { 17 | logger::debug(TRACE_ENTRY) << __PRETTY_FUNCTION__ << " " << enabling; 18 | if (enabling and !is_enabled()) 19 | { 20 | if (frame_size() <= 0 or sample_rate() <= 0.0) 21 | { 22 | logger::warning() << "Bad frame size " << frame_size() 23 | << " or sample rate " << sample_rate(); 24 | return; 25 | } 26 | 27 | bool wasempty = audio_hardware::add_outstream(this); 28 | super::set_enabled(true); 29 | if (wasempty or audio_hardware::get_sample_rate() < sample_rate()) { 30 | audio_hardware::reopen(); // (re-)open at suitable rate 31 | } 32 | } 33 | else if (!enabling and is_enabled()) 34 | { 35 | bool isempty = audio_hardware::remove_outstream(this); 36 | super::set_enabled(false); 37 | if (isempty) { 38 | audio_hardware::reopen(); // close or reopen without output 39 | } 40 | } 41 | } 42 | 43 | } // voicebox namespace 44 | -------------------------------------------------------------------------------- /voicebox/lib/rtaudio_source.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #include "voicebox/rtaudio_source.h" 10 | #include "voicebox/audio_hardware.h" 11 | #include "voicebox/audio_service.h" 12 | 13 | namespace pt = boost::posix_time; 14 | 15 | namespace voicebox { 16 | 17 | void rtaudio_source::set_enabled(bool enabling) 18 | { 19 | logger::debug(TRACE_ENTRY) << __PRETTY_FUNCTION__ << " " << enabling; 20 | if (enabling and !is_enabled()) 21 | { 22 | if (frame_size() <= 0 or sample_rate() <= 0.0) 23 | { 24 | logger::warning() << "Bad frame size " << frame_size() 25 | << " or sample rate " << sample_rate(); 26 | return; 27 | } 28 | 29 | bool wasempty = audio_hardware::add_instream(this); 30 | super::set_enabled(true); 31 | if (wasempty or audio_hardware::get_sample_rate() < sample_rate()) { 32 | audio_hardware::reopen(); // (re-)open at suitable rate 33 | } 34 | } 35 | else if (!enabling and is_enabled()) 36 | { 37 | bool isempty = audio_hardware::remove_instream(this); 38 | super::set_enabled(false); 39 | if (isempty) { 40 | audio_hardware::reopen(); // close or reopen without input 41 | } 42 | } 43 | } 44 | 45 | void rtaudio_source::accept_input(byte_array data) 46 | { 47 | if (!data.is_empty()) 48 | { 49 | byte_array buf; 50 | buf.resize(8); // Reserve space for timestamp 51 | buf.append(data); 52 | // Timestamp the packet with our own clock reading. 53 | int64_t ts = (pt::microsec_clock::universal_time() - epoch).total_milliseconds(); 54 | buf.as()[0] = ts; 55 | super::accept_input(buf); 56 | } 57 | // Totally ignore empty capture packets - shouldn't happen. 58 | } 59 | 60 | } // voicebox namespace 61 | -------------------------------------------------------------------------------- /voicebox/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SSS_LIBS sss routing krypto opus ${OPENSSL_LIBRARIES}) 2 | 3 | create_test(file_playback LIBS voicebox ${VOICEBOX_LIBS} arsenal NO_CTEST) 4 | create_test(file_send LIBS voicebox ${VOICEBOX_LIBS} ${SSS_LIBS} arsenal) 5 | 6 | create_test(sim_transmit LIBS voicebox ${VOICEBOX_LIBS} ${SSS_LIBS} arsenal) 7 | -------------------------------------------------------------------------------- /voicebox/tests/test_file_playback.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "voicebox/file_read_sink.h" 4 | #include "voicebox/rtaudio_sink.h" 5 | 6 | using namespace std; 7 | using namespace voicebox; 8 | 9 | int main() 10 | { 11 | file_read_sink readfile("bnb.s16"); 12 | rtaudio_sink sink; 13 | sink.set_producer(&readfile); 14 | 15 | sink.set_frame_size(480); 16 | sink.set_sample_rate(48000); 17 | sink.set_num_channels(2); 18 | 19 | sink.enable(); 20 | 21 | this_thread::sleep_for(chrono::seconds(30)); 22 | } 23 | -------------------------------------------------------------------------------- /voicebox/tests/test_file_send.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "sss/host.h" 4 | #include "sss/stream.h" 5 | #include "arsenal/settings_provider.h" 6 | #include "voicebox/file_read_sink.h" 7 | #include "voicebox/opus_encode_sink.h" 8 | #include "voicebox/packet_sink.h" 9 | 10 | using namespace std; 11 | using namespace sss; 12 | using namespace voicebox; 13 | 14 | int main() 15 | { 16 | shared_ptr host(host::create(settings_provider::instance())); 17 | shared_ptr stream(make_shared(host)); 18 | 19 | // stream->connect_to(eid, "streaming", "opus");//-- audio_service? 20 | 21 | file_read_sink readfile("bnb.s16"); 22 | opus_encode_sink encoder; 23 | packet_sink sink(stream); 24 | 25 | encoder.set_producer(&readfile); 26 | sink.set_producer(&encoder); 27 | 28 | sink.set_frame_size(480); 29 | sink.set_sample_rate(48000); 30 | sink.set_num_channels(2); 31 | 32 | sink.enable(); 33 | 34 | // Emulate 30 seconds of looped playback 35 | for (int i = 0; i < 3000; ++i) { 36 | byte_array buf; 37 | sink.produce_output(buf); 38 | this_thread::sleep_for(chrono::milliseconds(10)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /voicebox/tests/test_jitterbuffer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Part of Metta OS. Check http://atta-metta.net for latest version. 3 | // 4 | // Copyright 2007 - 2014, Stanislav Karchebnyy 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #define BOOST_TEST_MODULE Test_jitterbuffer 10 | #include 11 | 12 | #include "voicebox/jitterbuffer.h" 13 | 14 | using namespace std; 15 | using namespace voicebox; 16 | 17 | BOOST_AUTO_TEST_CASE(create_jb) 18 | { 19 | jitterbuffer jb_; 20 | BOOST_CHECK(conn != nullptr); 21 | } 22 | 23 | // TO TEST: 24 | // 25 | // Normal packet arrival 26 | // Out-of-order packet arrival 27 | // Too late packets 28 | // Filling the gaps 29 | // Maximum stream gap warning 30 | // Catch up if too slow (old packet discard) 31 | 32 | BOOST_AUTO_TEST_CASE(packets_in_order) 33 | { 34 | } 35 | 36 | BOOST_AUTO_TEST_CASE(packets_out_of_order) 37 | { 38 | } 39 | 40 | BOOST_AUTO_TEST_CASE(packets_too_late) 41 | { 42 | } 43 | 44 | -------------------------------------------------------------------------------- /voicebox/tests/test_sim_transmit.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Capture audio locally, 3 | // Send it through simulated network, 4 | // Playback locally again. 5 | // 6 | // Part of Metta OS. Check http://atta-metta.net for latest version. 7 | // 8 | // Copyright 2007 - 2014, Stanislav Karchebnyy 9 | // 10 | // Distributed under the Boost Software License, Version 1.0. 11 | // (See file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt) 12 | // 13 | #define BOOST_TEST_MODULE Test_sim_transmit 14 | #include 15 | 16 | #include "../../sss/tests/simulator_fixture.h" //@todo Fix include directory for test fixtures 17 | #include "voicebox/rtaudio_source.h" 18 | #include "voicebox/rtaudio_sink.h" 19 | #include "voicebox/packetizer.h" 20 | #include "voicebox/jitterbuffer.h" 21 | #include "voicebox/packet_source.h" 22 | #include "voicebox/packet_sink.h" 23 | 24 | using namespace std; 25 | using namespace sss; 26 | using namespace sss::simulation; 27 | using namespace voicebox; 28 | 29 | BOOST_FIXTURE_TEST_CASE(simple_sim_step, simulator_fixture) 30 | { 31 | // source_->pkt_->sender_ 32 | rtaudio_source source_; 33 | packetizer pkt_(&source_); 34 | packet_sink sender_(client, &pkt_); 35 | pkt_.on_ready_read.connect([&] { 36 | logger::debug() << "Packetizer on_ready_read - posting send event"; 37 | simulator->post([&] { 38 | logger::debug() << "SEND EVENT - sending all pending packets"; 39 | sender_.send_packets(); 40 | }); 41 | }); 42 | 43 | // receiver_->jb_->output_ 44 | packet_source receiver_; 45 | jitterbuffer jb_(&receiver_); 46 | rtaudio_sink output_(&jb_); 47 | 48 | client->on_link_up.connect([&] { 49 | logger::debug() << "client: link_up"; 50 | sender_.enable(); 51 | source_.enable(); 52 | }); 53 | 54 | server->on_new_connection.connect([&] { 55 | logger::debug() << "server: on_new_connection"; 56 | auto stream = server->accept(); 57 | if (!stream) { 58 | return; 59 | } 60 | receiver_.set_source(stream); 61 | receiver_.enable(); 62 | output_.enable(); 63 | }); 64 | 65 | logger::logging::set_verbosity(255); 66 | 67 | client->connect_to(server_host_eid, "simulator", "test", server_host_address); 68 | 69 | simulator->run(); 70 | 71 | this_thread::sleep_for(chrono::milliseconds(20)); 72 | 73 | simulator->run(); 74 | } 75 | --------------------------------------------------------------------------------