diff --git a/CMakeLists.txt b/CMakeLists.txt index 19d4124..99a94c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ project( VoIP_Project ) set( BUILD_TESTING OFF CACHE BOOL "Build tests (includes tests in all subprojects)." ) set( VOIP_BUILD_OPUS ON CACHE BOOL "Build opus audio coding library." ) +link_libraries(m) + include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) @@ -27,7 +29,7 @@ endif() if( ${CMAKE_SYSTEM_NAME} STREQUAL Windows ) set( AUDIO_WINDOWS_WASAPI ON CACHE BOOL "Set WASAPI audio driver for Windows." ) elseif( ${CMAKE_SYSTEM_NAME} STREQUAL Linux ) - set( AUDIO_LINUX_ALSA ON CACHE BOOL "Set ALSA audio driver for Linux." ) + set( AUDIO_LINUX_PULSE ON CACHE BOOL "Set ALSA audio driver for Linux." ) elseif( ${CMAKE_SYSTEM_NAME} STREQUAL Darwin ) set( AUDIO_OSX_CORE ON CACHE BOOL "Set CoreAudio audio driver for OSX." ) else() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 21750bd..afc28cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,7 +19,7 @@ set( SRC main.cpp # Windows only set( WIN_SRC windows_udp_socket.cpp ) # TODO Linux / OSX only -# set( UNIX_SRC unix_udp_socket.cpp ) +set( UNIX_SRC unix_udp_socket.cpp ) set( HDR voip_project.h audio_buffer.h @@ -35,8 +35,8 @@ set( HDR voip_project.h if( ${CMAKE_SYSTEM_NAME} STREQUAL Windows ) list( APPEND SRC ${WIN_SRC} ) -#else() -# list( APPEND SRC ${UNIX_SRC} ) +else() + list( APPEND SRC ${UNIX_SRC} ) endif() set( EXECUTABLE_NAME voip_project) diff --git a/src/audio_mixer.h b/src/audio_mixer.h index abaad91..86be72e 100644 --- a/src/audio_mixer.h +++ b/src/audio_mixer.h @@ -3,6 +3,7 @@ #define VOIP_AUDIO_MIXER_H_ #include +#include class AudioMixer { diff --git a/src/playout_buffer.h b/src/playout_buffer.h index 58b251d..58fb107 100644 --- a/src/playout_buffer.h +++ b/src/playout_buffer.h @@ -3,6 +3,7 @@ #define VOIP_PLAYOUT_BUFFER_H_ #include "audio_buffer.h" +#include class PlayoutBuffer { public: diff --git a/src/receiver.h b/src/receiver.h index 777e02d..09ae280 100644 --- a/src/receiver.h +++ b/src/receiver.h @@ -2,6 +2,8 @@ #ifndef VOIP_RECEIVER_H #define VOIP_RECEIVER_H +#include +#include #include #include #include diff --git a/src/soundcard_interaction.h b/src/soundcard_interaction.h index 6295832..a4846ef 100644 --- a/src/soundcard_interaction.h +++ b/src/soundcard_interaction.h @@ -5,6 +5,7 @@ #include "RtAudio.h" #include "audio_buffer.h" +#include // TODO after AudioBuffer is coded class SoundcardAudioProcessor { diff --git a/src/udp_socket.h b/src/udp_socket.h index db4e081..e6ef13b 100644 --- a/src/udp_socket.h +++ b/src/udp_socket.h @@ -8,8 +8,18 @@ #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 #define NOMINMAX 1 +#ifdef _WIN32 #include #include +#else + +#include +#include + +#define SOCKET int + +#endif + class SimpleWindowsUpdSocket { diff --git a/src/unix_udp_socket.cpp b/src/unix_udp_socket.cpp new file mode 100644 index 0000000..b1844ad --- /dev/null +++ b/src/unix_udp_socket.cpp @@ -0,0 +1,234 @@ + +#include "udp_socket.h" + +#include +#include +#include +#include +#include +static const int INVALID_SOCKET = -1; +static const int SOCKET_ERROR = -1; + +#define ioctlsocket ioctl + +SimpleWindowsUpdSocket::SimpleWindowsUpdSocket() : connectSocket_(INVALID_SOCKET), addressInfo_(), isOpen_(false), isAddressSet_(false) +{ + // connectSocket_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + // WSADATA wsaData; + // int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + // if (iResult != 0) + // { + // std::cerr << "WSAStartup failed: " << iResult << std::endl; + // } + +} + +/** +* Initializes addressInfo_ property with passed ipV4 address and port +*/ +void SimpleWindowsUpdSocket::setAddress(std::string address, std::string port) +{ + if (isOpen_) close(); + + port_ = port; + struct addrinfo hints; + + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + int iResult = getaddrinfo(address.c_str(), port.c_str(), &hints, &addressInfo_); + if (iResult != 0) + { + std::cerr << "getaddrinfo failed failed: " << iResult << std::endl; + } + else + { + if (addressInfo_->ai_canonname) + std::cerr << "Canonical name of socket: " << addressInfo_->ai_canonname << std::endl; + + if (addressInfo_->ai_family != AF_INET) + { + addressInfo_ = nullptr; + isAddressSet_ = true; + std::cerr << "SocketAddress::setAddress(): Socket addresses other than ipv4 not supported!" << std::endl; + } + } + isAddressSet_ = true; +} + +/** +* Initializes connectSocket_ with address information form addressInfo_ +*/ +void SimpleWindowsUpdSocket::open() +{ + if (isAddressSet_ == false) return; + + connectSocket_ = socket(addressInfo_->ai_family, addressInfo_->ai_socktype, addressInfo_->ai_protocol); + + timeout_.tv_sec = 0; + timeout_.tv_usec = 100000; + // Set up the file descriptor set for timeout check + + + FD_ZERO(&socketArray_); + FD_SET(connectSocket_, &socketArray_); + + if (connectSocket_ == INVALID_SOCKET) + { + std::cerr << "Error at socket(): " << strerror(errno) << std::endl; + freeaddrinfo(addressInfo_); + // WSACleanup(); + } + + isOpen_ = true; + + int32_t reuseaddr = 1; + if (::setsockopt(connectSocket_, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof(reuseaddr))) + { + std::cerr << "Error setting reuse option on socket!" << std::endl; + } +} + +/** +* Closes the socket. +*/ +void SimpleWindowsUpdSocket::close() +{ + if (isOpen_ == false) return; + + isOpen_ = false; + ::close(connectSocket_); +} + +/** +* Sends the data buffer to the previously set address +*/ +uint32_t SimpleWindowsUpdSocket::send(std::vector data, bool isBlocking) +{ + if (isOpen_ == false) return 0; + + u_long nonblocking = 0; + if (isBlocking == false) + { + nonblocking = 1; + if (ioctlsocket(connectSocket_, FIONBIO, &nonblocking) != 0) + { + std::cerr << "Could not set non-blocking mode on socket!" << std::endl; + } + } + + struct sockaddr* address = nullptr; + address = (struct sockaddr*)addressInfo_->ai_addr; + + + int32_t bytes_sent = ::sendto(connectSocket_, (char*) &data[0], (int) data.size(), 0, address, sizeof(sockaddr_in)); + if (bytes_sent == -1) + { + int e = errno; + + std::cerr << "Error in sendto: " << e << std::endl; + } + + if (isBlocking == false) + { + nonblocking = 0; + if (ioctlsocket(connectSocket_, FIONBIO, &nonblocking) != 0) + { + std::cerr << "Could not set blocking mode on socket!" << std::endl; + } + } + + return bytes_sent; +} + +/** +* Binds the previously set address as own address to prepare socket for incoming messages. +*/ +bool SimpleWindowsUpdSocket::bind() +{ + if (isOpen_ == false) + { + std::cerr << "Error: trying to bind a non open socket!" << std::endl; + return false; + } + + struct sockaddr* address = nullptr; + address = (struct sockaddr*)addressInfo_->ai_addr; + + if (::bind(connectSocket_, address, sizeof(sockaddr_in)) != 0) + { + int e = errno; + std::cerr << "Error binding socket: " << e << std::endl; + return false; + } + + return true; +} + +/** +* Checks socket connection for incoming messages and stores incoming message in data buffer. +*/ +uint32_t SimpleWindowsUpdSocket::receive(std::vector& data, uint32_t maxSize, bool isBlocking) +{ + if (isOpen_ == false) return 0; + + struct sockaddr *a; + socklen_t a_len; + + sockaddr_in addrIn; + addrIn.sin_port = 0; + addrIn.sin_family = AF_INET; + addrIn.sin_addr.s_addr = htonl(INADDR_ANY); + + a = (struct sockaddr*)&addrIn; + a_len = sizeof(sockaddr_in); + + u_long nonblocking = 0; + if (isBlocking == false) + { + nonblocking = 1; + if (ioctlsocket(connectSocket_, FIONBIO, &nonblocking) != 0) + { + std::cerr << "Could not set non-blocking mode on socket!" << std::endl; + } + } + + uint32_t toread = maxSize ? maxSize : (uint32_t) data.capacity(); + if (maxSize > data.capacity()) + { + std::cerr << "Data to read over buffer capacity!" << std::endl; + return 0; + } + + // Check for timeout, used to terminate thread on application close. + if (select((int) connectSocket_, &socketArray_, NULL, NULL, &timeout_) == 0) + { + return 0; + } + + int32_t read_bytes = ::recvfrom(connectSocket_, (char*)&data[0], toread, 0, a, &a_len); + + int32_t e = errno; + if (read_bytes == -1 || read_bytes == 0) + { + std::cerr << "Error in recvfrom: " << e << std::endl; + data.clear(); + return 0; + } + + if (isBlocking == false) + { + nonblocking = 0; + if (ioctlsocket(connectSocket_, FIONBIO, &nonblocking) != 0) + { + std::cerr << "Could not set blocking mode on socket!" << std::endl; + } + } + + data.resize(std::max(read_bytes, 0)); + + return read_bytes; +}