Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
459
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWASAPI.cpp
Executable file
459
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWASAPI.cpp
Executable file
|
|
@ -0,0 +1,459 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include "AudioInputWASAPI.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../VoIPController.h"
|
||||
|
||||
#define BUFFER_SIZE 960
|
||||
#define CHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); failed=true; return;}}
|
||||
#define SCHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); return;}}
|
||||
|
||||
template <class T> void SafeRelease(T **ppT)
|
||||
{
|
||||
if(*ppT)
|
||||
{
|
||||
(*ppT)->Release();
|
||||
*ppT = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
AudioInputWASAPI::AudioInputWASAPI(std::string deviceID){
|
||||
isRecording=false;
|
||||
remainingDataLen=0;
|
||||
refCount=1;
|
||||
HRESULT res;
|
||||
res=CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(FAILED(res) && res!=RPC_E_CHANGED_MODE){
|
||||
CHECK_RES(res, "CoInitializeEx");
|
||||
}
|
||||
#ifdef TGVOIP_WINXP_COMPAT
|
||||
HANDLE (WINAPI *__CreateEventExA)(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess);
|
||||
__CreateEventExA=(HANDLE (WINAPI *)(LPSECURITY_ATTRIBUTES, LPCSTR, DWORD, DWORD))GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateEventExA");
|
||||
#undef CreateEventEx
|
||||
#define CreateEventEx __CreateEventExA
|
||||
#endif
|
||||
shutdownEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
audioSamplesReadyEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
streamSwitchEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
ZeroMemory(&format, sizeof(format));
|
||||
format.wFormatTag=WAVE_FORMAT_PCM;
|
||||
format.nChannels=1;
|
||||
format.nSamplesPerSec=48000;
|
||||
format.nBlockAlign=2;
|
||||
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
|
||||
format.wBitsPerSample=16;
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
|
||||
CHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
|
||||
res=enumerator->RegisterEndpointNotificationCallback(this);
|
||||
CHECK_RES(res, "enumerator->RegisterEndpointNotificationCallback");
|
||||
audioSessionControl=NULL;
|
||||
device=NULL;
|
||||
#endif
|
||||
|
||||
audioClient=NULL;
|
||||
captureClient=NULL;
|
||||
thread=NULL;
|
||||
started=false;
|
||||
|
||||
SetCurrentDevice(deviceID);
|
||||
}
|
||||
|
||||
AudioInputWASAPI::~AudioInputWASAPI(){
|
||||
if(audioClient && started){
|
||||
audioClient->Stop();
|
||||
}
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
if(audioSessionControl){
|
||||
audioSessionControl->UnregisterAudioSessionNotification(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
SetEvent(shutdownEvent);
|
||||
if(thread){
|
||||
WaitForSingleObjectEx(thread, INFINITE, false);
|
||||
CloseHandle(thread);
|
||||
}
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
SafeRelease(&audioSessionControl);
|
||||
#endif
|
||||
SafeRelease(&captureClient);
|
||||
SafeRelease(&audioClient);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
SafeRelease(&device);
|
||||
#endif
|
||||
CloseHandle(shutdownEvent);
|
||||
CloseHandle(audioSamplesReadyEvent);
|
||||
CloseHandle(streamSwitchEvent);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
if(enumerator)
|
||||
enumerator->UnregisterEndpointNotificationCallback(this);
|
||||
SafeRelease(&enumerator);
|
||||
#endif
|
||||
}
|
||||
void AudioInputWASAPI::Start(){
|
||||
isRecording=true;
|
||||
if(!thread){
|
||||
thread=CreateThread(NULL, 0, AudioInputWASAPI::StartThread, this, 0, NULL);
|
||||
}
|
||||
|
||||
if(audioClient && !started){
|
||||
LOGI("audioClient->Start");
|
||||
audioClient->Start();
|
||||
started=true;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInputWASAPI::Stop(){
|
||||
isRecording=false;
|
||||
}
|
||||
|
||||
bool AudioInputWASAPI::IsRecording(){
|
||||
return isRecording;
|
||||
}
|
||||
|
||||
void AudioInputWASAPI::EnumerateDevices(std::vector<tgvoip::AudioInputDevice>& devs){
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
HRESULT res;
|
||||
res=CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(FAILED(res) && res!=RPC_E_CHANGED_MODE){
|
||||
SCHECK_RES(res, "CoInitializeEx");
|
||||
}
|
||||
|
||||
IMMDeviceEnumerator *deviceEnumerator = NULL;
|
||||
IMMDeviceCollection *deviceCollection = NULL;
|
||||
|
||||
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
|
||||
SCHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
|
||||
|
||||
res=deviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &deviceCollection);
|
||||
SCHECK_RES(res, "EnumAudioEndpoints");
|
||||
|
||||
UINT devCount;
|
||||
res=deviceCollection->GetCount(&devCount);
|
||||
SCHECK_RES(res, "GetCount");
|
||||
|
||||
for(UINT i=0;i<devCount;i++){
|
||||
IMMDevice* device;
|
||||
res=deviceCollection->Item(i, &device);
|
||||
SCHECK_RES(res, "GetDeviceItem");
|
||||
wchar_t* devID;
|
||||
res=device->GetId(&devID);
|
||||
SCHECK_RES(res, "get device id");
|
||||
|
||||
IPropertyStore* propStore;
|
||||
res=device->OpenPropertyStore(STGM_READ, &propStore);
|
||||
SafeRelease(&device);
|
||||
SCHECK_RES(res, "OpenPropertyStore");
|
||||
|
||||
PROPVARIANT friendlyName;
|
||||
PropVariantInit(&friendlyName);
|
||||
res=propStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
|
||||
SafeRelease(&propStore);
|
||||
|
||||
AudioInputDevice dev;
|
||||
|
||||
wchar_t actualFriendlyName[128];
|
||||
if(friendlyName.vt==VT_LPWSTR){
|
||||
wcsncpy(actualFriendlyName, friendlyName.pwszVal, sizeof(actualFriendlyName)/sizeof(wchar_t));
|
||||
}else{
|
||||
wcscpy(actualFriendlyName, L"Unknown");
|
||||
}
|
||||
PropVariantClear(&friendlyName);
|
||||
|
||||
char buf[256];
|
||||
WideCharToMultiByte(CP_UTF8, 0, devID, -1, buf, sizeof(buf), NULL, NULL);
|
||||
dev.id=buf;
|
||||
WideCharToMultiByte(CP_UTF8, 0, actualFriendlyName, -1, buf, sizeof(buf), NULL, NULL);
|
||||
dev.displayName=buf;
|
||||
devs.push_back(dev);
|
||||
|
||||
CoTaskMemFree(devID);
|
||||
}
|
||||
|
||||
SafeRelease(&deviceCollection);
|
||||
SafeRelease(&deviceEnumerator);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioInputWASAPI::SetCurrentDevice(std::string deviceID){
|
||||
if(thread){
|
||||
streamChangeToDevice=deviceID;
|
||||
SetEvent(streamSwitchEvent);
|
||||
}else{
|
||||
ActuallySetCurrentDevice(deviceID);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInputWASAPI::ActuallySetCurrentDevice(std::string deviceID){
|
||||
currentDevice=deviceID;
|
||||
HRESULT res;
|
||||
|
||||
if(audioClient){
|
||||
res=audioClient->Stop();
|
||||
CHECK_RES(res, "audioClient->Stop");
|
||||
}
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
if(audioSessionControl){
|
||||
res=audioSessionControl->UnregisterAudioSessionNotification(this);
|
||||
CHECK_RES(res, "audioSessionControl->UnregisterAudioSessionNotification");
|
||||
}
|
||||
|
||||
SafeRelease(&audioSessionControl);
|
||||
#endif
|
||||
SafeRelease(&captureClient);
|
||||
SafeRelease(&audioClient);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
SafeRelease(&device);
|
||||
|
||||
IMMDeviceCollection *deviceCollection = NULL;
|
||||
|
||||
if(deviceID=="default"){
|
||||
isDefaultDevice=true;
|
||||
res=enumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &device);
|
||||
CHECK_RES(res, "GetDefaultAudioEndpoint");
|
||||
}else{
|
||||
isDefaultDevice=false;
|
||||
res=enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &deviceCollection);
|
||||
CHECK_RES(res, "EnumAudioEndpoints");
|
||||
|
||||
UINT devCount;
|
||||
res=deviceCollection->GetCount(&devCount);
|
||||
CHECK_RES(res, "GetCount");
|
||||
|
||||
for(UINT i=0;i<devCount;i++){
|
||||
IMMDevice* device;
|
||||
res=deviceCollection->Item(i, &device);
|
||||
CHECK_RES(res, "GetDeviceItem");
|
||||
wchar_t* _devID;
|
||||
res=device->GetId(&_devID);
|
||||
CHECK_RES(res, "get device id");
|
||||
|
||||
char devID[128];
|
||||
WideCharToMultiByte(CP_UTF8, 0, _devID, -1, devID, 128, NULL, NULL);
|
||||
|
||||
CoTaskMemFree(_devID);
|
||||
if(deviceID==devID){
|
||||
this->device=device;
|
||||
//device->AddRef();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(deviceCollection)
|
||||
SafeRelease(&deviceCollection);
|
||||
|
||||
if(!device){
|
||||
LOGE("Didn't find capture device; failing");
|
||||
failed=true;
|
||||
return;
|
||||
}
|
||||
|
||||
res=device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&audioClient);
|
||||
CHECK_RES(res, "device->Activate");
|
||||
#else
|
||||
std::wstring devID;
|
||||
|
||||
if (deviceID=="default"){
|
||||
Platform::String^ defaultDevID=Windows::Media::Devices::MediaDevice::GetDefaultAudioCaptureId(Windows::Media::Devices::AudioDeviceRole::Communications);
|
||||
if(defaultDevID==nullptr){
|
||||
LOGE("Didn't find capture device; failing");
|
||||
failed=true;
|
||||
return;
|
||||
}else{
|
||||
isDefaultDevice=true;
|
||||
devID=defaultDevID->Data();
|
||||
}
|
||||
}else{
|
||||
int wchars_num=MultiByteToWideChar(CP_UTF8, 0, deviceID.c_str(), -1, NULL, 0);
|
||||
wchar_t* wstr=new wchar_t[wchars_num];
|
||||
MultiByteToWideChar(CP_UTF8, 0, deviceID.c_str(), -1, wstr, wchars_num);
|
||||
devID=wstr;
|
||||
}
|
||||
|
||||
HRESULT res1, res2;
|
||||
IAudioClient2* audioClient2=WindowsSandboxUtils::ActivateAudioDevice(devID.c_str(), &res1, &res2);
|
||||
CHECK_RES(res1, "activate1");
|
||||
CHECK_RES(res2, "activate2");
|
||||
|
||||
AudioClientProperties properties={};
|
||||
properties.cbSize=sizeof AudioClientProperties;
|
||||
properties.eCategory=AudioCategory_Communications;
|
||||
res = audioClient2->SetClientProperties(&properties);
|
||||
CHECK_RES(res, "audioClient2->SetClientProperties");
|
||||
|
||||
audioClient=audioClient2;
|
||||
#endif
|
||||
|
||||
// {2C693079-3F59-49FD-964F-61C005EAA5D3}
|
||||
const GUID guid = { 0x2c693079, 0x3f59, 0x49fd, { 0x96, 0x4f, 0x61, 0xc0, 0x5, 0xea, 0xa5, 0xd3 } };
|
||||
// Use 1000ms buffer to avoid resampling glitches on Windows 8.1 and older. This should not increase latency.
|
||||
res = audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, 1000*10000, 0, &format, &guid);
|
||||
CHECK_RES(res, "audioClient->Initialize");
|
||||
|
||||
uint32_t bufSize;
|
||||
res = audioClient->GetBufferSize(&bufSize);
|
||||
CHECK_RES(res, "audioClient->GetBufferSize");
|
||||
|
||||
LOGV("buffer size: %u", bufSize);
|
||||
estimatedDelay=0;
|
||||
REFERENCE_TIME latency, devicePeriod;
|
||||
if(SUCCEEDED(audioClient->GetStreamLatency(&latency))){
|
||||
if(SUCCEEDED(audioClient->GetDevicePeriod(&devicePeriod, NULL))){
|
||||
estimatedDelay=(int32_t)(latency/10000+devicePeriod/10000);
|
||||
}
|
||||
}
|
||||
|
||||
res = audioClient->SetEventHandle(audioSamplesReadyEvent);
|
||||
CHECK_RES(res, "audioClient->SetEventHandle");
|
||||
|
||||
res = audioClient->GetService(IID_PPV_ARGS(&captureClient));
|
||||
CHECK_RES(res, "audioClient->GetService");
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
res=audioClient->GetService(IID_PPV_ARGS(&audioSessionControl));
|
||||
CHECK_RES(res, "audioClient->GetService(IAudioSessionControl)");
|
||||
|
||||
res=audioSessionControl->RegisterAudioSessionNotification(this);
|
||||
CHECK_RES(res, "audioSessionControl->RegisterAudioSessionNotification");
|
||||
#endif
|
||||
|
||||
if(isRecording)
|
||||
audioClient->Start();
|
||||
|
||||
LOGV("set current input device done");
|
||||
}
|
||||
|
||||
DWORD WINAPI AudioInputWASAPI::StartThread(void* arg) {
|
||||
LOGV("WASAPI capture thread starting");
|
||||
((AudioInputWASAPI*)arg)->RunThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AudioInputWASAPI::RunThread() {
|
||||
if(failed)
|
||||
return;
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||
|
||||
HANDLE waitArray[]={shutdownEvent, streamSwitchEvent, audioSamplesReadyEvent};
|
||||
HRESULT res=CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
CHECK_RES(res, "CoInitializeEx in capture thread");
|
||||
|
||||
uint32_t bufferSize=0;
|
||||
uint64_t framesWritten=0;
|
||||
|
||||
bool running=true;
|
||||
//double prevCallback=VoIPController::GetCurrentTime();
|
||||
|
||||
while(running){
|
||||
DWORD waitResult=WaitForMultipleObjectsEx(3, waitArray, false, INFINITE, false);
|
||||
if(waitResult==WAIT_OBJECT_0){ // shutdownEvent
|
||||
LOGV("capture thread shutting down");
|
||||
running=false;
|
||||
}else if(waitResult==WAIT_OBJECT_0+1){ // streamSwitchEvent
|
||||
LOGV("stream switch");
|
||||
ActuallySetCurrentDevice(streamChangeToDevice);
|
||||
ResetEvent(streamSwitchEvent);
|
||||
bufferSize=0;
|
||||
LOGV("stream switch done");
|
||||
}else if(waitResult==WAIT_OBJECT_0+2){ // audioSamplesReadyEvent
|
||||
if(!audioClient)
|
||||
continue;
|
||||
res=captureClient->GetNextPacketSize(&bufferSize);
|
||||
CHECK_RES(res, "captureClient->GetNextPacketSize");
|
||||
BYTE* data;
|
||||
uint32_t framesAvailable=0;
|
||||
DWORD flags;
|
||||
|
||||
res=captureClient->GetBuffer(&data, &framesAvailable, &flags, NULL, NULL);
|
||||
CHECK_RES(res, "captureClient->GetBuffer");
|
||||
size_t dataLen=framesAvailable*2;
|
||||
assert(remainingDataLen+dataLen<sizeof(remainingData));
|
||||
|
||||
if(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY){
|
||||
LOGW("Audio capture data discontinuity");
|
||||
}
|
||||
|
||||
//double t=VoIPController::GetCurrentTime();
|
||||
//LOGV("audio capture: %u, time %f, flags %u", framesAvailable, t-prevCallback, flags);
|
||||
//prevCallback=t;
|
||||
|
||||
memcpy(remainingData+remainingDataLen, data, dataLen);
|
||||
remainingDataLen+=dataLen;
|
||||
while(remainingDataLen>960*2){
|
||||
if(isRecording)
|
||||
InvokeCallback(remainingData, 960*2);
|
||||
|
||||
//LOGV("remaining data len %u", remainingDataLen);
|
||||
memmove(remainingData, remainingData+(960*2), remainingDataLen-960*2);
|
||||
remainingDataLen-=960*2;
|
||||
}
|
||||
|
||||
res=captureClient->ReleaseBuffer(framesAvailable);
|
||||
CHECK_RES(res, "captureClient->ReleaseBuffer");
|
||||
//estimatedDelay=(int32_t)((devicePosition-framesWritten)/48);
|
||||
|
||||
framesWritten+=framesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
HRESULT AudioInputWASAPI::OnSessionDisconnected(AudioSessionDisconnectReason reason) {
|
||||
if(!isDefaultDevice){
|
||||
streamChangeToDevice="default";
|
||||
SetEvent(streamSwitchEvent);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AudioInputWASAPI::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR newDevID) {
|
||||
if(flow==eCapture && role==eCommunications && isDefaultDevice){
|
||||
streamChangeToDevice="default";
|
||||
SetEvent(streamSwitchEvent);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG AudioInputWASAPI::AddRef(){
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
ULONG AudioInputWASAPI::Release(){
|
||||
return InterlockedDecrement(&refCount);
|
||||
}
|
||||
|
||||
HRESULT AudioInputWASAPI::QueryInterface(REFIID iid, void** obj){
|
||||
if(!obj){
|
||||
return E_POINTER;
|
||||
}
|
||||
*obj=NULL;
|
||||
|
||||
if(iid==IID_IUnknown){
|
||||
*obj=static_cast<IUnknown*>(static_cast<IAudioSessionEvents*>(this));
|
||||
AddRef();
|
||||
}else if(iid==__uuidof(IMMNotificationClient)){
|
||||
*obj=static_cast<IMMNotificationClient*>(this);
|
||||
AddRef();
|
||||
}else if(iid==__uuidof(IAudioSessionEvents)){
|
||||
*obj=static_cast<IAudioSessionEvents*>(this);
|
||||
AddRef();
|
||||
}else{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
||||
105
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWASAPI.h
Executable file
105
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWASAPI.h
Executable file
|
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOINPUTWASAPI_H
|
||||
#define LIBTGVOIP_AUDIOINPUTWASAPI_H
|
||||
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
#define TGVOIP_WINDOWS_PHONE
|
||||
#endif
|
||||
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP
|
||||
#define TGVOIP_WINDOWS_DESKTOP
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201)
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
#include <mmdeviceapi.h>
|
||||
#endif
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
#include <audiopolicy.h>
|
||||
#include <functiondiscoverykeys.h>
|
||||
#else
|
||||
#include <audioclient.h>
|
||||
#include "WindowsSandboxUtils.h"
|
||||
#endif
|
||||
#pragma warning(pop)
|
||||
#include "../../audio/AudioInput.h"
|
||||
|
||||
namespace tgvoip{
|
||||
namespace audio{
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
class AudioInputWASAPI : public AudioInput, IMMNotificationClient, IAudioSessionEvents{
|
||||
#else
|
||||
class AudioInputWASAPI : public AudioInput{
|
||||
#endif
|
||||
|
||||
public:
|
||||
AudioInputWASAPI(std::string deviceID);
|
||||
virtual ~AudioInputWASAPI();
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
virtual bool IsRecording();
|
||||
virtual void SetCurrentDevice(std::string deviceID);
|
||||
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
STDMETHOD_(ULONG, AddRef)();
|
||||
STDMETHOD_(ULONG, Release)();
|
||||
#endif
|
||||
|
||||
private:
|
||||
void ActuallySetCurrentDevice(std::string deviceID);
|
||||
static DWORD WINAPI StartThread(void* arg);
|
||||
void RunThread();
|
||||
WAVEFORMATEX format;
|
||||
bool isRecording;
|
||||
HANDLE shutdownEvent;
|
||||
HANDLE audioSamplesReadyEvent;
|
||||
HANDLE streamSwitchEvent;
|
||||
HANDLE thread;
|
||||
IAudioClient* audioClient=NULL;
|
||||
IAudioCaptureClient* captureClient=NULL;
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
IMMDeviceEnumerator* enumerator;
|
||||
IAudioSessionControl* audioSessionControl;
|
||||
IMMDevice* device;
|
||||
#endif
|
||||
unsigned char remainingData[10240];
|
||||
size_t remainingDataLen;
|
||||
bool isDefaultDevice;
|
||||
ULONG refCount;
|
||||
std::string streamChangeToDevice;
|
||||
bool started;
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
STDMETHOD(OnDisplayNameChanged) (LPCWSTR /*NewDisplayName*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnIconPathChanged) (LPCWSTR /*NewIconPath*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnSimpleVolumeChanged) (float /*NewSimpleVolume*/, BOOL /*NewMute*/, LPCGUID /*EventContext*/) { return S_OK; }
|
||||
STDMETHOD(OnChannelVolumeChanged) (DWORD /*ChannelCount*/, float /*NewChannelVolumes*/[], DWORD /*ChangedChannel*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnGroupingParamChanged) (LPCGUID /*NewGroupingParam*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnStateChanged) (AudioSessionState /*NewState*/) { return S_OK; };
|
||||
STDMETHOD(OnSessionDisconnected) (AudioSessionDisconnectReason DisconnectReason);
|
||||
STDMETHOD(OnDeviceStateChanged) (LPCWSTR /*DeviceId*/, DWORD /*NewState*/) { return S_OK; }
|
||||
STDMETHOD(OnDeviceAdded) (LPCWSTR /*DeviceId*/) { return S_OK; };
|
||||
STDMETHOD(OnDeviceRemoved) (LPCWSTR /*DeviceId(*/) { return S_OK; };
|
||||
STDMETHOD(OnDefaultDeviceChanged) (EDataFlow Flow, ERole Role, LPCWSTR NewDefaultDeviceId);
|
||||
STDMETHOD(OnPropertyValueChanged) (LPCWSTR /*DeviceId*/, const PROPERTYKEY /*Key*/) { return S_OK; };
|
||||
|
||||
//
|
||||
// IUnknown
|
||||
//
|
||||
STDMETHOD(QueryInterface)(REFIID iid, void **pvObject);
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOINPUTWASAPI_H
|
||||
170
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWave.cpp
Executable file
170
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWave.cpp
Executable file
|
|
@ -0,0 +1,170 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include "AudioInputWave.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../VoIPController.h"
|
||||
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
#define BUFFER_SIZE 960
|
||||
#define CHECK_ERROR(res, msg) if(res!=MMSYSERR_NOERROR){wchar_t _buf[1024]; waveInGetErrorTextW(res, _buf, 1024); LOGE(msg ": %ws (MMRESULT=0x%08X)", _buf, res); failed=true;}
|
||||
|
||||
AudioInputWave::AudioInputWave(std::string deviceID){
|
||||
isRecording=false;
|
||||
|
||||
for(int i=0;i<4;i++){
|
||||
ZeroMemory(&buffers[i], sizeof(WAVEHDR));
|
||||
buffers[i].dwBufferLength=960*2;
|
||||
buffers[i].lpData=(char*)malloc(960*2);
|
||||
}
|
||||
|
||||
hWaveIn=NULL;
|
||||
|
||||
SetCurrentDevice(deviceID);
|
||||
}
|
||||
|
||||
AudioInputWave::~AudioInputWave(){
|
||||
for(int i=0;i<4;i++){
|
||||
free(buffers[i].lpData);
|
||||
}
|
||||
waveInClose(hWaveIn);
|
||||
}
|
||||
|
||||
void AudioInputWave::Start(){
|
||||
if(!isRecording){
|
||||
isRecording=true;
|
||||
|
||||
MMRESULT res;
|
||||
for(int i=0;i<4;i++){
|
||||
res=waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveInPrepareHeader failed");
|
||||
res=waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveInAddBuffer failed");
|
||||
}
|
||||
res=waveInStart(hWaveIn);
|
||||
CHECK_ERROR(res, "waveInStart failed");
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInputWave::Stop(){
|
||||
if(isRecording){
|
||||
isRecording=false;
|
||||
|
||||
MMRESULT res=waveInStop(hWaveIn);
|
||||
CHECK_ERROR(res, "waveInStop failed");
|
||||
res=waveInReset(hWaveIn);
|
||||
CHECK_ERROR(res, "waveInReset failed");
|
||||
for(int i=0;i<4;i++){
|
||||
res=waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveInUnprepareHeader failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CALLBACK AudioInputWave::WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2){
|
||||
if(uMsg==WIM_DATA){
|
||||
((AudioInputWave*)dwInstance)->OnData((WAVEHDR*)dwParam1);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInputWave::OnData(WAVEHDR* hdr){
|
||||
if(!isRecording)
|
||||
return;
|
||||
|
||||
InvokeCallback((unsigned char*)hdr->lpData, hdr->dwBufferLength);
|
||||
hdr->dwFlags&= ~WHDR_DONE;
|
||||
MMRESULT res=waveInAddBuffer(hWaveIn, hdr, sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveInAddBuffer failed");
|
||||
}
|
||||
|
||||
void AudioInputWave::EnumerateDevices(std::vector<tgvoip::AudioInputDevice>& devs){
|
||||
UINT num=waveInGetNumDevs();
|
||||
WAVEINCAPSW caps;
|
||||
char nameBuf[512];
|
||||
for(UINT i=0;i<num;i++){
|
||||
waveInGetDevCapsW(i, &caps, sizeof(caps));
|
||||
AudioInputDevice dev;
|
||||
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
|
||||
dev.displayName=std::string(nameBuf);
|
||||
dev.id=std::string(nameBuf);
|
||||
devs.push_back(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInputWave::SetCurrentDevice(std::string deviceID){
|
||||
currentDevice=deviceID;
|
||||
|
||||
bool wasRecording=isRecording;
|
||||
isRecording=false;
|
||||
if(hWaveIn){
|
||||
MMRESULT res;
|
||||
if(isRecording){
|
||||
res=waveInStop(hWaveIn);
|
||||
CHECK_ERROR(res, "waveInStop failed");
|
||||
res=waveInReset(hWaveIn);
|
||||
CHECK_ERROR(res, "waveInReset failed");
|
||||
for(int i=0;i<4;i++){
|
||||
res=waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveInUnprepareHeader failed");
|
||||
}
|
||||
}
|
||||
res=waveInClose(hWaveIn);
|
||||
CHECK_ERROR(res, "waveInClose failed");
|
||||
}
|
||||
|
||||
ZeroMemory(&format, sizeof(format));
|
||||
format.cbSize=0;
|
||||
format.wFormatTag=WAVE_FORMAT_PCM;
|
||||
format.nSamplesPerSec=48000;
|
||||
format.wBitsPerSample=16;
|
||||
format.nChannels=1;
|
||||
format.nBlockAlign=2;
|
||||
|
||||
LOGV("before open device %s", deviceID.c_str());
|
||||
|
||||
if(deviceID=="default"){
|
||||
MMRESULT res=waveInOpen(&hWaveIn, WAVE_MAPPER, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
CHECK_ERROR(res, "waveInOpen failed");
|
||||
}else{
|
||||
UINT num=waveInGetNumDevs();
|
||||
WAVEINCAPSW caps;
|
||||
char nameBuf[512];
|
||||
hWaveIn=NULL;
|
||||
for(UINT i=0;i<num;i++){
|
||||
waveInGetDevCapsW(i, &caps, sizeof(caps));
|
||||
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
|
||||
std::string name=std::string(nameBuf);
|
||||
if(name==deviceID){
|
||||
MMRESULT res=waveInOpen(&hWaveIn, i, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION | WAVE_MAPPED);
|
||||
CHECK_ERROR(res, "waveInOpen failed");
|
||||
LOGD("Opened device %s", nameBuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hWaveIn){
|
||||
SetCurrentDevice("default");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isRecording=wasRecording;
|
||||
|
||||
if(isRecording){
|
||||
MMRESULT res;
|
||||
for(int i=0;i<4;i++){
|
||||
res=waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveInPrepareHeader failed");
|
||||
res=waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveInAddBuffer failed");
|
||||
}
|
||||
res=waveInStart(hWaveIn);
|
||||
CHECK_ERROR(res, "waveInStart failed");
|
||||
}
|
||||
}
|
||||
40
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWave.h
Normal file
40
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioInputWave.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOINPUTWAVE_H
|
||||
#define LIBTGVOIP_AUDIOINPUTWAVE_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../../audio/AudioInput.h"
|
||||
|
||||
namespace tgvoip{
|
||||
namespace audio{
|
||||
|
||||
class AudioInputWave : public AudioInput{
|
||||
|
||||
public:
|
||||
AudioInputWave(std::string deviceID);
|
||||
virtual ~AudioInputWave();
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
virtual void SetCurrentDevice(std::string deviceID);
|
||||
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
|
||||
|
||||
private:
|
||||
static void CALLBACK WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
|
||||
void OnData(WAVEHDR* hdr);
|
||||
HWAVEIN hWaveIn;
|
||||
WAVEFORMATEX format;
|
||||
WAVEHDR buffers[4];
|
||||
bool isRecording;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOINPUTWAVE_H
|
||||
455
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioOutputWASAPI.cpp
Executable file
455
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioOutputWASAPI.cpp
Executable file
|
|
@ -0,0 +1,455 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include "AudioOutputWASAPI.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../VoIPController.h"
|
||||
|
||||
#define BUFFER_SIZE 960
|
||||
#define CHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); failed=true; return;}}
|
||||
#define SCHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); return;}}
|
||||
|
||||
template <class T> void SafeRelease(T **ppT)
|
||||
{
|
||||
if(*ppT)
|
||||
{
|
||||
(*ppT)->Release();
|
||||
*ppT = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TGVOIP_WINXP_COMPAT
|
||||
|
||||
#endif
|
||||
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
AudioOutputWASAPI::AudioOutputWASAPI(std::string deviceID){
|
||||
isPlaying=false;
|
||||
remainingDataLen=0;
|
||||
refCount=1;
|
||||
HRESULT res;
|
||||
res=CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(FAILED(res) && res!=RPC_E_CHANGED_MODE){
|
||||
CHECK_RES(res, "CoInitializeEx");
|
||||
}
|
||||
#ifdef TGVOIP_WINXP_COMPAT
|
||||
HANDLE (WINAPI *__CreateEventExA)(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess);
|
||||
__CreateEventExA=(HANDLE (WINAPI *)(LPSECURITY_ATTRIBUTES, LPCSTR, DWORD, DWORD))GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateEventExA");
|
||||
#undef CreateEventEx
|
||||
#define CreateEventEx __CreateEventExA
|
||||
#endif
|
||||
shutdownEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
audioSamplesReadyEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
streamSwitchEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
ZeroMemory(&format, sizeof(format));
|
||||
format.wFormatTag=WAVE_FORMAT_PCM;
|
||||
format.nChannels=1;
|
||||
format.nSamplesPerSec=48000;
|
||||
format.nBlockAlign=2;
|
||||
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
|
||||
format.wBitsPerSample=16;
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
|
||||
CHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
|
||||
res=enumerator->RegisterEndpointNotificationCallback(this);
|
||||
CHECK_RES(res, "enumerator->RegisterEndpointNotificationCallback");
|
||||
audioSessionControl=NULL;
|
||||
device=NULL;
|
||||
#endif
|
||||
|
||||
audioClient=NULL;
|
||||
renderClient=NULL;
|
||||
thread=NULL;
|
||||
|
||||
SetCurrentDevice(deviceID);
|
||||
}
|
||||
|
||||
AudioOutputWASAPI::~AudioOutputWASAPI(){
|
||||
if(audioClient){
|
||||
audioClient->Stop();
|
||||
}
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
if(audioSessionControl){
|
||||
audioSessionControl->UnregisterAudioSessionNotification(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
SetEvent(shutdownEvent);
|
||||
if(thread){
|
||||
WaitForSingleObjectEx(thread, INFINITE, false);
|
||||
CloseHandle(thread);
|
||||
}
|
||||
SafeRelease(&renderClient);
|
||||
SafeRelease(&audioClient);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
SafeRelease(&device);
|
||||
SafeRelease(&audioSessionControl);
|
||||
#endif
|
||||
CloseHandle(shutdownEvent);
|
||||
CloseHandle(audioSamplesReadyEvent);
|
||||
CloseHandle(streamSwitchEvent);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
if(enumerator)
|
||||
enumerator->UnregisterEndpointNotificationCallback(this);
|
||||
SafeRelease(&enumerator);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioOutputWASAPI::Start(){
|
||||
isPlaying=true;
|
||||
if(!thread){
|
||||
thread=CreateThread(NULL, 0, AudioOutputWASAPI::StartThread, this, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputWASAPI::Stop(){
|
||||
isPlaying=false;
|
||||
}
|
||||
|
||||
bool AudioOutputWASAPI::IsPlaying(){
|
||||
return isPlaying;
|
||||
}
|
||||
|
||||
void AudioOutputWASAPI::EnumerateDevices(std::vector<tgvoip::AudioOutputDevice>& devs){
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
HRESULT res;
|
||||
res=CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if(FAILED(res) && res!=RPC_E_CHANGED_MODE){
|
||||
SCHECK_RES(res, "CoInitializeEx");
|
||||
}
|
||||
|
||||
IMMDeviceEnumerator *deviceEnumerator = NULL;
|
||||
IMMDeviceCollection *deviceCollection = NULL;
|
||||
|
||||
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
|
||||
SCHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
|
||||
|
||||
res=deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection);
|
||||
SCHECK_RES(res, "EnumAudioEndpoints");
|
||||
|
||||
UINT devCount;
|
||||
res=deviceCollection->GetCount(&devCount);
|
||||
SCHECK_RES(res, "GetCount");
|
||||
|
||||
for(UINT i=0;i<devCount;i++){
|
||||
IMMDevice* device;
|
||||
res=deviceCollection->Item(i, &device);
|
||||
SCHECK_RES(res, "GetDeviceItem");
|
||||
wchar_t* devID;
|
||||
res=device->GetId(&devID);
|
||||
SCHECK_RES(res, "get device id");
|
||||
|
||||
IPropertyStore* propStore;
|
||||
res=device->OpenPropertyStore(STGM_READ, &propStore);
|
||||
SafeRelease(&device);
|
||||
SCHECK_RES(res, "OpenPropertyStore");
|
||||
|
||||
PROPVARIANT friendlyName;
|
||||
PropVariantInit(&friendlyName);
|
||||
res=propStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
|
||||
SafeRelease(&propStore);
|
||||
|
||||
AudioOutputDevice dev;
|
||||
|
||||
wchar_t actualFriendlyName[128];
|
||||
if(friendlyName.vt==VT_LPWSTR){
|
||||
wcsncpy(actualFriendlyName, friendlyName.pwszVal, sizeof(actualFriendlyName)/sizeof(wchar_t));
|
||||
}else{
|
||||
wcscpy(actualFriendlyName, L"Unknown");
|
||||
}
|
||||
PropVariantClear(&friendlyName);
|
||||
|
||||
char buf[256];
|
||||
WideCharToMultiByte(CP_UTF8, 0, devID, -1, buf, sizeof(buf), NULL, NULL);
|
||||
dev.id=buf;
|
||||
WideCharToMultiByte(CP_UTF8, 0, actualFriendlyName, -1, buf, sizeof(buf), NULL, NULL);
|
||||
dev.displayName=buf;
|
||||
devs.push_back(dev);
|
||||
|
||||
CoTaskMemFree(devID);
|
||||
}
|
||||
|
||||
SafeRelease(&deviceCollection);
|
||||
SafeRelease(&deviceEnumerator);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioOutputWASAPI::SetCurrentDevice(std::string deviceID){
|
||||
if(thread){
|
||||
streamChangeToDevice=deviceID;
|
||||
SetEvent(streamSwitchEvent);
|
||||
}else{
|
||||
ActuallySetCurrentDevice(deviceID);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputWASAPI::ActuallySetCurrentDevice(std::string deviceID){
|
||||
currentDevice=deviceID;
|
||||
HRESULT res;
|
||||
|
||||
if(audioClient){
|
||||
res=audioClient->Stop();
|
||||
CHECK_RES(res, "audioClient->Stop");
|
||||
}
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
if(audioSessionControl){
|
||||
res=audioSessionControl->UnregisterAudioSessionNotification(this);
|
||||
CHECK_RES(res, "audioSessionControl->UnregisterAudioSessionNotification");
|
||||
}
|
||||
|
||||
SafeRelease(&audioSessionControl);
|
||||
#endif
|
||||
SafeRelease(&renderClient);
|
||||
SafeRelease(&audioClient);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
SafeRelease(&device);
|
||||
|
||||
|
||||
IMMDeviceCollection *deviceCollection = NULL;
|
||||
|
||||
if(deviceID=="default"){
|
||||
isDefaultDevice=true;
|
||||
res=enumerator->GetDefaultAudioEndpoint(eRender, eCommunications, &device);
|
||||
CHECK_RES(res, "GetDefaultAudioEndpoint");
|
||||
}else{
|
||||
isDefaultDevice=false;
|
||||
res=enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection);
|
||||
CHECK_RES(res, "EnumAudioEndpoints");
|
||||
|
||||
UINT devCount;
|
||||
res=deviceCollection->GetCount(&devCount);
|
||||
CHECK_RES(res, "GetCount");
|
||||
|
||||
for(UINT i=0;i<devCount;i++){
|
||||
IMMDevice* device;
|
||||
res=deviceCollection->Item(i, &device);
|
||||
CHECK_RES(res, "GetDeviceItem");
|
||||
wchar_t* _devID;
|
||||
res=device->GetId(&_devID);
|
||||
CHECK_RES(res, "get device id");
|
||||
|
||||
char devID[128];
|
||||
WideCharToMultiByte(CP_UTF8, 0, _devID, -1, devID, 128, NULL, NULL);
|
||||
|
||||
CoTaskMemFree(_devID);
|
||||
if(deviceID==devID){
|
||||
this->device=device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(deviceCollection)
|
||||
SafeRelease(&deviceCollection);
|
||||
|
||||
if(!device){
|
||||
LOGE("Didn't find playback device; failing");
|
||||
failed=true;
|
||||
return;
|
||||
}
|
||||
|
||||
res=device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&audioClient);
|
||||
CHECK_RES(res, "device->Activate");
|
||||
#else
|
||||
std::wstring devID;
|
||||
|
||||
if (deviceID=="default"){
|
||||
Platform::String^ defaultDevID=Windows::Media::Devices::MediaDevice::GetDefaultAudioRenderId(Windows::Media::Devices::AudioDeviceRole::Communications);
|
||||
if(defaultDevID==nullptr){
|
||||
LOGE("Didn't find playback device; failing");
|
||||
failed=true;
|
||||
return;
|
||||
}else{
|
||||
isDefaultDevice=true;
|
||||
devID=defaultDevID->Data();
|
||||
}
|
||||
}else{
|
||||
int wchars_num=MultiByteToWideChar(CP_UTF8, 0, deviceID.c_str(), -1, NULL, 0);
|
||||
wchar_t* wstr=new wchar_t[wchars_num];
|
||||
MultiByteToWideChar(CP_UTF8, 0, deviceID.c_str(), -1, wstr, wchars_num);
|
||||
devID=wstr;
|
||||
}
|
||||
|
||||
HRESULT res1, res2;
|
||||
IAudioClient2* audioClient2=WindowsSandboxUtils::ActivateAudioDevice(devID.c_str(), &res1, &res2);
|
||||
CHECK_RES(res1, "activate1");
|
||||
CHECK_RES(res2, "activate2");
|
||||
|
||||
AudioClientProperties properties={};
|
||||
properties.cbSize=sizeof AudioClientProperties;
|
||||
properties.eCategory=AudioCategory_Communications;
|
||||
res = audioClient2->SetClientProperties(&properties);
|
||||
CHECK_RES(res, "audioClient2->SetClientProperties");
|
||||
|
||||
audioClient = audioClient2;
|
||||
#endif
|
||||
|
||||
// {2C693079-3F59-49FD-964F-61C005EAA5D3}
|
||||
const GUID guid = { 0x2c693079, 0x3f59, 0x49fd, { 0x96, 0x4f, 0x61, 0xc0, 0x5, 0xea, 0xa5, 0xd3 } };
|
||||
res = audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, 60 * 10000, 0, &format, &guid);
|
||||
CHECK_RES(res, "audioClient->Initialize");
|
||||
|
||||
uint32_t bufSize;
|
||||
res = audioClient->GetBufferSize(&bufSize);
|
||||
CHECK_RES(res, "audioClient->GetBufferSize");
|
||||
|
||||
LOGV("buffer size: %u", bufSize);
|
||||
estimatedDelay=0;
|
||||
REFERENCE_TIME latency, devicePeriod;
|
||||
if(SUCCEEDED(audioClient->GetStreamLatency(&latency))){
|
||||
if(SUCCEEDED(audioClient->GetDevicePeriod(&devicePeriod, NULL))){
|
||||
estimatedDelay=(int32_t)(latency/10000+devicePeriod/10000);
|
||||
}
|
||||
}
|
||||
|
||||
res = audioClient->SetEventHandle(audioSamplesReadyEvent);
|
||||
CHECK_RES(res, "audioClient->SetEventHandle");
|
||||
|
||||
res = audioClient->GetService(IID_PPV_ARGS(&renderClient));
|
||||
CHECK_RES(res, "audioClient->GetService");
|
||||
|
||||
BYTE* data;
|
||||
res = renderClient->GetBuffer(bufSize, &data);
|
||||
CHECK_RES(res, "renderClient->GetBuffer");
|
||||
|
||||
res = renderClient->ReleaseBuffer(bufSize, AUDCLNT_BUFFERFLAGS_SILENT);
|
||||
CHECK_RES(res, "renderClient->ReleaseBuffer");
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
res=audioClient->GetService(IID_PPV_ARGS(&audioSessionControl));
|
||||
CHECK_RES(res, "audioClient->GetService(IAudioSessionControl)");
|
||||
|
||||
res=audioSessionControl->RegisterAudioSessionNotification(this);
|
||||
CHECK_RES(res, "audioSessionControl->RegisterAudioSessionNotification");
|
||||
#endif
|
||||
|
||||
audioClient->Start();
|
||||
|
||||
LOGV("set current output device done");
|
||||
}
|
||||
|
||||
DWORD WINAPI AudioOutputWASAPI::StartThread(void* arg) {
|
||||
((AudioOutputWASAPI*)arg)->RunThread();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AudioOutputWASAPI::RunThread() {
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
||||
|
||||
HANDLE waitArray[]={shutdownEvent, streamSwitchEvent, audioSamplesReadyEvent};
|
||||
HRESULT res=CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
CHECK_RES(res, "CoInitializeEx in render thread");
|
||||
|
||||
uint32_t bufferSize;
|
||||
res=audioClient->GetBufferSize(&bufferSize);
|
||||
CHECK_RES(res, "audioClient->GetBufferSize");
|
||||
uint64_t framesWritten=0;
|
||||
|
||||
bool running=true;
|
||||
//double prevCallback=VoIPController::GetCurrentTime();
|
||||
|
||||
while(running){
|
||||
DWORD waitResult=WaitForMultipleObjectsEx(3, waitArray, false, INFINITE, false);
|
||||
if(waitResult==WAIT_OBJECT_0){ // shutdownEvent
|
||||
LOGV("render thread shutting down");
|
||||
running=false;
|
||||
}else if(waitResult==WAIT_OBJECT_0+1){ // streamSwitchEvent
|
||||
LOGV("stream switch");
|
||||
ActuallySetCurrentDevice(streamChangeToDevice);
|
||||
ResetEvent(streamSwitchEvent);
|
||||
LOGV("stream switch done");
|
||||
}else if(waitResult==WAIT_OBJECT_0+2){ // audioSamplesReadyEvent
|
||||
if(!audioClient)
|
||||
continue;
|
||||
|
||||
BYTE* data;
|
||||
uint32_t padding;
|
||||
uint32_t framesAvailable;
|
||||
res=audioClient->GetCurrentPadding(&padding);
|
||||
CHECK_RES(res, "audioClient->GetCurrentPadding");
|
||||
framesAvailable=bufferSize-padding;
|
||||
res=renderClient->GetBuffer(framesAvailable, &data);
|
||||
CHECK_RES(res, "renderClient->GetBuffer");
|
||||
|
||||
//double t=VoIPController::GetCurrentTime();
|
||||
//LOGV("framesAvail: %u, time: %f, isPlaying: %d", framesAvailable, t-prevCallback, isPlaying);
|
||||
//prevCallback=t;
|
||||
|
||||
size_t bytesAvailable=framesAvailable*2;
|
||||
while(bytesAvailable>remainingDataLen){
|
||||
if(isPlaying){
|
||||
InvokeCallback(remainingData+remainingDataLen, 960*2);
|
||||
}else{
|
||||
memset(remainingData+remainingDataLen, 0, 960*2);
|
||||
}
|
||||
remainingDataLen+=960*2;
|
||||
}
|
||||
memcpy(data, remainingData, bytesAvailable);
|
||||
if(remainingDataLen>bytesAvailable){
|
||||
memmove(remainingData, remainingData+bytesAvailable, remainingDataLen-bytesAvailable);
|
||||
}
|
||||
remainingDataLen-=bytesAvailable;
|
||||
|
||||
res=renderClient->ReleaseBuffer(framesAvailable, 0);
|
||||
CHECK_RES(res, "renderClient->ReleaseBuffer");
|
||||
framesWritten+=framesAvailable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
HRESULT AudioOutputWASAPI::OnSessionDisconnected(AudioSessionDisconnectReason reason) {
|
||||
if(!isDefaultDevice){
|
||||
streamChangeToDevice="default";
|
||||
SetEvent(streamSwitchEvent);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AudioOutputWASAPI::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR newDevID) {
|
||||
if(flow==eRender && role==eCommunications && isDefaultDevice){
|
||||
streamChangeToDevice="default";
|
||||
SetEvent(streamSwitchEvent);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG AudioOutputWASAPI::AddRef(){
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
ULONG AudioOutputWASAPI::Release(){
|
||||
return InterlockedDecrement(&refCount);
|
||||
}
|
||||
|
||||
HRESULT AudioOutputWASAPI::QueryInterface(REFIID iid, void** obj){
|
||||
if(!obj){
|
||||
return E_POINTER;
|
||||
}
|
||||
*obj=NULL;
|
||||
|
||||
if(iid==IID_IUnknown){
|
||||
*obj=static_cast<IUnknown*>(static_cast<IAudioSessionEvents*>(this));
|
||||
AddRef();
|
||||
}else if(iid==__uuidof(IMMNotificationClient)){
|
||||
*obj=static_cast<IMMNotificationClient*>(this);
|
||||
AddRef();
|
||||
}else if(iid==__uuidof(IAudioSessionEvents)){
|
||||
*obj=static_cast<IAudioSessionEvents*>(this);
|
||||
AddRef();
|
||||
}else{
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
||||
103
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioOutputWASAPI.h
Executable file
103
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioOutputWASAPI.h
Executable file
|
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOOUTPUTWASAPI_H
|
||||
#define LIBTGVOIP_AUDIOOUTPUTWASAPI_H
|
||||
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
#define TGVOIP_WINDOWS_PHONE
|
||||
#endif
|
||||
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP
|
||||
#define TGVOIP_WINDOWS_DESKTOP
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201)
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
#include <mmdeviceapi.h>
|
||||
#endif
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
#include <audiopolicy.h>
|
||||
#include <functiondiscoverykeys.h>
|
||||
#else
|
||||
#include <audioclient.h>
|
||||
#include "WindowsSandboxUtils.h"
|
||||
#endif
|
||||
#pragma warning(pop)
|
||||
#include "../../audio/AudioOutput.h"
|
||||
|
||||
namespace tgvoip{
|
||||
namespace audio{
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
class AudioOutputWASAPI : public AudioOutput, IMMNotificationClient, IAudioSessionEvents{
|
||||
#else
|
||||
class AudioOutputWASAPI : public AudioOutput{
|
||||
#endif
|
||||
public:
|
||||
AudioOutputWASAPI(std::string deviceID);
|
||||
virtual ~AudioOutputWASAPI();
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
virtual bool IsPlaying();
|
||||
virtual void SetCurrentDevice(std::string deviceID);
|
||||
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
STDMETHOD_(ULONG, AddRef)();
|
||||
STDMETHOD_(ULONG, Release)();
|
||||
#endif
|
||||
|
||||
private:
|
||||
void ActuallySetCurrentDevice(std::string deviceID);
|
||||
static DWORD WINAPI StartThread(void* arg);
|
||||
void RunThread();
|
||||
WAVEFORMATEX format;
|
||||
bool isPlaying;
|
||||
HANDLE shutdownEvent;
|
||||
HANDLE audioSamplesReadyEvent;
|
||||
HANDLE streamSwitchEvent;
|
||||
HANDLE thread;
|
||||
IAudioClient* audioClient=NULL;
|
||||
IAudioRenderClient* renderClient=NULL;
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
IMMDeviceEnumerator* enumerator;
|
||||
IAudioSessionControl* audioSessionControl;
|
||||
IMMDevice* device;
|
||||
#endif
|
||||
unsigned char remainingData[10240];
|
||||
size_t remainingDataLen;
|
||||
bool isDefaultDevice;
|
||||
ULONG refCount;
|
||||
std::string streamChangeToDevice;
|
||||
|
||||
#ifdef TGVOIP_WINDOWS_DESKTOP
|
||||
STDMETHOD(OnDisplayNameChanged) (LPCWSTR /*NewDisplayName*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnIconPathChanged) (LPCWSTR /*NewIconPath*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnSimpleVolumeChanged) (float /*NewSimpleVolume*/, BOOL /*NewMute*/, LPCGUID /*EventContext*/) { return S_OK; }
|
||||
STDMETHOD(OnChannelVolumeChanged) (DWORD /*ChannelCount*/, float /*NewChannelVolumes*/[], DWORD /*ChangedChannel*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnGroupingParamChanged) (LPCGUID /*NewGroupingParam*/, LPCGUID /*EventContext*/) { return S_OK; };
|
||||
STDMETHOD(OnStateChanged) (AudioSessionState /*NewState*/) { return S_OK; };
|
||||
STDMETHOD(OnSessionDisconnected) (AudioSessionDisconnectReason DisconnectReason);
|
||||
STDMETHOD(OnDeviceStateChanged) (LPCWSTR /*DeviceId*/, DWORD /*NewState*/) { return S_OK; }
|
||||
STDMETHOD(OnDeviceAdded) (LPCWSTR /*DeviceId*/) { return S_OK; };
|
||||
STDMETHOD(OnDeviceRemoved) (LPCWSTR /*DeviceId(*/) { return S_OK; };
|
||||
STDMETHOD(OnDefaultDeviceChanged) (EDataFlow Flow, ERole Role, LPCWSTR NewDefaultDeviceId);
|
||||
STDMETHOD(OnPropertyValueChanged) (LPCWSTR /*DeviceId*/, const PROPERTYKEY /*Key*/) { return S_OK; };
|
||||
|
||||
//
|
||||
// IUnknown
|
||||
//
|
||||
STDMETHOD(QueryInterface)(REFIID iid, void **pvObject);
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOOUTPUTWASAPI_H
|
||||
165
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioOutputWave.cpp
Executable file
165
TMessagesProj/jni/voip/libtgvoip/os/windows/AudioOutputWave.cpp
Executable file
|
|
@ -0,0 +1,165 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include "AudioOutputWave.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../VoIPController.h"
|
||||
|
||||
#define BUFFER_SIZE 960
|
||||
#define CHECK_ERROR(res, msg) if(res!=MMSYSERR_NOERROR){wchar_t _buf[1024]; waveOutGetErrorTextW(res, _buf, 1024); LOGE(msg ": %ws (MMRESULT=0x%08X)", _buf, res); failed=true;}
|
||||
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
AudioOutputWave::AudioOutputWave(std::string deviceID){
|
||||
isPlaying=false;
|
||||
hWaveOut=NULL;
|
||||
|
||||
for(int i=0;i<4;i++){
|
||||
ZeroMemory(&buffers[i], sizeof(WAVEHDR));
|
||||
buffers[i].dwBufferLength=960*2;
|
||||
buffers[i].lpData=(char*)malloc(960*2);
|
||||
}
|
||||
|
||||
SetCurrentDevice(deviceID);
|
||||
}
|
||||
|
||||
AudioOutputWave::~AudioOutputWave(){
|
||||
for(int i=0;i<4;i++){
|
||||
free(buffers[i].lpData);
|
||||
}
|
||||
waveOutClose(hWaveOut);
|
||||
}
|
||||
|
||||
void AudioOutputWave::Start(){
|
||||
if(!isPlaying){
|
||||
isPlaying=true;
|
||||
|
||||
for(int i=0;i<4;i++){
|
||||
MMRESULT res=waveOutPrepareHeader(hWaveOut, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveOutPrepareHeader failed");
|
||||
//InvokeCallback((unsigned char*)buffers[i].lpData, buffers[i].dwBufferLength);
|
||||
ZeroMemory(buffers[i].lpData, buffers[i].dwBufferLength);
|
||||
res=waveOutWrite(hWaveOut, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveOutWrite failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputWave::Stop(){
|
||||
if(isPlaying){
|
||||
isPlaying=false;
|
||||
|
||||
MMRESULT res=waveOutReset(hWaveOut);
|
||||
CHECK_ERROR(res, "waveOutReset failed");
|
||||
for(int i=0;i<4;i++){
|
||||
res=waveOutUnprepareHeader(hWaveOut, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveOutUnprepareHeader failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioOutputWave::IsPlaying(){
|
||||
return isPlaying;
|
||||
}
|
||||
|
||||
void AudioOutputWave::WaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
|
||||
if(uMsg==WOM_DONE){
|
||||
((AudioOutputWave*)dwInstance)->OnBufferDone((WAVEHDR*)dwParam1);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputWave::OnBufferDone(WAVEHDR* hdr){
|
||||
if(!isPlaying)
|
||||
return;
|
||||
|
||||
InvokeCallback((unsigned char*)hdr->lpData, hdr->dwBufferLength);
|
||||
hdr->dwFlags&= ~WHDR_DONE;
|
||||
MMRESULT res=waveOutWrite(hWaveOut, hdr, sizeof(WAVEHDR));
|
||||
}
|
||||
|
||||
void AudioOutputWave::EnumerateDevices(std::vector<tgvoip::AudioOutputDevice>& devs){
|
||||
UINT num=waveOutGetNumDevs();
|
||||
WAVEOUTCAPSW caps;
|
||||
char nameBuf[512];
|
||||
for(UINT i=0;i<num;i++){
|
||||
waveOutGetDevCapsW(i, &caps, sizeof(caps));
|
||||
AudioOutputDevice dev;
|
||||
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
|
||||
dev.displayName=std::string(nameBuf);
|
||||
dev.id=std::string(nameBuf);
|
||||
devs.push_back(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputWave::SetCurrentDevice(std::string deviceID){
|
||||
currentDevice=deviceID;
|
||||
|
||||
bool wasPlaying=isPlaying;
|
||||
isPlaying=false;
|
||||
LOGV("closing, hWaveOut=%d", (int)hWaveOut);
|
||||
if(hWaveOut){
|
||||
MMRESULT res;
|
||||
if(isPlaying){
|
||||
res=waveOutReset(hWaveOut);
|
||||
CHECK_ERROR(res, "waveOutReset failed");
|
||||
for(int i=0;i<4;i++){
|
||||
res=waveOutUnprepareHeader(hWaveOut, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveOutUnprepareHeader failed");
|
||||
}
|
||||
}
|
||||
res=waveOutClose(hWaveOut);
|
||||
CHECK_ERROR(res, "waveOutClose failed");
|
||||
}
|
||||
|
||||
ZeroMemory(&format, sizeof(format));
|
||||
format.cbSize=0;
|
||||
format.wFormatTag=WAVE_FORMAT_PCM;
|
||||
format.nSamplesPerSec=48000;
|
||||
format.wBitsPerSample=16;
|
||||
format.nChannels=1;
|
||||
format.nBlockAlign=2;
|
||||
|
||||
LOGV("before open device %s", deviceID.c_str());
|
||||
|
||||
if(deviceID=="default"){
|
||||
MMRESULT res=waveOutOpen(&hWaveOut, WAVE_MAPPER, &format, (DWORD_PTR)AudioOutputWave::WaveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
||||
CHECK_ERROR(res, "waveOutOpen failed");
|
||||
}else{
|
||||
UINT num=waveOutGetNumDevs();
|
||||
WAVEOUTCAPSW caps;
|
||||
char nameBuf[512];
|
||||
hWaveOut=NULL;
|
||||
for(UINT i=0;i<num;i++){
|
||||
waveOutGetDevCapsW(i, &caps, sizeof(caps));
|
||||
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
|
||||
std::string name=std::string(nameBuf);
|
||||
if(name==deviceID){
|
||||
MMRESULT res=waveOutOpen(&hWaveOut, i, &format, (DWORD_PTR)AudioOutputWave::WaveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION | WAVE_MAPPED);
|
||||
CHECK_ERROR(res, "waveOutOpen failed");
|
||||
LOGD("Opened device %s", nameBuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hWaveOut){
|
||||
SetCurrentDevice("default");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
isPlaying=wasPlaying;
|
||||
|
||||
if(isPlaying){
|
||||
MMRESULT res;
|
||||
for(int i=0;i<4;i++){
|
||||
res=waveOutPrepareHeader(hWaveOut, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveOutPrepareHeader failed");
|
||||
res=waveOutWrite(hWaveOut, &buffers[i], sizeof(WAVEHDR));
|
||||
CHECK_ERROR(res, "waveOutWrite failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOOUTPUTWAVE_H
|
||||
#define LIBTGVOIP_AUDIOOUTPUTWAVE_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../../audio/AudioOutput.h"
|
||||
|
||||
namespace tgvoip{
|
||||
namespace audio{
|
||||
|
||||
class AudioOutputWave : public AudioOutput{
|
||||
public:
|
||||
AudioOutputWave(std::string deviceID);
|
||||
virtual ~AudioOutputWave();
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
virtual bool IsPlaying();
|
||||
virtual void SetCurrentDevice(std::string deviceID);
|
||||
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
|
||||
|
||||
private:
|
||||
HWAVEOUT hWaveOut;
|
||||
WAVEFORMATEX format;
|
||||
WAVEHDR buffers[4];
|
||||
static void CALLBACK WaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
|
||||
void OnBufferDone(WAVEHDR* hdr);
|
||||
bool isPlaying;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOOUTPUTWAVE_H
|
||||
469
TMessagesProj/jni/voip/libtgvoip/os/windows/CXWrapper.cpp
Executable file
469
TMessagesProj/jni/voip/libtgvoip/os/windows/CXWrapper.cpp
Executable file
|
|
@ -0,0 +1,469 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <collection.h>
|
||||
#include "CXWrapper.h"
|
||||
#include <wrl.h>
|
||||
#include <robuffer.h>
|
||||
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace libtgvoip;
|
||||
using namespace Platform;
|
||||
using namespace tgvoip;
|
||||
using namespace Windows::Security::Cryptography;
|
||||
using namespace Windows::Security::Cryptography::Core;
|
||||
using namespace Windows::Storage::Streams;
|
||||
using namespace Windows::Data::Json;
|
||||
using namespace Windows::Phone::Media::Devices;
|
||||
|
||||
//CryptographicHash^ MicrosoftCryptoImpl::sha1Hash;
|
||||
//CryptographicHash^ MicrosoftCryptoImpl::sha256Hash;
|
||||
HashAlgorithmProvider^ MicrosoftCryptoImpl::sha1Provider;
|
||||
HashAlgorithmProvider^ MicrosoftCryptoImpl::sha256Provider;
|
||||
SymmetricKeyAlgorithmProvider^ MicrosoftCryptoImpl::aesKeyProvider;
|
||||
|
||||
/*struct tgvoip_cx_data{
|
||||
VoIPControllerWrapper^ self;
|
||||
};*/
|
||||
|
||||
VoIPControllerWrapper::VoIPControllerWrapper(){
|
||||
VoIPController::crypto.aes_ige_decrypt=MicrosoftCryptoImpl::AesIgeDecrypt;
|
||||
VoIPController::crypto.aes_ige_encrypt=MicrosoftCryptoImpl::AesIgeEncrypt;
|
||||
VoIPController::crypto.aes_ctr_encrypt = MicrosoftCryptoImpl::AesCtrEncrypt;
|
||||
VoIPController::crypto.sha1=MicrosoftCryptoImpl::SHA1;
|
||||
VoIPController::crypto.sha256=MicrosoftCryptoImpl::SHA256;
|
||||
VoIPController::crypto.rand_bytes=MicrosoftCryptoImpl::RandBytes;
|
||||
MicrosoftCryptoImpl::Init();
|
||||
controller=new VoIPController();
|
||||
controller->implData=(void*)this;
|
||||
VoIPController::Callbacks callbacks={0};
|
||||
callbacks.connectionStateChanged=VoIPControllerWrapper::OnStateChanged;
|
||||
callbacks.signalBarCountChanged=VoIPControllerWrapper::OnSignalBarsChanged;
|
||||
controller->SetCallbacks(callbacks);
|
||||
}
|
||||
|
||||
VoIPControllerWrapper::~VoIPControllerWrapper(){
|
||||
controller->Stop();
|
||||
delete controller;
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::Start(){
|
||||
controller->Start();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::Connect(){
|
||||
controller->Connect();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Array<libtgvoip::Endpoint^>^ endpoints, bool allowP2P, int32_t connectionMaxLayer){
|
||||
std::vector<tgvoip::Endpoint> eps;
|
||||
for (unsigned int i = 0; i < endpoints->Length; i++)
|
||||
{
|
||||
libtgvoip::Endpoint^ _ep = endpoints[i];
|
||||
tgvoip::Endpoint ep;
|
||||
ep.id = _ep->id;
|
||||
ep.type = tgvoip::Endpoint::Type::UDP_RELAY;
|
||||
char buf[128];
|
||||
if (_ep->ipv4){
|
||||
WideCharToMultiByte(CP_UTF8, 0, _ep->ipv4->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
ep.address = IPv4Address(buf);
|
||||
}
|
||||
if (_ep->ipv6){
|
||||
WideCharToMultiByte(CP_UTF8, 0, _ep->ipv6->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
ep.v6address = IPv6Address(buf);
|
||||
}
|
||||
ep.port = _ep->port;
|
||||
if (_ep->peerTag->Length != 16)
|
||||
throw ref new Platform::InvalidArgumentException("Peer tag must be exactly 16 bytes long");
|
||||
memcpy(ep.peerTag, _ep->peerTag->Data, 16);
|
||||
eps.push_back(ep);
|
||||
}
|
||||
controller->SetRemoteEndpoints(eps, allowP2P, connectionMaxLayer);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetNetworkType(NetworkType type){
|
||||
controller->SetNetworkType((int)type);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetMicMute(bool mute){
|
||||
controller->SetMicMute(mute);
|
||||
}
|
||||
|
||||
int64 VoIPControllerWrapper::GetPreferredRelayID(){
|
||||
return controller->GetPreferredRelayID();
|
||||
}
|
||||
|
||||
int32_t VoIPControllerWrapper::GetConnectionMaxLayer(){
|
||||
return tgvoip::VoIPController::GetConnectionMaxLayer();
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetEncryptionKey(const Platform::Array<uint8>^ key, bool isOutgoing){
|
||||
if(key->Length!=256)
|
||||
throw ref new Platform::InvalidArgumentException("Encryption key must be exactly 256 bytes long");
|
||||
controller->SetEncryptionKey((char*)key->Data, isOutgoing);
|
||||
}
|
||||
|
||||
int VoIPControllerWrapper::GetSignalBarsCount(){
|
||||
return controller->GetSignalBarsCount();
|
||||
}
|
||||
|
||||
CallState VoIPControllerWrapper::GetConnectionState(){
|
||||
return (CallState)controller->GetConnectionState();
|
||||
}
|
||||
|
||||
TrafficStats^ VoIPControllerWrapper::GetStats(){
|
||||
tgvoip::VoIPController::TrafficStats _stats;
|
||||
controller->GetStats(&_stats);
|
||||
|
||||
TrafficStats^ stats = ref new TrafficStats();
|
||||
stats->bytesSentWifi = _stats.bytesSentWifi;
|
||||
stats->bytesSentMobile = _stats.bytesSentMobile;
|
||||
stats->bytesRecvdWifi = _stats.bytesRecvdWifi;
|
||||
stats->bytesRecvdMobile = _stats.bytesRecvdMobile;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetDebugString(){
|
||||
std::string log = controller->GetDebugString();
|
||||
size_t len = sizeof(wchar_t)*(log.length() + 1);
|
||||
wchar_t* wlog = (wchar_t*)malloc(len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len / sizeof(wchar_t));
|
||||
Platform::String^ res = ref new Platform::String(wlog);
|
||||
free(wlog);
|
||||
return res;
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetDebugLog(){
|
||||
std::string log=controller->GetDebugLog();
|
||||
size_t len=sizeof(wchar_t)*(log.length()+1);
|
||||
wchar_t* wlog=(wchar_t*)malloc(len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len/sizeof(wchar_t));
|
||||
Platform::String^ res=ref new Platform::String(wlog);
|
||||
free(wlog);
|
||||
return res;
|
||||
}
|
||||
|
||||
Error VoIPControllerWrapper::GetLastError(){
|
||||
return (Error)controller->GetLastError();
|
||||
}
|
||||
|
||||
Platform::String^ VoIPControllerWrapper::GetVersion(){
|
||||
const char* v=VoIPController::GetVersion();
|
||||
wchar_t buf[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, v, -1, buf, sizeof(buf));
|
||||
return ref new Platform::String(buf);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnStateChanged(VoIPController* c, int state){
|
||||
reinterpret_cast<VoIPControllerWrapper^>(c->implData)->OnStateChangedInternal(state);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnSignalBarsChanged(VoIPController* c, int count){
|
||||
reinterpret_cast<VoIPControllerWrapper^>(c->implData)->OnSignalBarsChangedInternal(count);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnStateChangedInternal(int state){
|
||||
CallStateChanged(this, (CallState)state);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::OnSignalBarsChangedInternal(int count){
|
||||
SignalBarsChanged(this, count);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetConfig(VoIPConfig^ wrapper){
|
||||
VoIPController::Config config{0};
|
||||
config.initTimeout=wrapper->initTimeout;
|
||||
config.recvTimeout=wrapper->recvTimeout;
|
||||
config.dataSaving=(int)wrapper->dataSaving;
|
||||
config.logFilePath;
|
||||
config.statsDumpFilePath;
|
||||
|
||||
config.enableAEC=wrapper->enableAEC;
|
||||
config.enableNS=wrapper->enableNS;
|
||||
config.enableAGC=wrapper->enableAGC;
|
||||
|
||||
config.enableCallUpgrade=wrapper->enableCallUpgrade;
|
||||
|
||||
config.logPacketStats=wrapper->logPacketStats;
|
||||
config.enableVolumeControl=wrapper->enableVolumeControl;
|
||||
|
||||
config.enableVideoSend=wrapper->enableVideoSend;
|
||||
config.enableVideoReceive=wrapper->enableVideoReceive;
|
||||
|
||||
if(wrapper->logFilePath!=nullptr&&!wrapper->logFilePath->IsEmpty()){
|
||||
config.logFilePath = wstring(wrapper->logFilePath->Data());
|
||||
}
|
||||
if (wrapper->statsDumpFilePath != nullptr&&!wrapper->statsDumpFilePath->IsEmpty()){
|
||||
config.statsDumpFilePath = wstring(wrapper->statsDumpFilePath->Data());
|
||||
}
|
||||
|
||||
controller->SetConfig(config);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password){
|
||||
char _address[2000];
|
||||
char _username[256];
|
||||
char _password[256];
|
||||
|
||||
WideCharToMultiByte(CP_UTF8, 0, address->Data(), -1, _address, sizeof(_address), NULL, NULL);
|
||||
WideCharToMultiByte(CP_UTF8, 0, username->Data(), -1, _username, sizeof(_username), NULL, NULL);
|
||||
WideCharToMultiByte(CP_UTF8, 0, password->Data(), -1, _password, sizeof(_password), NULL, NULL);
|
||||
|
||||
controller->SetProxy((int)protocol, _address, port, _username, _password);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetAudioOutputGainControlEnabled(bool enabled){
|
||||
controller->SetAudioOutputGainControlEnabled(enabled);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetInputVolume(float level){
|
||||
controller->SetInputVolume(level);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SetOutputVolume(float level){
|
||||
controller->SetOutputVolume(level);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::UpdateServerConfig(Platform::String^ json){
|
||||
std::string config=ToUtf8(json->Data(), json->Length());
|
||||
ServerConfig::GetSharedInstance()->Update(config);
|
||||
}
|
||||
|
||||
void VoIPControllerWrapper::SwitchSpeaker(bool external){
|
||||
auto routingManager = AudioRoutingManager::GetDefault();
|
||||
if (external){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Speakerphone);
|
||||
}
|
||||
else{
|
||||
if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Bluetooth) == AvailableAudioRoutingEndpoints::Bluetooth){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Bluetooth);
|
||||
}
|
||||
else if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Earpiece) == AvailableAudioRoutingEndpoints::Earpiece){
|
||||
routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Earpiece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){
|
||||
IBuffer^ keybuf=IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
uint8_t tmpOut[16];
|
||||
uint8_t* xPrev=iv+16;
|
||||
uint8_t* yPrev=iv;
|
||||
uint8_t x[16];
|
||||
uint8_t y[16];
|
||||
for(size_t offset=0;offset<len;offset+=16){
|
||||
for (size_t i=0;i<16;i++){
|
||||
if (offset+i < len){
|
||||
x[i] = in[offset+i];
|
||||
}
|
||||
else{
|
||||
x[i]=0;
|
||||
}
|
||||
}
|
||||
XorInt128(x, yPrev, y);
|
||||
IBuffer^ inbuf=IBufferFromPtr(y, 16);
|
||||
IBuffer^ outbuf=CryptographicEngine::Encrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, tmpOut);
|
||||
XorInt128(tmpOut, xPrev, y);
|
||||
memcpy(xPrev, x, 16);
|
||||
memcpy(yPrev, y, 16);
|
||||
memcpy(out+offset, y, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){
|
||||
IBuffer^ keybuf=IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
uint8_t tmpOut[16];
|
||||
uint8_t* xPrev=iv;
|
||||
uint8_t* yPrev=iv+16;
|
||||
uint8_t x[16];
|
||||
uint8_t y[16];
|
||||
for(size_t offset=0;offset<len;offset+=16){
|
||||
for (size_t i=0;i<16;i++){
|
||||
if (offset+i < len){
|
||||
x[i] = in[offset+i];
|
||||
}
|
||||
else{
|
||||
x[i]=0;
|
||||
}
|
||||
}
|
||||
XorInt128(x, yPrev, y);
|
||||
IBuffer^ inbuf=IBufferFromPtr(y, 16);
|
||||
IBuffer^ outbuf=CryptographicEngine::Decrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, tmpOut);
|
||||
XorInt128(tmpOut, xPrev, y);
|
||||
memcpy(xPrev, x, 16);
|
||||
memcpy(yPrev, y, 16);
|
||||
memcpy(out+offset, y, 16);
|
||||
}
|
||||
}
|
||||
|
||||
#define GETU32(pt) (((uint32_t)(pt)[0] << 24) ^ ((uint32_t)(pt)[1] << 16) ^ ((uint32_t)(pt)[2] << 8) ^ ((uint32_t)(pt)[3]))
|
||||
#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }
|
||||
|
||||
typedef uint8_t u8;
|
||||
|
||||
#define L_ENDIAN
|
||||
|
||||
/* increment counter (128-bit int) by 2^64 */
|
||||
static void AES_ctr128_inc(unsigned char *counter) {
|
||||
unsigned long c;
|
||||
|
||||
/* Grab 3rd dword of counter and increment */
|
||||
#ifdef L_ENDIAN
|
||||
c = GETU32(counter + 8);
|
||||
c++;
|
||||
PUTU32(counter + 8, c);
|
||||
#else
|
||||
c = GETU32(counter + 4);
|
||||
c++;
|
||||
PUTU32(counter + 4, c);
|
||||
#endif
|
||||
|
||||
/* if no overflow, we're done */
|
||||
if (c)
|
||||
return;
|
||||
|
||||
/* Grab top dword of counter and increment */
|
||||
#ifdef L_ENDIAN
|
||||
c = GETU32(counter + 12);
|
||||
c++;
|
||||
PUTU32(counter + 12, c);
|
||||
#else
|
||||
c = GETU32(counter + 0);
|
||||
c++;
|
||||
PUTU32(counter + 0, c);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num){
|
||||
unsigned int n;
|
||||
unsigned long l = len;
|
||||
|
||||
//assert(in && out && key && counter && num);
|
||||
//assert(*num < AES_BLOCK_SIZE);
|
||||
|
||||
IBuffer^ keybuf = IBufferFromPtr(key, 32);
|
||||
CryptographicKey^ _key = aesKeyProvider->CreateSymmetricKey(keybuf);
|
||||
|
||||
n = *num;
|
||||
|
||||
while (l--) {
|
||||
if (n == 0) {
|
||||
IBuffer^ inbuf = IBufferFromPtr(counter, 16);
|
||||
IBuffer^ outbuf = CryptographicEngine::Encrypt(_key, inbuf, nullptr);
|
||||
IBufferToPtr(outbuf, 16, ecount_buf);
|
||||
//AES_encrypt(counter, ecount_buf, key);
|
||||
AES_ctr128_inc(counter);
|
||||
}
|
||||
*inout = *(inout++) ^ ecount_buf[n];
|
||||
n = (n + 1) % 16;
|
||||
}
|
||||
|
||||
*num = n;
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::SHA1(uint8_t* msg, size_t len, uint8_t* out){
|
||||
//EnterCriticalSection(&hashMutex);
|
||||
|
||||
IBuffer^ arr=IBufferFromPtr(msg, len);
|
||||
CryptographicHash^ hash=sha1Provider->CreateHash();
|
||||
hash->Append(arr);
|
||||
IBuffer^ res=hash->GetValueAndReset();
|
||||
IBufferToPtr(res, 20, out);
|
||||
|
||||
//LeaveCriticalSection(&hashMutex);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::SHA256(uint8_t* msg, size_t len, uint8_t* out){
|
||||
//EnterCriticalSection(&hashMutex);
|
||||
|
||||
IBuffer^ arr=IBufferFromPtr(msg, len);
|
||||
CryptographicHash^ hash=sha256Provider->CreateHash();
|
||||
hash->Append(arr);
|
||||
IBuffer^ res=hash->GetValueAndReset();
|
||||
IBufferToPtr(res, 32, out);
|
||||
//LeaveCriticalSection(&hashMutex);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::RandBytes(uint8_t* buffer, size_t len){
|
||||
IBuffer^ res=CryptographicBuffer::GenerateRandom(len);
|
||||
IBufferToPtr(res, len, buffer);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::Init(){
|
||||
/*sha1Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1)->CreateHash();
|
||||
sha256Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256)->CreateHash();*/
|
||||
sha1Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1);
|
||||
sha256Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256);
|
||||
aesKeyProvider=SymmetricKeyAlgorithmProvider::OpenAlgorithm(SymmetricAlgorithmNames::AesEcb);
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::XorInt128(uint8_t* a, uint8_t* b, uint8_t* out){
|
||||
uint64_t* _a=reinterpret_cast<uint64_t*>(a);
|
||||
uint64_t* _b=reinterpret_cast<uint64_t*>(b);
|
||||
uint64_t* _out=reinterpret_cast<uint64_t*>(out);
|
||||
_out[0]=_a[0]^_b[0];
|
||||
_out[1]=_a[1]^_b[1];
|
||||
}
|
||||
|
||||
void MicrosoftCryptoImpl::IBufferToPtr(IBuffer^ buffer, size_t len, uint8_t* out)
|
||||
{
|
||||
ComPtr<IBufferByteAccess> bufferByteAccess;
|
||||
reinterpret_cast<IInspectable*>(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
|
||||
|
||||
byte* hashBuffer;
|
||||
bufferByteAccess->Buffer(&hashBuffer);
|
||||
CopyMemory(out, hashBuffer, len);
|
||||
}
|
||||
|
||||
IBuffer^ MicrosoftCryptoImpl::IBufferFromPtr(uint8_t* msg, size_t len)
|
||||
{
|
||||
ComPtr<NativeBuffer> nativeBuffer=Make<NativeBuffer>((byte *)msg, len);
|
||||
return reinterpret_cast<IBuffer^>(nativeBuffer.Get());
|
||||
}
|
||||
|
||||
/*Platform::String^ VoIPControllerWrapper::TestAesIge(){
|
||||
MicrosoftCryptoImpl::Init();
|
||||
Platform::String^ res="";
|
||||
Platform::Array<uint8>^ data=ref new Platform::Array<uint8>(32);
|
||||
Platform::Array<uint8>^ out=ref new Platform::Array<uint8>(32);
|
||||
Platform::Array<uint8>^ key=ref new Platform::Array<uint8>(16);
|
||||
Platform::Array<uint8>^ iv=ref new Platform::Array<uint8>(32);
|
||||
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("0000000000000000000000000000000000000000000000000000000000000000"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("99706487A1CDE613BC6DE0B6F24B1C7AA448C8B9C3403E3467A8CAD89340F53B"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
res+="\n";
|
||||
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("4C2E204C6574277320686F70652042656E20676F74206974207269676874210A"), &data);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv);
|
||||
CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key);
|
||||
MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data);
|
||||
res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out));
|
||||
return res;
|
||||
}*/
|
||||
273
TMessagesProj/jni/voip/libtgvoip/os/windows/CXWrapper.h
Normal file
273
TMessagesProj/jni/voip/libtgvoip/os/windows/CXWrapper.h
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
#pragma once
|
||||
|
||||
#include <wrl.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <windows.storage.streams.h>
|
||||
#include <robuffer.h>
|
||||
#include <vector>
|
||||
#include "../../VoIPController.h"
|
||||
#include "../../VoIPServerConfig.h"
|
||||
|
||||
using namespace Platform;
|
||||
|
||||
#define STACK_ARRAY(TYPE, LEN) \
|
||||
static_cast<TYPE*>(::alloca((LEN) * sizeof(TYPE)))
|
||||
|
||||
inline std::wstring ToUtf16(const char* utf8, size_t len) {
|
||||
int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len),
|
||||
nullptr, 0);
|
||||
wchar_t* ws = STACK_ARRAY(wchar_t, len16);
|
||||
::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast<int>(len), ws, len16);
|
||||
return std::wstring(ws, len16);
|
||||
}
|
||||
|
||||
inline std::wstring ToUtf16(const std::string& str) {
|
||||
return ToUtf16(str.data(), str.length());
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const wchar_t* wide, size_t len) {
|
||||
int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
char* ns = STACK_ARRAY(char, len8);
|
||||
::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast<int>(len), ns, len8,
|
||||
nullptr, nullptr);
|
||||
return std::string(ns, len8);
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const wchar_t* wide) {
|
||||
return ToUtf8(wide, wcslen(wide));
|
||||
}
|
||||
|
||||
inline std::string ToUtf8(const std::wstring& wstr) {
|
||||
return ToUtf8(wstr.data(), wstr.length());
|
||||
}
|
||||
|
||||
namespace libtgvoip{
|
||||
public ref class Endpoint sealed{
|
||||
public:
|
||||
property int64 id;
|
||||
property uint16 port;
|
||||
property Platform::String^ ipv4;
|
||||
property Platform::String^ ipv6;
|
||||
property Platform::Array<uint8>^ peerTag;
|
||||
};
|
||||
|
||||
public ref class TrafficStats sealed{
|
||||
public:
|
||||
property uint64_t bytesSentWifi;
|
||||
property uint64_t bytesRecvdWifi;
|
||||
property uint64_t bytesSentMobile;
|
||||
property uint64_t bytesRecvdMobile;
|
||||
};
|
||||
|
||||
public enum class CallState : int{
|
||||
WaitInit=1,
|
||||
WaitInitAck,
|
||||
Established,
|
||||
Failed
|
||||
};
|
||||
|
||||
public enum class Error : int{
|
||||
Unknown=0,
|
||||
Incompatible,
|
||||
Timeout,
|
||||
AudioIO
|
||||
};
|
||||
|
||||
public enum class NetworkType : int{
|
||||
Unknown=0,
|
||||
GPRS,
|
||||
EDGE,
|
||||
UMTS,
|
||||
HSPA,
|
||||
LTE,
|
||||
WiFi,
|
||||
Ethernet,
|
||||
OtherHighSpeed,
|
||||
OtherLowSpeed,
|
||||
Dialup,
|
||||
OtherMobile
|
||||
};
|
||||
|
||||
public enum class DataSavingMode{
|
||||
Never=0,
|
||||
MobileOnly,
|
||||
Always
|
||||
};
|
||||
|
||||
public enum class ProxyProtocol{
|
||||
None=0,
|
||||
SOCKS5
|
||||
};
|
||||
|
||||
public ref class VoIPConfig sealed {
|
||||
public:
|
||||
VoIPConfig() {
|
||||
logPacketStats = false;
|
||||
enableVolumeControl = false;
|
||||
enableVideoSend = false;
|
||||
enableVideoReceive = false;
|
||||
}
|
||||
|
||||
property double initTimeout;
|
||||
property double recvTimeout;
|
||||
property DataSavingMode dataSaving;
|
||||
property String^ logFilePath;
|
||||
property String^ statsDumpFilePath;
|
||||
|
||||
property bool enableAEC;
|
||||
property bool enableNS;
|
||||
property bool enableAGC;
|
||||
|
||||
property bool enableCallUpgrade;
|
||||
|
||||
property bool logPacketStats;
|
||||
property bool enableVolumeControl;
|
||||
|
||||
property bool enableVideoSend;
|
||||
property bool enableVideoReceive;
|
||||
};
|
||||
|
||||
ref class VoIPControllerWrapper;
|
||||
public delegate void CallStateChangedEventHandler(VoIPControllerWrapper^ sender, CallState newState);
|
||||
|
||||
ref class VoIPControllerWrapper;
|
||||
public delegate void SignalBarsChangedEventHandler(VoIPControllerWrapper^ sender, int newCount);
|
||||
|
||||
public ref class VoIPControllerWrapper sealed{
|
||||
public:
|
||||
VoIPControllerWrapper();
|
||||
virtual ~VoIPControllerWrapper();
|
||||
void Start();
|
||||
void Connect();
|
||||
void SetPublicEndpoints(const Platform::Array<Endpoint^>^ endpoints, bool allowP2P, int32_t connectionMaxLayer);
|
||||
void SetNetworkType(NetworkType type);
|
||||
void SetMicMute(bool mute);
|
||||
void SetEncryptionKey(const Platform::Array<uint8>^ key, bool isOutgoing);
|
||||
void SetConfig(VoIPConfig^ config);
|
||||
void SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password);
|
||||
int GetSignalBarsCount();
|
||||
CallState GetConnectionState();
|
||||
TrafficStats^ GetStats();
|
||||
Platform::String^ GetDebugString();
|
||||
Platform::String^ GetDebugLog();
|
||||
Error GetLastError();
|
||||
static Platform::String^ GetVersion();
|
||||
int64 GetPreferredRelayID();
|
||||
void SetAudioOutputGainControlEnabled(bool enabled);
|
||||
|
||||
void SetInputVolume(float level);
|
||||
void SetOutputVolume(float level);
|
||||
|
||||
property String^ CurrentAudioInput
|
||||
{
|
||||
String^ get()
|
||||
{
|
||||
return ref new String(ToUtf16(controller->GetCurrentAudioInputID()).data());
|
||||
}
|
||||
void set(String^ value)
|
||||
{
|
||||
controller->SetCurrentAudioInput(ToUtf8(value->Data()));
|
||||
}
|
||||
}
|
||||
|
||||
property String^ CurrentAudioOutput
|
||||
{
|
||||
String^ get()
|
||||
{
|
||||
return ref new String(ToUtf16(controller->GetCurrentAudioOutputID()).data());
|
||||
}
|
||||
void set(String^ value)
|
||||
{
|
||||
controller->SetCurrentAudioOutput(ToUtf8(value->Data()));
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GetConnectionMaxLayer();
|
||||
static void UpdateServerConfig(Platform::String^ json);
|
||||
static void SwitchSpeaker(bool external);
|
||||
//static Platform::String^ TestAesIge();
|
||||
|
||||
event CallStateChangedEventHandler^ CallStateChanged;
|
||||
event SignalBarsChangedEventHandler^ SignalBarsChanged;
|
||||
|
||||
private:
|
||||
static void OnStateChanged(tgvoip::VoIPController* c, int state);
|
||||
static void OnSignalBarsChanged(tgvoip::VoIPController* c, int count);
|
||||
void OnStateChangedInternal(int state);
|
||||
void OnSignalBarsChangedInternal(int count);
|
||||
tgvoip::VoIPController* controller;
|
||||
};
|
||||
|
||||
ref class MicrosoftCryptoImpl{
|
||||
public:
|
||||
static void AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv);
|
||||
static void AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv);
|
||||
static void AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num);
|
||||
static void SHA1(uint8_t* msg, size_t len, uint8_t* out);
|
||||
static void SHA256(uint8_t* msg, size_t len, uint8_t* out);
|
||||
static void RandBytes(uint8_t* buffer, size_t len);
|
||||
static void Init();
|
||||
private:
|
||||
static inline void XorInt128(uint8_t* a, uint8_t* b, uint8_t* out);
|
||||
static void IBufferToPtr(Windows::Storage::Streams::IBuffer^ buffer, size_t len, uint8_t* out);
|
||||
static Windows::Storage::Streams::IBuffer^ IBufferFromPtr(uint8_t* msg, size_t len);
|
||||
/*static Windows::Security::Cryptography::Core::CryptographicHash^ sha1Hash;
|
||||
static Windows::Security::Cryptography::Core::CryptographicHash^ sha256Hash;*/
|
||||
static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha1Provider;
|
||||
static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha256Provider;
|
||||
static Windows::Security::Cryptography::Core::SymmetricKeyAlgorithmProvider^ aesKeyProvider;
|
||||
};
|
||||
|
||||
class NativeBuffer :
|
||||
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>,
|
||||
ABI::Windows::Storage::Streams::IBuffer,
|
||||
Windows::Storage::Streams::IBufferByteAccess>
|
||||
{
|
||||
public:
|
||||
NativeBuffer(byte *buffer, UINT totalSize)
|
||||
{
|
||||
m_length=totalSize;
|
||||
m_buffer=buffer;
|
||||
}
|
||||
|
||||
virtual ~NativeBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
STDMETHODIMP RuntimeClassInitialize(byte *buffer, UINT totalSize)
|
||||
{
|
||||
m_length=totalSize;
|
||||
m_buffer=buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP Buffer(byte **value)
|
||||
{
|
||||
*value=m_buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP get_Capacity(UINT32 *value)
|
||||
{
|
||||
*value=m_length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP get_Length(UINT32 *value)
|
||||
{
|
||||
*value=m_length;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP put_Length(UINT32 value)
|
||||
{
|
||||
m_length=value;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
UINT32 m_length;
|
||||
byte *m_buffer;
|
||||
};
|
||||
}
|
||||
719
TMessagesProj/jni/voip/libtgvoip/os/windows/NetworkSocketWinsock.cpp
Executable file
719
TMessagesProj/jni/voip/libtgvoip/os/windows/NetworkSocketWinsock.cpp
Executable file
|
|
@ -0,0 +1,719 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "NetworkSocketWinsock.h"
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
|
||||
#else
|
||||
#include <IPHlpApi.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include "../../logging.h"
|
||||
#include "../../VoIPController.h"
|
||||
#include "WindowsSpecific.h"
|
||||
#include "../../Buffers.h"
|
||||
|
||||
using namespace tgvoip;
|
||||
|
||||
NetworkSocketWinsock::NetworkSocketWinsock(NetworkProtocol protocol) : NetworkSocket(protocol), lastRecvdV4(0), lastRecvdV6("::0"){
|
||||
needUpdateNat64Prefix=true;
|
||||
nat64Present=false;
|
||||
switchToV6at=0;
|
||||
isV4Available=false;
|
||||
closing=false;
|
||||
fd=INVALID_SOCKET;
|
||||
|
||||
#ifdef TGVOIP_WINXP_COMPAT
|
||||
DWORD version=GetVersion();
|
||||
isAtLeastVista=LOBYTE(LOWORD(version))>=6; // Vista is 6.0, XP is 5.1 and 5.2
|
||||
#else
|
||||
isAtLeastVista=true;
|
||||
#endif
|
||||
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
LOGD("Initialized winsock, version %d.%d", wsaData.wHighVersion, wsaData.wVersion);
|
||||
tcpConnectedAddress=NULL;
|
||||
|
||||
if(protocol==PROTO_TCP)
|
||||
timeout=10.0;
|
||||
lastSuccessfulOperationTime=VoIPController::GetCurrentTime();
|
||||
}
|
||||
|
||||
NetworkSocketWinsock::~NetworkSocketWinsock(){
|
||||
if(tcpConnectedAddress)
|
||||
delete tcpConnectedAddress;
|
||||
if(pendingOutgoingPacket)
|
||||
delete pendingOutgoingPacket;
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::SetMaxPriority(){
|
||||
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::Send(NetworkPacket *packet){
|
||||
if(!packet || (protocol==PROTO_UDP && !packet->address)){
|
||||
LOGW("tried to send null packet");
|
||||
return;
|
||||
}
|
||||
int res;
|
||||
if(protocol==PROTO_UDP){
|
||||
IPv4Address *v4addr=dynamic_cast<IPv4Address *>(packet->address);
|
||||
if(isAtLeastVista){
|
||||
sockaddr_in6 addr;
|
||||
if(v4addr){
|
||||
if(needUpdateNat64Prefix && !isV4Available && VoIPController::GetCurrentTime()>switchToV6at && switchToV6at!=0){
|
||||
LOGV("Updating NAT64 prefix");
|
||||
nat64Present=false;
|
||||
addrinfo *addr0;
|
||||
int res=getaddrinfo("ipv4only.arpa", NULL, NULL, &addr0);
|
||||
if(res!=0){
|
||||
LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res));
|
||||
}else{
|
||||
addrinfo *addrPtr;
|
||||
unsigned char *addr170=NULL;
|
||||
unsigned char *addr171=NULL;
|
||||
for(addrPtr=addr0; addrPtr; addrPtr=addrPtr->ai_next){
|
||||
if(addrPtr->ai_family==AF_INET6){
|
||||
sockaddr_in6 *translatedAddr=(sockaddr_in6 *) addrPtr->ai_addr;
|
||||
uint32_t v4part=*((uint32_t *) &translatedAddr->sin6_addr.s6_addr[12]);
|
||||
if(v4part==0xAA0000C0 && !addr170){
|
||||
addr170=translatedAddr->sin6_addr.s6_addr;
|
||||
}
|
||||
if(v4part==0xAB0000C0 && !addr171){
|
||||
addr171=translatedAddr->sin6_addr.s6_addr;
|
||||
}
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
//LOGV("Got translated address: %s", inet_ntop(AF_INET6, &translatedAddr->sin6_addr, buf, sizeof(buf)));
|
||||
}
|
||||
}
|
||||
if(addr170 && addr171 && memcmp(addr170, addr171, 12)==0){
|
||||
nat64Present=true;
|
||||
memcpy(nat64Prefix, addr170, 12);
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
//LOGV("Found nat64 prefix from %s", inet_ntop(AF_INET6, addr170, buf, sizeof(buf)));
|
||||
}else{
|
||||
LOGV("Didn't find nat64");
|
||||
}
|
||||
freeaddrinfo(addr0);
|
||||
}
|
||||
needUpdateNat64Prefix=false;
|
||||
}
|
||||
memset(&addr, 0, sizeof(sockaddr_in6));
|
||||
addr.sin6_family=AF_INET6;
|
||||
*((uint32_t *) &addr.sin6_addr.s6_addr[12])=v4addr->GetAddress();
|
||||
if(nat64Present)
|
||||
memcpy(addr.sin6_addr.s6_addr, nat64Prefix, 12);
|
||||
else
|
||||
addr.sin6_addr.s6_addr[11]=addr.sin6_addr.s6_addr[10]=0xFF;
|
||||
|
||||
}else{
|
||||
IPv6Address *v6addr=dynamic_cast<IPv6Address *>(packet->address);
|
||||
assert(v6addr!=NULL);
|
||||
memcpy(addr.sin6_addr.s6_addr, v6addr->GetAddress(), 16);
|
||||
}
|
||||
addr.sin6_port=htons(packet->port);
|
||||
res=sendto(fd, (const char*)packet->data, packet->length, 0, (const sockaddr *) &addr, sizeof(addr));
|
||||
}else if(v4addr){
|
||||
sockaddr_in addr;
|
||||
addr.sin_addr.s_addr=v4addr->GetAddress();
|
||||
addr.sin_port=htons(packet->port);
|
||||
addr.sin_family=AF_INET;
|
||||
res=sendto(fd, (const char*)packet->data, packet->length, 0, (const sockaddr*)&addr, sizeof(addr));
|
||||
}
|
||||
}else{
|
||||
res=send(fd, (const char*)packet->data, packet->length, 0);
|
||||
}
|
||||
if(res==SOCKET_ERROR){
|
||||
int error=WSAGetLastError();
|
||||
if(error==WSAEWOULDBLOCK){
|
||||
if(pendingOutgoingPacket){
|
||||
LOGE("Got EAGAIN but there's already a pending packet");
|
||||
failed=true;
|
||||
}else{
|
||||
LOGV("Socket %d not ready to send", fd);
|
||||
pendingOutgoingPacket=new Buffer(packet->length);
|
||||
pendingOutgoingPacket->CopyFrom(packet->data, 0, packet->length);
|
||||
readyToSend=false;
|
||||
}
|
||||
}else{
|
||||
LOGE("error sending: %d / %s", error, WindowsSpecific::GetErrorMessage(error).c_str());
|
||||
if(error==WSAENETUNREACH && !isV4Available && VoIPController::GetCurrentTime()<switchToV6at){
|
||||
switchToV6at=VoIPController::GetCurrentTime();
|
||||
LOGI("Network unreachable, trying NAT64");
|
||||
}
|
||||
}
|
||||
}else if(res<packet->length && protocol==PROTO_TCP){
|
||||
if(pendingOutgoingPacket){
|
||||
LOGE("send returned less than packet length but there's already a pending packet");
|
||||
failed=true;
|
||||
}else{
|
||||
LOGV("Socket %d not ready to send", fd);
|
||||
pendingOutgoingPacket=new Buffer(packet->length-res);
|
||||
pendingOutgoingPacket->CopyFrom(packet->data+res, 0, packet->length-res);
|
||||
readyToSend=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkSocketWinsock::OnReadyToSend(){
|
||||
if(pendingOutgoingPacket){
|
||||
NetworkPacket pkt={0};
|
||||
pkt.data=**pendingOutgoingPacket;
|
||||
pkt.length=pendingOutgoingPacket->Length();
|
||||
Send(&pkt);
|
||||
delete pendingOutgoingPacket;
|
||||
pendingOutgoingPacket=NULL;
|
||||
return false;
|
||||
}
|
||||
readyToSend=true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::Receive(NetworkPacket *packet){
|
||||
if(protocol==PROTO_UDP){
|
||||
if(isAtLeastVista){
|
||||
int addrLen=sizeof(sockaddr_in6);
|
||||
sockaddr_in6 srcAddr;
|
||||
int res=recvfrom(fd, (char*)packet->data, packet->length, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen);
|
||||
if(res!=SOCKET_ERROR)
|
||||
packet->length=(size_t) res;
|
||||
else{
|
||||
int error=WSAGetLastError();
|
||||
LOGE("error receiving %d / %s", error, WindowsSpecific::GetErrorMessage(error).c_str());
|
||||
packet->length=0;
|
||||
return;
|
||||
}
|
||||
//LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime());
|
||||
if(!isV4Available && IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr)){
|
||||
isV4Available=true;
|
||||
LOGI("Detected IPv4 connectivity, will not try IPv6");
|
||||
}
|
||||
if(IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr) || (nat64Present && memcmp(nat64Prefix, srcAddr.sin6_addr.s6_addr, 12)==0)){
|
||||
in_addr v4addr=*((in_addr *) &srcAddr.sin6_addr.s6_addr[12]);
|
||||
lastRecvdV4=IPv4Address(v4addr.s_addr);
|
||||
packet->address=&lastRecvdV4;
|
||||
}else{
|
||||
lastRecvdV6=IPv6Address(srcAddr.sin6_addr.s6_addr);
|
||||
packet->address=&lastRecvdV6;
|
||||
}
|
||||
packet->port=ntohs(srcAddr.sin6_port);
|
||||
}else{
|
||||
int addrLen=sizeof(sockaddr_in);
|
||||
sockaddr_in srcAddr;
|
||||
int res=recvfrom(fd, (char*)packet->data, packet->length, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen);
|
||||
if(res!=SOCKET_ERROR)
|
||||
packet->length=(size_t) res;
|
||||
else{
|
||||
LOGE("error receiving %d", WSAGetLastError());
|
||||
packet->length=0;
|
||||
return;
|
||||
}
|
||||
lastRecvdV4=IPv4Address(srcAddr.sin_addr.s_addr);
|
||||
packet->address=&lastRecvdV4;
|
||||
packet->port=ntohs(srcAddr.sin_port);
|
||||
}
|
||||
packet->protocol=PROTO_UDP;
|
||||
}else if(protocol==PROTO_TCP){
|
||||
int res=recv(fd, (char*)packet->data, packet->length, 0);
|
||||
if(res==SOCKET_ERROR){
|
||||
int error=WSAGetLastError();
|
||||
LOGE("Error receiving from TCP socket: %d / %s", error, WindowsSpecific::GetErrorMessage(error).c_str());
|
||||
failed=true;
|
||||
}else{
|
||||
packet->length=(size_t)res;
|
||||
packet->address=tcpConnectedAddress;
|
||||
packet->port=tcpConnectedPort;
|
||||
packet->protocol=PROTO_TCP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::Open(){
|
||||
if(protocol==PROTO_UDP){
|
||||
fd=socket(isAtLeastVista ? AF_INET6 : AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if(fd==INVALID_SOCKET){
|
||||
int error=WSAGetLastError();
|
||||
LOGE("error creating socket: %d", error);
|
||||
failed=true;
|
||||
return;
|
||||
}
|
||||
|
||||
int res;
|
||||
if(isAtLeastVista){
|
||||
DWORD flag=0;
|
||||
res=setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&flag, sizeof(flag));
|
||||
if(res==SOCKET_ERROR){
|
||||
LOGE("error enabling dual stack socket: %d", WSAGetLastError());
|
||||
failed=true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
u_long one=1;
|
||||
ioctlsocket(fd, FIONBIO, &one);
|
||||
|
||||
SetMaxPriority();
|
||||
|
||||
int tries=0;
|
||||
sockaddr* addr;
|
||||
sockaddr_in addr4;
|
||||
sockaddr_in6 addr6;
|
||||
int addrLen;
|
||||
if(isAtLeastVista){
|
||||
//addr.sin6_addr.s_addr=0;
|
||||
memset(&addr6, 0, sizeof(sockaddr_in6));
|
||||
//addr.sin6_len=sizeof(sa_family_t);
|
||||
addr6.sin6_family=AF_INET6;
|
||||
addr=(sockaddr*)&addr6;
|
||||
addrLen=sizeof(addr6);
|
||||
}else{
|
||||
sockaddr_in addr4;
|
||||
addr4.sin_addr.s_addr=0;
|
||||
addr4.sin_family=AF_INET;
|
||||
addr=(sockaddr*)&addr4;
|
||||
addrLen=sizeof(addr4);
|
||||
}
|
||||
for(tries=0;tries<10;tries++){
|
||||
uint16_t port=htons(GenerateLocalPort());
|
||||
if(isAtLeastVista)
|
||||
((sockaddr_in6*)addr)->sin6_port=port;
|
||||
else
|
||||
((sockaddr_in*)addr)->sin_port=port;
|
||||
res=::bind(fd, addr, addrLen);
|
||||
LOGV("trying bind to port %u", ntohs(port));
|
||||
if(res<0){
|
||||
LOGE("error binding to port %u: %d / %s", ntohs(port), errno, strerror(errno));
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(tries==10){
|
||||
if(isAtLeastVista)
|
||||
((sockaddr_in6*)addr)->sin6_port=0;
|
||||
else
|
||||
((sockaddr_in*)addr)->sin_port=0;
|
||||
res=::bind(fd, addr, addrLen);
|
||||
if(res<0){
|
||||
LOGE("error binding to port %u: %d / %s", 0, errno, strerror(errno));
|
||||
//SetState(STATE_FAILED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
getsockname(fd, addr, (socklen_t*) &addrLen);
|
||||
uint16_t localUdpPort;
|
||||
if(isAtLeastVista)
|
||||
localUdpPort=ntohs(((sockaddr_in6*)addr)->sin6_port);
|
||||
else
|
||||
localUdpPort=ntohs(((sockaddr_in*)addr)->sin_port);
|
||||
LOGD("Bound to local UDP port %u", localUdpPort);
|
||||
|
||||
needUpdateNat64Prefix=true;
|
||||
isV4Available=false;
|
||||
switchToV6at=VoIPController::GetCurrentTime()+ipv6Timeout;
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::Close(){
|
||||
closing=true;
|
||||
failed=true;
|
||||
if(fd!=INVALID_SOCKET)
|
||||
closesocket(fd);
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::OnActiveInterfaceChanged(){
|
||||
needUpdateNat64Prefix=true;
|
||||
isV4Available=false;
|
||||
switchToV6at=VoIPController::GetCurrentTime()+ipv6Timeout;
|
||||
}
|
||||
|
||||
std::string NetworkSocketWinsock::GetLocalInterfaceInfo(IPv4Address *v4addr, IPv6Address *v6addr){
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
Windows::Networking::Connectivity::ConnectionProfile^ profile=Windows::Networking::Connectivity::NetworkInformation::GetInternetConnectionProfile();
|
||||
if(profile){
|
||||
Windows::Foundation::Collections::IVectorView<Windows::Networking::HostName^>^ hostnames=Windows::Networking::Connectivity::NetworkInformation::GetHostNames();
|
||||
for(unsigned int i=0;i<hostnames->Size;i++){
|
||||
Windows::Networking::HostName^ n = hostnames->GetAt(i);
|
||||
if(n->Type!=Windows::Networking::HostNameType::Ipv4 && n->Type!=Windows::Networking::HostNameType::Ipv6)
|
||||
continue;
|
||||
if(n->IPInformation->NetworkAdapter->Equals(profile->NetworkAdapter)){
|
||||
if(v4addr && n->Type==Windows::Networking::HostNameType::Ipv4){
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
WideCharToMultiByte(CP_UTF8, 0, n->RawName->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
*v4addr=IPv4Address(buf);
|
||||
}else if(v6addr && n->Type==Windows::Networking::HostNameType::Ipv6){
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
WideCharToMultiByte(CP_UTF8, 0, n->RawName->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
*v6addr=IPv6Address(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
char buf[128];
|
||||
WideCharToMultiByte(CP_UTF8, 0, profile->NetworkAdapter->NetworkAdapterId.ToString()->Data(), -1, buf, sizeof(buf), NULL, NULL);
|
||||
return std::string(buf);
|
||||
}
|
||||
return "";
|
||||
#else
|
||||
IP_ADAPTER_ADDRESSES* addrs=(IP_ADAPTER_ADDRESSES*)malloc(15*1024);
|
||||
ULONG size=15*1024;
|
||||
ULONG flags=GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
|
||||
|
||||
ULONG res=GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addrs, &size);
|
||||
if(res==ERROR_BUFFER_OVERFLOW){
|
||||
addrs=(IP_ADAPTER_ADDRESSES*)realloc(addrs, size);
|
||||
res=GetAdaptersAddresses(AF_UNSPEC, flags, NULL, addrs, &size);
|
||||
}
|
||||
|
||||
ULONG bestMetric=0;
|
||||
std::string bestName("");
|
||||
|
||||
if(res==ERROR_SUCCESS){
|
||||
IP_ADAPTER_ADDRESSES* current=addrs;
|
||||
while(current){
|
||||
char* name=current->AdapterName;
|
||||
LOGV("Adapter '%s':", name);
|
||||
IP_ADAPTER_UNICAST_ADDRESS* curAddr=current->FirstUnicastAddress;
|
||||
if(current->OperStatus!=IfOperStatusUp){
|
||||
LOGV("-> (down)");
|
||||
current=current->Next;
|
||||
continue;
|
||||
}
|
||||
if(current->IfType==IF_TYPE_SOFTWARE_LOOPBACK){
|
||||
LOGV("-> (loopback)");
|
||||
current=current->Next;
|
||||
continue;
|
||||
}
|
||||
if(isAtLeastVista)
|
||||
LOGV("v4 metric: %u, v6 metric: %u", current->Ipv4Metric, current->Ipv6Metric);
|
||||
while(curAddr){
|
||||
sockaddr* addr=curAddr->Address.lpSockaddr;
|
||||
if(addr->sa_family==AF_INET && v4addr){
|
||||
sockaddr_in* ipv4=(sockaddr_in*)addr;
|
||||
LOGV("-> V4: %s", V4AddressToString(ipv4->sin_addr.s_addr).c_str());
|
||||
uint32_t ip=ntohl(ipv4->sin_addr.s_addr);
|
||||
if((ip & 0xFFFF0000)!=0xA9FE0000){
|
||||
if(isAtLeastVista){
|
||||
if(current->Ipv4Metric>bestMetric){
|
||||
bestMetric=current->Ipv4Metric;
|
||||
bestName=std::string(current->AdapterName);
|
||||
*v4addr=IPv4Address(ipv4->sin_addr.s_addr);
|
||||
}
|
||||
}else{
|
||||
bestName=std::string(current->AdapterName);
|
||||
*v4addr=IPv4Address(ipv4->sin_addr.s_addr);
|
||||
}
|
||||
}
|
||||
}else if(addr->sa_family==AF_INET6 && v6addr){
|
||||
sockaddr_in6* ipv6=(sockaddr_in6*)addr;
|
||||
LOGV("-> V6: %s", V6AddressToString(ipv6->sin6_addr.s6_addr).c_str());
|
||||
if(!IN6_IS_ADDR_LINKLOCAL(&ipv6->sin6_addr)){
|
||||
*v6addr=IPv6Address(ipv6->sin6_addr.s6_addr);
|
||||
}
|
||||
}
|
||||
curAddr=curAddr->Next;
|
||||
}
|
||||
current=current->Next;
|
||||
}
|
||||
}
|
||||
|
||||
free(addrs);
|
||||
return bestName;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t NetworkSocketWinsock::GetLocalPort(){
|
||||
if(!isAtLeastVista){
|
||||
sockaddr_in addr;
|
||||
size_t addrLen=sizeof(sockaddr_in);
|
||||
getsockname(fd, (sockaddr*)&addr, (socklen_t*)&addrLen);
|
||||
return ntohs(addr.sin_port);
|
||||
}
|
||||
sockaddr_in6 addr;
|
||||
size_t addrLen=sizeof(sockaddr_in6);
|
||||
getsockname(fd, (sockaddr*)&addr, (socklen_t*) &addrLen);
|
||||
return ntohs(addr.sin6_port);
|
||||
}
|
||||
|
||||
std::string NetworkSocketWinsock::V4AddressToString(uint32_t address){
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
sockaddr_in addr;
|
||||
ZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin_family=AF_INET;
|
||||
addr.sin_addr.s_addr=address;
|
||||
DWORD len=sizeof(buf);
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
wchar_t wbuf[INET_ADDRSTRLEN];
|
||||
ZeroMemory(wbuf, sizeof(wbuf));
|
||||
WSAAddressToStringW((sockaddr*)&addr, sizeof(addr), NULL, wbuf, &len);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, sizeof(buf), NULL, NULL);
|
||||
#else
|
||||
WSAAddressToStringA((sockaddr*)&addr, sizeof(addr), NULL, buf, &len);
|
||||
#endif
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
std::string NetworkSocketWinsock::V6AddressToString(const unsigned char *address){
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
sockaddr_in6 addr;
|
||||
ZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin6_family=AF_INET6;
|
||||
memcpy(addr.sin6_addr.s6_addr, address, 16);
|
||||
DWORD len=sizeof(buf);
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
wchar_t wbuf[INET6_ADDRSTRLEN];
|
||||
ZeroMemory(wbuf, sizeof(wbuf));
|
||||
WSAAddressToStringW((sockaddr*)&addr, sizeof(addr), NULL, wbuf, &len);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, sizeof(buf), NULL, NULL);
|
||||
#else
|
||||
WSAAddressToStringA((sockaddr*)&addr, sizeof(addr), NULL, buf, &len);
|
||||
#endif
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
uint32_t NetworkSocketWinsock::StringToV4Address(std::string address){
|
||||
sockaddr_in addr;
|
||||
ZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin_family=AF_INET;
|
||||
int size=sizeof(addr);
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
wchar_t buf[INET_ADDRSTRLEN];
|
||||
MultiByteToWideChar(CP_UTF8, 0, address.c_str(), -1, buf, INET_ADDRSTRLEN);
|
||||
WSAStringToAddressW(buf, AF_INET, NULL, (sockaddr*)&addr, &size);
|
||||
#else
|
||||
WSAStringToAddressA((char*)address.c_str(), AF_INET, NULL, (sockaddr*)&addr, &size);
|
||||
#endif
|
||||
return addr.sin_addr.s_addr;
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::StringToV6Address(std::string address, unsigned char *out){
|
||||
sockaddr_in6 addr;
|
||||
ZeroMemory(&addr, sizeof(addr));
|
||||
addr.sin6_family=AF_INET6;
|
||||
int size=sizeof(addr);
|
||||
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
|
||||
wchar_t buf[INET6_ADDRSTRLEN];
|
||||
MultiByteToWideChar(CP_UTF8, 0, address.c_str(), -1, buf, INET6_ADDRSTRLEN);
|
||||
WSAStringToAddressW(buf, AF_INET, NULL, (sockaddr*)&addr, &size);
|
||||
#else
|
||||
WSAStringToAddressA((char*)address.c_str(), AF_INET, NULL, (sockaddr*)&addr, &size);
|
||||
#endif
|
||||
memcpy(out, addr.sin6_addr.s6_addr, 16);
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::Connect(const NetworkAddress *address, uint16_t port){
|
||||
const IPv4Address* v4addr=dynamic_cast<const IPv4Address*>(address);
|
||||
const IPv6Address* v6addr=dynamic_cast<const IPv6Address*>(address);
|
||||
sockaddr_in v4;
|
||||
sockaddr_in6 v6;
|
||||
sockaddr* addr=NULL;
|
||||
size_t addrLen=0;
|
||||
if(v4addr){
|
||||
v4.sin_family=AF_INET;
|
||||
v4.sin_addr.s_addr=v4addr->GetAddress();
|
||||
v4.sin_port=htons(port);
|
||||
addr=reinterpret_cast<sockaddr*>(&v4);
|
||||
addrLen=sizeof(v4);
|
||||
}else if(v6addr){
|
||||
v6.sin6_family=AF_INET6;
|
||||
memcpy(v6.sin6_addr.s6_addr, v6addr->GetAddress(), 16);
|
||||
v6.sin6_flowinfo=0;
|
||||
v6.sin6_scope_id=0;
|
||||
v6.sin6_port=htons(port);
|
||||
addr=reinterpret_cast<sockaddr*>(&v6);
|
||||
addrLen=sizeof(v6);
|
||||
}else{
|
||||
LOGE("Unknown address type in TCP connect");
|
||||
failed=true;
|
||||
return;
|
||||
}
|
||||
fd=socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(fd==INVALID_SOCKET){
|
||||
LOGE("Error creating TCP socket: %d", WSAGetLastError());
|
||||
failed=true;
|
||||
return;
|
||||
}
|
||||
u_long one=1;
|
||||
ioctlsocket(fd, FIONBIO, &one);
|
||||
int opt=1;
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt));
|
||||
DWORD timeout=5000;
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));
|
||||
timeout=60000;
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
|
||||
int res=connect(fd, (const sockaddr*) addr, addrLen);
|
||||
if(res!=0){
|
||||
int error=WSAGetLastError();
|
||||
if(error!=WSAEINPROGRESS && error!=WSAEWOULDBLOCK){
|
||||
LOGW("error connecting TCP socket to %s:%u: %d / %s", address->ToString().c_str(), port, error, WindowsSpecific::GetErrorMessage(error).c_str());
|
||||
closesocket(fd);
|
||||
failed=true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
tcpConnectedAddress=v4addr ? (NetworkAddress*)new IPv4Address(*v4addr) : (NetworkAddress*)new IPv6Address(*v6addr);
|
||||
tcpConnectedPort=port;
|
||||
LOGI("successfully connected to %s:%d", tcpConnectedAddress->ToString().c_str(), tcpConnectedPort);
|
||||
}
|
||||
|
||||
IPv4Address *NetworkSocketWinsock::ResolveDomainName(std::string name){
|
||||
addrinfo* addr0;
|
||||
IPv4Address* ret=NULL;
|
||||
int res=getaddrinfo(name.c_str(), NULL, NULL, &addr0);
|
||||
if(res!=0){
|
||||
LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res));
|
||||
}else{
|
||||
addrinfo* addrPtr;
|
||||
for(addrPtr=addr0;addrPtr;addrPtr=addrPtr->ai_next){
|
||||
if(addrPtr->ai_family==AF_INET){
|
||||
sockaddr_in* addr=(sockaddr_in*)addrPtr->ai_addr;
|
||||
ret=new IPv4Address(addr->sin_addr.s_addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(addr0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
NetworkAddress *NetworkSocketWinsock::GetConnectedAddress(){
|
||||
return tcpConnectedAddress;
|
||||
}
|
||||
|
||||
uint16_t NetworkSocketWinsock::GetConnectedPort(){
|
||||
return tcpConnectedPort;
|
||||
}
|
||||
|
||||
void NetworkSocketWinsock::SetTimeouts(int sendTimeout, int recvTimeout){
|
||||
DWORD timeout=sendTimeout*1000;
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));
|
||||
timeout=recvTimeout*1000;
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
|
||||
}
|
||||
|
||||
bool NetworkSocketWinsock::Select(std::vector<NetworkSocket*> &readFds, std::vector<NetworkSocket*>& writeFds, std::vector<NetworkSocket*> &errorFds, SocketSelectCanceller* _canceller){
|
||||
fd_set readSet;
|
||||
fd_set errorSet;
|
||||
fd_set writeSet;
|
||||
SocketSelectCancellerWin32* canceller=dynamic_cast<SocketSelectCancellerWin32*>(_canceller);
|
||||
timeval timeout={0, 10000};
|
||||
bool anyFailed=false;
|
||||
int res=0;
|
||||
|
||||
do{
|
||||
FD_ZERO(&readSet);
|
||||
FD_ZERO(&writeSet);
|
||||
FD_ZERO(&errorSet);
|
||||
|
||||
for(std::vector<NetworkSocket*>::iterator itr=readFds.begin();itr!=readFds.end();++itr){
|
||||
int sfd=GetDescriptorFromSocket(*itr);
|
||||
if(sfd==0){
|
||||
LOGW("can't select on one of sockets because it's not a NetworkSocketWinsock instance");
|
||||
continue;
|
||||
}
|
||||
FD_SET(sfd, &readSet);
|
||||
}
|
||||
|
||||
for(NetworkSocket*& s:writeFds){
|
||||
int sfd=GetDescriptorFromSocket(s);
|
||||
if(sfd==0){
|
||||
LOGW("can't select on one of sockets because it's not a NetworkSocketWinsock instance");
|
||||
continue;
|
||||
}
|
||||
FD_SET(sfd, &writeSet);
|
||||
}
|
||||
|
||||
for(std::vector<NetworkSocket*>::iterator itr=errorFds.begin();itr!=errorFds.end();++itr){
|
||||
int sfd=GetDescriptorFromSocket(*itr);
|
||||
if(sfd==0){
|
||||
LOGW("can't select on one of sockets because it's not a NetworkSocketWinsock instance");
|
||||
continue;
|
||||
}
|
||||
if((*itr)->timeout>0 && VoIPController::GetCurrentTime()-(*itr)->lastSuccessfulOperationTime>(*itr)->timeout){
|
||||
LOGW("Socket %d timed out", sfd);
|
||||
(*itr)->failed=true;
|
||||
}
|
||||
anyFailed |= (*itr)->IsFailed();
|
||||
FD_SET(sfd, &errorSet);
|
||||
}
|
||||
if(canceller && canceller->canceled)
|
||||
break;
|
||||
res=select(0, &readSet, &writeSet, &errorSet, &timeout);
|
||||
//LOGV("select result %d", res);
|
||||
if(res==SOCKET_ERROR)
|
||||
LOGE("SELECT ERROR %d", WSAGetLastError());
|
||||
}while(res==0);
|
||||
|
||||
|
||||
if(canceller && canceller->canceled && !anyFailed){
|
||||
canceller->canceled=false;
|
||||
return false;
|
||||
}else if(anyFailed){
|
||||
FD_ZERO(&readSet);
|
||||
FD_ZERO(&errorSet);
|
||||
}
|
||||
|
||||
std::vector<NetworkSocket*>::iterator itr=readFds.begin();
|
||||
while(itr!=readFds.end()){
|
||||
int sfd=GetDescriptorFromSocket(*itr);
|
||||
if(FD_ISSET(sfd, &readSet))
|
||||
(*itr)->lastSuccessfulOperationTime=VoIPController::GetCurrentTime();
|
||||
if(sfd==0 || !FD_ISSET(sfd, &readSet) || !(*itr)->OnReadyToReceive()){
|
||||
itr=readFds.erase(itr);
|
||||
}else{
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
itr=writeFds.begin();
|
||||
while(itr!=writeFds.end()){
|
||||
int sfd=GetDescriptorFromSocket(*itr);
|
||||
if(FD_ISSET(sfd, &writeSet)){
|
||||
(*itr)->lastSuccessfulOperationTime=VoIPController::GetCurrentTime();
|
||||
LOGI("Socket %d is ready to send", sfd);
|
||||
}
|
||||
if(sfd==0 || !FD_ISSET(sfd, &writeSet) || !(*itr)->OnReadyToSend()){
|
||||
itr=writeFds.erase(itr);
|
||||
}else{
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
itr=errorFds.begin();
|
||||
while(itr!=errorFds.end()){
|
||||
int sfd=GetDescriptorFromSocket(*itr);
|
||||
if((sfd==0 || !FD_ISSET(sfd, &errorSet)) && !(*itr)->IsFailed()){
|
||||
itr=errorFds.erase(itr);
|
||||
}else{
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
//LOGV("select fds left: read=%d, error=%d", readFds.size(), errorFds.size());
|
||||
|
||||
return readFds.size()>0 || errorFds.size()>0;
|
||||
}
|
||||
|
||||
SocketSelectCancellerWin32::SocketSelectCancellerWin32(){
|
||||
canceled=false;
|
||||
}
|
||||
|
||||
SocketSelectCancellerWin32::~SocketSelectCancellerWin32(){
|
||||
}
|
||||
|
||||
void SocketSelectCancellerWin32::CancelSelect(){
|
||||
canceled=true;
|
||||
}
|
||||
|
||||
int NetworkSocketWinsock::GetDescriptorFromSocket(NetworkSocket *socket){
|
||||
NetworkSocketWinsock* sp=dynamic_cast<NetworkSocketWinsock*>(socket);
|
||||
if(sp)
|
||||
return sp->fd;
|
||||
NetworkSocketWrapper* sw=dynamic_cast<NetworkSocketWrapper*>(socket);
|
||||
if(sw)
|
||||
return GetDescriptorFromSocket(sw->GetWrapped());
|
||||
return 0;
|
||||
}
|
||||
72
TMessagesProj/jni/voip/libtgvoip/os/windows/NetworkSocketWinsock.h
Executable file
72
TMessagesProj/jni/voip/libtgvoip/os/windows/NetworkSocketWinsock.h
Executable file
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_NETWORKSOCKETWINSOCK_H
|
||||
#define LIBTGVOIP_NETWORKSOCKETWINSOCK_H
|
||||
|
||||
#include "../../NetworkSocket.h"
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace tgvoip {
|
||||
class Buffer;
|
||||
|
||||
class SocketSelectCancellerWin32 : public SocketSelectCanceller{
|
||||
friend class NetworkSocketWinsock;
|
||||
public:
|
||||
SocketSelectCancellerWin32();
|
||||
virtual ~SocketSelectCancellerWin32();
|
||||
virtual void CancelSelect();
|
||||
private:
|
||||
bool canceled;
|
||||
};
|
||||
|
||||
class NetworkSocketWinsock : public NetworkSocket{
|
||||
public:
|
||||
NetworkSocketWinsock(NetworkProtocol protocol);
|
||||
virtual ~NetworkSocketWinsock();
|
||||
virtual void Send(NetworkPacket* packet);
|
||||
virtual void Receive(NetworkPacket* packet);
|
||||
virtual void Open();
|
||||
virtual void Close();
|
||||
virtual std::string GetLocalInterfaceInfo(IPv4Address* v4addr, IPv6Address* v6addr);
|
||||
virtual void OnActiveInterfaceChanged();
|
||||
virtual uint16_t GetLocalPort();
|
||||
virtual void Connect(const NetworkAddress* address, uint16_t port);
|
||||
|
||||
static std::string V4AddressToString(uint32_t address);
|
||||
static std::string V6AddressToString(const unsigned char address[16]);
|
||||
static uint32_t StringToV4Address(std::string address);
|
||||
static void StringToV6Address(std::string address, unsigned char* out);
|
||||
static IPv4Address* ResolveDomainName(std::string name);
|
||||
static bool Select(std::vector<NetworkSocket*>& readFds, std::vector<NetworkSocket*>& writeFds, std::vector<NetworkSocket*>& errorFds, SocketSelectCanceller* canceller);
|
||||
virtual NetworkAddress *GetConnectedAddress();
|
||||
virtual uint16_t GetConnectedPort();
|
||||
virtual void SetTimeouts(int sendTimeout, int recvTimeout);
|
||||
virtual bool OnReadyToSend() override;
|
||||
|
||||
protected:
|
||||
virtual void SetMaxPriority();
|
||||
|
||||
private:
|
||||
static int GetDescriptorFromSocket(NetworkSocket* socket);
|
||||
uintptr_t fd;
|
||||
bool needUpdateNat64Prefix;
|
||||
bool nat64Present;
|
||||
double switchToV6at;
|
||||
bool isV4Available;
|
||||
IPv4Address lastRecvdV4;
|
||||
IPv6Address lastRecvdV6;
|
||||
bool isAtLeastVista;
|
||||
bool closing;
|
||||
NetworkAddress* tcpConnectedAddress;
|
||||
uint16_t tcpConnectedPort;
|
||||
Buffer* pendingOutgoingPacket=NULL;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //LIBTGVOIP_NETWORKSOCKETWINSOCK_H
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include "WindowsSandboxUtils.h"
|
||||
#include <audioclient.h>
|
||||
#include <windows.h>
|
||||
#ifdef TGVOIP_WP_SILVERLIGHT
|
||||
#include <phoneaudioclient.h>
|
||||
#endif
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
|
||||
IAudioClient2* WindowsSandboxUtils::ActivateAudioDevice(const wchar_t* devID, HRESULT* callRes, HRESULT* actRes) {
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
// Did I say that I hate pointlessly asynchronous things?
|
||||
HANDLE event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
|
||||
ActivationHandler activationHandler(event);
|
||||
IActivateAudioInterfaceAsyncOperation* actHandler;
|
||||
HRESULT cr = ActivateAudioInterfaceAsync(devID, __uuidof(IAudioClient2), NULL, (IActivateAudioInterfaceCompletionHandler*)&activationHandler, &actHandler);
|
||||
if (callRes)
|
||||
*callRes = cr;
|
||||
DWORD resulttt = WaitForSingleObjectEx(event, INFINITE, false);
|
||||
DWORD last = GetLastError();
|
||||
CloseHandle(event);
|
||||
if (actRes)
|
||||
*actRes = activationHandler.actResult;
|
||||
return activationHandler.client;
|
||||
#else
|
||||
IAudioClient2* client;
|
||||
HRESULT res=ActivateAudioInterface(devID, __uuidof(IAudioClient2), (void**)&client);
|
||||
if(callRes)
|
||||
*callRes=S_OK;
|
||||
if(actRes)
|
||||
*actRes=res;
|
||||
return client;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
ActivationHandler::ActivationHandler(HANDLE _event) : event(_event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
STDMETHODIMP ActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation * operation)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
HRESULT hrActivateResult = S_OK;
|
||||
IUnknown *punkAudioInterface = nullptr;
|
||||
|
||||
hr = operation->GetActivateResult(&hrActivateResult, &punkAudioInterface);
|
||||
if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult))
|
||||
{
|
||||
punkAudioInterface->QueryInterface(IID_PPV_ARGS(&client));
|
||||
}
|
||||
|
||||
SetEvent(event);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
#define LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
|
||||
#include <audioclient.h>
|
||||
#include <windows.h>
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
#include <mmdeviceapi.h>
|
||||
#endif
|
||||
#include <wrl.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace tgvoip {
|
||||
|
||||
#ifndef TGVOIP_WP_SILVERLIGHT
|
||||
class ActivationHandler :
|
||||
public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler >
|
||||
{
|
||||
public:
|
||||
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation);
|
||||
|
||||
ActivationHandler(HANDLE _event);
|
||||
HANDLE event;
|
||||
IAudioClient2* client;
|
||||
HRESULT actResult;
|
||||
};
|
||||
#endif
|
||||
|
||||
class WindowsSandboxUtils {
|
||||
public:
|
||||
static IAudioClient2* ActivateAudioDevice(const wchar_t* devID, HRESULT* callResult, HRESULT* actResult);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LIBTGVOIP_WINDOWS_SANDBOX_UTILS
|
||||
9
TMessagesProj/jni/voip/libtgvoip/os/windows/WindowsSpecific.cpp
Executable file
9
TMessagesProj/jni/voip/libtgvoip/os/windows/WindowsSpecific.cpp
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#include "WindowsSpecific.h"
|
||||
|
||||
using namespace tgvoip;
|
||||
|
||||
std::string WindowsSpecific::GetErrorMessage(DWORD code){
|
||||
char buf[1024]={0};
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buf, sizeof(buf), NULL);
|
||||
return std::string(buf);
|
||||
}
|
||||
16
TMessagesProj/jni/voip/libtgvoip/os/windows/WindowsSpecific.h
Executable file
16
TMessagesProj/jni/voip/libtgvoip/os/windows/WindowsSpecific.h
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef LIBTGVOIP_WINDOWS_SPECIFIC_H
|
||||
#define LIBTGVOIP_WINDOWS_SPECIFIC_H
|
||||
|
||||
#include <string>
|
||||
#include <Windows.h>
|
||||
|
||||
namespace tgvoip{
|
||||
|
||||
class WindowsSpecific{
|
||||
public:
|
||||
static std::string GetErrorMessage(DWORD code);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // LIBTGVOIP_WINDOWS_SPECIFIC_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue