mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-28 16:36:17 +02:00
re-added Outurnate's Windows client for master branch
This commit is contained in:
parent
6118e1a79d
commit
094ec9b53c
20 changed files with 857 additions and 107 deletions
183
CMakeLists.txt
183
CMakeLists.txt
|
@ -8,7 +8,10 @@ option(BUILD_SHARED_LIBS "Build snapcast in a shared context" ON)
|
||||||
option(BUILD_STATIC_LIBS "Build snapcast in a static context" ON)
|
option(BUILD_STATIC_LIBS "Build snapcast in a static context" ON)
|
||||||
option(BUILD_TESTS "Build tests (run tests with make test)" ON)
|
option(BUILD_TESTS "Build tests (run tests with make test)" ON)
|
||||||
|
|
||||||
option(BUILD_SERVER "Build Snapserver" ON)
|
if(NOT WIN32)
|
||||||
|
option(BUILD_SERVER "Build Snapserver" ON) # no Windows server for now
|
||||||
|
endif()
|
||||||
|
|
||||||
option(BUILD_CLIENT "Build Snapclient" ON)
|
option(BUILD_CLIENT "Build Snapclient" ON)
|
||||||
|
|
||||||
option(BUILD_WITH_FLAC "Build with FLAC support" ON)
|
option(BUILD_WITH_FLAC "Build with FLAC support" ON)
|
||||||
|
@ -88,110 +91,126 @@ IF(${BIGENDIAN})
|
||||||
ENDIF(${BIGENDIAN})
|
ENDIF(${BIGENDIAN})
|
||||||
|
|
||||||
# Check dependencies
|
# Check dependencies
|
||||||
find_package(PkgConfig REQUIRED)
|
|
||||||
|
if(NOT WIN32) # no PkgConfig on Windows...
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
include(CMakePushCheckState)
|
include(CMakePushCheckState)
|
||||||
include(CheckIncludeFileCXX)
|
include(CheckIncludeFileCXX)
|
||||||
|
|
||||||
if(MACOSX)
|
|
||||||
set(BONJOUR_FOUND true)
|
|
||||||
if (BONJOUR_FOUND)
|
|
||||||
add_definitions(-DHAS_BONJOUR)
|
|
||||||
endif (BONJOUR_FOUND)
|
|
||||||
|
|
||||||
add_definitions(-DFREEBSD -DHAS_DAEMON)
|
|
||||||
link_directories("/usr/local/lib")
|
|
||||||
list(APPEND INCLUDE_DIRS "/usr/local/include")
|
|
||||||
list(APPEND CMAKE_REQUIRED_INCLUDES "${INCLUDE_DIRS}")
|
|
||||||
elseif(ANDROID)
|
|
||||||
# add_definitions("-DNO_CPP11_STRING")
|
|
||||||
else()
|
|
||||||
if (BUILD_CLIENT)
|
|
||||||
pkg_search_module(ALSA REQUIRED alsa)
|
|
||||||
if (ALSA_FOUND)
|
|
||||||
add_definitions(-DHAS_ALSA)
|
|
||||||
endif (ALSA_FOUND)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_WITH_AVAHI)
|
|
||||||
pkg_search_module(AVAHI avahi-client)
|
|
||||||
if (AVAHI_FOUND)
|
|
||||||
add_definitions(-DHAS_AVAHI)
|
|
||||||
endif (AVAHI_FOUND)
|
|
||||||
endif(BUILD_WITH_AVAHI)
|
|
||||||
|
|
||||||
add_definitions(-DHAS_DAEMON)
|
|
||||||
|
|
||||||
if(FREEBSD)
|
|
||||||
add_definitions(-DFREEBSD)
|
|
||||||
link_directories("/usr/local/lib")
|
|
||||||
list(APPEND INCLUDE_DIRS "/usr/local/include")
|
|
||||||
list(APPEND CMAKE_REQUIRED_INCLUDES "${INCLUDE_DIRS}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include_directories(${INCLUDE_DIRS})
|
include_directories(${INCLUDE_DIRS})
|
||||||
|
|
||||||
|
|
||||||
include(${CMAKE_SOURCE_DIR}/cmake/CheckCXX11StringSupport.cmake)
|
include(${CMAKE_SOURCE_DIR}/cmake/CheckCXX11StringSupport.cmake)
|
||||||
|
|
||||||
CHECK_CXX11_STRING_SUPPORT(HAS_CXX11_STRING_SUPPORT)
|
CHECK_CXX11_STRING_SUPPORT(HAS_CXX11_STRING_SUPPORT)
|
||||||
if(NOT HAS_CXX11_STRING_SUPPORT)
|
if(NOT HAS_CXX11_STRING_SUPPORT)
|
||||||
add_definitions("-DNO_CPP11_STRING")
|
add_definitions("-DNO_CPP11_STRING")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_WITH_FLAC)
|
|
||||||
pkg_search_module(FLAC flac)
|
|
||||||
if (FLAC_FOUND)
|
|
||||||
add_definitions("-DHAS_FLAC")
|
|
||||||
endif (FLAC_FOUND)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_WITH_VORBIS OR BUILD_WITH_TREMOR)
|
if(NOT WIN32)
|
||||||
pkg_search_module(OGG ogg)
|
|
||||||
if (OGG_FOUND)
|
|
||||||
add_definitions("-DHAS_OGG")
|
|
||||||
endif (OGG_FOUND)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_WITH_VORBIS)
|
if(MACOSX)
|
||||||
pkg_search_module(VORBIS vorbis)
|
set(BONJOUR_FOUND true)
|
||||||
if (VORBIS_FOUND)
|
if (BONJOUR_FOUND)
|
||||||
add_definitions("-DHAS_VORBIS")
|
add_definitions(-DHAS_BONJOUR)
|
||||||
endif (VORBIS_FOUND)
|
endif (BONJOUR_FOUND)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_WITH_TREMOR)
|
add_definitions(-DFREEBSD -DHAS_DAEMON)
|
||||||
pkg_search_module(TREMOR vorbisidec)
|
link_directories("/usr/local/lib")
|
||||||
if (TREMOR_FOUND)
|
list(APPEND INCLUDE_DIRS "/usr/local/include")
|
||||||
add_definitions("-DHAS_TREMOR")
|
list(APPEND CMAKE_REQUIRED_INCLUDES "${INCLUDE_DIRS}")
|
||||||
endif (TREMOR_FOUND)
|
elseif(ANDROID)
|
||||||
endif()
|
# add_definitions("-DNO_CPP11_STRING")
|
||||||
|
else()
|
||||||
|
if (BUILD_CLIENT)
|
||||||
|
pkg_search_module(ALSA REQUIRED alsa)
|
||||||
|
if (ALSA_FOUND)
|
||||||
|
add_definitions(-DHAS_ALSA)
|
||||||
|
endif (ALSA_FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(BUILD_WITH_VORBIS)
|
if(BUILD_WITH_AVAHI)
|
||||||
pkg_search_module(VORBISENC vorbisenc)
|
pkg_search_module(AVAHI avahi-client)
|
||||||
if (VORBISENC_FOUND)
|
if (AVAHI_FOUND)
|
||||||
add_definitions("-DHAS_VORBIS_ENC")
|
add_definitions(-DHAS_AVAHI)
|
||||||
endif(VORBISENC_FOUND)
|
endif (AVAHI_FOUND)
|
||||||
endif()
|
endif(BUILD_WITH_AVAHI)
|
||||||
|
|
||||||
if(BUILD_WITH_OPUS)
|
add_definitions(-DHAS_DAEMON)
|
||||||
pkg_search_module(OPUS opus)
|
|
||||||
if (OPUS_FOUND)
|
|
||||||
add_definitions("-DHAS_OPUS")
|
|
||||||
endif (OPUS_FOUND)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_WITH_EXPAT)
|
if(FREEBSD)
|
||||||
pkg_search_module(EXPAT expat)
|
add_definitions(-DFREEBSD)
|
||||||
if (EXPAT_FOUND)
|
link_directories("/usr/local/lib")
|
||||||
add_definitions("-DHAS_EXPAT")
|
list(APPEND INCLUDE_DIRS "/usr/local/include")
|
||||||
endif (EXPAT_FOUND)
|
list(APPEND CMAKE_REQUIRED_INCLUDES "${INCLUDE_DIRS}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(BUILD_WITH_FLAC)
|
||||||
|
pkg_search_module(FLAC flac)
|
||||||
|
if (FLAC_FOUND)
|
||||||
|
add_definitions("-DHAS_FLAC")
|
||||||
|
endif (FLAC_FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_WITH_VORBIS OR BUILD_WITH_TREMOR)
|
||||||
|
pkg_search_module(OGG ogg)
|
||||||
|
if (OGG_FOUND)
|
||||||
|
add_definitions("-DHAS_OGG")
|
||||||
|
endif (OGG_FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_WITH_VORBIS)
|
||||||
|
pkg_search_module(VORBIS vorbis)
|
||||||
|
if (VORBIS_FOUND)
|
||||||
|
add_definitions("-DHAS_VORBIS")
|
||||||
|
endif (VORBIS_FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_WITH_TREMOR)
|
||||||
|
pkg_search_module(TREMOR vorbisidec)
|
||||||
|
if (TREMOR_FOUND)
|
||||||
|
add_definitions("-DHAS_TREMOR")
|
||||||
|
endif (TREMOR_FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_WITH_VORBIS)
|
||||||
|
pkg_search_module(VORBISENC vorbisenc)
|
||||||
|
if (VORBISENC_FOUND)
|
||||||
|
add_definitions("-DHAS_VORBIS_ENC")
|
||||||
|
endif(VORBISENC_FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_WITH_OPUS)
|
||||||
|
pkg_search_module(OPUS opus)
|
||||||
|
if (OPUS_FOUND)
|
||||||
|
add_definitions("-DHAS_OPUS")
|
||||||
|
endif (OPUS_FOUND)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_WITH_EXPAT)
|
||||||
|
pkg_search_module(EXPAT expat)
|
||||||
|
if (EXPAT_FOUND)
|
||||||
|
add_definitions("-DHAS_EXPAT")
|
||||||
|
endif (EXPAT_FOUND)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Boost 1.70 REQUIRED)
|
find_package(Boost 1.70 REQUIRED)
|
||||||
add_definitions("-DBOOST_ERROR_CODE_HEADER_ONLY")
|
add_definitions("-DBOOST_ERROR_CODE_HEADER_ONLY")
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
find_package(FLAC REQUIRED)
|
||||||
|
find_package(Ogg REQUIRED)
|
||||||
|
find_package(Vorbis REQUIRED)
|
||||||
|
add_definitions(-DNTDDI_VERSION=0x06020000 -D_WIN32_WINNT=0x0602 -DWINVER=0x0602 -DWINDOWS -DWIN32_LEAN_AND_MEAN -DUNICODE -D_UNICODE -D_CRT_SECURE_NO_WARNINGS )
|
||||||
|
add_definitions(-DHAS_OGG -DHAS_VORBIS -DHAS_FLAC -DHAS_VORBIS_ENC -DHAS_WASAPI)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(common)
|
add_subdirectory(common)
|
||||||
|
|
||||||
if (BUILD_SERVER)
|
if (BUILD_SERVER)
|
||||||
|
|
|
@ -14,7 +14,7 @@ set(CLIENT_INCLUDE
|
||||||
${CMAKE_SOURCE_DIR}/client
|
${CMAKE_SOURCE_DIR}/client
|
||||||
${CMAKE_SOURCE_DIR}/common
|
${CMAKE_SOURCE_DIR}/common
|
||||||
${ASIO_INCLUDE_DIRS}
|
${ASIO_INCLUDE_DIRS}
|
||||||
${POPL_INCLUDE_DIRS})
|
${POPL_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
|
||||||
if(MACOSX)
|
if(MACOSX)
|
||||||
|
@ -30,6 +30,11 @@ if(MACOSX)
|
||||||
find_library(COREFOUNDATION_LIB CoreFoundation)
|
find_library(COREFOUNDATION_LIB CoreFoundation)
|
||||||
find_library(AUDIOTOOLBOX_LIB AudioToolbox)
|
find_library(AUDIOTOOLBOX_LIB AudioToolbox)
|
||||||
list(APPEND CLIENT_LIBRARIES ${COREAUDIO_LIB} ${COREFOUNDATION_LIB} ${AUDIOTOOLBOX_LIB})
|
list(APPEND CLIENT_LIBRARIES ${COREAUDIO_LIB} ${COREFOUNDATION_LIB} ${AUDIOTOOLBOX_LIB})
|
||||||
|
elseif (WIN32)
|
||||||
|
list(REMOVE_ITEM CLIENT_INCLUDE "common/daemon.cpp")
|
||||||
|
list(APPEND CLIENT_SOURCES player/wasapi_player.cpp decoder/ogg_decoder.cpp)
|
||||||
|
list(APPEND CLIENT_LIBRARIES ${FLAC_LIBRARIES} ${OGG_LIBRARIES} ${VORBIS_LIBRARIES} ${OGG_LIBRARIES} wsock32 ws2_32 avrt ksuser iphlpapi)
|
||||||
|
list(APPEND CLIENT_INCLUDE ${FLAC_INCLUDE_DIRS} ${OGG_INCLUDE_DIRS} ${VORBIS_INCLUDE_DIRS})
|
||||||
else()
|
else()
|
||||||
# Avahi
|
# Avahi
|
||||||
if (AVAHI_FOUND)
|
if (AVAHI_FOUND)
|
||||||
|
|
|
@ -59,7 +59,12 @@ void ClientConnection::socketRead(void* _to, size_t _bytes)
|
||||||
|
|
||||||
std::string ClientConnection::getMacAddress()
|
std::string ClientConnection::getMacAddress()
|
||||||
{
|
{
|
||||||
std::string mac = ::getMacAddress(socket_.native_handle());
|
std::string mac =
|
||||||
|
#ifndef WINDOWS
|
||||||
|
::getMacAddress(socket_.native_handle());
|
||||||
|
#else
|
||||||
|
::getMacAddress(socket_.local_endpoint().address().to_string());
|
||||||
|
#endif
|
||||||
if (mac.empty())
|
if (mac.empty())
|
||||||
mac = "00:00:00:00:00:00";
|
mac = "00:00:00:00:00:00";
|
||||||
LOG(INFO) << "My MAC: \"" << mac << "\", socket: " << socket_.native_handle() << "\n";
|
LOG(INFO) << "My MAC: \"" << mac << "\", socket: " << socket_.native_handle() << "\n";
|
||||||
|
|
|
@ -142,6 +142,10 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg::
|
||||||
#ifdef HAS_COREAUDIO
|
#ifdef HAS_COREAUDIO
|
||||||
if (!player_ && (player_name.empty() || (player_name == "coreaudio")))
|
if (!player_ && (player_name.empty() || (player_name == "coreaudio")))
|
||||||
player_ = make_unique<CoreAudioPlayer>(pcm_device, stream_);
|
player_ = make_unique<CoreAudioPlayer>(pcm_device, stream_);
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_WASAPI
|
||||||
|
if (!player_ && (player_name.empty() || (player_name == "wasapi")))
|
||||||
|
player_ = make_unique<WASAPIPlayer>(pcm_device, stream_);
|
||||||
#endif
|
#endif
|
||||||
if (!player_)
|
if (!player_)
|
||||||
throw SnapException("No audio player support");
|
throw SnapException("No audio player support");
|
||||||
|
|
|
@ -37,6 +37,9 @@
|
||||||
#ifdef HAS_COREAUDIO
|
#ifdef HAS_COREAUDIO
|
||||||
#include "player/coreaudio_player.hpp"
|
#include "player/coreaudio_player.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WINDOWS
|
||||||
|
#include "player/wasapi_player.h"
|
||||||
|
#endif
|
||||||
#include "client_connection.hpp"
|
#include "client_connection.hpp"
|
||||||
#include "client_settings.hpp"
|
#include "client_settings.hpp"
|
||||||
#include "metadata.hpp"
|
#include "metadata.hpp"
|
||||||
|
|
337
client/player/wasapi_player.cpp
Normal file
337
client/player/wasapi_player.cpp
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
#include "wasapi_player.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <audioclient.h>
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
#include <comdef.h>
|
||||||
|
#include <comip.h>
|
||||||
|
#include <avrt.h>
|
||||||
|
#include <ksmedia.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <functiondiscoverykeys_devpkey.h>
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
#include "common/snap_exception.hpp"
|
||||||
|
#include "common/aixlog.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
static constexpr auto LOG_TAG = "WASAPI";
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct COMMemDeleter
|
||||||
|
{
|
||||||
|
void operator() (T* obj)
|
||||||
|
{
|
||||||
|
if (obj != NULL)
|
||||||
|
{
|
||||||
|
CoTaskMemFree(obj);
|
||||||
|
obj = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using com_mem_ptr = unique_ptr<T, COMMemDeleter<T> >;
|
||||||
|
|
||||||
|
using com_handle = unique_ptr<void, function<BOOL(HANDLE)> >;
|
||||||
|
|
||||||
|
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
|
||||||
|
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
|
||||||
|
const IID IID_IAudioClient = __uuidof(IAudioClient);
|
||||||
|
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
|
||||||
|
const IID IID_IAudioClock = __uuidof(IAudioClock);
|
||||||
|
|
||||||
|
_COM_SMARTPTR_TYPEDEF(IMMDevice,__uuidof(IMMDevice));
|
||||||
|
_COM_SMARTPTR_TYPEDEF(IMMDeviceCollection,__uuidof(IMMDeviceCollection));
|
||||||
|
_COM_SMARTPTR_TYPEDEF(IMMDeviceEnumerator,__uuidof(IMMDeviceEnumerator));
|
||||||
|
_COM_SMARTPTR_TYPEDEF(IAudioClient,__uuidof(IAudioClient));
|
||||||
|
_COM_SMARTPTR_TYPEDEF(IPropertyStore,__uuidof(IPropertyStore));
|
||||||
|
|
||||||
|
#define REFTIMES_PER_SEC 10000000
|
||||||
|
#define REFTIMES_PER_MILLISEC 10000
|
||||||
|
|
||||||
|
EXTERN_C const PROPERTYKEY DECLSPEC_SELECTANY PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 };
|
||||||
|
|
||||||
|
#define CHECK_HR(hres) if(FAILED(hres)){stringstream ss;ss<<"HRESULT fault status: "<<hex<<(hres)<<" line "<<dec<<__LINE__<<endl;throw SnapException(ss.str());}
|
||||||
|
|
||||||
|
WASAPIPlayer::WASAPIPlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream)
|
||||||
|
: Player(pcmDevice, stream)
|
||||||
|
{
|
||||||
|
HRESULT hr = CoInitializeEx(
|
||||||
|
NULL,
|
||||||
|
COINIT_MULTITHREADED);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
WASAPIPlayer::~WASAPIPlayer()
|
||||||
|
{
|
||||||
|
WASAPIPlayer::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline PcmDevice convertToDevice(int idx, IMMDevicePtr& device)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
PcmDevice desc;
|
||||||
|
|
||||||
|
LPWSTR id = NULL;
|
||||||
|
hr = device->GetId(&id);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
IPropertyStorePtr properties = nullptr;
|
||||||
|
hr = device->OpenPropertyStore(STGM_READ, &properties);
|
||||||
|
|
||||||
|
PROPVARIANT deviceName;
|
||||||
|
PropVariantInit(&deviceName);
|
||||||
|
|
||||||
|
hr = properties->GetValue(PKEY_Device_FriendlyName, &deviceName);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
desc.idx = idx;
|
||||||
|
desc.name = wstring_convert<codecvt_utf8<wchar_t>, wchar_t>().to_bytes(id);
|
||||||
|
desc.description = wstring_convert<codecvt_utf8<wchar_t>, wchar_t>().to_bytes(deviceName.pwszVal);
|
||||||
|
|
||||||
|
CoTaskMemFree(id);
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<PcmDevice> WASAPIPlayer::pcm_list()
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
IMMDeviceCollectionPtr devices = nullptr;
|
||||||
|
IMMDeviceEnumeratorPtr deviceEnumerator = nullptr;
|
||||||
|
|
||||||
|
hr = CoInitializeEx(
|
||||||
|
NULL,
|
||||||
|
COINIT_MULTITHREADED);
|
||||||
|
if (hr != CO_E_ALREADYINITIALIZED)
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
hr = CoCreateInstance(
|
||||||
|
CLSID_MMDeviceEnumerator, NULL,
|
||||||
|
CLSCTX_ALL, IID_IMMDeviceEnumerator,
|
||||||
|
(void**)&deviceEnumerator);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &devices);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
UINT deviceCount;
|
||||||
|
devices->GetCount(&deviceCount);
|
||||||
|
|
||||||
|
if (deviceCount == 0)
|
||||||
|
throw SnapException("no valid devices");
|
||||||
|
|
||||||
|
vector<PcmDevice> deviceList;
|
||||||
|
|
||||||
|
{
|
||||||
|
IMMDevicePtr defaultDevice = nullptr;
|
||||||
|
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
auto dev = convertToDevice(0, defaultDevice);
|
||||||
|
dev.name = "default";
|
||||||
|
deviceList.push_back(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UINT i = 0; i < deviceCount; ++i)
|
||||||
|
{
|
||||||
|
IMMDevicePtr device = nullptr;
|
||||||
|
|
||||||
|
hr = devices->Item(i, &device);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
deviceList.push_back(convertToDevice(i + 1, device));
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WASAPIPlayer::worker()
|
||||||
|
{
|
||||||
|
assert(sizeof(char) == sizeof(BYTE));
|
||||||
|
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
// Create the format specifier
|
||||||
|
com_mem_ptr<WAVEFORMATEX> waveformat((WAVEFORMATEX*)(CoTaskMemAlloc(sizeof(WAVEFORMATEX))));
|
||||||
|
waveformat->wFormatTag = WAVE_FORMAT_PCM;
|
||||||
|
waveformat->nChannels = stream_->getFormat().channels();
|
||||||
|
waveformat->nSamplesPerSec = stream_->getFormat().rate();
|
||||||
|
waveformat->wBitsPerSample = stream_->getFormat().bits();
|
||||||
|
|
||||||
|
waveformat->nBlockAlign = waveformat->nChannels * waveformat->wBitsPerSample / 8;
|
||||||
|
waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nBlockAlign;
|
||||||
|
|
||||||
|
waveformat->cbSize = 0;
|
||||||
|
|
||||||
|
com_mem_ptr<WAVEFORMATEXTENSIBLE> waveformatExtended((WAVEFORMATEXTENSIBLE*)(CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE))));
|
||||||
|
waveformatExtended->Format = *waveformat;
|
||||||
|
waveformatExtended->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
|
waveformatExtended->Format.cbSize = 22;
|
||||||
|
waveformatExtended->Samples.wValidBitsPerSample = waveformat->wBitsPerSample;
|
||||||
|
waveformatExtended->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||||
|
waveformatExtended->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
|
|
||||||
|
// Retrieve the device enumerator
|
||||||
|
IMMDeviceEnumeratorPtr deviceEnumerator = nullptr;
|
||||||
|
hr = CoCreateInstance(
|
||||||
|
CLSID_MMDeviceEnumerator, NULL,
|
||||||
|
CLSCTX_ALL, IID_IMMDeviceEnumerator,
|
||||||
|
(void**)&deviceEnumerator);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Register the default playback device (eRender for playback)
|
||||||
|
IMMDevicePtr device = nullptr;
|
||||||
|
if (pcmDevice_.idx == 0)
|
||||||
|
{
|
||||||
|
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IMMDeviceCollectionPtr devices = nullptr;
|
||||||
|
hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &devices);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
devices->Item(pcmDevice_.idx, &device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate the device
|
||||||
|
IAudioClientPtr audioClient = nullptr;
|
||||||
|
hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
hr = audioClient->IsFormatSupported(
|
||||||
|
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||||
|
&(waveformatExtended->Format),
|
||||||
|
NULL);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Get the device period
|
||||||
|
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
|
||||||
|
hr = audioClient->GetDevicePeriod(NULL, &hnsRequestedDuration);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Initialize the client at minimum latency
|
||||||
|
hr = audioClient->Initialize(
|
||||||
|
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||||
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||||
|
hnsRequestedDuration,
|
||||||
|
hnsRequestedDuration,
|
||||||
|
&(waveformatExtended->Format),
|
||||||
|
NULL);
|
||||||
|
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
|
||||||
|
{
|
||||||
|
UINT32 alignedBufferSize;
|
||||||
|
hr = audioClient->GetBufferSize(&alignedBufferSize);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
audioClient.Attach(NULL, false);
|
||||||
|
hnsRequestedDuration = (REFERENCE_TIME)((10000.0 * 1000 / waveformatExtended->Format.nSamplesPerSec * alignedBufferSize) + 0.5);
|
||||||
|
hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
hr = audioClient->Initialize(
|
||||||
|
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||||
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||||
|
hnsRequestedDuration,
|
||||||
|
hnsRequestedDuration,
|
||||||
|
&(waveformatExtended->Format),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Register an event to refill the buffer
|
||||||
|
com_handle eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL), &::CloseHandle);
|
||||||
|
if (eventHandle == NULL)
|
||||||
|
CHECK_HR(E_FAIL);
|
||||||
|
hr = audioClient->SetEventHandle(HANDLE(eventHandle.get()));
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Get size of buffer
|
||||||
|
UINT32 bufferFrameCount;
|
||||||
|
hr = audioClient->GetBufferSize(&bufferFrameCount);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Get the rendering service
|
||||||
|
IAudioRenderClient* renderClient = NULL;
|
||||||
|
hr = audioClient->GetService(
|
||||||
|
IID_IAudioRenderClient,
|
||||||
|
(void**)&renderClient);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Grab the clock service
|
||||||
|
IAudioClock* clock = NULL;
|
||||||
|
hr = audioClient->GetService(
|
||||||
|
IID_IAudioClock,
|
||||||
|
(void**)&clock);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
// Boost our priority
|
||||||
|
DWORD taskIndex = 0;
|
||||||
|
com_handle taskHandle(AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex),
|
||||||
|
&::AvRevertMmThreadCharacteristics);
|
||||||
|
if (taskHandle == NULL)
|
||||||
|
CHECK_HR(E_FAIL);
|
||||||
|
|
||||||
|
// And, action!
|
||||||
|
hr = audioClient->Start();
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
size_t bufferSize = bufferFrameCount * waveformatExtended->Format.nBlockAlign;
|
||||||
|
BYTE* buffer;
|
||||||
|
unique_ptr<char[]> queueBuffer(new char[bufferSize]);
|
||||||
|
UINT64 position = 0, bufferPosition = 0, frequency;
|
||||||
|
clock->GetFrequency(&frequency);
|
||||||
|
|
||||||
|
while (active_)
|
||||||
|
{
|
||||||
|
DWORD returnVal = WaitForSingleObject(eventHandle.get(), 2000);
|
||||||
|
if (returnVal != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
CHECK_HR(ERROR_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread was sleeping above, double check that we are still running
|
||||||
|
if (!active_)
|
||||||
|
break;
|
||||||
|
|
||||||
|
clock->GetPosition(&position, NULL);
|
||||||
|
|
||||||
|
if (stream_->getPlayerChunk(queueBuffer.get(), microseconds(
|
||||||
|
((bufferPosition * 1000000) / waveformat->nSamplesPerSec) -
|
||||||
|
((position * 1000000) / frequency)),
|
||||||
|
bufferFrameCount))
|
||||||
|
{
|
||||||
|
adjustVolume(queueBuffer.get(), bufferFrameCount);
|
||||||
|
hr = renderClient->GetBuffer(bufferFrameCount, &buffer);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
memcpy(buffer, queueBuffer.get(), bufferSize);
|
||||||
|
hr = renderClient->ReleaseBuffer(bufferFrameCount, 0);
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
bufferPosition += bufferFrameCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::clog << static_cast<AixLog::Severity>(INFO) << AixLog::Tag(LOG_TAG);
|
||||||
|
LOG(INFO, LOG_TAG) << "Failed to get chunk\n";
|
||||||
|
|
||||||
|
hr = audioClient->Stop();
|
||||||
|
CHECK_HR(hr);
|
||||||
|
hr = audioClient->Reset();
|
||||||
|
CHECK_HR(hr);
|
||||||
|
|
||||||
|
while (active_ && !stream_->waitForChunk(std::chrono::milliseconds(100)))
|
||||||
|
LOG(INFO, LOG_TAG) << "Waiting for chunk\n";
|
||||||
|
|
||||||
|
hr = audioClient->Start();
|
||||||
|
CHECK_HR(hr);
|
||||||
|
bufferPosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
client/player/wasapi_player.h
Normal file
35
client/player/wasapi_player.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/***
|
||||||
|
This file is part of snapcast
|
||||||
|
Copyright (C) 2014-2016 Johannes Pohl
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifndef WASAPI_PLAYER_H
|
||||||
|
#define WASAPI_PLAYER_H
|
||||||
|
|
||||||
|
#include "player.hpp"
|
||||||
|
|
||||||
|
class WASAPIPlayer : public Player
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WASAPIPlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream);
|
||||||
|
virtual ~WASAPIPlayer();
|
||||||
|
|
||||||
|
static std::vector<PcmDevice> pcm_list(void);
|
||||||
|
protected:
|
||||||
|
virtual void worker();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#ifndef WINDOWS
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "browseZeroConf/browse_mdns.hpp"
|
#include "browseZeroConf/browse_mdns.hpp"
|
||||||
#include "common/popl.hpp"
|
#include "common/popl.hpp"
|
||||||
|
@ -46,8 +48,13 @@ using namespace std::chrono_literals;
|
||||||
|
|
||||||
PcmDevice getPcmDevice(const std::string& soundcard)
|
PcmDevice getPcmDevice(const std::string& soundcard)
|
||||||
{
|
{
|
||||||
|
#if defined(HAS_ALSA) || defined(WINDOWS)
|
||||||
|
vector<PcmDevice> pcmDevices =
|
||||||
#ifdef HAS_ALSA
|
#ifdef HAS_ALSA
|
||||||
vector<PcmDevice> pcmDevices = AlsaPlayer::pcm_list();
|
AlsaPlayer::pcm_list();
|
||||||
|
#else
|
||||||
|
WASAPIPlayer::pcm_list();
|
||||||
|
#endif
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -63,7 +70,6 @@ PcmDevice getPcmDevice(const std::string& soundcard)
|
||||||
for (auto dev : pcmDevices)
|
for (auto dev : pcmDevices)
|
||||||
if (dev.name.find(soundcard) != string::npos)
|
if (dev.name.find(soundcard) != string::npos)
|
||||||
return dev;
|
return dev;
|
||||||
#else
|
|
||||||
std::ignore = soundcard;
|
std::ignore = soundcard;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -90,7 +96,7 @@ int main(int argc, char** argv)
|
||||||
auto groffSwitch = op.add<Switch, Attribute::hidden>("", "groff", "produce groff message");
|
auto groffSwitch = op.add<Switch, Attribute::hidden>("", "groff", "produce groff message");
|
||||||
auto debugOption = op.add<Implicit<string>, Attribute::hidden>("", "debug", "enable debug logging", ""); // TODO: &settings.logging.debug);
|
auto debugOption = op.add<Implicit<string>, Attribute::hidden>("", "debug", "enable debug logging", ""); // TODO: &settings.logging.debug);
|
||||||
auto versionSwitch = op.add<Switch>("v", "version", "show version number");
|
auto versionSwitch = op.add<Switch>("v", "version", "show version number");
|
||||||
#if defined(HAS_ALSA)
|
#if defined(HAS_ALSA) || defined(WINDOWS)
|
||||||
auto listSwitch = op.add<Switch>("l", "list", "list PCM devices");
|
auto listSwitch = op.add<Switch>("l", "list", "list PCM devices");
|
||||||
/*auto soundcardValue =*/op.add<Value<string>>("s", "soundcard", "index or name of the pcm device", "default", &pcm_device);
|
/*auto soundcardValue =*/op.add<Value<string>>("s", "soundcard", "index or name of the pcm device", "default", &pcm_device);
|
||||||
#endif
|
#endif
|
||||||
|
@ -132,10 +138,15 @@ int main(int argc, char** argv)
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_ALSA
|
#if defined(HAS_ALSA) || defined(WINDOWS)
|
||||||
if (listSwitch->is_set())
|
if (listSwitch->is_set())
|
||||||
{
|
{
|
||||||
vector<PcmDevice> pcmDevices = AlsaPlayer::pcm_list();
|
vector<PcmDevice> pcmDevices =
|
||||||
|
#ifdef HAS_ALSA
|
||||||
|
AlsaPlayer::pcm_list();
|
||||||
|
#else
|
||||||
|
WASAPIPlayer::pcm_list();
|
||||||
|
#endif
|
||||||
for (auto dev : pcmDevices)
|
for (auto dev : pcmDevices)
|
||||||
{
|
{
|
||||||
cout << dev.idx << ": " << dev.name << "\n" << dev.description << "\n\n";
|
cout << dev.idx << ": " << dev.name << "\n" << dev.description << "\n\n";
|
||||||
|
@ -214,6 +225,10 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAS_WASAPI)
|
||||||
|
settings.player.player_name = "wasapi";
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAS_SOXR
|
#ifdef HAS_SOXR
|
||||||
if (sample_format->is_set())
|
if (sample_format->is_set())
|
||||||
{
|
{
|
||||||
|
@ -228,7 +243,11 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
bool active = true;
|
bool active = true;
|
||||||
std::shared_ptr<Controller> controller;
|
std::shared_ptr<Controller> controller;
|
||||||
auto signal_handler = install_signal_handler({SIGHUP, SIGTERM, SIGINT},
|
auto signal_handler = install_signal_handler({
|
||||||
|
#ifndef WINDOWS // no sighup on windows
|
||||||
|
SIGHUP,
|
||||||
|
#endif
|
||||||
|
SIGTERM, SIGINT},
|
||||||
[&active, &controller](int signal, const std::string& strsignal) {
|
[&active, &controller](int signal, const std::string& strsignal) {
|
||||||
SLOG(INFO) << "Received signal " << signal << ": " << strsignal << "\n";
|
SLOG(INFO) << "Received signal " << signal << ": " << strsignal << "\n";
|
||||||
active = false;
|
active = false;
|
||||||
|
|
|
@ -390,7 +390,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
|
||||||
// and can play 30ms of the stream
|
// and can play 30ms of the stream
|
||||||
uint32_t silent_frames = static_cast<size_t>(-chunk_->format.nsRate() * std::chrono::duration_cast<cs::nsec>(age).count());
|
uint32_t silent_frames = static_cast<size_t>(-chunk_->format.nsRate() * std::chrono::duration_cast<cs::nsec>(age).count());
|
||||||
bool result = (silent_frames <= frames);
|
bool result = (silent_frames <= frames);
|
||||||
silent_frames = std::min(silent_frames, frames);
|
silent_frames = (std::min)(silent_frames, frames);
|
||||||
LOG(DEBUG, LOG_TAG) << "Silent frames: " << silent_frames << ", frames: " << frames
|
LOG(DEBUG, LOG_TAG) << "Silent frames: " << silent_frames << ", frames: " << frames
|
||||||
<< ", age: " << std::chrono::duration_cast<cs::usec>(age).count() / 1000. << "\n";
|
<< ", age: " << std::chrono::duration_cast<cs::usec>(age).count() / 1000. << "\n";
|
||||||
getSilentPlayerChunk(outputBuffer, silent_frames);
|
getSilentPlayerChunk(outputBuffer, silent_frames);
|
||||||
|
@ -453,7 +453,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
|
||||||
if ((cs::usec(shortMedian_) > kCorrectionBegin) && (cs::usec(miniMedian) > cs::usec(50)) && (cs::usec(age) > cs::usec(50)))
|
if ((cs::usec(shortMedian_) > kCorrectionBegin) && (cs::usec(miniMedian) > cs::usec(50)) && (cs::usec(age) > cs::usec(50)))
|
||||||
{
|
{
|
||||||
double rate = (shortMedian_ / 100) * 0.00005;
|
double rate = (shortMedian_ / 100) * 0.00005;
|
||||||
rate = 1.0 - std::min(rate, 0.0005);
|
rate = 1.0 - (std::min)(rate, 0.0005);
|
||||||
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
||||||
// we are late (age > 0), this means we are not playing fast enough
|
// we are late (age > 0), this means we are not playing fast enough
|
||||||
// => the real sample rate seems to be lower, we have to drop some frames
|
// => the real sample rate seems to be lower, we have to drop some frames
|
||||||
|
@ -462,7 +462,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
|
||||||
else if ((cs::usec(shortMedian_) < -kCorrectionBegin) && (cs::usec(miniMedian) < -cs::usec(50)) && (cs::usec(age) < -cs::usec(50)))
|
else if ((cs::usec(shortMedian_) < -kCorrectionBegin) && (cs::usec(miniMedian) < -cs::usec(50)) && (cs::usec(age) < -cs::usec(50)))
|
||||||
{
|
{
|
||||||
double rate = (-shortMedian_ / 100) * 0.00005;
|
double rate = (-shortMedian_ / 100) * 0.00005;
|
||||||
rate = 1.0 + std::min(rate, 0.0005);
|
rate = 1.0 + (std::min)(rate, 0.0005);
|
||||||
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
||||||
// we are early (age > 0), this means we are playing too fast
|
// we are early (age > 0), this means we are playing too fast
|
||||||
// => the real sample rate seems to be higher, we have to insert some frames
|
// => the real sample rate seems to be higher, we have to insert some frames
|
||||||
|
|
22
cmake/FindFLAC.cmake
Normal file
22
cmake/FindFLAC.cmake
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# - Try to find FLAC
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# FLAC_FOUND - system has libFLAC
|
||||||
|
# FLAC_INCLUDE_DIRS - the libFLAC include directory
|
||||||
|
# FLAC_LIBRARIES - The libFLAC libraries
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules (FLAC flac)
|
||||||
|
list(APPEND FLAC_INCLUDE_DIRS ${FLAC_INCLUDEDIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT FLAC_FOUND)
|
||||||
|
find_path(FLAC_INCLUDE_DIRS FLAC/all.h)
|
||||||
|
find_library(FLAC_LIBRARIES FLAC)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(FLAC DEFAULT_MSG FLAC_INCLUDE_DIRS FLAC_LIBRARIES)
|
||||||
|
|
||||||
|
mark_as_advanced(FLAC_INCLUDE_DIRS FLAC_LIBRARIES)
|
22
cmake/FindOgg.cmake
Normal file
22
cmake/FindOgg.cmake
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# - Try to find ogg
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# OGG_FOUND - system has ogg
|
||||||
|
# OGG_INCLUDE_DIRS - the ogg include directory
|
||||||
|
# OGG_LIBRARIES - The ogg libraries
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules (OGG ogg)
|
||||||
|
list(APPEND OGG_INCLUDE_DIRS ${OGG_INCLUDEDIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT OGG_FOUND)
|
||||||
|
find_path(OGG_INCLUDE_DIRS ogg/ogg.h)
|
||||||
|
find_library(OGG_LIBRARIES ogg)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Ogg DEFAULT_MSG OGG_INCLUDE_DIRS OGG_LIBRARIES)
|
||||||
|
|
||||||
|
mark_as_advanced(OGG_INCLUDE_DIRS OGG_LIBRARIES)
|
22
cmake/FindVorbis.cmake
Normal file
22
cmake/FindVorbis.cmake
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# - Try to find vorbis
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# VORBIS_FOUND - system has libvorbis
|
||||||
|
# VORBIS_INCLUDE_DIRS - the libvorbis include directory
|
||||||
|
# VORBIS_LIBRARIES - The libvorbis libraries
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules (VORBIS vorbis)
|
||||||
|
list(APPEND VORBIS_INCLUDE_DIRS ${VORBIS_INCLUDEDIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT VORBIS_FOUND)
|
||||||
|
find_path(VORBIS_INCLUDE_DIRS vorbis/vorbisenc.h)
|
||||||
|
find_library(VORBIS_LIBRARIES vorbis)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Vorbis DEFAULT_MSG VORBIS_INCLUDE_DIRS VORBIS_LIBRARIES)
|
||||||
|
|
||||||
|
mark_as_advanced(VORBIS_INCLUDE_DIRS VORBIS_LIBRARIES)
|
17
cmake/FindVorbisEnc.cmake
Normal file
17
cmake/FindVorbisEnc.cmake
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# - Try to find vorbisenc
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# VORBISENC_FOUND - system has libvorbisenc
|
||||||
|
# VORBISENC_INCLUDE_DIRS - the libvorbisenc include directory
|
||||||
|
# VORBISENC_LIBRARIES - The libvorbisenc libraries
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules (VORBISENC vorbisenc)
|
||||||
|
list(APPEND VORBISENC_INCLUDE_DIRS ${VORBISENC_INCLUDEDIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Vorbis DEFAULT_MSG VORBISENC_INCLUDE_DIRS VORBISENC_LIBRARIES)
|
||||||
|
|
||||||
|
mark_as_advanced(VORBISENC_INCLUDE_DIRS VORBISENC_LIBRARIES)
|
|
@ -1 +1,5 @@
|
||||||
add_library(common STATIC daemon.cpp sample_format.cpp)
|
if(NOT WIN32)
|
||||||
|
add_library(common STATIC daemon.cpp sample_format.cpp)
|
||||||
|
else()
|
||||||
|
add_library(common STATIC sample_format.cpp)
|
||||||
|
endif()
|
||||||
|
|
|
@ -108,6 +108,21 @@
|
||||||
#define SPECIAL AixLog::Type::special
|
#define SPECIAL AixLog::Type::special
|
||||||
#define TIMESTAMP AixLog::Timestamp(std::chrono::system_clock::now())
|
#define TIMESTAMP AixLog::Timestamp(std::chrono::system_clock::now())
|
||||||
|
|
||||||
|
|
||||||
|
// stijnvdb: sorry! :) LOG(SEV, "tag") was not working for Windows and I couldn't figure out how to fix it for windows without potentially breaking everything else...
|
||||||
|
// https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros (Jason Deng)
|
||||||
|
#ifdef WIN32
|
||||||
|
#define LOG_2(severity, tag) AIXLOG_INTERNAL__LOG_SEVERITY_TAG(severity, tag)
|
||||||
|
#define LOG_1(severity) AIXLOG_INTERNAL__LOG_SEVERITY(severity)
|
||||||
|
#define LOG_0() LOG_1(0)
|
||||||
|
|
||||||
|
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
|
||||||
|
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
|
||||||
|
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, LOG_2, LOG_1, ))
|
||||||
|
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(__VA_ARGS__())
|
||||||
|
#define LOG(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief
|
||||||
* Severity of the log message
|
* Severity of the log message
|
||||||
|
@ -754,7 +769,8 @@ struct SinkOutputDebugString : public Sink
|
||||||
|
|
||||||
void log(const Metadata& metadata, const std::string& message) override
|
void log(const Metadata& metadata, const std::string& message) override
|
||||||
{
|
{
|
||||||
OutputDebugString(message.c_str());
|
std::wstring wide = std::wstring(message.begin(), message.end());
|
||||||
|
OutputDebugString(wide.c_str());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -914,7 +930,8 @@ struct SinkEventLog : public Sink
|
||||||
{
|
{
|
||||||
SinkEventLog(const std::string& ident, Severity severity, Type type = Type::all) : Sink(severity, type)
|
SinkEventLog(const std::string& ident, Severity severity, Type type = Type::all) : Sink(severity, type)
|
||||||
{
|
{
|
||||||
event_log = RegisterEventSource(NULL, ident.c_str());
|
std::wstring wide = std::wstring(ident.begin(), ident.end()); // stijnvdb: RegisterEventSource expands to RegisterEventSourceW which takes wchar_t
|
||||||
|
event_log = RegisterEventSource(NULL, wide.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
WORD get_type(Severity severity) const
|
WORD get_type(Severity severity) const
|
||||||
|
@ -940,8 +957,10 @@ struct SinkEventLog : public Sink
|
||||||
|
|
||||||
void log(const Metadata& metadata, const std::string& message) override
|
void log(const Metadata& metadata, const std::string& message) override
|
||||||
{
|
{
|
||||||
|
std::wstring wide = std::wstring(message.begin(), message.end());
|
||||||
// We need this temp variable because we cannot take address of rValue
|
// We need this temp variable because we cannot take address of rValue
|
||||||
const char* c_str = message.c_str();
|
const wchar_t* c_str = wide.c_str();
|
||||||
|
|
||||||
ReportEvent(event_log, get_type(metadata.severity), 0, 0, NULL, 1, 0, &c_str, NULL);
|
ReportEvent(event_log, get_type(metadata.severity), 0, 0, NULL, 1, 0, &c_str, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,36 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
|
#ifndef WINDOWS
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef WINDOWS // Implementation from http://stackoverflow.com/a/26085827/2510022
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
inline static int gettimeofday(struct timeval* tp, struct timezone* tzp)
|
||||||
|
{
|
||||||
|
// Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
|
||||||
|
static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
|
||||||
|
|
||||||
|
SYSTEMTIME system_time;
|
||||||
|
FILETIME file_time;
|
||||||
|
uint64_t time;
|
||||||
|
|
||||||
|
GetSystemTime(&system_time);
|
||||||
|
SystemTimeToFileTime(&system_time, &file_time);
|
||||||
|
time = ((uint64_t)file_time.dwLowDateTime);
|
||||||
|
time += ((uint64_t)file_time.dwHighDateTime) << 32;
|
||||||
|
|
||||||
|
tp->tv_sec = (long)((time - EPOCH) / 10000000L);
|
||||||
|
tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
template<typename CharT, typename TraitsT = std::char_traits<CharT> >
|
template<typename CharT, typename TraitsT = std::char_traits<CharT> >
|
||||||
class vectorwrapbuf : public std::basic_streambuf<CharT, TraitsT>
|
class vectorwrapbuf : public std::basic_streambuf<CharT, TraitsT>
|
||||||
|
@ -69,7 +96,11 @@ struct tv
|
||||||
tv()
|
tv()
|
||||||
{
|
{
|
||||||
timeval t;
|
timeval t;
|
||||||
|
#ifdef WINDOWS
|
||||||
|
gettimeofday(&t, NULL);
|
||||||
|
#else
|
||||||
chronos::steadytimeofday(&t);
|
chronos::steadytimeofday(&t);
|
||||||
|
#endif
|
||||||
sec = t.tv_sec;
|
sec = t.tv_sec;
|
||||||
usec = t.tv_usec;
|
usec = t.tv_usec;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,25 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#ifdef WINDOWS
|
||||||
|
const char* strsignal(int sig)
|
||||||
|
{
|
||||||
|
switch (sig)
|
||||||
|
{
|
||||||
|
case SIGTERM:
|
||||||
|
return "SIGTERM";
|
||||||
|
case SIGINT:
|
||||||
|
return "SIGINT";
|
||||||
|
case SIGBREAK:
|
||||||
|
return "SIGBREAK";
|
||||||
|
case SIGABRT:
|
||||||
|
return "SIGABRT";
|
||||||
|
default:
|
||||||
|
return "Unhandled";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
using signal_callback = std::function<void(int signal, const std::string& name)>;
|
using signal_callback = std::function<void(int signal, const std::string& name)>;
|
||||||
|
|
||||||
static std::future<int> install_signal_handler(std::set<int> signals, const signal_callback& on_signal = nullptr)
|
static std::future<int> install_signal_handler(std::set<int> signals, const signal_callback& on_signal = nullptr)
|
||||||
|
@ -49,4 +68,4 @@ static std::future<int> install_signal_handler(std::set<int> signals, const sign
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -20,16 +20,72 @@
|
||||||
#define TIME_DEFS_H
|
#define TIME_DEFS_H
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <sys/time.h>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#ifdef MACOS
|
#ifdef MACOS
|
||||||
#include <mach/clock.h>
|
#include <mach/clock.h>
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef WINDOWS
|
||||||
|
#include <sys/time.h>
|
||||||
|
#else // from the GNU C library implementation of sys/time.h
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
#define timersub(a, b, result) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||||
|
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||||
|
if ((result)->tv_usec < 0) \
|
||||||
|
{ \
|
||||||
|
--(result)->tv_sec; \
|
||||||
|
(result)->tv_usec += 1000000; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define CLOCK_MONOTONIC 42 // discarded on windows plaforms
|
||||||
|
|
||||||
|
// from http://stackoverflow.com/a/38212960/2510022
|
||||||
|
#define BILLION (1E9)
|
||||||
|
|
||||||
|
static BOOL g_first_time = 1;
|
||||||
|
static LARGE_INTEGER g_counts_per_sec;
|
||||||
|
|
||||||
|
inline static int clock_gettime(int dummy, struct timespec* ct)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER count;
|
||||||
|
|
||||||
|
if (g_first_time)
|
||||||
|
{
|
||||||
|
g_first_time = 0;
|
||||||
|
|
||||||
|
if (0 == QueryPerformanceFrequency(&g_counts_per_sec))
|
||||||
|
{
|
||||||
|
g_counts_per_sec.QuadPart = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((NULL == ct) || (g_counts_per_sec.QuadPart <= 0) || (0 == QueryPerformanceCounter(&count)))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ct->tv_sec = count.QuadPart / g_counts_per_sec.QuadPart;
|
||||||
|
ct->tv_nsec = ((count.QuadPart % g_counts_per_sec.QuadPart) * BILLION) / g_counts_per_sec.QuadPart;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace chronos
|
namespace chronos
|
||||||
{
|
{
|
||||||
using clk = std::chrono::steady_clock;
|
using clk =
|
||||||
|
#ifndef WINDOWS
|
||||||
|
std::chrono::steady_clock;
|
||||||
|
#else
|
||||||
|
std::chrono::system_clock;
|
||||||
|
#endif
|
||||||
using time_point_clk = std::chrono::time_point<clk>;
|
using time_point_clk = std::chrono::time_point<clk>;
|
||||||
using sec = std::chrono::seconds;
|
using sec = std::chrono::seconds;
|
||||||
using msec = std::chrono::milliseconds;
|
using msec = std::chrono::milliseconds;
|
||||||
|
|
119
common/utils.hpp
119
common/utils.hpp
|
@ -32,19 +32,21 @@
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#ifndef WINDOWS
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#endif
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#ifndef FREEBSD
|
#if !defined(WINDOWS) && !defined(FREEBSD)
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
#endif
|
#endif
|
||||||
#include <sys/utsname.h>
|
|
||||||
#ifdef MACOS
|
#ifdef MACOS
|
||||||
#include <IOKit/IOCFPlugIn.h>
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
#include <IOKit/IOTypes.h>
|
#include <IOKit/IOTypes.h>
|
||||||
|
@ -54,12 +56,20 @@
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
#include <sys/system_properties.h>
|
#include <sys/system_properties.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WINDOWS
|
||||||
|
#include <chrono>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <direct.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
#include <versionhelpers.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace strutils = utils::string;
|
namespace strutils = utils::string;
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef WINDOWS
|
||||||
static std::string execGetOutput(const std::string& cmd)
|
static std::string execGetOutput(const std::string& cmd)
|
||||||
{
|
{
|
||||||
std::shared_ptr<FILE> pipe(popen((cmd + " 2> /dev/null").c_str(), "r"), pclose);
|
std::shared_ptr<FILE> pipe(popen((cmd + " 2> /dev/null").c_str(), "r"), pclose);
|
||||||
|
@ -74,6 +84,7 @@ static std::string execGetOutput(const std::string& cmd)
|
||||||
}
|
}
|
||||||
return strutils::trim(result);
|
return strutils::trim(result);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
|
@ -97,11 +108,40 @@ static std::string getOS()
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
os = strutils::trim_copy("Android " + getProp("ro.build.version.release"));
|
os = strutils::trim_copy("Android " + getProp("ro.build.version.release"));
|
||||||
|
#elif WINDOWS
|
||||||
|
if (/*IsWindows10OrGreater()*/ FALSE)
|
||||||
|
os = "Windows 10";
|
||||||
|
else if (IsWindows8Point1OrGreater())
|
||||||
|
os = "Windows 8.1";
|
||||||
|
else if (IsWindows8OrGreater())
|
||||||
|
os = "Windows 8";
|
||||||
|
else if (IsWindows7SP1OrGreater())
|
||||||
|
os = "Windows 7 SP1";
|
||||||
|
else if (IsWindows7OrGreater())
|
||||||
|
os = "Windows 7";
|
||||||
|
else if (IsWindowsVistaSP2OrGreater())
|
||||||
|
os = "Windows Vista SP2";
|
||||||
|
else if (IsWindowsVistaSP1OrGreater())
|
||||||
|
os = "Windows Vista SP1";
|
||||||
|
else if (IsWindowsVistaOrGreater())
|
||||||
|
os = "Windows Vista";
|
||||||
|
else if (IsWindowsXPSP3OrGreater())
|
||||||
|
os = "Windows XP SP3";
|
||||||
|
else if (IsWindowsXPSP2OrGreater())
|
||||||
|
os = "Windows XP SP2";
|
||||||
|
else if (IsWindowsXPSP1OrGreater())
|
||||||
|
os = "Windows XP SP1";
|
||||||
|
else if (IsWindowsXPOrGreater())
|
||||||
|
os = "Windows XP";
|
||||||
|
else
|
||||||
|
os = "Unknown Windows";
|
||||||
#else
|
#else
|
||||||
os = execGetOutput("lsb_release -d");
|
os = execGetOutput("lsb_release -d");
|
||||||
if ((os.find(":") != std::string::npos) && (os.find("lsb_release") == std::string::npos))
|
if ((os.find(":") != std::string::npos) && (os.find("lsb_release") == std::string::npos))
|
||||||
os = strutils::trim_copy(os.substr(os.find(":") + 1));
|
os = strutils::trim_copy(os.substr(os.find(":") + 1));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef WINDOWS
|
||||||
if (os.empty())
|
if (os.empty())
|
||||||
{
|
{
|
||||||
os = strutils::trim_copy(execGetOutput("grep /etc/os-release /etc/openwrt_release -e PRETTY_NAME -e DISTRIB_DESCRIPTION"));
|
os = strutils::trim_copy(execGetOutput("grep /etc/os-release /etc/openwrt_release -e PRETTY_NAME -e DISTRIB_DESCRIPTION"));
|
||||||
|
@ -118,6 +158,7 @@ static std::string getOS()
|
||||||
uname(&u);
|
uname(&u);
|
||||||
os = u.sysname;
|
os = u.sysname;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
strutils::trim(os);
|
strutils::trim(os);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
@ -148,17 +189,46 @@ static std::string getArch()
|
||||||
if (!arch.empty())
|
if (!arch.empty())
|
||||||
return arch;
|
return arch;
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef WINDOWS
|
||||||
arch = execGetOutput("arch");
|
arch = execGetOutput("arch");
|
||||||
if (arch.empty())
|
if (arch.empty())
|
||||||
arch = execGetOutput("uname -i");
|
arch = execGetOutput("uname -i");
|
||||||
if (arch.empty() || (arch == "unknown"))
|
if (arch.empty() || (arch == "unknown"))
|
||||||
arch = execGetOutput("uname -m");
|
arch = execGetOutput("uname -m");
|
||||||
|
#else
|
||||||
|
SYSTEM_INFO sysInfo;
|
||||||
|
GetSystemInfo(&sysInfo);
|
||||||
|
switch (sysInfo.wProcessorArchitecture)
|
||||||
|
{
|
||||||
|
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||||
|
arch = "amd64";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROCESSOR_ARCHITECTURE_ARM:
|
||||||
|
arch = "arm";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROCESSOR_ARCHITECTURE_IA64:
|
||||||
|
arch = "ia64";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||||
|
arch = "intel";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case PROCESSOR_ARCHITECTURE_UNKNOWN:
|
||||||
|
arch = "unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return strutils::trim_copy(arch);
|
return strutils::trim_copy(arch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static long uptime()
|
static long uptime()
|
||||||
{
|
{
|
||||||
|
#ifndef WINDOWS
|
||||||
#ifndef FREEBSD
|
#ifndef FREEBSD
|
||||||
struct sysinfo info;
|
struct sysinfo info;
|
||||||
sysinfo(&info);
|
sysinfo(&info);
|
||||||
|
@ -181,6 +251,9 @@ static long uptime()
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
|
#else
|
||||||
|
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::milliseconds(GetTickCount())).count();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,6 +274,7 @@ static std::string generateUUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef WINDOWS
|
||||||
/// https://gist.github.com/OrangeTide/909204
|
/// https://gist.github.com/OrangeTide/909204
|
||||||
static std::string getMacAddress(int sock)
|
static std::string getMacAddress(int sock)
|
||||||
{
|
{
|
||||||
|
@ -306,7 +380,42 @@ static std::string getMacAddress(int sock)
|
||||||
#endif
|
#endif
|
||||||
return mac;
|
return mac;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static std::string getMacAddress(const std::string& address)
|
||||||
|
{
|
||||||
|
IP_ADAPTER_INFO* first;
|
||||||
|
IP_ADAPTER_INFO* pos;
|
||||||
|
ULONG bufferLength = sizeof(IP_ADAPTER_INFO);
|
||||||
|
first = (IP_ADAPTER_INFO*)malloc(bufferLength);
|
||||||
|
|
||||||
|
if (GetAdaptersInfo(first, &bufferLength) == ERROR_BUFFER_OVERFLOW)
|
||||||
|
{
|
||||||
|
free(first);
|
||||||
|
first = (IP_ADAPTER_INFO*)malloc(bufferLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
char mac[19];
|
||||||
|
if (GetAdaptersInfo(first, &bufferLength) == NO_ERROR)
|
||||||
|
for (pos = first; pos != NULL; pos = pos->Next)
|
||||||
|
{
|
||||||
|
IP_ADDR_STRING* firstAddr = &pos->IpAddressList;
|
||||||
|
IP_ADDR_STRING* posAddr;
|
||||||
|
for (posAddr = firstAddr; posAddr != NULL; posAddr = posAddr->Next)
|
||||||
|
if (_stricmp(posAddr->IpAddress.String, address.c_str()) == 0)
|
||||||
|
{
|
||||||
|
sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", pos->Address[0], pos->Address[1], pos->Address[2], pos->Address[3], pos->Address[4],
|
||||||
|
pos->Address[5]);
|
||||||
|
|
||||||
|
free(first);
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
free(first);
|
||||||
|
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static std::string getHostId(const std::string defaultId = "")
|
static std::string getHostId(const std::string defaultId = "")
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,8 +21,10 @@
|
||||||
|
|
||||||
#include "string_utils.hpp"
|
#include "string_utils.hpp"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#ifndef WINDOWS
|
||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#endif
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -39,7 +41,7 @@ static bool exists(const std::string& filename)
|
||||||
return infile.good();
|
return infile.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef WINDOWS
|
||||||
static void do_chown(const std::string& file_path, const std::string& user_name, const std::string& group_name)
|
static void do_chown(const std::string& file_path, const std::string& user_name, const std::string& group_name)
|
||||||
{
|
{
|
||||||
if (user_name.empty() && group_name.empty())
|
if (user_name.empty() && group_name.empty())
|
||||||
|
@ -88,7 +90,7 @@ static int mkdirRecursive(const char* path, mode_t mode)
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} // namespace file
|
} // namespace file
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue