runtime_device_change_detection_wmme.patch

Runtime Device Change Detection with WMME hostapi - , 08/14/2010 12:35 pm

Download (17.8 kB)

new-python-sipsimple-2/patches/pjsip-2830-runtime_device_change_detection_wmme.patch 2010-08-14 10:13:00 +0000
1
diff -r 72f180130a42 build/temp.win32-2.6/Release/pjsip/third_party/build/portaudio/os-auto.mak.in
2
--- third_party/build/portaudio/os-auto.mak.in	Wed Aug 11 19:38:40 2010 +0200
3
+++ third_party/build/portaudio/os-auto.mak.in	Sat Aug 14 11:42:35 2010 +0200
4
@@ -75,5 +75,5 @@
5
 ifeq ($(AC_PJMEDIA_SND),pa_win32)
6
 export PORTAUDIO_OBJS += pa_win_hostapis.o pa_win_util.o \
7
 		       pa_win_wmme.o pa_win_waveformat.o
8
-export CFLAGS += -DPA_NO_ASIO -DPA_NO_DS
9
+export CFLAGS += -DPA_NO_ASIO -DPA_NO_DS -mwindows -static-libstdc++
10
 endif
new-python-sipsimple-2/patches/portaudio-1420-runtime_device_change_detection_wmme.patch 2010-08-14 10:13:00 +0000
1
diff -r 72f180130a42 build/temp.win32-2.6/Release/pjsip/third_party/portaudio/src/hostapi/wmme/pa_win_wmme.c
2
--- src/hostapi/wmme/pa_win_wmme.c	Wed Aug 11 19:38:40 2010 +0200
3
+++ src/hostapi/wmme/pa_win_wmme.c	Sat Aug 14 11:42:36 2010 +0200
4
@@ -117,6 +117,7 @@
5
 #include <stdlib.h>
6
 #include <math.h>
7
 #include <windows.h>
8
+#include <dbt.h>
9
 #include <mmsystem.h>
10
 #ifndef UNDER_CE
11
 #include <process.h>
12
@@ -153,9 +154,9 @@
13
 
14
 /* use CreateThread for CYGWIN, _beginthreadex for all others */
15
 #ifndef __CYGWIN__
16
-#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
17
+#define CREATE_THREAD(ThreadProc, arg, threadId) (HANDLE)_beginthreadex( 0, 0, ThreadProc, arg, 0, threadId )
18
 #else
19
-#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
20
+#define CREATE_THREAD(ThreadProc, arg, threadId) CreateThread( 0, 0, ThreadProc, arg, 0, threadId                    )
21
 #endif
22
 
23
 #if (defined(UNDER_CE))
24
@@ -223,6 +224,8 @@
25
 #endif /* __cplusplus */
26
 
27
 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
28
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi );
29
+static DWORD WINAPI DeviceDetectionThreadProc( void *pArg );
30
 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
31
                            PaStream** stream,
32
                            const PaStreamParameters *inputParameters,
33
@@ -1076,7 +1079,7 @@
34
     InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
35
 
36
     (*hostApi)->Terminate = Terminate;
37
-    (*hostApi)->RescanDevices = NULL;
38
+    (*hostApi)->RescanDevices = RescanDevices;
39
     (*hostApi)->OpenStream = OpenStream;
40
     (*hostApi)->IsFormatSupported = IsFormatSupported;
41
 
42
@@ -1091,6 +1094,9 @@
43
                                       GetStreamTime, PaUtil_DummyGetCpuLoad,
44
                                       ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
45
 
46
+    HANDLE deviceDetectionThread;
47
+    deviceDetectionThread = CREATE_THREAD(DeviceDetectionThreadProc, (void*)(winMmeHostApi), NULL);
48
+
49
     return result;
50
 
51
 error:
52
@@ -2758,6 +2764,302 @@
53
     return result;
54
 }
55
 
56
+/* compare stored vs actual list of devices, and update the list if there are
57
+    new or removed devices.
58
+*/
59
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi )
60
+{ 
61
+    PaError result = paNoError;
62
+    int i;
63
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
64
+    int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
65
+    PaWinMmeDeviceInfo *deviceInfoArray;
66
+    int deviceInfoInitializationSucceeded;
67
+    PaTime defaultLowLatency, defaultHighLatency;
68
+    PaHostApiIndex hostApiIndex = Pa_HostApiTypeIdToHostApiIndex(hostApi->info.type);
69
+
70
+    maximumPossibleDeviceCount = 0;
71
+
72
+    inputDeviceCount = waveInGetNumDevs();
73
+    if( inputDeviceCount > 0 ) maximumPossibleDeviceCount += inputDeviceCount + 1;
74
+
75
+    outputDeviceCount = waveOutGetNumDevs();
76
+    if( outputDeviceCount > 0 ) maximumPossibleDeviceCount += outputDeviceCount + 1;
77
+
78
+    if( inputDeviceCount + 1 == winMmeHostApi->inputDeviceCount && outputDeviceCount + 1 == winMmeHostApi->outputDeviceCount)
79
+    {
80
+        /* No new devices in list */
81
+        return paNoError;
82
+    }
83
+    hostApi->info.deviceCount = 0;
84
+    hostApi->info.defaultInputDevice = paNoDevice;
85
+    hostApi->info.defaultOutputDevice = paNoDevice;
86
+    winMmeHostApi->inputDeviceCount = 0;
87
+    winMmeHostApi->outputDeviceCount = 0;
88
+
89
+    /* WARN: doc says this operation is time consuming */
90
+    PaUtil_GroupFreeMemory( winMmeHostApi->allocations, hostApi->deviceInfos );
91
+    if( maximumPossibleDeviceCount > 0 )
92
+    {
93
+        hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
94
+                winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
95
+        if( !hostApi->deviceInfos ) return paInsufficientMemory;
96
+
97
+        /* allocate all device info structs in a contiguous block */
98
+        deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
99
+                winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
100
+        if( !deviceInfoArray ) return paInsufficientMemory;
101
+
102
+
103
+        winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
104
+                winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
105
+        if( !winMmeHostApi->winMmeDeviceIds ) return paInsufficientMemory;
106
+
107
+        GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
108
+
109
+        /* Rescan input devices */
110
+        if( inputDeviceCount > 0 )
111
+        {
112
+            /* -1 is the WAVE_MAPPER */ 
113
+            for( i = -1; i < inputDeviceCount; ++i )
114
+            {
115
+                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
116
+                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ hostApi->info.deviceCount ];
117
+                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
118
+                deviceInfo->structVersion = 2;
119
+                deviceInfo->hostApi = hostApiIndex;
120
+
121
+                deviceInfo->maxInputChannels = 0;
122
+                deviceInfo->maxOutputChannels = 0;
123
+
124
+                deviceInfo->defaultLowInputLatency = defaultLowLatency;
125
+                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
126
+                deviceInfo->defaultHighInputLatency = defaultHighLatency;
127
+                deviceInfo->defaultHighOutputLatency = defaultHighLatency;
128
+
129
+                result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
130
+                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
131
+                if( result != paNoError )
132
+                    return result;
133
+
134
+                if( deviceInfoInitializationSucceeded )
135
+                {
136
+                    if( hostApi->info.defaultInputDevice == paNoDevice )
137
+                        hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
138
+
139
+                    winMmeHostApi->winMmeDeviceIds[ hostApi->info.deviceCount ] = winMmeDeviceId;
140
+                    hostApi->deviceInfos[ hostApi->info.deviceCount ] = deviceInfo;
141
+
142
+                    winMmeHostApi->inputDeviceCount++;
143
+                    hostApi->info.deviceCount++;
144
+                }
145
+            }
146
+        }
147
+
148
+        /* Rescan output devices */
149
+        if( outputDeviceCount > 0 )
150
+        {
151
+            /* -1 is the WAVE_MAPPER */
152
+            for( i = -1; i < outputDeviceCount; ++i )
153
+            {
154
+                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
155
+                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ hostApi->info.deviceCount ];
156
+                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
157
+                deviceInfo->structVersion = 2;
158
+                deviceInfo->hostApi = hostApiIndex;
159
+
160
+                deviceInfo->maxInputChannels = 0;
161
+                deviceInfo->maxOutputChannels = 0;
162
+
163
+                deviceInfo->defaultLowInputLatency = defaultLowLatency;
164
+                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
165
+                deviceInfo->defaultHighInputLatency = defaultHighLatency;
166
+                deviceInfo->defaultHighOutputLatency = defaultHighLatency; 
167
+
168
+                result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
169
+                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
170
+                if( result != paNoError )
171
+                    return result;
172
+
173
+                if( deviceInfoInitializationSucceeded )
174
+                {
175
+                    if( hostApi->info.defaultOutputDevice == paNoDevice )
176
+                        hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
177
+
178
+                    winMmeHostApi->winMmeDeviceIds[ hostApi->info.deviceCount ] = winMmeDeviceId;
179
+                    hostApi->deviceInfos[ hostApi->info.deviceCount ] = deviceInfo;
180
+
181
+                    winMmeHostApi->outputDeviceCount++;
182
+                    hostApi->info.deviceCount++;
183
+                }
184
+            }
185
+        }
186
+    }    
187
+    return paNoError;
188
+}
189
+
190
+/* Processes OS messages arriving at the hWnd window */
191
+INT_PTR WINAPI ProcessOSMessage( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
192
+{
193
+    /* winMmeHostApi is used in order to query the number of audio devices currently handled by PA */
194
+    static PaWinMmeHostApiRepresentation *winMmeHostApi_ = NULL;
195
+    switch( message )
196
+    {
197
+        case WM_CREATE:
198
+            /* Initialize hostApi pointer on the first run. */
199
+            if (winMmeHostApi_ == NULL)
200
+            {
201
+                CREATESTRUCT *CrtStrPtr = (CREATESTRUCT *) lParam;
202
+                winMmeHostApi_ = (PaWinMmeHostApiRepresentation*)(CrtStrPtr->lpCreateParams);
203
+            }
204
+            break;
205
+        case WM_DEVICECHANGE:
206
+            /* Possible insertion or removal of device. There's some issues:
207
+
208
+                - Some devices/drivers does not trigger arrival nor
209
+                removecomplete events, but only devnodes_changed events.
210
+                Therefore, we process all of those type of events.
211
+
212
+                - Some hardware can send many devnodes_changed events at the
213
+                same time (up to ~15 of such events). These batches are
214
+                detected using temporal locality, using constMaxBatchPeriod_
215
+                and processedTimeStamp_. Once the device is detected, the
216
+                rest of redundant events are discarded.  In order to know if
217
+                there's a new device or not, actual audio devices count is
218
+                compared to stored audio devices count (via winMmeHostApi_).
219
+                A possible improvement would be to process each message in a
220
+                separate thread.
221
+
222
+                - Hardware takes some time to settle and be recognized by
223
+                drivers. A small window of time is given in order to account
224
+                for this (constMaxSettleTime_);
225
+
226
+                Settle time should be slightly lower than batch period.
227
+            */
228
+            if ( wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE || wParam == DBT_DEVNODES_CHANGED )
229
+            {
230
+                const int constMaxBatchPeriod_ = 3; /* seconds */
231
+                const int constMaxSettleTime_ = (constMaxBatchPeriod_ * 1000) - 500; /* milliseconds */
232
+
233
+                /* Initialize reference timestamp on first run, using a past
234
+                time, so that first event belongs to a new batch. */
235
+                static time_t processedTimeStamp_ = 0;
236
+                if ( processedTimeStamp_ == 0 ) processedTimeStamp_ = time( NULL ) - ( constMaxBatchPeriod_ * 2 );
237
+
238
+                /* Loop that allows hardware to settle */
239
+                int settleTimeLeft = constMaxSettleTime_;
240
+                while ( settleTimeLeft > 0 )
241
+                {
242
+                    /* Check if actual devices lists (I/O) sizes have actually
243
+                    changed before notifying upper levels */
244
+                    if( waveInGetNumDevs() + 1 != winMmeHostApi_->inputDeviceCount || waveOutGetNumDevs() + 1 != winMmeHostApi_->outputDeviceCount)
245
+                    {
246
+                        /* Hardware actually changed */
247
+                        PaUtil_DevicesChanged( paUtilHardwareDevicesChanged );
248
+                        processedTimeStamp_ = time( NULL );
249
+                        break;
250
+                    }
251
+                    else
252
+                    {
253
+                        /* Hardware hasn't changed [yet] */
254
+                        //if ( difftime( time( NULL ), processedTimeStamp_ ) < constMaxBatchPeriod_ )
255
+                        {
256
+                            /* We're still in the same batch of messages, disregard */
257
+                            //processedTimeStamp_ = time( NULL );
258
+                        }
259
+                        /* Hardware settling pass... */
260
+                        Sleep(250);
261
+                        settleTimeLeft -= 250;
262
+                    }
263
+                }
264
+            }
265
+            break;
266
+        case WM_CLOSE:
267
+            if ( ! DestroyWindow( hWnd ) )
268
+            {
269
+                PA_DEBUG(("ProcessOSMessage: Couldn't destroy message window.\n"));
270
+            }
271
+            break;
272
+        case WM_DESTROY:
273
+            PostQuitMessage( 0 );
274
+            break;
275
+        default:
276
+            break;
277
+    }
278
+    return 1;
279
+}
280
+
281
+
282
+/* Creates the window that will receive OS messages */
283
+static PaError CreateOSMessagesWindow( PaWinMmeHostApiRepresentation *winMmeHostApi )
284
+{
285
+    PaError result = paUnanticipatedHostError;
286
+
287
+    /* Set up and register window class */
288
+    WNDCLASSEX wndClass;
289
+    ZeroMemory( &wndClass, sizeof(WNDCLASSEX) );
290
+    wndClass.cbSize = sizeof(WNDCLASSEX);
291
+    wndClass.style = CS_OWNDC;
292
+    wndClass.lpfnWndProc = (WNDPROC)(ProcessOSMessage);
293
+    wndClass.hInstance = (HINSTANCE)(GetModuleHandle( 0 ));
294
+    wndClass.lpszClassName = "DeviceChangeMessageWindow";
295
+
296
+    if ( RegisterClassEx(&wndClass) )
297
+    {
298
+        /* Create the window that will receive OS messages */
299
+        HWND hWnd = CreateWindowEx( 0, "DeviceChangeMessageWindow", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, (LPVOID)(winMmeHostApi) );
300
+        if ( hWnd != NULL )
301
+        {
302
+            if ( UpdateWindow( hWnd ) != 0 )
303
+            {
304
+                result = paNoError;
305
+            }
306
+        }
307
+    }
308
+
309
+    return result;
310
+}
311
+
312
+static PaError DispatchOSMessages()
313
+{
314
+    PaError result = paNoError;
315
+    MSG msg; 
316
+    int retVal;
317
+
318
+    /* Process OS messages with low cpu-usage wait loop */
319
+    while( (retVal = GetMessage( &msg, NULL, 0, 0 ) ) != 0 ) 
320
+    { 
321
+        if ( retVal == -1 )
322
+        {
323
+            PA_DEBUG("DispatchOSMessages: Couldn't process OS message.\n");
324
+            result = paUnanticipatedHostError;
325
+            break;
326
+        }
327
+        else
328
+        {
329
+            TranslateMessage( &msg );
330
+            DispatchMessage( &msg );
331
+        }
332
+    } 
333
+
334
+    return result;
335
+}
336
+static DWORD WINAPI DeviceDetectionThreadProc( void *pArg )
337
+{
338
+    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)(pArg);
339
+    DWORD result = -1;
340
+
341
+    if ( CreateOSMessagesWindow(winMmeHostApi) == paNoError )
342
+    {
343
+        if ( DispatchOSMessages() == paNoError )
344
+        {
345
+            result = 0;
346
+        }
347
+    }
348
+
349
+    return result;
350
+}
351
+
352
 
353
 static DWORD WINAPI ProcessingThreadProc( void *pArg )
354
 {
355
@@ -3312,7 +3614,7 @@
356
         if( result != paNoError ) goto error;
357
 
358
         /* Create thread that waits for audio buffers to be ready for processing. */
359
-        stream->processingThread = CREATE_THREAD;
360
+        stream->processingThread = CREATE_THREAD(ProcessingThreadProc, stream, &stream->processingThreadId);
361
         if( !stream->processingThread )
362
         {
363
             result = paUnanticipatedHostError;
new-python-sipsimple-2/setup_pjsip.py 2010-08-14 10:13:00 +0000
109 109
                   "patches/pjsip-2830-dont_accept_sdp_everywhere.patch",
110 110
                   "patches/pjsip-2830-allocate_thread_desc_from_pool.patch",
111 111
                   "patches/pjsip-2830-do_not_close_stream_too_fast.patch",
112
                   "patches/pjsip-2830-hide_route_header.patch"]
112
                   "patches/pjsip-2830-hide_route_header.patch",
113
                   "patches/pjsip-2830-runtime_device_change_detection_wmme.patch"]
113 114
    pjsip_svn_repos = {"trunk": "http://svn.pjsip.org/repos/pjproject/trunk",
114 115
                       "1.0": "http://svn.pjsip.org/repos/pjproject/branches/1.0"}
115 116
    portaudio_patch_files = ["patches/portaudio-1420-runtime_device_change_detection.patch",
116 117
                    "patches/portaudio-1420-compile_snow_leopard.patch",
117
                    "patches/portaudio-1420-pa_mac_core_x64_assert_fix.patch"]
118
                    "patches/portaudio-1420-pa_mac_core_x64_assert_fix.patch",
119
                    "patches/portaudio-1420-runtime_device_change_detection_wmme.patch"]
118 120

  
119 121
    trunk_overrides = []
120 122