Sat Aug 14 12:07:59 Hora de verano romance 2010 bruno@stenyak.com * Runtime device change detection WMME diff -rN -u old-python-sipsimple-1/patches/pjsip-2830-runtime_device_change_detection_wmme.patch new-python-sipsimple-2/patches/pjsip-2830-runtime_device_change_detection_wmme.patch --- old-python-sipsimple-1/patches/pjsip-2830-runtime_device_change_detection_wmme.patch 1970-01-01 00:00:00 +0000 +++ new-python-sipsimple-2/patches/pjsip-2830-runtime_device_change_detection_wmme.patch 2010-08-14 10:13:00 +0000 @@ -0,0 +1,10 @@ +diff -r 72f180130a42 build/temp.win32-2.6/Release/pjsip/third_party/build/portaudio/os-auto.mak.in +--- third_party/build/portaudio/os-auto.mak.in Wed Aug 11 19:38:40 2010 +0200 ++++ third_party/build/portaudio/os-auto.mak.in Sat Aug 14 11:42:35 2010 +0200 +@@ -75,5 +75,5 @@ + ifeq ($(AC_PJMEDIA_SND),pa_win32) + export PORTAUDIO_OBJS += pa_win_hostapis.o pa_win_util.o \ + pa_win_wmme.o pa_win_waveformat.o +-export CFLAGS += -DPA_NO_ASIO -DPA_NO_DS ++export CFLAGS += -DPA_NO_ASIO -DPA_NO_DS -mwindows -static-libstdc++ + endif diff -rN -u old-python-sipsimple-1/patches/portaudio-1420-runtime_device_change_detection_wmme.patch new-python-sipsimple-2/patches/portaudio-1420-runtime_device_change_detection_wmme.patch --- old-python-sipsimple-1/patches/portaudio-1420-runtime_device_change_detection_wmme.patch 1970-01-01 00:00:00 +0000 +++ new-python-sipsimple-2/patches/portaudio-1420-runtime_device_change_detection_wmme.patch 2010-08-14 10:13:00 +0000 @@ -0,0 +1,363 @@ +diff -r 72f180130a42 build/temp.win32-2.6/Release/pjsip/third_party/portaudio/src/hostapi/wmme/pa_win_wmme.c +--- src/hostapi/wmme/pa_win_wmme.c Wed Aug 11 19:38:40 2010 +0200 ++++ src/hostapi/wmme/pa_win_wmme.c Sat Aug 14 11:42:36 2010 +0200 +@@ -117,6 +117,7 @@ + #include + #include + #include ++#include + #include + #ifndef UNDER_CE + #include +@@ -153,9 +154,9 @@ + + /* use CreateThread for CYGWIN, _beginthreadex for all others */ + #ifndef __CYGWIN__ +-#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ) ++#define CREATE_THREAD(ThreadProc, arg, threadId) (HANDLE)_beginthreadex( 0, 0, ThreadProc, arg, 0, threadId ) + #else +-#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ) ++#define CREATE_THREAD(ThreadProc, arg, threadId) CreateThread( 0, 0, ThreadProc, arg, 0, threadId ) + #endif + + #if (defined(UNDER_CE)) +@@ -223,6 +224,8 @@ + #endif /* __cplusplus */ + + static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); ++static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi ); ++static DWORD WINAPI DeviceDetectionThreadProc( void *pArg ); + static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** stream, + const PaStreamParameters *inputParameters, +@@ -1076,7 +1079,7 @@ + InitializeDefaultDeviceIdsFromEnv( winMmeHostApi ); + + (*hostApi)->Terminate = Terminate; +- (*hostApi)->RescanDevices = NULL; ++ (*hostApi)->RescanDevices = RescanDevices; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + +@@ -1091,6 +1094,9 @@ + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + ++ HANDLE deviceDetectionThread; ++ deviceDetectionThread = CREATE_THREAD(DeviceDetectionThreadProc, (void*)(winMmeHostApi), NULL); ++ + return result; + + error: +@@ -2758,6 +2764,302 @@ + return result; + } + ++/* compare stored vs actual list of devices, and update the list if there are ++ new or removed devices. ++*/ ++static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi ) ++{ ++ PaError result = paNoError; ++ int i; ++ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; ++ int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount; ++ PaWinMmeDeviceInfo *deviceInfoArray; ++ int deviceInfoInitializationSucceeded; ++ PaTime defaultLowLatency, defaultHighLatency; ++ PaHostApiIndex hostApiIndex = Pa_HostApiTypeIdToHostApiIndex(hostApi->info.type); ++ ++ maximumPossibleDeviceCount = 0; ++ ++ inputDeviceCount = waveInGetNumDevs(); ++ if( inputDeviceCount > 0 ) maximumPossibleDeviceCount += inputDeviceCount + 1; ++ ++ outputDeviceCount = waveOutGetNumDevs(); ++ if( outputDeviceCount > 0 ) maximumPossibleDeviceCount += outputDeviceCount + 1; ++ ++ if( inputDeviceCount + 1 == winMmeHostApi->inputDeviceCount && outputDeviceCount + 1 == winMmeHostApi->outputDeviceCount) ++ { ++ /* No new devices in list */ ++ return paNoError; ++ } ++ hostApi->info.deviceCount = 0; ++ hostApi->info.defaultInputDevice = paNoDevice; ++ hostApi->info.defaultOutputDevice = paNoDevice; ++ winMmeHostApi->inputDeviceCount = 0; ++ winMmeHostApi->outputDeviceCount = 0; ++ ++ /* WARN: doc says this operation is time consuming */ ++ PaUtil_GroupFreeMemory( winMmeHostApi->allocations, hostApi->deviceInfos ); ++ if( maximumPossibleDeviceCount > 0 ) ++ { ++ hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( ++ winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount ); ++ if( !hostApi->deviceInfos ) return paInsufficientMemory; ++ ++ /* allocate all device info structs in a contiguous block */ ++ deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory( ++ winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount ); ++ if( !deviceInfoArray ) return paInsufficientMemory; ++ ++ ++ winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory( ++ winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount ); ++ if( !winMmeHostApi->winMmeDeviceIds ) return paInsufficientMemory; ++ ++ GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency ); ++ ++ /* Rescan input devices */ ++ if( inputDeviceCount > 0 ) ++ { ++ /* -1 is the WAVE_MAPPER */ ++ for( i = -1; i < inputDeviceCount; ++i ) ++ { ++ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); ++ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ hostApi->info.deviceCount ]; ++ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; ++ deviceInfo->structVersion = 2; ++ deviceInfo->hostApi = hostApiIndex; ++ ++ deviceInfo->maxInputChannels = 0; ++ deviceInfo->maxOutputChannels = 0; ++ ++ deviceInfo->defaultLowInputLatency = defaultLowLatency; ++ deviceInfo->defaultLowOutputLatency = defaultLowLatency; ++ deviceInfo->defaultHighInputLatency = defaultHighLatency; ++ deviceInfo->defaultHighOutputLatency = defaultHighLatency; ++ ++ result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, ++ winMmeDeviceId, &deviceInfoInitializationSucceeded ); ++ if( result != paNoError ) ++ return result; ++ ++ if( deviceInfoInitializationSucceeded ) ++ { ++ if( hostApi->info.defaultInputDevice == paNoDevice ) ++ hostApi->info.defaultInputDevice = hostApi->info.deviceCount; ++ ++ winMmeHostApi->winMmeDeviceIds[ hostApi->info.deviceCount ] = winMmeDeviceId; ++ hostApi->deviceInfos[ hostApi->info.deviceCount ] = deviceInfo; ++ ++ winMmeHostApi->inputDeviceCount++; ++ hostApi->info.deviceCount++; ++ } ++ } ++ } ++ ++ /* Rescan output devices */ ++ if( outputDeviceCount > 0 ) ++ { ++ /* -1 is the WAVE_MAPPER */ ++ for( i = -1; i < outputDeviceCount; ++i ) ++ { ++ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); ++ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ hostApi->info.deviceCount ]; ++ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; ++ deviceInfo->structVersion = 2; ++ deviceInfo->hostApi = hostApiIndex; ++ ++ deviceInfo->maxInputChannels = 0; ++ deviceInfo->maxOutputChannels = 0; ++ ++ deviceInfo->defaultLowInputLatency = defaultLowLatency; ++ deviceInfo->defaultLowOutputLatency = defaultLowLatency; ++ deviceInfo->defaultHighInputLatency = defaultHighLatency; ++ deviceInfo->defaultHighOutputLatency = defaultHighLatency; ++ ++ result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, ++ winMmeDeviceId, &deviceInfoInitializationSucceeded ); ++ if( result != paNoError ) ++ return result; ++ ++ if( deviceInfoInitializationSucceeded ) ++ { ++ if( hostApi->info.defaultOutputDevice == paNoDevice ) ++ hostApi->info.defaultOutputDevice = hostApi->info.deviceCount; ++ ++ winMmeHostApi->winMmeDeviceIds[ hostApi->info.deviceCount ] = winMmeDeviceId; ++ hostApi->deviceInfos[ hostApi->info.deviceCount ] = deviceInfo; ++ ++ winMmeHostApi->outputDeviceCount++; ++ hostApi->info.deviceCount++; ++ } ++ } ++ } ++ } ++ return paNoError; ++} ++ ++/* Processes OS messages arriving at the hWnd window */ ++INT_PTR WINAPI ProcessOSMessage( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) ++{ ++ /* winMmeHostApi is used in order to query the number of audio devices currently handled by PA */ ++ static PaWinMmeHostApiRepresentation *winMmeHostApi_ = NULL; ++ switch( message ) ++ { ++ case WM_CREATE: ++ /* Initialize hostApi pointer on the first run. */ ++ if (winMmeHostApi_ == NULL) ++ { ++ CREATESTRUCT *CrtStrPtr = (CREATESTRUCT *) lParam; ++ winMmeHostApi_ = (PaWinMmeHostApiRepresentation*)(CrtStrPtr->lpCreateParams); ++ } ++ break; ++ case WM_DEVICECHANGE: ++ /* Possible insertion or removal of device. There's some issues: ++ ++ - Some devices/drivers does not trigger arrival nor ++ removecomplete events, but only devnodes_changed events. ++ Therefore, we process all of those type of events. ++ ++ - Some hardware can send many devnodes_changed events at the ++ same time (up to ~15 of such events). These batches are ++ detected using temporal locality, using constMaxBatchPeriod_ ++ and processedTimeStamp_. Once the device is detected, the ++ rest of redundant events are discarded. In order to know if ++ there's a new device or not, actual audio devices count is ++ compared to stored audio devices count (via winMmeHostApi_). ++ A possible improvement would be to process each message in a ++ separate thread. ++ ++ - Hardware takes some time to settle and be recognized by ++ drivers. A small window of time is given in order to account ++ for this (constMaxSettleTime_); ++ ++ Settle time should be slightly lower than batch period. ++ */ ++ if ( wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE || wParam == DBT_DEVNODES_CHANGED ) ++ { ++ const int constMaxBatchPeriod_ = 3; /* seconds */ ++ const int constMaxSettleTime_ = (constMaxBatchPeriod_ * 1000) - 500; /* milliseconds */ ++ ++ /* Initialize reference timestamp on first run, using a past ++ time, so that first event belongs to a new batch. */ ++ static time_t processedTimeStamp_ = 0; ++ if ( processedTimeStamp_ == 0 ) processedTimeStamp_ = time( NULL ) - ( constMaxBatchPeriod_ * 2 ); ++ ++ /* Loop that allows hardware to settle */ ++ int settleTimeLeft = constMaxSettleTime_; ++ while ( settleTimeLeft > 0 ) ++ { ++ /* Check if actual devices lists (I/O) sizes have actually ++ changed before notifying upper levels */ ++ if( waveInGetNumDevs() + 1 != winMmeHostApi_->inputDeviceCount || waveOutGetNumDevs() + 1 != winMmeHostApi_->outputDeviceCount) ++ { ++ /* Hardware actually changed */ ++ PaUtil_DevicesChanged( paUtilHardwareDevicesChanged ); ++ processedTimeStamp_ = time( NULL ); ++ break; ++ } ++ else ++ { ++ /* Hardware hasn't changed [yet] */ ++ //if ( difftime( time( NULL ), processedTimeStamp_ ) < constMaxBatchPeriod_ ) ++ { ++ /* We're still in the same batch of messages, disregard */ ++ //processedTimeStamp_ = time( NULL ); ++ } ++ /* Hardware settling pass... */ ++ Sleep(250); ++ settleTimeLeft -= 250; ++ } ++ } ++ } ++ break; ++ case WM_CLOSE: ++ if ( ! DestroyWindow( hWnd ) ) ++ { ++ PA_DEBUG(("ProcessOSMessage: Couldn't destroy message window.\n")); ++ } ++ break; ++ case WM_DESTROY: ++ PostQuitMessage( 0 ); ++ break; ++ default: ++ break; ++ } ++ return 1; ++} ++ ++ ++/* Creates the window that will receive OS messages */ ++static PaError CreateOSMessagesWindow( PaWinMmeHostApiRepresentation *winMmeHostApi ) ++{ ++ PaError result = paUnanticipatedHostError; ++ ++ /* Set up and register window class */ ++ WNDCLASSEX wndClass; ++ ZeroMemory( &wndClass, sizeof(WNDCLASSEX) ); ++ wndClass.cbSize = sizeof(WNDCLASSEX); ++ wndClass.style = CS_OWNDC; ++ wndClass.lpfnWndProc = (WNDPROC)(ProcessOSMessage); ++ wndClass.hInstance = (HINSTANCE)(GetModuleHandle( 0 )); ++ wndClass.lpszClassName = "DeviceChangeMessageWindow"; ++ ++ if ( RegisterClassEx(&wndClass) ) ++ { ++ /* Create the window that will receive OS messages */ ++ HWND hWnd = CreateWindowEx( 0, "DeviceChangeMessageWindow", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, (LPVOID)(winMmeHostApi) ); ++ if ( hWnd != NULL ) ++ { ++ if ( UpdateWindow( hWnd ) != 0 ) ++ { ++ result = paNoError; ++ } ++ } ++ } ++ ++ return result; ++} ++ ++static PaError DispatchOSMessages() ++{ ++ PaError result = paNoError; ++ MSG msg; ++ int retVal; ++ ++ /* Process OS messages with low cpu-usage wait loop */ ++ while( (retVal = GetMessage( &msg, NULL, 0, 0 ) ) != 0 ) ++ { ++ if ( retVal == -1 ) ++ { ++ PA_DEBUG("DispatchOSMessages: Couldn't process OS message.\n"); ++ result = paUnanticipatedHostError; ++ break; ++ } ++ else ++ { ++ TranslateMessage( &msg ); ++ DispatchMessage( &msg ); ++ } ++ } ++ ++ return result; ++} ++static DWORD WINAPI DeviceDetectionThreadProc( void *pArg ) ++{ ++ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)(pArg); ++ DWORD result = -1; ++ ++ if ( CreateOSMessagesWindow(winMmeHostApi) == paNoError ) ++ { ++ if ( DispatchOSMessages() == paNoError ) ++ { ++ result = 0; ++ } ++ } ++ ++ return result; ++} ++ + + static DWORD WINAPI ProcessingThreadProc( void *pArg ) + { +@@ -3312,7 +3614,7 @@ + if( result != paNoError ) goto error; + + /* Create thread that waits for audio buffers to be ready for processing. */ +- stream->processingThread = CREATE_THREAD; ++ stream->processingThread = CREATE_THREAD(ProcessingThreadProc, stream, &stream->processingThreadId); + if( !stream->processingThread ) + { + result = paUnanticipatedHostError; diff -rN -u old-python-sipsimple-1/setup_pjsip.py new-python-sipsimple-2/setup_pjsip.py --- old-python-sipsimple-1/setup_pjsip.py 2010-08-14 10:13:00 +0000 +++ new-python-sipsimple-2/setup_pjsip.py 2010-08-14 10:13:00 +0000 @@ -109,12 +109,14 @@ "patches/pjsip-2830-dont_accept_sdp_everywhere.patch", "patches/pjsip-2830-allocate_thread_desc_from_pool.patch", "patches/pjsip-2830-do_not_close_stream_too_fast.patch", - "patches/pjsip-2830-hide_route_header.patch"] + "patches/pjsip-2830-hide_route_header.patch", + "patches/pjsip-2830-runtime_device_change_detection_wmme.patch"] pjsip_svn_repos = {"trunk": "http://svn.pjsip.org/repos/pjproject/trunk", "1.0": "http://svn.pjsip.org/repos/pjproject/branches/1.0"} portaudio_patch_files = ["patches/portaudio-1420-runtime_device_change_detection.patch", "patches/portaudio-1420-compile_snow_leopard.patch", - "patches/portaudio-1420-pa_mac_core_x64_assert_fix.patch"] + "patches/portaudio-1420-pa_mac_core_x64_assert_fix.patch", + "patches/portaudio-1420-runtime_device_change_detection_wmme.patch"] trunk_overrides = []